fluxion-vio 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +189 -2
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +411 -0
- package/dist/index.d.mts +411 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/instruction.md +552 -0
- package/package.json +72 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kasukabe tsumugi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,190 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Fluxion Vio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.org/package/fluxion-vio)
|
|
4
|
+
[](https://www.npmjs.org/package/fluxion-vio)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="assets/fluxion.png" width="240px" alt="Fluxion Vio logo" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
**Fluxion Vio** is a branch of [Fluxion v0.20.4](https://github.com/baendlorel/fluxion): a filesystem-routing HTTP server with PHP-style hot reload. The file path is the URL path — edit a file under `dir`, and the next request serves the new code. No build step, no route table, no watcher, no restart.
|
|
12
|
+
|
|
13
|
+
It is meant for **modest traffic** behind a trusted network: internal tools, admin panels, small APIs, webhooks, prototypes. It is **not** a security boundary and not built for high concurrency — for anything public, put a reverse proxy in front.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add fluxion-vio
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`esbuild` ships as a dependency and compiles every file under `dir` on load, so **you do not need `tsx` for your routes**: the entry point may be plain JavaScript while everything under `dir` stays TypeScript.
|
|
22
|
+
|
|
23
|
+
A TypeScript entry point is _not_ compiled by Fluxion — run it with Node ≥ 22.18's built-in type stripping, or with `tsx`.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
`server.js`:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { fluxion } from 'fluxion-vio';
|
|
31
|
+
|
|
32
|
+
await fluxion({ dir: './dynamic', host: '127.0.0.1', port: 3000 });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`dynamic/hello.ts`:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { defineFluxionModule } from 'fluxion-vio';
|
|
39
|
+
|
|
40
|
+
export default defineFluxionModule(async (req) => {
|
|
41
|
+
return { message: 'hello vio', path: req.url.pathname };
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
node server.js
|
|
47
|
+
curl http://127.0.0.1:3000/hello.ts
|
|
48
|
+
# {"message":"hello vio","path":"/hello.ts"}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Routing
|
|
52
|
+
|
|
53
|
+
- Files matching `apiInclude` (default `**/*.ts`) are API handlers.
|
|
54
|
+
- Files matching `staticInclude` (default `**/*`) are static resources, `GET`/`HEAD` only.
|
|
55
|
+
- Files matching `exclude` are never served.
|
|
56
|
+
- The file extension is part of the route.
|
|
57
|
+
|
|
58
|
+
| File | Route | Type |
|
|
59
|
+
| ------------------------- | ------------------ | ----------- |
|
|
60
|
+
| `dynamic/hello.ts` | `/hello.ts` | API handler |
|
|
61
|
+
| `dynamic/user/profile.ts` | `/user/profile.ts` | API handler |
|
|
62
|
+
| `dynamic/index.html` | `/index.html` | Static file |
|
|
63
|
+
|
|
64
|
+
## API handlers
|
|
65
|
+
|
|
66
|
+
Handlers must be created with `defineFluxionModule()`; a bare exported function is rejected.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { defineFluxionModule, defineFluxionMiddleware } from 'fluxion-vio';
|
|
70
|
+
|
|
71
|
+
const auth = defineFluxionMiddleware(async (req, cx, rawReq, rawRes) => {
|
|
72
|
+
if (!req.headers.authorization) {
|
|
73
|
+
rawRes.statusCode = 401;
|
|
74
|
+
rawRes.end('Unauthorized');
|
|
75
|
+
return; // the handler is not called
|
|
76
|
+
}
|
|
77
|
+
req.meta.user = { id: 1 };
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export default defineFluxionModule({
|
|
81
|
+
middlewares: [auth],
|
|
82
|
+
methods: ['GET', 'POST'], // omit to accept every method
|
|
83
|
+
handlerTimeoutMs: 10_000, // override options.handlerTimeoutMs
|
|
84
|
+
handler: async (req) => ({ user: req.meta.user }),
|
|
85
|
+
disposer: async () => {
|
|
86
|
+
// runs after the response for this request has been sent
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Handler signature: `handler(req, cx, rawReq, rawRes)`
|
|
92
|
+
|
|
93
|
+
| Argument | Contents |
|
|
94
|
+
| -------- | --------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `req` | `{ method, ip, url, query, body, headers, cookie, meta }` — `meta` is shared with middleware. |
|
|
96
|
+
| `cx` | `{ logger }` |
|
|
97
|
+
| `rawReq` | Node.js `http.IncomingMessage` |
|
|
98
|
+
| `rawRes` | Node.js `http.ServerResponse` |
|
|
99
|
+
|
|
100
|
+
Return a value to send `200` + JSON, or write to `rawRes` yourself. If `rawRes.writableEnded` is true, nothing else is sent.
|
|
101
|
+
|
|
102
|
+
## Request body
|
|
103
|
+
|
|
104
|
+
Parsed before the handler runs, except for `GET`/`HEAD`:
|
|
105
|
+
|
|
106
|
+
- `application/json` → the object; primitives become `{ value }`; invalid JSON becomes `{ raw }`.
|
|
107
|
+
- Form data → key/value fields. `text/*` → `{ raw }`. Binary → `{}` (size check only).
|
|
108
|
+
- Bodies over `maxRequestBytes` (default 8 MB) get `413`.
|
|
109
|
+
|
|
110
|
+
## Middleware
|
|
111
|
+
|
|
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
|
+
|
|
114
|
+
## HTTP exceptions
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { defineFluxionModule, NotFoundException } from 'fluxion-vio';
|
|
118
|
+
|
|
119
|
+
export default defineFluxionModule(() => {
|
|
120
|
+
throw new NotFoundException('nothing here');
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`BadRequest` 400 · `Unauthorized` 401 · `Forbidden` 403 · `NotFound` 404 · `MethodNotAllowed` 405 · `NotAcceptable` 406 · `RequestTimeout` 408 · `Conflict` 409 · `Gone` 410 · `PayloadTooLarge` 413 · `UnsupportedMediaType` 415 · `UnprocessableEntity` 422 · `TooManyRequests` 429 · `InternalServerError` 500 · `NotImplemented` 501 · `BadGateway` 502 · `ServiceUnavailable` 503 · `GatewayTimeout` 504
|
|
125
|
+
|
|
126
|
+
(each is exported as `<Name>Exception`). Uncaught errors return a generic `500`; details are never sent to the client.
|
|
127
|
+
|
|
128
|
+
## Hot reload
|
|
129
|
+
|
|
130
|
+
Every request resolves its file from disk, so there is no route table to refresh and no watcher:
|
|
131
|
+
|
|
132
|
+
- Modules are loaded on demand and remembered together with the `mtime` and `size` of the file and of every file it imports from `dir`.
|
|
133
|
+
- On the next request those are compared. If any one differs, the whole chain is dropped and re-evaluated; otherwise the loaded module is reused without reading anything.
|
|
134
|
+
- Content is never compared, so an edit that keeps both `mtime` and `size` goes unnoticed.
|
|
135
|
+
- Packages from `node_modules` and files outside `dir` go through Node's own `require` and stay cached. Files outside `dir` are therefore **not** compiled: a TS helper placed there relies on Node's built-in type stripping (no `enum`, `namespace`, or decorators).
|
|
136
|
+
- A deleted file means `404`; recreating it serves it immediately. A new file is loaded only when a request asks for it or a changed file starts importing it.
|
|
137
|
+
|
|
138
|
+
Module-level state survives between requests until a reload resets it — keep handlers stateless.
|
|
139
|
+
|
|
140
|
+
## Options
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
interface FluxionOptions {
|
|
144
|
+
dir: string; // route directory, created if missing
|
|
145
|
+
host: string;
|
|
146
|
+
port: number;
|
|
147
|
+
|
|
148
|
+
handlerTimeoutMs?: number; // 5000
|
|
149
|
+
middlewareTimeoutMs?: number; // 3000
|
|
150
|
+
staticResourceTimeoutMs?: number; // 180000
|
|
151
|
+
|
|
152
|
+
apiInclude?: string[]; // ['**/*.ts']
|
|
153
|
+
staticInclude?: string[]; // ['**/*']
|
|
154
|
+
exclude?: string[]; // node_modules, .git, dist, ...
|
|
155
|
+
|
|
156
|
+
maxRequestBytes?: number; // 8_000_000
|
|
157
|
+
logger?: 'one-line' | 'json-line' | ((entry) => void); // 'one-line'
|
|
158
|
+
target?: string | string[]; // esbuild target, default 'node<major>'
|
|
159
|
+
https?: { key: string | Buffer; cert: string | Buffer; ca?: string | Buffer | Array<string | Buffer> };
|
|
160
|
+
csp?: string | false; // "default-src 'self'"; false omits the header
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`https.key`/`cert`/`ca` accept PEM content or a path relative to the current working directory. For small services, terminating TLS at a reverse proxy is usually simpler.
|
|
165
|
+
|
|
166
|
+
## Security
|
|
167
|
+
|
|
168
|
+
Every response gets `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `X-XSS-Protection: 1; mode=block`, and a CSP header; path traversal outside `dir` is rejected. Beyond that Vio provides no authentication, authorization, CSRF protection, rate limiting, or sandboxing — handlers run with full Node.js privileges. Bind to a private interface, keep secrets out of `dir` (it is web-served by default), and never expose the server directly to the internet.
|
|
169
|
+
|
|
170
|
+
## Deployment
|
|
171
|
+
|
|
172
|
+
Single process, no supervisor. Use pm2, systemd, Docker, or a similar manager:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pm2 start "node server.js" --name fluxion-vio-app
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Need clustering? Run several instances behind a proxy, or use upstream Fluxion.
|
|
179
|
+
|
|
180
|
+
## Migrating from Fluxion
|
|
181
|
+
|
|
182
|
+
The APIs are identical; rename the package and import specifier, and replace `launchDaemon` with a process manager.
|
|
183
|
+
|
|
184
|
+
## Development
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pnpm build # bundle with tsdown
|
|
188
|
+
pnpm test # vitest
|
|
189
|
+
pnpm lint # oxlint
|
|
190
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
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:
|
|
2
|
+
- "apiInclude" for API handler patterns (default: ["**/*.ts"])
|
|
3
|
+
- "staticInclude" for static resource patterns (default: ["**/*"])
|
|
4
|
+
Example migration:
|
|
5
|
+
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;
|
|
7
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","fs","sep","path","fs"],"sources":["../src/common/logger.ts","../src/common/consts.ts","../src/common/error.ts","../src/defines/options.ts","../src/common/promise-try.ts","../src/http/headers.ts","../src/http/request.ts","../src/http/respond.ts","../src/http/query.ts","../src/http/exceptions.ts","../src/http/body.ts","../src/http/cookie.ts","../src/http/server.ts","../node_modules/.pnpm/type-narrow@0.2.2/node_modules/type-narrow/dist/index.mjs","../src/common/loader.ts","../src/common/injector.ts","../src/router/index.ts","../src/fluxion.ts","../src/defines/index.ts","../src/index.ts"],"sourcesContent":["import type { FluxionContext } from '@/types.js';\nimport type { otherstring } from '@/types.js';\nimport stringify from 'fast-json-stable-stringify';\nimport { createWriteStream, existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { mkdirSync } from 'node:fs';\n\ntype LogLevel = 'CORE' | 'INFO' | 'WARN' | 'ERROR' | 'SUCC' | 'DEBUG' | 'VERBOSE' | otherstring;\n\ninterface LogEntry {\n timestamp: string;\n level: LogLevel;\n [key: string]: unknown;\n}\n\nexport type LoggerOption = 'one-line' | 'json-line' | FluxionLoggerFn;\n\nexport type FluxionLoggerFn = (entry: LogEntry) => void;\n\nexport interface MessageObject {\n [key: string]: unknown;\n message?: string;\n}\n\nexport interface FluxionLogger {\n /**\n * [WARN] We assert that `fields` is an object or undefined.\n */\n write(level: LogLevel, messageOrObject: string | MessageObject): void;\n info(messageOrObject: string | MessageObject): void;\n warn(messageOrObject: string | MessageObject): void;\n error(messageOrObject: string | MessageObject): void;\n succ(messageOrObject: string | MessageObject): void;\n debug(messageOrObject: string | MessageObject): void;\n verbose(messageOrObject: string | MessageObject): void;\n}\n\n/**\n * Internal-only logger used by fluxion's own subsystems (router, injector,\n * ...). It extends the public {@link FluxionLogger}\n * with a `core` level that records framework-originated logs — e.g. route\n * registration, module lifecycle — so they are visually distinct\n * from logs emitted by user handlers.\n *\n * ! This type is NOT exported to application code: {@link FluxionModuleContext}\n * exposes only {@link FluxionLogger}, keeping\n * `core` off-limits to user handlers.\n */\nexport interface InternalFluxionLogger extends FluxionLogger {\n core(messageOrObject: string | MessageObject): void;\n}\n\nconst safeStringify = (value: unknown): string => {\n try {\n return stringify(value);\n } catch {\n return '[unserializable]';\n }\n};\n\nconst ColoredLevels: Record<LogLevel, string> = {\n CORE: `CORE`,\n INFO: `INFO`,\n WARN: `WARN`,\n ERROR: `ERROR`,\n SUCC: `SUCC`,\n DEBUG: `DEBUG`,\n VERBOSE: `VERBOSE`,\n};\n\nexport const oneLineLogger: FluxionLoggerFn = (entry: LogEntry) => {\n const { level: rawLevel, timestamp: rawTimestamp, message: rawMessage, pid, ...fields } = entry;\n\n const timestamp = `[${rawTimestamp}]`;\n const level = ColoredLevels[rawLevel] ?? rawLevel;\n const pidText = pid === undefined ? '' : ` [${pid}]`;\n const fieldsText = Object.keys(fields).length > 0 ? ` ${safeStringify(fields)}` : '';\n\n // 智能处理消息内容:如果有 message 字段就显示,否则只显示 fields\n const content = rawMessage ? rawMessage + fieldsText : fieldsText.trim();\n\n // eslint-disable-next-line @typescript-eslint/no-console\n console.log(`${timestamp} ${level}${pidText} ${content}`);\n};\n\n/**\n * 创建文件日志写入器\n */\nfunction createFileSink(logFilePath: string): (entry: LogEntry) => void {\n // 确保日志目录存在\n if (!existsSync(logFilePath)) {\n const dir = dirname(logFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n const fileStream = createWriteStream(logFilePath, { flags: 'a' });\n\n return (entry: LogEntry) => {\n try {\n const timestamp = entry.timestamp || new Date().toISOString();\n const level = entry.level || 'INFO';\n const pid = entry.pid !== undefined ? ` [${entry.pid}]` : '';\n const message = entry.message ? entry.message : safeStringify(entry);\n\n fileStream.write(`[${timestamp}] ${level}${pid} ${message}\\n`);\n } catch {\n // 忽略文件写入错误\n }\n };\n}\n\n/**\n * & Logger Options here is checked by normalizeOptions function.\n */\nfunction resolveLoggerSink(cx: Pick<FluxionContext, 'options'>): FluxionLoggerFn {\n const instanceLogPath = process.env.FLUXION_INSTANCE_LOG;\n const fileSink = instanceLogPath ? createFileSink(instanceLogPath) : null;\n\n const loggerOption = cx.options.logger;\n if (loggerOption === undefined || loggerOption === 'one-line') {\n if (fileSink) {\n return (entry: LogEntry) => {\n oneLineLogger(entry);\n fileSink(entry);\n };\n }\n return oneLineLogger;\n }\n\n if (loggerOption === 'json-line') {\n // eslint-disable-next-line @typescript-eslint/no-console\n const jsonSink = (entry: LogEntry) => console.log(safeStringify(entry));\n if (fileSink) {\n return (entry: LogEntry) => {\n jsonSink(entry);\n fileSink(entry);\n };\n }\n return jsonSink;\n }\n\n if (fileSink) {\n // 自定义 logger + 文件输出\n return (entry: LogEntry) => {\n loggerOption(entry);\n fileSink(entry);\n };\n }\n\n return loggerOption;\n}\n\nexport function createLogger(cx: Pick<FluxionContext, 'options'>): InternalFluxionLogger {\n const sink = resolveLoggerSink(cx);\n\n const logger: InternalFluxionLogger = {\n write(level: LogLevel, o: string | object): void {\n const entry: LogEntry =\n typeof o === 'string'\n ? {\n message: o,\n timestamp: new Date().toISOString(),\n level,\n }\n : {\n ...o,\n timestamp: new Date().toISOString(),\n level,\n };\n\n try {\n sink(entry);\n } catch {\n // Ignore logger sink failures to avoid breaking request handling.\n }\n },\n info(messageOrObject: string | MessageObject): void {\n this.write('INFO', messageOrObject);\n },\n warn(messageOrObject: string | MessageObject): void {\n this.write('WARN', messageOrObject);\n },\n error(messageOrObject: string | MessageObject): void {\n this.write('ERROR', messageOrObject);\n },\n succ(messageOrObject: string | MessageObject): void {\n this.write('SUCC', messageOrObject);\n },\n debug(messageOrObject: string | MessageObject): void {\n this.write('DEBUG', messageOrObject);\n },\n verbose(messageOrObject: string | MessageObject): void {\n this.write('VERBOSE', messageOrObject);\n },\n core(messageOrObject: string | MessageObject): void {\n this.write('CORE', messageOrObject);\n },\n };\n\n return logger;\n}\n\n/**\n * Create a worker logger that prefixes all log messages with the worker PID.\n */\nexport function createWorkerLogger(baseLogger: FluxionLogger, pid: number): InternalFluxionLogger {\n return {\n write(level: LogLevel, messageOrObject: string | MessageObject): void {\n baseLogger.write(\n level,\n typeof messageOrObject === 'string' ? { message: messageOrObject, pid } : { ...messageOrObject, pid },\n );\n },\n info(messageOrObject: string | MessageObject): void {\n this.write('INFO', messageOrObject);\n },\n warn(messageOrObject: string | MessageObject): void {\n this.write('WARN', messageOrObject);\n },\n error(messageOrObject: string | MessageObject): void {\n this.write('ERROR', messageOrObject);\n },\n succ(messageOrObject: string | MessageObject): void {\n this.write('SUCC', messageOrObject);\n },\n debug(messageOrObject: string | MessageObject): void {\n this.write('DEBUG', messageOrObject);\n },\n verbose(messageOrObject: string | MessageObject): void {\n this.write('VERBOSE', messageOrObject);\n },\n core(messageOrObject: string | MessageObject): void {\n this.write('CORE', messageOrObject);\n },\n };\n}\n\n/**\n * ! Error.isError needs Node.js 24\n */\nexport const getErrorMessage =\n typeof Error.isError === 'function'\n ? (e: unknown): string => (Error.isError(e) ? e.message : String(e))\n : (e: unknown): string => (e as any)?.message || String(e);\n","export const DUMMY_BASE_URL = 'http://fluxion.local';\n\nexport namespace flags {\n export const OptionsNormalized = Symbol('fluxion.options.normalized');\n export const StaticHandled = Symbol('fluxion.router.StaticHandled');\n export const HandlerTimeout = Symbol('fluxion.handlerTimeout');\n export const MiddlewareTimeout = Symbol('fluxion.middlewareTimeout');\n}\n\nexport const STATIC_CONTENT_TYPES: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.cjs': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.jsonld': 'application/ld+json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.md': 'text/markdown; charset=utf-8',\n '.xml': 'application/xml; charset=utf-8',\n '.csv': 'text/csv; charset=utf-8',\n '.yaml': 'application/yaml; charset=utf-8',\n '.yml': 'application/yaml; charset=utf-8',\n '.toml': 'application/toml; charset=utf-8',\n\n '.ico': 'image/x-icon',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.svg': 'image/svg+xml',\n '.bmp': 'image/bmp',\n '.tif': 'image/tiff',\n '.tiff': 'image/tiff',\n\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.eot': 'application/vnd.ms-fontobject',\n\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.ogg': 'audio/ogg',\n '.oga': 'audio/ogg',\n '.m4a': 'audio/mp4',\n '.aac': 'audio/aac',\n '.flac': 'audio/flac',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.ogv': 'video/ogg',\n '.mov': 'video/quicktime',\n '.m4v': 'video/mp4',\n\n '.pdf': 'application/pdf',\n '.wasm': 'application/wasm',\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n '.rar': 'application/vnd.rar',\n '.7z': 'application/x-7z-compressed',\n '.webmanifest': 'application/manifest+json; charset=utf-8',\n '.exe': 'application/octet-stream',\n};\n\nexport enum HttpCode {\n // 2xx Success\n Ok = 200,\n Created = 201,\n Accepted = 202,\n NoContent = 204,\n PartialContent = 206,\n\n // 3xx Redirection\n MovedPermanently = 301,\n Found = 302,\n NotModified = 304,\n TemporaryRedirect = 307,\n PermanentRedirect = 308,\n\n // 4xx Client Error\n BadRequest = 400,\n Unauthorized = 401,\n Forbidden = 403,\n NotFound = 404,\n MethodNotAllowed = 405,\n NotAcceptable = 406,\n RequestTimeout = 408,\n Conflict = 409,\n Gone = 410,\n PayloadTooLarge = 413,\n UnsupportedMediaType = 415,\n UnprocessableEntity = 422,\n TooManyRequests = 429,\n\n // 5xx Server Error\n InternalServerError = 500,\n NotImplemented = 501,\n BadGateway = 502,\n ServiceUnavailable = 503,\n GatewayTimeout = 504,\n}\n\nexport const enum ModuleType {\n Api,\n StaticResource,\n}\n","export class FluxionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FluxionVioError';\n }\n}\n","import { FluxionError } from '@/common/error.js';\nimport type { FluxionOptions, NormalizedFluxionOptions } from '@/types.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { flags } from '@/common/consts.js';\n\n/**\n * Read certificate content from a file path or return the content directly.\n * Relative paths are resolved against the current working directory.\n */\nfunction readCertificateContent(content: string | Buffer): Buffer {\n if (Buffer.isBuffer(content)) {\n return content;\n }\n if (typeof content === 'string') {\n // Check if it looks like a file path (not a PEM certificate)\n // PEM certificates start with \"-----BEGIN\"\n if (!content.startsWith('-----BEGIN')) {\n const filePath = path.resolve(content);\n if (fs.existsSync(filePath)) {\n return fs.readFileSync(filePath);\n }\n }\n return Buffer.from(content);\n }\n throw new FluxionError('Certificate content must be a string or Buffer');\n}\n\n/**\n * Normalize HTTPS options.\n */\nfunction normalizeHttpsOptions(https: FluxionOptions['https']): NormalizedFluxionOptions['https'] | undefined {\n if (!https) {\n return undefined;\n }\n\n if (typeof https !== 'object' || https === null || Array.isArray(https)) {\n throw new FluxionError('FluxionOptions.https must be an object');\n }\n if (typeof https.key !== 'string' && !Buffer.isBuffer(https.key)) {\n throw new FluxionError('FluxionOptions.https.key must be a string or Buffer');\n }\n if (typeof https.cert !== 'string' && !Buffer.isBuffer(https.cert)) {\n throw new FluxionError('FluxionOptions.https.cert must be a string or Buffer');\n }\n\n const result: NormalizedFluxionOptions['https'] = {\n key: readCertificateContent(https.key),\n cert: readCertificateContent(https.cert),\n };\n\n if (https.ca !== undefined) {\n if (Array.isArray(https.ca)) {\n result.ca = https.ca.map((item) => readCertificateContent(item));\n } else {\n result.ca = readCertificateContent(https.ca);\n }\n }\n\n return result;\n}\n\n/**\n * Normalize options and create necessary resources like the dynamic directory and logger.\n */\nexport function defineFluxionOptions(o: FluxionOptions): NormalizedFluxionOptions {\n if (typeof o !== 'object' || o === null || Array.isArray(o)) {\n throw new FluxionError('FluxionOptions must be an object');\n }\n\n // Check for deprecated 'include' option\n if ('include' in o) {\n throw new FluxionError(\n 'The \"include\" option has been removed. Please use:\\n' +\n ' - \"apiInclude\" for API handler patterns (default: [\"**/*.ts\"])\\n' +\n ' - \"staticInclude\" for static resource patterns (default: [\"**/*\"])\\n' +\n 'Example migration:\\n' +\n ' OLD: { include: [\"**/*.ts\", \"**/*.js\"], apiInclude: [\"**/*.ts\"] }\\n' +\n ' NEW: { apiInclude: [\"**/*.ts\"], staticInclude: [\"**/*.js\"] }',\n );\n }\n\n const {\n dir: rawDir,\n host,\n port: userPort,\n handlerTimeoutMs = 5000,\n middlewareTimeoutMs = 3000,\n staticResourceTimeoutMs = 3 * 60 * 1000,\n target = `node${process.versions.node.split('.')[0]}`,\n maxRequestBytes = 8_000_000,\n apiInclude = ['**/*.ts'],\n staticInclude = ['**/*'],\n exclude = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.vscode/**',\n '**/.idea/**',\n '**/*.log',\n '**/.DS_Store',\n '**/coverage/**',\n '**/.nyc_output/**',\n '**/*.tmp',\n '**/*.temp',\n ],\n https,\n csp = \"default-src 'self'\",\n } = o as FluxionOptions;\n\n const port = userPort;\n\n const logger = o.logger ?? 'one-line';\n if (logger !== 'one-line' && logger !== 'json-line' && typeof logger !== 'function') {\n throw new FluxionError(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`);\n }\n\n if (typeof rawDir !== 'string') {\n throw new FluxionError('FluxionOptions.dir must be a string');\n }\n // Ends with a separator, so \"is this file under dir\" is a plain `startsWith`\n const resolvedDir = path.resolve(process.cwd(), rawDir);\n const dir = resolvedDir.endsWith(path.sep) ? resolvedDir : resolvedDir + path.sep;\n\n if (typeof target !== 'string' && !(Array.isArray(target) && target.every((t) => typeof t === 'string'))) {\n throw new FluxionError('FluxionOptions.target must be a string or an array of strings');\n }\n\n if (typeof host !== 'string') {\n throw new FluxionError('FluxionOptions.host must be a string');\n }\n\n if (!Number.isSafeInteger(handlerTimeoutMs) || handlerTimeoutMs <= 100) {\n throw new FluxionError('FluxionOptions.handlerTimeoutMs must be an integer greater than 100');\n }\n\n if (!Number.isSafeInteger(middlewareTimeoutMs) || middlewareTimeoutMs <= 100) {\n throw new FluxionError('FluxionOptions.middlewareTimeoutMs must be an integer greater than 100');\n }\n\n if (typeof port !== 'number' || !Number.isSafeInteger(port)) {\n throw new FluxionError('FluxionOptions.port must be a positive integer');\n }\n\n if (port <= 1 || port > 65535) {\n throw new FluxionError('FluxionOptions.port must be 1 ~ 65535');\n }\n\n if (typeof maxRequestBytes !== 'number' || maxRequestBytes <= 0 || !Number.isSafeInteger(maxRequestBytes)) {\n throw new FluxionError('FluxionOptions.maxRequestBytes must be a positive integer');\n }\n\n if (csp !== false && typeof csp !== 'string') {\n throw new FluxionError('FluxionOptions.csp must be a string or false');\n }\n\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const result = {\n dir,\n host,\n port,\n handlerTimeoutMs,\n middlewareTimeoutMs,\n staticResourceTimeoutMs,\n target,\n maxRequestBytes,\n logger,\n apiInclude,\n staticInclude,\n exclude,\n https: normalizeHttpsOptions(https),\n csp,\n normalizedFlag: flags.OptionsNormalized,\n };\n\n if (process.argv.includes('--verbose')) {\n console.log('normalized options', { ...result, https: result.https ? 'set' : 'unset' });\n }\n\n return result;\n}\n","/**\n * For low version of Node.js that does not support `Promise.try`, we can implement it ourselves.\n *\n * Only for async functions.\n */\nexport function PromiseTry<T extends (...args: any[]) => any>(fn: T, ...args: Parameters<T>) {\n return new Promise<ReturnType<T>>((resolve, reject) => {\n // in case `fn` throws synchronously, we catch it and reject the promise\n try {\n const r = fn(...args);\n if (r instanceof Promise) {\n r.then(resolve).catch(reject);\n } else {\n resolve(r);\n }\n } catch (e) {\n reject(e);\n }\n });\n}\n","import type { IncomingMessage } from 'node:http';\n\nexport function getRealIp(req: IncomingMessage): string {\n const forwardedFor = req.headersDistinct['x-forwarded-for'];\n if (forwardedFor) {\n const firstForwarded = forwardedFor[0]?.split(',')[0]?.trim();\n if (firstForwarded && firstForwarded.length > 0) {\n return firstForwarded;\n }\n }\n\n const realIp = req.headersDistinct['x-real-ip']?.[0].trim();\n if (realIp !== undefined) {\n return realIp;\n }\n\n return req.socket.remoteAddress ?? 'unknown';\n}\n\nexport function isTextualContentType(contentType: string | undefined): boolean {\n if (contentType === undefined) {\n return false;\n }\n\n const normalized = contentType.toLowerCase();\n\n return (\n normalized.startsWith('text/') ||\n normalized.includes('json') ||\n normalized.includes('xml') ||\n normalized.includes('x-www-form-urlencoded') ||\n normalized.includes('javascript')\n );\n}\n","import { DUMMY_BASE_URL } from '@/common/consts.js';\n\nexport function toURL(rawUrl: string | undefined): URL | undefined {\n if (rawUrl === undefined) {\n return undefined;\n }\n\n try {\n return new URL(rawUrl, DUMMY_BASE_URL);\n } catch {\n return undefined;\n }\n}\n","import type { ServerResponse } from 'node:http';\nimport { HttpCode } from '@/common/consts.js';\n\nexport function sendJson(res: ServerResponse, payload: unknown, statusCode: HttpCode = HttpCode.Ok): void {\n res.statusCode = statusCode;\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.end(JSON.stringify(payload));\n}\n\nexport function safeSendJson(res: ServerResponse, payload: unknown, statusCode: HttpCode = HttpCode.Ok): void {\n if (res.writableEnded) {\n return;\n }\n\n if (res.headersSent) {\n res.end();\n return;\n }\n\n sendJson(res, payload, statusCode);\n}\n","export function parseQuery(searchParams: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n\n for (const [key, value] of searchParams.entries()) {\n const existing = query[key];\n\n if (existing === undefined) {\n query[key] = value;\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n query[key] = [existing, value];\n }\n\n return query;\n}\n","import { HttpCode } from '@/common/consts.js';\n\n/**\n * Base class for all HTTP exceptions\n */\nexport abstract class HttpException extends Error implements NodeJS.ErrnoException {\n errno?: number | undefined;\n code?: string | undefined;\n\n constructor(message: string, statusCode: HttpCode, code: string) {\n super(message);\n this.name = 'HttpException';\n this.errno = statusCode;\n this.code = code;\n }\n}\n\n// 4xx Client Error Exceptions\n\n/**\n * 400 Bad Request - Malformed or invalid request\n */\nexport class BadRequestException extends HttpException {\n constructor(message: string = 'Bad Request') {\n super(message, HttpCode.BadRequest, 'BAD_REQUEST');\n }\n}\n\n/**\n * 401 Unauthorized - Authentication required or failed\n */\nexport class UnauthorizedException extends HttpException {\n constructor(message: string = 'Unauthorized') {\n super(message, HttpCode.Unauthorized, 'UNAUTHORIZED');\n }\n}\n\n/**\n * 403 Forbidden - Valid request but refused authorization\n */\nexport class ForbiddenException extends HttpException {\n constructor(message: string = 'Forbidden') {\n super(message, HttpCode.Forbidden, 'FORBIDDEN');\n }\n}\n\n/**\n * 404 Not Found - Resource does not exist\n */\nexport class NotFoundException extends HttpException {\n constructor(message: string = 'Not Found') {\n super(message, HttpCode.NotFound, 'NOT_FOUND');\n }\n}\n\n/**\n * 405 Method Not Allowed - HTTP method not supported for resource\n */\nexport class MethodNotAllowedException extends HttpException {\n constructor(message: string = 'Method Not Allowed') {\n super(message, HttpCode.MethodNotAllowed, 'METHOD_NOT_ALLOWED');\n }\n}\n\n/**\n * 406 Not Acceptable - Cannot generate acceptable response\n */\nexport class NotAcceptableException extends HttpException {\n constructor(message: string = 'Not Acceptable') {\n super(message, HttpCode.NotAcceptable, 'NOT_ACCEPTABLE');\n }\n}\n\n/**\n * 408 Request Timeout - Client did not produce request within time\n */\nexport class RequestTimeoutException extends HttpException {\n constructor(message: string = 'Request Timeout') {\n super(message, HttpCode.RequestTimeout, 'REQUEST_TIMEOUT');\n }\n}\n\n/**\n * 409 Conflict - Request conflicts with current state\n */\nexport class ConflictException extends HttpException {\n constructor(message: string = 'Conflict') {\n super(message, HttpCode.Conflict, 'CONFLICT');\n }\n}\n\n/**\n * 410 Gone - Resource no longer available\n */\nexport class GoneException extends HttpException {\n constructor(message: string = 'Gone') {\n super(message, HttpCode.Gone, 'GONE');\n }\n}\n\n/**\n * 413 Payload Too Large - Request entity larger than limits\n */\nexport class PayloadTooLargeException extends HttpException {\n constructor(message: string = 'Payload Too Large') {\n super(message, HttpCode.PayloadTooLarge, 'PAYLOAD_TOO_LARGE');\n }\n}\n\n/**\n * 415 Unsupported Media Type - Requested format not supported\n */\nexport class UnsupportedMediaTypeException extends HttpException {\n constructor(message: string = 'Unsupported Media Type') {\n super(message, HttpCode.UnsupportedMediaType, 'UNSUPPORTED_MEDIA_TYPE');\n }\n}\n\n/**\n * 422 Unprocessable Entity - Syntactically correct but semantically erroneous\n */\nexport class UnprocessableEntityException extends HttpException {\n constructor(message: string = 'Unprocessable Entity') {\n super(message, HttpCode.UnprocessableEntity, 'UNPROCESSABLE_ENTITY');\n }\n}\n\n/**\n * 429 Too Many Requests - Rate limit exceeded\n */\nexport class TooManyRequestsException extends HttpException {\n constructor(message: string = 'Too Many Requests') {\n super(message, HttpCode.TooManyRequests, 'TOO_MANY_REQUESTS');\n }\n}\n\n// 5xx Server Error Exceptions\n\n/**\n * 500 Internal Server Error - Unexpected server condition\n */\nexport class InternalServerErrorException extends HttpException {\n constructor(message: string = 'Internal Server Error') {\n super(message, HttpCode.InternalServerError, 'INTERNAL_SERVER_ERROR');\n }\n}\n\n/**\n * 501 Not Implemented - Server does not support functionality\n */\nexport class NotImplementedException extends HttpException {\n constructor(message: string = 'Not Implemented') {\n super(message, HttpCode.NotImplemented, 'NOT_IMPLEMENTED');\n }\n}\n\n/**\n * 502 Bad Gateway - Invalid response from upstream server\n */\nexport class BadGatewayException extends HttpException {\n constructor(message: string = 'Bad Gateway') {\n super(message, HttpCode.BadGateway, 'BAD_GATEWAY');\n }\n}\n\n/**\n * 503 Service Unavailable - Server temporarily unavailable\n */\nexport class ServiceUnavailableException extends HttpException {\n constructor(message: string = 'Service Unavailable') {\n super(message, HttpCode.ServiceUnavailable, 'SERVICE_UNAVAILABLE');\n }\n}\n\n/**\n * 504 Gateway Timeout - Upstream server timeout\n */\nexport class GatewayTimeoutException extends HttpException {\n constructor(message: string = 'Gateway Timeout') {\n super(message, HttpCode.GatewayTimeout, 'GATEWAY_TIMEOUT');\n }\n}\n","import type http from 'node:http';\n\nimport { isTextualContentType } from './headers.js';\nimport { parseQuery } from './query.js';\nimport { PayloadTooLargeException } from './exceptions.js';\n\nexport interface BodyPreview {\n exists: boolean;\n value?: string;\n bytes: number;\n truncated: boolean;\n}\n\nfunction createPayloadTooLargeException(receivedBytes: number, maxBytes: number): PayloadTooLargeException {\n return new PayloadTooLargeException(\n `request body too large: ${receivedBytes.toString()} bytes exceeds ${maxBytes.toString()} bytes`,\n );\n}\n\nfunction getHeaderValue(headerValue: string | string[] | undefined): string | undefined {\n return Array.isArray(headerValue) ? headerValue[0] : headerValue;\n}\n\nfunction createEmptyPreview(): BodyPreview {\n return {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n}\n\nfunction createPreview(\n previewBuffer: Buffer,\n totalBytes: number,\n contentType: string | undefined,\n truncated: boolean,\n): BodyPreview {\n if (totalBytes === 0) {\n return createEmptyPreview();\n }\n\n if (isTextualContentType(contentType)) {\n return {\n exists: true,\n value: previewBuffer.toString('utf8'),\n bytes: totalBytes,\n truncated,\n };\n }\n\n return {\n exists: true,\n value: `<binary body: ${totalBytes} bytes>`,\n bytes: totalBytes,\n truncated,\n };\n}\n\nasync function readRequestBodyWithPreview(\n req: http.IncomingMessage,\n method: string,\n maxBytes: number,\n previewMaxBytes = 8192,\n): Promise<{ rawBody: Buffer | undefined; preview: BodyPreview }> {\n if (method === 'GET' || method === 'HEAD') {\n return {\n rawBody: undefined,\n preview: createEmptyPreview(),\n };\n }\n\n if (req.readableEnded) {\n return {\n rawBody: undefined,\n preview: createEmptyPreview(),\n };\n }\n\n const contentLengthRaw = getHeaderValue(req.headers['content-length']);\n const declaredBytes = contentLengthRaw !== undefined ? Number.parseInt(contentLengthRaw, 10) : NaN;\n\n return new Promise((resolve, reject) => {\n const rawBodyChunks: Buffer[] = [];\n const previewChunks: Buffer[] = [];\n let totalBytes = 0;\n let previewBytes = 0;\n let previewTruncated = false;\n let settled = false;\n\n const cleanup = (): void => {\n req.off('data', onData);\n req.off('end', onEnd);\n req.off('error', onError);\n req.off('aborted', onAborted);\n };\n\n const settle = (action: () => void): void => {\n if (settled) {\n return;\n }\n\n settled = true;\n action();\n };\n\n // Check content-length upfront, but still drain the request\n if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {\n cleanup();\n req.resume();\n settle(() => reject(createPayloadTooLargeException(declaredBytes, maxBytes)));\n return;\n }\n\n const onData = (chunk: Buffer | string | Uint8Array): void => {\n const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n totalBytes += bufferChunk.byteLength;\n\n if (totalBytes > maxBytes) {\n cleanup();\n req.resume();\n settle(() => reject(createPayloadTooLargeException(totalBytes, maxBytes)));\n return;\n }\n\n rawBodyChunks.push(bufferChunk);\n\n if (previewBytes < previewMaxBytes) {\n const remaining = previewMaxBytes - previewBytes;\n const nextSlice = bufferChunk.subarray(0, remaining);\n previewChunks.push(nextSlice);\n previewBytes += nextSlice.length;\n\n if (nextSlice.length < bufferChunk.length) {\n previewTruncated = true;\n }\n } else {\n previewTruncated = true;\n }\n };\n\n const onEnd = (): void => {\n cleanup();\n settle(() => {\n const rawBody = rawBodyChunks.length > 0 ? Buffer.concat(rawBodyChunks) : undefined;\n const previewBuffer = previewChunks.length > 0 ? Buffer.concat(previewChunks) : Buffer.alloc(0);\n\n resolve({\n rawBody,\n preview: createPreview(\n previewBuffer,\n rawBody?.byteLength ?? 0,\n getHeaderValue(req.headers['content-type']),\n previewTruncated,\n ),\n });\n });\n };\n\n const onError = (error: Error): void => {\n cleanup();\n settle(() => reject(error));\n };\n\n const onAborted = (): void => {\n cleanup();\n settle(() => reject(new Error('request aborted while reading body')));\n };\n\n req.on('data', onData);\n req.once('end', onEnd);\n req.once('error', onError);\n req.once('aborted', onAborted);\n });\n}\n\nexport async function parseBody(\n req: http.IncomingMessage,\n method: string,\n maxBytes: number,\n): Promise<{ body: Record<string, any>; preview: BodyPreview }> {\n const { rawBody, preview } = await readRequestBodyWithPreview(req, method, maxBytes);\n\n if (rawBody === undefined || rawBody.byteLength === 0) {\n return {\n body: {},\n preview,\n };\n }\n\n const contentType = getHeaderValue(req.headers['content-type'])?.toLowerCase() ?? '';\n\n if (contentType.includes('json')) {\n const textBody = rawBody.toString('utf8').trim();\n\n if (textBody.length === 0) {\n return {\n body: {},\n preview,\n };\n }\n\n try {\n const parsed = JSON.parse(textBody) as unknown;\n\n if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return {\n body: parsed as Record<string, any>,\n preview,\n };\n }\n\n return {\n body: { value: parsed },\n preview,\n };\n } catch {\n return {\n body: { raw: textBody },\n preview,\n };\n }\n }\n\n if (contentType.includes('x-www-form-urlencoded')) {\n return {\n body: parseQuery(new URLSearchParams(rawBody.toString('utf8'))),\n preview,\n };\n }\n\n if (isTextualContentType(contentType)) {\n return {\n body: { raw: rawBody.toString('utf8') },\n preview,\n };\n }\n\n return {\n body: {},\n preview,\n };\n}\n","/**\n * Parse Cookie header string into an object\n */\nexport function parseCookie(cookieHeader: string | undefined): Record<string, string> {\n if (!cookieHeader) {\n return {};\n }\n\n const cookies: Record<string, string> = {};\n const pairs = cookieHeader.split(';');\n let count = 0;\n const MAX_COOKIE_KEYS = 100;\n\n for (const pair of pairs) {\n if (count >= MAX_COOKIE_KEYS) {\n break;\n }\n const [key, ...valueParts] = pair.split('=');\n if (!key) continue;\n\n const trimmedKey = key.trim();\n const value = valueParts.join('=').trim();\n cookies[trimmedKey] = decodeURIComponent(value);\n count++;\n }\n\n return cookies;\n}\n\n/**\n * Serialize an object into a Cookie header string\n */\nexport function serializeCookie(\n name: string,\n value: string,\n options?: {\n maxAge?: number;\n expires?: Date;\n domain?: string;\n path?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n },\n): string {\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;\n\n if (options) {\n if (options.maxAge !== undefined) {\n cookie += `; Max-Age=${options.maxAge}`;\n }\n if (options.expires) {\n cookie += `; Expires=${options.expires.toUTCString()}`;\n }\n if (options.domain) {\n cookie += `; Domain=${options.domain}`;\n }\n if (options.path) {\n cookie += `; Path=${options.path}`;\n }\n if (options.secure) {\n cookie += '; Secure';\n }\n if (options.httpOnly) {\n cookie += '; HttpOnly';\n }\n if (options.sameSite) {\n cookie += `; SameSite=${options.sameSite}`;\n }\n }\n\n return cookie;\n}\n","import http from 'node:http';\nimport https from 'node:https';\n\nimport type { FluxionContext, FluxionModuleContext, FluxionRequest, NormalizedModule } from '../types.js';\nimport { HttpCode, flags, ModuleType } from '@/common/consts.js';\nimport { PromiseTry } from '@/common/promise-try.js';\nimport { getErrorMessage } from '@/common/logger.js';\n\nimport { getRealIp } from './headers.js';\nimport { toURL } from './request.js';\nimport { safeSendJson } from './respond.js';\nimport { parseBody, type BodyPreview } from './body.js';\nimport { parseQuery } from './query.js';\nimport { parseCookie } from './cookie.js';\nimport { HttpException } from './exceptions.js';\n\nconst waiter = (mainPromise: Promise<any>, timeoutMs: number, flag: symbol) =>\n Promise.race([mainPromise, new Promise((r) => setTimeout(() => r(flag), timeoutMs))]);\n\n/**\n * Run a module's disposer once the response has been sent.\n * Modules without a disposer are left untouched.\n */\nfunction disposeAfterResponse(cx: FluxionContext, res: http.ServerResponse, m: NormalizedModule) {\n // The module instance is shared by requests while its files are unchanged: the disposer runs once per response.\n if (typeof m.disposer !== 'function') {\n return;\n }\n const disposer = m.disposer;\n res.once('close', () => {\n PromiseTry(disposer).catch((e: unknown) => {\n cx.logger.error({ message: 'DisposerFailed', url: m.absolutePath, error: getErrorMessage(e) });\n });\n });\n}\n\nexport function createServer(cx: FluxionContext): Promise<http.Server | https.Server> {\n const moduleCx: FluxionModuleContext = Object.freeze({ logger: cx.logger });\n\n const requestHandler = async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const method = req.method ?? 'GET';\n const ip = getRealIp(req);\n const url = toURL(req.url);\n if (url === undefined) {\n safeSendJson(res, { message: 'Bad Request: req.url is undefined' }, HttpCode.BadRequest);\n return;\n }\n\n const normalized: FluxionRequest = {\n method,\n ip,\n url,\n query: parseQuery(url.searchParams),\n body: {},\n headers: req.headers,\n cookie: parseCookie(req.headers.cookie as string | undefined),\n meta: {},\n };\n\n // Security headers for all responses\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('X-XSS-Protection', '1; mode=block');\n // Content-Security-Policy (user-configurable, header omitted when set to false)\n if (cx.options.csp !== false) {\n res.setHeader('Content-Security-Policy', cx.options.csp);\n }\n\n let bodyPreview: BodyPreview = {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n\n cx.logger.core({ message: 'request', method, ip, path: url.pathname });\n\n const start = performance.now();\n res.once('finish', () => {\n const o: Record<string, unknown> = {\n message: 'response',\n method,\n ip,\n path: url.pathname,\n status: res.statusCode,\n duration: (performance.now() - start).toFixed(4),\n };\n\n if (Object.keys(normalized.query).length > 0) {\n o.query = normalized.query;\n }\n\n if (bodyPreview.exists) {\n o.body = bodyPreview.value;\n o.bodyBytes = bodyPreview.bytes;\n o.bodyTruncated = bodyPreview.truncated;\n }\n\n cx.logger.core(o);\n });\n\n // * Start request handling\n try {\n const parsed = await parseBody(req, normalized.method, cx.options.maxRequestBytes);\n normalized.body = parsed.body;\n bodyPreview = parsed.preview;\n\n const m = await cx.router(url);\n if (!m) {\n safeSendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n return;\n }\n\n if (req.method && m.methods && !m.methods.includes(req.method)) {\n safeSendJson(res, { message: 'Method Not Allowed' }, HttpCode.MethodNotAllowed);\n return;\n }\n\n // Clean up this request's module instance after the response is sent.\n disposeAfterResponse(cx, res, m);\n\n const timeoutMs =\n m.type === ModuleType.Api\n ? (m.handlerTimeoutMs ?? cx.options.handlerTimeoutMs)\n : cx.options.staticResourceTimeoutMs;\n\n // Middleware execution\n if (m.middlewares) {\n for (let i = 0; i < m.middlewares.length; i++) {\n const result = await waiter(\n PromiseTry(m.middlewares[i], normalized, moduleCx, req, res),\n cx.options.middlewareTimeoutMs,\n flags.MiddlewareTimeout,\n );\n\n if (result === flags.MiddlewareTimeout) {\n cx.logger.warn({\n message: 'MiddlewareTimeout',\n method: normalized.method,\n ip: normalized.ip,\n });\n safeSendJson(res, { message: 'Internal Server Error' }, HttpCode.InternalServerError);\n return;\n }\n if (res.writableEnded) {\n return;\n }\n if (res.headersSent) {\n res.end();\n return;\n }\n }\n }\n\n const result = await waiter(\n PromiseTry(m.handler, normalized, moduleCx, req, res),\n timeoutMs,\n flags.HandlerTimeout,\n );\n\n if (result === flags.HandlerTimeout) {\n cx.logger.warn({ message: 'HandlerTimeout', method: normalized.method, ip: normalized.ip });\n safeSendJson(res, { message: 'Handler timed out' }, HttpCode.InternalServerError);\n return;\n }\n\n if (result !== flags.StaticHandled) {\n safeSendJson(res, result);\n }\n } catch (e) {\n if (e instanceof HttpException) {\n cx.logger.error({\n ...normalized,\n message: 'RequestFailed',\n error: e.message,\n });\n safeSendJson(res, { message: e.message }, e.errno);\n } else {\n cx.logger.error({\n ...normalized,\n message: 'RequestFailed',\n error: getErrorMessage(e),\n });\n safeSendJson(res, { message: 'Internal Server Error' }, HttpCode.InternalServerError);\n }\n }\n };\n\n const server = cx.options.https\n ? https.createServer(\n {\n key: cx.options.https.key,\n cert: cx.options.https.cert,\n ca: cx.options.https.ca,\n },\n requestHandler,\n )\n : http.createServer(requestHandler);\n\n return new Promise((resolve, reject) => {\n let listening = false;\n\n server.on('close', () => {\n cx.logger.core({\n message: 'ServerClosed',\n host: cx.options.host,\n port: cx.options.port,\n });\n });\n\n server.once('listening', () => {\n listening = true;\n cx.logger.core({\n message: 'FluxionStarted',\n version: '__VERSION__',\n pid: process.pid,\n protocol: cx.options.https ? 'https' : 'http',\n host: cx.options.host,\n port: cx.options.port,\n });\n cx.logger.core({\n message: 'DynamicDirectory',\n directory: cx.options.dir,\n });\n resolve(server);\n });\n\n server.on('error', (e) => {\n cx.logger.error({\n message: 'ServerError',\n error: getErrorMessage(e),\n });\n if (listening) {\n // Server encountered an error after binding — log and let the\n // caller (PM2 / user code) decide how to recover instead of\n // forcing process.exit(1) here.\n return;\n }\n reject(e);\n });\n\n server.listen(cx.options.port, cx.options.host);\n });\n}\n","function n(n) {}\n\nconst t = n;\n\nfunction o() {\n return n => {};\n}\n\nexport { o as createNarrower, n as narrow, t as static_cast };\n","import { buildSync } from 'esbuild';\nimport { compileFunction } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { readFileSync, statSync } from 'node:fs';\nimport { dirname, extname, isAbsolute, join, resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { NormalizedFluxionOptions } from '@/types.js';\n\n/**\n * A module loaded by `FluxionLoader`. `deps` holds the instances it required, so a stale dependency shows up\n * even when the cache has already been refilled.\n */\nexport interface LoadedModule {\n id: string;\n filename: string;\n exports: any;\n mtimeMs: number;\n size: number;\n deps: Map<string, LoadedModule>;\n}\n\n/**\n * Every loaded module, keyed by absolute path, shared by all fluxion instances. Delete an entry to force a reload.\n */\nexport const moduleCache = new Map<string, LoadedModule>();\n\nconst NODE_MODULES = `${sep}node_modules${sep}`;\n\nconst COMPILED = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\nconst EXTENSIONS = [...COMPILED, '.json'];\n// By TypeScript convention `import './a.js'` points at `a.ts`\nconst JS_TO_TS: Record<string, string[]> = {\n '.js': ['.ts', '.tsx'],\n '.jsx': ['.tsx'],\n '.mjs': ['.mts'],\n '.cjs': ['.cts'],\n};\n\nconst isBare = (specifier: string) => !specifier.startsWith('.') && !isAbsolute(specifier);\n\nconst isChanged = (m: LoadedModule) => {\n try {\n const s = statSync(m.filename);\n return s.mtimeMs !== m.mtimeMs || s.size !== m.size;\n } catch {\n // gone counts as changed\n return true;\n }\n};\n\nconst isFile = (path: string) => {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n};\n\n/**\n * Find the real file behind an extension-less or `.js`-style path, the way esbuild would.\n */\nconst findFile = (base: string) => {\n if (isFile(base)) {\n return base;\n }\n\n const ext = extname(base);\n const stem = base.slice(0, base.length - ext.length);\n for (const e of JS_TO_TS[ext] ?? []) {\n if (isFile(stem + e)) {\n return stem + e;\n }\n }\n for (const e of EXTENSIONS) {\n if (isFile(base + e)) {\n return base + e;\n }\n }\n for (const e of EXTENSIONS) {\n if (isFile(join(base, 'index' + e))) {\n return join(base, 'index' + e);\n }\n }\n};\n\n/**\n * Load a ts/js file under `options.dir`. Local files it imports are loaded the same way, each compiled by esbuild on\n * its own; node_modules, builtins and files outside `dir` go through the native `require`.\n *\n * A file is loaded fresh the first time and its mtime and size are recorded together with those of every file it\n * required. Asking again compares them: if any one differs, the whole chain is dropped and loaded fresh, otherwise\n * the cached exports are returned. Content is never compared.\n *\n * Synchronous, because `require` is. `require()` returns `exports`, so `export default x` is `result.default`.\n */\nexport class FluxionLoader {\n private readonly opts: Pick<NormalizedFluxionOptions, 'dir' | 'target'>;\n\n constructor(options: Pick<NormalizedFluxionOptions, 'dir' | 'target'>) {\n this.opts = options;\n }\n\n private compile(file: string) {\n const { outputFiles } = buildSync({\n entryPoints: [file],\n bundle: false,\n write: false,\n platform: 'node',\n format: 'cjs',\n target: this.opts.target,\n sourcemap: 'inline',\n sourcesContent: false,\n logLevel: 'silent',\n // `import(x)` becomes `require(x)`, because a vm function has no way to run a native dynamic import\n supported: { 'dynamic-import': false },\n define: {\n 'import.meta.url': JSON.stringify(pathToFileURL(file).href),\n 'import.meta.filename': JSON.stringify(file),\n 'import.meta.dirname': JSON.stringify(dirname(file)),\n },\n });\n return outputFiles[0].text;\n }\n\n require(absolutePath: string): any {\n return this.load(resolve(absolutePath)).exports;\n }\n\n private isHot(file: string) {\n const ext = extname(file);\n return file.startsWith(this.opts.dir) && !file.includes(NODE_MODULES) && (COMPILED.has(ext) || ext === '.json');\n }\n\n /**\n * Return the cached module if nothing in its chain changed, otherwise drop the chain and load it fresh.\n */\n private load(file: string): LoadedModule {\n const cached = moduleCache.get(file);\n if (cached === undefined) {\n return this.loadFresh(file);\n }\n\n const chain = this.collectChain(cached);\n if (!this.anyStale(chain)) {\n return cached;\n }\n for (const m of chain) {\n // A newer instance of the same path may have been loaded by someone else meanwhile: that one is fine, keep it\n if (moduleCache.get(m.filename) === m) {\n moduleCache.delete(m.filename);\n }\n }\n return this.loadFresh(file);\n }\n\n private collectChain(root: LoadedModule) {\n const chain = new Set([root]);\n // a Set visits what is added while iterating, and the `has` check built into it stops cycles\n for (const m of chain) {\n for (const dep of m.deps.values()) {\n chain.add(dep);\n }\n }\n return chain;\n }\n\n private anyStale(chain: Set<LoadedModule>) {\n for (const m of chain) {\n if (moduleCache.get(m.filename) !== m || isChanged(m)) {\n return true;\n }\n }\n return false;\n }\n\n private loadFresh(file: string): LoadedModule {\n // Read before the file is, so an edit in between is seen as a change next time instead of being missed\n const { mtimeMs, size } = statSync(file);\n const mod: LoadedModule = { id: file, filename: file, exports: {}, mtimeMs, size, deps: new Map() };\n // Registered before it runs, so a circular import sees the partial exports instead of looping\n moduleCache.set(file, mod);\n try {\n this.evaluate(file, mod);\n } catch (e) {\n moduleCache.delete(file);\n throw e;\n }\n return mod;\n }\n\n private evaluate(file: string, mod: LoadedModule) {\n const ext = extname(file);\n if (ext === '.json') {\n mod.exports = JSON.parse(readFileSync(file, 'utf8'));\n return;\n }\n if (!COMPILED.has(ext)) {\n throw new Error(`FluxionLoader: unsupported file type \"${ext}\" (${file})`);\n }\n\n const fn = compileFunction(this.compile(file), ['exports', 'require', 'module', '__filename', '__dirname'], {\n filename: file,\n });\n fn.call(mod.exports, mod.exports, this.createModuleRequire(mod), mod, file, dirname(file));\n }\n\n /**\n * The only closure left per module, and it is made once per fresh load: it has to remember which module is asking.\n */\n private createModuleRequire(from: LoadedModule) {\n const native = createRequire(from.filename);\n const locate = (specifier: string) =>\n isBare(specifier) ? undefined : findFile(resolve(dirname(from.filename), specifier));\n\n const req = (specifier: string) => {\n const file = locate(specifier);\n if (file === undefined) {\n // builtins, packages and anything that cannot be found; the native require reports the latter\n return native(specifier);\n }\n if (!this.isHot(file)) {\n return native(file);\n }\n const dep = this.load(file);\n // Also recorded for requires made long after loading (`import()` inside a function)\n from.deps.set(file, dep);\n return dep.exports;\n };\n req.resolve = (specifier: string) => locate(specifier) ?? native.resolve(specifier);\n req.cache = native.cache;\n return req;\n }\n}\n","import { FluxionError } from './error.js';\nimport type { FluxionContext, NormalizedModule } from '@/types.js';\nimport type { Stats } from 'node:fs';\nimport { static_cast } from 'type-narrow';\nimport { ModuleType } from './consts.js';\nimport { FluxionLoader } from './loader.js';\n\nfunction isFluxionModule(cx: Pick<FluxionContext, 'options' | 'logger'>, o: unknown): o is NormalizedModule {\n if (typeof o !== 'object' || o === null) {\n return false;\n }\n\n static_cast<NormalizedModule>(o);\n\n if (typeof o.handler !== 'function') {\n cx.logger.error(`handler must be a function`);\n return false;\n }\n\n if (o.disposer !== undefined && typeof o.disposer !== 'function') {\n cx.logger.error(`disposer must be a function if provided`);\n return false;\n }\n\n const ms = o.handlerTimeoutMs;\n if (ms !== undefined && (!Number.isSafeInteger(ms) || ms < 100)) {\n cx.logger.error(`handlerTimeoutMs must be an integer >= 100 if provided`);\n return false;\n }\n\n if (o.type !== ModuleType.Api) {\n cx.logger.error(`You must use defineFluxionModule to create module`);\n return false;\n }\n\n return true;\n}\n\nexport function loadFluxionModule(\n cx: Pick<FluxionContext, 'options' | 'logger'>,\n absolutePath: string,\n stat: Stats,\n): NormalizedModule {\n const loader = new FluxionLoader(cx.options);\n\n let m = loader.require(absolutePath);\n if (isFluxionModule(cx, m.default)) {\n m = m.default;\n } else if (isFluxionModule(cx, m)) {\n } else {\n throw new FluxionError(\n `Invalid handler module '${absolutePath}', make sure it satisfies defineFluxionModule(...) helper`,\n );\n }\n\n m.absolutePath = absolutePath;\n m.mtimeMs = stat.mtimeMs;\n\n return m;\n}\n","import type { FluxionContext, NormalizedModule } from '../types.js';\nimport { createReadStream } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { minimatch } from 'minimatch';\nimport { ModuleType, STATIC_CONTENT_TYPES, flags } from '@/common/consts.js';\nimport { loadFluxionModule } from '@/common/injector.js';\n\n/**\n * Resolve a request URL to the module that should handle it.\n * Nothing is registered: the file is looked up on every call. API modules are reloaded only when the file or one\n * of the files it imports has changed (mtime or size).\n */\nexport type FluxionRouter = (url: URL) => Promise<NormalizedModule | undefined>;\n\nexport function createRouter(cx: Pick<FluxionContext, 'options' | 'logger'>): FluxionRouter {\n return async (url) => {\n const relativePath = url.pathname.replace(/^[/]+/, '').replace(/[/]+$/, '');\n const absolutePath = path.join(cx.options.dir, relativePath);\n\n // ! Fail if the resolved path escapes the configured directory\n const withinDir = path.relative(cx.options.dir, absolutePath);\n if (withinDir.startsWith('..') || path.isAbsolute(withinDir)) {\n return undefined;\n }\n\n // Excluded files are never served\n if (cx.options.exclude.some((p) => minimatch(relativePath, p))) {\n return undefined;\n }\n\n // The file must exist and be a regular file\n const stat = await fs.stat(absolutePath).catch(() => undefined);\n if (!stat || !stat.isFile()) {\n return undefined;\n }\n\n if (cx.options.apiInclude.some((p) => minimatch(relativePath, p))) {\n return loadFluxionModule(cx, absolutePath, stat);\n }\n\n if (cx.options.staticInclude.some((p) => minimatch(relativePath, p))) {\n return makeStaticResource(absolutePath);\n }\n\n return undefined;\n };\n}\n\nfunction makeStaticResource(absolutePath: string): NormalizedModule {\n return {\n type: ModuleType.StaticResource,\n mtimeMs: NaN,\n absolutePath,\n handler: async (normalized, cx, req, res) => {\n if (normalized.method !== 'GET' && normalized.method !== 'HEAD') {\n res.statusCode = 405;\n res.setHeader('Allow', 'GET, HEAD');\n res.end();\n return;\n }\n\n const stat = await fs.stat(absolutePath).catch((e: Error) => e);\n if (stat instanceof Error) {\n res.statusCode = 404;\n res.end('Not Accessible');\n cx.logger.error({ action: 'StaticResourceError', url: normalized.url, error: stat.message });\n return null;\n }\n\n if (!stat.isFile()) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const extension = path.extname(absolutePath).toLowerCase();\n const contentType = STATIC_CONTENT_TYPES[extension] ?? 'application/octet-stream';\n\n res.statusCode = 200;\n res.setHeader('Content-Type', contentType);\n res.setHeader('Content-Length', String(stat.size));\n\n if (normalized.method === 'HEAD') {\n res.end();\n return;\n }\n\n return new Promise<symbol>((resolve, reject) => {\n const stream = createReadStream(absolutePath);\n\n const cleanup = () => {\n stream.off('error', onError);\n stream.off('end', onEnd);\n res.off('close', onClientClose);\n req.off('aborted', onClientClose);\n };\n\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n\n const onEnd = () => {\n cleanup();\n resolve(flags.StaticHandled);\n };\n\n const onClientClose = () => {\n cleanup();\n stream.destroy();\n resolve(flags.StaticHandled);\n };\n\n stream.on('error', onError);\n stream.on('end', onEnd);\n res.on('close', onClientClose);\n req.on('aborted', onClientClose);\n\n stream.pipe(res);\n });\n },\n };\n}\n","import type { FluxionContext, FluxionOptions, NormalizedFluxionOptions } from './types.js';\n\nimport { createLogger } from './common/logger.js';\nimport { flags } from './common/consts.js';\nimport { defineFluxionOptions } from './defines/options.js';\nimport { createServer } from './http/server.js';\nimport { createRouter } from './router/index.js';\n\n/**\n * Creates a fluxion-vio server instance.\n * @package Fluxion Vio\n * @version __VERSION__\n */\nexport async function fluxion(options: FluxionOptions | NormalizedFluxionOptions) {\n const alreadyNormalized = (options as NormalizedFluxionOptions).normalizedFlag === flags.OptionsNormalized;\n const context = { options: alreadyNormalized ? options : defineFluxionOptions(options) } as FluxionContext;\n\n context.logger = createLogger(context as Pick<FluxionContext, 'options'>);\n context.router = createRouter(context as Pick<FluxionContext, 'options' | 'logger'>);\n\n // Start HTTP server\n const server = await createServer(context);\n\n // Register signal handlers for graceful shutdown\n const shutdown = (signal: NodeJS.Signals) => {\n context.logger.warn({ message: 'ShuttingDown', pid: process.pid, signal });\n server.close(() => process.exit(0));\n };\n\n process.once('SIGINT', () => shutdown('SIGINT'));\n process.once('SIGTERM', () => shutdown('SIGTERM'));\n}\n","import { FluxionError } from '@/common/error.js';\nimport type {\n FluxionHandler,\n FluxionDisposer,\n FluxionModule,\n FluxionMiddleware,\n FluxionModuleObject,\n} from '@/types.js';\nimport type { FluxionLoggerFn } from '@/common/logger.js';\nimport { ModuleType } from '@/common/consts.js';\n\nexport { defineFluxionOptions } from './options.js';\n\n/**\n * Use handler function and optional disposer function to define a Fluxion module.\n * @param handler Main function that handles request and response instances\n * @param disposer Deal with resource cleanup once the response has been sent. Optional.\n */\nexport function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDisposer): FluxionModuleObject;\n/**\n * Provides type safety for defining Fluxion modules.\n */\nexport function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleObject;\nexport function defineFluxionModule(\n a: FluxionModule | FluxionHandler,\n disposer?: FluxionDisposer,\n): FluxionModuleObject {\n if (typeof a === 'function') {\n if (disposer !== undefined && typeof disposer !== 'function') {\n throw new FluxionError(`Invalid disposer, expected a function but got ${typeof disposer}`);\n }\n return { handler: a, disposer, type: ModuleType.Api };\n }\n\n if (typeof a !== 'object' || a === null) {\n throw new FluxionError(\n `Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof a}`,\n );\n }\n\n if (typeof a.handler !== 'function') {\n throw new FluxionError(`Invalid FluxionModule, \"handler\" must be a function`);\n }\n\n if (a.disposer !== undefined && typeof a.disposer !== 'function') {\n throw new FluxionError(`Invalid FluxionModule, \"disposer\" must be a function if provided`);\n }\n\n if (a.methods !== undefined && (!Array.isArray(a.methods) || a.methods.some((v) => typeof v !== 'string'))) {\n throw new FluxionError(`Invalid FluxionModule, \"methods\" must be an array of strings if provided`);\n }\n\n if (\n a.middlewares !== undefined &&\n (!Array.isArray(a.middlewares) || a.middlewares.some((v) => typeof v !== 'function'))\n ) {\n throw new FluxionError(`Invalid FluxionModule, \"middlewares\" must be an array of functions if provided`);\n }\n\n return { ...a, type: ModuleType.Api };\n}\n\nexport function defineFluxionMiddleware(middleware: FluxionMiddleware): FluxionMiddleware {\n if (typeof middleware !== 'function') {\n throw new FluxionError(`Invalid FluxionMiddleware, expected a function but got ${typeof middleware}`);\n }\n return middleware;\n}\n\nexport function defineFluxionLogger(loggerFn: FluxionLoggerFn) {\n if (typeof loggerFn !== 'function') {\n throw new FluxionError(`Invalid FluxionLoggerFn, expected a function but got ${typeof loggerFn}`);\n }\n return loggerFn;\n}\n","import { fluxion } from './fluxion.js';\nimport { HttpCode } from './common/consts.js';\n\nexport { fluxion, HttpCode };\n\n/**\n * Export fluxion version for users' debugging.\n * @version __VERSION__\n */\nexport const VERSION = '__VERSION__';\n\n// Export all HTTP exceptions\nexport {\n HttpException,\n BadRequestException,\n UnauthorizedException,\n ForbiddenException,\n NotFoundException,\n MethodNotAllowedException,\n NotAcceptableException,\n RequestTimeoutException,\n ConflictException,\n GoneException,\n PayloadTooLargeException,\n UnsupportedMediaTypeException,\n UnprocessableEntityException,\n TooManyRequestsException,\n InternalServerErrorException,\n NotImplementedException,\n BadGatewayException,\n ServiceUnavailableException,\n GatewayTimeoutException,\n} from './http/exceptions.js';\n\nexport * from './defines/index.js';\n\nexport type {\n FluxionRequest,\n FluxionModuleContext,\n FluxionDisposer,\n FluxionHandler,\n FluxionModule,\n FluxionOptions,\n} from './types.js';\n"],"x_google_ignoreList":[13],"mappings":"o4BAoDA,MAAM,EAAiB,GAA2B,CAChD,GAAI,CACF,OAAA,EAAA,EAAA,QAAA,CAAiB,CAAK,CACxB,MAAQ,CACN,MAAO,kBACT,CACF,EAEM,EAA0C,CAC9C,KAAM,OACN,KAAM,OACN,KAAM,OACN,MAAO,QACP,KAAM,OACN,MAAO,QACP,QAAS,SACX,EAEa,EAAkC,GAAoB,CACjE,GAAM,CAAE,MAAO,EAAU,UAAW,EAAc,QAAS,EAAY,MAAK,GAAG,GAAW,EAEpF,EAAY,IAAI,EAAa,GAC7B,EAAQ,EAAc,IAAa,EACnC,EAAU,IAAQ,IAAA,GAAY,GAAK,KAAK,EAAI,GAC5C,EAAa,OAAO,KAAK,CAAM,CAAC,CAAC,OAAS,EAAI,IAAI,EAAc,CAAM,IAAM,GAG5E,EAAU,EAAa,EAAa,EAAa,EAAW,KAAK,EAGvE,QAAQ,IAAI,GAAG,EAAU,GAAG,IAAQ,EAAQ,GAAG,GAAS,CAC1D,EAKA,SAAS,GAAe,EAAgD,CAEtE,GAAI,EAAA,EAAA,EAAA,WAAA,CAAY,CAAW,EAAG,CAC5B,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAW,GAC3B,EAAA,EAAA,WAAA,CAAY,CAAG,IACjB,EAAA,EAAA,UAAA,CAAU,EAAK,CAAE,UAAW,EAAK,CAAC,CAEtC,CAEA,IAAM,GAAA,EAAA,EAAA,kBAAA,CAA+B,EAAa,CAAE,MAAO,GAAI,CAAC,EAEhE,MAAQ,IAAoB,CAC1B,GAAI,CACF,IAAM,EAAY,EAAM,WAAa,IAAI,KAAK,CAAA,CAAE,YAAY,EACtD,EAAQ,EAAM,OAAS,OACvB,EAAM,EAAM,MAAQ,IAAA,GAAgC,GAApB,KAAK,EAAM,IAAI,GAC/C,EAAU,EAAM,QAAU,EAAM,QAAU,EAAc,CAAK,EAEnE,EAAW,MAAM,IAAI,EAAU,IAAI,IAAQ,EAAI,GAAG,EAAQ,GAAG,CAC/D,MAAQ,CAER,CACF,CACF,CAKA,SAAS,EAAkB,EAAsD,CAC/E,IAAM,EAAkB,QAAQ,IAAI,qBAC9B,EAAW,EAAkB,GAAe,CAAe,EAAI,KAE/D,EAAe,EAAG,QAAQ,OAChC,GAAI,IAAiB,IAAA,IAAa,IAAiB,WAOjD,OANI,EACM,GAAoB,CAC1B,EAAc,CAAK,EACnB,EAAS,CAAK,CAChB,EAEK,EAGT,GAAI,IAAiB,YAAa,CAEhC,IAAM,EAAY,GAAoB,QAAQ,IAAI,EAAc,CAAK,CAAC,EAOtE,OANI,EACM,GAAoB,CAC1B,EAAS,CAAK,EACd,EAAS,CAAK,CAChB,EAEK,CACT,CAUA,OARI,EAEM,GAAoB,CAC1B,EAAa,CAAK,EAClB,EAAS,CAAK,CAChB,EAGK,CACT,CAEA,SAAgB,GAAa,EAA4D,CACvF,IAAM,EAAO,EAAkB,CAAE,EA8CjC,MAAO,CA3CL,MAAM,EAAiB,EAA0B,CAC/C,IAAM,EACJ,OAAO,GAAM,SACT,CACE,QAAS,EACT,UAAW,IAAI,KAAK,CAAA,CAAE,YAAY,EAClC,OACF,EACA,CACE,GAAG,EACH,UAAW,IAAI,KAAK,CAAA,CAAE,YAAY,EAClC,OACF,EAEN,GAAI,CACF,EAAK,CAAK,CACZ,MAAQ,CAER,CACF,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,MAAM,EAA+C,CACnD,KAAK,MAAM,QAAS,CAAe,CACrC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,MAAM,EAA+C,CACnD,KAAK,MAAM,QAAS,CAAe,CACrC,EACA,QAAQ,EAA+C,CACrD,KAAK,MAAM,UAAW,CAAe,CACvC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,CAGU,CACd,CAwCA,MAAa,EACX,OAAO,MAAM,SAAY,WACpB,GAAwB,MAAM,QAAQ,CAAC,EAAI,EAAE,QAAU,OAAO,CAAC,EAC/D,GAAwB,GAAW,SAAW,OAAO,CAAC,ECnPtD,IAAA,mCAC4B,OAAO,4BAA4B,kBACvC,OAAO,8BAA8B,mBACpC,OAAO,wBAAwB,sBAC5B,OAAO,2BAA2B,IACpE,AAAA,IAAA,CAAA,CAAD,EAEA,MAAa,GAA+C,CAC1D,OAAQ,0BACR,QAAS,2BACT,OAAQ,2BACR,MAAO,iCACP,OAAQ,iCACR,OAAQ,iCACR,QAAS,kCACT,UAAW,qCACX,OAAQ,kCACR,OAAQ,4BACR,MAAO,+BACP,OAAQ,iCACR,OAAQ,0BACR,QAAS,kCACT,OAAQ,kCACR,QAAS,kCAET,OAAQ,eACR,OAAQ,YACR,OAAQ,aACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,QAAS,aACT,OAAQ,gBACR,OAAQ,YACR,OAAQ,aACR,QAAS,aAET,QAAS,YACT,SAAU,aACV,OAAQ,WACR,OAAQ,WACR,OAAQ,gCAER,OAAQ,aACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,OAAQ,kBACR,OAAQ,YAER,OAAQ,kBACR,QAAS,mBACT,OAAQ,kBACR,MAAO,mBACP,OAAQ,oBACR,OAAQ,sBACR,MAAO,8BACP,eAAgB,2CAChB,OAAQ,0BACV,EAEA,IAAY,GAAL,SAAA,EAAA,OAEL,GAAA,EAAA,GAAA,KAAA,KACA,EAAA,EAAA,QAAA,KAAA,UACA,EAAA,EAAA,SAAA,KAAA,WACA,EAAA,EAAA,UAAA,KAAA,YACA,EAAA,EAAA,eAAA,KAAA,iBAGA,EAAA,EAAA,iBAAA,KAAA,mBACA,EAAA,EAAA,MAAA,KAAA,QACA,EAAA,EAAA,YAAA,KAAA,cACA,EAAA,EAAA,kBAAA,KAAA,oBACA,EAAA,EAAA,kBAAA,KAAA,oBAGA,EAAA,EAAA,WAAA,KAAA,aACA,EAAA,EAAA,aAAA,KAAA,eACA,EAAA,EAAA,UAAA,KAAA,YACA,EAAA,EAAA,SAAA,KAAA,WACA,EAAA,EAAA,iBAAA,KAAA,mBACA,EAAA,EAAA,cAAA,KAAA,gBACA,EAAA,EAAA,eAAA,KAAA,iBACA,EAAA,EAAA,SAAA,KAAA,WACA,EAAA,EAAA,KAAA,KAAA,OACA,EAAA,EAAA,gBAAA,KAAA,kBACA,EAAA,EAAA,qBAAA,KAAA,uBACA,EAAA,EAAA,oBAAA,KAAA,sBACA,EAAA,EAAA,gBAAA,KAAA,kBAGA,EAAA,EAAA,oBAAA,KAAA,sBACA,EAAA,EAAA,eAAA,KAAA,iBACA,EAAA,EAAA,WAAA,KAAA,aACA,EAAA,EAAA,mBAAA,KAAA,qBACA,EAAA,EAAA,eAAA,KAAA,kBACF,EAAA,CAAA,CAAA,ECzGA,IAAa,EAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,CAC3B,MAAM,CAAO,EACb,KAAK,KAAO,iBACd,CACF,ECMA,SAAS,EAAuB,EAAkC,CAChE,GAAI,OAAO,SAAS,CAAO,EACzB,OAAO,EAET,GAAI,OAAO,GAAY,SAAU,CAG/B,GAAI,CAAC,EAAQ,WAAW,YAAY,EAAG,CACrC,IAAM,EAAWA,EAAAA,QAAK,QAAQ,CAAO,EACrC,GAAIC,EAAAA,QAAG,WAAW,CAAQ,EACxB,OAAOA,EAAAA,QAAG,aAAa,CAAQ,CAEnC,CACA,OAAO,OAAO,KAAK,CAAO,CAC5B,CACA,MAAM,IAAI,EAAa,gDAAgD,CACzE,CAKA,SAAS,GAAsB,EAA+E,CAC5G,GAAI,CAAC,EACH,OAGF,GAAI,OAAO,GAAU,WAAY,GAAkB,MAAM,QAAQ,CAAK,EACpE,MAAM,IAAI,EAAa,wCAAwC,EAEjE,GAAI,OAAO,EAAM,KAAQ,UAAY,CAAC,OAAO,SAAS,EAAM,GAAG,EAC7D,MAAM,IAAI,EAAa,qDAAqD,EAE9E,GAAI,OAAO,EAAM,MAAS,UAAY,CAAC,OAAO,SAAS,EAAM,IAAI,EAC/D,MAAM,IAAI,EAAa,sDAAsD,EAG/E,IAAM,EAA4C,CAChD,IAAK,EAAuB,EAAM,GAAG,EACrC,KAAM,EAAuB,EAAM,IAAI,CACzC,EAUA,OARI,EAAM,KAAO,IAAA,KACX,MAAM,QAAQ,EAAM,EAAE,EACxB,EAAO,GAAK,EAAM,GAAG,IAAK,GAAS,EAAuB,CAAI,CAAC,EAE/D,EAAO,GAAK,EAAuB,EAAM,EAAE,GAIxC,CACT,CAKA,SAAgB,EAAqB,EAA6C,CAChF,GAAI,OAAO,GAAM,WAAY,GAAc,MAAM,QAAQ,CAAC,EACxD,MAAM,IAAI,EAAa,kCAAkC,EAI3D,GAAI,YAAa,EACf,MAAM,IAAI,EACR;;;;;+DAMF,EAGF,GAAM,CACJ,IAAK,EACL,OACA,KAAM,EACN,mBAAmB,IACnB,sBAAsB,IACtB,0BAA0B,IAAS,IACnC,SAAS,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC,KACjD,kBAAkB,IAClB,aAAa,CAAC,SAAS,EACvB,gBAAgB,CAAC,MAAM,EACvB,UAAU,CACR,qBACA,aACA,aACA,cACA,gBACA,cACA,WACA,eACA,iBACA,oBACA,WACA,WACF,EACA,QACA,MAAM,sBACJ,EAEE,EAAO,EAEP,EAAS,EAAE,QAAU,WAC3B,GAAI,IAAW,YAAc,IAAW,aAAe,OAAO,GAAW,WACvE,MAAM,IAAI,EAAa,oFAAoF,EAG7G,GAAI,OAAO,GAAW,SACpB,MAAM,IAAI,EAAa,qCAAqC,EAG9D,IAAM,EAAcD,EAAAA,QAAK,QAAQ,QAAQ,IAAI,EAAG,CAAM,EAChD,EAAM,EAAY,SAASA,EAAAA,QAAK,GAAG,EAAI,EAAc,EAAcA,EAAAA,QAAK,IAE9E,GAAI,OAAO,GAAW,UAAY,EAAE,MAAM,QAAQ,CAAM,GAAK,EAAO,MAAO,GAAM,OAAO,GAAM,QAAQ,GACpG,MAAM,IAAI,EAAa,+DAA+D,EAGxF,GAAI,OAAO,GAAS,SAClB,MAAM,IAAI,EAAa,sCAAsC,EAG/D,GAAI,CAAC,OAAO,cAAc,CAAgB,GAAK,GAAoB,IACjE,MAAM,IAAI,EAAa,qEAAqE,EAG9F,GAAI,CAAC,OAAO,cAAc,CAAmB,GAAK,GAAuB,IACvE,MAAM,IAAI,EAAa,wEAAwE,EAGjG,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,cAAc,CAAI,EACxD,MAAM,IAAI,EAAa,gDAAgD,EAGzE,GAAI,GAAQ,GAAK,EAAO,MACtB,MAAM,IAAI,EAAa,uCAAuC,EAGhE,GAAI,OAAO,GAAoB,UAAY,GAAmB,GAAK,CAAC,OAAO,cAAc,CAAe,EACtG,MAAM,IAAI,EAAa,2DAA2D,EAGpF,GAAI,IAAQ,IAAS,OAAO,GAAQ,SAClC,MAAM,IAAI,EAAa,8CAA8C,EAGlEC,EAAAA,QAAG,WAAW,CAAG,GACpB,EAAA,QAAG,UAAU,EAAK,CAAE,UAAW,EAAK,CAAC,EAGvC,IAAM,EAAS,CACb,MACA,OACA,OACA,mBACA,sBACA,0BACA,SACA,kBACA,SACA,aACA,gBACA,UACA,MAAO,GAAsB,CAAK,EAClC,MACA,eAAgB,EAAM,iBACxB,EAMA,OAJI,QAAQ,KAAK,SAAS,WAAW,GACnC,QAAQ,IAAI,qBAAsB,CAAE,GAAG,EAAQ,MAAO,EAAO,MAAQ,MAAQ,OAAQ,CAAC,EAGjF,CACT,CCpLA,SAAgB,EAA8C,EAAO,GAAG,EAAqB,CAC3F,OAAO,IAAI,SAAwB,EAAS,IAAW,CAErD,GAAI,CACF,IAAM,EAAI,EAAG,GAAG,CAAI,EAChB,aAAa,QACf,EAAE,KAAK,CAAO,CAAC,CAAC,MAAM,CAAM,EAE5B,EAAQ,CAAC,CAEb,OAAS,EAAG,CACV,EAAO,CAAC,CACV,CACF,CAAC,CACH,CCjBA,SAAgB,GAAU,EAA8B,CACtD,IAAM,EAAe,EAAI,gBAAgB,mBACzC,GAAI,EAAc,CAChB,IAAM,EAAiB,EAAa,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,EAC5D,GAAI,GAAkB,EAAe,OAAS,EAC5C,OAAO,CAEX,CAEA,IAAM,EAAS,EAAI,gBAAgB,YAAY,GAAG,EAAE,CAAC,KAAK,EAK1D,OAJI,IAAW,IAAA,GAIR,EAAI,OAAO,eAAiB,UAH1B,CAIX,CAEA,SAAgB,EAAqB,EAA0C,CAC7E,GAAI,IAAgB,IAAA,GAClB,MAAO,GAGT,IAAM,EAAa,EAAY,YAAY,EAE3C,OACE,EAAW,WAAW,OAAO,GAC7B,EAAW,SAAS,MAAM,GAC1B,EAAW,SAAS,KAAK,GACzB,EAAW,SAAS,uBAAuB,GAC3C,EAAW,SAAS,YAAY,CAEpC,CC/BA,SAAgB,GAAM,EAA6C,CAC7D,OAAW,IAAA,GAIf,GAAI,CACF,OAAO,IAAI,IAAI,EAAQ,sBAAc,CACvC,MAAQ,CACN,MACF,CACF,CCTA,SAAgB,GAAS,EAAqB,EAAkB,EAAA,IAA0C,CACxG,EAAI,WAAa,EACjB,EAAI,UAAU,eAAgB,iCAAiC,EAC/D,EAAI,IAAI,KAAK,UAAU,CAAO,CAAC,CACjC,CAEA,SAAgB,EAAa,EAAqB,EAAkB,EAAA,IAA0C,CACxG,MAAI,cAIR,IAAI,EAAI,YAAa,CACnB,EAAI,IAAI,EACR,MACF,CAEA,GAAS,EAAK,EAAS,CAAU,CAFjC,CAGF,CCpBA,SAAgB,EAAW,EAAkE,CAC3F,IAAM,EAA2C,CAAC,EAElD,IAAK,GAAM,CAAC,EAAK,KAAU,EAAa,QAAQ,EAAG,CACjD,IAAM,EAAW,EAAM,GAEvB,GAAI,IAAa,IAAA,GAAW,CAC1B,EAAM,GAAO,EACb,QACF,CAEA,GAAI,MAAM,QAAQ,CAAQ,EAAG,CAC3B,EAAS,KAAK,CAAK,EACnB,QACF,CAEA,EAAM,GAAO,CAAC,EAAU,CAAK,CAC/B,CAEA,OAAO,CACT,CCfA,IAAsB,EAAtB,cAA4C,KAAuC,CACjF,MACA,KAEA,YAAY,EAAiB,EAAsB,EAAc,CAC/D,MAAM,CAAO,EACb,KAAK,KAAO,gBACZ,KAAK,MAAQ,EACb,KAAK,KAAO,CACd,CACF,EAOa,GAAb,cAAyC,CAAc,CACrD,YAAY,EAAkB,cAAe,CAC3C,MAAM,EAAA,IAA8B,aAAa,CACnD,CACF,EAKa,GAAb,cAA2C,CAAc,CACvD,YAAY,EAAkB,eAAgB,CAC5C,MAAM,EAAA,IAAgC,cAAc,CACtD,CACF,EAKa,GAAb,cAAwC,CAAc,CACpD,YAAY,EAAkB,YAAa,CACzC,MAAM,EAAA,IAA6B,WAAW,CAChD,CACF,EAKa,EAAb,cAAuC,CAAc,CACnD,YAAY,EAAkB,YAAa,CACzC,MAAM,EAAA,IAA4B,WAAW,CAC/C,CACF,EAKa,EAAb,cAA+C,CAAc,CAC3D,YAAY,EAAkB,qBAAsB,CAClD,MAAM,EAAA,IAAoC,oBAAoB,CAChE,CACF,EAKa,EAAb,cAA4C,CAAc,CACxD,YAAY,EAAkB,iBAAkB,CAC9C,MAAM,EAAA,IAAiC,gBAAgB,CACzD,CACF,EAKa,EAAb,cAA6C,CAAc,CACzD,YAAY,EAAkB,kBAAmB,CAC/C,MAAM,EAAA,IAAkC,iBAAiB,CAC3D,CACF,EAKa,EAAb,cAAuC,CAAc,CACnD,YAAY,EAAkB,WAAY,CACxC,MAAM,EAAA,IAA4B,UAAU,CAC9C,CACF,EAKa,EAAb,cAAmC,CAAc,CAC/C,YAAY,EAAkB,OAAQ,CACpC,MAAM,EAAA,IAAwB,MAAM,CACtC,CACF,EAKa,EAAb,cAA8C,CAAc,CAC1D,YAAY,EAAkB,oBAAqB,CACjD,MAAM,EAAA,IAAmC,mBAAmB,CAC9D,CACF,EAKa,EAAb,cAAmD,CAAc,CAC/D,YAAY,EAAkB,yBAA0B,CACtD,MAAM,EAAA,IAAwC,wBAAwB,CACxE,CACF,EAKa,EAAb,cAAkD,CAAc,CAC9D,YAAY,EAAkB,uBAAwB,CACpD,MAAM,EAAA,IAAuC,sBAAsB,CACrE,CACF,EAKa,EAAb,cAA8C,CAAc,CAC1D,YAAY,EAAkB,oBAAqB,CACjD,MAAM,EAAA,IAAmC,mBAAmB,CAC9D,CACF,EAOa,EAAb,cAAkD,CAAc,CAC9D,YAAY,EAAkB,wBAAyB,CACrD,MAAM,EAAA,IAAuC,uBAAuB,CACtE,CACF,EAKa,GAAb,cAA6C,CAAc,CACzD,YAAY,EAAkB,kBAAmB,CAC/C,MAAM,EAAA,IAAkC,iBAAiB,CAC3D,CACF,EAKa,GAAb,cAAyC,CAAc,CACrD,YAAY,EAAkB,cAAe,CAC3C,MAAM,EAAA,IAA8B,aAAa,CACnD,CACF,EAKa,GAAb,cAAiD,CAAc,CAC7D,YAAY,EAAkB,sBAAuB,CACnD,MAAM,EAAA,IAAsC,qBAAqB,CACnE,CACF,EAKa,GAAb,cAA6C,CAAc,CACzD,YAAY,EAAkB,kBAAmB,CAC/C,MAAM,EAAA,IAAkC,iBAAiB,CAC3D,CACF,ECxKA,SAAS,EAA+B,EAAuB,EAA4C,CACzG,OAAO,IAAI,EACT,2BAA2B,EAAc,SAAS,EAAE,iBAAiB,EAAS,SAAS,EAAE,OAC3F,CACF,CAEA,SAAS,EAAe,EAAgE,CACtF,OAAO,MAAM,QAAQ,CAAW,EAAI,EAAY,GAAK,CACvD,CAEA,SAAS,GAAkC,CACzC,MAAO,CACL,OAAQ,GACR,MAAO,EACP,UAAW,EACb,CACF,CAEA,SAAS,GACP,EACA,EACA,EACA,EACa,CAcb,OAbI,IAAe,EACV,EAAmB,EAGxB,EAAqB,CAAW,EAC3B,CACL,OAAQ,GACR,MAAO,EAAc,SAAS,MAAM,EACpC,MAAO,EACP,WACF,EAGK,CACL,OAAQ,GACR,MAAO,iBAAiB,EAAW,SACnC,MAAO,EACP,WACF,CACF,CAEA,eAAe,GACb,EACA,EACA,EACA,EAAkB,KAC8C,CAQhE,GAPI,IAAW,OAAS,IAAW,QAO/B,EAAI,cACN,MAAO,CACL,QAAS,IAAA,GACT,QAAS,EAAmB,CAC9B,EAGF,IAAM,EAAmB,EAAe,EAAI,QAAQ,iBAAiB,EAC/D,EAAgB,IAAqB,IAAA,GAAoD,IAAxC,OAAO,SAAS,EAAkB,EAAE,EAE3F,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAA0B,CAAC,EAC3B,EAA0B,CAAC,EAC7B,EAAa,EACb,EAAe,EACf,EAAmB,GACnB,EAAU,GAER,MAAsB,CAC1B,EAAI,IAAI,OAAQ,CAAM,EACtB,EAAI,IAAI,MAAO,CAAK,EACpB,EAAI,IAAI,QAAS,CAAO,EACxB,EAAI,IAAI,UAAW,CAAS,CAC9B,EAEM,EAAU,GAA6B,CACvC,IAIJ,EAAU,GACV,EAAO,EACT,EAGA,GAAI,OAAO,SAAS,CAAa,GAAK,EAAgB,EAAU,CAC9D,EAAQ,EACR,EAAI,OAAO,EACX,MAAa,EAAO,EAA+B,EAAe,CAAQ,CAAC,CAAC,EAC5E,MACF,CAEA,IAAM,EAAU,GAA8C,CAC5D,IAAM,EAAc,OAAO,SAAS,CAAK,EAAI,EAAQ,OAAO,KAAK,CAAK,EAGtE,GAFA,GAAc,EAAY,WAEtB,EAAa,EAAU,CACzB,EAAQ,EACR,EAAI,OAAO,EACX,MAAa,EAAO,EAA+B,EAAY,CAAQ,CAAC,CAAC,EACzE,MACF,CAIA,GAFA,EAAc,KAAK,CAAW,EAE1B,EAAe,EAAiB,CAClC,IAAM,EAAY,EAAkB,EAC9B,EAAY,EAAY,SAAS,EAAG,CAAS,EACnD,EAAc,KAAK,CAAS,EAC5B,GAAgB,EAAU,OAEtB,EAAU,OAAS,EAAY,SACjC,EAAmB,GAEvB,KACE,GAAmB,EAEvB,EAEM,MAAoB,CACxB,EAAQ,EACR,MAAa,CACX,IAAM,EAAU,EAAc,OAAS,EAAI,OAAO,OAAO,CAAa,EAAI,IAAA,GAG1E,EAAQ,CACN,UACA,QAAS,GAJW,EAAc,OAAS,EAAI,OAAO,OAAO,CAAa,EAAI,OAAO,MAAM,CAAC,EAM1F,GAAS,YAAc,EACvB,EAAe,EAAI,QAAQ,eAAe,EAC1C,CACF,CACF,CAAC,CACH,CAAC,CACH,EAEM,EAAW,GAAuB,CACtC,EAAQ,EACR,MAAa,EAAO,CAAK,CAAC,CAC5B,EAEM,MAAwB,CAC5B,EAAQ,EACR,MAAa,EAAW,MAAM,oCAAoC,CAAC,CAAC,CACtE,EAEA,EAAI,GAAG,OAAQ,CAAM,EACrB,EAAI,KAAK,MAAO,CAAK,EACrB,EAAI,KAAK,QAAS,CAAO,EACzB,EAAI,KAAK,UAAW,CAAS,CAC/B,CAAC,CACH,CAEA,eAAsB,GACpB,EACA,EACA,EAC8D,CAC9D,GAAM,CAAE,UAAS,WAAY,MAAM,GAA2B,EAAK,EAAQ,CAAQ,EAEnF,GAAI,IAAY,IAAA,IAAa,EAAQ,aAAe,EAClD,MAAO,CACL,KAAM,CAAC,EACP,SACF,EAGF,IAAM,EAAc,EAAe,EAAI,QAAQ,eAAe,CAAC,EAAE,YAAY,GAAK,GAElF,GAAI,EAAY,SAAS,MAAM,EAAG,CAChC,IAAM,EAAW,EAAQ,SAAS,MAAM,CAAC,CAAC,KAAK,EAE/C,GAAI,EAAS,SAAW,EACtB,MAAO,CACL,KAAM,CAAC,EACP,SACF,EAGF,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,CAAQ,EASlC,OAPuB,OAAO,GAAW,UAArC,GAAiD,CAAC,MAAM,QAAQ,CAAM,EACjE,CACL,KAAM,EACN,SACF,EAGK,CACL,KAAM,CAAE,MAAO,CAAO,EACtB,SACF,CACF,MAAQ,CACN,MAAO,CACL,KAAM,CAAE,IAAK,CAAS,EACtB,SACF,CACF,CACF,CAgBA,OAdI,EAAY,SAAS,uBAAuB,EACvC,CACL,KAAM,EAAW,IAAI,gBAAgB,EAAQ,SAAS,MAAM,CAAC,CAAC,EAC9D,SACF,EAGE,EAAqB,CAAW,EAC3B,CACL,KAAM,CAAE,IAAK,EAAQ,SAAS,MAAM,CAAE,EACtC,SACF,EAGK,CACL,KAAM,CAAC,EACP,SACF,CACF,CC9OA,SAAgB,GAAY,EAA0D,CACpF,GAAI,CAAC,EACH,MAAO,CAAC,EAGV,IAAM,EAAkC,CAAC,EACnC,EAAQ,EAAa,MAAM,GAAG,EAChC,EAAQ,EAGZ,IAAK,IAAM,KAAQ,EAAO,CACxB,GAAI,GAAS,IACX,MAEF,GAAM,CAAC,EAAK,GAAG,GAAc,EAAK,MAAM,GAAG,EAC3C,GAAI,CAAC,EAAK,SAEV,IAAM,EAAa,EAAI,KAAK,EACtB,EAAQ,EAAW,KAAK,GAAG,CAAC,CAAC,KAAK,EACxC,EAAQ,GAAc,mBAAmB,CAAK,EAC9C,GACF,CAEA,OAAO,CACT,CCXA,MAAM,GAAU,EAA2B,EAAmB,IAC5D,QAAQ,KAAK,CAAC,EAAa,IAAI,QAAS,GAAM,eAAiB,EAAE,CAAI,EAAG,CAAS,CAAC,CAAC,CAAC,EAMtF,SAAS,GAAqB,EAAoB,EAA0B,EAAqB,CAE/F,GAAI,OAAO,EAAE,UAAa,WACxB,OAEF,IAAM,EAAW,EAAE,SACnB,EAAI,KAAK,YAAe,CACtB,EAAW,CAAQ,CAAC,CAAC,MAAO,GAAe,CACzC,EAAG,OAAO,MAAM,CAAE,QAAS,iBAAkB,IAAK,EAAE,aAAc,MAAO,EAAgB,CAAC,CAAE,CAAC,CAC/F,CAAC,CACH,CAAC,CACH,CAEA,SAAgB,GAAa,EAAyD,CACpF,IAAM,EAAiC,OAAO,OAAO,CAAE,OAAQ,EAAG,MAAO,CAAC,EAEpE,EAAiB,MAAO,EAA2B,IAA6B,CACpF,IAAM,EAAS,EAAI,QAAU,MACvB,EAAK,GAAU,CAAG,EAClB,EAAM,GAAM,EAAI,GAAG,EACzB,GAAI,IAAQ,IAAA,GAAW,CACrB,EAAa,EAAK,CAAE,QAAS,mCAAoC,EAAA,GAAsB,EACvF,MACF,CAEA,IAAM,EAA6B,CACjC,SACA,KACA,MACA,MAAO,EAAW,EAAI,YAAY,EAClC,KAAM,CAAC,EACP,QAAS,EAAI,QACb,OAAQ,GAAY,EAAI,QAAQ,MAA4B,EAC5D,KAAM,CAAC,CACT,EAGA,EAAI,UAAU,yBAA0B,SAAS,EACjD,EAAI,UAAU,kBAAmB,MAAM,EACvC,EAAI,UAAU,mBAAoB,eAAe,EAE7C,EAAG,QAAQ,MAAQ,IACrB,EAAI,UAAU,0BAA2B,EAAG,QAAQ,GAAG,EAGzD,IAAI,EAA2B,CAC7B,OAAQ,GACR,MAAO,EACP,UAAW,EACb,EAEA,EAAG,OAAO,KAAK,CAAE,QAAS,UAAW,SAAQ,KAAI,KAAM,EAAI,QAAS,CAAC,EAErE,IAAM,EAAQ,YAAY,IAAI,EAC9B,EAAI,KAAK,aAAgB,CACvB,IAAM,EAA6B,CACjC,QAAS,WACT,SACA,KACA,KAAM,EAAI,SACV,OAAQ,EAAI,WACZ,UAAW,YAAY,IAAI,EAAI,EAAA,CAAO,QAAQ,CAAC,CACjD,EAEI,OAAO,KAAK,EAAW,KAAK,CAAC,CAAC,OAAS,IACzC,EAAE,MAAQ,EAAW,OAGnB,EAAY,SACd,EAAE,KAAO,EAAY,MACrB,EAAE,UAAY,EAAY,MAC1B,EAAE,cAAgB,EAAY,WAGhC,EAAG,OAAO,KAAK,CAAC,CAClB,CAAC,EAGD,GAAI,CACF,IAAM,EAAS,MAAM,GAAU,EAAK,EAAW,OAAQ,EAAG,QAAQ,eAAe,EACjF,EAAW,KAAO,EAAO,KACzB,EAAc,EAAO,QAErB,IAAM,EAAI,MAAM,EAAG,OAAO,CAAG,EAC7B,GAAI,CAAC,EAAG,CACN,EAAa,EAAK,CAAE,QAAS,WAAY,EAAA,GAAoB,EAC7D,MACF,CAEA,GAAI,EAAI,QAAU,EAAE,SAAW,CAAC,EAAE,QAAQ,SAAS,EAAI,MAAM,EAAG,CAC9D,EAAa,EAAK,CAAE,QAAS,oBAAqB,EAAA,GAA4B,EAC9E,MACF,CAGA,GAAqB,EAAI,EAAK,CAAC,EAE/B,IAAM,EACJ,EAAE,OAAA,EACG,EAAE,kBAAoB,EAAG,QAAQ,iBAClC,EAAG,QAAQ,wBAGjB,GAAI,EAAE,YACJ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAE,YAAY,OAAQ,IAAK,CAO7C,GAAI,MANiB,EACnB,EAAW,EAAE,YAAY,GAAI,EAAY,EAAU,EAAK,CAAG,EAC3D,EAAG,QAAQ,oBACX,EAAM,iBACR,IAEe,EAAM,kBAAmB,CACtC,EAAG,OAAO,KAAK,CACb,QAAS,oBACT,OAAQ,EAAW,OACnB,GAAI,EAAW,EACjB,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,uBAAwB,EAAA,GAA+B,EACpF,MACF,CACA,GAAI,EAAI,cACN,OAEF,GAAI,EAAI,YAAa,CACnB,EAAI,IAAI,EACR,MACF,CACF,CAGF,IAAM,EAAS,MAAM,EACnB,EAAW,EAAE,QAAS,EAAY,EAAU,EAAK,CAAG,EACpD,EACA,EAAM,cACR,EAEA,GAAI,IAAW,EAAM,eAAgB,CACnC,EAAG,OAAO,KAAK,CAAE,QAAS,iBAAkB,OAAQ,EAAW,OAAQ,GAAI,EAAW,EAAG,CAAC,EAC1F,EAAa,EAAK,CAAE,QAAS,mBAAoB,EAAA,GAA+B,EAChF,MACF,CAEI,IAAW,EAAM,eACnB,EAAa,EAAK,CAAM,CAE5B,OAAS,EAAG,CACN,aAAa,GACf,EAAG,OAAO,MAAM,CACd,GAAG,EACH,QAAS,gBACT,MAAO,EAAE,OACX,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,EAAE,OAAQ,EAAG,EAAE,KAAK,IAEjD,EAAG,OAAO,MAAM,CACd,GAAG,EACH,QAAS,gBACT,MAAO,EAAgB,CAAC,CAC1B,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,uBAAwB,EAAA,GAA+B,EAExF,CACF,EAEM,EAAS,EAAG,QAAQ,MACtB,EAAA,QAAM,aACJ,CACE,IAAK,EAAG,QAAQ,MAAM,IACtB,KAAM,EAAG,QAAQ,MAAM,KACvB,GAAI,EAAG,QAAQ,MAAM,EACvB,EACA,CACF,EACA,EAAA,QAAK,aAAa,CAAc,EAEpC,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAI,EAAY,GAEhB,EAAO,GAAG,YAAe,CACvB,EAAG,OAAO,KAAK,CACb,QAAS,eACT,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,CACH,CAAC,EAED,EAAO,KAAK,gBAAmB,CAC7B,EAAY,GACZ,EAAG,OAAO,KAAK,CACb,QAAS,iBACT,QAAS,QACT,IAAK,QAAQ,IACb,SAAU,EAAG,QAAQ,MAAQ,QAAU,OACvC,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,EACD,EAAG,OAAO,KAAK,CACb,QAAS,mBACT,UAAW,EAAG,QAAQ,GACxB,CAAC,EACD,EAAQ,CAAM,CAChB,CAAC,EAED,EAAO,GAAG,QAAU,GAAM,CACxB,EAAG,OAAO,MAAM,CACd,QAAS,cACT,MAAO,EAAgB,CAAC,CAC1B,CAAC,EACG,IAMJ,EAAO,CAAC,CACV,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,KAAM,EAAG,QAAQ,IAAI,CAChD,CAAC,CACH,CClPA,SAAS,GAAE,EAAG,CAAC,CAEf,MAAM,GAAI,GCsBG,EAAc,IAAI,IAEzB,GAAe,GAAGC,EAAAA,IAAI,cAAcA,EAAAA,MAEpC,EAAW,IAAI,IAAI,CAAC,MAAO,OAAQ,OAAQ,OAAQ,MAAO,OAAQ,OAAQ,MAAM,CAAC,EACjF,EAAa,CAAC,GAAG,EAAU,OAAO,EAElC,GAAqC,CACzC,MAAO,CAAC,MAAO,MAAM,EACrB,OAAQ,CAAC,MAAM,EACf,OAAQ,CAAC,MAAM,EACf,OAAQ,CAAC,MAAM,CACjB,EAEM,GAAU,GAAsB,CAAC,EAAU,WAAW,GAAG,GAAK,EAAA,EAAA,EAAA,WAAA,CAAY,CAAS,EAEnF,GAAa,GAAoB,CACrC,GAAI,CACF,IAAM,GAAA,EAAA,EAAA,SAAA,CAAa,EAAE,QAAQ,EAC7B,OAAO,EAAE,UAAY,EAAE,SAAW,EAAE,OAAS,EAAE,IACjD,MAAQ,CAEN,MAAO,EACT,CACF,EAEM,EAAU,GAAiB,CAC/B,GAAI,CACF,OAAA,EAAA,EAAA,SAAA,CAAgB,CAAI,CAAC,CAAC,OAAO,CAC/B,MAAQ,CACN,MAAO,EACT,CACF,EAKM,GAAY,GAAiB,CACjC,GAAI,EAAO,CAAI,EACb,OAAO,EAGT,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EAClB,EAAO,EAAK,MAAM,EAAG,EAAK,OAAS,EAAI,MAAM,EACnD,IAAK,IAAM,KAAK,GAAS,IAAQ,CAAC,EAChC,GAAI,EAAO,EAAO,CAAC,EACjB,OAAO,EAAO,EAGlB,IAAK,IAAM,KAAK,EACd,GAAI,EAAO,EAAO,CAAC,EACjB,OAAO,EAAO,EAGlB,IAAK,IAAM,KAAK,EACd,GAAI,GAAA,EAAA,EAAA,KAAA,CAAY,EAAM,QAAU,CAAC,CAAC,EAChC,OAAA,EAAA,EAAA,KAAA,CAAY,EAAM,QAAU,CAAC,CAGnC,EAYA,IAAa,GAAb,KAA2B,CACzB,KAEA,YAAY,EAA2D,CACrE,KAAK,KAAO,CACd,CAEA,QAAgB,EAAc,CAC5B,GAAM,CAAE,gBAAA,EAAA,EAAA,UAAA,CAA0B,CAChC,YAAa,CAAC,CAAI,EAClB,OAAQ,GACR,MAAO,GACP,SAAU,OACV,OAAQ,MACR,OAAQ,KAAK,KAAK,OAClB,UAAW,SACX,eAAgB,GAChB,SAAU,SAEV,UAAW,CAAE,iBAAkB,EAAM,EACrC,OAAQ,CACN,kBAAmB,KAAK,WAAA,EAAA,EAAA,cAAA,CAAwB,CAAI,CAAC,CAAC,IAAI,EAC1D,uBAAwB,KAAK,UAAU,CAAI,EAC3C,sBAAuB,KAAK,WAAA,EAAA,EAAA,QAAA,CAAkB,CAAI,CAAC,CACrD,CACF,CAAC,EACD,OAAO,EAAY,EAAE,CAAC,IACxB,CAEA,QAAQ,EAA2B,CACjC,OAAO,KAAK,MAAA,EAAA,EAAA,QAAA,CAAa,CAAY,CAAC,CAAC,CAAC,OAC1C,CAEA,MAAc,EAAc,CAC1B,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EACxB,OAAO,EAAK,WAAW,KAAK,KAAK,GAAG,GAAK,CAAC,EAAK,SAAS,EAAY,IAAM,EAAS,IAAI,CAAG,GAAK,IAAQ,QACzG,CAKA,KAAa,EAA4B,CACvC,IAAM,EAAS,EAAY,IAAI,CAAI,EACnC,GAAI,IAAW,IAAA,GACb,OAAO,KAAK,UAAU,CAAI,EAG5B,IAAM,EAAQ,KAAK,aAAa,CAAM,EACtC,GAAI,CAAC,KAAK,SAAS,CAAK,EACtB,OAAO,EAET,IAAK,IAAM,KAAK,EAEV,EAAY,IAAI,EAAE,QAAQ,IAAM,GAClC,EAAY,OAAO,EAAE,QAAQ,EAGjC,OAAO,KAAK,UAAU,CAAI,CAC5B,CAEA,aAAqB,EAAoB,CACvC,IAAM,EAAQ,IAAI,IAAI,CAAC,CAAI,CAAC,EAE5B,IAAK,IAAM,KAAK,EACd,IAAK,IAAM,KAAO,EAAE,KAAK,OAAO,EAC9B,EAAM,IAAI,CAAG,EAGjB,OAAO,CACT,CAEA,SAAiB,EAA0B,CACzC,IAAK,IAAM,KAAK,EACd,GAAI,EAAY,IAAI,EAAE,QAAQ,IAAM,GAAK,GAAU,CAAC,EAClD,MAAO,GAGX,MAAO,EACT,CAEA,UAAkB,EAA4B,CAE5C,GAAM,CAAE,UAAS,SAAA,EAAA,EAAA,SAAA,CAAkB,CAAI,EACjC,EAAoB,CAAE,GAAI,EAAM,SAAU,EAAM,QAAS,CAAC,EAAG,UAAS,OAAM,KAAM,IAAI,GAAM,EAElG,EAAY,IAAI,EAAM,CAAG,EACzB,GAAI,CACF,KAAK,SAAS,EAAM,CAAG,CACzB,OAAS,EAAG,CAEV,MADA,EAAY,OAAO,CAAI,EACjB,CACR,CACA,OAAO,CACT,CAEA,SAAiB,EAAc,EAAmB,CAChD,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EACxB,GAAI,IAAQ,QAAS,CACnB,EAAI,QAAU,KAAK,OAAA,EAAA,EAAA,aAAA,CAAmB,EAAM,MAAM,CAAC,EACnD,MACF,CACA,GAAI,CAAC,EAAS,IAAI,CAAG,EACnB,MAAU,MAAM,yCAAyC,EAAI,KAAK,EAAK,EAAE,GAM3E,EAAA,EAAA,gBAAA,CAH2B,KAAK,QAAQ,CAAI,EAAG,CAAC,UAAW,UAAW,SAAU,aAAc,WAAW,EAAG,CAC1G,SAAU,CACZ,CACC,CAAC,CAAC,KAAK,EAAI,QAAS,EAAI,QAAS,KAAK,oBAAoB,CAAG,EAAG,EAAK,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,CAAC,CAC3F,CAKA,oBAA4B,EAAoB,CAC9C,IAAM,GAAA,EAAA,EAAA,cAAA,CAAuB,EAAK,QAAQ,EACpC,EAAU,GACd,GAAO,CAAS,EAAI,IAAA,GAAY,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,CAAyB,EAAK,QAAQ,EAAG,CAAS,CAAC,EAE/E,EAAO,GAAsB,CACjC,IAAM,EAAO,EAAO,CAAS,EAC7B,GAAI,IAAS,IAAA,GAEX,OAAO,EAAO,CAAS,EAEzB,GAAI,CAAC,KAAK,MAAM,CAAI,EAClB,OAAO,EAAO,CAAI,EAEpB,IAAM,EAAM,KAAK,KAAK,CAAI,EAG1B,OADA,EAAK,KAAK,IAAI,EAAM,CAAG,EAChB,EAAI,OACb,EAGA,MAFA,GAAI,QAAW,GAAsB,EAAO,CAAS,GAAK,EAAO,QAAQ,CAAS,EAClF,EAAI,MAAQ,EAAO,MACZ,CACT,CACF,ECjOA,SAAS,EAAgB,EAAgD,EAAmC,CAC1G,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAO,GAKT,GAFA,GAA8B,CAAC,EAE3B,OAAO,EAAE,SAAY,WAEvB,OADA,EAAG,OAAO,MAAM,4BAA4B,EACrC,GAGT,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WAEpD,OADA,EAAG,OAAO,MAAM,yCAAyC,EAClD,GAGT,IAAM,EAAK,EAAE,iBAWb,OAVI,IAAO,IAAA,KAAc,CAAC,OAAO,cAAc,CAAE,GAAK,EAAK,MACzD,EAAG,OAAO,MAAM,wDAAwD,EACjE,IAGL,EAAE,OAAA,EAKC,IAJL,EAAG,OAAO,MAAM,mDAAmD,EAC5D,GAIX,CAEA,SAAgB,GACd,EACA,EACA,EACkB,CAGlB,IAAI,EAAI,IAFW,GAAc,EAAG,OAEvB,CAAC,CAAC,QAAQ,CAAY,EACnC,GAAI,EAAgB,EAAI,EAAE,OAAO,EAC/B,EAAI,EAAE,aACD,GAAI,GAAgB,EAAI,CAAC,EAE9B,MAAM,IAAI,EACR,2BAA2B,EAAa,0DAC1C,EAMF,MAHA,GAAE,aAAe,EACjB,EAAE,QAAU,EAAK,QAEV,CACT,CC5CA,SAAgB,GAAa,EAA+D,CAC1F,OAAO,KAAO,IAAQ,CACpB,IAAM,EAAe,EAAI,SAAS,QAAQ,QAAS,EAAE,CAAC,CAAC,QAAQ,QAAS,EAAE,EACpE,EAAeC,EAAAA,QAAK,KAAK,EAAG,QAAQ,IAAK,CAAY,EAGrD,EAAYA,EAAAA,QAAK,SAAS,EAAG,QAAQ,IAAK,CAAY,EAM5D,GALI,EAAU,WAAW,IAAI,GAAKA,EAAAA,QAAK,WAAW,CAAS,GAKvD,EAAG,QAAQ,QAAQ,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EAC3D,OAIF,IAAM,EAAO,MAAMC,EAAAA,QAAG,KAAK,CAAY,CAAC,CAAC,UAAY,IAAA,EAAS,EAC1D,MAAC,GAAQ,CAAC,EAAK,OAAO,GAI1B,IAAI,EAAG,QAAQ,WAAW,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EAC9D,OAAO,GAAkB,EAAI,EAAc,CAAI,EAGjD,GAAI,EAAG,QAAQ,cAAc,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EACjE,OAAO,EAAmB,CAAY,CAJS,CAQnD,CACF,CAEA,SAAS,EAAmB,EAAwC,CAClE,MAAO,CACL,KAAA,EACA,QAAS,IACT,eACA,QAAS,MAAO,EAAY,EAAI,EAAK,IAAQ,CAC3C,GAAI,EAAW,SAAW,OAAS,EAAW,SAAW,OAAQ,CAC/D,EAAI,WAAa,IACjB,EAAI,UAAU,QAAS,WAAW,EAClC,EAAI,IAAI,EACR,MACF,CAEA,IAAM,EAAO,MAAMA,EAAAA,QAAG,KAAK,CAAY,CAAC,CAAC,MAAO,GAAa,CAAC,EAC9D,GAAI,aAAgB,MAIlB,MAHA,GAAI,WAAa,IACjB,EAAI,IAAI,gBAAgB,EACxB,EAAG,OAAO,MAAM,CAAE,OAAQ,sBAAuB,IAAK,EAAW,IAAK,MAAO,EAAK,OAAQ,CAAC,EACpF,KAGT,GAAI,CAAC,EAAK,OAAO,EAAG,CAClB,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAGA,IAAM,EAAc,GADFD,EAAAA,QAAK,QAAQ,CAAY,CAAC,CAAC,YACI,IAAM,2BAMvD,GAJA,EAAI,WAAa,IACjB,EAAI,UAAU,eAAgB,CAAW,EACzC,EAAI,UAAU,iBAAkB,OAAO,EAAK,IAAI,CAAC,EAE7C,EAAW,SAAW,OAAQ,CAChC,EAAI,IAAI,EACR,MACF,CAEA,OAAO,IAAI,SAAiB,EAAS,IAAW,CAC9C,IAAM,GAAA,EAAA,EAAA,iBAAA,CAA0B,CAAY,EAEtC,MAAgB,CACpB,EAAO,IAAI,QAAS,CAAO,EAC3B,EAAO,IAAI,MAAO,CAAK,EACvB,EAAI,IAAI,QAAS,CAAa,EAC9B,EAAI,IAAI,UAAW,CAAa,CAClC,EAEM,EAAW,GAAiB,CAChC,EAAQ,EACR,EAAO,CAAK,CACd,EAEM,MAAc,CAClB,EAAQ,EACR,EAAQ,EAAM,aAAa,CAC7B,EAEM,MAAsB,CAC1B,EAAQ,EACR,EAAO,QAAQ,EACf,EAAQ,EAAM,aAAa,CAC7B,EAEA,EAAO,GAAG,QAAS,CAAO,EAC1B,EAAO,GAAG,MAAO,CAAK,EACtB,EAAI,GAAG,QAAS,CAAa,EAC7B,EAAI,GAAG,UAAW,CAAa,EAE/B,EAAO,KAAK,CAAG,CACjB,CAAC,CACH,CACF,CACF,CC9GA,eAAsB,GAAQ,EAAoD,CAEhF,IAAM,EAAU,CAAE,QADS,EAAqC,iBAAmB,EAAM,kBAC1C,EAAU,EAAqB,CAAO,CAAE,EAEvF,EAAQ,OAAS,GAAa,CAA0C,EACxE,EAAQ,OAAS,GAAa,CAAqD,EAGnF,IAAM,EAAS,MAAM,GAAa,CAAO,EAGnC,EAAY,GAA2B,CAC3C,EAAQ,OAAO,KAAK,CAAE,QAAS,eAAgB,IAAK,QAAQ,IAAK,QAAO,CAAC,EACzE,EAAO,UAAY,QAAQ,KAAK,CAAC,CAAC,CACpC,EAEA,QAAQ,KAAK,aAAgB,EAAS,QAAQ,CAAC,EAC/C,QAAQ,KAAK,cAAiB,EAAS,SAAS,CAAC,CACnD,CCRA,SAAgB,GACd,EACA,EACqB,CACrB,GAAI,OAAO,GAAM,WAAY,CAC3B,GAAI,IAAa,IAAA,IAAa,OAAO,GAAa,WAChD,MAAM,IAAI,EAAa,iDAAiD,OAAO,GAAU,EAE3F,MAAO,CAAE,QAAS,EAAG,WAAU,KAAA,CAAqB,CACtD,CAEA,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAM,IAAI,EACR,oFAAoF,OAAO,GAC7F,EAGF,GAAI,OAAO,EAAE,SAAY,WACvB,MAAM,IAAI,EAAa,qDAAqD,EAG9E,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WACpD,MAAM,IAAI,EAAa,kEAAkE,EAG3F,GAAI,EAAE,UAAY,IAAA,KAAc,CAAC,MAAM,QAAQ,EAAE,OAAO,GAAK,EAAE,QAAQ,KAAM,GAAM,OAAO,GAAM,QAAQ,GACtG,MAAM,IAAI,EAAa,0EAA0E,EAGnG,GACE,EAAE,cAAgB,IAAA,KACjB,CAAC,MAAM,QAAQ,EAAE,WAAW,GAAK,EAAE,YAAY,KAAM,GAAM,OAAO,GAAM,UAAU,GAEnF,MAAM,IAAI,EAAa,gFAAgF,EAGzG,MAAO,CAAE,GAAG,EAAG,KAAA,CAAqB,CACtC,CAEA,SAAgB,GAAwB,EAAkD,CACxF,GAAI,OAAO,GAAe,WACxB,MAAM,IAAI,EAAa,0DAA0D,OAAO,GAAY,EAEtG,OAAO,CACT,CAEA,SAAgB,GAAoB,EAA2B,CAC7D,GAAI,OAAO,GAAa,WACtB,MAAM,IAAI,EAAa,wDAAwD,OAAO,GAAU,EAElG,OAAO,CACT"}
|