yinzerflow 0.6.12 → 0.7.0

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/index.js.map CHANGED
@@ -1,60 +1,61 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../node_modules/dayjs/dayjs.min.js", "../app/core/YinzerFlow.ts", "../app/core/execution/RequestHandlerImpl.ts", "../app/core/utils/log.ts", "../app/constants/colors.ts", "../app/constants/log.ts", "../app/core/execution/utils/parseJson.ts", "../app/core/execution/utils/parseMultipart.ts", "../app/constants/http.ts", "../app/core/execution/utils/parseUrlEncodedForm.ts", "../app/core/execution/utils/determineEncoding.ts", "../app/core/execution/utils/inferContentType.ts", "../app/core/execution/utils/parseBody.ts", "../app/core/utils/string.ts", "../app/core/execution/utils/parseHttpRequest.ts", "../app/core/execution/utils/parseQuery.ts", "../app/core/execution/utils/parseIpAddress.ts", "../app/core/execution/utils/extractBoundaryFromHeader.ts", "../app/core/execution/utils/parseRequestHeaders.ts", "../app/core/execution/RequestImpl.ts", "../app/core/execution/ResponseImpl.ts", "../app/core/execution/utils/formatBodyIntoString.ts", "../app/core/execution/utils/mapStatusCodeToMessage.ts", "../app/core/execution/utils/validateResponseHeaders.ts", "../app/core/utils/calculateContentSizeInBytes.ts", "../app/core/execution/ContextImpl.ts", "../app/core/setup/utils/handleCustomConfiguration.ts", "../app/core/execution/HookRegistryImpl.ts", "../app/core/setup/utils/compileRoutePatter.ts", "../app/core/setup/utils/normalizeStringPatterns.ts", "../app/core/setup/utils/validateParameterNames.ts", "../app/core/setup/RouteRegistryImpl.ts", "../app/core/setup/utils/routeUtils.ts", "../app/core/setup/GroupApp.ts", "../app/core/setup/SetupImpl.ts", "../app/core/utils/networkLog.ts", "../app/core/modules/rateLimit/stores/inMemory.ts", "../app/core/modules/rateLimit/stores/redis.ts", "../app/core/utils/time.ts", "../app/core/modules/rateLimit/stores/index.ts", "../app/core/modules/rateLimit/strategies/SlidingWindowCounterStrategy.ts", "../app/core/modules/rateLimit/RateLimiter.ts", "../app/constants/rateLimit.ts", "../app/core/modules/rateLimit/RateLimitConfig.ts", "../app/core/modules/rateLimit/rateLimithooks.ts", "../app/core/modules/cookieParser/CookieParser.ts", "../app/core/modules/cookieParser/CookieParserConfig.ts", "../app/core/modules/cookieParser/cookieParserHooks.ts", "../app/core/modules/cors/Cors.ts", "../app/core/modules/cors/corsHooks.ts", "../app/core/modules/cors/CorsConfig.ts"],
3
+ "sources": ["../app/core/YinzerFlow.ts", "../app/core/execution/RequestHandlerImpl.ts", "../app/constants/colors.ts", "../app/constants/log.ts", "../app/core/utils/log.ts", "../app/core/utils/bytes.ts", "../app/core/execution/utils/parseJson.ts", "../app/core/execution/utils/parseMultipart.ts", "../app/constants/http.ts", "../app/core/execution/utils/parseUrlEncodedForm.ts", "../app/core/execution/utils/determineEncoding.ts", "../app/core/execution/utils/inferContentType.ts", "../app/core/execution/utils/parseBody.ts", "../app/core/utils/string.ts", "../app/core/execution/utils/parseHttpRequest.ts", "../app/core/execution/utils/parseQuery.ts", "../app/core/execution/utils/parseIpAddress.ts", "../app/core/execution/utils/extractBoundaryFromHeader.ts", "../app/core/execution/utils/parseRequestHeaders.ts", "../app/core/execution/RequestImpl.ts", "../app/core/execution/utils/formatBodyIntoString.ts", "../app/core/execution/utils/mapStatusCodeToMessage.ts", "../app/core/execution/utils/validateResponseHeaders.ts", "../app/core/execution/ResponseImpl.ts", "../app/core/execution/ContextImpl.ts", "../app/core/utils/time.ts", "../app/core/setup/utils/handleCustomConfiguration.ts", "../app/core/execution/HookRegistryImpl.ts", "../app/core/setup/utils/compileRoutePatter.ts", "../app/core/setup/utils/normalizeStringPatterns.ts", "../app/core/setup/utils/validateParameterNames.ts", "../app/core/setup/RouteRegistryImpl.ts", "../app/core/setup/utils/routeUtils.ts", "../app/core/setup/GroupApp.ts", "../app/core/setup/SetupImpl.ts", "../app/core/utils/accessLog.ts", "../app/core/modules/rateLimit/stores/inMemory.ts", "../app/core/modules/rateLimit/stores/redis.ts", "../app/core/modules/rateLimit/stores/index.ts", "../app/core/modules/rateLimit/strategies/SlidingWindowCounterStrategy.ts", "../app/core/modules/rateLimit/RateLimiter.ts", "../app/constants/rateLimit.ts", "../app/core/modules/rateLimit/RateLimitConfig.ts", "../app/core/modules/rateLimit/rateLimithooks.ts", "../app/core/modules/cookieParser/CookieParser.ts", "../app/core/modules/cookieParser/CookieParserConfig.ts", "../app/core/modules/cookieParser/cookieParserHooks.ts", "../app/core/modules/cors/Cors.ts", "../app/core/modules/cors/corsHooks.ts", "../app/core/modules/cors/CorsConfig.ts", "../app/core/utils/sanitize.ts", "../app/core/modules/diagnostics/DiagnosticsMonitor.ts"],
4
4
  "sourcesContent": [
5
- "!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define(e):(t=\"undefined\"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){\"use strict\";var t=1e3,e=6e4,n=36e5,r=\"millisecond\",i=\"second\",s=\"minute\",u=\"hour\",a=\"day\",o=\"week\",c=\"month\",f=\"quarter\",h=\"year\",d=\"date\",l=\"Invalid Date\",$=/^(\\d{4})[-/]?(\\d{1,2})?[-/]?(\\d{0,2})[Tt\\s]*(\\d{1,2})?:?(\\d{1,2})?:?(\\d{1,2})?[.:]?(\\d+)?$/,y=/\\[([^\\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:\"en\",weekdays:\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\"),months:\"January_February_March_April_May_June_July_August_September_October_November_December\".split(\"_\"),ordinal:function(t){var e=[\"th\",\"st\",\"nd\",\"rd\"],n=t%100;return\"[\"+t+(e[(n-20)%10]||e[n]||e[0])+\"]\"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:\"\"+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?\"+\":\"-\")+m(r,2,\"0\")+\":\"+m(i,2,\"0\")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return+(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return{M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||\"\").toLowerCase().replace(/s$/,\"\")},u:function(t){return void 0===t}},g=\"en\",D={};D[g]=M;var p=\"$isDayjsObject\",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if(\"string\"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split(\"-\");if(!i&&u.length>1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n=\"object\"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if(\"string\"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||\"0\").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate(\"s\"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v=\"set\"+(this.$u?\"UTC\":\"\");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+\"Hours\",0);case u:return $(v+\"Minutes\",1);case s:return $(v+\"Seconds\",2);case i:return $(v+\"Milliseconds\",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=b.p(t),f=\"set\"+(this.$u?\"UTC\":\"\"),l=(n={},n[a]=f+\"Date\",n[d]=f+\"Date\",n[c]=f+\"Month\",n[h]=f+\"FullYear\",n[u]=f+\"Hours\",n[s]=f+\"Minutes\",n[i]=f+\"Seconds\",n[r]=f+\"Milliseconds\",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||\"YYYY-MM-DDTHH:mm:ssZ\",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,\"0\")},$=f||function(t,e,n){var r=t<12?\"AM\":\"PM\";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case\"YY\":return String(e.$y).slice(-2);case\"YYYY\":return b.s(e.$y,4,\"0\");case\"M\":return a+1;case\"MM\":return b.s(a+1,2,\"0\");case\"MMM\":return h(n.monthsShort,a,c,3);case\"MMMM\":return h(c,a);case\"D\":return e.$D;case\"DD\":return b.s(e.$D,2,\"0\");case\"d\":return String(e.$W);case\"dd\":return h(n.weekdaysMin,e.$W,o,2);case\"ddd\":return h(n.weekdaysShort,e.$W,o,3);case\"dddd\":return o[e.$W];case\"H\":return String(s);case\"HH\":return b.s(s,2,\"0\");case\"h\":return d(1);case\"hh\":return d(2);case\"a\":return $(s,u,!0);case\"A\":return $(s,u,!1);case\"m\":return String(u);case\"mm\":return b.s(u,2,\"0\");case\"s\":return String(e.$s);case\"ss\":return b.s(e.$s,2,\"0\");case\"SSS\":return b.s(e.$ms,3,\"0\");case\"Z\":return i}return null}(t)||i.replace(\":\",\"\")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[[\"$ms\",r],[\"$s\",i],[\"$m\",s],[\"$H\",u],[\"$W\",a],[\"$M\",c],[\"$y\",h],[\"$D\",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=!0),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));",
6
- "import { createServer } from 'net';\nimport type { Socket } from 'net';\n\nimport { RequestHandlerImpl } from '@core/execution/RequestHandlerImpl.ts';\nimport { ContextImpl } from '@core/execution/ContextImpl.ts';\nimport { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport { log } from '@core/utils/log.ts';\nimport type { ServerOptions } from '@typedefs/public/Configuration.js';\nimport { getStatusEmoji, logPerformanceDetails, networkLog } from '@core/utils/networkLog.ts';\nimport { calculateContentSizeInBytes } from '@core/utils/calculateContentSizeInBytes.ts';\nimport { _createGlobalRateLimitHook } from '@core/modules/rateLimit/rateLimithooks.ts';\nimport { RateLimiter } from '@core/modules/rateLimit/RateLimiter.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\nimport { cookieParserHook } from '@core/modules/cookieParser/cookieParserHooks.ts';\nimport { CookieParserConfig } from '@core/modules/cookieParser/CookieParserConfig.ts';\nimport { corsHook } from '@core/modules/cors/corsHooks.ts';\nimport { CorsConfig } from '@core/modules/cors/CorsConfig.ts';\nimport type { InternalCorsEnabledOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * Maximum overhead allowance for HTTP headers on top of body parser limits (64KB).\n *\n * The TCP buffer limit is `max(bodyParserLimits) + maxHeaderOverhead` because the raw\n * TCP stream contains both headers and body. The body portion is bounded by the user's\n * body parser config (json.maxSize, urlEncoded.maxSize, fileUploads.maxTotalSize), but\n * we also need room for the request line, headers, and the blank line separator.\n *\n * 64KB is generous — most HTTP headers are 2-8KB (nginx defaults to 8KB max). This\n * leaves plenty of room for large cookie headers or verbose auth tokens while still\n * rejecting obviously malicious oversized requests at the TCP level before parsing.\n */\nconst maxHeaderOverhead = 65_536;\n\n/**\n * Main YinzerFlow application class for building HTTP servers.\n *\n * YinzerFlow is a lightweight, high-performance HTTP server framework built on Node.js\n * that provides an elegant API for routing, middleware, and request handling. It extends\n * the Setup interface to provide all route configuration capabilities plus server management.\n *\n * ## Key Features\n *\n * - **High Performance**: Built on Node.js net module for maximum performance\n * - **Type Safety**: Full TypeScript support with generics for type-safe contexts\n * - **Route Groups**: Nested route organization with shared hooks and middleware\n * - **Global Hooks**: beforeAll/afterAll hooks for cross-cutting concerns\n * - **State Management**: Request-scoped state for sharing data between middleware\n * - **Graceful Shutdown**: Automatic graceful shutdown handling\n * - **Logging**: Built-in logging with custom logger support\n * - **Network Logging**: Request/response logging with performance metrics\n *\n * ## Basic Usage\n *\n * @example\n * ```typescript\n * import { YinzerFlow } from 'yinzerflow';\n *\n * // Create app with basic configuration\n * const app = new YinzerFlow({ port: 3000 });\n *\n * // Register routes\n * app.get('/api/users', async (ctx) => {\n * return { users: ['John', 'Jane'] };\n * });\n *\n * app.post('/api/users', async (ctx) => {\n * const { name, email } = ctx.request.body;\n * return { message: 'User created', name, email };\n * });\n *\n * // Set up global hooks\n * app.beforeAll([\n * async (ctx) => {\n * ctx.state.requestId = generateRequestId();\n * ctx.state.startTime = Date.now();\n * }\n * ]);\n *\n * app.afterAll([\n * async (ctx) => {\n * const processingTime = Date.now() - ctx.state.startTime;\n * ctx.response.addHeaders({\n * 'X-Processing-Time': `${processingTime}ms`,\n * 'X-Request-ID': ctx.state.requestId\n * });\n * }\n * ]);\n *\n * // Create route groups\n * app.group('/api/v1', (api) => {\n * api.group('/admin', (admin) => {\n * admin.get('/users', async (ctx) => {\n * return { adminUsers: ['Admin1', 'Admin2'] };\n * });\n * });\n * });\n *\n * // Start the server\n * app.listen();\n * ```\n *\n * ## Advanced Configuration\n *\n * @example\n * ```typescript\n * // Advanced configuration with custom logging\n * const app = new YinzerFlow({\n * port: 8080,\n * host: '0.0.0.0',\n * logLevel: 'debug',\n * networkLogs: true,\n * autoGracefulShutdown: true,\n * logger: {\n * info: (message, ...args) => console.log(`[INFO] ${message}`, ...args),\n * warn: (message, ...args) => console.warn(`[WARN] ${message}`, ...args),\n * error: (message, ...args) => console.error(`[ERROR] ${message}`, ...args),\n * debug: (message, ...args) => console.debug(`[DEBUG] ${message}`, ...args)\n * },\n * networkLogger: {\n * info: (message, ...args) => console.log(`[NETWORK] ${message}`, ...args)\n * }\n * });\n *\n * // Custom error handling\n * app.onError(async (ctx, error) => {\n * ctx.response.setStatusCode(500);\n * return {\n * error: 'Internal server error',\n * message: process.env.NODE_ENV === 'production' ? 'Something went wrong' : error.message\n * };\n * });\n *\n * // Custom not-found handling\n * app.onNotFound(async (ctx) => {\n * ctx.response.setStatusCode(404);\n * return {\n * error: 'Not found',\n * path: ctx.request.path,\n * availableEndpoints: ['/api/users', '/api/posts', '/health']\n * };\n * });\n * ```\n *\n * ## Server Lifecycle\n *\n * 1. **Initialization**: Constructor sets up logging and configuration\n * 2. **Route Setup**: Register routes, hooks, and middleware\n * 3. **Server Start**: Call `listen()` to start accepting connections\n * 4. **Request Processing**: Handle incoming HTTP requests\n * 5. **Graceful Shutdown**: Automatic cleanup on process termination\n *\n * @see {@link SetupImpl} for route configuration capabilities\n * @see {@link ServerOptions} for configuration options\n * @see {@link ContextImpl} for request context implementation\n * @see {@link RequestHandlerImpl} for request processing\n */\nexport class YinzerFlow extends SetupImpl {\n private _isListening = false;\n private _server?: ReturnType<typeof createServer>;\n private _globalRateLimiter?: RateLimiter | undefined;\n private readonly _maxBufferSize: number;\n\n constructor(configuration?: ServerOptions) {\n super(configuration);\n\n // Pre-compute max buffer size from body parser limits (immutable after construction)\n this._maxBufferSize =\n Math.max(\n this._configuration.bodyParser.json.maxSize,\n this._configuration.bodyParser.urlEncoded.maxSize,\n this._configuration.bodyParser.fileUploads.maxTotalSize,\n ) + maxHeaderOverhead;\n\n // Replace global logger if custom logger is provided\n if (this._configuration.logger) {\n // Replace the global log instance with the custom logger\n Object.assign(log, this._configuration.logger);\n }\n\n // Set network logger if provided (optional - can be same as app logger or different)\n if (this._configuration.networkLogs) {\n networkLog.enable(this._configuration.networkLogger);\n }\n\n // Setup global rate limiting, if there is none provided, it will be enabled by default. We need to set the confgratin before the conditional since it is defaulted on and users might not pass it in the configuration.\n const rateLimitConfig = new RateLimitConfig(configuration?.rateLimit);\n if (configuration?.rateLimit?.enabled) {\n this._globalRateLimiter = new RateLimiter(rateLimitConfig);\n const hook = _createGlobalRateLimitHook(this._globalRateLimiter);\n this.beforeAll([hook]);\n }\n\n // Setup cookie parser, if enabled, since it is deisabled by default we can set the configuration after the conditional\n if (configuration?.cookieParser?.enabled) {\n const cookieParserConfig = new CookieParserConfig(configuration.cookieParser);\n const cookieParserHookFunc = cookieParserHook(cookieParserConfig.config);\n this.beforeAll([cookieParserHookFunc]);\n }\n\n // Setup CORS, if enabled, as a beforeRouting hook\n if (configuration?.cors?.enabled) {\n const corsConfig = CorsConfig.merge(configuration.cors);\n CorsConfig.validate(corsConfig);\n // Type assertion safe here: validate() throws if config is disabled\n const corsHookFunc = corsHook(corsConfig as InternalCorsEnabledOptions);\n this.beforeRouting([corsHookFunc]);\n }\n\n // Setup automatic graceful shutdown if enabled\n if (this._configuration.gracefulShutdownTimeout) {\n const gracefulShutdownTimeout = _convertTimeToMs(this._configuration.gracefulShutdownTimeout);\n if (gracefulShutdownTimeout > 0) {\n this._setupGracefulShutdown(gracefulShutdownTimeout);\n }\n }\n }\n\n /**\n * Setup server with all event listeners\n */\n private _setupServer(resolve: () => void, reject: (error: Error) => void, requestHandler: RequestHandlerImpl): void {\n if (!this._server) return;\n\n this._server.on('error', (error: Error) => {\n networkLog.log.error(`YinzerFlow server error at ${this._configuration.host}:${this._configuration.port} - ${error.message}`);\n // Clean up the failed server to prevent handle leaks on re-listen\n this._server?.close();\n delete this._server;\n reject(error);\n });\n\n this._server.on('listening', () => {\n this._isListening = true;\n networkLog.log.info(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is up and running`);\n resolve();\n });\n\n this._server.on('connection', (socket) => {\n this._handleConnection(socket, requestHandler);\n });\n }\n\n /**\n * Process incoming request data\n */\n private async _processRequest({\n data,\n socket,\n requestHandler,\n clientAddress,\n }: {\n data: Buffer;\n socket: Socket;\n requestHandler: RequestHandlerImpl;\n clientAddress: string;\n }): Promise<void> {\n const startTime = Date.now();\n\n // Log incoming request\n networkLog.log.info('Incoming request', `${clientAddress} ${calculateContentSizeInBytes(data)}bytes`);\n\n const context = new ContextImpl(data, this, clientAddress);\n\n await requestHandler.handle(context);\n\n // Guard against writing to a socket that was destroyed (client disconnect, timeout, etc.)\n if (!socket.destroyed) {\n socket.write(context._response._stringBody);\n socket.end();\n }\n\n const endTime = Date.now();\n const processingTime = endTime - startTime;\n\n // Log request response — use Buffer.byteLength on already-serialized string body\n // instead of re-serializing with calculateContentSizeInBytes\n const responseBytes = Buffer.byteLength(context._response._stringBody, 'utf8');\n networkLog.log.info(\n `${getStatusEmoji(context._response._statusCode)} ${clientAddress} \"${context.request.method} ${context.request.path} ${context.request.protocol}\" ${context._response._statusCode} ${responseBytes}bytes \"${context.request.headers.referer ?? '-'}\" \"${context.request.headers['user-agent'] ?? '-'}\" ${processingTime}ms`,\n );\n logPerformanceDetails(processingTime);\n }\n\n /**\n * Handle errors from request processing — log and destroy socket\n */\n private _handleRequestError(error: unknown, clientAddress: string, socket: Socket): void {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n networkLog.log.error(`Visitor from ${clientAddress} experienced an error during request processing: ${errorMessage}`, error);\n if (!socket.destroyed) {\n socket.destroy();\n }\n }\n\n /**\n * Dispatch assembled request buffer to the request processor.\n * Wraps `_processRequest` with the standard error catch handler.\n */\n private _dispatchRequest({\n data,\n socket,\n requestHandler,\n clientAddress,\n }: {\n data: Buffer;\n socket: Socket;\n requestHandler: RequestHandlerImpl;\n clientAddress: string;\n }): void {\n this._processRequest({ data, socket, requestHandler, clientAddress }).catch((error: unknown) => this._handleRequestError(error, clientAddress, socket));\n }\n\n /**\n * Reject an oversized request with HTTP 413 (if headers were parsed) and destroy the socket.\n * Logs the current body parser limits for debugging.\n */\n private _rejectOversizedRequest({\n socket,\n clientAddress,\n totalLength,\n headersParsed,\n }: {\n socket: Socket;\n clientAddress: string;\n totalLength: number;\n headersParsed: boolean;\n }): void {\n if (headersParsed) {\n const errorBody = JSON.stringify({\n error: 'Payload too large',\n maxSize: this._maxBufferSize,\n received: totalLength,\n });\n socket.write(\n `HTTP/1.1 413 Payload Too Large\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(errorBody, 'utf8')}\\r\\n` +\n `Connection: close\\r\\n\\r\\n${errorBody}`,\n );\n }\n networkLog.log.warn(\n `Request from ${clientAddress} exceeded maximum buffer size (${totalLength} > ${this._maxBufferSize} bytes). ` +\n `Current limits: json=${this._configuration.bodyParser.json.maxSize}, ` +\n `urlEncoded=${this._configuration.bodyParser.urlEncoded.maxSize}, ` +\n `fileUploads=${this._configuration.bodyParser.fileUploads.maxTotalSize}`,\n );\n socket.destroy();\n }\n\n /**\n * Check if buffered data looks like the start of an HTTP request.\n *\n * First checks the first byte against known HTTP method start characters\n * (G, P, D, H, O), then validates the first 8 bytes against the full method pattern.\n * Returns `false` for non-HTTP traffic so it can be dispatched for a graceful error.\n */\n private _looksLikeHttp(chunks: Array<Buffer>, totalLength: number, buffer: Buffer): boolean {\n if (totalLength >= 1) {\n const firstByte = chunks[0]?.[0] ?? 0;\n // cspell:disable-next-line\n // HTTP methods start with: G(ET)=0x47, P(OST/UT/ATCH)=0x50, D(ELETE)=0x44, H(EAD)=0x48, O(PTIONS)=0x4f\n if (firstByte !== 0x47 && firstByte !== 0x50 && firstByte !== 0x44 && firstByte !== 0x48 && firstByte !== 0x4f) {\n return false;\n }\n }\n if (totalLength >= 8) {\n const start = buffer.subarray(0, 8).toString();\n if (!/^(?:GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\\s/.test(start)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Extract Content-Length value from raw HTTP headers.\n * Returns 0 if the header is missing (e.g. GET requests with no body).\n */\n private _parseContentLength(buffer: Buffer, headerEndIndex: number): number {\n const headersStr = buffer.subarray(0, headerEndIndex).toString();\n const match = /content-length:\\s*(?<digits>\\d+)/i.exec(headersStr);\n return match?.groups?.digits ? parseInt(match.groups.digits, 10) : 0;\n }\n\n /**\n * Handle incoming TCP socket connections and their complete lifecycle.\n *\n * Implements a TCP stream reassembly state machine that buffers chunks until the\n * complete HTTP request is received, then dispatches to `_processRequest`.\n *\n * **Header Phase**: Accumulate chunks, search for `\\r\\n\\r\\n`, parse Content-Length.\n * **Body Phase**: Track totalLength without concatenating until body is complete.\n * **Dispatch**: `Buffer.concat(chunks)` once to produce the final contiguous buffer.\n *\n * @see _looksLikeHttp for non-HTTP early detection\n * @see _rejectOversizedRequest for DoS protection (413 + socket destroy)\n * @see _processRequest for how the assembled buffer is parsed and handled\n */\n private _handleConnection(socket: Socket, requestHandler: RequestHandlerImpl): void {\n const clientAddress = socket.remoteAddress ?? 'unknown';\n const connectionStartTime = Date.now();\n let hasReceivedData = false;\n\n networkLog.log.info(`New visitor from ${clientAddress}`);\n\n // TCP stream reassembly state — mutable, read/written by data handler\n const chunks: Array<Buffer> = [];\n let totalLength = 0;\n let headersParsed = false;\n let expectedBodyLength = 0;\n let headerEndIndex = -1;\n let requestDispatched = false;\n\n socket.on('data', (chunk) => {\n if (!hasReceivedData) {\n hasReceivedData = true;\n const delay = Date.now() - connectionStartTime;\n if (delay > 100) {\n networkLog.log.warn(`Delayed data from ${clientAddress} (${delay}ms connection delay)`);\n }\n }\n\n if (requestDispatched) return;\n\n chunks.push(chunk);\n totalLength += chunk.length;\n\n if (totalLength > this._maxBufferSize) {\n this._rejectOversizedRequest({ socket, clientAddress, totalLength, headersParsed });\n return;\n }\n\n // Header phase: find \\r\\n\\r\\n boundary and parse Content-Length\n if (!headersParsed) {\n const buffer = Buffer.concat(chunks, totalLength);\n headerEndIndex = buffer.indexOf('\\r\\n\\r\\n');\n\n if (headerEndIndex === -1) {\n if (!this._looksLikeHttp(chunks, totalLength, buffer)) {\n requestDispatched = true;\n this._dispatchRequest({ data: buffer, socket, requestHandler, clientAddress });\n }\n return;\n }\n\n headersParsed = true;\n expectedBodyLength = this._parseContentLength(buffer, headerEndIndex);\n const bodyStart = headerEndIndex + 4;\n\n if (buffer.length - bodyStart >= expectedBodyLength) {\n requestDispatched = true;\n this._dispatchRequest({ data: buffer, socket, requestHandler, clientAddress });\n }\n return;\n }\n\n // Body phase: check if accumulated length satisfies Content-Length\n if (totalLength - (headerEndIndex + 4) >= expectedBodyLength) {\n requestDispatched = true;\n this._dispatchRequest({ data: Buffer.concat(chunks, totalLength), socket, requestHandler, clientAddress });\n }\n });\n\n socket.on('error', (error: Error) => {\n networkLog.log.error(`Visitor from ${clientAddress} experienced an error during socket connection: ${error.message}`, error);\n });\n\n socket.on('close', () => {\n const connectionDuration = Date.now() - connectionStartTime;\n\n if (hasReceivedData) {\n networkLog.log.info(`Visitor from ${clientAddress} headed out (${connectionDuration}ms total)`);\n return;\n }\n\n if (connectionDuration < 10) {\n networkLog.log.info(`${clientAddress} quick connectivity check (${connectionDuration}ms) - health probe`);\n } else {\n networkLog.log.warn(`${clientAddress} disconnected without sending data (${connectionDuration}ms) - potential probe`);\n }\n });\n }\n\n async listen(): Promise<void> {\n if (this._isListening) {\n throw new Error('Server is already listening');\n }\n\n return new Promise((resolve, reject) => {\n const requestHandler = new RequestHandlerImpl(this);\n this._server = createServer();\n\n this._setupServer(resolve, reject, requestHandler);\n this._server.listen(this._configuration.port, this._configuration.host);\n });\n }\n\n async close(): Promise<void> {\n if (!this._isListening || !this._server) {\n return;\n }\n\n // Clean up rate limiter resources (intervals, memory)\n if (this._globalRateLimiter) {\n await this._globalRateLimiter.destroy();\n this._globalRateLimiter = undefined;\n }\n\n return new Promise((resolve) => {\n if (!this._server) {\n this._isListening = false; // Probably redundant but just in case\n resolve();\n return;\n }\n\n this._server.close(() => {\n this._isListening = false;\n networkLog.log.warn(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is shutting down - See yinz later`);\n resolve();\n });\n });\n }\n\n status(): {\n isListening: boolean;\n port: number | undefined;\n host: string | undefined;\n } {\n return {\n isListening: this._isListening,\n port: this._configuration.port,\n host: this._configuration.host,\n };\n }\n\n /**\n * Setup automatic graceful shutdown handlers\n */\n private _setupGracefulShutdown(gracefulShutdownTimeout: number): void {\n if (gracefulShutdownTimeout <= 0) {\n return;\n }\n\n // Only setup if no handlers are already registered\n if (process.listenerCount('SIGTERM') === 0 && process.listenerCount('SIGINT') === 0) {\n const shutdown = (signal: string): void => {\n log.info(`🛑 Received ${signal}, shutting down gracefully in ${this._configuration.gracefulShutdownTimeout}...`);\n setTimeout(() => {\n this.close()\n .then(() => {\n log.info('✅ Server shut down gracefully');\n process.exit(0);\n })\n .catch((error) => {\n log.error('❌ Error during graceful shutdown:', error);\n process.exit(1);\n });\n }, gracefulShutdownTimeout);\n };\n\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('SIGINT', () => shutdown('SIGINT'));\n }\n }\n}\n",
7
- "import dayjs from 'dayjs';\nimport type { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport type { InternalContextImpl } from '@typedefs/internal/InternalContextImpl.ts';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.js';\nimport { log } from '@core/utils/log.ts';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\nimport type { InternalRouteRegistry } from '@typedefs/internal/InternalRouteRegistryImpl.js';\nimport type { InternalGlobalHookOptions } from '@typedefs/internal/InternalHookRegistryImpl.js';\n\n/**\n * Handles the complete lifecycle of an HTTP request\n *\n * Flow:\n * 1. Receive parsed context (request + response builders)\n * 2. Match route and execute handlers\n * 3. Build final response in context\n * 4. Context.rawResponse is ready for sending\n */\nexport class RequestHandlerImpl {\n private readonly setup: InternalSetupImpl;\n\n constructor(setup: SetupImpl) {\n this.setup = setup;\n }\n\n /**\n * Process an HTTP request using the provided context\n */\n async handle(context: InternalContextImpl): Promise<void> {\n try {\n // 1. Run beforeRouting hooks (includes CORS if enabled) - stop if any hook returns a value\n if (await this._handleBeforeRoutingHooks(context)) {\n return void 0;\n }\n\n // 2. Match route based on context.request.method + context.request.path\n const matchedRoute = await this._matchRoute(context);\n if (!matchedRoute) return void 0;\n\n // Set route params in the request context\n Object.assign(context.request.params as unknown as Record<string, string>, matchedRoute.params);\n\n const { handler, options } = matchedRoute;\n const { beforeHooks = [], afterHooks = [] } = options;\n\n // 3. Run beforeAll hooks - stop if any hook returns a value\n if (await this._handleBeforeAllHooks(context)) {\n return void 0;\n }\n\n // 4. Run beforeGroup hooks and beforeRoute hooks - stop if any hook returns a value\n // * The before group hooks and beforeRoute hooks are in the same array and ordered on route registration.\n if (await this._handleBeforeHooks(context, beforeHooks)) {\n return void 0;\n }\n\n // 5. Execute route handler.\n // * We are saving the response to a variable because in this case we might not\n // * send a response to the client until after the after hooks since the after hooks might modify the response.\n let routeResponse: unknown = null;\n try {\n routeResponse = await handler(context);\n } catch (handlerError) {\n throw handlerError;\n }\n\n // 6. Run afterRoute hooks and afterGroup hooks\n // * The after group hooks and afterRoute hooks are in the same array and ordered on route registration.\n for (const hook of afterHooks) await hook(context);\n\n // 7. Run afterAll hooks\n const afterAllHooks = this.setup._hooks._afterAll;\n for (const hook of afterAllHooks) {\n // Check if hook should run based on route options\n if (!this._shouldRunHook(hook.options, context.request.path)) {\n continue;\n }\n await hook.handler(context);\n }\n\n // 8. Build response (set content-type, etc.)\n context._response._setBody(routeResponse);\n\n // 9. If this was a HEAD request, remove the body and convert it to a GET request.\n // Im waiting to do this until after the after hooks since the after hooks might modify the response, headers, etc.\n if (context.request.method === 'HEAD') {\n context._response._setBody(null);\n }\n\n // 10. Add default framework headers and parse the body into a string\n // This is done when we call context._response._parseResponseIntoString()\n context._response._parseResponseIntoString();\n\n return void 0;\n } catch (error) {\n // Use the error handler from setup\n await this.handleError(context, error);\n }\n }\n\n /**\n * Handle errors using the user-defined or default error handler\n * The error handler returns a response object that we apply to the context\n */\n private async handleError(context: InternalContextImpl, error: unknown): Promise<void> {\n try {\n // Get the error handler (user-defined or default)\n const errorHandler = this.setup._hooks._onError;\n\n // Call the error handler - it returns a response object\n const errorResponse = await errorHandler(context, error);\n\n // Apply the response to the context\n context._response._setBody(errorResponse);\n\n // Format the response for sending (same as normal flow)\n context._response._parseResponseIntoString();\n context._response._setHeadersIfNotSet({\n Date: dayjs().format('ddd, DD MMM YYYY HH:mm:ss [GMT]'),\n 'Content-Length': context._response._stringBody.split('\\n\\n')[1]?.length.toString() ?? '0',\n });\n } catch (errorHandlerError) {\n // If the error handler itself fails, fall back to basic response\n log.error('Error handler failed, this might be an internal error in the YinzerFlow framework: ', errorHandlerError);\n\n context.response.setStatusCode(500);\n context._response._setBody({\n success: false,\n message: 'Internal Server Error',\n });\n\n // Format the fallback response too\n context._response._parseResponseIntoString();\n context._response._setHeadersIfNotSet({\n Date: dayjs().format('ddd, DD MMM YYYY HH:mm:ss [GMT]'),\n 'Content-Length': context._response._stringBody.split('\\n\\n')[1]?.length.toString() ?? '0',\n });\n }\n }\n\n private async _handleBeforeRoutingHooks(context: InternalContextImpl): Promise<boolean> {\n const beforeRoutingHooks = this.setup._hooks._beforeRouting;\n for (const hook of beforeRoutingHooks) {\n // Check if hook should run based on route options\n if (!this._shouldRunHook(hook.options, context.request.path)) {\n continue;\n }\n\n const result = await hook.handler(context);\n if (result !== undefined) {\n context._response._setBody(result);\n context._response._parseResponseIntoString();\n return true; // Signal that we should stop processing\n }\n }\n return false; // Signal that we should continue processing\n }\n\n private async _matchRoute(context: InternalContextImpl): Promise<InternalRouteRegistry | null> {\n const matchedRoute = this.setup._routeRegistry._findRoute(context.request.method, context.request.path);\n\n if (!matchedRoute) {\n const notFoundResponse = await this.setup._hooks._onNotFound(context);\n context._response._setBody(notFoundResponse);\n context._response._parseResponseIntoString(); // Needed so the YinzerFlow can send the response as a string\n return null; // Signal that no route was found and response is already set\n }\n\n return matchedRoute;\n }\n\n private async _handleBeforeAllHooks(context: InternalContextImpl): Promise<boolean> {\n const beforeAllHooks = this.setup._hooks._beforeAll;\n for (const hook of beforeAllHooks) {\n // Check if hook should run based on route options\n if (!this._shouldRunHook(hook.options, context.request.path)) {\n continue;\n }\n\n const result = await hook.handler(context);\n if (result !== undefined) {\n context._response._setBody(result);\n context._response._parseResponseIntoString();\n return true; // Signal that we should stop processing\n }\n }\n return false; // Signal that we should continue processing\n }\n\n private async _handleBeforeHooks(context: InternalContextImpl, hooks: Array<HandlerCallback>): Promise<boolean> {\n for (const hook of hooks) {\n const result = await hook(context);\n if (result !== undefined) {\n context._response._setBody(result);\n context._response._parseResponseIntoString();\n return true; // Signal that we should stop processing\n }\n }\n return false; // Signal that we should continue processing\n }\n\n /**\n * Determines if a hook should run based on its options and the current request path\n */\n private _shouldRunHook(options: InternalGlobalHookOptions | undefined, requestPath: string): boolean {\n if (!options) {\n return true; // No options means run for all routes\n }\n\n const { routesToInclude, routesToExclude } = options;\n\n // If routesToExclude contains the current path, don't run\n if (routesToExclude.some((pattern) => this._matchesPattern(requestPath, pattern))) {\n return false;\n }\n\n // If routesToInclude is empty, run for all routes (unless excluded above)\n if (routesToInclude.length === 0) {\n return true;\n }\n\n // If routesToInclude has patterns, only run if current path matches one of them\n return routesToInclude.some((pattern) => this._matchesPattern(requestPath, pattern));\n }\n\n /**\n * Simple pattern matching for route filtering\n * Supports basic wildcard patterns like /api/* and exact matches\n */\n private _matchesPattern(path: string, pattern: string): boolean {\n // Exact match\n if (pattern === path) {\n return true;\n }\n\n // Wildcard pattern (e.g., /api/*)\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n return path.startsWith(prefix);\n }\n\n // No match\n return false;\n }\n}\n",
8
- "import dayjs from 'dayjs';\nimport { colors } from '@constants/colors.ts';\nimport { logLevels } from '@constants/log.js';\nimport type { LogLevel } from '@typedefs/constants/log.ts';\nimport type { LoggerConfig } from '@typedefs/public/Logger.ts';\nimport type { Colors } from '@typedefs/constants/colors.js';\n\n/**\n * YinzerFlow Main Logging System 🏗️\n *\n * Simple, clean logging with Pittsburgh personality!\n * - Numeric levels (0=off, 1=error, 2=warn, 3=info)\n * - Smart formatting: Objects get pretty JSON, strings get YinzerFlow colors\n * - Table support: Use log.table() for structured data display\n * - Network logging is separate (see networkLog.ts)\n */\n\nconst LOG_LEVELS = {\n off: 0,\n error: 1,\n warn: 2,\n info: 3,\n} as const;\n\nconst YINZER_PHRASES = {\n positive: [\"n'at!\", 'yinz are good!', \"that's the way!\", 'right on!', \"lookin' good!\", 'way to go!', 'keep it up!'],\n neutral: [\"n'at\", 'yinz know', \"just sayin'\", \"that's how it is\", 'what can ya do', 'it happens'],\n negative: ['aw jeez', \"that ain't right\", 'what a jagoff move', \"that's terrible n'at\", 'somebody messed up', 'this is bad news', 'yinz better fix this'],\n} as const;\n\nconst _getRandomPhrase = (type: 'negative' | 'neutral' | 'positive'): string => {\n const phrases = YINZER_PHRASES[type];\n return phrases[Math.floor(Math.random() * phrases.length)] ?? '';\n};\n\nconst _formatTimestamp = (): string => dayjs().format('YYYY-MM-DD HH:mm:ss.SSS');\n\nconst _logWithStyle = (level: LogLevel, prefix: string, ...args: Array<unknown>): void => {\n const timestamp = _formatTimestamp();\n let bodyColor: Colors = colors.reset;\n if (prefix === 'NETWORK') {\n bodyColor = colors.gray;\n }\n\n if (level === 'error') {\n const logPrefix = `${colors.red}[${prefix}] ❌ [${timestamp}] [ERROR]${colors.reset}`;\n console.error(`${logPrefix}`, `${bodyColor}`, ...args, `${colors.reset} - ${_getRandomPhrase('negative')}`);\n return;\n }\n\n if (level === 'warn') {\n const logPrefix = `${colors.yellow}[${prefix}] ⚠️ [${timestamp}] [WARN]${colors.reset}`;\n console.warn(`${logPrefix}`, `${bodyColor}`, ...args, `${colors.reset} - ${_getRandomPhrase('neutral')}`);\n return;\n }\n\n if (level === 'off') {\n return;\n }\n\n const logPrefix = `${colors.cyan}[${prefix}] ✅ [${timestamp}] [INFO]${colors.reset}`;\n console.info(`${logPrefix}`, `${bodyColor}`, ...args, `${colors.reset} - ${_getRandomPhrase('positive')}`);\n};\n\nconst _logTable = (prefix: string, data: unknown, ...additionalArgs: Array<unknown>): void => {\n const timestamp = _formatTimestamp();\n const logPrefix = `${colors.magenta}[${prefix}] 📊 [${timestamp}] [TABLE]${colors.reset}`;\n\n console.log(`${logPrefix} - ${_getRandomPhrase('positive')}`);\n console.table(data);\n\n if (additionalArgs.length > 0) {\n console.log(`${colors.gray}Additional context:${colors.reset}`, ...additionalArgs);\n }\n};\n\n/**\n * Creates a logger instance with isolated state\n *\n * @param initialConfig - Optional configuration for the logger\n * @returns Logger instance with logging methods\n */\nconst createLogger = (\n initialConfig?: LoggerConfig,\n): {\n info: (...args: Array<unknown>) => void;\n warn: (...args: Array<unknown>) => void;\n error: (...args: Array<unknown>) => void;\n table: (data: unknown, ...additionalArgs: Array<unknown>) => void;\n levels: typeof LOG_LEVELS;\n} => {\n const state = {\n logLevel: initialConfig?.logLevel ?? logLevels.info,\n prefix: initialConfig?.prefix ?? 'YINZER',\n logger: initialConfig?.logger ?? null,\n };\n\n const _getNumericLevel = (level: string): number => (LOG_LEVELS as Record<string, number>)[level] ?? LOG_LEVELS.info;\n\n const info = (...args: Array<unknown>): void => {\n if (_getNumericLevel(state.logLevel) < LOG_LEVELS.info) return;\n\n if (state.logger) {\n state.logger.info(...args);\n return;\n }\n\n _logWithStyle('info', state.prefix, ...args);\n };\n\n const warn = (...args: Array<unknown>): void => {\n if (_getNumericLevel(state.logLevel) < LOG_LEVELS.warn) return;\n\n if (state.logger) {\n state.logger.warn(...args);\n return;\n }\n\n _logWithStyle('warn', state.prefix, ...args);\n };\n\n const error = (...args: Array<unknown>): void => {\n if (_getNumericLevel(state.logLevel) < LOG_LEVELS.error) return;\n\n if (state.logger) {\n state.logger.error(...args);\n return;\n }\n\n _logWithStyle('error', state.prefix, ...args);\n };\n\n const table = (data: unknown, ...additionalArgs: Array<unknown>): void => {\n if (_getNumericLevel(state.logLevel) < LOG_LEVELS.info) return;\n\n if (state.logger) {\n // Custom loggers probably don't have table method, so fallback to info\n state.logger.info('TABLE:', data, ...additionalArgs);\n return;\n }\n\n _logTable(state.prefix, data, ...additionalArgs);\n };\n\n return {\n info,\n warn,\n error,\n table,\n levels: LOG_LEVELS,\n };\n};\n\n// Default shared logger instance\nexport const log = createLogger();\n\n// Factory for creating custom logger instances\nexport { createLogger };\n",
5
+ "import { createServer } from 'net';\nimport type { Socket } from 'net';\n\nimport { RequestHandlerImpl } from '@core/execution/RequestHandlerImpl.ts';\nimport { ContextImpl } from '@core/execution/ContextImpl.ts';\nimport { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport { createLogger, loggerBrand } from '@core/utils/log.ts';\nimport type { ServerOptions } from '@typedefs/public/Configuration.js';\nimport { accessLogBaseConfig, getStatusEmoji } from '@core/utils/accessLog.ts';\nimport { _createGlobalRateLimitHook } from '@core/modules/rateLimit/rateLimithooks.ts';\nimport { RateLimiter } from '@core/modules/rateLimit/RateLimiter.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\nimport { cookieParserHook } from '@core/modules/cookieParser/cookieParserHooks.ts';\nimport { CookieParserConfig } from '@core/modules/cookieParser/CookieParserConfig.ts';\nimport { corsHook } from '@core/modules/cors/corsHooks.ts';\nimport { CorsConfig } from '@core/modules/cors/CorsConfig.ts';\nimport type { InternalCorsEnabledOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { DiagnosticsMonitor } from '@core/modules/diagnostics/DiagnosticsMonitor.ts';\nimport { _sanitizeLogField } from '@core/utils/sanitize.ts';\n\n/**\n * Maximum overhead allowance for HTTP headers on top of body parser limits (64KB).\n *\n * The TCP buffer limit is `max(bodyParserLimits) + maxHeaderOverhead` because the raw\n * TCP stream contains both headers and body. The body portion is bounded by the user's\n * body parser config (json.maxSize, urlEncoded.maxSize, fileUploads.maxTotalSize), but\n * we also need room for the request line, headers, and the blank line separator.\n *\n * 64KB is generous — most HTTP headers are 2-8KB (nginx defaults to 8KB max). This\n * leaves plenty of room for large cookie headers or verbose auth tokens while still\n * rejecting obviously malicious oversized requests at the TCP level before parsing.\n */\nconst maxHeaderOverhead = 65_536;\n\n/**\n * Main YinzerFlow application class for building HTTP servers.\n *\n * YinzerFlow is a lightweight, high-performance HTTP server framework built on Node.js\n * that provides an elegant API for routing, middleware, and request handling. It extends\n * the Setup interface to provide all route configuration capabilities plus server management.\n *\n * ## Key Features\n *\n * - **High Performance**: Built on Node.js net module for maximum performance\n * - **Type Safety**: Full TypeScript support with generics for type-safe contexts\n * - **Route Groups**: Nested route organization with shared hooks and middleware\n * - **Global Hooks**: beforeAll/afterAll hooks for cross-cutting concerns\n * - **State Management**: Request-scoped state for sharing data between middleware\n * - **Graceful Shutdown**: Automatic graceful shutdown handling\n * - **Logging**: Built-in logging with custom logger support\n * - **Network Logging**: Request/response logging with performance metrics\n *\n * ## Basic Usage\n *\n * @example\n * ```typescript\n * import { YinzerFlow } from 'yinzerflow';\n *\n * // Create app with basic configuration\n * const app = new YinzerFlow({ port: 3000 });\n *\n * // Register routes\n * app.get('/api/users', async (ctx) => {\n * return { users: ['John', 'Jane'] };\n * });\n *\n * app.post('/api/users', async (ctx) => {\n * const { name, email } = ctx.request.body;\n * return { message: 'User created', name, email };\n * });\n *\n * // Set up global hooks\n * app.beforeAll([\n * async (ctx) => {\n * ctx.state.requestId = generateRequestId();\n * ctx.state.startTime = Date.now();\n * }\n * ]);\n *\n * app.afterAll([\n * async (ctx) => {\n * const processingTime = Date.now() - ctx.state.startTime;\n * ctx.response.addHeaders({\n * 'X-Processing-Time': `${processingTime}ms`,\n * 'X-Request-ID': ctx.state.requestId\n * });\n * }\n * ]);\n *\n * // Create route groups\n * app.group('/api/v1', (api) => {\n * api.group('/admin', (admin) => {\n * admin.get('/users', async (ctx) => {\n * return { adminUsers: ['Admin1', 'Admin2'] };\n * });\n * });\n * });\n *\n * // Start the server\n * app.listen();\n * ```\n *\n * ## Advanced Configuration\n *\n * @example\n * ```typescript\n * // Advanced configuration with custom logging\n * const app = new YinzerFlow({\n * port: 8080,\n * host: '0.0.0.0',\n * logging: {\n * level: 'debug',\n * personality: true,\n * requests: true,\n * logger: {\n * info: (message, ...args) => console.log(`[INFO] ${message}`, ...args),\n * warn: (message, ...args) => console.warn(`[WARN] ${message}`, ...args),\n * error: (message, ...args) => console.error(`[ERROR] ${message}`, ...args),\n * debug: (message, ...args) => console.debug(`[DEBUG] ${message}`, ...args)\n * }\n * }\n * });\n *\n * // Custom error handling\n * app.onError(async (ctx, error) => {\n * ctx.response.setStatusCode(500);\n * return {\n * error: 'Internal server error',\n * message: process.env.NODE_ENV === 'production' ? 'Something went wrong' : error.message\n * };\n * });\n *\n * // Custom not-found handling\n * app.onNotFound(async (ctx) => {\n * ctx.response.setStatusCode(404);\n * return {\n * error: 'Not found',\n * path: ctx.request.path,\n * availableEndpoints: ['/api/users', '/api/posts', '/health']\n * };\n * });\n * ```\n *\n * ## Server Lifecycle\n *\n * 1. **Initialization**: Constructor sets up logging and configuration\n * 2. **Route Setup**: Register routes, hooks, and middleware\n * 3. **Server Start**: Call `listen()` to start accepting connections\n * 4. **Request Processing**: Handle incoming HTTP requests\n * 5. **Graceful Shutdown**: Automatic cleanup on process termination\n *\n * @see {@link SetupImpl} for route configuration capabilities\n * @see {@link ServerOptions} for configuration options\n * @see {@link ContextImpl} for request context implementation\n * @see {@link RequestHandlerImpl} for request processing\n */\nexport class YinzerFlow extends SetupImpl {\n private _isListening = false;\n private _server?: ReturnType<typeof createServer>;\n private _globalRateLimiter?: RateLimiter | undefined;\n private _diagnostics?: DiagnosticsMonitor | undefined;\n private readonly _maxBufferSize: number;\n private _accessLog?: ReturnType<typeof createLogger>;\n private _accessLogEnabled = false;\n\n constructor(configuration?: ServerOptions) {\n super(configuration);\n\n // Pre-compute max buffer size from body parser limits (immutable after construction)\n this._maxBufferSize =\n Math.max(\n this._configuration.bodyParser.json.maxSize,\n this._configuration.bodyParser.urlEncoded.maxSize,\n this._configuration.bodyParser.fileUploads.maxTotalSize,\n ) + maxHeaderOverhead;\n\n this._configureLogging();\n\n // Setup global rate limiting, if there is none provided, it will be enabled by default. We need to set the confgratin before the conditional since it is defaulted on and users might not pass it in the configuration.\n const rateLimitConfig = new RateLimitConfig(configuration?.rateLimit, this._log);\n if (configuration?.rateLimit?.enabled) {\n this._globalRateLimiter = new RateLimiter(rateLimitConfig);\n const onRateLimitHit = this._diagnostics ? (ip: string, path: string): void => this._diagnostics?.onRateLimitHit(ip, path) : undefined;\n const hook = _createGlobalRateLimitHook(this._globalRateLimiter, onRateLimitHit);\n this.beforeAll([hook]);\n }\n\n // Setup cookie parser, if enabled, since it is deisabled by default we can set the configuration after the conditional\n if (configuration?.cookieParser?.enabled) {\n const cookieParserConfig = new CookieParserConfig(configuration.cookieParser, this._log);\n const cookieParserHookFunc = cookieParserHook(cookieParserConfig.config);\n this.beforeAll([cookieParserHookFunc]);\n }\n\n // Setup CORS, if enabled, as a beforeRouting hook\n if (configuration?.cors?.enabled) {\n const corsConfig = CorsConfig.merge(configuration.cors);\n CorsConfig.validate(corsConfig);\n // Type assertion safe here: validate() throws if config is disabled\n const corsHookFunc = corsHook(corsConfig as InternalCorsEnabledOptions);\n this.beforeRouting([corsHookFunc]);\n }\n\n // Setup automatic graceful shutdown if enabled\n if (this._configuration.gracefulShutdownTimeout) {\n const gracefulShutdownTimeout = _convertTimeToMs(this._configuration.gracefulShutdownTimeout);\n if (gracefulShutdownTimeout > 0) {\n this._setupGracefulShutdown(gracefulShutdownTimeout);\n }\n }\n }\n\n /** Public accessor for this instance's logger. */\n get log(): typeof this._log {\n return this._log;\n }\n\n /**\n * Configure all three logging channels: app logger, access logs, diagnostics.\n *\n * Creates a per-instance logger (D1 fix — no more singleton mutation).\n * Uses Symbol brand to detect framework loggers (D2/2.4 fix — replaces duck-typed `_state`).\n */\n private _configureLogging(): void {\n const loggingConfig = this._configuration.logging;\n\n // C2 fix: If a branded logger was provided, extract its underlying output sink\n // instead of mutating the user's branded logger state. Mutating caused cross-instance\n // side effects when the same branded logger was shared between YinzerFlow instances.\n // The per-instance this._log handles its own formatting (prefix, personality).\n let loggerSink = loggingConfig.logger;\n if (loggerSink && loggerBrand in (loggerSink as unknown as Record<string | symbol, unknown>)) {\n const brandedState = (loggerSink as unknown as Record<string | symbol, unknown>)[loggerBrand] as { logger?: typeof loggerSink };\n loggerSink = brandedState.logger ?? undefined;\n }\n\n // Create per-instance logger (D1 fix: no more Object.assign to module-level singleton)\n this._log = createLogger({\n level: loggingConfig.level,\n prefix: loggingConfig.prefix,\n personality: loggingConfig.personality,\n logger: loggerSink,\n });\n\n // Thread logger into subsystems\n this._hooks.setLogger(this._log);\n\n // C1 fix: Create per-instance access log (no more module-level singleton)\n if (loggingConfig.requests) {\n this._accessLog = createLogger({\n ...accessLogBaseConfig,\n level: 'info',\n logger: loggingConfig.accessLogger,\n });\n this._accessLogEnabled = true;\n }\n\n // Setup diagnostics monitor if any thresholds are configured\n const diagnostics = new DiagnosticsMonitor(loggingConfig.diagnostics, loggingConfig.personality);\n if (diagnostics.hasAnyEnabled()) {\n this._diagnostics = diagnostics;\n this._diagnostics.start();\n }\n }\n\n /**\n * Setup server with all event listeners\n */\n private _setupServer(resolve: () => void, reject: (error: Error) => void, requestHandler: RequestHandlerImpl): void {\n if (!this._server) return;\n\n this._server.on('error', (error: Error) => {\n this._log.error(`YinzerFlow server error at ${this._configuration.host}:${this._configuration.port} - ${error.message}`);\n // Clean up the failed server to prevent handle leaks on re-listen\n this._server?.close();\n delete this._server;\n reject(error);\n });\n\n this._server.on('listening', () => {\n this._isListening = true;\n this._log.info(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is up and running`);\n resolve();\n });\n\n this._server.on('connection', (socket) => {\n this._handleConnection(socket, requestHandler);\n });\n }\n\n /**\n * Process incoming request data\n */\n private async _processRequest({\n data,\n socket,\n requestHandler,\n clientAddress,\n }: {\n data: Buffer;\n socket: Socket;\n requestHandler: RequestHandlerImpl;\n clientAddress: string;\n }): Promise<void> {\n const startTime = Date.now();\n\n const context = new ContextImpl(data, this, clientAddress);\n\n await requestHandler.handle(context);\n\n // Guard against writing to a socket that was destroyed (client disconnect, timeout, etc.)\n if (!socket.destroyed) {\n socket.write(context._response._stringBody);\n socket.end();\n }\n\n const processingTime = Date.now() - startTime;\n\n // Only compute byte length and build log strings when something will consume them\n if (this._accessLogEnabled || this._diagnostics) {\n const responseBytes = Buffer.byteLength(context._response._stringBody, 'utf8');\n\n // H3 fix: Sanitize attacker-controlled fields once for both channels\n const safeMethod = _sanitizeLogField(context.request.method);\n const safePath = _sanitizeLogField(context.request.path);\n\n // Access log — one nginx-style line per request (C1: per-instance, H4: separate guard)\n if (this._accessLogEnabled) {\n this._accessLog?.info(\n `${getStatusEmoji(context._response._statusCode)} ${clientAddress} \"${safeMethod} ${safePath} ${context.request.protocol}\" ${context._response._statusCode} ${responseBytes}bytes \"${_sanitizeLogField(context.request.headers.referer ?? '-')}\" \"${_sanitizeLogField(context.request.headers['user-agent'] ?? '-')}\" ${processingTime}ms`,\n );\n }\n\n // Diagnostics — check thresholds after response is sent (receives pre-sanitized values)\n this._diagnostics?.checkRequest({\n duration: processingTime,\n reqBytes: data.length,\n resBytes: responseBytes,\n method: safeMethod,\n path: safePath,\n });\n }\n }\n\n /**\n * Handle errors from request processing — log and destroy socket\n */\n private _handleRequestError(error: unknown, clientAddress: string, socket: Socket): void {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n this._log.error(`Request processing error from ${clientAddress}: ${errorMessage}`, error);\n if (!socket.destroyed) {\n socket.destroy();\n }\n }\n\n /**\n * Dispatch assembled request buffer to the request processor.\n * Wraps `_processRequest` with the standard error catch handler.\n */\n private _dispatchRequest({\n data,\n socket,\n requestHandler,\n clientAddress,\n }: {\n data: Buffer;\n socket: Socket;\n requestHandler: RequestHandlerImpl;\n clientAddress: string;\n }): void {\n this._processRequest({ data, socket, requestHandler, clientAddress }).catch((error: unknown) => this._handleRequestError(error, clientAddress, socket));\n }\n\n /**\n * Reject an oversized request with HTTP 413 (if headers were parsed) and destroy the socket.\n * Logs the current body parser limits for debugging.\n */\n private _rejectOversizedRequest({\n socket,\n clientAddress,\n totalLength,\n headersParsed,\n }: {\n socket: Socket;\n clientAddress: string;\n totalLength: number;\n headersParsed: boolean;\n }): void {\n if (headersParsed) {\n const errorBody = JSON.stringify({\n error: 'Payload too large',\n maxSize: this._maxBufferSize,\n received: totalLength,\n });\n socket.write(\n `HTTP/1.1 413 Payload Too Large\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(errorBody, 'utf8')}\\r\\n` +\n `Connection: close\\r\\n\\r\\n${errorBody}`,\n );\n }\n this._log.warn(\n `Request from ${clientAddress} exceeded maximum buffer size (${totalLength} > ${this._maxBufferSize} bytes). ` +\n `Current limits: json=${this._configuration.bodyParser.json.maxSize}, ` +\n `urlEncoded=${this._configuration.bodyParser.urlEncoded.maxSize}, ` +\n `fileUploads=${this._configuration.bodyParser.fileUploads.maxTotalSize}`,\n );\n socket.destroy();\n }\n\n /**\n * Check if buffered data looks like the start of an HTTP request.\n *\n * First checks the first byte against known HTTP method start characters\n * (G, P, D, H, O), then validates the first 8 bytes against the full method pattern.\n * Returns `false` for non-HTTP traffic so it can be dispatched for a graceful error.\n */\n private _looksLikeHttp(chunks: Array<Buffer>, totalLength: number, buffer: Buffer): boolean {\n if (totalLength >= 1) {\n const firstByte = chunks[0]?.[0] ?? 0;\n // cspell:disable-next-line\n // HTTP methods start with: G(ET)=0x47, P(OST/UT/ATCH)=0x50, D(ELETE)=0x44, H(EAD)=0x48, O(PTIONS)=0x4f\n if (firstByte !== 0x47 && firstByte !== 0x50 && firstByte !== 0x44 && firstByte !== 0x48 && firstByte !== 0x4f) {\n return false;\n }\n }\n if (totalLength >= 8) {\n const start = buffer.subarray(0, 8).toString();\n if (!/^(?:GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\\s/.test(start)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Extract Content-Length value from raw HTTP headers.\n * Returns 0 if the header is missing (e.g. GET requests with no body).\n */\n private _parseContentLength(buffer: Buffer, headerEndIndex: number): number {\n const headersStr = buffer.subarray(0, headerEndIndex).toString();\n const match = /content-length:\\s*(?<digits>\\d+)/i.exec(headersStr);\n return match?.groups?.digits ? parseInt(match.groups.digits, 10) : 0;\n }\n\n /**\n * Handle incoming TCP socket connections and their complete lifecycle.\n *\n * Implements a TCP stream reassembly state machine that buffers chunks until the\n * complete HTTP request is received, then dispatches to `_processRequest`.\n *\n * **Header Phase**: Accumulate chunks, search for `\\r\\n\\r\\n`, parse Content-Length.\n * **Body Phase**: Track totalLength without concatenating until body is complete.\n * **Dispatch**: `Buffer.concat(chunks)` once to produce the final contiguous buffer.\n *\n * @see _looksLikeHttp for non-HTTP early detection\n * @see _rejectOversizedRequest for DoS protection (413 + socket destroy)\n * @see _processRequest for how the assembled buffer is parsed and handled\n */\n private _handleConnection(socket: Socket, requestHandler: RequestHandlerImpl): void {\n const clientAddress = socket.remoteAddress ?? 'unknown';\n const connectionStartTime = Date.now();\n let hasReceivedData = false;\n\n this._log.debug(`New connection from ${clientAddress}`);\n\n // TCP stream reassembly state — mutable, read/written by data handler\n const chunks: Array<Buffer> = [];\n let totalLength = 0;\n let headersParsed = false;\n let expectedBodyLength = 0;\n let headerEndIndex = -1;\n let requestDispatched = false;\n\n socket.on('data', (chunk) => {\n if (!hasReceivedData) {\n hasReceivedData = true;\n const delay = Date.now() - connectionStartTime;\n if (delay > 100) {\n this._log.debug(`Delayed data from ${clientAddress} (${delay}ms connection delay)`);\n }\n }\n\n if (requestDispatched) return;\n\n chunks.push(chunk);\n totalLength += chunk.length;\n\n if (totalLength > this._maxBufferSize) {\n this._rejectOversizedRequest({ socket, clientAddress, totalLength, headersParsed });\n return;\n }\n\n // Header phase: find \\r\\n\\r\\n boundary and parse Content-Length\n if (!headersParsed) {\n const buffer = Buffer.concat(chunks, totalLength);\n headerEndIndex = buffer.indexOf('\\r\\n\\r\\n');\n\n if (headerEndIndex === -1) {\n if (!this._looksLikeHttp(chunks, totalLength, buffer)) {\n requestDispatched = true;\n this._dispatchRequest({ data: buffer, socket, requestHandler, clientAddress });\n }\n return;\n }\n\n headersParsed = true;\n expectedBodyLength = this._parseContentLength(buffer, headerEndIndex);\n const bodyStart = headerEndIndex + 4;\n\n if (buffer.length - bodyStart >= expectedBodyLength) {\n requestDispatched = true;\n this._dispatchRequest({ data: buffer, socket, requestHandler, clientAddress });\n }\n return;\n }\n\n // Body phase: check if accumulated length satisfies Content-Length\n if (totalLength - (headerEndIndex + 4) >= expectedBodyLength) {\n requestDispatched = true;\n this._dispatchRequest({ data: Buffer.concat(chunks, totalLength), socket, requestHandler, clientAddress });\n }\n });\n\n socket.on('error', (error: Error) => {\n this._log.error(`Socket error from ${clientAddress}: ${error.message}`, error);\n });\n\n socket.on('close', () => {\n const connectionDuration = Date.now() - connectionStartTime;\n\n if (hasReceivedData) {\n this._log.debug(`Connection closed from ${clientAddress} (${connectionDuration}ms total)`);\n return;\n }\n\n if (connectionDuration < 10) {\n this._log.debug(`${clientAddress} quick connectivity check (${connectionDuration}ms) - health probe`);\n } else {\n this._log.debug(`${clientAddress} disconnected without sending data (${connectionDuration}ms) - potential probe`);\n }\n });\n }\n\n async listen(): Promise<void> {\n if (this._isListening) {\n throw new Error('Server is already listening');\n }\n\n return new Promise((resolve, reject) => {\n const requestHandler = new RequestHandlerImpl(this);\n this._server = createServer();\n\n this._setupServer(resolve, reject, requestHandler);\n this._server.listen(this._configuration.port, this._configuration.host);\n });\n }\n\n async close(): Promise<void> {\n if (!this._isListening || !this._server) {\n return;\n }\n\n // Clean up rate limiter resources (intervals, memory)\n if (this._globalRateLimiter) {\n await this._globalRateLimiter.destroy();\n this._globalRateLimiter = undefined;\n }\n\n // Clean up diagnostics timers\n if (this._diagnostics) {\n this._diagnostics.destroy();\n this._diagnostics = undefined;\n }\n\n return new Promise((resolve) => {\n if (!this._server) {\n this._isListening = false; // Probably redundant but just in case\n resolve();\n return;\n }\n\n this._server.close(() => {\n this._isListening = false;\n this._log.warn(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is shutting down`);\n resolve();\n });\n });\n }\n\n status(): {\n isListening: boolean;\n port: number | undefined;\n host: string | undefined;\n } {\n return {\n isListening: this._isListening,\n port: this._configuration.port,\n host: this._configuration.host,\n };\n }\n\n /**\n * Setup automatic graceful shutdown handlers\n */\n private _setupGracefulShutdown(gracefulShutdownTimeout: number): void {\n if (gracefulShutdownTimeout <= 0) {\n return;\n }\n\n // Only setup if no handlers are already registered\n if (process.listenerCount('SIGTERM') === 0 && process.listenerCount('SIGINT') === 0) {\n const shutdown = (signal: string): void => {\n this._log.info(`🛑 Received ${signal}, shutting down gracefully in ${this._configuration.gracefulShutdownTimeout}...`);\n setTimeout(() => {\n this.close()\n .then(() => {\n this._log.info('✅ Server shut down gracefully');\n process.exit(0);\n })\n .catch((error) => {\n this._log.error('❌ Error during graceful shutdown:', error);\n process.exit(1);\n });\n }, gracefulShutdownTimeout);\n };\n\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('SIGINT', () => shutdown('SIGINT'));\n }\n }\n}\n",
6
+ "import type { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport type { InternalContextImpl } from '@typedefs/internal/InternalContextImpl.ts';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.js';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\nimport type { InternalRouteRegistry } from '@typedefs/internal/InternalRouteRegistryImpl.js';\nimport type { InternalGlobalHookOptions } from '@typedefs/internal/InternalHookRegistryImpl.js';\n\n/**\n * Handles the complete lifecycle of an HTTP request\n *\n * Flow:\n * 1. Receive parsed context (request + response builders)\n * 2. Match route and execute handlers\n * 3. Build final response in context\n * 4. Context.rawResponse is ready for sending\n */\nexport class RequestHandlerImpl {\n private readonly setup: InternalSetupImpl;\n\n constructor(setup: SetupImpl) {\n this.setup = setup;\n }\n\n /**\n * Process an HTTP request using the provided context\n */\n async handle(context: InternalContextImpl): Promise<void> {\n try {\n // 1. Run beforeRouting hooks (includes CORS if enabled) - stop if any hook returns a value\n if (await this._handleBeforeRoutingHooks(context)) {\n return void 0;\n }\n\n // 2. Match route based on context.request.method + context.request.path\n const matchedRoute = await this._matchRoute(context);\n if (!matchedRoute) return void 0;\n\n // Set route params in the request context\n Object.assign(context.request.params as unknown as Record<string, string>, matchedRoute.params);\n\n const { handler, options } = matchedRoute;\n const { beforeHooks = [], afterHooks = [] } = options;\n\n // 3. Run beforeAll hooks - stop if any hook returns a value\n if (await this._handleBeforeAllHooks(context)) {\n return void 0;\n }\n\n // 4. Run beforeGroup hooks and beforeRoute hooks - stop if any hook returns a value\n // * The before group hooks and beforeRoute hooks are in the same array and ordered on route registration.\n if (await this._handleBeforeHooks(context, beforeHooks)) {\n return void 0;\n }\n\n // 5. Execute route handler\n const routeResponse = await handler(context);\n\n // 6. Set body BEFORE after-hooks so it's preserved even if a hook throws\n context._response._setBody(routeResponse);\n\n // 7. Run afterRoute hooks and afterGroup hooks\n // * The after group hooks and afterRoute hooks are in the same array and ordered on route registration.\n for (const hook of afterHooks) await hook(context);\n\n // 8. Run afterAll hooks\n const afterAllHooks = this.setup._hooks._afterAll;\n for (const hook of afterAllHooks) {\n if (!this._shouldRunHook(hook.options, context.request.path)) {\n continue;\n }\n await hook.handler(context);\n }\n\n // 9. If this was a HEAD request, remove the body.\n // Waiting until after hooks since they might modify the response, headers, etc.\n if (context.request.method === 'HEAD') {\n context._response._setBody(null);\n }\n\n // 10. Add default framework headers and parse the body into a string\n context._response._parseResponseIntoString();\n\n return void 0;\n } catch (error) {\n // Use the error handler from setup\n await this.handleError(context, error);\n }\n }\n\n /**\n * Handle errors using the user-defined or default error handler\n * The error handler returns a response object that we apply to the context\n */\n private async handleError(context: InternalContextImpl, error: unknown): Promise<void> {\n try {\n // Get the error handler (user-defined or default)\n const errorHandler = this.setup._hooks._onError;\n\n // Call the error handler - it returns a response object\n const errorResponse = await errorHandler(context, error);\n\n // Apply the response to the context\n context._response._setBody(errorResponse);\n\n // Format the response for sending (same as normal flow)\n context._response._parseResponseIntoString();\n } catch (errorHandlerError) {\n // If the error handler itself fails, fall back to basic response\n this.setup._log.error('Your custom error handler threw an error. Check your onError() handler for bugs: ', errorHandlerError);\n\n context.response.setStatusCode(500);\n context._response._setBody({\n success: false,\n message: 'Internal Server Error',\n });\n\n // Format the fallback response too\n context._response._parseResponseIntoString();\n }\n }\n\n async _handleBeforeRoutingHooks(context: InternalContextImpl): Promise<boolean> {\n const beforeRoutingHooks = this.setup._hooks._beforeRouting;\n for (const hook of beforeRoutingHooks) {\n // Check if hook should run based on route options\n if (!this._shouldRunHook(hook.options, context.request.path)) {\n continue;\n }\n\n const result = await hook.handler(context);\n if (this._applyHookResponse(result, context)) return true;\n }\n return false;\n }\n\n async _matchRoute(context: InternalContextImpl): Promise<InternalRouteRegistry | null> {\n const matchedRoute = this.setup._routeRegistry._findRoute(context.request.method, context.request.path);\n\n if (!matchedRoute) {\n const notFoundResponse = await this.setup._hooks._onNotFound(context);\n context._response._setBody(notFoundResponse);\n context._response._parseResponseIntoString(); // Needed so the YinzerFlow can send the response as a string\n return null; // Signal that no route was found and response is already set\n }\n\n return matchedRoute;\n }\n\n async _handleBeforeAllHooks(context: InternalContextImpl): Promise<boolean> {\n const beforeAllHooks = this.setup._hooks._beforeAll;\n for (const hook of beforeAllHooks) {\n // Check if hook should run based on route options\n if (!this._shouldRunHook(hook.options, context.request.path)) {\n continue;\n }\n\n const result = await hook.handler(context);\n if (this._applyHookResponse(result, context)) return true;\n }\n return false;\n }\n\n async _handleBeforeHooks(context: InternalContextImpl, hooks: Array<HandlerCallback>): Promise<boolean> {\n for (const hook of hooks) {\n const result = await hook(context);\n if (this._applyHookResponse(result, context)) return true;\n }\n return false;\n }\n\n /** Apply a hook's return value as the response. Returns true if hook short-circuited. */\n _applyHookResponse(result: unknown, context: InternalContextImpl): boolean {\n if (result === undefined) return false;\n context._response._setBody(result);\n context._response._parseResponseIntoString();\n return true;\n }\n\n /**\n * Determines if a hook should run based on its options and the current request path\n */\n _shouldRunHook(options: InternalGlobalHookOptions | undefined, requestPath: string): boolean {\n if (!options) {\n return true; // No options means run for all routes\n }\n\n const { routesToInclude = [], routesToExclude = [] } = options;\n\n // If routesToExclude contains the current path, don't run\n if (routesToExclude.some((pattern) => this._matchesPattern(requestPath, pattern))) {\n return false;\n }\n\n // If routesToInclude is empty, run for all routes (unless excluded above)\n if (routesToInclude.length === 0) {\n return true;\n }\n\n // If routesToInclude has patterns, only run if current path matches one of them\n return routesToInclude.some((pattern) => this._matchesPattern(requestPath, pattern));\n }\n\n /**\n * Simple pattern matching for route filtering\n * Supports basic wildcard patterns like /api/* and exact matches\n */\n _matchesPattern(path: string, pattern: string): boolean {\n // Exact match\n if (pattern === path) {\n return true;\n }\n\n // Wildcard pattern (e.g., /api/*) — must match segment boundary\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -1); // Keep trailing slash: \"/api/\" from \"/api/*\"\n return path.startsWith(prefix) || path === pattern.slice(0, -2);\n }\n\n // No match\n return false;\n }\n}\n",
9
7
  "// Shared ANSI Color codes (reused from main log system)\nexport const colors = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[96m', // Cyan for info\n yellow: '\\x1b[93m', // Yellow for warn\n red: '\\x1b[91m', // Red for error\n green: '\\x1b[92m', // Green for success\n magenta: '\\x1b[95m', // Magenta for performance\n gray: '\\x1b[90m', // Gray for network logs\n} as const;\n",
10
- "/**\n * YinzerFlow Logging Levels\n *\n * String-based logging levels for intuitive configuration:\n * - 'off': No logging at all. Mainly used for network logging internally, but feel free to use it for other purposes.\n * - 'error': Only errors\n * - 'warn': Warnings and errors (includes security warnings, slow requests)\n * - 'info': Info, warnings, and errors (standard application logging)\n *\n * Network logging is controlled separately via boolean networkLogging config.\n */\nexport const logLevels = {\n off: 'off',\n error: 'error',\n warn: 'warn',\n info: 'info',\n} as const;\n",
11
- "import type { InternalJsonParserOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { log } from '@core/utils/log.ts';\n\n/**\n * Dangerous prototype properties that can lead to prototype pollution\n */\nconst DANGEROUS_PROPERTIES = ['__proto__', 'constructor', 'prototype'];\n\n/**\n * Parse JSON request body with comprehensive security protections\n *\n * Security Features:\n * - Request size validation\n * - Nesting depth limits to prevent stack overflow\n * - Prototype pollution protection\n * - Memory exhaustion protection (max keys, string length, array length)\n * - Proper error handling with security context\n */\nexport const parseApplicationJson = (body: string, config: InternalJsonParserOptions): unknown => {\n // Handle empty strings, whitespace, and null characters\n if (!body || !body.trim() || body.trim() === '\\0') {\n return undefined;\n }\n\n // SECURITY: Validate request body size to prevent DoS attacks\n const bodySize = Buffer.byteLength(body, 'utf8');\n if (bodySize > config.maxSize) {\n log.warn('[SECURITY] JSON request body too large', {\n size: bodySize,\n limit: config.maxSize,\n sizeMB: Math.round(bodySize / 1024 / 1024),\n });\n throw new Error(`Request body too large: ${bodySize} bytes exceeds limit of ${config.maxSize} bytes`);\n }\n\n let parsedData: unknown = null; // Initialize to fix linting\n\n try {\n // First pass: Parse JSON to get structure\n parsedData = JSON.parse(body);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid JSON syntax: ${message}`);\n }\n\n // SECURITY: Validate parsed data structure for security vulnerabilities\n try {\n _validateJsonStructure(parsedData, config, 1); // Start at depth 1 (root level)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`JSON security validation failed: ${message}`);\n }\n\n return parsedData;\n};\n\n/**\n * Validate primitive values (strings, numbers, etc.)\n */\nconst _validatePrimitive = (data: unknown, config: InternalJsonParserOptions): void => {\n if (typeof data === 'string' && data.length > config.maxStringLength) {\n throw new Error(`String too long: ${data.length} characters exceeds limit of ${config.maxStringLength}`);\n }\n};\n\n/**\n * Validate array structure and elements\n */\nconst _validateArray = (data: Array<unknown>, config: InternalJsonParserOptions, depth: number): void => {\n // SECURITY: Check array length to prevent memory exhaustion\n if (data.length > config.maxArrayLength) {\n throw new Error(`Array too large: ${data.length} elements exceeds limit of ${config.maxArrayLength}`);\n }\n\n // Recursively validate array elements\n for (const item of data) {\n _validateJsonStructure(item, config, depth + 1);\n }\n};\n\n/**\n * Validate object keys for security issues\n */\nconst _validateObjectKeys = (keys: Array<string>, config: InternalJsonParserOptions): void => {\n // SECURITY: Check number of keys to prevent memory exhaustion\n if (keys.length > config.maxKeys) {\n throw new Error(`Object has too many keys: ${keys.length} exceeds limit of ${config.maxKeys}`);\n }\n\n // SECURITY: Check for prototype pollution attempts\n if (!config.allowPrototypeProperties) {\n for (const key of keys) {\n if (DANGEROUS_PROPERTIES.includes(key)) {\n log.warn('[SECURITY] Prototype pollution attempt detected', {\n property: key,\n dangerousProperties: DANGEROUS_PROPERTIES,\n });\n throw new Error(`Prototype pollution attempt detected: property '${key}' is not allowed`);\n }\n }\n }\n};\n\n/**\n * Validate object properties and values\n */\nconst _validateObjectProperties = (data: Record<string, unknown>, config: InternalJsonParserOptions, depth: number): void => {\n const keys = Object.keys(data);\n\n for (const key of keys) {\n // SECURITY: Check key length\n if (key.length > config.maxStringLength) {\n throw new Error(`Object key too long: '${key.substring(0, 50)}...' exceeds limit of ${config.maxStringLength}`);\n }\n\n const value = data[key];\n\n // SECURITY: Check string value length\n if (typeof value === 'string' && value.length > config.maxStringLength) {\n throw new Error(`String value too long: property '${key}' has ${value.length} characters, exceeds limit of ${config.maxStringLength}`);\n }\n\n // Recursively validate nested structures\n _validateJsonStructure(value, config, depth + 1);\n }\n};\n\n/**\n * Recursively validate JSON structure for security vulnerabilities\n *\n * @param data - Data to validate\n * @param config - Security configuration\n * @param depth - Current nesting depth (starts at 1 for root level)\n */\nconst _validateJsonStructure = (data: unknown, config: InternalJsonParserOptions, depth: number): void => {\n // SECURITY: Check nesting depth to prevent stack overflow attacks\n if (depth > config.maxDepth) {\n log.warn('[SECURITY] JSON nesting too deep - potential stack overflow attack', {\n currentDepth: depth,\n maxDepth: config.maxDepth,\n });\n throw new Error(`JSON nesting too deep: current depth ${depth} exceeds maximum depth of ${config.maxDepth}`);\n }\n\n // Handle null and primitives\n if (data === null || typeof data !== 'object') {\n _validatePrimitive(data, config);\n return;\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n _validateArray(data, config, depth);\n return;\n }\n\n // Handle objects\n const keys = Object.keys(data);\n _validateObjectKeys(keys, config);\n _validateObjectProperties(data as Record<string, unknown>, config, depth);\n};\n",
12
- "import type { InternalContentDisposition, InternalFileUpload, InternalMultipartFormData } from '@typedefs/internal/InternalRequestImpl.ts';\nimport type { InternalFileUploadOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { log } from '@core/utils/log.ts';\n\n/**\n * Split a multipart section into headers and content\n *\n * @example\n * ```ts\n * splitMultipartSection('Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\\r\\nContent-Type: text/plain\\r\\n\\r\\nThis is the content of the file.\\r\\n');\n * // Returns ['Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\\r\\nContent-Type: text/plain\\r\\n', 'This is the content of the file.\\r\\n']\n * ```\n */\nconst splitMultipartSection = (section: string): [string, string] => {\n const trimmedSection = section.startsWith('\\r\\n') ? section.slice(2) : section;\n const headerEndIndex = trimmedSection.indexOf('\\r\\n\\r\\n');\n\n if (headerEndIndex === -1) {\n return ['', ''];\n }\n\n const headers = trimmedSection.slice(0, headerEndIndex);\n const content = trimmedSection.slice(headerEndIndex + 4);\n return [headers, content];\n};\n\n/**\n * Simple Content-Disposition parser for multipart sections\n *\n * @example\n * ```ts\n * parseContentDisposition('Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\\r\\nContent-Type: text/plain\\r\\n\\r\\nThis is the content of the file.\\r\\n');\n * // Returns { name: 'file', filename: 'example.txt' }\n * ```\n */\nconst parseContentDisposition = (headerLine: string): InternalContentDisposition => {\n const result: InternalContentDisposition = { name: '' };\n\n const nameMatch = /name=(?:\"(?<temp2>[^\"]*)\"|(?<temp1>[^;,\\s]+))/i.exec(headerLine);\n const filenameMatch = /filename=(?:\"(?<temp2>[^\"]*)\"|(?<temp1>[^;,\\s]+))/i.exec(headerLine);\n\n if (nameMatch) {\n result.name = nameMatch[1] ?? nameMatch[2] ?? '';\n }\n\n if (filenameMatch) {\n const filename = filenameMatch[1] ?? filenameMatch[2];\n if (filename) {\n result.filename = filename;\n }\n }\n\n return result;\n};\n\n/**\n * Extract Content-Type from multipart section headers\n *\n * @example\n * ```ts\n * extractSectionContentType('Content-Type: text/plain\\r\\n\\r\\nThis is the content of the file.\\r\\n');\n * // Returns 'text/plain'\n * ```\n */\nconst extractSectionContentType = (headers: string): string => {\n const lines = headers.split(/\\r?\\n/);\n const contentTypeLine = lines.find((line) => line.toLowerCase().startsWith('content-type:'));\n\n if (!contentTypeLine) return 'application/octet-stream';\n\n return (\n contentTypeLine\n .slice(contentTypeLine.indexOf(':') + 1)\n .trim()\n .split(';')[0]\n ?.trim() ?? 'application/octet-stream'\n );\n};\n\n/**\n * Determine if content should be treated as binary\n *\n * @example\n * ```ts\n * isBinaryContent('image/png');\n * // Returns true\n * ```\n */\nconst isBinaryContent = (contentTypeValue: string): boolean => {\n const binaryTypes = ['image/', 'audio/', 'video/', 'application/octet-stream', 'application/pdf', 'application/zip', 'application/x-'];\n\n return binaryTypes.some((type) => contentTypeValue.toLowerCase().startsWith(type));\n};\n\n/**\n * Calculate content length for string or Buffer\n *\n * @example\n * ```ts\n * calculateContentLength('Hello, world!');\n * // Returns 13\n * ```\n */\nconst calculateContentLength = (content: Buffer | string): number => (Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content, 'utf8'));\n\n/**\n * Validate file against security configuration\n */\nconst _validateFileUpload = (file: InternalFileUpload, config?: InternalFileUploadOptions): void => {\n if (!config) return;\n\n // SECURITY: Check file size\n if (file.size > config.maxFileSize) {\n log.warn('[SECURITY] File upload too large', {\n filename: file.filename,\n size: file.size,\n limit: config.maxFileSize,\n sizeMB: Math.round(file.size / 1024 / 1024),\n });\n throw new Error(`File too large: ${file.filename} is ${file.size} bytes, exceeds limit of ${config.maxFileSize} bytes`);\n }\n\n // SECURITY: Check filename length\n if (file.filename && file.filename.length > config.maxFilenameLength) {\n throw new Error(`Filename too long: ${file.filename.length} characters exceeds limit of ${config.maxFilenameLength}`);\n }\n\n // SECURITY: Check file extension\n if (file.filename) {\n const extension = file.filename.toLowerCase().substring(file.filename.lastIndexOf('.'));\n\n // Check blocked extensions\n if (config.blockedExtensions.includes(extension)) {\n log.warn('[SECURITY] Blocked file type upload attempt', {\n filename: file.filename,\n extension,\n blockedExtensions: config.blockedExtensions,\n });\n throw new Error(`File type not allowed: ${extension} files are blocked for security reasons`);\n }\n\n // Check allowed extensions (if specified)\n if (config.allowedExtensions.length > 0 && !config.allowedExtensions.includes(extension)) {\n throw new Error(`File type not allowed: ${extension} is not in the allowed extensions list`);\n }\n }\n};\n\n/**\n * Handle file upload processing with binary support and security validation\n *\n * @example\n * ```ts\n * handleFileUpload({\n * contentDisposition: { name: 'file', filename: 'example.txt' },\n * contentSection: 'This is the content of the file.\\r\\n',\n * });\n * ```\n */\nconst handleFileUpload = ({\n contentDisposition,\n contentSection,\n headersSection,\n config,\n}: {\n contentDisposition: InternalContentDisposition;\n contentSection: string;\n headersSection: string;\n config?: InternalFileUploadOptions | undefined;\n}): InternalFileUpload => {\n const contentTypeValue = extractSectionContentType(headersSection);\n\n // Remove trailing \\r\\n that's part of the multipart boundary\n const trimmedContent = contentSection.endsWith('\\r\\n') ? contentSection.slice(0, -2) : contentSection;\n\n // For binary files, convert string to Buffer\n const content: Buffer | string = isBinaryContent(contentTypeValue) ? Buffer.from(trimmedContent, 'binary') : trimmedContent;\n\n const file: InternalFileUpload = {\n filename: contentDisposition.filename ?? '',\n contentType: contentTypeValue,\n size: calculateContentLength(content),\n content,\n };\n\n // SECURITY: Validate file against configuration\n _validateFileUpload(file, config);\n\n return file;\n};\n\n/**\n * Parse multipart form data request body with binary file support and security protections\n *\n * Security Features:\n * - File size validation per file and total\n * - File extension filtering (allow/block lists)\n * - Filename length validation\n * - File count limits\n *\n * @example\n * ```ts\n * const config = { maxFileSize: 10485760, maxFiles: 10, blockedExtensions: ['.exe'] };\n * parseMultipartFormData('...multipart body...', 'boundary123', config);\n * // Returns { fields: { ... }, files: [...] }\n * ```\n */\nexport const parseMultipartFormData = (body: string, boundary: string, config?: InternalFileUploadOptions): InternalMultipartFormData => {\n const result: InternalMultipartFormData = {\n fields: {},\n files: [],\n };\n\n // Split the body into parts using the boundary\n const parts = body.split(`--${boundary}`).slice(1); // Skip the first empty part\n\n let totalFileSize = 0;\n\n for (const part of parts) {\n // Skip empty parts and the final boundary marker\n if (!part || part.trim() === '' || part.trim() === '--') continue;\n\n // Parse the part headers and content\n const [headersSection, contentSection] = splitMultipartSection(part);\n if (!headersSection) continue; // Skip malformed parts\n\n // Find Content-Disposition header\n const lines = headersSection.split(/\\r?\\n/);\n const dispositionLine = lines.find((line) => line.toLowerCase().startsWith('content-disposition:'));\n if (!dispositionLine) continue;\n\n const contentDisposition = parseContentDisposition(dispositionLine);\n if (!contentDisposition.name) continue;\n\n // Handle file upload\n if (contentDisposition.filename !== undefined) {\n // SECURITY: Check file count limit\n if (config && result.files.length >= config.maxFiles) {\n log.warn('[SECURITY] Too many files in upload request', {\n fileCount: result.files.length,\n maxFiles: config.maxFiles,\n });\n throw new Error(`Too many files: maximum of ${config.maxFiles} files allowed per request`);\n }\n\n const file = handleFileUpload({\n contentDisposition,\n contentSection,\n headersSection,\n config,\n });\n\n totalFileSize += file.size;\n\n // SECURITY: Check total file size\n if (config && totalFileSize > config.maxTotalSize) {\n log.warn('[SECURITY] Total upload size too large', {\n totalSize: totalFileSize,\n limit: config.maxTotalSize,\n totalSizeMB: Math.round(totalFileSize / 1024 / 1024),\n });\n throw new Error(`Total file size too large: ${totalFileSize} bytes exceeds limit of ${config.maxTotalSize} bytes`);\n }\n\n result.files.push(file);\n }\n\n // Handle regular form field\n if (contentDisposition.filename === undefined) {\n // Remove trailing \\r\\n that's part of the multipart boundary\n const trimmedContent = contentSection.endsWith('\\r\\n') ? contentSection.slice(0, -2) : contentSection;\n result.fields[contentDisposition.name] = trimmedContent;\n }\n }\n\n return result;\n};\n",
8
+ "/**\n * YinzerFlow Logging Levels\n *\n * String-based logging levels for intuitive configuration:\n * - 'off': No logging at all\n * - 'error': Only errors\n * - 'warn': Warnings and errors (includes security warnings)\n * - 'info': Info, warnings, and errors (standard application logging)\n * - 'debug': All messages including verbose connection details\n *\n * Access logging (request/response lines) is controlled separately via `logging.requests`.\n */\nexport const logLevels = {\n off: 'off',\n error: 'error',\n warn: 'warn',\n info: 'info',\n debug: 'debug',\n} as const;\n",
9
+ "import { colors } from '@constants/colors.ts';\nimport { logLevels } from '@constants/log.js';\nimport type { LogLevel } from '@typedefs/constants/log.ts';\nimport type { Logger, LoggerConfig } from '@typedefs/public/Logger.ts';\nimport type { Colors } from '@typedefs/constants/colors.js';\n\n/**\n * YinzerFlow Main Logging System\n *\n * - Numeric levels (0=off, 1=error, 2=warn, 3=info, 4=debug)\n * - Smart formatting: prefix, timestamp, optional Pittsburgh personality\n * - Table support: Use log.table() for structured data display\n * - Custom logger as output sink: replaces console, raw args delegated\n * - Access logging is separate (see accessLog.ts)\n *\n * ## Config Cascade\n *\n * - `logging.personality` and `logging.prefix` on the framework config are the\n * single source of truth for all framework log output.\n * - `createLogger({ level })` controls filtering per instance.\n * - `logging.logger` is an **output sink** — it replaces `console` as the\n * destination. Raw args are always delegated to the custom logger.\n * If the sink is a framework logger (has `loggerBrand`), its personality/prefix\n * are overridden to match the framework config before delegation.\n * If it's an external logger (Winston, Pino), args pass through\n * untouched the external logger handles its own formatting.\n */\n\n/** Symbol brand for identifying framework-created loggers. Unforgeable — prevents duck-type collisions. */\nexport const loggerBrand = Symbol('YinzerFlowLogger');\n\nconst LOG_LEVELS = {\n off: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n} as const;\n\nconst YINZER_PHRASES = {\n positive: [\"n'at!\", 'yinz are good!', \"that's the way!\", 'right on!', \"lookin' good!\", 'way to go!', 'keep it up!'],\n neutral: [\"n'at\", 'yinz know', \"just sayin'\", \"that's how it is\", 'what can ya do', 'it happens'],\n negative: ['aw jeez', \"that ain't right\", 'what a jagoff move', \"that's terrible n'at\", 'somebody messed up', 'this is bad news', 'yinz better fix this'],\n} as const;\n\nconst _getRandomPhrase = (type: 'negative' | 'neutral' | 'positive'): string => {\n const phrases = YINZER_PHRASES[type];\n return phrases[Math.floor(Math.random() * phrases.length)] ?? '';\n};\n\nconst _pad = (n: number, len = 2): string => String(n).padStart(len, '0');\n\nconst _formatTimestamp = (): string => {\n const now = new Date();\n return `${now.getFullYear()}-${_pad(now.getMonth() + 1)}-${_pad(now.getDate())} ${_pad(now.getHours())}:${_pad(now.getMinutes())}:${_pad(now.getSeconds())}.${_pad(now.getMilliseconds(), 3)}`;\n};\n\nconst _getPhraseType = (level: LogLevel): 'negative' | 'neutral' | 'positive' => {\n if (level === 'error') return 'negative';\n if (level === 'warn') return 'neutral';\n return 'positive';\n};\n\ninterface LogStyleOpts {\n level: LogLevel;\n prefix: string;\n personality: boolean;\n output: Logger;\n}\n\nconst _logWithStyle = (opts: LogStyleOpts, ...args: Array<unknown>): void => {\n const { level, prefix, personality, output } = opts;\n const timestamp = _formatTimestamp();\n let bodyColor: Colors = colors.reset;\n if (prefix === 'ACCESS' || prefix === 'DIAGNOSTIC') {\n bodyColor = colors.gray;\n }\n\n const phrase = personality ? ` - ${_getRandomPhrase(_getPhraseType(level))}` : '';\n\n if (level === 'error') {\n const logPrefix = `${colors.red}[${prefix}] ❌ [${timestamp}] [ERROR]${colors.reset}`;\n output.error(`${logPrefix}`, `${bodyColor}`, ...args, `${colors.reset}${phrase}`);\n return;\n }\n\n if (level === 'warn') {\n const logPrefix = `${colors.yellow}[${prefix}] ⚠️ [${timestamp}] [WARN]${colors.reset}`;\n output.warn(`${logPrefix}`, `${bodyColor}`, ...args, `${colors.reset}${phrase}`);\n return;\n }\n\n if (level === 'debug') {\n const logPrefix = `${colors.gray}[${prefix}] 🔍 [${timestamp}] [DEBUG]${colors.reset}`;\n (output.debug ?? output.info)(`${logPrefix}`, `${colors.gray}`, ...args, `${colors.reset}`);\n return;\n }\n\n if (level === 'off') {\n return;\n }\n\n const logPrefix = `${colors.cyan}[${prefix}] [${timestamp}] [INFO]${colors.reset}`;\n output.info(`${logPrefix}`, `${bodyColor}`, ...args, `${colors.reset}${phrase}`);\n};\n\n/** Default output wraps console */\nconst _consoleOutput: Logger = {\n info: (...args: Array<unknown>) => console.info(...args),\n warn: (...args: Array<unknown>) => console.warn(...args),\n error: (...args: Array<unknown>) => console.error(...args),\n debug: (...args: Array<unknown>) => console.debug(...args),\n};\n\nconst _logTable = (opts: { prefix: string; personality: boolean; output: Logger }, data: unknown, ...additionalArgs: Array<unknown>): void => {\n const { prefix, personality, output } = opts;\n const timestamp = _formatTimestamp();\n const phrase = personality ? ` - ${_getRandomPhrase('positive')}` : '';\n const logPrefix = `${colors.magenta}[${prefix}] 📊 [${timestamp}] [TABLE]${colors.reset}`;\n\n output.info(`${logPrefix}${phrase}`);\n console.table(data);\n\n if (additionalArgs.length > 0) {\n output.info(`${colors.gray}Additional context:${colors.reset}`, ...additionalArgs);\n }\n};\n\n/**\n * Creates a logger instance with isolated state.\n *\n * @param initialConfig.level - Log level threshold messages at this severity and above are output (default: 'info')\n * @param initialConfig.prefix - Log line prefix (default: 'YINZER')\n * @returns Logger instance with logging methods and Symbol-branded state for framework use\n */\nconst createLogger = (\n initialConfig?: LoggerConfig & { personality?: boolean; logger?: Logger | null | undefined },\n): {\n info: (...args: Array<unknown>) => void;\n warn: (...args: Array<unknown>) => void;\n error: (...args: Array<unknown>) => void;\n debug: (...args: Array<unknown>) => void;\n table: (data: unknown, ...additionalArgs: Array<unknown>) => void;\n levels: typeof LOG_LEVELS;\n [loggerBrand]: { level: string; prefix: string; personality: boolean };\n} => {\n const state = {\n level: initialConfig?.level ?? initialConfig?.logLevel ?? logLevels.info,\n prefix: initialConfig?.prefix ?? 'YINZER',\n personality: initialConfig?.personality ?? true,\n logger: initialConfig?.logger ?? null,\n };\n\n const numericLevel = (LOG_LEVELS as Record<string, number>)[state.level] ?? LOG_LEVELS.info;\n\n /**\n * Route a log call. Three paths:\n * 1. Custom logger is a framework logger (has loggerBrand) → delegate raw (it formats itself)\n * 2. Custom logger is external (Winston/Pino) pass raw args (it has its own formatter)\n * 3. No custom logger → format ourselves, output to console\n */\n const _emit = (level: 'debug' | 'error' | 'info' | 'warn', args: Array<unknown>): void => {\n if (state.logger) {\n // Path 1 & 2: custom logger (framework or external) delegate raw args.\n // Framework loggers format with their own (mutated) state.\n // External loggers (Winston/Pino) have their own formatters no ANSI wrapping.\n const method = level === 'debug' ? (state.logger.debug ?? state.logger.info) : state.logger[level];\n method.call(state.logger, ...args);\n return;\n }\n\n // Path 3: no custom logger — we format, output to console\n _logWithStyle({ level, prefix: state.prefix, personality: state.personality, output: _consoleOutput }, ...args);\n };\n\n const info = (...args: Array<unknown>): void => {\n if (numericLevel < LOG_LEVELS.info) return;\n _emit('info', args);\n };\n\n const warn = (...args: Array<unknown>): void => {\n if (numericLevel < LOG_LEVELS.warn) return;\n _emit('warn', args);\n };\n\n const error = (...args: Array<unknown>): void => {\n if (numericLevel < LOG_LEVELS.error) return;\n _emit('error', args);\n };\n\n const debug = (...args: Array<unknown>): void => {\n if (numericLevel < LOG_LEVELS.debug) return;\n _emit('debug', args);\n };\n\n const table = (data: unknown, ...additionalArgs: Array<unknown>): void => {\n if (numericLevel < LOG_LEVELS.info) return;\n\n if (state.logger) {\n // Custom logger delegate raw (it handles its own formatting)\n state.logger.info('TABLE:', data, ...additionalArgs);\n return;\n }\n\n _logTable({ prefix: state.prefix, personality: state.personality, output: _consoleOutput }, data, ...additionalArgs);\n };\n\n return {\n info,\n warn,\n error,\n debug,\n table,\n levels: LOG_LEVELS,\n [loggerBrand]: state,\n };\n};\n\n// Default shared logger instance\nexport const log = createLogger();\n\n// Factory for creating custom logger instances\nexport { createLogger };\n",
10
+ "import type { ByteString } from '@typedefs/public/Bytes.js';\n\nconst BYTE_MULTIPLIERS: Record<string, number> = {\n b: 1,\n kb: 1024,\n mb: 1024 * 1024,\n gb: 1024 * 1024 * 1024,\n};\n\n/**\n * Convert byte size string or raw number to bytes.\n *\n * Accepts human-readable byte strings (`'256kb'`, `'1mb'`) or raw numbers.\n * Raw numbers pass through unchanged.\n *\n * @param size - ByteString (e.g. '256kb', '1mb') or raw byte count\n * @returns Size in bytes\n * @throws Error if format is invalid or value is not positive\n *\n * @example\n * ```typescript\n * _convertBytesToBytes('1kb') // 1024\n * _convertBytesToBytes('256kb') // 262144\n * _convertBytesToBytes('1mb') // 1048576\n * _convertBytesToBytes(4096) // 4096\n * ```\n *\n * @internal\n */\nexport const _convertBytesToBytes = (size: ByteString | number): number => {\n if (typeof size === 'number') {\n if (!Number.isFinite(size) || size <= 0) {\n throw new Error(`Invalid byte value: \"${size}\". Must be a positive number`);\n }\n return size;\n }\n\n if (typeof size !== 'string') {\n throw new Error('Invalid byte format. Expected format: 512b, 256kb, 1mb, 2gb');\n }\n\n const match = /^(?<value>\\d+(?:\\.\\d+)?)(?<unit>b|kb|mb|gb)$/.exec(size);\n if (!match?.groups) {\n throw new Error(`Invalid byte format: \"${size}\". Expected format: 512b, 256kb, 1mb, 2gb`);\n }\n\n const value = parseFloat(match.groups.value ?? '0');\n const unit = match.groups.unit ?? '';\n const multiplier = BYTE_MULTIPLIERS[unit];\n\n if (!multiplier) {\n throw new Error(`Invalid byte unit: \"${unit}\". Expected: b, kb, mb, gb`);\n }\n\n if (value <= 0) {\n throw new Error(`Invalid byte value: \"${value}\". Must be a positive number`);\n }\n\n return Math.floor(value * multiplier);\n};\n\n/**\n * Format byte count for display in warning/log messages.\n * Auto-selects the most readable unit.\n *\n * @internal\n */\nexport const _formatBytesForDisplay = (bytes: number): string => {\n if (bytes >= 1024 * 1024 * 1024) return `${Math.round(bytes / 1024 / 1024 / 1024)}GB`;\n if (bytes >= 1024 * 1024) return `${Math.round(bytes / 1024 / 1024)}MB`;\n if (bytes >= 1024) return `${Math.round(bytes / 1024)}KB`;\n return `${bytes}B`;\n};\n",
11
+ "import type { InternalJsonParserOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { log } from '@core/utils/log.ts';\nimport { _formatBytesForDisplay } from '@core/utils/bytes.ts';\nimport type { Logger } from '@typedefs/public/Logger.js';\n\n/**\n * Dangerous prototype properties that can lead to prototype pollution\n */\nconst DANGEROUS_PROPERTIES = ['__proto__', 'constructor', 'prototype'];\n\n/** Validation context threaded through recursive JSON structure checks */\ninterface JsonValidationCtx {\n config: InternalJsonParserOptions;\n logger: Logger;\n}\n\n/**\n * Parse JSON request body with comprehensive security protections\n *\n * Security Features:\n * - Request size validation\n * - Nesting depth limits to prevent stack overflow\n * - Prototype pollution protection\n * - Memory exhaustion protection (max keys, string length, array length)\n * - Proper error handling with security context\n */\nexport const parseApplicationJson = (body: string, config: InternalJsonParserOptions, logger?: Logger): unknown => {\n const _log = logger ?? log;\n // Handle empty strings, whitespace, and null characters\n if (!body || !body.trim() || body.trim() === '\\0') {\n return undefined;\n }\n\n // SECURITY: Validate request body size to prevent DoS attacks\n const bodySize = Buffer.byteLength(body, 'utf8');\n if (bodySize > config.maxSize) {\n _log.warn('[SECURITY] JSON request body too large', {\n size: _formatBytesForDisplay(bodySize),\n limit: config.maxSize,\n });\n throw new Error(`Request body too large: ${bodySize} bytes exceeds limit of ${config.maxSize} bytes`);\n }\n\n let parsedData: unknown = null; // Initialize to fix linting\n\n try {\n // First pass: Parse JSON to get structure\n parsedData = JSON.parse(body);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid JSON syntax: ${message}`);\n }\n\n // SECURITY: Validate parsed data structure for security vulnerabilities\n try {\n _validateJsonStructure(parsedData, { config, logger: _log }, 1); // Start at depth 1 (root level)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`JSON security validation failed: ${message}`);\n }\n\n return parsedData;\n};\n\n/**\n * Validate primitive values (strings, numbers, etc.)\n */\nconst _validatePrimitive = (data: unknown, config: InternalJsonParserOptions): void => {\n if (typeof data === 'string' && data.length > config.maxStringLength) {\n throw new Error(`String too long: ${data.length} characters exceeds limit of ${config.maxStringLength}`);\n }\n};\n\n/**\n * Validate array structure and elements\n */\nconst _validateArray = (data: Array<unknown>, ctx: JsonValidationCtx, depth: number): void => {\n // SECURITY: Check array length to prevent memory exhaustion\n if (data.length > ctx.config.maxArrayLength) {\n throw new Error(`Array too large: ${data.length} elements exceeds limit of ${ctx.config.maxArrayLength}`);\n }\n\n // Recursively validate array elements\n for (const item of data) {\n _validateJsonStructure(item, ctx, depth + 1);\n }\n};\n\n/**\n * Validate object keys for security issues\n */\nconst _validateObjectKeys = (keys: Array<string>, ctx: JsonValidationCtx): void => {\n // SECURITY: Check number of keys to prevent memory exhaustion\n if (keys.length > ctx.config.maxKeys) {\n throw new Error(`Object has too many keys: ${keys.length} exceeds limit of ${ctx.config.maxKeys}`);\n }\n\n // SECURITY: Check for prototype pollution attempts\n if (!ctx.config.allowPrototypeProperties) {\n for (const key of keys) {\n if (DANGEROUS_PROPERTIES.includes(key)) {\n ctx.logger.warn('[SECURITY] Prototype pollution attempt detected', {\n property: key,\n dangerousProperties: DANGEROUS_PROPERTIES,\n });\n throw new Error(`Prototype pollution attempt detected: property '${key}' is not allowed`);\n }\n }\n }\n};\n\n/**\n * Validate object properties and values\n */\nconst _validateObjectProperties = (data: Record<string, unknown>, ctx: JsonValidationCtx, depth: number): void => {\n const keys = Object.keys(data);\n\n for (const key of keys) {\n // SECURITY: Check key length\n if (key.length > ctx.config.maxStringLength) {\n throw new Error(`Object key too long: '${key.substring(0, 50)}...' exceeds limit of ${ctx.config.maxStringLength}`);\n }\n\n const value = data[key];\n\n // SECURITY: Check string value length\n if (typeof value === 'string' && value.length > ctx.config.maxStringLength) {\n throw new Error(`String value too long: property '${key}' has ${value.length} characters, exceeds limit of ${ctx.config.maxStringLength}`);\n }\n\n // Recursively validate nested structures\n _validateJsonStructure(value, ctx, depth + 1);\n }\n};\n\n/**\n * Recursively validate JSON structure for security vulnerabilities\n *\n * @param data - Data to validate\n * @param ctx - Validation context (config + logger)\n * @param depth - Current nesting depth (starts at 1 for root level)\n */\nconst _validateJsonStructure = (data: unknown, ctx: JsonValidationCtx, depth: number): void => {\n // SECURITY: Check nesting depth to prevent stack overflow attacks\n if (depth > ctx.config.maxDepth) {\n ctx.logger.warn('[SECURITY] JSON nesting too deep - potential stack overflow attack', {\n currentDepth: depth,\n maxDepth: ctx.config.maxDepth,\n });\n throw new Error(`JSON nesting too deep: current depth ${depth} exceeds maximum depth of ${ctx.config.maxDepth}`);\n }\n\n // Handle null and primitives\n if (data === null || typeof data !== 'object') {\n _validatePrimitive(data, ctx.config);\n return;\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n _validateArray(data, ctx, depth);\n return;\n }\n\n // Handle objects\n const keys = Object.keys(data);\n _validateObjectKeys(keys, ctx);\n _validateObjectProperties(data as Record<string, unknown>, ctx, depth);\n};\n",
12
+ "import type { InternalContentDisposition, InternalFileUpload, InternalMultipartFormData } from '@typedefs/internal/InternalRequestImpl.ts';\nimport type { InternalFileUploadOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { log } from '@core/utils/log.ts';\nimport { _formatBytesForDisplay } from '@core/utils/bytes.ts';\nimport type { Logger } from '@typedefs/public/Logger.js';\n\n/**\n * Split a multipart section into headers and content\n *\n * @example\n * ```ts\n * splitMultipartSection('Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\\r\\nContent-Type: text/plain\\r\\n\\r\\nThis is the content of the file.\\r\\n');\n * // Returns ['Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\\r\\nContent-Type: text/plain\\r\\n', 'This is the content of the file.\\r\\n']\n * ```\n */\nconst splitMultipartSection = (section: string): [string, string] => {\n const trimmedSection = section.startsWith('\\r\\n') ? section.slice(2) : section;\n const headerEndIndex = trimmedSection.indexOf('\\r\\n\\r\\n');\n\n if (headerEndIndex === -1) {\n return ['', ''];\n }\n\n const headers = trimmedSection.slice(0, headerEndIndex);\n const content = trimmedSection.slice(headerEndIndex + 4);\n return [headers, content];\n};\n\n/**\n * Simple Content-Disposition parser for multipart sections\n *\n * @example\n * ```ts\n * parseContentDisposition('Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\\r\\nContent-Type: text/plain\\r\\n\\r\\nThis is the content of the file.\\r\\n');\n * // Returns { name: 'file', filename: 'example.txt' }\n * ```\n */\nconst parseContentDisposition = (headerLine: string): InternalContentDisposition => {\n const result: InternalContentDisposition = { name: '' };\n\n const nameMatch = /name=(?:\"(?<temp2>[^\"]*)\"|(?<temp1>[^;,\\s]+))/i.exec(headerLine);\n const filenameMatch = /filename=(?:\"(?<temp2>[^\"]*)\"|(?<temp1>[^;,\\s]+))/i.exec(headerLine);\n\n if (nameMatch) {\n result.name = nameMatch[1] ?? nameMatch[2] ?? '';\n }\n\n if (filenameMatch) {\n const filename = filenameMatch[1] ?? filenameMatch[2];\n if (filename) {\n result.filename = filename;\n }\n }\n\n return result;\n};\n\n/**\n * Extract Content-Type from multipart section headers\n *\n * @example\n * ```ts\n * extractSectionContentType('Content-Type: text/plain\\r\\n\\r\\nThis is the content of the file.\\r\\n');\n * // Returns 'text/plain'\n * ```\n */\nconst extractSectionContentType = (headers: string): string => {\n const lines = headers.split(/\\r?\\n/);\n const contentTypeLine = lines.find((line) => line.toLowerCase().startsWith('content-type:'));\n\n if (!contentTypeLine) return 'application/octet-stream';\n\n return (\n contentTypeLine\n .slice(contentTypeLine.indexOf(':') + 1)\n .trim()\n .split(';')[0]\n ?.trim() ?? 'application/octet-stream'\n );\n};\n\n/**\n * Determine if content should be treated as binary\n *\n * @example\n * ```ts\n * isBinaryContent('image/png');\n * // Returns true\n * ```\n */\nconst isBinaryContent = (contentTypeValue: string): boolean => {\n const binaryTypes = ['image/', 'audio/', 'video/', 'application/octet-stream', 'application/pdf', 'application/zip', 'application/x-'];\n\n return binaryTypes.some((type) => contentTypeValue.toLowerCase().startsWith(type));\n};\n\n/**\n * Calculate content length for string or Buffer\n *\n * @example\n * ```ts\n * calculateContentLength('Hello, world!');\n * // Returns 13\n * ```\n */\nconst calculateContentLength = (content: Buffer | string): number => (Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content, 'utf8'));\n\n/**\n * Validate file against security configuration\n */\nconst _validateFileUpload = (file: InternalFileUpload, config?: InternalFileUploadOptions, logger?: Logger): void => {\n if (!config) return;\n const _log = logger ?? log;\n\n // SECURITY: Check file size\n if (file.size > config.maxFileSize) {\n _log.warn('[SECURITY] File upload too large', {\n filename: file.filename,\n size: _formatBytesForDisplay(file.size),\n limit: config.maxFileSize,\n });\n throw new Error(`File too large: ${file.filename} is ${file.size} bytes, exceeds limit of ${config.maxFileSize} bytes`);\n }\n\n // SECURITY: Check filename length\n if (file.filename && file.filename.length > config.maxFilenameLength) {\n throw new Error(`Filename too long: ${file.filename.length} characters exceeds limit of ${config.maxFilenameLength}`);\n }\n\n // SECURITY: Check file extension\n if (file.filename) {\n const extension = file.filename.toLowerCase().substring(file.filename.lastIndexOf('.'));\n\n // Check blocked extensions\n if (config.blockedExtensions.includes(extension)) {\n _log.warn('[SECURITY] Blocked file type upload attempt', {\n filename: file.filename,\n extension,\n blockedExtensions: config.blockedExtensions,\n });\n throw new Error(`File type not allowed: ${extension} files are blocked for security reasons`);\n }\n\n // Check allowed extensions (if specified)\n if (config.allowedExtensions.length > 0 && !config.allowedExtensions.includes(extension)) {\n throw new Error(`File type not allowed: ${extension} is not in the allowed extensions list`);\n }\n }\n};\n\n/**\n * Handle file upload processing with binary support and security validation\n *\n * @example\n * ```ts\n * handleFileUpload({\n * contentDisposition: { name: 'file', filename: 'example.txt' },\n * contentSection: 'This is the content of the file.\\r\\n',\n * });\n * ```\n */\nconst handleFileUpload = ({\n contentDisposition,\n contentSection,\n headersSection,\n config,\n logger,\n}: {\n contentDisposition: InternalContentDisposition;\n contentSection: string;\n headersSection: string;\n config?: InternalFileUploadOptions | undefined;\n logger?: Logger | undefined;\n}): InternalFileUpload => {\n const contentTypeValue = extractSectionContentType(headersSection);\n\n // Remove trailing \\r\\n that's part of the multipart boundary\n const trimmedContent = contentSection.endsWith('\\r\\n') ? contentSection.slice(0, -2) : contentSection;\n\n // For binary files, convert string to Buffer\n const content: Buffer | string = isBinaryContent(contentTypeValue) ? Buffer.from(trimmedContent, 'binary') : trimmedContent;\n\n const file: InternalFileUpload = {\n filename: contentDisposition.filename ?? '',\n contentType: contentTypeValue,\n size: calculateContentLength(content),\n content,\n };\n\n // SECURITY: Validate file against configuration\n _validateFileUpload(file, config, logger);\n\n return file;\n};\n\n/**\n * Parse multipart form data request body with binary file support and security protections\n *\n * Security Features:\n * - File size validation per file and total\n * - File extension filtering (allow/block lists)\n * - Filename length validation\n * - File count limits\n *\n * @example\n * ```ts\n * const config = { maxFileSize: 10485760, maxFiles: 10, blockedExtensions: ['.exe'] };\n * parseMultipartFormData('...multipart body...', 'boundary123', config);\n * // Returns { fields: { ... }, files: [...] }\n * ```\n */\nexport const parseMultipartFormData = (\n body: string,\n boundary: string,\n opts?: { config?: InternalFileUploadOptions; logger?: Logger },\n): InternalMultipartFormData => {\n const config = opts?.config;\n const _log = opts?.logger ?? log;\n const result: InternalMultipartFormData = {\n fields: {},\n files: [],\n };\n\n // Split the body into parts using the boundary\n const parts = body.split(`--${boundary}`).slice(1); // Skip the first empty part\n\n let totalFileSize = 0;\n\n for (const part of parts) {\n // Skip empty parts and the final boundary marker\n if (!part || part.trim() === '' || part.trim() === '--') continue;\n\n // Parse the part headers and content\n const [headersSection, contentSection] = splitMultipartSection(part);\n if (!headersSection) continue; // Skip malformed parts\n\n // Find Content-Disposition header\n const lines = headersSection.split(/\\r?\\n/);\n const dispositionLine = lines.find((line) => line.toLowerCase().startsWith('content-disposition:'));\n if (!dispositionLine) continue;\n\n const contentDisposition = parseContentDisposition(dispositionLine);\n if (!contentDisposition.name) continue;\n\n // Handle file upload\n if (contentDisposition.filename !== undefined) {\n // SECURITY: Check file count limit\n if (config && result.files.length >= config.maxFiles) {\n _log.warn('[SECURITY] Too many files in upload request', {\n fileCount: result.files.length,\n maxFiles: config.maxFiles,\n });\n throw new Error(`Too many files: maximum of ${config.maxFiles} files allowed per request`);\n }\n\n const file = handleFileUpload({\n contentDisposition,\n contentSection,\n headersSection,\n config,\n logger: _log,\n });\n\n totalFileSize += file.size;\n\n // SECURITY: Check total file size\n if (config && totalFileSize > config.maxTotalSize) {\n _log.warn('[SECURITY] Total upload size too large', {\n totalSize: _formatBytesForDisplay(totalFileSize),\n limit: config.maxTotalSize,\n });\n throw new Error(`Total file size too large: ${totalFileSize} bytes exceeds limit of ${config.maxTotalSize} bytes`);\n }\n\n result.files.push(file);\n }\n\n // Handle regular form field\n if (contentDisposition.filename === undefined) {\n // Remove trailing \\r\\n that's part of the multipart boundary\n const trimmedContent = contentSection.endsWith('\\r\\n') ? contentSection.slice(0, -2) : contentSection;\n result.fields[contentDisposition.name] = trimmedContent;\n }\n }\n\n return result;\n};\n",
13
13
  "/**\n * HTTP Constants\n *\n * This file contains all HTTP-related constants used throughout the application.\n * Centralizing these constants makes them easier to maintain and ensures consistency.\n */\n\n/**\n * HTTP Status Text\n * Maps status codes to their standard text representations\n */\n// Use const assertions for better tree-shaking\nexport const httpStatus = {\n ok: 'OK',\n created: 'Created',\n accepted: 'Accepted',\n noContent: 'No Content',\n movedPermanently: 'Moved Permanently',\n found: 'Found',\n notModified: 'Not Modified',\n badRequest: 'Bad Request',\n unauthorized: 'Unauthorized',\n forbidden: 'Forbidden',\n notFound: 'Not Found',\n methodNotAllowed: 'Method Not Allowed',\n conflict: 'Conflict',\n unsupportedMediaType: 'Unsupported Media Type',\n tooManyRequests: 'Too Many Requests',\n internalServerError: 'Internal Server Error',\n} as const;\n\n/**\n * HTTP Status Codes\n * Standard HTTP status codes used in responses\n */\nexport const httpStatusCode = {\n ok: 200,\n created: 201,\n accepted: 202,\n noContent: 204,\n movedPermanently: 301,\n found: 302,\n notModified: 304,\n badRequest: 400,\n unauthorized: 401,\n forbidden: 403,\n notFound: 404,\n methodNotAllowed: 405,\n conflict: 409,\n unsupportedMediaType: 415,\n tooManyRequests: 429,\n internalServerError: 500,\n} as const;\n\n/**\n * HTTP Methods\n * Standard HTTP methods used in requests\n */\nexport const httpMethod = {\n delete: 'DELETE',\n get: 'GET',\n head: 'HEAD',\n post: 'POST',\n put: 'PUT',\n patch: 'PATCH',\n options: 'OPTIONS',\n} as const;\n\n/**\n * Common Content Types\n * Frequently used content types for HTTP communication\n */\nexport const contentType = {\n json: 'application/json',\n html: 'text/html',\n form: 'application/x-www-form-urlencoded',\n multipart: 'multipart/form-data',\n xml: 'application/xml',\n text: 'text/plain',\n csv: 'text/csv',\n yamlApplication: 'application/yaml',\n yamlText: 'text/yaml',\n urlEncodedJson: 'application/x-www-form-urlencoded+json',\n} as const;\n\n/**\n * HTTP header names organized by category\n * Use with CreateEnum to get type-safe header names with intellisense\n */\nexport const httpHeaders = {\n // Authentication & Authorization\n authorization: 'Authorization', // Bearer tokens, Basic auth, etc.\n proxyAuthorization: 'Proxy-Authorization', // Auth through proxy\n wwwAuthenticate: 'WWW-Authenticate', // Server auth challenge\n\n // Caching & Validation\n cacheControl: 'Cache-Control', // Cache directives\n etag: 'ETag', // Resource version identifier\n expires: 'Expires', // Cache expiration date\n lastModified: 'Last-Modified', // Resource modification date\n ifMatch: 'If-Match', // Conditional request based on ETag\n ifNoneMatch: 'If-None-Match', // Conditional request based on ETag\n ifModifiedSince: 'If-Modified-Since', // Conditional request based on date\n ifUnmodifiedSince: 'If-Unmodified-Since', // Conditional request based on date\n ifRange: 'If-Range', // Conditional range request\n age: 'Age', // Time in proxy cache\n vary: 'Vary', // Response varies based on headers\n\n // Content Information\n contentType: 'Content-Type', // Media type of content\n contentLength: 'Content-Length', // Size in bytes\n contentEncoding: 'Content-Encoding', // Compression method\n contentLanguage: 'Content-Language', // Natural language\n contentDisposition: 'Content-Disposition', // Inline vs attachment\n contentLocation: 'Content-Location', // Alternate location\n contentRange: 'Content-Range', // Partial content range\n\n // CORS (Cross-Origin Resource Sharing)\n accessControlAllowCredentials: 'Access-Control-Allow-Credentials', // Allow credentials in CORS\n accessControlAllowHeaders: 'Access-Control-Allow-Headers', // Allowed request headers\n accessControlAllowMethods: 'Access-Control-Allow-Methods', // Allowed HTTP methods\n accessControlAllowOrigin: 'Access-Control-Allow-Origin', // Allowed origins\n accessControlExposeHeaders: 'Access-Control-Expose-Headers', // Exposed response headers\n accessControlMaxAge: 'Access-Control-Max-Age', // Preflight cache duration\n accessControlRequestHeaders: 'Access-Control-Request-Headers', // Preflight request headers\n accessControlRequestMethod: 'Access-Control-Request-Method', // Preflight request method\n\n // Request Information\n accept: 'Accept', // Acceptable media types\n acceptEncoding: 'Accept-Encoding', // Acceptable encodings\n acceptLanguage: 'Accept-Language', // Acceptable languages\n acceptRanges: 'Accept-Ranges', // Server supports ranges\n host: 'Host', // Target host and port\n userAgent: 'User-Agent', // Client software info\n referer: 'Referer', // Previous page URL\n origin: 'Origin', // Request origin\n from: 'From', // User email address\n expect: 'Expect', // Server requirements\n\n // Response Information\n location: 'Location', // Redirect URL\n server: 'Server', // Server software info\n date: 'Date', // Message timestamp\n allow: 'Allow', // Supported HTTP methods\n retryAfter: 'Retry-After', // Retry delay\n\n // Range Requests\n range: 'Range', // Requested byte range\n\n // Security Headers\n contentSecurityPolicy: 'Content-Security-Policy', // CSP directives\n contentSecurityPolicyReportOnly: 'Content-Security-Policy-Report-Only', // CSP report mode\n strictTransportSecurity: 'Strict-Transport-Security', // HTTPS enforcement\n xContentTypeOptions: 'X-Content-Type-Options', // Prevent MIME sniffing\n xFrameOptions: 'X-Frame-Options', // Clickjacking protection\n xXSSProtection: 'X-XSS-Protection', // XSS filter control\n referrerPolicy: 'Referrer-Policy', // Referrer info policy\n permissionsPolicy: 'Permissions-Policy', // Feature permissions\n crossOriginEmbedderPolicy: 'Cross-Origin-Embedder-Policy', // Embedding control\n crossOriginOpenerPolicy: 'Cross-Origin-Opener-Policy', // Window opening control\n crossOriginResourcePolicy: 'Cross-Origin-Resource-Policy', // Resource sharing control\n\n // Cookies\n cookie: 'Cookie', // Client cookies\n setCookie: 'Set-Cookie', // Server cookie instructions\n\n // Connection Management\n connection: 'Connection', // Connection control\n keepAlive: 'Keep-Alive', // Keep-alive parameters\n upgrade: 'Upgrade', // Protocol upgrade\n upgradeInsecureRequests: 'Upgrade-Insecure-Requests', // HTTPS upgrade\n\n // Transfer & Encoding\n transferEncoding: 'Transfer-Encoding', // Transfer encoding method\n te: 'TE', // Acceptable transfer encodings\n trailer: 'Trailer', // Trailer field names\n\n // Proxy & Forwarding\n forwarded: 'Forwarded', // Proxy information\n xForwardedFor: 'X-Forwarded-For', // Proxy information\n via: 'Via', // Proxy chain info\n maxForwards: 'Max-Forwards', // Hop limit\n\n // Alternative Services\n altSvc: 'Alt-Svc', // Alternative services\n altUsed: 'Alt-Used', // Used alternative service\n\n // Timing & Performance\n timingAllowOrigin: 'Timing-Allow-Origin', // Timing API access\n serverTiming: 'Server-Timing', // Server performance metrics\n\n // Refresh & Links\n refresh: 'Refresh', // Auto-refresh directive\n link: 'Link', // Related resources\n\n // Custom & Extension Headers\n xPoweredBy: 'X-Powered-By', // Server technology\n xPermittedCrossDomainPolicies: 'X-Permitted-Cross-Domain-Policies', // Flash policy\n reportTo: 'Report-To', // Error reporting endpoint\n serviceWorkerAllowed: 'Service-Worker-Allowed', // Service worker scope\n sourceMap: 'SourceMap', // Source map location\n priority: 'Priority', // Request priority\n secGPC: 'Sec-GPC', // Global Privacy Control\n\n // Data & Clearing\n clearSiteData: 'Clear-Site-Data', // Clear browser data\n noVarySearch: 'No-Vary-Search', // Search param cache control\n} as const;\n\nexport const httpEncoding = {\n base64: 'base64',\n binary: 'binary',\n utf8: 'utf8',\n} as const;\n",
14
14
  "import type { InternalUrlEncodedOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * Validate form field counts and basic structure\n */\nconst _validateFormStructure = (pairs: Array<string>, config: InternalUrlEncodedOptions): void => {\n // SECURITY: Check field count to prevent memory exhaustion\n if (pairs.length > config.maxFields) {\n throw new Error(`Too many form fields: ${pairs.length} exceeds limit of ${config.maxFields}`);\n }\n};\n\n/**\n * Validate individual field name and value lengths\n */\nconst _validateFieldLengths = (key: string, value: string | undefined, config: InternalUrlEncodedOptions): void => {\n // SECURITY: Check field name length\n if (key.length > config.maxFieldNameLength) {\n throw new Error(`Form field name too long: ${key.length} characters exceeds limit of ${config.maxFieldNameLength}`);\n }\n\n // SECURITY: Check field value length\n if (value && value.length > config.maxFieldLength) {\n throw new Error(`Form field value too long: field '${key}' has ${value.length} characters, exceeds limit of ${config.maxFieldLength}`);\n }\n};\n\n/**\n * Validate decoded field lengths (they may expand during decoding)\n */\nconst _validateDecodedLengths = (decodedKey: string, decodedValue: string, config: InternalUrlEncodedOptions): void => {\n // SECURITY: Check decoded field name length (could expand during decoding)\n if (decodedKey.length > config.maxFieldNameLength) {\n throw new Error(`Decoded form field name too long: ${decodedKey.length} characters exceeds limit of ${config.maxFieldNameLength}`);\n }\n\n // SECURITY: Check decoded field value length\n if (decodedValue.length > config.maxFieldLength) {\n throw new Error(\n `Decoded form field value too long: field '${decodedKey}' has ${decodedValue.length} characters, exceeds limit of ${config.maxFieldLength}`,\n );\n }\n};\n\n/**\n * Process a single form field pair with security validation\n */\nconst _processFieldPair = (pair: string, params: Record<string, string>, config?: InternalUrlEncodedOptions): void => {\n const [key, value] = pair.split('=');\n if (!key) return;\n\n // Validate field lengths if config provided\n if (config) {\n _validateFieldLengths(key, value, config);\n }\n\n try {\n const decodedKey = decodeURIComponent(key);\n const decodedValue = value ? decodeURIComponent(value) : '';\n\n // Validate decoded lengths if config provided\n if (config) {\n _validateDecodedLengths(decodedKey, decodedValue, config);\n }\n\n params[decodedKey] = decodedValue;\n } catch (error) {\n // Handle malformed URL encoding - check if it's a validation error or decoding error\n if (error instanceof Error && error.message.includes('exceeds limit')) {\n throw error; // Re-throw validation errors\n }\n\n // Handle malformed URL encoding gracefully by using original values\n params[key] = value ?? '';\n }\n};\n\n/**\n * Parse URL-encoded form data with security protections\n *\n * Security Features:\n * - Field count limits to prevent memory exhaustion\n * - Field name length validation\n * - Field value length validation\n * - Malformed URL encoding handling\n *\n * @example\n * ```ts\n * const config = { maxFields: 1000, maxFieldNameLength: 100, maxFieldLength: 1048576 };\n * parseUrlEncodedForm('name=John&age=30', config);\n * // Returns { name: 'John', age: '30' }\n * ```\n */\nexport const parseUrlEncodedForm = (body: string, config?: InternalUrlEncodedOptions): Record<string, string> => {\n const params: Record<string, string> = {};\n const pairs = body.split('&');\n\n // Validate form structure if config provided\n if (config) {\n _validateFormStructure(pairs, config);\n }\n\n // Process each field pair\n for (const pair of pairs) {\n _processFieldPair(pair, params, config);\n }\n\n return params;\n};\n",
15
15
  "import type { InternalHttpEncoding } from '@typedefs/constants/http.js';\n\n/**\n * Common binary file signatures (magic numbers)\n * Used for detecting file types when Content-Type header is missing\n */\nconst BINARY_SIGNATURES = {\n // Images\n JPEG: [0xff, 0xd8, 0xff],\n PNG: [0x89, 0x50, 0x4e, 0x47],\n GIF87A: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61], // GIF87a\n GIF89A: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61], // GIF89a\n BMP: [0x42, 0x4d],\n TIFF_LE: [0x49, 0x49, 0x2a, 0x00], // Little endian\n TIFF_BE: [0x4d, 0x4d, 0x00, 0x2a], // Big endian\n WEBP: [0x52, 0x49, 0x46, 0x46], // RIFF header (WebP has 'WEBP' at offset 8)\n ICO: [0x00, 0x00, 0x01, 0x00],\n\n // Audio\n MP3_ID3: [0x49, 0x44, 0x33], // ID3 tag\n MP3_FRAME: [0xff, 0xfb], // MP3 frame\n WAV: [0x52, 0x49, 0x46, 0x46], // RIFF header (WAV has 'WAVE' at offset 8)\n FLAC: [0x66, 0x4c, 0x61, 0x43], // fLaC\n OGG: [0x4f, 0x67, 0x67, 0x53], // OggS\n\n // Video\n MP4_FTYP: [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70], // Common MP4 signature\n MP4_FTYP_ALT: [0x00, 0x00, 0x00, 0x1c, 0x66, 0x74, 0x79, 0x70], // Alternative MP4\n AVI: [0x52, 0x49, 0x46, 0x46], // RIFF header (AVI has 'AVI ' at offset 8)\n WEBM: [0x1a, 0x45, 0xdf, 0xa3], // EBML header\n\n // Documents\n PDF: [0x25, 0x50, 0x44, 0x46], // %PDF\n\n // Archives\n ZIP: [0x50, 0x4b, 0x03, 0x04], // ZIP file\n ZIP_EMPTY: [0x50, 0x4b, 0x05, 0x06], // Empty ZIP\n ZIP_SPANNED: [0x50, 0x4b, 0x07, 0x08], // Spanned ZIP\n RAR: [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00], // Rar!\n RAR5: [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00], // Rar! v5\n SEVENZ: [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c], // 7z\n GZIP: [0x1f, 0x8b], // gzip\n\n // Executables\n EXE: [0x4d, 0x5a], // MZ (DOS/Windows executable)\n ELF: [0x7f, 0x45, 0x4c, 0x46], // ELF (Linux executable)\n\n // Office Documents (modern Office files are ZIP-based)\n OFFICE_OLD: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1], // Old MS Office\n} as const;\n\n/**\n * Check if buffer matches a binary signature\n */\nconst matchesSignature = (buffer: Buffer, signature: ReadonlyArray<number>): boolean => {\n if (buffer.length < signature.length) return false;\n return signature.every((byte, index) => buffer[index] === byte);\n};\n\n/**\n * Check for special cases that require additional validation\n */\nconst validateSpecialSignatures = (buffer: Buffer): boolean => {\n // WebP: RIFF header + 'WEBP' at offset 8\n if (matchesSignature(buffer, BINARY_SIGNATURES.WEBP) && buffer.length >= 12) {\n const webpMarker = buffer.subarray(8, 12);\n return webpMarker.toString('ascii') === 'WEBP';\n }\n\n // WAV: RIFF header + 'WAVE' at offset 8\n if (matchesSignature(buffer, BINARY_SIGNATURES.WAV) && buffer.length >= 12) {\n const waveMarker = buffer.subarray(8, 12);\n return waveMarker.toString('ascii') === 'WAVE';\n }\n\n // AVI: RIFF header + 'AVI ' at offset 8\n if (matchesSignature(buffer, BINARY_SIGNATURES.AVI) && buffer.length >= 12) {\n const aviMarker = buffer.subarray(8, 12);\n return aviMarker.toString('ascii') === 'AVI ';\n }\n\n return false;\n};\n\n/**\n * Determine the appropriate encoding based on Content-Type header\n * Falls back to content inspection if no header is present\n */\nexport const determineEncoding = (contentType?: string, body?: unknown): InternalHttpEncoding => {\n if (!contentType) {\n // No content-type header, infer from body content\n return inferEncodingFromBody(body);\n }\n\n const lowerContentType = contentType.toLowerCase();\n\n // Binary content types that should use base64 encoding\n if (\n lowerContentType.startsWith('image/') ||\n lowerContentType.startsWith('video/') ||\n lowerContentType.startsWith('audio/') ||\n lowerContentType === 'application/pdf' ||\n lowerContentType === 'application/octet-stream' ||\n lowerContentType.startsWith('application/zip') ||\n lowerContentType.startsWith('application/x-') // Many binary formats use x- prefix\n ) {\n return 'base64';\n }\n\n // Text-based content types use utf8\n if (\n lowerContentType.startsWith('text/') ||\n lowerContentType.startsWith('application/json') ||\n lowerContentType.startsWith('application/xml') ||\n lowerContentType.startsWith('application/javascript')\n ) {\n return 'utf8';\n }\n\n // Default to binary for unknown types\n return 'binary';\n};\n\n/**\n * Infer encoding when no Content-Type header is present\n */\nexport const inferEncodingFromBody = (body?: unknown): 'base64' | 'binary' | 'utf8' => {\n // If it's a Buffer, check for binary signatures\n if (Buffer.isBuffer(body)) {\n return detectBinaryFormat(body) ? 'base64' : 'utf8';\n }\n\n // Objects and arrays should be JSON, use utf8\n if (typeof body === 'object' && body !== null) return 'utf8';\n\n // Strings use utf8\n if (typeof body === 'string') return 'utf8';\n\n // Everything else defaults to utf8\n return 'utf8';\n};\n\n/**\n * Detect if buffer contains binary data based on file signatures\n */\nconst detectBinaryFormat = (buffer: Buffer): boolean => {\n if (buffer.length === 0) return false;\n\n // Check all known binary signatures\n const signatures = Object.values(BINARY_SIGNATURES);\n\n for (const signature of signatures) {\n if (matchesSignature(buffer, signature)) {\n return true;\n }\n }\n\n // Check special cases that need additional validation\n if (validateSpecialSignatures(buffer)) {\n return true;\n }\n\n // Heuristic: if buffer contains a lot of null bytes or non-printable chars, likely binary\n const nullBytes = buffer.filter((byte) => byte === 0).length;\n const nonPrintableBytes = buffer.filter((byte) => byte < 32 && byte !== 9 && byte !== 10 && byte !== 13).length;\n\n // If more than 10% null bytes or 30% non-printable, consider it binary\n return nullBytes / buffer.length > 0.1 || nonPrintableBytes / buffer.length > 0.3;\n};\n",
16
16
  "import { determineEncoding } from './determineEncoding.ts';\nimport { contentType } from '@constants/http.ts';\n\n// =============================================================================\n// JSON Detection\n// =============================================================================\n\n/**\n * Detect if string content is valid JSON\n * Validates by attempting to parse the content\n */\nconst isJsonContent = (content: string): boolean => {\n if (!((content.startsWith('{') && content.endsWith('}')) || (content.startsWith('[') && content.endsWith(']')))) {\n return false;\n }\n\n try {\n JSON.parse(content);\n return true;\n } catch {\n return false;\n }\n};\n\n// =============================================================================\n// Form Data Detection\n// =============================================================================\n\n/**\n * Detect if string content is URL-encoded form data\n */\nconst isUrlEncodedFormContent = (content: string): boolean => content.includes('=') && content.includes('&');\n\n/**\n * Detect if string content is multipart form data\n */\nconst isMultipartFormContent = (content: string): boolean => content.includes('boundary=');\n\n// =============================================================================\n// Object Type Detection\n// =============================================================================\n\n/**\n * Check if value is a plain object or array (but not special object types)\n */\nconst isPlainObjectOrArray = (value: unknown): boolean =>\n typeof value === 'object' &&\n value !== null &&\n !Buffer.isBuffer(value) &&\n !(value instanceof Uint8Array) &&\n !(value instanceof ArrayBuffer) &&\n !(value instanceof Date);\n\n/**\n * Check if value is a Date object\n */\nconst isDateObject = (value: unknown): value is Date => value instanceof Date;\n\n// =============================================================================\n// Binary Data Helpers\n// =============================================================================\n\n/**\n * Convert various binary types to Buffer for consistent processing\n */\nconst convertToBuffer = (data: ArrayBuffer | Buffer | Uint8Array): Buffer => {\n if (Buffer.isBuffer(data)) return data;\n return Buffer.from(data as ArrayBuffer);\n};\n\n/**\n * Determine content type for binary data using encoding detection\n */\nconst inferBinaryContentType = (buffer: Buffer): string => {\n const encoding = determineEncoding(undefined, buffer);\n return encoding === 'base64' ? 'application/octet-stream' : 'text/plain';\n};\n\n// =============================================================================\n// Main String Content Type Inference\n// =============================================================================\n\n/**\n * Infer content type from string body content when Content-Type header is missing\n * Focuses on main HTTP content types: JSON, form data, plain text\n *\n * @param body - String content to analyze\n * @returns Detected content type\n */\nexport const inferContentTypeFromString = (body: string): string => {\n const trimmedBody = body.trim();\n\n // Check for JSON first (most specific)\n if (isJsonContent(trimmedBody)) {\n return contentType.json;\n }\n\n // Check for form data\n if (isUrlEncodedFormContent(trimmedBody)) {\n return contentType.form;\n }\n\n if (isMultipartFormContent(trimmedBody)) {\n return contentType.multipart;\n }\n\n // Default to plain text for everything else\n return 'text/plain';\n};\n\n// =============================================================================\n// Main Content Type Inference\n// =============================================================================\n\n/**\n * Infer content type from any response body type when Content-Type header is missing\n * Handles the main types: objects (JSON), strings, binary data, primitives\n *\n * @param body - Response body of any supported type\n * @returns Detected content type\n */\nexport const inferContentType = (body: unknown): string => {\n // Handle null/undefined\n if (body === null || body === undefined) {\n return 'text/plain';\n }\n\n // Handle Date objects specially (convert to string)\n if (isDateObject(body)) {\n return 'text/plain';\n }\n\n // Handle plain objects and arrays -> JSON\n if (isPlainObjectOrArray(body)) {\n return contentType.json;\n }\n\n // Handle strings with basic detection\n if (typeof body === 'string') {\n return inferContentTypeFromString(body);\n }\n\n // Handle binary data types\n if (Buffer.isBuffer(body) || body instanceof Uint8Array || body instanceof ArrayBuffer) {\n const buffer = convertToBuffer(body);\n return inferBinaryContentType(buffer);\n }\n\n // All other primitives -> plain text\n return 'text/plain';\n};\n",
17
- "import { parseApplicationJson } from './parseJson.ts';\nimport { parseMultipartFormData } from './parseMultipart.ts';\nimport { contentType } from '@constants/http.ts';\n\nimport { parseUrlEncodedForm } from '@core/execution/utils/parseUrlEncodedForm.ts';\nimport type { InternalContentType } from '@typedefs/constants/http.js';\nimport { inferContentTypeFromString } from '@core/execution/utils/inferContentType.ts';\nimport type { InternalBodyParserOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * Options for parsing request body\n */\nexport interface ParseBodyOptions {\n /**\n * Content-Type header value\n */\n headerContentType?: InternalContentType | undefined;\n\n /**\n * Multipart boundary (for multipart/form-data)\n */\n boundary?: string | undefined;\n\n /**\n * Body parser security configuration\n */\n config?: InternalBodyParserOptions;\n}\n\n/**\n * Validate request body size based on content type\n */\nconst _validateBodySize = (body: string, mainContentType: string, config: InternalBodyParserOptions): void => {\n const bodySize = Buffer.byteLength(body, 'utf8');\n\n if (mainContentType === contentType.json) {\n if (bodySize > config.json.maxSize) {\n throw new Error(`JSON body too large: ${bodySize} bytes exceeds limit of ${config.json.maxSize} bytes`);\n }\n } else if (mainContentType === contentType.form) {\n if (bodySize > config.urlEncoded.maxSize) {\n throw new Error(`URL-encoded body too large: ${bodySize} bytes exceeds limit of ${config.urlEncoded.maxSize} bytes`);\n }\n } else if (mainContentType === contentType.multipart) {\n if (bodySize > config.fileUploads.maxTotalSize) {\n throw new Error(`Multipart body too large: ${bodySize} bytes exceeds limit of ${config.fileUploads.maxTotalSize} bytes`);\n }\n }\n};\n\n/**\n * Parse request body based on Content-Type header with comprehensive security protections\n *\n * This function determines the appropriate parsing strategy based on the Content-Type header:\n * - application/json: Parse as JSON with security protections\n * - multipart/form-data: Parse as multipart with file uploads\n * - application/x-www-form-urlencoded: Parse as URL-encoded form data\n * - text/*: Return as raw string (with size limits)\n * - default: Return as raw string (with size limits)\n *\n * Security Features:\n * - Request body size validation\n * - JSON DoS attack prevention\n * - Prototype pollution protection\n * - Memory exhaustion protection\n *\n * @param body - Raw request body string\n * @param options - Parsing options including content type, boundary, and configuration\n * @returns Parsed body in appropriate format\n * @throws Error if body is too large, malformed, or contains security threats\n */\nexport const parseBody = (body: string, options: ParseBodyOptions = {}): unknown => {\n const { headerContentType, boundary, config } = options;\n\n // Handle empty body\n if (!body || !body.trim()) {\n return undefined;\n }\n\n // Determine the content type - either passed in or inferred\n const mainContentType = headerContentType ?? inferContentTypeFromString(body);\n\n // SECURITY: Apply size limits based on content type if config is provided\n if (config) {\n _validateBodySize(body, mainContentType, config);\n }\n\n // Parse based on Content-Type\n if (mainContentType === contentType.json) {\n if (!config) {\n throw new Error('Body parser configuration is required for JSON parsing');\n }\n return parseApplicationJson(body, config.json);\n }\n\n if (mainContentType === contentType.multipart) {\n if (!boundary) throw new Error('Invalid multipart form data: missing boundary');\n return parseMultipartFormData(body, boundary, config?.fileUploads);\n }\n\n if (mainContentType === contentType.form) {\n return parseUrlEncodedForm(body, config?.urlEncoded);\n }\n\n // For all other content types, return the raw body\n // Note: Size limits are already applied above if config is provided\n return body;\n};\n",
17
+ "import { parseApplicationJson } from './parseJson.ts';\nimport { parseMultipartFormData } from './parseMultipart.ts';\nimport { contentType } from '@constants/http.ts';\n\nimport { parseUrlEncodedForm } from '@core/execution/utils/parseUrlEncodedForm.ts';\nimport type { InternalContentType } from '@typedefs/constants/http.js';\nimport { inferContentTypeFromString } from '@core/execution/utils/inferContentType.ts';\nimport type { InternalBodyParserOptions, InternalFileUploadOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport type { Logger } from '@typedefs/public/Logger.js';\n\n/**\n * Options for parsing request body\n */\nexport interface ParseBodyOptions {\n /**\n * Content-Type header value\n */\n headerContentType?: InternalContentType | undefined;\n\n /**\n * Multipart boundary (for multipart/form-data)\n */\n boundary?: string | undefined;\n\n /**\n * Body parser security configuration\n */\n config?: InternalBodyParserOptions;\n\n /**\n * Optional logger for security warnings. Falls back to module-level log.\n */\n logger?: Logger | undefined;\n}\n\n/**\n * Validate request body size based on content type\n */\nconst _validateBodySize = (body: string, mainContentType: string, config: InternalBodyParserOptions): void => {\n const bodySize = Buffer.byteLength(body, 'utf8');\n\n if (mainContentType === contentType.json) {\n if (bodySize > config.json.maxSize) {\n throw new Error(`JSON body too large: ${bodySize} bytes exceeds limit of ${config.json.maxSize} bytes`);\n }\n } else if (mainContentType === contentType.form) {\n if (bodySize > config.urlEncoded.maxSize) {\n throw new Error(`URL-encoded body too large: ${bodySize} bytes exceeds limit of ${config.urlEncoded.maxSize} bytes`);\n }\n } else if (mainContentType === contentType.multipart) {\n if (bodySize > config.fileUploads.maxTotalSize) {\n throw new Error(`Multipart body too large: ${bodySize} bytes exceeds limit of ${config.fileUploads.maxTotalSize} bytes`);\n }\n }\n};\n\n/**\n * Parse request body based on Content-Type header with comprehensive security protections\n *\n * This function determines the appropriate parsing strategy based on the Content-Type header:\n * - application/json: Parse as JSON with security protections\n * - multipart/form-data: Parse as multipart with file uploads\n * - application/x-www-form-urlencoded: Parse as URL-encoded form data\n * - text/*: Return as raw string (with size limits)\n * - default: Return as raw string (with size limits)\n *\n * Security Features:\n * - Request body size validation\n * - JSON DoS attack prevention\n * - Prototype pollution protection\n * - Memory exhaustion protection\n *\n * @param body - Raw request body string\n * @param options - Parsing options including content type, boundary, and configuration\n * @returns Parsed body in appropriate format\n * @throws Error if body is too large, malformed, or contains security threats\n */\nexport const parseBody = (body: string, options: ParseBodyOptions = {}): unknown => {\n const { headerContentType, boundary, config, logger } = options;\n\n // Handle empty body\n if (!body || !body.trim()) {\n return undefined;\n }\n\n // Determine the content type - either passed in or inferred\n const mainContentType = headerContentType ?? inferContentTypeFromString(body);\n\n // SECURITY: Apply size limits based on content type if config is provided\n if (config) {\n _validateBodySize(body, mainContentType, config);\n }\n\n // Parse based on Content-Type\n if (mainContentType === contentType.json) {\n if (!config) {\n throw new Error('Body parser configuration is required for JSON parsing');\n }\n return parseApplicationJson(body, config.json, logger);\n }\n\n if (mainContentType === contentType.multipart) {\n if (!boundary) throw new Error('Invalid multipart form data: missing boundary');\n const multipartOpts: { config?: InternalFileUploadOptions; logger?: Logger } = {};\n if (config?.fileUploads) multipartOpts.config = config.fileUploads;\n if (logger) multipartOpts.logger = logger;\n return parseMultipartFormData(body, boundary, multipartOpts);\n }\n\n if (mainContentType === contentType.form) {\n return parseUrlEncodedForm(body, config?.urlEncoded);\n }\n\n // For all other content types, return the raw body\n // Note: Size limits are already applied above if config is provided\n return body;\n};\n",
18
18
  "/**\n * String manipulation utilities\n *\n * This file encapsulates string manipulation utilities used throughout the framework.\n */\n\n/**\n * Divides a string into two parts based on a separator\n * @param str The string to divide\n * @param separator The separator to use\n * @returns A tuple containing the first part and the rest\n */\nexport const divideString = (str: string, separator: string): [string, string] => {\n const index = str.indexOf(separator);\n if (index === -1) {\n return [str, ''];\n }\n const first = str.slice(0, index);\n const rest = str.slice(index + separator.length);\n return [first, rest];\n};\n",
19
19
  "import type { InternalHttpMethod } from '@typedefs/constants/http.js';\nimport { httpMethod } from '@constants/http.ts';\nimport { divideString } from '@core/utils/string.ts';\n\n/**\n * Parse raw HTTP request string into its components that are needed for the request builder\n *\n * @example\n * ```ts\n * parseHttpRequest('GET /path?key1=value1&key2=value2 HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n');\n * // Returns { method: 'GET', path: '/path?key1=value1&key2=value2', protocol: 'HTTP/1.1', headersRaw: 'Host: example.com\\r\\n', rawBody: '' }\n * ```\n */\nexport const parseHttpRequest = (request: string): { method: InternalHttpMethod; path: string; protocol: string; headersRaw: string; rawBody: string } => {\n /**\n * The request is a string that contains the following information:\n * - The first line contains the request method, path, and protocol\n * - The headers are separated from the body by two newlines\n */\n\n // Handle empty or malformed requests gracefully\n if (!request || !request.trim()) {\n return {\n method: 'GET' as InternalHttpMethod,\n path: '/',\n protocol: 'HTTP/1.1',\n headersRaw: '',\n rawBody: '',\n };\n }\n\n const [firstLine, rest] = divideString(request, '\\r\\n');\n const [method, path, protocol] = firstLine.split(' ', 3);\n const [headersRaw, rawBody] = divideString(rest, '\\r\\n\\r\\n');\n\n // Validate method and provide fallback\n if (!method || !Object.values(httpMethod).includes(method as InternalHttpMethod)) {\n return {\n method: 'GET' as InternalHttpMethod,\n path: path ?? '/',\n protocol: protocol ?? 'HTTP/1.1',\n headersRaw,\n rawBody,\n };\n }\n\n return {\n method: method as InternalHttpMethod,\n path: path ?? '/',\n protocol: protocol ?? 'HTTP/1.1',\n headersRaw,\n rawBody,\n };\n};\n",
20
20
  "/**\n * Parse the query string from the path\n *\n * @example\n * ```ts\n * parseQuery('https://example.com/path?key1=value1&key2=value2');\n * // Returns { key1: 'value1', key2: 'value2' }\n * ```\n */\nexport const parseQuery = (path: string): Record<string, string> => {\n if (!path) return {};\n\n if (!path.includes('?')) return {};\n\n const [, queryString] = path.split('?');\n if (!queryString) return {};\n\n const params: Record<string, string> = {};\n const pairs = queryString.split('&');\n\n for (const pair of pairs) {\n const [key, value] = pair.split('=');\n if (key) {\n try {\n const decodedKey = decodeURIComponent(key);\n const decodedValue = value ? decodeURIComponent(value) : '';\n params[decodedKey] = decodedValue;\n } catch {\n // If decoding fails, use the original values\n params[key] = value ?? '';\n }\n }\n }\n\n return params;\n};\n",
21
21
  "import type { InternalHttpHeaders } from '@typedefs/constants/http.js';\nimport type { InternalIpAddressResult } from '@typedefs/internal/InternalIpAddress.js';\nimport type { InternalIpSecurityOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.ts';\n\n// Private IP ranges (RFC 1918, RFC 4193, RFC 3927)\nconst PRIVATE_IP_RANGES = [\n // IPv4 private ranges\n /^(?<classA>10)\\./,\n /^(?<classB>172)\\.(?<classBRange>1[6-9]|2[0-9]|3[0-1])\\./,\n /^(?<classC>192)\\.(?<classCRange>168)\\./,\n /^(?<linkLocal>169)\\.(?<linkLocalRange>254)\\./, // link-local\n /^(?<loopback>127)\\./, // loopback\n // IPv6 private ranges\n /^(?<ipv6Loopback>::1)$/, // loopback\n /^(?<ipv6LinkLocal>fe80):/i, // link-local\n /^(?<ipv6UniqueLocalFC>fc00):/i, // unique local\n /^(?<ipv6UniqueLocalFD>fd00):/i, // unique local\n];\n\n/**\n * Validates if an IP address is properly formatted\n */\nexport const isValidIpAddress = (ip: string): boolean => {\n if (!ip || typeof ip !== 'string') return false;\n\n // Remove brackets from IPv6 addresses\n const cleanIp = ip.replace(/^\\[|\\]$/g, '');\n\n // IPv4 validation with named capture groups\n const ipv4Regex = /^(?<octet>(?<highByte>25[0-5]|(?<midByte>2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$/;\n if (ipv4Regex.test(cleanIp)) {\n const parts = cleanIp.split('.');\n return (\n parts.length === 4 &&\n parts.every((part) => {\n const num = parseInt(part, 10);\n return num >= 0 && num <= 255;\n })\n );\n }\n\n // IPv6 validation with named capture groups (simplified but robust)\n // First check for invalid patterns (multiple :: compression)\n if (cleanIp.includes('::') && (cleanIp.match(/::/g) ?? []).length > 1) {\n return false; // Multiple :: compression is invalid\n }\n\n const ipv6Regex =\n /^(?<ipv6Address>(?<fullAddress>(?<hexQuad>[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(?<compressedAddress>(?<leadingPart>[0-9a-fA-F]{1,4}:){1,7}:)|(?<mixedCompression>(?<frontPart>[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})|(?<doubleColonOnly>::)|(?<linkLocal>fe80:(?<linkSuffix>:[0-9a-fA-F]{0,4}){0,4}(?<zoneId>%[0-9a-zA-Z]+)?)|(?<ipv4MappedFull>::ffff:(?<mappedIpv4>(?<mappedOctet>[0-9]{1,3}\\.){3}[0-9]{1,3}))|(?<generalPattern>(?<segmentGroup>[0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}))$/;\n\n return ipv6Regex.test(cleanIp);\n};\n\n/**\n * Checks if an IP address is in a private range\n */\nexport const isPrivateIp = (ip: string): boolean => {\n if (!ip) return false;\n const cleanIp = ip.replace(/^\\[|\\]$/g, '');\n return PRIVATE_IP_RANGES.some((range) => range.test(cleanIp));\n};\n\n/**\n * Checks if an IP is in the trusted proxy list\n * Supports '*' wildcard to trust any proxy\n */\nexport const isTrustedProxy = (ip: string, trustedProxies: Array<string>): boolean => {\n if (!ip || !trustedProxies.length) return false;\n\n // Support '*' wildcard to trust any proxy\n if (trustedProxies.includes('*')) return true;\n\n return trustedProxies.includes(ip);\n};\n\n/**\n * Detects potential IP spoofing patterns\n */\nexport const detectSpoofingPatterns = (chain: Array<string>, config: InternalIpSecurityOptions): boolean => {\n if (!config.detectSpoofing || chain.length <= 1) return false;\n\n // Pattern 1: Too many hops (potential amplification attack)\n if (chain.length > config.maxChainLength) return true;\n\n // Pattern 2: Duplicate IPs in chain (suspicious)\n const uniqueIps = new Set(chain);\n if (uniqueIps.size !== chain.length) return true;\n\n // Pattern 3: Mix of invalid and valid IPs (obfuscation attempt)\n const validCount = chain.filter(isValidIpAddress).length;\n if (validCount > 0 && validCount < chain.length) return true;\n\n // Pattern 4: Reverse proxy spoofing detection disabled\n // Note: The pattern \"public_ip, private_ip\" is normal for legitimate X-Forwarded-For headers\n // More sophisticated detection would require knowledge of expected network topology\n\n return false;\n};\n\n/**\n * Validates X-Forwarded-For proxy chain\n */\nconst _validateXForwardedForChain = (ipChain: Array<string>, config: InternalIpSecurityOptions): boolean => {\n if (ipChain.length <= 1) return true;\n\n const lastIp = ipChain[ipChain.length - 1];\n return Boolean(lastIp && isTrustedProxy(lastIp, config.trustedProxies));\n};\n\n/**\n * Extracts client IP from IP chain based on header type\n */\nconst _extractClientIp = (ipChain: Array<string>, headerName: string): string | undefined => {\n if (headerName === 'x-forwarded-for') {\n return ipChain[0]; // Client IP is first in X-Forwarded-For\n }\n return ipChain[ipChain.length - 1]; // For other headers, take the last value\n};\n\n/**\n * Creates a valid IP result object\n */\nconst _createValidIpResult = (options: {\n clientIp: string;\n headerName: string;\n ipChain: Array<string>;\n config: InternalIpSecurityOptions;\n}): InternalIpAddressResult => {\n const { clientIp, headerName, ipChain, config } = options;\n const isPrivate = isPrivateIp(clientIp);\n const trusted = headerName === 'x-forwarded-for' ? isTrustedProxy(ipChain[ipChain.length - 1] ?? '', config.trustedProxies) : true;\n\n return {\n ip: clientIp,\n isValid: true,\n isPrivate,\n source: headerName,\n trusted,\n };\n};\n\n/**\n * Creates an invalid IP result object\n */\nconst _createInvalidIpResult = (): InternalIpAddressResult => ({\n ip: '',\n isValid: false,\n isPrivate: false,\n source: 'socket',\n trusted: false,\n});\n\n/**\n * Extracts and validates IP addresses from headers with security checks\n */\nconst _extractIpFromHeaders = (headers: Partial<Record<InternalHttpHeaders, string>>, config: InternalIpSecurityOptions): InternalIpAddressResult => {\n // Try each header in preference order\n for (const headerName of config.headerPreference) {\n const headerValue = headers[headerName];\n if (!headerValue) continue;\n\n // Handle comma-separated IP chains\n const ipChain = headerValue\n .split(',')\n .map((ip) => ip.trim())\n .filter(Boolean);\n if (ipChain.length === 0) continue;\n\n // Detect spoofing patterns\n if (detectSpoofingPatterns(ipChain, config)) continue;\n\n // For X-Forwarded-For, validate the proxy chain\n if (headerName === 'x-forwarded-for' && !_validateXForwardedForChain(ipChain, config)) {\n continue;\n }\n\n // Extract the client IP\n const clientIp = _extractClientIp(ipChain, headerName);\n if (!clientIp || !isValidIpAddress(clientIp)) continue;\n\n // Check if private IPs are allowed\n const isPrivate = isPrivateIp(clientIp);\n if (isPrivate && !config.allowPrivateIps) continue;\n\n return _createValidIpResult({ clientIp, headerName, ipChain, config });\n }\n\n return _createInvalidIpResult();\n};\n\n/**\n * Advanced IP parsing with comprehensive security validation\n * Uses server-level IP security configuration with optional overrides\n */\nexport const parseIpAddressSecure = (\n setup: InternalSetupImpl,\n headers: Partial<Record<InternalHttpHeaders, string>>,\n configOverride: Partial<InternalIpSecurityOptions> = {},\n): InternalIpAddressResult => {\n const serverConfig = setup._configuration.ipSecurity;\n const finalConfig = { ...serverConfig, ...configOverride };\n\n // Try to extract IP from headers first\n const headerResult = _extractIpFromHeaders(headers, finalConfig);\n if (headerResult.isValid) {\n return headerResult;\n }\n\n // No valid IP found from headers\n return {\n ip: '',\n isValid: false,\n isPrivate: false,\n source: 'socket',\n trusted: false,\n };\n};\n\n/**\n * Simple IP parsing function that returns just the IP address string\n * Provides backward compatibility for existing code\n */\nexport const parseIpAddress = (setup: InternalSetupImpl, headers: Partial<Record<InternalHttpHeaders, string>>): string => {\n const result = parseIpAddressSecure(setup, headers);\n return result.ip;\n};\n",
22
22
  "/**\n * Extract the boundary from the content type header\n *\n * @example\n * ```ts\n * extractBoundaryFromHeader('multipart/form-data; boundary=----WebKitFormBoundary1234567890');\n * // Returns '----WebKitFormBoundary1234567890'\n * ```\n */\nexport const extractBoundaryFromHeader = (contentTypeHeader?: string): string | undefined => {\n if (!contentTypeHeader) return undefined;\n\n const boundaryMatch = /boundary\\s*=\\s*(?<temp1>[^;,\\s]*)/i.exec(contentTypeHeader);\n return boundaryMatch?.[1];\n};\n",
23
23
  "import type { InternalHttpHeaders } from '@typedefs/constants/http.js';\n\n// Security limits to prevent DoS attacks\nconst MAX_HEADERS = 100;\nconst MAX_HEADER_NAME_LENGTH = 200;\nconst MAX_HEADER_VALUE_LENGTH = 8192;\n\n/**\n * Parse incoming request headers from raw headers string with security validation\n *\n * This is the main orchestrator function that delegates to specialized helpers\n * for each processing step: validation → parsing → sanitization → security\n *\n * @example\n * ```ts\n * parseRequestHeaders('Host: example.com\\r\\nContent-Type: application/json\\r\\n\\r\\n');\n * // Returns { host: 'example.com', 'content-type': 'application/json' }\n * ```\n */\nexport const parseRequestHeaders = (rawHeaders: string): Partial<Record<InternalHttpHeaders, string>> => {\n if (!rawHeaders) return {};\n\n // Step 1: Pre-parse validation (format, DoS protection)\n validateRawHeaders(rawHeaders);\n\n // Step 2: Parse header structure into key-value pairs\n const parsedHeaders = parseHeaderStructure(rawHeaders);\n\n // Step 3: Sanitize header values (remove control chars, etc.)\n const sanitizedHeaders = sanitizeHeaders(parsedHeaders);\n\n // Step 4: Apply security policies (business logic, injection prevention)\n const securedHeaders = applySecurityPolicies(sanitizedHeaders);\n\n return securedHeaders;\n};\n\n/**\n * Step 1: Validate raw headers before parsing\n * Checks for DoS protection and basic format sanity\n */\nexport const validateRawHeaders = (rawHeaders: string): void => {\n // SECURITY: Limit number of potential headers to prevent DoS\n const headerLines = rawHeaders.split(/\\r\\n|\\r|\\n/);\n if (headerLines.length > MAX_HEADERS) {\n throw new Error(`Too many headers: maximum ${MAX_HEADERS} allowed`);\n }\n\n // Basic format validation could go here\n // (e.g., overall size limits, obvious malformation)\n};\n\n/**\n * Step 2: Parse header structure into key-value pairs\n * Handles line ending normalization and basic parsing\n */\nexport const parseHeaderStructure = (rawHeaders: string): Record<string, string> => {\n const headers: Record<string, string> = {};\n\n // Normalize line endings and split\n const normalizedHeaders = rawHeaders.replace(/\\r\\n|\\r|\\n/g, '\\n');\n const headerLines = normalizedHeaders.split('\\n');\n\n for (const line of headerLines) {\n if (!line.trim()) continue;\n\n const colonIndex = line.indexOf(':');\n if (colonIndex === -1) continue;\n\n const key = line.slice(0, colonIndex).trim();\n const value = line.slice(colonIndex + 1).trim();\n\n if (!key) continue;\n\n // SECURITY: Validate header name against RFC 7230\n if (!isValidHeaderName(key)) {\n throw new Error(`Invalid header name: ${key}`);\n }\n\n // SECURITY: Limit header name length\n if (key.length > MAX_HEADER_NAME_LENGTH) {\n throw new Error(`Header name too long: maximum ${MAX_HEADER_NAME_LENGTH} characters allowed`);\n }\n\n // SECURITY: Limit header value length\n if (value.length > MAX_HEADER_VALUE_LENGTH) {\n throw new Error(`Header value too long: maximum ${MAX_HEADER_VALUE_LENGTH} characters allowed`);\n }\n\n // Store with lowercase key for consistency\n headers[key.toLowerCase()] = value;\n }\n\n return headers;\n};\n\n/**\n * Step 3: Sanitize header values\n * Removes control characters and normalizes values\n */\nexport const sanitizeHeaders = (headers: Record<string, string>): Record<string, string> => {\n const sanitized: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n sanitized[key] = sanitizeHeaderValue(value);\n }\n\n return sanitized;\n};\n\n/**\n * Step 4: Apply security policies\n * Business logic validation and injection prevention\n */\nexport const applySecurityPolicies = (headers: Record<string, string>): Record<string, string> =>\n // Note: CRLF injection detection would go here if we were setting response headers\n // For parsing incoming request headers, this step mainly handles business logic validation\n\n // Could add checks like:\n // - Suspicious header combinations\n // - Known attack patterns in values\n // - Business-specific header validation\n\n headers;\n\n/**\n * Validate header name according to RFC 7230\n */\nconst isValidHeaderName = (name: string): boolean => {\n // RFC 7230: header-name = token\n // token = 1*tchar\n // tchar = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\" / \"+\" / \"-\" / \".\" /\n // \"^\" / \"_\" / \"`\" / \"|\" / \"~\" / DIGIT / ALPHA\n const validHeaderNameRegex = /^[a-zA-Z0-9!#$%&'*+\\-.^_`|~]+$/;\n return validHeaderNameRegex.test(name);\n};\n\n/**\n * Sanitize header value to prevent injection attacks\n */\nconst sanitizeHeaderValue = (value: string): string => {\n // SECURITY: Remove control characters except horizontal tab\n // Allow printable ASCII, horizontal tab (0x09), and extended ASCII\n const sanitized = value.replace(/[\\x00-\\x08\\x0A-\\x1F\\x7F]/g, '');\n return sanitized;\n};\n",
24
- "import { parseBody } from './utils/parseBody.ts';\nimport type { InternalContentType, InternalHttpHeaders, InternalHttpMethod } from '@typedefs/constants/http.js';\nimport { parseHttpRequest } from '@core/execution/utils/parseHttpRequest.ts';\nimport { parseQuery } from '@core/execution/utils/parseQuery.ts';\nimport { parseIpAddress } from '@core/execution/utils/parseIpAddress.ts';\nimport { extractBoundaryFromHeader } from '@core/execution/utils/extractBoundaryFromHeader.ts';\nimport { parseRequestHeaders } from '@core/execution/utils/parseRequestHeaders.ts';\nimport type { Request } from '@typedefs/public/Request.ts';\nimport type { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport type { InternalRequestImpl } from '@typedefs/internal/InternalRequestImpl.ts';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.ts';\n\nexport class RequestImpl implements InternalRequestImpl {\n readonly _rawRequest: Buffer | string;\n readonly _setup: InternalSetupImpl;\n\n method: InternalHttpMethod;\n path: string;\n protocol: string;\n headers: Partial<Record<InternalHttpHeaders, string>>;\n body: unknown;\n query: Record<string, unknown>;\n params: Record<string, string>;\n ipAddress: string;\n rawBody: Buffer | string;\n cookies = new Map<string, string>();\n signedCookies = new Map<string, string>();\n\n constructor(rawRequest: Request['rawBody'], setup: SetupImpl, clientAddress?: string) {\n this._rawRequest = rawRequest;\n this._setup = setup;\n\n // Set initial IP address from socket, will be updated if headers provide better info\n this.ipAddress = clientAddress ?? '';\n\n const { method, path, protocol, headers, body, query, params, rawBody } = this._parseRequestIntoObject();\n\n this.method = method;\n this.path = path;\n this.protocol = protocol;\n this.headers = headers;\n this.body = body;\n this.query = query ?? {};\n this.params = params ?? {};\n this.rawBody = rawBody;\n\n // Update IP address if parsing from headers provides something\n const parsedIpAddress = parseIpAddress(this._setup, headers);\n if (parsedIpAddress) {\n this.ipAddress = parsedIpAddress;\n }\n }\n\n private _parseRequestIntoObject(): Omit<Request, 'cookies' | 'ipAddress' | 'signedCookies'> {\n const request = this._rawRequest.toString();\n\n const { method, path, protocol, headersRaw, rawBody } = parseHttpRequest(request);\n\n const headers = parseRequestHeaders(headersRaw);\n\n // Extract content type and boundary for body parsing\n const contentTypeHeader = headers['content-type'];\n const mainContentType = contentTypeHeader?.split(';')[0]?.trim().toLowerCase() as InternalContentType;\n const boundary = extractBoundaryFromHeader(contentTypeHeader);\n\n return {\n method,\n path,\n protocol,\n headers,\n body: parseBody(rawBody, {\n headerContentType: mainContentType,\n boundary,\n config: this._setup._configuration.bodyParser,\n }),\n query: parseQuery(path),\n params: {}, // Route params will be set in RequestHandlerImpl when route is matched\n rawBody,\n };\n }\n}\n",
25
- "import dayjs from 'dayjs';\nimport { formatBodyIntoString } from '@core/execution/utils/formatBodyIntoString.ts';\nimport { determineEncoding } from '@core/execution/utils/determineEncoding.ts';\nimport { inferContentType } from '@core/execution/utils/inferContentType.ts';\nimport { mapStatusCodeToMessage } from '@core/execution/utils/mapStatusCodeToMessage.ts';\nimport { filterAndValidateHeaders } from '@core/execution/utils/validateResponseHeaders.ts';\nimport { httpEncoding, httpStatus, httpStatusCode } from '@constants/http.ts';\nimport type { InternalHttpEncoding, InternalHttpHeaders, InternalHttpStatus, InternalHttpStatusCode } from '@typedefs/constants/http.js';\nimport type { Request } from '@typedefs/public/Request.ts';\nimport type { InternalResponseImpl } from '@typedefs/internal/InternalResponseImpl.d.ts';\nimport { calculateContentSizeInBytes } from '@core/utils/calculateContentSizeInBytes.ts';\n\nexport class ResponseImpl implements InternalResponseImpl {\n readonly _request: Request;\n\n _statusCode: InternalHttpStatusCode = httpStatusCode.ok;\n _status: InternalHttpStatus = httpStatus.ok;\n _headers: Partial<Record<InternalHttpHeaders, string>> = {};\n _setCookies: Array<string> = []; // Track multiple Set-Cookie headers\n _body: unknown = '';\n _stringBody = '';\n _encoding: InternalHttpEncoding = httpEncoding.utf8;\n\n constructor(request: Request) {\n this._request = request;\n this._setSecurityHeaders();\n }\n\n _parseResponseIntoString(): void {\n // Example: HTTP/1.1 200 OK\n const statusLine = `${this._request.protocol} ${this._statusCode} ${this._status}`;\n\n // Example: Content-Type: text/html\n const headerLines = Object.entries(this._headers).map(([key, value]) => `${key}: ${value}`);\n\n // Add multiple Set-Cookie headers\n const setCookieLines = this._setCookies.map((value) => `Set-Cookie: ${value}`);\n\n // Combine all header lines (regular headers + Set-Cookie headers)\n const allHeaderLines = [...headerLines, ...setCookieLines];\n\n // Determine encoding based on Content-Type header and body content\n const encoding = determineEncoding(this._headers['content-type'], this._body);\n\n // Example: <html><body><h1>Hello, world!</h1></body></html> or { \"message\": \"Hello, world!\" }\n const body = formatBodyIntoString(this._body, { encoding });\n\n // Example: HTTP/1.1 200 OK\\nContent-Type: text/html\\n\\n<html><body><h1>Hello, world!</h1></body></html>\n this._encoding = encoding;\n\n // Fix: Handle the case when there are no headers properly\n const headersSection = allHeaderLines.length > 0 ? `${allHeaderLines.join('\\n')}\\n` : '';\n this._stringBody = `${statusLine}\\n${headersSection}\\n${body}`;\n\n const contentLength = calculateContentSizeInBytes(this._stringBody);\n this._setHeadersIfNotSet({\n Date: dayjs().format('ddd, DD MMM YYYY HH:mm:ss [GMT]'),\n 'Content-Length': String(contentLength),\n });\n }\n\n _setHeadersIfNotSet(headers: Partial<Record<InternalHttpHeaders, string>>): void {\n // SECURITY: Filter undefined values and validate response headers for CRLF injection\n // Only validate headers that aren't already set\n const headersToSet: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined && !(key in this._headers)) {\n headersToSet[key] = value;\n }\n }\n\n const validatedHeaders = filterAndValidateHeaders(headersToSet);\n\n // Set headers after validation passes\n Object.assign(this._headers, validatedHeaders);\n }\n\n _setBody(body: unknown): void {\n this._body = body;\n\n // Auto-set content-type if not already set\n if (!this._headers['content-type']) {\n const detectedContentType = inferContentType(body);\n this._setHeadersIfNotSet({\n 'Content-Type': detectedContentType,\n });\n }\n }\n\n setStatusCode(statusCode: InternalHttpStatusCode): void {\n this._statusCode = statusCode;\n this._status = mapStatusCodeToMessage(statusCode);\n }\n\n addHeaders(headers: Partial<Record<InternalHttpHeaders, string>>): void {\n // SECURITY: Filter undefined values and validate response headers for CRLF injection\n const validatedHeaders = filterAndValidateHeaders(headers);\n\n // Handle Set-Cookie specially - support multiple values\n for (const [key, value] of Object.entries(validatedHeaders)) {\n if (key === 'Set-Cookie') {\n if (value) {\n this._setCookies.push(value);\n }\n } else {\n // Regular headers overwrite if duplicate\n this._headers[key] = value;\n }\n }\n }\n\n removeHeaders(headerNames: Array<InternalHttpHeaders>): void {\n for (const headerName of headerNames) {\n delete this._headers[headerName];\n }\n }\n\n /**\n * Set default security headers to protect against common vulnerabilities\n * These headers are set only if not already present, allowing users to override if needed\n */\n _setSecurityHeaders(): void {\n this._setHeadersIfNotSet({\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'X-XSS-Protection': '1; mode=block',\n 'Referrer-Policy': 'strict-origin-when-cross-origin',\n });\n }\n}\n",
24
+ "import { parseBody } from './utils/parseBody.ts';\nimport type { InternalContentType, InternalHttpHeaders, InternalHttpMethod } from '@typedefs/constants/http.js';\nimport { parseHttpRequest } from '@core/execution/utils/parseHttpRequest.ts';\nimport { parseQuery } from '@core/execution/utils/parseQuery.ts';\nimport { parseIpAddress } from '@core/execution/utils/parseIpAddress.ts';\nimport { extractBoundaryFromHeader } from '@core/execution/utils/extractBoundaryFromHeader.ts';\nimport { parseRequestHeaders } from '@core/execution/utils/parseRequestHeaders.ts';\nimport type { Request } from '@typedefs/public/Request.ts';\nimport type { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport type { InternalRequestImpl } from '@typedefs/internal/InternalRequestImpl.ts';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.ts';\n\nexport class RequestImpl implements InternalRequestImpl {\n readonly _rawRequest: Buffer | string;\n readonly _setup: InternalSetupImpl;\n\n method: InternalHttpMethod;\n path: string;\n protocol: string;\n headers: Partial<Record<InternalHttpHeaders, string>>;\n body: unknown;\n query: Record<string, unknown>;\n params: Record<string, string>;\n ipAddress: string;\n rawBody: Buffer | string;\n cookies = new Map<string, string>();\n signedCookies = new Map<string, string>();\n\n constructor(rawRequest: Request['rawBody'], setup: SetupImpl, clientAddress?: string) {\n this._rawRequest = rawRequest;\n this._setup = setup;\n\n // Set initial IP address from socket, will be updated if headers provide better info\n this.ipAddress = clientAddress ?? '';\n\n const { method, path, protocol, headers, body, query, params, rawBody } = this._parseRequestIntoObject();\n\n this.method = method;\n this.path = path;\n this.protocol = protocol;\n this.headers = headers;\n this.body = body;\n this.query = query ?? {};\n this.params = params ?? {};\n this.rawBody = rawBody;\n\n // Update IP address if parsing from headers provides something\n const parsedIpAddress = parseIpAddress(this._setup, headers);\n if (parsedIpAddress) {\n this.ipAddress = parsedIpAddress;\n }\n }\n\n private _parseRequestIntoObject(): Omit<Request, 'cookies' | 'ipAddress' | 'signedCookies'> {\n const request = this._rawRequest.toString();\n\n const { method, path, protocol, headersRaw, rawBody } = parseHttpRequest(request);\n\n const headers = parseRequestHeaders(headersRaw);\n\n // Extract content type and boundary for body parsing\n const contentTypeHeader = headers['content-type'];\n const mainContentType = contentTypeHeader?.split(';')[0]?.trim().toLowerCase() as InternalContentType;\n const boundary = extractBoundaryFromHeader(contentTypeHeader);\n\n return {\n method,\n path,\n protocol,\n headers,\n body: parseBody(rawBody, {\n headerContentType: mainContentType,\n boundary,\n config: this._setup._configuration.bodyParser,\n logger: this._setup._log,\n }),\n query: parseQuery(path),\n params: {}, // Route params will be set in RequestHandlerImpl when route is matched\n rawBody,\n };\n }\n}\n",
26
25
  "/**\n * Format the body into a string for HTTP response transmission\n *\n * Handles various body types including:\n * - Objects/Arrays: JSON stringified\n * - Strings: returned as-is\n * - Binary data (Buffer/Uint8Array): converted to base64 or kept as binary string\n * - Primitives: converted to string\n * - null/undefined: empty string\n *\n * @example\n * ```typescript\n * // JSON object\n * const body = { message: 'Hello, world!' };\n * const formattedBody = formatBodyIntoString(body);\n * // formattedBody === '{\"message\":\"Hello, world!\"}'\n *\n * // Binary data\n * const binaryBody = Buffer.from('binary data');\n * const formattedBinary = formatBodyIntoString(binaryBody);\n * // formattedBinary === 'binary data' (as string)\n *\n * // Base64 encoding option for true binary\n * const imageBuffer = Buffer.from([0xFF, 0xD8, 0xFF]); // JPEG header\n * const base64Body = formatBodyIntoString(imageBuffer, { encoding: 'base64' });\n * // base64Body === '/9j/' (base64 encoded)\n * ```\n */\nexport const formatBodyIntoString = (body: unknown, options?: { encoding?: 'base64' | 'binary' | 'utf8' }): string => {\n const encoding = options?.encoding ?? 'utf8';\n\n // Handle null/undefined\n if (body === null || body === undefined) return '';\n\n // Handle binary data types\n if (Buffer.isBuffer(body)) return handleBuffer(body, encoding);\n if (body instanceof Uint8Array) return handleUint8Array(body, encoding);\n if (body instanceof ArrayBuffer) return handleArrayBuffer(body, encoding);\n\n // Handle strings\n if (typeof body === 'string') return body;\n\n // Handle objects and arrays (including Date, etc.)\n if (typeof body === 'object') return handleObjectsAndArrays(body);\n\n // Handle all primitives and functions - they all stringify the same way\n // This covers: number, boolean, bigint, symbol, function\n return String(body as string);\n};\n\nconst handleBuffer = (body: Buffer, encoding: 'base64' | 'binary' | 'utf8'): string => {\n if (encoding === 'base64') return body.toString('base64');\n if (encoding === 'binary') return body.toString('binary');\n return body.toString('utf8');\n};\n\nconst handleUint8Array = (body: Uint8Array, encoding: 'base64' | 'binary' | 'utf8'): string => {\n const buffer = Buffer.from(body);\n return handleBuffer(buffer, encoding);\n};\n\nconst handleArrayBuffer = (body: ArrayBuffer, encoding: 'base64' | 'binary' | 'utf8'): string => {\n const buffer = Buffer.from(body);\n return handleBuffer(buffer, encoding);\n};\n\nconst handleObjectsAndArrays = (body: unknown): string => {\n try {\n return JSON.stringify(body);\n } catch (_) {\n // Fallback for non-serializable objects (circular refs, etc.)\n // This will return something like \"[object Object]\" but that's better than crashing\n return String(body);\n }\n};\n",
27
26
  "import { httpStatus, httpStatusCode } from '@constants/http.ts';\nimport type { InternalHttpStatus, InternalHttpStatusCode } from '@typedefs/constants/http.js';\n\n/**\n * Map of status codes to their corresponding status messages\n * Built dynamically from the constants to ensure they stay in sync\n */\nconst statusCodeMap = new Map<number, string>();\n\n// Build the map from the constants\nfor (const [key, code] of Object.entries(httpStatusCode)) {\n const statusKey = key as keyof typeof httpStatus;\n const message = httpStatus[statusKey];\n\n statusCodeMap.set(code, message);\n}\n\n/**\n * Map a status code to its corresponding HTTP status message\n *\n * @param statusCode - The HTTP status code to map\n * @returns The corresponding HTTP status message\n *\n * @example\n * ```typescript\n * mapStatusCodeToMessage(200) // returns \"OK\"\n * mapStatusCodeToMessage(404) // returns \"Not Found\"\n * mapStatusCodeToMessage(500) // returns \"Internal Server Error\"\n * ```\n */\nexport const mapStatusCodeToMessage = (statusCode: InternalHttpStatusCode): InternalHttpStatus => {\n const message = statusCodeMap.get(statusCode);\n\n if (!message) {\n throw new Error(`Unknown status code: ${statusCode}`);\n }\n\n return message as InternalHttpStatus;\n};\n\n/**\n * Check if a status code is valid/supported\n *\n * @param statusCode - The status code to validate\n * @returns true if the status code is supported\n */\nexport const isValidStatusCode = (statusCode: number): statusCode is InternalHttpStatusCode => statusCodeMap.has(statusCode);\n",
28
27
  "/**\n * Response header validation utilities for security and format compliance\n *\n * This module contains security-focused header validation functions,\n * particularly for preventing CRLF injection attacks in outgoing response headers.\n */\n\n/**\n * Validate response header value for CRLF injection attempts\n * Use this when setting response headers with user input\n */\nexport const validateResponseHeaderValue = (headerName: string, headerValue: string): void => {\n // SECURITY: Check for CRLF injection in response header values\n // This is where CRLF injection actually matters - when setting response headers\n\n if (typeof headerValue !== 'string') {\n throw new Error(`Header value must be a string, got ${typeof headerValue}`);\n }\n\n // SECURITY: Detect CRLF injection patterns that could inject additional headers\n if (headerValue.includes('\\r') || headerValue.includes('\\n')) {\n throw new Error(`Header value contains invalid line break characters: ${headerName}`);\n }\n\n // SECURITY: Detect common injection patterns\n const suspiciousPatterns = [\n // Pattern: value\\r\\nSet-Cookie: or value\\nLocation: etc.\n /[\\r\\n](?:set-cookie|location|authorization|www-authenticate):/i,\n // Pattern: Double CRLF (could inject HTTP response)\n /\\r\\n\\r\\n|\\n\\n/,\n // Pattern: HTTP response line injection\n /[\\r\\n]http\\/\\d\\.\\d\\s+\\d+/i,\n ];\n\n for (const pattern of suspiciousPatterns) {\n if (pattern.test(headerValue)) {\n throw new Error(`Header value contains suspicious injection pattern: ${headerName}`);\n }\n }\n};\n\n/**\n * Validate multiple response header values\n * Convenience function for validating header objects\n */\nexport const validateResponseHeaders = (headers: Record<string, string>): void => {\n for (const [name, value] of Object.entries(headers)) {\n validateResponseHeaderValue(name, value);\n }\n};\n\n/**\n * Filter and validate headers, removing undefined values and validating security\n * Returns only valid, defined header entries\n */\nexport const filterAndValidateHeaders = (headers: Partial<Record<string, string | undefined>>): Record<string, string> => {\n // Filter out undefined values using simple, readable approach\n const definedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n definedHeaders[key] = value;\n }\n }\n\n // Validate the filtered headers\n validateResponseHeaders(definedHeaders);\n\n return definedHeaders;\n};\n",
29
- "/**\n * Determine the Content-Length header value for a response body\n *\n * Calculates the byte length of the response body after it's been\n * formatted into a string for HTTP transmission. This accounts for\n * different encodings and body types.\n */\n\n/**\n * Calculates the byte size of a response body for Content-Length,\n * supporting Buffer (binary), string (utf8), and JSON-serializable objects.\n *\n * - Buffers: returns .length (raw binary)\n * - Strings: returns byte length in utf8 (handles text, base64, and \"binary\" as string)\n * - Objects: JSON.stringify, then utf8 byte length\n * - null/undefined/unknown: returns 0\n *\n * Yes, this covers binary data—if you pass a Buffer, you get the true binary size.\n * If you pass a \"binary\" string (e.g., '\\xFF\\xD8'), you get the utf8 byte length,\n * which matches Node's Content-Length calculation for string bodies.\n */\nexport const calculateContentSizeInBytes = (responseBody: unknown): number => {\n if (_isBuffer(responseBody)) {\n return responseBody.length;\n }\n\n if (typeof responseBody === 'string') {\n // Handles text, base64, and \"binary\" as string (e.g., '\\xFF\\xD8')\n return Buffer.byteLength(responseBody, 'utf8');\n }\n\n if (_isJsonSerializableObject(responseBody)) {\n try {\n const json = JSON.stringify(responseBody);\n return Buffer.byteLength(json, 'utf8');\n } catch {\n return 0;\n }\n }\n\n return 0;\n};\n\nconst _isBuffer = (val: unknown): val is Buffer =>\n // Node.js Buffer check (covers binary data)\n typeof Buffer !== 'undefined' && Buffer.isBuffer(val);\n\nconst _isJsonSerializableObject = (val: unknown): val is object => typeof val === 'object' && val !== null;\n",
28
+ "import { formatBodyIntoString } from '@core/execution/utils/formatBodyIntoString.ts';\nimport { determineEncoding } from '@core/execution/utils/determineEncoding.ts';\nimport { inferContentType } from '@core/execution/utils/inferContentType.ts';\nimport { mapStatusCodeToMessage } from '@core/execution/utils/mapStatusCodeToMessage.ts';\nimport { filterAndValidateHeaders } from '@core/execution/utils/validateResponseHeaders.ts';\nimport { httpEncoding, httpStatus, httpStatusCode } from '@constants/http.ts';\nimport type { InternalHttpEncoding, InternalHttpHeaders, InternalHttpStatus, InternalHttpStatusCode } from '@typedefs/constants/http.js';\nimport type { Request } from '@typedefs/public/Request.ts';\nimport type { InternalResponseImpl } from '@typedefs/internal/InternalResponseImpl.d.ts';\n\n/** Format current time as HTTP Date header (RFC 7231 §7.1.1.1) */\nconst _formatHttpDate = (): string => {\n const d = new Date();\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as const;\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] as const;\n const pad = (n: number): string => (n < 10 ? `0${n}` : String(n));\n return `${days[d.getUTCDay()]}, ${pad(d.getUTCDate())} ${months[d.getUTCMonth()]} ${d.getUTCFullYear()} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} GMT`;\n};\n\n/** Cached HTTP Date header refreshed every second. Avoids Date allocation per response. */\nlet _cachedDateHeader = _formatHttpDate();\nconst _dateRefreshTimer = setInterval(() => {\n _cachedDateHeader = _formatHttpDate();\n}, 1000);\n_dateRefreshTimer.unref();\n\nexport class ResponseImpl implements InternalResponseImpl {\n readonly _request: Request;\n\n _statusCode: InternalHttpStatusCode = httpStatusCode.ok;\n _status: InternalHttpStatus = httpStatus.ok;\n _headers: Partial<Record<InternalHttpHeaders, string>> = {};\n _setCookies: Array<string> = []; // Track multiple Set-Cookie headers\n _body: unknown = '';\n _stringBody = '';\n _encoding: InternalHttpEncoding = httpEncoding.utf8;\n\n constructor(request: Request) {\n this._request = request;\n this._setSecurityHeaders();\n }\n\n _parseResponseIntoString(): void {\n const statusLine = `${this._request.protocol} ${this._statusCode} ${this._status}`;\n const encoding = determineEncoding(this._headers['content-type'], this._body);\n const body = formatBodyIntoString(this._body, { encoding });\n\n // Set Date + Content-Length BEFORE assembling headers into the response string.\n // Content-Length = body bytes only (not status line + headers), per HTTP/1.1 spec.\n this._setHeadersIfNotSet({\n Date: _cachedDateHeader,\n 'Content-Length': String(Buffer.byteLength(body, 'utf8')),\n });\n\n this._encoding = encoding;\n\n const headerLines = Object.entries(this._headers).map(([key, value]) => `${key}: ${value}`);\n const setCookieLines = this._setCookies.map((value) => `Set-Cookie: ${value}`);\n const allHeaderLines = [...headerLines, ...setCookieLines];\n const headersSection = allHeaderLines.length > 0 ? `${allHeaderLines.join('\\r\\n')}\\r\\n` : '';\n\n this._stringBody = `${statusLine}\\r\\n${headersSection}\\r\\n${body}`;\n }\n\n _setHeadersIfNotSet(headers: Partial<Record<InternalHttpHeaders, string>>): void {\n // SECURITY: Filter undefined values and validate response headers for CRLF injection\n // Only validate headers that aren't already set\n const headersToSet: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined && !(key in this._headers)) {\n headersToSet[key] = value;\n }\n }\n\n const validatedHeaders = filterAndValidateHeaders(headersToSet);\n\n // Set headers after validation passes\n Object.assign(this._headers, validatedHeaders);\n }\n\n _setBody(body: unknown): void {\n this._body = body;\n\n // Auto-set content-type if not already set\n if (!this._headers['content-type']) {\n const detectedContentType = inferContentType(body);\n this._setHeadersIfNotSet({\n 'Content-Type': detectedContentType,\n });\n }\n }\n\n setStatusCode(statusCode: InternalHttpStatusCode): void {\n this._statusCode = statusCode;\n this._status = mapStatusCodeToMessage(statusCode);\n }\n\n addHeaders(headers: Partial<Record<InternalHttpHeaders, string>>): void {\n // SECURITY: Filter undefined values and validate response headers for CRLF injection\n const validatedHeaders = filterAndValidateHeaders(headers);\n\n // Handle Set-Cookie specially - support multiple values\n for (const [key, value] of Object.entries(validatedHeaders)) {\n if (key === 'Set-Cookie') {\n if (value) {\n this._setCookies.push(value);\n }\n } else {\n // Regular headers overwrite if duplicate\n this._headers[key] = value;\n }\n }\n }\n\n removeHeaders(headerNames: Array<InternalHttpHeaders>): void {\n for (const headerName of headerNames) {\n delete this._headers[headerName];\n }\n }\n\n /**\n * Set default security headers to protect against common vulnerabilities\n * These headers are set only if not already present, allowing users to override if needed\n */\n _setSecurityHeaders(): void {\n this._setHeadersIfNotSet({\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'X-XSS-Protection': '1; mode=block',\n 'Referrer-Policy': 'strict-origin-when-cross-origin',\n });\n }\n}\n",
30
29
  "import { RequestImpl } from '@core/execution/RequestImpl.ts';\nimport { ResponseImpl } from '@core/execution/ResponseImpl.ts';\nimport type { SetupImpl } from '@core/setup/SetupImpl.ts';\nimport type { InternalContextImpl } from '@typedefs/internal/InternalContextImpl.js';\nimport type { InternalRequestImpl } from '@typedefs/internal/InternalRequestImpl.js';\nimport type { InternalResponseImpl } from '@typedefs/internal/InternalResponseImpl.js';\nimport type { CookieOptions, Cookies } from '@typedefs/public/CookieParser.js';\nimport type { Request } from '@typedefs/public/Request.ts';\nimport type { Response } from '@typedefs/public/Response.ts';\n\n/**\n * ContextImpl is the core class that handles the building of the context.\n * It is responsible for building the request and response objects, plus\n * managing user-defined state data.\n *\n * ## What is ContextImpl?\n *\n * ContextImpl is the concrete implementation of the Context interface. It's\n * automatically created for each incoming request and provides a unified\n * interface for accessing request data, controlling responses, and managing\n * request-scoped state.\n *\n * ## How ContextImpl Works\n *\n * 1. **Construction**: Creates RequestImpl and ResponseImpl instances\n * 2. **State Initialization**: Initializes empty state object for user data\n * 3. **Request Processing**: Handles raw request data and builds structured request\n * 4. **Response Control**: Provides methods to control HTTP response behavior\n * 5. **State Management**: Allows middleware and handlers to store custom data\n * 6. **Cleanup**: Automatically garbage collected when request completes\n *\n * @example\n * ```typescript\n * // ContextImpl is automatically created for each request\n * const context = new ContextImpl(rawRequest, setup, clientAddress);\n *\n * // Access request data\n * const userId = context.request.params.id;\n * const userData = context.request.body;\n *\n * // Store custom state\n * context.state.user = { id: userId, name: 'John' };\n * context.state.requestId = generateRequestId();\n *\n * // Control response\n * context.response.setStatusCode(200);\n * context.response.addHeaders({ 'X-User-ID': userId });\n *\n * // Return response data\n * return { success: true, user: context.state.user };\n * ```\n *\n * @see {@link Context} for the public interface\n * @see {@link RequestImpl} for request implementation details\n * @see {@link ResponseImpl} for response implementation details\n * @see {@link InternalContextImpl} for internal interface details\n */\nexport class ContextImpl implements InternalContextImpl {\n readonly _request: InternalRequestImpl;\n readonly _response: InternalResponseImpl;\n\n /**\n * The incoming request object containing all request data and metadata.\n *\n * Provides access to headers, body, query parameters, route parameters,\n * and client information like IP address.\n *\n * @example\n * ```typescript\n * const handler: HandlerCallback = async (ctx) => {\n * // Access route parameters\n * const userId = ctx.request.params.id;\n *\n * // Access request body\n * const userData = ctx.request.body;\n *\n * // Access headers\n * const authToken = ctx.request.headers.authorization;\n *\n * // Access client information\n * const clientIp = ctx.request.ipAddress;\n *\n * return { userId, userData, hasAuth: !!authToken };\n * };\n * ```\n *\n * @see {@link Request} for complete request interface documentation\n */\n request: Request;\n\n /**\n * The outgoing response object for controlling HTTP response behavior.\n *\n * Provides methods to set status codes, add headers, and control\n * response formatting and content.\n *\n * @example\n * ```typescript\n * const handler: HandlerCallback = async (ctx) => {\n * const { request, response } = ctx;\n *\n * // Set successful status code\n * response.setStatusCode(201);\n *\n * // Add custom headers\n * response.addHeaders({\n * 'Location': `/api/users/${request.params.id}`,\n * 'X-User-ID': request.params.id,\n * 'Cache-Control': 'max-age=3600'\n * });\n *\n * // Return response body (Content-Type automatically set)\n * return { message: 'User created successfully' };\n * };\n * ```\n *\n * @see {@link Response} for complete response interface documentation\n */\n response: Response;\n\n /**\n * User-defined state data that persists throughout the request lifecycle.\n *\n * This property allows middleware and route handlers to store and share\n * custom data. The state is request-scoped and will be automatically garbage\n * collected when the request completes and the context goes out of scope.\n *\n * ## State Lifecycle\n *\n * 1. **Request Start**: State object is created as empty object\n * 2. **Global Hooks**: `beforeAll` hooks can populate state\n * 3. **Route Hooks**: `beforeHooks` can access and modify state\n * 4. **Route Handler**: Your handler can access and modify state\n * 5. **Route Hooks**: `afterHooks` can access state and modify response\n * 6. **Global Hooks**: `afterAll` hooks can access state and modify response\n * 7. **Request End**: Context goes out of scope and is automatically garbage collected\n *\n * @example\n * ```typescript\n * // Authentication middleware\n * const authMiddleware: HandlerCallback = async (ctx) => {\n * const token = ctx.request.headers.authorization;\n * const user = await validateToken(token);\n *\n * // Store user data in state for route handlers\n * ctx.state.user = user;\n * ctx.state.permissions = await getUserPermissions(user.id);\n * ctx.state.isAuthenticated = true;\n * };\n *\n * // Route that uses the authenticated state\n * app.get('/api/admin/users', authMiddleware, async (ctx) => {\n * // Access the user data set by middleware\n * const { user, permissions, isAuthenticated } = ctx.state;\n *\n * if (!permissions.includes('admin')) {\n * throw new Error('Insufficient permissions');\n * }\n *\n * // Add route-specific state\n * ctx.state.routeAccessed = true;\n * ctx.state.lastAccess = new Date();\n *\n * return { message: 'Admin access granted', user };\n * });\n * ```\n *\n * @see {@link Context} for complete context interface documentation\n */\n state: Record<string, unknown> = {};\n\n /**\n * Creates a new ContextImpl instance for handling a single request.\n *\n * @param rawRequest - The raw request data (Buffer or string) from the client\n * @param setup - The SetupImpl instance that manages routes and hooks\n * @param clientAddress - Optional client IP address for security and logging\n *\n * @example\n * ```typescript\n * // ContextImpl is typically created internally by YinzerFlow\n * const context = new ContextImpl(\n * Buffer.from('GET /api/users HTTP/1.1...'),\n * setupInstance,\n * '192.168.1.100'\n * );\n *\n * // Access the context properties\n * console.log(context.request.method); // \"GET\"\n * console.log(context.request.url); // \"/api/users\"\n * console.log(context.request.ipAddress); // \"192.168.1.100\"\n * ```\n *\n * @see {@link SetupImpl} for setup implementation details\n * @see {@link RequestImpl} for request building details\n * @see {@link ResponseImpl} for response building details\n */\n cookies: Cookies = {\n set: (_name: string, _value: string, _options?: CookieOptions): void => {\n // Initialized by cookieParserHook if enabled\n },\n sign: (_name: string, _value: string): string => '',\n unsign: (_name: string, _signedValue: string): string | false => false,\n };\n\n constructor(rawRequest: Buffer | string, setup: SetupImpl, clientAddress?: string) {\n this._request = new RequestImpl(rawRequest, setup, clientAddress);\n this._response = new ResponseImpl(this._request);\n\n this.request = this._request;\n this.response = this._response;\n }\n}\n",
31
- "import type { ServerOptions } from '@typedefs/public/Configuration.js';\nimport type { InternalServerOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { log } from '@core/utils/log.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\n\n/**\n * Default body parser configuration with secure defaults\n */\nconst DEFAULT_BODY_PARSER_CONFIG = {\n json: {\n maxSize: 262144, // 256KB - reasonable for JSON APIs (Express uses 100KB)\n maxDepth: 10, // Prevent deeply nested objects that can cause stack overflow\n allowPrototypeProperties: false, // SECURITY: Block prototype pollution by default\n maxKeys: 1000, // Prevent memory exhaustion from objects with too many keys\n maxStringLength: 1048576, // 1MB per string - prevent memory exhaustion\n maxArrayLength: 10000, // Prevent memory exhaustion from large arrays\n },\n fileUploads: {\n maxFileSize: 10485760, // 10MB per file - reasonable for documents/images\n maxTotalSize: 52428800, // 50MB total - prevent bulk upload attacks\n maxFiles: 10, // Reasonable number of files per request\n allowedExtensions: [], // Empty array = all extensions allowed\n blockedExtensions: ['.exe', '.bat', '.cmd', '.scr', '.pif', '.com'], // Block dangerous executables\n maxFilenameLength: 255, // Standard filesystem limit\n },\n urlEncoded: {\n maxSize: 1048576, // 1MB for form data\n maxFields: 1000, // Prevent field spam attacks\n maxFieldNameLength: 100, // Reasonable field name length\n maxFieldLength: 1048576, // 1MB per field value\n },\n};\n\n/**\n * Default IP security configuration with secure defaults\n */\nconst DEFAULT_IP_SECURITY_CONFIG = {\n trustedProxies: ['127.0.0.1', '::1'], // Localhost only by default\n allowPrivateIps: true, // Allow private IPs for internal usage\n headerPreference: ['x-forwarded-for', 'x-real-ip', 'cf-connecting-ip', 'x-client-ip', 'true-client-ip'],\n maxChainLength: 10, // Reasonable proxy chain length\n detectSpoofing: true, // Enable security by default\n};\n\n/**\n * Default configuration object\n */\nconst DEFAULT_CONFIGURATION: InternalServerOptions = {\n port: 5000,\n host: '0.0.0.0',\n networkLogs: false,\n gracefulShutdownTimeout: '15m', // Enabled by default\n cors: {\n enabled: false, // Disabled by default\n },\n bodyParser: DEFAULT_BODY_PARSER_CONFIG,\n ipSecurity: DEFAULT_IP_SECURITY_CONFIG,\n};\n\n/**\n * Validate JSON parser configuration minimums\n */\nconst _validateJsonConfig = (config: InternalServerOptions['bodyParser']['json']): void => {\n if (config.maxSize < 1) {\n throw new Error('bodyParser.json.maxSize must be at least 1 byte');\n }\n\n if (config.maxDepth < 1) {\n throw new Error('bodyParser.json.maxDepth must be at least 1');\n }\n\n if (config.maxKeys < 1) {\n throw new Error('bodyParser.json.maxKeys must be at least 1');\n }\n\n if (config.maxStringLength < 1) {\n throw new Error('bodyParser.json.maxStringLength must be at least 1 byte');\n }\n\n if (config.maxArrayLength < 1) {\n throw new Error('bodyParser.json.maxArrayLength must be at least 1');\n }\n};\n\n/**\n * Validate file upload configuration minimums\n */\nconst _validateFileUploadConfig = (config: InternalServerOptions['bodyParser']['fileUploads']): void => {\n if (config.maxFileSize < 1) {\n throw new Error('bodyParser.fileUploads.maxFileSize must be at least 1 byte');\n }\n\n if (config.maxTotalSize < 1) {\n throw new Error('bodyParser.fileUploads.maxTotalSize must be at least 1 byte');\n }\n\n if (config.maxFiles < 1) {\n throw new Error('bodyParser.fileUploads.maxFiles must be at least 1');\n }\n\n if (config.maxFilenameLength < 1) {\n throw new Error('bodyParser.fileUploads.maxFilenameLength must be at least 1 character');\n }\n};\n\n/**\n * Validate URL-encoded configuration minimums\n */\nconst _validateUrlEncodedConfig = (config: InternalServerOptions['bodyParser']['urlEncoded']): void => {\n if (config.maxSize < 1) {\n throw new Error('bodyParser.urlEncoded.maxSize must be at least 1 byte');\n }\n\n if (config.maxFields < 1) {\n throw new Error('bodyParser.urlEncoded.maxFields must be at least 1');\n }\n\n if (config.maxFieldNameLength < 1) {\n throw new Error('bodyParser.urlEncoded.maxFieldNameLength must be at least 1 character');\n }\n\n if (config.maxFieldLength < 1) {\n throw new Error('bodyParser.urlEncoded.maxFieldLength must be at least 1 byte');\n }\n};\n\n/**\n * Validate IP security configuration minimums\n */\nconst _validateIpSecurityConfig = (config: InternalServerOptions['ipSecurity']): void => {\n if (!Array.isArray(config.trustedProxies)) {\n throw new Error('ipSecurity.trustedProxies must be an array');\n }\n\n if (!Array.isArray(config.headerPreference)) {\n throw new Error('ipSecurity.headerPreference must be an array');\n }\n\n if (config.headerPreference.length === 0) {\n throw new Error('ipSecurity.headerPreference must contain at least one header');\n }\n\n if (config.maxChainLength < 1) {\n throw new Error('ipSecurity.maxChainLength must be at least 1');\n }\n\n if (config.maxChainLength > 50) {\n throw new Error('ipSecurity.maxChainLength must not exceed 50 to prevent DoS attacks');\n }\n};\n\n/**\n * Issue security warnings for risky JSON configurations\n */\nconst _warnJsonConfig = (config: InternalServerOptions['bodyParser']['json']): void => {\n if (config.allowPrototypeProperties) {\n log.warn(\n '[SECURITY WARNING] bodyParser.json.allowPrototypeProperties is enabled. This allows prototype pollution attacks. ' +\n 'Only enable this if you absolutely need it and have other protections in place.',\n );\n }\n\n // Warn about very large JSON sizes (but don't block them)\n if (config.maxSize > 10485760) {\n // 10MB\n log.warn(\n `[SECURITY WARNING] bodyParser.json.maxSize is set to ${config.maxSize} bytes (${Math.round(config.maxSize / 1024 / 1024)}MB). ` +\n 'Large JSON payloads can cause memory exhaustion and DoS attacks. Consider if this size is necessary.',\n );\n }\n\n // Warn about very deep nesting (but don't block it)\n if (config.maxDepth > 50) {\n log.warn(\n `[SECURITY WARNING] bodyParser.json.maxDepth is set to ${config.maxDepth}. ` +\n 'Very deep JSON nesting can cause stack overflow attacks. Consider if this depth is necessary.',\n );\n }\n};\n\n/**\n * Issue security warnings for risky file upload configurations\n */\nconst _warnFileUploadConfig = (config: InternalServerOptions['bodyParser']['fileUploads']): void => {\n // Warn about very large file uploads (but don't block them)\n if (config.maxFileSize > 104857600) {\n // 100MB\n log.warn(\n `[SECURITY WARNING] bodyParser.fileUploads.maxFileSize is set to ${config.maxFileSize} bytes (${Math.round(config.maxFileSize / 1024 / 1024)}MB). ` +\n 'Large file uploads can consume significant server resources.',\n );\n }\n\n if (config.maxTotalSize > 1073741824) {\n // 1GB\n log.warn(\n `[SECURITY WARNING] bodyParser.fileUploads.maxTotalSize is set to ${config.maxTotalSize} bytes (${Math.round(config.maxTotalSize / 1024 / 1024 / 1024)}GB). ` +\n 'Very large total upload sizes can cause memory and disk space exhaustion.',\n );\n }\n\n // Validate file extension security\n const dangerousExtensions = ['.exe', '.bat', '.cmd', '.scr', '.pif', '.com', '.vbs', '.jar', '.app'];\n const allowedDangerous = config.allowedExtensions.filter((ext) => dangerousExtensions.includes(ext.toLowerCase()));\n\n if (allowedDangerous.length > 0) {\n log.warn(\n `[SECURITY WARNING] bodyParser.fileUploads.allowedExtensions includes dangerous file types: ${allowedDangerous.join(', ')}. ` +\n 'This could allow execution of malicious files. Only allow these if absolutely necessary.',\n );\n }\n\n // Warn if no blocked extensions and no allowed extensions (completely open)\n if (config.blockedExtensions.length === 0 && config.allowedExtensions.length === 0) {\n log.warn(\n '[SECURITY WARNING] File uploads have no extension restrictions (no blockedExtensions and no allowedExtensions). ' +\n 'Consider adding blockedExtensions or allowedExtensions to improve security.',\n );\n }\n};\n\n/**\n * Issue security warnings for risky IP security configurations\n */\nconst _warnIpSecurityConfig = (config: InternalServerOptions['ipSecurity']): void => {\n // Warn about wildcard or overly permissive trusted proxies\n if (config.trustedProxies.length === 0) {\n log.warn('[SECURITY WARNING] ipSecurity.trustedProxies is empty. No proxy headers will be trusted, which may prevent proper client IP detection.');\n }\n\n // Warn about very long proxy chains\n if (config.maxChainLength > 20) {\n log.warn(\n `[SECURITY WARNING] ipSecurity.maxChainLength is set to ${config.maxChainLength}. ` +\n 'Very long proxy chains can consume significant resources and may indicate amplification attacks.',\n );\n }\n\n // Warn if spoofing detection is disabled\n if (!config.detectSpoofing) {\n log.warn(\n '[SECURITY WARNING] ipSecurity.detectSpoofing is disabled. ' +\n 'This reduces protection against IP spoofing attacks. Only disable if you have other protective measures.',\n );\n }\n};\n\n/**\n * Handle body parser configuration merging and validation\n */\nconst _handleBodyParserConfig = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.bodyParser) {\n defaultConfig.bodyParser = {\n json: {\n ...DEFAULT_BODY_PARSER_CONFIG.json,\n ...userConfig.bodyParser.json,\n },\n fileUploads: {\n ...DEFAULT_BODY_PARSER_CONFIG.fileUploads,\n ...userConfig.bodyParser.fileUploads,\n },\n urlEncoded: {\n ...DEFAULT_BODY_PARSER_CONFIG.urlEncoded,\n ...userConfig.bodyParser.urlEncoded,\n },\n };\n\n // Validate configuration for security\n _validateBodyParserConfig(defaultConfig.bodyParser);\n }\n};\n\n/**\n * Handle IP security configuration merging and validation\n */\nconst _handleIpSecurityConfig = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.ipSecurity) {\n defaultConfig.ipSecurity = {\n ...DEFAULT_IP_SECURITY_CONFIG,\n ...userConfig.ipSecurity,\n };\n\n // Validate configuration for security\n _validateIpSecurityConfig(defaultConfig.ipSecurity);\n _warnIpSecurityConfig(defaultConfig.ipSecurity);\n }\n};\n\n/**\n * Validate port number\n */\nconst _validatePort = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.port !== undefined) {\n const normalizedPort = Number(userConfig.port);\n if (isNaN(normalizedPort) || normalizedPort < 1 || normalizedPort > 65535) {\n throw new Error('Invalid port number');\n }\n defaultConfig.port = normalizedPort;\n }\n};\n\n/**\n * Validate body parser configuration to prevent broken settings and warn about risky configurations\n */\nconst _validateBodyParserConfig = (config: InternalServerOptions['bodyParser']): void => {\n // Validate minimums for all parser types\n _validateJsonConfig(config.json);\n _validateFileUploadConfig(config.fileUploads);\n _validateUrlEncodedConfig(config.urlEncoded);\n\n // Issue security warnings for risky configurations (but don't block them)\n _warnJsonConfig(config.json);\n _warnFileUploadConfig(config.fileUploads);\n};\n\n/**\n * Handle custom configuration\n */\nexport const handleCustomConfiguration = (configuration?: ServerOptions): InternalServerOptions => {\n // Start with default configuration\n const result = { ...DEFAULT_CONFIGURATION };\n\n // Merge user configuration with proper handling of nested objects\n Object.assign(result, configuration);\n\n // Handle special configuration sections\n _handleBodyParserConfig(result, configuration);\n _handleIpSecurityConfig(result, configuration);\n _validatePort(result, configuration);\n\n return result;\n};\n",
32
- "import { colors } from '@constants/colors.ts';\nimport { httpStatusCode } from '@constants/http.ts';\nimport { log } from '@core/utils/log.ts';\nimport type { InternalGlobalHookOptions, InternalHookRegistryImpl } from '@typedefs/internal/InternalHookRegistryImpl.js';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\n\nexport class HookRegistryImpl implements InternalHookRegistryImpl {\n readonly _beforeRouting: Set<{\n handler: HandlerCallback;\n options?: InternalGlobalHookOptions;\n }>;\n readonly _beforeAll: Set<{\n handler: HandlerCallback;\n options?: InternalGlobalHookOptions;\n }>;\n readonly _afterAll: Set<{\n handler: HandlerCallback;\n options?: InternalGlobalHookOptions;\n }>;\n _onError: HandlerCallback;\n _onNotFound: HandlerCallback;\n\n constructor() {\n this._beforeRouting = new Set();\n this._beforeAll = new Set();\n this._afterAll = new Set();\n this._onError = (ctx, error: unknown): unknown => {\n log.error('Error while handeling your request: ', error);\n ctx.response.setStatusCode(httpStatusCode.internalServerError);\n return { success: false, message: 'Internal Server Error' };\n };\n this._onNotFound = (ctx): unknown => {\n ctx.response.setStatusCode(httpStatusCode.notFound);\n return { success: false, message: '404 Not Found' };\n };\n }\n\n _addBeforeRoutingHooks(handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions): void {\n this._validateHandlersArray(handlers, 'beforeRouting');\n for (const handler of handlers) this._beforeRouting.add({ handler, options: options ?? { routesToExclude: [], routesToInclude: [] } });\n }\n\n _addBeforeHooks(handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions): void {\n this._validateHandlersArray(handlers, 'beforeAll');\n for (const handler of handlers) this._beforeAll.add({ handler, options: options ?? { routesToExclude: [], routesToInclude: [] } });\n }\n\n _addAfterHooks(handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions): void {\n this._validateHandlersArray(handlers, 'afterAll');\n for (const handler of handlers) this._afterAll.add({ handler, options: options ?? { routesToExclude: [], routesToInclude: [] } });\n }\n\n private _validateHandlersArray(handlers: unknown, methodName: string): asserts handlers is Array<HandlerCallback> {\n if (!Array.isArray(handlers)) {\n const receivedType = typeof handlers;\n const isFunction = receivedType === 'function';\n\n throw new Error(\n `YinzerFlow: ${methodName}() expects an array of handler functions, but received ${receivedType}.${\n isFunction ?\n `\\n\\n❌ Incorrect: app.${methodName}${colors.red}(${colors.reset}(ctx) => { ... }${colors.red})${colors.reset}\\n✅ Correct: app.${methodName}${colors.green}([${colors.reset}(ctx) => { ... }${colors.green}])${colors.reset}\\n\\nNote: Wrap your handler function in ${colors.magenta}square brackets${colors.reset} to make it an array.\\n\\n`\n : `\\n\\n Expected: Array<HandlerCallback>\\n Received: ${receivedType}`\n }`,\n );\n }\n\n if (handlers.length === 0) {\n log.warn(`${methodName}() called with empty array. No hooks will be registered.`);\n return;\n }\n\n for (let i = 0; i < handlers.length; i++) {\n const handler = handlers[i] as unknown;\n if (typeof handler !== 'function') {\n throw new Error(`YinzerFlow: ${methodName}() array contains non-function at index ${i}. Expected: function, received: ${typeof handler}`);\n }\n }\n }\n\n _addOnError(handler: HandlerCallback): void {\n this._onError = handler;\n }\n\n _addOnNotFound(handler: HandlerCallback): void {\n this._onNotFound = handler;\n }\n}\n",
30
+ "import type { TimeString } from '@typedefs/public/Time.js';\n\n/**\n * Convert time string to milliseconds\n *\n * Internal helper for parsing user-friendly time formats into milliseconds.\n * Supports seconds (s), minutes (m), hours (h), and days (d).\n *\n * @param time - Time string in format: number + unit (s/m/h/d)\n * @returns Time in milliseconds\n * @throws Error if time format is invalid\n *\n * @example\n * ```typescript\n * _convertTimeToMs('500ms') // 500\n * _convertTimeToMs('30s') // 30000\n * _convertTimeToMs('15m') // 900000\n * _convertTimeToMs('2h') // 7200000\n * _convertTimeToMs('1d') // 86400000\n * ```\n *\n * @internal\n */\nexport const _convertTimeToMs = (time: TimeString | number): number => {\n if (typeof time === 'number') {\n if (!Number.isFinite(time) || time <= 0) {\n throw new Error(`Invalid time value: \"${time}\". Must be a positive number`);\n }\n return time;\n }\n\n // Validate string format\n if (typeof time !== 'string' || time.length < 2) {\n throw new Error('Invalid time format. Expected format: 1ms, 1s, 1m, 1h, 1d');\n }\n\n // Extract unit (last character)\n const unit = time.includes('ms') ? time.slice(-2) : time.slice(-1);\n const value = time.includes('ms') ? time.slice(0, -2) : time.slice(0, -1);\n\n // Validate unit\n if (!['ms', 's', 'm', 'h', 'd'].includes(unit)) {\n throw new Error(`Invalid time unit: \"${unit}\". Expected: s (seconds), m (minutes), h (hours), or d (days)`);\n }\n\n // Parse numeric value\n const numValue = Number(value);\n if (isNaN(numValue) || numValue <= 0) {\n throw new Error(`Invalid time value: \"${value}\". Must be a positive number`);\n }\n\n // Convert to milliseconds based on unit\n switch (unit) {\n case 'ms':\n return numValue;\n case 's':\n return numValue * 1000;\n case 'm':\n return numValue * 60 * 1000;\n case 'h':\n return numValue * 60 * 60 * 1000;\n case 'd':\n return numValue * 24 * 60 * 60 * 1000;\n default:\n throw new Error(`Unsupported time unit: \"${unit}\"`);\n }\n};\n",
31
+ "import type { ServerOptions } from '@typedefs/public/Configuration.js';\nimport type { InternalLoggingOptions, InternalServerOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport { log, loggerBrand } from '@core/utils/log.ts';\nimport { logLevels } from '@constants/log.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport { _convertBytesToBytes, _formatBytesForDisplay } from '@core/utils/bytes.ts';\n\n/**\n * Default body parser configuration with secure defaults\n */\nconst DEFAULT_BODY_PARSER_CONFIG = {\n json: {\n maxSize: 262144, // 256KB - reasonable for JSON APIs (Express uses 100KB)\n maxDepth: 10, // Prevent deeply nested objects that can cause stack overflow\n allowPrototypeProperties: false, // SECURITY: Block prototype pollution by default\n maxKeys: 1000, // Prevent memory exhaustion from objects with too many keys\n maxStringLength: 1048576, // 1MB per string - prevent memory exhaustion\n maxArrayLength: 10000, // Prevent memory exhaustion from large arrays\n },\n fileUploads: {\n maxFileSize: 10485760, // 10MB per file - reasonable for documents/images\n maxTotalSize: 52428800, // 50MB total - prevent bulk upload attacks\n maxFiles: 10, // Reasonable number of files per request\n allowedExtensions: [], // Empty array = all extensions allowed\n blockedExtensions: ['.exe', '.bat', '.cmd', '.scr', '.pif', '.com'], // Block dangerous executables\n maxFilenameLength: 255, // Standard filesystem limit\n },\n urlEncoded: {\n maxSize: 1048576, // 1MB for form data\n maxFields: 1000, // Prevent field spam attacks\n maxFieldNameLength: 100, // Reasonable field name length\n maxFieldLength: 1048576, // 1MB per field value\n },\n};\n\n/**\n * Default IP security configuration with secure defaults\n */\nconst DEFAULT_IP_SECURITY_CONFIG = {\n trustedProxies: ['127.0.0.1', '::1'], // Localhost only by default\n allowPrivateIps: true, // Allow private IPs for internal usage\n headerPreference: ['x-forwarded-for', 'x-real-ip', 'cf-connecting-ip', 'x-client-ip', 'true-client-ip'],\n maxChainLength: 10, // Reasonable proxy chain length\n detectSpoofing: true, // Enable security by default\n};\n\n/**\n * Default diagnostics configuration — all disabled (zero noise)\n */\nconst DEFAULT_DIAGNOSTICS_CONFIG = {\n slowRequests: false as const,\n largeResponses: false as const,\n largeRequests: false as const,\n memory: false as const,\n eventLoop: false as const,\n rateLimits: false,\n};\n\n/**\n * Default logging configuration\n */\nconst DEFAULT_LOGGING_CONFIG: InternalLoggingOptions = {\n level: 'warn',\n prefix: 'YINZER',\n personality: true,\n requests: false,\n diagnostics: DEFAULT_DIAGNOSTICS_CONFIG,\n};\n\n/**\n * Default configuration object\n */\nconst DEFAULT_CONFIGURATION: InternalServerOptions = {\n port: 5000,\n host: '0.0.0.0',\n gracefulShutdownTimeout: '15m', // Enabled by default\n cors: {\n enabled: false, // Disabled by default\n },\n logging: DEFAULT_LOGGING_CONFIG,\n bodyParser: DEFAULT_BODY_PARSER_CONFIG,\n ipSecurity: DEFAULT_IP_SECURITY_CONFIG,\n};\n\n/**\n * Validate JSON parser configuration minimums\n */\nconst _validateJsonConfig = (config: InternalServerOptions['bodyParser']['json']): void => {\n if (config.maxSize < 1) {\n throw new Error('bodyParser.json.maxSize must be at least 1 byte');\n }\n\n if (config.maxDepth < 1) {\n throw new Error('bodyParser.json.maxDepth must be at least 1');\n }\n\n if (config.maxKeys < 1) {\n throw new Error('bodyParser.json.maxKeys must be at least 1');\n }\n\n if (config.maxStringLength < 1) {\n throw new Error('bodyParser.json.maxStringLength must be at least 1 byte');\n }\n\n if (config.maxArrayLength < 1) {\n throw new Error('bodyParser.json.maxArrayLength must be at least 1');\n }\n};\n\n/**\n * Validate file upload configuration minimums\n */\nconst _validateFileUploadConfig = (config: InternalServerOptions['bodyParser']['fileUploads']): void => {\n if (config.maxFileSize < 1) {\n throw new Error('bodyParser.fileUploads.maxFileSize must be at least 1 byte');\n }\n\n if (config.maxTotalSize < 1) {\n throw new Error('bodyParser.fileUploads.maxTotalSize must be at least 1 byte');\n }\n\n if (config.maxFiles < 1) {\n throw new Error('bodyParser.fileUploads.maxFiles must be at least 1');\n }\n\n if (config.maxFilenameLength < 1) {\n throw new Error('bodyParser.fileUploads.maxFilenameLength must be at least 1 character');\n }\n};\n\n/**\n * Validate URL-encoded configuration minimums\n */\nconst _validateUrlEncodedConfig = (config: InternalServerOptions['bodyParser']['urlEncoded']): void => {\n if (config.maxSize < 1) {\n throw new Error('bodyParser.urlEncoded.maxSize must be at least 1 byte');\n }\n\n if (config.maxFields < 1) {\n throw new Error('bodyParser.urlEncoded.maxFields must be at least 1');\n }\n\n if (config.maxFieldNameLength < 1) {\n throw new Error('bodyParser.urlEncoded.maxFieldNameLength must be at least 1 character');\n }\n\n if (config.maxFieldLength < 1) {\n throw new Error('bodyParser.urlEncoded.maxFieldLength must be at least 1 byte');\n }\n};\n\n/**\n * Validate IP security configuration minimums\n */\nconst _validateIpSecurityConfig = (config: InternalServerOptions['ipSecurity']): void => {\n if (!Array.isArray(config.trustedProxies)) {\n throw new Error('ipSecurity.trustedProxies must be an array');\n }\n\n if (!Array.isArray(config.headerPreference)) {\n throw new Error('ipSecurity.headerPreference must be an array');\n }\n\n if (config.headerPreference.length === 0) {\n throw new Error('ipSecurity.headerPreference must contain at least one header');\n }\n\n if (config.maxChainLength < 1) {\n throw new Error('ipSecurity.maxChainLength must be at least 1');\n }\n\n if (config.maxChainLength > 50) {\n throw new Error('ipSecurity.maxChainLength must not exceed 50 to prevent DoS attacks');\n }\n};\n\n/**\n * Issue security warnings for risky JSON configurations\n */\nconst _warnJsonConfig = (config: InternalServerOptions['bodyParser']['json']): void => {\n if (config.allowPrototypeProperties) {\n log.warn(\n '[SECURITY WARNING] bodyParser.json.allowPrototypeProperties is enabled. This allows prototype pollution attacks. ' +\n 'Only enable this if you absolutely need it and have other protections in place.',\n );\n }\n\n // Warn about very large JSON sizes (but don't block them)\n if (config.maxSize > 10485760) {\n // 10MB\n log.warn(\n `[SECURITY WARNING] bodyParser.json.maxSize is set to ${config.maxSize} bytes (${_formatBytesForDisplay(config.maxSize)}). ` +\n 'Large JSON payloads can cause memory exhaustion and DoS attacks. Consider if this size is necessary.',\n );\n }\n\n // Warn about very deep nesting (but don't block it)\n if (config.maxDepth > 50) {\n log.warn(\n `[SECURITY WARNING] bodyParser.json.maxDepth is set to ${config.maxDepth}. ` +\n 'Very deep JSON nesting can cause stack overflow attacks. Consider if this depth is necessary.',\n );\n }\n};\n\n/**\n * Issue security warnings for risky file upload configurations\n */\nconst _warnFileUploadConfig = (config: InternalServerOptions['bodyParser']['fileUploads']): void => {\n // Warn about very large file uploads (but don't block them)\n if (config.maxFileSize > 104857600) {\n // 100MB\n log.warn(\n `[SECURITY WARNING] bodyParser.fileUploads.maxFileSize is set to ${config.maxFileSize} bytes (${_formatBytesForDisplay(config.maxFileSize)}). ` +\n 'Large file uploads can consume significant server resources.',\n );\n }\n\n if (config.maxTotalSize > 1073741824) {\n // 1GB\n log.warn(\n `[SECURITY WARNING] bodyParser.fileUploads.maxTotalSize is set to ${config.maxTotalSize} bytes (${_formatBytesForDisplay(config.maxTotalSize)}). ` +\n 'Very large total upload sizes can cause memory and disk space exhaustion.',\n );\n }\n\n // Validate file extension security\n const dangerousExtensions = ['.exe', '.bat', '.cmd', '.scr', '.pif', '.com', '.vbs', '.jar', '.app'];\n const allowedDangerous = config.allowedExtensions.filter((ext) => dangerousExtensions.includes(ext.toLowerCase()));\n\n if (allowedDangerous.length > 0) {\n log.warn(\n `[SECURITY WARNING] bodyParser.fileUploads.allowedExtensions includes dangerous file types: ${allowedDangerous.join(', ')}. ` +\n 'This could allow execution of malicious files. Only allow these if absolutely necessary.',\n );\n }\n\n // Warn if no blocked extensions and no allowed extensions (completely open)\n if (config.blockedExtensions.length === 0 && config.allowedExtensions.length === 0) {\n log.warn(\n '[SECURITY WARNING] File uploads have no extension restrictions (no blockedExtensions and no allowedExtensions). ' +\n 'Consider adding blockedExtensions or allowedExtensions to improve security.',\n );\n }\n};\n\n/**\n * Issue security warnings for risky IP security configurations\n */\nconst _warnIpSecurityConfig = (config: InternalServerOptions['ipSecurity']): void => {\n // Warn about wildcard or overly permissive trusted proxies\n if (config.trustedProxies.length === 0) {\n log.warn('[SECURITY WARNING] ipSecurity.trustedProxies is empty. No proxy headers will be trusted, which may prevent proper client IP detection.');\n }\n\n // Warn about very long proxy chains\n if (config.maxChainLength > 20) {\n log.warn(\n `[SECURITY WARNING] ipSecurity.maxChainLength is set to ${config.maxChainLength}. ` +\n 'Very long proxy chains can consume significant resources and may indicate amplification attacks.',\n );\n }\n\n // Warn if spoofing detection is disabled\n if (!config.detectSpoofing) {\n log.warn(\n '[SECURITY WARNING] ipSecurity.detectSpoofing is disabled. ' +\n 'This reduces protection against IP spoofing attacks. Only disable if you have other protective measures.',\n );\n }\n};\n\n/**\n * Handle body parser configuration merging and validation\n */\nconst _handleBodyParserConfig = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.bodyParser) {\n defaultConfig.bodyParser = {\n json: {\n ...DEFAULT_BODY_PARSER_CONFIG.json,\n ...userConfig.bodyParser.json,\n },\n fileUploads: {\n ...DEFAULT_BODY_PARSER_CONFIG.fileUploads,\n ...userConfig.bodyParser.fileUploads,\n },\n urlEncoded: {\n ...DEFAULT_BODY_PARSER_CONFIG.urlEncoded,\n ...userConfig.bodyParser.urlEncoded,\n },\n };\n\n // Validate configuration for security\n _validateBodyParserConfig(defaultConfig.bodyParser);\n }\n};\n\n/**\n * Handle IP security configuration merging and validation\n */\nconst _handleIpSecurityConfig = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.ipSecurity) {\n defaultConfig.ipSecurity = {\n ...DEFAULT_IP_SECURITY_CONFIG,\n ...userConfig.ipSecurity,\n };\n\n // Validate configuration for security\n _validateIpSecurityConfig(defaultConfig.ipSecurity);\n _warnIpSecurityConfig(defaultConfig.ipSecurity);\n }\n};\n\n/**\n * Validate port number\n */\nconst _validatePort = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.port !== undefined) {\n const normalizedPort = Number(userConfig.port);\n if (isNaN(normalizedPort) || normalizedPort < 1 || normalizedPort > 65535) {\n throw new Error('Invalid port number');\n }\n defaultConfig.port = normalizedPort;\n }\n};\n\n/**\n * Validate body parser configuration to prevent broken settings and warn about risky configurations\n */\nconst _validateBodyParserConfig = (config: InternalServerOptions['bodyParser']): void => {\n // Validate minimums for all parser types\n _validateJsonConfig(config.json);\n _validateFileUploadConfig(config.fileUploads);\n _validateUrlEncodedConfig(config.urlEncoded);\n\n // Issue security warnings for risky configurations (but don't block them)\n _warnJsonConfig(config.json);\n _warnFileUploadConfig(config.fileUploads);\n};\n\n/**\n * Valid log level values for validation\n */\nconst VALID_LOG_LEVELS = new Set(Object.values(logLevels));\n\n/**\n * Keys that have dedicated deep-merge handlers in handleCustomConfiguration.\n * These are skipped during the shallow merge pass to avoid clobbering the\n * deep-merged result with a raw user-provided value.\n */\nconst DEEP_MERGE_KEYS = new Set(['logging', 'bodyParser', 'ipSecurity']);\n\n/**\n * Validate a single diagnostic threshold field (TimeString or ByteString).\n * Wraps the converter call, re-throws with a descriptive field-specific message.\n */\nconst _validateThresholdField = (opts: { field: string; value: number | string; converter: (v: never) => number; examples: string }): void => {\n try {\n opts.converter(opts.value as never);\n } catch {\n throw new Error(`logging.diagnostics.${opts.field} must be a valid threshold value (e.g. ${opts.examples}). Got: \"${String(opts.value)}\"`);\n }\n};\n\n/**\n * Validate logging configuration values\n */\nconst _validateLoggingConfig = (config: InternalLoggingOptions): void => {\n if (!VALID_LOG_LEVELS.has(config.level)) {\n throw new Error(`logging.level must be one of: ${[...VALID_LOG_LEVELS].join(', ')}. Got: \"${config.level}\"`);\n }\n\n const diag = config.diagnostics;\n\n // Validate TimeString/number thresholds\n if (diag.slowRequests !== false) {\n _validateThresholdField({ field: 'slowRequests', value: diag.slowRequests, converter: _convertTimeToMs, examples: \"'100ms', '1s', '30s'\" });\n }\n if (diag.memory !== false) {\n _validateThresholdField({ field: 'memory', value: diag.memory, converter: _convertTimeToMs, examples: \"'100ms', '1s', '30s'\" });\n }\n if (diag.eventLoop !== false) {\n _validateThresholdField({ field: 'eventLoop', value: diag.eventLoop, converter: _convertTimeToMs, examples: \"'100ms', '1s', '30s'\" });\n }\n\n // Validate ByteString/number thresholds\n if (diag.largeResponses !== false) {\n _validateThresholdField({ field: 'largeResponses', value: diag.largeResponses, converter: _convertBytesToBytes, examples: \"'1mb', '256kb', '10mb'\" });\n }\n if (diag.largeRequests !== false) {\n _validateThresholdField({ field: 'largeRequests', value: diag.largeRequests, converter: _convertBytesToBytes, examples: \"'1mb', '256kb', '10mb'\" });\n }\n};\n\n/**\n * Handle logging configuration merging and validation\n */\nconst _handleLoggingConfig = (defaultConfig: InternalServerOptions, userConfig?: ServerOptions): void => {\n if (userConfig?.logging) {\n // If the user passed a branded logger (created with createLogger()), inherit its settings.\n // Merge cascade: DEFAULT_LOGGING_CONFIG → branded logger settings → explicit user config\n let brandedDefaults: Partial<InternalLoggingOptions> = {};\n const customLogger = userConfig.logging.logger as Record<string | symbol, unknown> | undefined;\n if (customLogger && loggerBrand in customLogger) {\n const branded = customLogger[loggerBrand] as { level: string; prefix: string; personality: boolean };\n brandedDefaults = {\n level: branded.level as InternalLoggingOptions['level'],\n prefix: branded.prefix,\n personality: branded.personality,\n };\n }\n\n defaultConfig.logging = {\n ...DEFAULT_LOGGING_CONFIG,\n ...brandedDefaults,\n ...userConfig.logging,\n diagnostics: {\n ...DEFAULT_DIAGNOSTICS_CONFIG,\n ...userConfig.logging.diagnostics,\n },\n };\n\n _validateLoggingConfig(defaultConfig.logging);\n }\n};\n\n/**\n * Handle custom configuration\n */\nexport const handleCustomConfiguration = (configuration?: ServerOptions): InternalServerOptions => {\n // Start with default configuration\n const result = { ...DEFAULT_CONFIGURATION };\n\n // Shallow merge — filter out explicit undefined values so they don't overwrite defaults.\n // Without this, `new YinzerFlow({ logging: undefined })` would clobber the default logging config.\n if (configuration) {\n for (const key of Object.keys(configuration)) {\n if (!DEEP_MERGE_KEYS.has(key) && (configuration as Record<string, unknown>)[key] !== undefined) {\n (result as Record<string, unknown>)[key] = (configuration as Record<string, unknown>)[key];\n }\n }\n }\n\n // Handle special configuration sections (deep merge + validation)\n _handleLoggingConfig(result, configuration);\n _handleBodyParserConfig(result, configuration);\n _handleIpSecurityConfig(result, configuration);\n _validatePort(result, configuration);\n\n return result;\n};\n",
32
+ "import { colors } from '@constants/colors.ts';\nimport { httpStatusCode } from '@constants/http.ts';\nimport { log } from '@core/utils/log.ts';\nimport type { InternalGlobalHookOptions, InternalHookRegistryImpl } from '@typedefs/internal/InternalHookRegistryImpl.js';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\n\nexport class HookRegistryImpl implements InternalHookRegistryImpl {\n readonly _beforeRouting: Set<{\n handler: HandlerCallback;\n options?: InternalGlobalHookOptions;\n }>;\n readonly _beforeAll: Set<{\n handler: HandlerCallback;\n options?: InternalGlobalHookOptions;\n }>;\n readonly _afterAll: Set<{\n handler: HandlerCallback;\n options?: InternalGlobalHookOptions;\n }>;\n _onError: HandlerCallback;\n _onNotFound: HandlerCallback;\n\n /** Per-instance logger. Reads `this._logger` at call time so it picks up the logger set by YinzerFlow. */\n private _logger: { info: (...args: Array<unknown>) => void; warn: (...args: Array<unknown>) => void; error: (...args: Array<unknown>) => void } = log;\n\n /** Called by YinzerFlow._configureLogging() to inject the per-instance logger. */\n setLogger(logger: { info: (...args: Array<unknown>) => void; warn: (...args: Array<unknown>) => void; error: (...args: Array<unknown>) => void }): void {\n this._logger = logger;\n }\n\n constructor() {\n this._beforeRouting = new Set();\n this._beforeAll = new Set();\n this._afterAll = new Set();\n this._onError = (ctx, error: unknown): unknown => {\n this._logger.error('Error while handling your request: ', error);\n ctx.response.setStatusCode(httpStatusCode.internalServerError);\n return { success: false, message: 'Internal Server Error' };\n };\n this._onNotFound = (ctx): unknown => {\n ctx.response.setStatusCode(httpStatusCode.notFound);\n return { success: false, message: '404 Not Found' };\n };\n }\n\n _addBeforeRoutingHooks(handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions): void {\n this._validateHandlersArray(handlers, 'beforeRouting');\n for (const handler of handlers) this._beforeRouting.add({ handler, options: options ?? { routesToExclude: [], routesToInclude: [] } });\n }\n\n _addBeforeHooks(handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions): void {\n this._validateHandlersArray(handlers, 'beforeAll');\n for (const handler of handlers) this._beforeAll.add({ handler, options: options ?? { routesToExclude: [], routesToInclude: [] } });\n }\n\n _addAfterHooks(handlers: Array<HandlerCallback>, options?: InternalGlobalHookOptions): void {\n this._validateHandlersArray(handlers, 'afterAll');\n for (const handler of handlers) this._afterAll.add({ handler, options: options ?? { routesToExclude: [], routesToInclude: [] } });\n }\n\n private _validateHandlersArray(handlers: unknown, methodName: string): asserts handlers is Array<HandlerCallback> {\n if (!Array.isArray(handlers)) {\n const receivedType = typeof handlers;\n const isFunction = receivedType === 'function';\n\n throw new Error(\n `YinzerFlow: ${methodName}() expects an array of handler functions, but received ${receivedType}.${\n isFunction ?\n `\\n\\n❌ Incorrect: app.${methodName}${colors.red}(${colors.reset}(ctx) => { ... }${colors.red})${colors.reset}\\n✅ Correct: app.${methodName}${colors.green}([${colors.reset}(ctx) => { ... }${colors.green}])${colors.reset}\\n\\nNote: Wrap your handler function in ${colors.magenta}square brackets${colors.reset} to make it an array.\\n\\n`\n : `\\n\\n Expected: Array<HandlerCallback>\\n Received: ${receivedType}`\n }`,\n );\n }\n\n if (handlers.length === 0) {\n this._logger.warn(`${methodName}() called with empty array. No hooks will be registered.`);\n return;\n }\n\n for (let i = 0; i < handlers.length; i++) {\n const handler = handlers[i] as unknown;\n if (typeof handler !== 'function') {\n throw new Error(`YinzerFlow: ${methodName}() array contains non-function at index ${i}. Expected: function, received: ${typeof handler}`);\n }\n }\n }\n\n _addOnError(handler: HandlerCallback): void {\n this._onError = handler;\n }\n\n _addOnNotFound(handler: HandlerCallback): void {\n this._onNotFound = handler;\n }\n}\n",
33
33
  "import type { InternalPreCompiledRoute, InternalRouteRegistry } from '@typedefs/internal/InternalRouteRegistryImpl.d.ts';\n\n/**\n * Compile a route pattern into a regular expression\n *\n * @example\n * ```ts\n * compileRoutePattern({ path: '/users/:id/posts/:postId' });\n * // Returns { pattern: /^users\\/([^/]+)\\/posts\\/([^/]+)$/, paramNames: ['id', 'postId'], isParameterized: true }\n * ```\n */\nexport const compileRoutePattern = (route: InternalRouteRegistry): InternalPreCompiledRoute => {\n const paramNames: Array<string> = [];\n\n // Convert route pattern to regex with capture groups\n // Example: /users/:id/posts/:postId → /users/([^/]+)/posts/([^/]+)\n const pattern = route.path\n .replace(/:\\w+/g, (match) => {\n const paramName = match.slice(1); // Remove the ':' prefix\n paramNames.push(paramName);\n return '([^/]+)'; // Capture group: match any characters except '/'\n })\n .replace(/\\//g, '\\\\/'); // Escape forward slashes for regex\n\n return {\n ...route,\n pattern: new RegExp(`^${pattern}$`), // ^ and $ ensure full string match\n paramNames,\n isParameterized: true,\n };\n};\n",
34
34
  "import { log } from '@core/utils/log.ts';\n\n/**\n * Normalize the path to ensure consistent format\n *\n * Handles common HTTP path variations for consistent route matching:\n * - Always starts with '/'\n * - No double slashes\n * - Consistent trailing slash handling\n * - Strips query parameters (for route matching)\n * - URL decodes encoded characters\n * - Resolves dot segments for security\n *\n * Examples:\n * - \"users\" → \"/users\"\n * - \"//users\" → \"/users\"\n * - \"/users/\" → \"/users\" (consistent - no trailing slash)\n * - \"/users?page=1\" → \"/users\"\n * - \"/users%20profile\" → \"/users profile\"\n * - \"/users/../admin\" → \"/admin\" (dot segment resolved)\n * - \"/api/./users\" → \"/api/users\" (current dir resolved)\n *\n * @example\n * ```ts\n * normalizePath('users');\n * // Returns \"/users\"\n * ```\n */\nexport const normalizePath = (path: string): string => {\n // Step 1: Strip query parameters and fragments for route matching\n // \"/users?page=1#section\" → \"/users\"\n let [normalizedPath] = path.split('?');\n if (!normalizedPath) return '';\n\n [normalizedPath] = normalizedPath.split('#');\n if (!normalizedPath) return '';\n\n // Step 2: URL decode encoded characters\n // \"/users%20profile\" → \"/users profile\"\n try {\n normalizedPath = decodeURIComponent(normalizedPath);\n } catch (_) {\n // If decoding fails (malformed URL), use original\n // This prevents crashes from malicious URLs\n log.warn('Failed to decode URL path', { path: normalizedPath });\n }\n\n // Step 3: Add leading slash if not present\n // \"users\" → \"/users\"\n normalizedPath = normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`;\n\n // Step 4: Remove double slashes\n // \"//users\" → \"/users\"\n normalizedPath = normalizedPath.replace(/\\/\\/+/g, '/');\n\n // Step 5: Resolve dot segments for security and consistency\n // SECURITY CRITICAL: Prevents directory traversal attacks\n // \"/users/../admin\" → \"/admin\"\n // \"/api/./users\" → \"/api/users\"\n // \"/users/../../etc/passwd\" → \"/etc/passwd\" (contained within app context)\n normalizedPath = resolveDotSegments(normalizedPath);\n\n // Step 6: Remove trailing slash for consistency (except root path)\n // \"/users/\" → \"/users\", but \"/\" stays \"/\"\n if (normalizedPath.length > 1 && normalizedPath.endsWith('/')) {\n normalizedPath = normalizedPath.slice(0, -1);\n }\n\n return normalizedPath;\n};\n\n/**\n * Resolve dot segments according to RFC 3986 Section 5.2.4\n *\n * SECURITY PURPOSE: Prevents directory traversal attacks by resolving relative paths\n *\n * Dot segments are dangerous because they allow attackers to:\n * - Bypass access controls: \"/users/../admin\" → \"/admin\"\n * - Access system files: \"/api/../../etc/passwd\" → \"/etc/passwd\"\n * - Traverse outside intended directories\n *\n * ALGORITHM:\n * - \".\" (current directory) → remove completely\n * - \"..\" (parent directory) → remove this segment AND the previous segment\n * - Regular segments → keep as-is\n *\n * Examples:\n * - \"/users/./profile\" → \"/users/profile\"\n * - \"/users/../admin\" → \"/admin\"\n * - \"/a/b/c/../../d\" → \"/a/d\"\n * - \"/../secret\" → \"/secret\" (can't go above root)\n *\n * @param path - Path with potential dot segments\n * @returns Path with dot segments resolved\n */\nconst resolveDotSegments = (path: string): string => {\n // Split path into segments, preserving empty strings from leading slash\n const segments = path.split('/');\n const resolved: Array<string> = [];\n\n for (const segment of segments) {\n if (segment === '.' || segment === '') {\n // Current directory \".\" - skip completely\n // Empty segments from double slashes - also skip\n if (segment === '' && resolved.length === 0) {\n // Keep the first empty segment to maintain leading slash\n resolved.push(segment);\n }\n continue;\n }\n\n if (segment === '..') {\n // Parent directory \"..\" - go up one level\n if (resolved.length > 1) {\n // Remove last segment (go to parent)\n // But don't remove the leading empty string (which represents root \"/\")\n resolved.pop();\n }\n // If we're already at root, ignore \"..\" (can't go above root)\n } else {\n // Regular segment - keep it\n resolved.push(segment);\n }\n }\n\n // Rejoin segments\n const result = resolved.join('/');\n\n // Ensure we always have at least \"/\" for root\n return result || '/';\n};\n\n/**\n * Normalize route structure for conflict detection\n * Converts /users/:id and /users/:userId to the same structure: /users/:param\n */\nexport const normalizeRouteStructure = (path: string): string => path.replace(/:\\w+/g, ':param');\n",
35
35
  "/**\n * Validate that parameter names are unique within a single route\n *\n * WHY THIS MATTERS:\n * - Routes like \"/users/:id/jobs/:id\" are confusing and error-prone\n * - Multiple params with same name would overwrite each other: { id: \"lastValue\" }\n * - Forces developers to use descriptive, unique names: \"/users/:userId/jobs/:jobId\"\n * - Makes code self-documenting and prevents runtime bugs\n *\n * EXAMPLES OF PROBLEMS THIS PREVENTS:\n * - Bad: \"/users/:id/posts/:id\" → unclear which :id refers to what\n * - Good: \"/users/:userId/posts/:postId\" → crystal clear intent\n *\n * @example\n * ```ts\n * validateParameterNames('/users/:id/posts/:id');\n * // Throws error: \"Route /users/:id/posts/:id has duplicate parameter names: id. Parameter names must be unique within a route for clarity and to prevent conflicts.\"\n * ```\n */\nexport const validateParameterNames = (routePath: string): void => {\n // Extract all parameter names from the route\n // \"/users/:userId/posts/:postId\" → [\":userId\", \":postId\"] → [\"userId\", \"postId\"]\n const paramMatches = routePath.match(/:\\w+/g);\n if (!paramMatches) return; // No parameters to validate\n\n const paramNames = paramMatches.map((param) => param.slice(1)); // Remove ':' prefix\n const uniqueParamNames = new Set(paramNames);\n\n // Check for duplicates\n if (paramNames.length !== uniqueParamNames.size) {\n const duplicates = paramNames.filter((name, index) => paramNames.indexOf(name) !== index);\n throw new Error(\n `Route ${routePath} has duplicate parameter names: ${duplicates.join(', ')}. ` +\n 'Parameter names must be unique within a route for clarity and to prevent conflicts.',\n );\n }\n};\n",
36
36
  "import { compileRoutePattern } from '@core/setup/utils/compileRoutePatter.ts';\nimport { normalizePath, normalizeRouteStructure } from '@core/setup/utils/normalizeStringPatterns.ts';\nimport { validateParameterNames } from '@core/setup/utils/validateParameterNames.ts';\nimport type { InternalHttpMethod } from '@typedefs/constants/http.ts';\nimport type { InternalPreCompiledRoute, InternalRouteRegistry, InternalRouteRegistryImpl } from '@typedefs/internal/InternalRouteRegistryImpl.js';\n\n/**\n * RouteRegistry: Efficient route storage and matching\n *\n * DESIGN DECISION: Parameter extraction happens here (not in RequestImpl) because:\n * 1. We already compile regexes for route matching - reusing them for param extraction is free\n * 2. Avoids duplicate regex compilation at request time (performance critical)\n * 3. Single source of truth for route patterns and their compiled forms\n *\n * PERFORMANCE STRATEGY:\n * - Exact routes (no params): O(1) Map lookup\n * - Parameterized routes: O(n) iteration with pre-compiled regex matching\n * - Most apps have few parameterized routes, so O(n) is acceptable\n *\n * EXAMPLES:\n * - \"/users\" (exact) → stored in exactRoutes Map for instant lookup\n * - \"/users/:id\" (parameterized) → compiled to regex, stored in parameterizedRoutes Array\n */\nexport class RouteRegistryImpl implements InternalRouteRegistryImpl {\n /**\n * Fast O(1) lookup for routes without parameters\n * Example: GET /users, POST /login, etc.\n */\n readonly _exactRoutes = new Map<InternalHttpMethod, Map<string, InternalRouteRegistry>>();\n\n /**\n * Array of pre-compiled parameterized routes for O(n) matching\n * Example: GET /users/:id, POST /users/:id/posts/:postId, etc.\n *\n * We use an array because:\n * 1. Route patterns can't be used as Map keys (they're not exact matches)\n * 2. Most apps have relatively few parameterized routes\n * 3. Pre-compiled regexes make matching fast\n */\n readonly _parameterizedRoutes = new Map<InternalHttpMethod, Array<InternalPreCompiledRoute>>();\n\n /**\n * Register a new route\n *\n * PERFORMANCE NOTE: This happens at server startup, so we can afford\n * more expensive operations like regex compilation here.\n */\n _register({ method, path, handler, options }: InternalRouteRegistry): void {\n const normalizedPath = normalizePath(path);\n const isParameterized = normalizedPath.includes(':');\n\n // Validate route before registration\n if (isParameterized) {\n validateParameterNames(normalizedPath);\n }\n\n // Prevent duplicate route registration\n // We check for exact pattern duplicates, not request path matches\n if (this._hasExactRoutePattern(method, normalizedPath)) {\n throw new Error(`Route ${normalizedPath} already exists for method ${method}`);\n }\n\n const route = { method, path: normalizedPath, handler, options, params: {} };\n\n if (isParameterized) {\n // Store in parameterized routes with pre-compiled regex\n this._storeParameterizedRoute(method, route);\n } else {\n // Store in exact routes for O(1) lookup\n this._storeExactRoute(method, normalizedPath, route);\n }\n }\n\n /**\n * Find a route and extract parameters from the request path\n *\n * RUNTIME PERFORMANCE: This is called for every HTTP request, so it must be fast!\n * 1. Try exact match first (O(1) - fastest case)\n * 2. Fall back to parameterized routes (O(n) with pre-compiled regex)\n */\n _findRoute(method: InternalHttpMethod, path: string): InternalRouteRegistry | undefined {\n const normalizedPath = normalizePath(path);\n\n // FAST PATH: Try exact match first (most common case)\n const exactRoute = this._exactRoutes.get(method)?.get(normalizedPath);\n if (exactRoute) {\n return exactRoute;\n }\n\n // PARAMETERIZED PATH: Check routes with parameters\n const parameterizedRoute = this._findParameterizedRoute(method, normalizedPath);\n if (parameterizedRoute) {\n return parameterizedRoute;\n }\n\n return undefined;\n }\n\n /**\n * Check if a route pattern already exists (for conflict detection)\n * This is different from findRoute which matches request paths to patterns\n */\n private _hasExactRoutePattern(method: InternalHttpMethod, pattern: string): boolean {\n // Check exact routes\n if (this._exactRoutes.get(method)?.has(pattern)) {\n return true;\n }\n\n // For parameterized routes, check if the structure conflicts\n // Example: /users/:id conflicts with /users/:userId (same structure, different param names)\n if (pattern.includes(':')) {\n const normalizedPattern = normalizeRouteStructure(pattern);\n const paramRoutes = this._parameterizedRoutes.get(method);\n\n if (paramRoutes) {\n return paramRoutes.some((route) => normalizeRouteStructure(route.path) === normalizedPattern);\n }\n } else {\n // Check parameterized routes for exact pattern match\n const paramRoutes = this._parameterizedRoutes.get(method);\n if (paramRoutes) {\n return paramRoutes.some((route) => route.path === pattern);\n }\n }\n\n return false;\n }\n\n /**\n * Store an exact route (no parameters) for O(1) lookup\n */\n private _storeExactRoute(method: InternalHttpMethod, path: string, route: InternalRouteRegistry): void {\n if (!this._exactRoutes.has(method)) {\n this._exactRoutes.set(method, new Map());\n }\n\n this._exactRoutes.get(method)?.set(path, route);\n }\n\n /**\n * Store a parameterized route with pre-compiled regex pattern\n */\n private _storeParameterizedRoute(method: InternalHttpMethod, route: InternalRouteRegistry): void {\n if (!this._parameterizedRoutes.has(method)) {\n this._parameterizedRoutes.set(method, []);\n }\n\n /**\n * Compile a route pattern into a regex with parameter extraction\n *\n * This is the magic that converts route patterns to regexes:\n * - \"/users/:id\" → /^\\/users\\/([^\\/]+)$/\n * - \"/users/:id/posts/:postId\" → /^\\/users\\/([^\\/]+)\\/posts\\/([^\\/]+)$/\n *\n * WHY AT REGISTRATION TIME: Regex compilation is expensive, so we do it once\n * at server startup rather than on every request.\n */\n const compiled = compileRoutePattern(route);\n this._parameterizedRoutes.get(method)?.push(compiled);\n }\n\n /**\n * Find and match a parameterized route, extracting parameters\n * This is slower than the exact route match (O(n)) because it has to iterate through all the parameterized routes\n * and should only be used if the route is parameterized, otherwise it will be slower than the exact route match.\n */\n private _findParameterizedRoute(method: InternalHttpMethod, path: string): InternalRouteRegistry | undefined {\n const paramRoutes = this._parameterizedRoutes.get(method);\n if (!paramRoutes) return undefined;\n\n // Try each parameterized route until we find a match\n for (const compiledRoute of paramRoutes) {\n const match = path.match(compiledRoute.pattern);\n if (match) {\n const params: Record<string, string> = {};\n\n // Extract parameters from regex capture groups\n // match[0] is the full match, match[1+] are the captured groups\n for (let i = 0; i < compiledRoute.paramNames.length; i++) {\n const paramValue = match[i + 1];\n const paramName = compiledRoute.paramNames[i];\n\n if (paramValue !== undefined && paramName !== undefined) {\n params[paramName] = paramValue;\n }\n }\n\n return { ...compiledRoute, params };\n }\n }\n\n return undefined;\n }\n}\n",
37
37
  "import type { InternalRouteRegistryOptions } from '@typedefs/internal/InternalRouteRegistryImpl.js';\nimport type { InternalSetupMethod } from '@typedefs/internal/InternalSetupImpl.js';\nimport type { httpMethod } from '@constants/http.ts';\nimport type { RouteGroup } from '@typedefs/public/Setup.js';\n\n/**\n * Utility type that maps HTTP methods to their handler functions\n * Ensures consistency between SetupImpl and GroupApp\n */\nexport type HttpMethodHandlers = Record<Lowercase<keyof typeof httpMethod>, InternalSetupMethod>;\n\n/**\n * Route group method signature for consistent typing across interfaces\n * Used for both main setup and nested group registration\n */\nexport type RouteGroupMethod = (prefix: string, callback: (group: RouteGroup) => void, options?: InternalRouteRegistryOptions) => RouteGroup;\n\n/**\n * Ensure route options are complete by filling in missing optional properties\n * This allows developers to specify only the hooks they need while maintaining\n * internal compatibility with the system\n */\nexport const ensureCompleteRouteOptions = (options?: InternalRouteRegistryOptions): InternalRouteRegistryOptions => ({\n beforeHooks: options?.beforeHooks ?? [],\n afterHooks: options?.afterHooks ?? [],\n});\n\n/**\n * Merge route options with proper hook ordering\n * - beforeHooks: parent hooks first, then child hooks\n * - afterHooks: child hooks first, then parent hooks\n * Handles optional hooks gracefully\n */\nexport const mergeRouteOptions = (parentOptions: InternalRouteRegistryOptions, childOptions?: InternalRouteRegistryOptions): InternalRouteRegistryOptions => ({\n beforeHooks: [...(parentOptions.beforeHooks ?? []), ...(childOptions?.beforeHooks ?? [])],\n afterHooks: [...(childOptions?.afterHooks ?? []), ...(parentOptions.afterHooks ?? [])],\n});\n\n/**\n * Build a route path by joining prefix and path segments\n * Handles leading/trailing slashes intelligently\n */\nexport const buildRoutePath = (prefix: string, path: string): string => {\n const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return `${cleanPrefix}${cleanPath}`;\n};\n",
38
38
  "import { httpMethod } from '@constants/http.ts';\nimport type { InternalHttpMethod } from '@typedefs/constants/http.ts';\nimport type { InternalRouteRegistryOptions } from '@typedefs/internal/InternalRouteRegistryImpl.js';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.js';\nimport type { HttpMethodHandlers, RouteGroupMethod } from '@core/setup/utils/routeUtils.js';\nimport { buildRoutePath, ensureCompleteRouteOptions, mergeRouteOptions } from '@core/setup/utils/routeUtils.js';\nimport type { RouteGroup } from '@typedefs/public/Setup.js';\n\nexport interface InternalGroupApp extends HttpMethodHandlers {\n readonly group: RouteGroupMethod;\n}\n\nexport class GroupApp implements InternalGroupApp {\n private readonly _setup: InternalSetupImpl;\n private readonly _prefix: string;\n private readonly _options: InternalRouteRegistryOptions;\n\n constructor(setup: InternalSetupImpl, prefix: string, options?: InternalRouteRegistryOptions) {\n this._setup = setup;\n this._prefix = prefix;\n this._options = ensureCompleteRouteOptions(options);\n }\n\n private _createRouteHandler(method: InternalHttpMethod) {\n return (path: string, handler: HandlerCallback<any>, routeOptions?: InternalRouteRegistryOptions): void => {\n const fullPath = buildRoutePath(this._prefix, path);\n const mergedOptions = mergeRouteOptions(this._options, routeOptions);\n\n this._setup._routeRegistry._register({\n method,\n handler,\n path: fullPath,\n options: mergedOptions,\n params: {},\n });\n\n // If this is a GET route, automatically register the corresponding HEAD route\n if (method === httpMethod.get) {\n this._setup._routeRegistry._register({\n method: httpMethod.head,\n handler,\n path: fullPath,\n options: mergedOptions,\n params: {},\n });\n }\n };\n }\n\n // HTTP method handlers - using the utility type to ensure consistency\n get = this._createRouteHandler(httpMethod.get);\n head = this._createRouteHandler(httpMethod.head);\n post = this._createRouteHandler(httpMethod.post);\n put = this._createRouteHandler(httpMethod.put);\n delete = this._createRouteHandler(httpMethod.delete);\n patch = this._createRouteHandler(httpMethod.patch);\n options = this._createRouteHandler(httpMethod.options);\n\n // Nested group support\n group(prefix: string, callback: (group: RouteGroup) => void, options?: InternalRouteRegistryOptions): RouteGroup {\n const nestedPrefix = buildRoutePath(this._prefix, prefix);\n const nestedOptions = mergeRouteOptions(this._options, options);\n\n const nestedGroup = new GroupApp(this._setup, nestedPrefix, nestedOptions);\n callback(nestedGroup);\n\n return nestedGroup;\n }\n}\n",
39
- "import { httpMethod } from '@constants/http.ts';\nimport { handleCustomConfiguration } from '@core/setup/utils/handleCustomConfiguration.ts';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.ts';\nimport { HookRegistryImpl } from '@core/execution/HookRegistryImpl.ts';\nimport type { InternalGlobalHookOptions } from '@typedefs/internal/InternalHookRegistryImpl.js';\nimport type { InternalServerOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport type { ServerOptions } from '@typedefs/public/Configuration.js';\nimport type { InternalRouteRegistryOptions } from '@typedefs/internal/InternalRouteRegistryImpl.js';\nimport { RouteRegistryImpl } from '@core/setup/RouteRegistryImpl.ts';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\nimport { GroupApp } from '@core/setup/GroupApp.ts';\nimport { ensureCompleteRouteOptions } from '@core/setup/utils/routeUtils.js';\nimport type { RouteGroup } from '@typedefs/public/Setup.js';\n\nexport class SetupImpl implements InternalSetupImpl {\n readonly _configuration: InternalServerOptions;\n readonly _routeRegistry = new RouteRegistryImpl();\n readonly _hooks = new HookRegistryImpl();\n\n constructor(customConfiguration?: ServerOptions) {\n this._configuration = handleCustomConfiguration(customConfiguration);\n }\n\n // ===== Route Registration =====\n get(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n const routeOptions = ensureCompleteRouteOptions(options);\n // Register GET route\n this._routeRegistry._register({ method: httpMethod.get, handler, path, options: routeOptions, params: {} });\n // Automatically register corresponding HEAD route\n this._routeRegistry._register({ method: httpMethod.head, handler, path, options: routeOptions, params: {} });\n }\n\n head(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.head, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n post(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.post, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n put(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.put, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n patch(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.patch, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n delete(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.delete, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n options(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.options, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n group(prefix: string, callback: (group: RouteGroup) => void, options?: InternalRouteRegistryOptions): RouteGroup {\n // Create a group app that can handle nested groups and route registration\n const groupApp = new GroupApp(this, prefix, options);\n\n // Execute callback to register routes\n callback(groupApp);\n\n return groupApp;\n }\n\n /**\n * Hook Registration\n *\n * Note these are going to be called dynamically at run time, for now\n * we are just storing them in the server object until runtime. Although\n * it is slower during lookup, it is more flexible and memory efficient\n * allowing for more flexibility to include hook modification, conditional\n * hook execution, and better debugging.\n */\n beforeRouting(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void {\n this._hooks._addBeforeRoutingHooks(handlers, options);\n }\n\n beforeAll(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void {\n this._hooks._addBeforeHooks(handlers, options);\n }\n\n afterAll(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void {\n this._hooks._addAfterHooks(handlers, options);\n }\n\n onError(handler: HandlerCallback<any>): void {\n this._hooks._addOnError(handler);\n }\n\n onNotFound(handler: HandlerCallback<any>): void {\n this._hooks._addOnNotFound(handler);\n }\n}\n",
40
- "import { colors } from '@constants/colors.ts';\nimport { logLevels } from '@constants/log.ts';\nimport { createLogger } from '@core/utils/log.ts';\nimport type { Logger, LoggerConfig } from '@typedefs/public/Logger.ts';\n\nconst baseConfig: LoggerConfig = {\n prefix: 'NETWORK',\n logLevel: 'off',\n logger: undefined,\n};\n\nexport const networkLog = {\n log: createLogger(baseConfig),\n enable: (customLogger?: Logger): void => {\n networkLog.log = createLogger({ ...baseConfig, logLevel: logLevels.info, logger: customLogger });\n },\n};\n\n/**\n * Get status emoji for response codes\n */\nexport const getStatusEmoji = (statusCode: number): string => {\n if (statusCode >= 200 && statusCode < 300) return '✅';\n if (statusCode >= 300 && statusCode < 400) return '🔄';\n if (statusCode >= 400 && statusCode < 500) return '❌';\n if (statusCode >= 500) return '💥';\n return '❓';\n};\n\n/**\n * Performance thresholds with Pittsburgh personality\n */\nconst PERFORMANCE_THRESHOLDS = [\n { maxTime: 50, emoji: '⚡', phrase: 'faster than a Stillers touchdown!' },\n { maxTime: 100, emoji: '🔥', phrase: \"smooth as butter n'at!\" },\n { maxTime: 200, emoji: '✅', phrase: 'not bad yinz!' },\n { maxTime: 500, emoji: '⚠️', phrase: \"slowin' down a bit there\" },\n { maxTime: 1000, emoji: '🐌', phrase: \"that's draggin' n'at\" },\n { maxTime: Infinity, emoji: '💥', phrase: 'what a jagoff response time!' },\n] as const;\n\n/**\n * Get performance details for response time with Pittsburgh personality\n */\nexport const logPerformanceDetails = (timeMs: number): void => {\n const threshold = PERFORMANCE_THRESHOLDS.find((t) => timeMs < t.maxTime) ?? PERFORMANCE_THRESHOLDS[PERFORMANCE_THRESHOLDS.length - 1];\n if (!threshold) throw new Error('No threshold found for performance details');\n\n networkLog.log.warn(`${colors.magenta} ${threshold.emoji} Response time: ${timeMs}ms - ${threshold.phrase}${colors.reset}`);\n};\n",
39
+ "import { httpMethod } from '@constants/http.ts';\nimport { handleCustomConfiguration } from '@core/setup/utils/handleCustomConfiguration.ts';\nimport type { InternalSetupImpl } from '@typedefs/internal/InternalSetupImpl.ts';\nimport { HookRegistryImpl } from '@core/execution/HookRegistryImpl.ts';\nimport type { InternalGlobalHookOptions } from '@typedefs/internal/InternalHookRegistryImpl.js';\nimport type { InternalServerOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport type { ServerOptions } from '@typedefs/public/Configuration.js';\nimport type { InternalRouteRegistryOptions } from '@typedefs/internal/InternalRouteRegistryImpl.js';\nimport { RouteRegistryImpl } from '@core/setup/RouteRegistryImpl.ts';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\nimport { GroupApp } from '@core/setup/GroupApp.ts';\nimport { ensureCompleteRouteOptions } from '@core/setup/utils/routeUtils.js';\nimport type { RouteGroup } from '@typedefs/public/Setup.js';\nimport { log } from '@core/utils/log.ts';\n\nexport class SetupImpl implements InternalSetupImpl {\n readonly _configuration: InternalServerOptions;\n readonly _routeRegistry = new RouteRegistryImpl();\n readonly _hooks = new HookRegistryImpl();\n /** Per-instance logger. Defaults to module-level `log`, replaced by `_configureLogging()` in YinzerFlow. */\n _log: typeof log = log;\n\n constructor(customConfiguration?: ServerOptions) {\n this._configuration = handleCustomConfiguration(customConfiguration);\n }\n\n // ===== Route Registration =====\n get(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n const routeOptions = ensureCompleteRouteOptions(options);\n // Register GET route\n this._routeRegistry._register({ method: httpMethod.get, handler, path, options: routeOptions, params: {} });\n // Automatically register corresponding HEAD route\n this._routeRegistry._register({ method: httpMethod.head, handler, path, options: routeOptions, params: {} });\n }\n\n head(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.head, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n post(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.post, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n put(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.put, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n patch(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.patch, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n delete(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.delete, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n options(path: string, handler: HandlerCallback<any>, options?: InternalRouteRegistryOptions): void {\n this._routeRegistry._register({ method: httpMethod.options, handler, path, options: ensureCompleteRouteOptions(options), params: {} });\n }\n\n group(prefix: string, callback: (group: RouteGroup) => void, options?: InternalRouteRegistryOptions): RouteGroup {\n // Create a group app that can handle nested groups and route registration\n const groupApp = new GroupApp(this, prefix, options);\n\n // Execute callback to register routes\n callback(groupApp);\n\n return groupApp;\n }\n\n /**\n * Hook Registration\n *\n * Note these are going to be called dynamically at run time, for now\n * we are just storing them in the server object until runtime. Although\n * it is slower during lookup, it is more flexible and memory efficient\n * allowing for more flexibility to include hook modification, conditional\n * hook execution, and better debugging.\n */\n beforeRouting(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void {\n this._hooks._addBeforeRoutingHooks(handlers, options);\n }\n\n beforeAll(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void {\n this._hooks._addBeforeHooks(handlers, options);\n }\n\n afterAll(handlers: Array<HandlerCallback<any>>, options?: InternalGlobalHookOptions): void {\n this._hooks._addAfterHooks(handlers, options);\n }\n\n onError(handler: HandlerCallback<any>): void {\n this._hooks._addOnError(handler);\n }\n\n onNotFound(handler: HandlerCallback<any>): void {\n this._hooks._addOnNotFound(handler);\n }\n}\n",
40
+ "import { logLevels } from '@constants/log.ts';\n\n/**\n * Access Log nginx-style request/response logging.\n *\n * Separate channel from the app logger. Controlled by `logging.requests` (on/off).\n * Defaults to off. When enabled, logs one line per request in a parseable format.\n *\n * Format: {statusEmoji} {ip} \"{METHOD} {path} {proto}\" {statusCode} {bytes} \"{referer}\" \"{ua}\" {duration}ms\n *\n * The logger instance is created per YinzerFlow instance (C1 fix — no module-level singleton).\n * See YinzerFlow._configureLogging() for instance creation.\n */\n\n/** Base config for access log loggers. Personality is always off for machine-parseable output. */\nexport const accessLogBaseConfig = {\n prefix: 'ACCESS',\n level: logLevels.off,\n personality: false,\n} as const;\n\n/**\n * Get status emoji for response codes\n */\nexport const getStatusEmoji = (statusCode: number): string => {\n if (statusCode >= 200 && statusCode < 300) return '✅';\n if (statusCode >= 300 && statusCode < 400) return '🔄';\n if (statusCode >= 400 && statusCode < 500) return '❌';\n if (statusCode >= 500) return '💥';\n return '❓';\n};\n",
41
41
  "import type { InternalRateLimitStore } from '@typedefs/internal/modules/rateLimit/index.js';\n\n/**\n * Create an in-memory store for sliding window counter data\n */\nexport const createInMemoryStore = <T>(): InternalRateLimitStore<T> => {\n const store = new Map<string, T>();\n\n return {\n get: async (key: string) => Promise.resolve(store.get(key)),\n set: async (key: string, value: T): Promise<void> => {\n store.set(key, value);\n return Promise.resolve();\n },\n delete: async (key: string): Promise<void> => {\n store.delete(key);\n return Promise.resolve();\n },\n destroy: async (): Promise<void> => {\n store.clear();\n return Promise.resolve();\n },\n };\n};\n",
42
42
  "import { Redis } from 'ioredis';\nimport type { InternalRateLimitStore } from '@typedefs/internal/modules/rateLimit/index.js';\nimport { log } from '@core/utils/log.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport type { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\nimport type { RedisClient } from '@typedefs/public/RateLimit.js';\n\n/**\n * Create a Redis-based store for rate limiting data\n *\n * This store is designed to work with any rate limiting algorithm by providing\n * a generic key-value interface. It handles serialization/deserialization\n * and provides automatic key expiration.\n *\n * ## Features\n *\n * - **Algorithm agnostic**: Works with sliding window counter, token bucket, etc.\n * - **Automatic expiration**: Keys expire automatically to prevent memory leaks\n * - **JSON serialization**: Handles complex data structures\n * - **Error handling**: Graceful fallback on Redis errors\n * - **Debug logging**: Optional detailed logging for troubleshooting\n *\n * ## Usage\n *\n * ```typescript\n * import { createRedisStore } from '@core/modules/rateLimit/stores/redis.ts';\n * import Redis from 'ioredis';\n *\n * const redis = new Redis({\n * host: 'localhost',\n * port: 6379,\n * retryDelayOnFailover: 100,\n * });\n *\n * const store = createRedisStore({\n * client: redis,\n * keyPrefix: 'myapp:rate_limit:',\n * defaultTtl: 3600\n * });\n * ```\n *\n * ## Key Format\n *\n * Keys are formatted as: `{prefix}{algorithm}:{identifier}`\n *\n * Examples:\n * - `rate_limit:sliding_window_counter:192.168.1.100`\n * - `rate_limit:token_bucket:user:12345`\n * - `myapp:rate_limit:sliding_window_counter:api_key:abc123`\n *\n * @param config - Redis store configuration\n * @returns Rate limit store instance\n */\nexport const createRedisStore = async <T>(config: RateLimitConfig): Promise<InternalRateLimitStore<T>> => {\n const { store } = config;\n if (store.type !== 'redis') throw new Error(`Expected Redis store configuration but got: ${JSON.stringify(store)}`);\n const { client, keyPrefix = 'rate_limit:', maxRetries = 3, retryDelay = 1000 } = store;\n const retryDelayMs = _convertTimeToMs(retryDelay);\n let connectionHealthy = false;\n\n // Validate Redis connection with retry logic\n const _validateConnection = async (): Promise<void> => {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n await client.ping();\n connectionHealthy = true;\n log.info(`[RedisStore] Successfully connected to Redis (attempt ${attempt})`);\n return;\n } catch (error) {\n log.warn(`[RedisStore] Redis connection attempt ${attempt}/${maxRetries} failed:`, error);\n\n if (attempt < maxRetries) {\n log.info(`[RedisStore] Retrying connection in ${retryDelay}...`);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, retryDelayMs);\n });\n } else {\n log.error('[RedisStore] All Redis connection attempts failed. Store will operate in degraded mode.');\n connectionHealthy = false;\n }\n }\n }\n };\n\n // Initialize connection validation (non-blocking)\n await _validateConnection();\n\n return {\n get: async (key: string) => _get({ client, key, keyPrefix, connectionHealthy }),\n set: async (key: string, value: T) => _set({ client, config, key, value, keyPrefix, connectionHealthy }),\n delete: async (key: string) => _delete({ client, key, keyPrefix, connectionHealthy }),\n destroy: async () => _destroy({ client, keyPrefix, connectionHealthy }),\n };\n};\n\n/**\n * Build Redis key with prefix\n */\nconst _buildKey = (key: string, keyPrefix: string): string => `${keyPrefix}${key}`;\n\n/**\n * Serialize value to JSON string\n */\nconst _serialize = <T>(value: T): string => {\n try {\n return JSON.stringify(value);\n } catch (error) {\n log.error('[RedisStore] Failed to serialize value:', error);\n throw new Error('Failed to serialize rate limit data');\n }\n};\n\n/**\n * Deserialize JSON string to value\n */\nconst _deserialize = <T>(json: string): T => {\n try {\n return JSON.parse(json) as T;\n } catch (error) {\n log.error('[RedisStore] Failed to deserialize value:', error);\n throw new Error('Failed to deserialize rate limit data');\n }\n};\n\n/**\n * Handle Redis errors gracefully\n */\nconst _handleError = (operation: string, error: unknown, connectionHealthy: boolean): void => {\n if (connectionHealthy) {\n log.warn(`[RedisStore] Redis ${operation} failed (connection was healthy):`, error);\n } else {\n log.error(`[RedisStore] Redis ${operation} failed (connection unhealthy):`, error);\n }\n // Don't throw - allow the application to continue with degraded functionality\n};\n\n/**\n * Set key with TTL, handling both ioredis and redis package differences\n */\nconst _setWithTtl = async ({ client, key, value, ttlSeconds }: { client: RedisClient; key: string; value: string; ttlSeconds: number }): Promise<void> => {\n try {\n if (client instanceof Redis) {\n await client.set(key, value, 'EX', ttlSeconds);\n } else {\n await client.set(key, value, { EX: ttlSeconds });\n }\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Unsupported Redis client or Redis operation failed: ${error.message}`);\n }\n throw error;\n }\n};\n\n/**\n * Set key while preserving TTL, handling both ioredis and redis package differences\n */\nconst _setKeepTtl = async ({ client, key, value }: { client: RedisClient; key: string; value: string }): Promise<void> => {\n try {\n if (client instanceof Redis) {\n await client.set(key, value, 'KEEPTTL'); // cspell:disable-line\n } else {\n await client.set(key, value, { KEEPTTL: true }); // cspell:disable-line\n }\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Unsupported Redis client or Redis operation failed: ${error.message}`);\n }\n throw error;\n }\n};\n\n/**\n * Get value from Redis\n */\nconst _get = async <T>({\n client,\n key,\n keyPrefix,\n connectionHealthy,\n}: {\n client: RedisClient;\n key: string;\n keyPrefix: string;\n connectionHealthy: boolean;\n}): Promise<T | undefined> => {\n try {\n const redisKey = _buildKey(key, keyPrefix);\n const value = await client.get(redisKey);\n\n if (value === null) {\n return undefined;\n }\n\n return _deserialize<T>(value);\n } catch (error) {\n _handleError('GET', error, connectionHealthy);\n return undefined;\n }\n};\n\n/**\n * Set value in Redis with TTL\n */\nconst _set = async <T>({\n client,\n config,\n key,\n value,\n keyPrefix,\n connectionHealthy,\n}: {\n client: RedisClient;\n config: RateLimitConfig;\n key: string;\n value: T;\n keyPrefix: string;\n connectionHealthy: boolean;\n}): Promise<void> => {\n try {\n const redisKey = _buildKey(key, keyPrefix);\n const serialized = _serialize(value);\n\n // Check if key exists first\n const exists = await client.exists(redisKey);\n\n if (exists) {\n // Key exists - update value but preserve TTL using KEEPTTL (cspell:disable-line)\n await _setKeepTtl({ client, key: redisKey, value: serialized });\n } else {\n // New key - set with TTL\n await _setWithTtl({ client, key: redisKey, value: serialized, ttlSeconds: Math.floor(config.window / 1000) });\n }\n } catch (error) {\n _handleError('SET', error, connectionHealthy);\n }\n};\n\n/**\n * Delete key from Redis\n */\nconst _delete = async ({\n client,\n key,\n keyPrefix,\n connectionHealthy,\n}: {\n client: RedisClient;\n key: string;\n keyPrefix: string;\n connectionHealthy: boolean;\n}): Promise<void> => {\n try {\n const redisKey = _buildKey(key, keyPrefix);\n await client.del(redisKey);\n } catch (error) {\n _handleError('DELETE', error, connectionHealthy);\n }\n};\n\n/**\n * Delete all keys related to this store\n */\nconst _destroy = async ({ client, keyPrefix, connectionHealthy }: { client: RedisClient; keyPrefix: string; connectionHealthy: boolean }): Promise<void> => {\n try {\n const pattern = `${keyPrefix}*`;\n const keys = await client.keys(pattern);\n if (keys.length > 0) {\n await Promise.all(keys.map(async (key) => client.del(key)));\n log.info(`[RedisStore] Destroyed ${keys.length} rate limit keys`);\n }\n } catch (error) {\n _handleError('DESTROY', error, connectionHealthy);\n }\n};\n",
43
- "import type { TimeString } from '@typedefs/public/Time.js';\n\n/**\n * Convert time string to milliseconds\n *\n * Internal helper for parsing user-friendly time formats into milliseconds.\n * Supports seconds (s), minutes (m), hours (h), and days (d).\n *\n * @param time - Time string in format: number + unit (s/m/h/d)\n * @returns Time in milliseconds\n * @throws Error if time format is invalid\n *\n * @example\n * ```typescript\n * _convertTimeToMs('500ms') // 500\n * _convertTimeToMs('30s') // 30000\n * _convertTimeToMs('15m') // 900000\n * _convertTimeToMs('2h') // 7200000\n * _convertTimeToMs('1d') // 86400000\n * ```\n *\n * @internal\n */\nexport const _convertTimeToMs = (time: TimeString | number): number => {\n if (typeof time === 'number') {\n return time;\n }\n\n // Validate string format\n if (typeof time !== 'string') {\n throw new Error('Invalid time format. Expected format: 1ms, 1s, 1m, 1h, 1d');\n }\n\n if (time.length < 2) {\n throw new Error('Invalid time format. Expected format: 1ms, 1s, 1m, 1h, 1d');\n }\n\n if (time.length > 3) {\n throw new Error('Invalid time format. Expected format: 1ms, 1s, 1m, 1h, 1d');\n }\n\n // Extract unit (last character)\n const unit = time.includes('ms') ? time.slice(-2) : time.slice(-1);\n const value = time.includes('ms') ? time.slice(0, -2) : time.slice(0, -1);\n\n // Validate unit\n if (!['ms', 's', 'm', 'h', 'd'].includes(unit)) {\n throw new Error(`Invalid time unit: \"${unit}\". Expected: s (seconds), m (minutes), h (hours), or d (days)`);\n }\n\n // Parse numeric value\n const numValue = Number(value);\n if (isNaN(numValue) || numValue <= 0) {\n throw new Error(`Invalid time value: \"${value}\". Must be a positive number`);\n }\n\n // Convert to milliseconds based on unit\n switch (unit) {\n case 'ms':\n return numValue;\n case 's':\n return numValue * 1000;\n case 'm':\n return numValue * 60 * 1000;\n case 'h':\n return numValue * 60 * 60 * 1000;\n case 'd':\n return numValue * 24 * 60 * 60 * 1000;\n default:\n throw new Error(`Unsupported time unit: \"${unit}\"`);\n }\n};\n",
44
43
  "import { createInMemoryStore } from './inMemory.ts';\nimport { createRedisStore } from './redis.ts';\nimport type { InternalRateLimitStore } from '@typedefs/internal/modules/rateLimit/index.js';\nimport type { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\n\nconst createStoreFactories = <T>() =>\n ({\n memory: () => createInMemoryStore<T>(),\n redis: async (config: RateLimitConfig) => createRedisStore<T>(config),\n }) as const;\n\nexport const createRateLimitStore = async <T>(rateLimitConfig: RateLimitConfig): Promise<InternalRateLimitStore<T>> => {\n const storeFactories = createStoreFactories<T>();\n const factory = storeFactories[rateLimitConfig.store.type];\n if (!factory) throw new Error(`Unsupported store type: ${rateLimitConfig.store.type}`); // eslint-disable-line @typescript-eslint/no-unnecessary-condition -- javascript compatibility\n return factory(rateLimitConfig);\n};\n",
45
44
  "import type { Context } from '@typedefs/public/Context.js';\nimport type {\n InternalRateLimitResult,\n InternalRateLimitStore,\n InternalRateLimitStrategy,\n InternalSlidingWindowCounterEntry,\n} from '@typedefs/internal/modules/rateLimit/index.js';\nimport type { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\nimport { createRateLimitStore } from '@core/modules/rateLimit/stores/index.ts';\n\n/**\n * Sliding Window Counter Strategy\n *\n * Uses a weighted count of current and previous windows to provide accurate rate limiting\n * with minimal memory overhead.\n *\n * ## Algorithm:\n * 1. Track counts in current window and previous window\n * 2. Calculate weighted estimate based on time elapsed in current window\n * 3. Formula: current_count + (previous_count × (1 - elapsed_percentage))\n *\n * ## Example (100 req/15min window):\n * - Time: 8 minutes into current window\n * - Current window: 20 requests\n * - Previous window: 95 requests\n * - Estimated: 20 + (95 × (7/15)) = 20 + 44.33 = 64.33 requests\n *\n * ## Memory Efficiency:\n * - Only 3 numbers per IP: ~24 bytes\n * - vs Sliding Window Log: ~800 bytes (100 timestamps)\n * - 33x more efficient!\n *\n * @see https://en.wikipedia.org/wiki/Rate_limiting#Sliding_window_counter\n */\nexport class SlidingWindowCounterStrategy implements InternalRateLimitStrategy {\n private readonly _config: RateLimitConfig;\n private _store: InternalRateLimitStore<InternalSlidingWindowCounterEntry> | null = null;\n\n constructor(config: RateLimitConfig) {\n this._config = config;\n }\n\n private async _getStore(): Promise<InternalRateLimitStore<InternalSlidingWindowCounterEntry>> {\n this._store ??= await createRateLimitStore<InternalSlidingWindowCounterEntry>(this._config);\n return this._store;\n }\n\n /**\n * Check if request should be allowed using sliding window counter algorithm\n */\n async check(context: Context<any>): Promise<InternalRateLimitResult> {\n const store = await this._getStore();\n const key = this._config.keyGenerator(context);\n const now = Date.now();\n\n // Get or create entry for this client\n const entry = (await store.get(key)) ?? {\n currentWindowCount: 0,\n previousWindowCount: 0,\n windowStart: now,\n };\n\n // Calculate time elapsed from the ORIGINAL window start (before any updates)\n const timeElapsed = now - entry.windowStart;\n\n // Check if we've moved to a new window\n if (timeElapsed >= this._config.window) {\n // Hard reset - start fresh in new window\n entry.previousWindowCount = 0;\n entry.currentWindowCount = 0;\n entry.windowStart = now;\n }\n\n // Calculate weighted count using sliding window formula\n // Use the UPDATED windowStart for accurate percentage calculation\n const currentTimeElapsed = now - entry.windowStart;\n const percentageIntoCurrentWindow = currentTimeElapsed / this._config.window;\n const weightedPreviousCount = entry.previousWindowCount * (1 - percentageIntoCurrentWindow);\n const estimatedCount = entry.currentWindowCount + weightedPreviousCount;\n\n // Check if limit is exceeded\n const allowed = estimatedCount < this._config.max;\n\n if (allowed) {\n // Increment current window count\n entry.currentWindowCount++;\n }\n\n // Save updated entry\n await store.set(key, entry);\n\n // Calculate remaining requests\n const remaining = Math.max(0, Math.floor(this._config.max - estimatedCount - (allowed ? 1 : 0)));\n\n // Calculate reset time (end of current window)\n const resetTime = entry.windowStart + this._config.window;\n\n return {\n allowed,\n remaining,\n resetTime,\n totalHits: Math.ceil(estimatedCount + (allowed ? 1 : 0)),\n limit: this._config.max,\n };\n }\n\n /**\n * Clean up resources\n */\n async destroy(): Promise<void> {\n const store = await this._getStore();\n await store.destroy();\n }\n}\n",
46
45
  "import type { InternalRateLimitResult, InternalRateLimitStrategy } from '@typedefs/internal/modules/rateLimit/index.js';\nimport type { Context } from '@typedefs/public/Context.js';\nimport { SlidingWindowCounterStrategy } from '@core/modules/rateLimit/strategies/SlidingWindowCounterStrategy.ts';\nimport type { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\n/**\n * Calculate the retry-after value in seconds\n */\nconst _calculateRetryAfter = (resetTime: number): number => Math.ceil((resetTime - Date.now()) / 1000);\n\n/**\n * Factory function to create the appropriate rate limiting strategy\n *\n * @param config - Rate limit configuration\n * @returns Strategy instance based on configured algorithm\n * @throws Error if algorithm is not supported\n */\nconst _createStrategy = (config: RateLimitConfig): InternalRateLimitStrategy =>\n // Currently only one algorithm is implemented\n // When adding more algorithms, refactor to switch statement:\n // switch (config.algorithm) {\n // case rateLimitAlgorithm.slidingWindowCounter:\n // return new SlidingWindowCounterStrategy(config);\n // case rateLimitAlgorithm.tokenBucket:\n // return new TokenBucketStrategy(config);\n // default:\n // throw new Error(`Algorithm \"${config.algorithm}\" is not implemented`);\n // }\n new SlidingWindowCounterStrategy(config);\n\n/**\n * RateLimiter class with pluggable algorithm support\n *\n * Uses the Strategy Pattern to support multiple rate limiting algorithms:\n * - **Sliding Window Counter**: Memory efficient, Redis-ready, 99%+ accurate (default)\n * - **Token Bucket**: (Future) Smooth traffic patterns with continuous refill\n *\n * ## Architecture\n *\n * The RateLimiter acts as a facade that delegates to algorithm-specific strategy classes.\n * This makes it easy to add new algorithms without modifying existing code.\n *\n * ## Memory Efficiency\n *\n * - Sliding Window Counter: ~24 bytes per IP (3 numbers)\n * - vs Sliding Window Log: ~800 bytes per IP (100 timestamps)\n * - **33x more memory efficient!**\n *\n * ## Usage\n *\n * @example\n * ```typescript\n * // In-memory rate limiter (default)\n * const limiter = new RateLimiter({\n * algorithm: 'sliding-window-counter', // Default\n * window: 60000, // 1 minute\n * max: 10, // 10 requests per minute\n * standardHeaders: true,\n * skipSuccessfulRequests: false,\n * skipFailedRequests: false,\n * keyGenerator: (ctx) => ctx.request.ipAddress,\n * handler: (ctx) => ({ success: false, message: 'Too many requests' })\n * });\n *\n * // Redis-based rate limiter (for production)\n * import Redis from 'ioredis';\n * const redis = new Redis({ host: 'localhost', port: 6379 });\n *\n * const redisLimiter = new RateLimiter({\n * algorithm: 'sliding-window-counter',\n * window: '15m',\n * max: 100,\n * keyGenerator: (ctx) => ctx.request.ipAddress,\n * handler: (ctx) => ({ success: false, message: 'Rate limit exceeded' })\n * }, {\n * type: 'redis',\n * redis: {\n * client: redis,\n * keyPrefix: 'myapp:rate_limit:',\n * defaultTtl: 3600\n * }\n * });\n *\n * const result = limiter.check(context);\n * if (!result.allowed) {\n * // Rate limit exceeded\n * context.response.setStatusCode(429);\n * return { error: 'Too many requests', retryAfter: result.resetTime };\n * }\n * ```\n *\n * @see {@link SlidingWindowCounterStrategy} for algorithm details\n */\nexport class RateLimiter {\n private readonly _config: RateLimitConfig;\n private readonly _strategy: InternalRateLimitStrategy;\n\n constructor(config: RateLimitConfig) {\n this._config = config;\n this._strategy = _createStrategy(config);\n }\n\n /**\n * Check if a request should be allowed\n *\n * Delegates to the configured strategy (sliding window counter, token bucket, etc.)\n * to determine if the request is within rate limits.\n *\n * @param context - Request context containing IP and identifying information\n * @returns Result indicating if request is allowed and current limit status\n *\n * @example\n * ```typescript\n * const result = limiter.check(context);\n *\n * if (result.allowed) {\n * // Request is allowed\n * console.log(`Remaining: ${result.remaining}/${result.limit}`);\n * } else {\n * // Rate limit exceeded\n * const retryAfter = Math.ceil((result.resetTime - Date.now()) / 1000);\n * console.log(`Rate limit exceeded. Retry after ${retryAfter}s`);\n * }\n * ```\n */\n async check(context: Context<any>): Promise<InternalRateLimitResult> {\n return this._strategy.check(context);\n }\n\n /**\n * Destroy the rate limiter and clean up resources\n * Call this when shutting down the server to free memory\n *\n * @example\n * ```typescript\n * const limiter = new RateLimiter(config);\n * // ... use limiter ...\n * limiter.destroy(); // Clean up on shutdown\n * ```\n */\n async destroy(): Promise<void> {\n await this._strategy.destroy();\n }\n\n get config(): RateLimitConfig {\n return this._config;\n }\n}\n\n/**\n * Add standard rate limit headers to response\n *\n * Implements RFC draft standard headers:\n * - RateLimit-Limit: Maximum requests per window\n * - RateLimit-Remaining: Requests remaining in current window\n * - RateLimit-Reset: Time when the rate limit resets (Unix timestamp)\n * - Retry-After: Seconds until the client can retry (only when limit exceeded)\n *\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers\n */\nexport const addRateLimitHeaders = (context: Context<any>, result: InternalRateLimitResult): void => {\n // Standard rate limit headers (RFC draft)\n context.response.addHeaders({\n 'RateLimit-Limit': String(result.limit),\n 'RateLimit-Remaining': String(result.remaining),\n 'RateLimit-Reset': String(Math.ceil(result.resetTime / 1000)), // Unix timestamp in seconds\n });\n\n // Add Retry-After header when limit is exceeded\n if (!result.allowed) {\n const retryAfter = _calculateRetryAfter(result.resetTime);\n context.response.addHeaders({\n 'Retry-After': String(retryAfter),\n });\n }\n};\n",
47
46
  "/**\n * Rate limiting algorithm types\n *\n * Available algorithms for rate limiting:\n * - sliding-window-counter: Memory efficient, Redis-ready, 99%+ accurate\n * - token-bucket: (Future) Allows smooth traffic patterns with continuous refill\n * - sliding-window-log: (Future) 100% accurate but memory intensive\n */\nexport const rateLimitAlgorithm = {\n slidingWindowCounter: 'sliding-window-counter',\n // tokenBucket: 'token-bucket', // Future enhancement\n // slidingWindowLog: 'sliding-window-log', // Future enhancement\n} as const;\n\n/**\n * Rate limiting store type options\n *\n * Available store types for rate limiting:\n * - memory: In-memory store\n * - redis: Redis store\n */\nexport const rateLimitStoreType = {\n memory: 'memory',\n redis: 'redis',\n} as const;\n",
48
- "import { httpStatusCode } from '@constants/http.ts';\nimport { rateLimitAlgorithm } from '@constants/rateLimit.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport { log } from '@core/utils/log.ts';\nimport type { RateLimitAlgorithm } from '@typedefs/constants/rateLimit.js';\nimport type { Context, HandlerCallback } from '@typedefs/public/Context.js';\nimport type { RateLimitOptions, StoreConfig } from '@typedefs/public/RateLimit.js';\nimport type { HandlerCallbackGenerics } from '@typedefs/public/HandlerCallbackGenerics.js';\n\nexport class RateLimitConfig implements RateLimitOptions {\n algorithm: RateLimitAlgorithm;\n store: StoreConfig;\n window: number; // milliseconds\n max: number;\n standardHeaders: boolean;\n skipSuccessfulRequests: boolean;\n skipFailedRequests: boolean;\n keyGenerator: (ctx: Context<any>) => string;\n handler: <T extends HandlerCallbackGenerics>(ctx: Context<T>) => HandlerCallback<T>;\n\n constructor(config?: RateLimitOptions) {\n this._validateConfig(config);\n\n this.algorithm = config?.algorithm ?? rateLimitAlgorithm.slidingWindowCounter;\n this.store = config?.store ?? { type: 'memory' };\n this.window = _convertTimeToMs(config?.window ?? '15m');\n this.max = config?.max ?? 100;\n this.standardHeaders = config?.standardHeaders ?? true;\n this.skipSuccessfulRequests = config?.skipSuccessfulRequests ?? false;\n this.skipFailedRequests = config?.skipFailedRequests ?? false;\n this.keyGenerator = config?.keyGenerator ?? defaultKeyGenerator;\n this.handler = (config?.handler ?? defaultHandler) as <T extends HandlerCallbackGenerics>(ctx: Context<T>) => HandlerCallback<T>;\n }\n\n private _validateConfig(config?: RateLimitOptions): void {\n if (!config) return;\n _validateRateLimitConfig(config);\n _warnRateLimitConfig(config);\n }\n\n get config(): RateLimitOptions {\n return {\n algorithm: this.algorithm,\n window: this.window,\n max: this.max,\n standardHeaders: this.standardHeaders,\n skipSuccessfulRequests: this.skipSuccessfulRequests,\n skipFailedRequests: this.skipFailedRequests,\n keyGenerator: this.keyGenerator,\n handler: this.handler,\n };\n }\n}\n\n/**\n * Validate rate limit configuration minimums\n */\nconst _validateRateLimitConfig = (config: RateLimitOptions): void => {\n if (config.max !== undefined) {\n if (typeof config.max !== 'number' || isNaN(config.max)) {\n throw new Error('rateLimit.max must be a number');\n }\n\n if (config.max < 1) {\n throw new Error('rateLimit.max must be at least 1 request per window');\n }\n\n if (!Number.isInteger(config.max)) {\n throw new Error('rateLimit.max must be an integer (no decimals)');\n }\n }\n\n if (config.window !== undefined) {\n // Validate time string format if it's a string\n if (typeof config.window === 'string') {\n // Use named capture groups for clarity and lint compliance\n const timeRegex = /^(?<value>\\d+)(?<unit>s|m|h|d)$/;\n if (!timeRegex.test(config.window)) {\n throw new Error(\n `rateLimit.window must be a valid time string (e.g., '30s', '15m', '2h', '1d') or milliseconds as a number. Received: \"${config.window}\"`,\n );\n }\n\n // Convert and validate the milliseconds value\n const ms = _convertTimeToMs(config.window);\n if (ms < 1000) {\n throw new Error(\n `rateLimit.window must be at least 1000ms (1 second). Received: ${config.window} (${ms}ms). ` +\n 'Very short time windows can cause performance issues and inaccurate rate limiting.',\n );\n }\n } else if (typeof config.window === 'number') {\n if (isNaN(config.window)) {\n throw new Error('rateLimit.window must be a valid number when using milliseconds');\n }\n\n if (config.window < 1000) {\n throw new Error(\n `rateLimit.window must be at least 1000ms (1 second). Received: ${config.window}ms. ` +\n 'Very short time windows can cause performance issues and inaccurate rate limiting.',\n );\n }\n\n if (!Number.isInteger(config.window)) {\n throw new Error('rateLimit.window must be an integer when using milliseconds (no decimals)');\n }\n } else {\n throw new Error('rateLimit.window must be a time string (e.g., \"15m\") or milliseconds as a number');\n }\n }\n};\n\n/**\n * Issue security warnings for risky rate limit configurations\n */\nconst _warnRateLimitConfig = (config: RateLimitOptions): void => {\n // Warn if rate limiting is disabled\n if (config.enabled === false) {\n log.warn(\n '[SECURITY WARNING] Rate limiting is disabled. ' +\n 'This removes DoS protection from your API. Only disable for development or if you have external rate limiting (e.g., API gateway, CDN).',\n );\n }\n\n // Warn about very permissive rate limits\n if (config.max !== undefined && config.max > 10000) {\n log.warn(\n `[SECURITY WARNING] rateLimit.max is set to ${config.max} requests. ` +\n 'Very high rate limits may not provide adequate DoS protection. Consider if this limit is necessary for your use case.',\n );\n }\n\n // Warn about very long time windows\n if (config.window !== undefined) {\n const windowMs = typeof config.window === 'string' ? _convertTimeToMs(config.window) : config.window;\n const oneHourMs = 3600000;\n\n if (windowMs > oneHourMs) {\n const hours = Math.round(windowMs / oneHourMs);\n log.warn(\n `[SECURITY WARNING] rateLimit.window is set to ${typeof config.window === 'string' ? config.window : `${windowMs}ms`} (${hours}h). ` +\n 'Very long time windows may allow burst attacks before limits are enforced. Consider shorter windows for better protection.',\n );\n }\n }\n\n // Warn about very short time windows (performance concern)\n if (config.window !== undefined) {\n const windowMs = typeof config.window === 'string' ? _convertTimeToMs(config.window) : config.window;\n\n if (windowMs < 10000 && config.max !== undefined && config.max > 100) {\n // Less than 10 seconds with high request count\n log.warn(\n `[PERFORMANCE WARNING] rateLimit.window is set to ${typeof config.window === 'string' ? config.window : `${windowMs}ms`} with max ${config.max} requests. ` +\n 'Very short time windows with high request counts can cause performance overhead. Consider increasing the window or decreasing max.',\n );\n }\n }\n};\n\nconst defaultHandler = (ctx: Context<any>): { success: false; message: string } => {\n ctx.response.setStatusCode(httpStatusCode.tooManyRequests);\n return {\n success: false,\n message: 'Yinz are sending too many requests. Slow down, jagoff!',\n };\n};\n\nconst defaultKeyGenerator = (ctx: Context<any>): string => ctx.request.ipAddress;\n",
49
- "import { RateLimiter, addRateLimitHeaders } from '@core/modules/rateLimit/RateLimiter.ts';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\n\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport type { RateLimitOptions } from '@typedefs/public/RateLimit.js';\nimport { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\nimport type { HandlerCallbackGenerics } from '@typedefs/public/HandlerCallbackGenerics.js';\n\n/**\n * Create a rate limiting hook for use in route options\n *\n * This function creates a beforeRoute hook that implements rate limiting\n * for a specific route. When used in beforeRoute, it will apply AFTER the\n * global rate limit (if enabled), making it additive. To skip global rate\n * limiting and use only per-route, combine with skipRateLimit().\n *\n * @example\n * ```typescript\n * import { rateLimit, skipRateLimit } from 'yinzerflow';\n *\n * // Strict rate limiting IN ADDITION to global limit\n * app.get('/api/expensive',\n * { beforeRoute: [rateLimit({ max: 5, windowMs: 60000 })] },\n * async (ctx) => {\n * return { data: 'expensive computation' };\n * }\n * );\n *\n * // ONLY per-route rate limiting (skip global)\n * app.post('/api/auth/login',\n * {\n * beforeRoute: [\n * skipRateLimit(), // Skip global\n * rateLimit({ max: 5, windowMs: 15 * 60 * 1000 }) // Apply route-specific\n * ]\n * },\n * async (ctx) => {\n * // Login logic\n * }\n * );\n * ```\n */\nexport const rateLimitHook =\n <T extends HandlerCallbackGenerics>(rateLimitOptions: RateLimitOptions): HandlerCallback<T> =>\n // Return the hook function\n async (context) => {\n const rateLimitConfig = new RateLimitConfig(rateLimitOptions);\n const rateLimiter = new RateLimiter(rateLimitConfig);\n\n // Check if request is within rate limit\n const result = await rateLimiter.check(context);\n\n // Add headers if configured\n if (rateLimiter.config.standardHeaders) {\n addRateLimitHeaders(context, result);\n }\n\n // Check if limit exceeded\n if (!result.allowed) {\n return rateLimiter.config.handler<T>(context);\n }\n\n // Continue to next hook/handler\n return void 0;\n };\n\n/**\n * Create a global rate limiting hook used internally by the framework\n *\n * This function is used to create the global rate limiting hook used internally by the framework\n */\nexport const _createGlobalRateLimitHook =\n <T extends HandlerCallbackGenerics>(rateLimiter: RateLimiter): HandlerCallback<T> =>\n // Return the hook function\n async (context) => {\n // Check if request is within rate limit\n const result = await rateLimiter.check(context);\n\n // Add headers if configured\n if (rateLimiter.config.standardHeaders) {\n addRateLimitHeaders(context, result);\n }\n\n // Check if limit exceeded\n if (!result.allowed) {\n return rateLimiter.config.handler(context);\n }\n\n // Continue to next hook/handler\n return void 0;\n };\n",
47
+ "import { httpStatusCode } from '@constants/http.ts';\nimport { rateLimitAlgorithm } from '@constants/rateLimit.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport { log } from '@core/utils/log.ts';\nimport type { Logger } from '@typedefs/public/Logger.js';\nimport type { RateLimitAlgorithm } from '@typedefs/constants/rateLimit.js';\nimport type { Context, HandlerCallback } from '@typedefs/public/Context.js';\nimport type { RateLimitOptions, StoreConfig } from '@typedefs/public/RateLimit.js';\nimport type { HandlerCallbackGenerics } from '@typedefs/public/HandlerCallbackGenerics.js';\n\nexport class RateLimitConfig implements RateLimitOptions {\n algorithm: RateLimitAlgorithm;\n store: StoreConfig;\n window: number; // milliseconds\n max: number;\n standardHeaders: boolean;\n skipSuccessfulRequests: boolean;\n skipFailedRequests: boolean;\n keyGenerator: (ctx: Context<any>) => string;\n handler: <T extends HandlerCallbackGenerics>(ctx: Context<T>) => HandlerCallback<T>;\n\n constructor(config?: RateLimitOptions, logger?: Logger) {\n this._validateConfig(config, logger ?? log);\n\n this.algorithm = config?.algorithm ?? rateLimitAlgorithm.slidingWindowCounter;\n this.store = config?.store ?? { type: 'memory' };\n this.window = _convertTimeToMs(config?.window ?? '15m');\n this.max = config?.max ?? 100;\n this.standardHeaders = config?.standardHeaders ?? true;\n this.skipSuccessfulRequests = config?.skipSuccessfulRequests ?? false;\n this.skipFailedRequests = config?.skipFailedRequests ?? false;\n this.keyGenerator = config?.keyGenerator ?? defaultKeyGenerator;\n this.handler = (config?.handler ?? defaultHandler) as <T extends HandlerCallbackGenerics>(ctx: Context<T>) => HandlerCallback<T>;\n }\n\n private _validateConfig(config: RateLimitOptions | undefined, logger: Logger): void {\n if (!config) return;\n _validateRateLimitConfig(config);\n _warnRateLimitConfig(config, logger);\n }\n\n get config(): RateLimitOptions {\n return {\n algorithm: this.algorithm,\n window: this.window,\n max: this.max,\n standardHeaders: this.standardHeaders,\n skipSuccessfulRequests: this.skipSuccessfulRequests,\n skipFailedRequests: this.skipFailedRequests,\n keyGenerator: this.keyGenerator,\n handler: this.handler,\n };\n }\n}\n\n/**\n * Validate rate limit configuration minimums\n */\nconst _validateRateLimitConfig = (config: RateLimitOptions): void => {\n if (config.max !== undefined) {\n if (typeof config.max !== 'number' || isNaN(config.max)) {\n throw new Error('rateLimit.max must be a number');\n }\n\n if (config.max < 1) {\n throw new Error('rateLimit.max must be at least 1 request per window');\n }\n\n if (!Number.isInteger(config.max)) {\n throw new Error('rateLimit.max must be an integer (no decimals)');\n }\n }\n\n if (config.window !== undefined) {\n // Validate time string format if it's a string\n if (typeof config.window === 'string') {\n // Use named capture groups for clarity and lint compliance\n const timeRegex = /^(?<value>\\d+)(?<unit>s|m|h|d)$/;\n if (!timeRegex.test(config.window)) {\n throw new Error(\n `rateLimit.window must be a valid time string (e.g., '30s', '15m', '2h', '1d') or milliseconds as a number. Received: \"${config.window}\"`,\n );\n }\n\n // Convert and validate the milliseconds value\n const ms = _convertTimeToMs(config.window);\n if (ms < 1000) {\n throw new Error(\n `rateLimit.window must be at least 1000ms (1 second). Received: ${config.window} (${ms}ms). ` +\n 'Very short time windows can cause performance issues and inaccurate rate limiting.',\n );\n }\n } else if (typeof config.window === 'number') {\n if (isNaN(config.window)) {\n throw new Error('rateLimit.window must be a valid number when using milliseconds');\n }\n\n if (config.window < 1000) {\n throw new Error(\n `rateLimit.window must be at least 1000ms (1 second). Received: ${config.window}ms. ` +\n 'Very short time windows can cause performance issues and inaccurate rate limiting.',\n );\n }\n\n if (!Number.isInteger(config.window)) {\n throw new Error('rateLimit.window must be an integer when using milliseconds (no decimals)');\n }\n } else {\n throw new Error('rateLimit.window must be a time string (e.g., \"15m\") or milliseconds as a number');\n }\n }\n};\n\n/**\n * Issue security warnings for risky rate limit configurations\n */\nconst _warnRateLimitConfig = (config: RateLimitOptions, logger: Logger): void => {\n // Warn if rate limiting is disabled\n if (config.enabled === false) {\n logger.warn(\n '[SECURITY WARNING] Rate limiting is disabled. ' +\n 'This removes DoS protection from your API. Only disable for development or if you have external rate limiting (e.g., API gateway, CDN).',\n );\n }\n\n // Warn about very permissive rate limits\n if (config.max !== undefined && config.max > 10000) {\n logger.warn(\n `[SECURITY WARNING] rateLimit.max is set to ${config.max} requests. ` +\n 'Very high rate limits may not provide adequate DoS protection. Consider if this limit is necessary for your use case.',\n );\n }\n\n // Warn about very long time windows\n if (config.window !== undefined) {\n const windowMs = _convertTimeToMs(config.window);\n const oneHourMs = 3600000;\n\n if (windowMs > oneHourMs) {\n const hours = Math.round(windowMs / oneHourMs);\n logger.warn(\n `[SECURITY WARNING] rateLimit.window is set to ${typeof config.window === 'string' ? config.window : `${windowMs}ms`} (${hours}h). ` +\n 'Very long time windows may allow burst attacks before limits are enforced. Consider shorter windows for better protection.',\n );\n }\n }\n\n // Warn about very short time windows (performance concern)\n if (config.window !== undefined) {\n const windowMs = _convertTimeToMs(config.window);\n\n if (windowMs < 10000 && config.max !== undefined && config.max > 100) {\n // Less than 10 seconds with high request count\n logger.warn(\n `[PERFORMANCE WARNING] rateLimit.window is set to ${typeof config.window === 'string' ? config.window : `${windowMs}ms`} with max ${config.max} requests. ` +\n 'Very short time windows with high request counts can cause performance overhead. Consider increasing the window or decreasing max.',\n );\n }\n }\n};\n\nconst defaultHandler = (ctx: Context<any>): { success: false; message: string } => {\n ctx.response.setStatusCode(httpStatusCode.tooManyRequests);\n return {\n success: false,\n message: 'Yinz are sending too many requests. Slow down, jagoff!',\n };\n};\n\nconst defaultKeyGenerator = (ctx: Context<any>): string => ctx.request.ipAddress;\n",
48
+ "import { RateLimiter, addRateLimitHeaders } from '@core/modules/rateLimit/RateLimiter.ts';\nimport type { HandlerCallback } from '@typedefs/public/Context.js';\n\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport type { RateLimitOptions } from '@typedefs/public/RateLimit.js';\nimport { RateLimitConfig } from '@core/modules/rateLimit/RateLimitConfig.ts';\nimport type { HandlerCallbackGenerics } from '@typedefs/public/HandlerCallbackGenerics.js';\n\n/**\n * Create a rate limiting hook for use in route options\n *\n * This function creates a beforeRoute hook that implements rate limiting\n * for a specific route. When used in beforeRoute, it will apply AFTER the\n * global rate limit (if enabled), making it additive. To skip global rate\n * limiting and use only per-route, combine with skipRateLimit().\n *\n * @example\n * ```typescript\n * import { rateLimit, skipRateLimit } from 'yinzerflow';\n *\n * // Strict rate limiting IN ADDITION to global limit\n * app.get('/api/expensive',\n * { beforeRoute: [rateLimit({ max: 5, windowMs: 60000 })] },\n * async (ctx) => {\n * return { data: 'expensive computation' };\n * }\n * );\n *\n * // ONLY per-route rate limiting (skip global)\n * app.post('/api/auth/login',\n * {\n * beforeRoute: [\n * skipRateLimit(), // Skip global\n * rateLimit({ max: 5, windowMs: 15 * 60 * 1000 }) // Apply route-specific\n * ]\n * },\n * async (ctx) => {\n * // Login logic\n * }\n * );\n * ```\n */\nexport const rateLimitHook =\n <T extends HandlerCallbackGenerics>(rateLimitOptions: RateLimitOptions): HandlerCallback<T> =>\n // Return the hook function\n async (context) => {\n const rateLimitConfig = new RateLimitConfig(rateLimitOptions);\n const rateLimiter = new RateLimiter(rateLimitConfig);\n\n // Check if request is within rate limit\n const result = await rateLimiter.check(context);\n\n // Add headers if configured\n if (rateLimiter.config.standardHeaders) {\n addRateLimitHeaders(context, result);\n }\n\n // Check if limit exceeded\n if (!result.allowed) {\n return rateLimiter.config.handler<T>(context);\n }\n\n // Continue to next hook/handler\n return void 0;\n };\n\n/**\n * Create a global rate limiting hook used internally by the framework\n *\n * This function is used to create the global rate limiting hook used internally by the framework\n */\nexport const _createGlobalRateLimitHook =\n <T extends HandlerCallbackGenerics>(rateLimiter: RateLimiter, onRateLimitHit?: (ip: string, path: string) => void): HandlerCallback<T> =>\n // Return the hook function\n async (context) => {\n // Check if request is within rate limit\n const result = await rateLimiter.check(context);\n\n // Add headers if configured\n if (rateLimiter.config.standardHeaders) {\n addRateLimitHeaders(context, result);\n }\n\n // Check if limit exceeded\n if (!result.allowed) {\n onRateLimitHit?.(context.request.ipAddress, context.request.path);\n return rateLimiter.config.handler(context);\n }\n\n // Continue to next hook/handler\n return void 0;\n };\n",
50
49
  "import { createHmac } from 'node:crypto';\nimport { CookieParserConfig } from './CookieParserConfig.ts';\nimport type { CookieOptions, CookieParserOptions } from '@typedefs/public/CookieParser.js';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\n\n/**\n * Cookie parser implementation with HMAC-SHA256 signing support\n *\n * This class handles parsing incoming cookies, setting outgoing cookies, and\n * signing/validating cookies for tamper detection.\n *\n * @example\n * ```typescript\n * // Create a cookie parser with signing\n * const parser = new CookieParser({\n * secret: process.env.COOKIE_SECRET,\n * defaults: {\n * httpOnly: true,\n * secure: true,\n * sameSite: 'strict'\n * }\n * });\n *\n * // Parse incoming cookies\n * const cookieHeader = 'sessionId=abc123; userId=42';\n * const cookies = parser.parse(cookieHeader);\n *\n * // Set a cookie\n * const setCookieHeader = parser.set('sessionId', 'abc123', {\n * httpOnly: true,\n * secure: true,\n * maxAge: '1h' // or 3600 for 1 hour in seconds\n * });\n *\n * // Sign a cookie\n * const signed = parser.sign('sessionId', 'abc123');\n * // returns: \"abc123.hmacsignature\"\n *\n * // Unsign and validate\n * const original = parser.unsign(signed);\n * // returns: \"abc123\" if valid, false if tampered\n * ```\n */\nexport class CookieParser {\n private readonly _config: CookieParserConfig;\n\n constructor(config?: CookieParserOptions) {\n this._config = new CookieParserConfig(config);\n }\n\n /**\n * Parse a Cookie header into a Map of cookie name-value pairs\n *\n * Handles URL decoding and malformed cookies gracefully.\n *\n * @param cookieHeader - The raw Cookie header value\n * @returns Map of cookie name to value\n *\n * @example\n * ```typescript\n * const cookieHeader = 'sessionId=abc123; userId=42; theme=dark';\n * const cookies = parser.parse(cookieHeader);\n *\n * cookies.get('sessionId'); // \"abc123\"\n * cookies.get('userId'); // \"42\"\n * cookies.get('theme'); // \"dark\"\n * ```\n */\n parse(cookieHeader: string): Map<string, string> {\n const cookies = new Map<string, string>();\n\n // Return empty map if no cookies\n if (!cookieHeader || typeof cookieHeader !== 'string') {\n return cookies;\n }\n\n // Split by semicolon and parse each cookie\n const cookiePairs = cookieHeader.split(';');\n for (const pair of cookiePairs) {\n const trimmed = pair.trim();\n if (!trimmed) continue;\n\n // Split on first = sign\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) continue;\n\n const name = trimmed.slice(0, equalIndex).trim();\n const value = trimmed.slice(equalIndex + 1).trim();\n\n if (name && value) {\n try {\n cookies.set(name, decodeURIComponent(value));\n } catch (_error) {\n // Skip cookies with invalid URL encoding\n continue;\n }\n }\n }\n\n return cookies;\n }\n\n /**\n * Build a Set-Cookie header string\n *\n * Applies default cookie options from configuration and handles URL encoding.\n *\n * @param name - Cookie name (will be URL encoded)\n * @param value - Cookie value (will be URL encoded)\n * @param options - Optional cookie attributes\n * @returns Formatted Set-Cookie header string\n *\n * @example\n * ```typescript\n * const header = parser.set('sessionId', 'abc123', {\n * httpOnly: true,\n * secure: true,\n * maxAge: '1h', // or 3600 for 1 hour in seconds\n * sameSite: 'strict'\n * });\n *\n * // Returns: \"sessionId=abc123; HttpOnly; Secure; Max-Age=3600; SameSite=Strict\"\n * ```\n */\n set(name: string, value: string, options?: CookieOptions): string {\n // URL encode name and value\n const encodedName = encodeURIComponent(name);\n const encodedValue = encodeURIComponent(value);\n\n // Combine defaults with provided options\n const cookieOptions = this._mergeOptions(options);\n\n // Build cookie parts\n const parts = [`${encodedName}=${encodedValue}`];\n\n // Add expires\n if (cookieOptions.expires) {\n parts.push(`Expires=${cookieOptions.expires.toUTCString()}`);\n }\n\n // Add maxAge\n if (cookieOptions.maxAge !== undefined) {\n parts.push(`Max-Age=${cookieOptions.maxAge}`);\n }\n\n // Add domain\n if (cookieOptions.domain) {\n parts.push(`Domain=${cookieOptions.domain}`);\n }\n\n // Add path\n if (cookieOptions.path) {\n parts.push(`Path=${cookieOptions.path}`);\n }\n\n // Add secure flag\n if (cookieOptions.secure) {\n parts.push('Secure');\n }\n\n // Add httpOnly flag\n if (cookieOptions.httpOnly) {\n parts.push('HttpOnly');\n }\n\n // Add sameSite\n if (cookieOptions.sameSite) {\n parts.push(`SameSite=${cookieOptions.sameSite.charAt(0).toUpperCase() + cookieOptions.sameSite.slice(1)}`);\n }\n\n return parts.join('; ');\n }\n\n /**\n * Sign a cookie value using HMAC-SHA256\n *\n * Creates a signature that can be validated to detect tampering.\n * The signed value format is: `value.signature`\n *\n * @param name - Cookie name (used in signature calculation)\n * @param value - Cookie value to sign\n * @returns Signed cookie value\n *\n * @example\n * ```typescript\n * const signed = parser.sign('sessionId', 'abc123');\n * // Returns: \"abc123.xyz789...\" (value with appended signature)\n * ```\n */\n sign(name: string, value: string): string {\n if (!this._config.secret) {\n throw new Error('Cannot sign cookie: no secret configured');\n }\n\n // Create HMAC signature using name=value\n const signature = createHmac('sha256', this._config.secret).update(`${name}=${value}`).digest('base64url').replace(/=/g, ''); // Remove padding\n\n // Return value.signature format\n return `${value}.${signature}`;\n }\n\n /**\n * Validate and unsign a signed cookie value\n *\n * Verifies the HMAC signature and returns the original value if valid.\n *\n * @param name - Cookie name (used in signature validation)\n * @param signedValue - The signed cookie value\n * @returns Original value if signature is valid, false if tampered\n *\n * @example\n * ```typescript\n * const signed = \"abc123.xyz789...\";\n * const original = parser.unsign('sessionId', signed);\n *\n * if (original === false) {\n * // Cookie was tampered with\n * console.error('Tampered cookie detected');\n * } else {\n * // Cookie is valid\n * console.log('Original value:', original);\n * }\n * ```\n */\n unsign(name: string, signedValue: string): string | false {\n if (!this._config.secret) {\n throw new Error('Cannot unsign cookie: no secret configured');\n }\n\n // Split value and signature\n const lastDotIndex = signedValue.lastIndexOf('.');\n if (lastDotIndex === -1) {\n return false; // No signature found\n }\n\n const value = signedValue.slice(0, lastDotIndex);\n const receivedSignature = signedValue.slice(lastDotIndex + 1);\n\n // Create expected signature\n const expectedSignature = createHmac('sha256', this._config.secret).update(`${name}=${value}`).digest('base64url').replace(/=/g, ''); // Remove padding\n\n // Compare signatures\n return expectedSignature === receivedSignature ? value : false;\n }\n\n /**\n * Merge default options with provided options\n */\n private _mergeOptions(options?: CookieOptions): CookieOptions {\n const merged = {\n ...this._config.defaults,\n ...options,\n };\n\n // Convert TimeString to seconds for maxAge\n if (merged.maxAge !== undefined && typeof merged.maxAge === 'string') {\n merged.maxAge = _convertTimeToMs(merged.maxAge) / 1000;\n }\n\n return merged;\n }\n\n /**\n * Get the cookie parser configuration\n */\n get config(): CookieParserConfig {\n return this._config;\n }\n\n /**\n * Check if a cookie should be signed based on configuration\n */\n shouldSign(name: string): boolean {\n if (!this._config.secret) {\n return false;\n }\n\n // If signed array is undefined, sign all cookies\n if (this._config.signed === undefined || this._config.signed.length === 0) {\n return true;\n }\n\n // Otherwise, only sign if name is in signed array\n return this._config.signed.includes(name);\n }\n}\n",
51
- "import type { CookieParserOptions } from '@typedefs/public/CookieParser.js';\nimport { log } from '@core/utils/log.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\n\nexport class CookieParserConfig {\n enabled: boolean;\n secret: string | undefined;\n signed: Array<string> | undefined;\n defaults: CookieParserOptions['defaults'] | undefined;\n\n constructor(config?: CookieParserOptions) {\n this._validateConfig(config);\n this.enabled = config?.enabled ?? false;\n this.secret = config?.secret;\n this.signed = config?.signed;\n this.defaults = config?.defaults;\n }\n\n private _validateConfig(config?: CookieParserOptions): void {\n if (!config) return;\n _validateCookieConfig(config);\n _warnCookieConfig(config);\n }\n\n get config(): Partial<CookieParserOptions> {\n const config: Partial<CookieParserOptions> = {\n enabled: this.enabled,\n };\n\n if (this.secret !== undefined) {\n config.secret = this.secret;\n }\n if (this.signed !== undefined) {\n config.signed = this.signed;\n }\n if (this.defaults !== undefined) {\n config.defaults = this.defaults;\n }\n\n return config;\n }\n}\n\n/**\n * Validate cookie parser configuration\n */\nconst _validateCookieConfig = (config: CookieParserOptions): void => {\n // Validate secret\n if (config.secret !== undefined) {\n if (typeof config.secret !== 'string') {\n throw new Error('cookieParser.secret must be a string');\n }\n\n if (config.secret.length < 32) {\n throw new Error('cookieParser.secret must be at least 32 characters for security. Use a strong, random secret stored in environment variables.');\n }\n }\n\n // Validate signed array\n if (config.signed !== undefined) {\n if (!Array.isArray(config.signed)) {\n throw new Error('cookieParser.signed must be an array of cookie names');\n }\n\n for (const cookieName of config.signed) {\n if (typeof cookieName !== 'string') {\n throw new Error('cookieParser.signed must be an array of strings (cookie names)');\n }\n\n if (cookieName.length === 0) {\n throw new Error('cookieParser.signed cannot contain empty cookie names');\n }\n }\n }\n\n // Validate defaults\n if (config.defaults) {\n _validateCookieOptions(config.defaults);\n }\n};\n\n/**\n * Validate cookie options\n */\nconst _validateCookieOptions = (options: CookieParserOptions['defaults']): void => {\n if (!options) return;\n\n _validateMaxAge(options);\n _validateSecure(options);\n _validateHttpOnly(options);\n _validateSameSite(options);\n _validateDomain(options);\n _validatePath(options);\n _validateExpires(options);\n};\n\n/**\n * Validate maxAge option\n */\nconst _validateMaxAge = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.maxAge === undefined) return;\n\n // Validate time string format if it's a string\n if (typeof options.maxAge === 'string') {\n const timeRegex = /^(?<value>\\d+)(?<unit>ms|s|m|h|d)$/;\n if (!timeRegex.test(options.maxAge)) {\n throw new Error(\n `cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: \"${options.maxAge}\"`,\n );\n }\n return;\n }\n\n // Validate number format\n if (typeof options.maxAge !== 'number' || isNaN(options.maxAge)) {\n throw new Error(\n `cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: \"${options.maxAge}\"`,\n );\n }\n\n if (options.maxAge < 0) {\n throw new Error('cookieParser.defaults.maxAge must be 0 or greater');\n }\n\n if (!Number.isInteger(options.maxAge)) {\n throw new Error('cookieParser.defaults.maxAge must be an integer (no decimals)');\n }\n};\n\n/**\n * Validate secure option\n */\nconst _validateSecure = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.secure === undefined) return;\n if (typeof options.secure !== 'boolean') {\n throw new Error('cookieParser.defaults.secure must be a boolean');\n }\n};\n\n/**\n * Validate httpOnly option\n */\nconst _validateHttpOnly = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.httpOnly === undefined) return;\n if (typeof options.httpOnly !== 'boolean') {\n throw new Error('cookieParser.defaults.httpOnly must be a boolean');\n }\n};\n\n/**\n * Validate sameSite option\n */\nconst _validateSameSite = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.sameSite === undefined) return;\n if (!['strict', 'lax', 'none'].includes(options.sameSite)) {\n throw new Error('cookieParser.defaults.sameSite must be one of: \"strict\", \"lax\", \"none\"');\n }\n};\n\n/**\n * Validate domain option\n */\nconst _validateDomain = (options: CookieParserOptions['defaults']): void => {\n if (!options) return;\n if (options.domain === undefined) return;\n\n if (typeof options.domain !== 'string') {\n throw new Error('cookieParser.defaults.domain must be a string');\n }\n\n if (options.domain.length === 0) {\n throw new Error('cookieParser.defaults.domain cannot be empty');\n }\n};\n\n/**\n * Validate path option\n */\nconst _validatePath = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.path === undefined) return;\n\n if (typeof options.path !== 'string') {\n throw new Error('cookieParser.defaults.path must be a string');\n }\n\n if (!options.path.startsWith('/')) {\n throw new Error('cookieParser.defaults.path must start with \"/\"');\n }\n};\n\n/**\n * Validate expires option\n */\nconst _validateExpires = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.expires === undefined) return;\n if (!(options.expires instanceof Date)) {\n throw new Error('cookieParser.defaults.expires must be a Date object');\n }\n};\n\n/**\n * Issue security warnings for risky cookie configurations\n */\nconst _warnCookieConfig = (config: CookieParserOptions): void => {\n // Warn if cookie parser is disabled\n if (config.enabled === false) {\n log.warn('[SECURITY WARNING] Cookie parser is disabled. Cookies will not be parsed or validated. Only disable for special use cases.');\n }\n\n // Warn if no secret in production\n const isProduction = process.env.NODE_ENV === 'production';\n if (isProduction && !config.secret) {\n log.warn(\n '[SECURITY WARNING] No secret provided for cookie signing in production. ' +\n 'Cookies will not be signed and cannot be validated for tampering. Consider using a secret.',\n );\n }\n\n // Warn if default secure is false in production\n if (isProduction && config.defaults?.secure === false) {\n log.warn(\n '[SECURITY WARNING] cookieParser.defaults.secure is false in production. ' +\n 'Cookies will be sent over HTTP, which is insecure. Always use secure cookies in production.',\n );\n }\n\n // Warn if default httpOnly is false in production\n if (isProduction && config.defaults?.httpOnly === false) {\n log.warn(\n '[SECURITY WARNING] cookieParser.defaults.httpOnly is false in production. ' +\n 'Cookies will be accessible to JavaScript, which increases XSS risk. ' +\n 'Only disable httpOnly for cookies that need JavaScript access.',\n );\n }\n\n // Warn about SameSite=none without secure\n if (config.defaults?.sameSite === 'none' && config.defaults.secure !== true) {\n log.warn('[SECURITY WARNING] SameSite=none requires secure=true. Browsers will reject cookies with SameSite=none without secure flag.');\n }\n};\n",
50
+ "import type { CookieParserOptions } from '@typedefs/public/CookieParser.js';\nimport { log } from '@core/utils/log.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport type { Logger } from '@typedefs/public/Logger.js';\n\nexport class CookieParserConfig {\n enabled: boolean;\n secret: string | undefined;\n signed: Array<string> | undefined;\n defaults: CookieParserOptions['defaults'] | undefined;\n\n constructor(config?: CookieParserOptions, logger?: Logger) {\n this._validateConfig(config, logger ?? log);\n this.enabled = config?.enabled ?? false;\n this.secret = config?.secret;\n this.signed = config?.signed;\n this.defaults = config?.defaults;\n }\n\n private _validateConfig(config: CookieParserOptions | undefined, logger: Logger): void {\n if (!config) return;\n _validateCookieConfig(config);\n _warnCookieConfig(config, logger);\n }\n\n get config(): Partial<CookieParserOptions> {\n const config: Partial<CookieParserOptions> = {\n enabled: this.enabled,\n };\n\n if (this.secret !== undefined) {\n config.secret = this.secret;\n }\n if (this.signed !== undefined) {\n config.signed = this.signed;\n }\n if (this.defaults !== undefined) {\n config.defaults = this.defaults;\n }\n\n return config;\n }\n}\n\n/**\n * Validate cookie parser configuration\n */\nconst _validateCookieConfig = (config: CookieParserOptions): void => {\n // Validate secret\n if (config.secret !== undefined) {\n if (typeof config.secret !== 'string') {\n throw new Error('cookieParser.secret must be a string');\n }\n\n if (config.secret.length < 32) {\n throw new Error('cookieParser.secret must be at least 32 characters for security. Use a strong, random secret stored in environment variables.');\n }\n }\n\n // Validate signed array\n if (config.signed !== undefined) {\n if (!Array.isArray(config.signed)) {\n throw new Error('cookieParser.signed must be an array of cookie names');\n }\n\n for (const cookieName of config.signed) {\n if (typeof cookieName !== 'string') {\n throw new Error('cookieParser.signed must be an array of strings (cookie names)');\n }\n\n if (cookieName.length === 0) {\n throw new Error('cookieParser.signed cannot contain empty cookie names');\n }\n }\n }\n\n // Validate defaults\n if (config.defaults) {\n _validateCookieOptions(config.defaults);\n }\n};\n\n/**\n * Validate cookie options\n */\nconst _validateCookieOptions = (options: CookieParserOptions['defaults']): void => {\n if (!options) return;\n\n _validateMaxAge(options);\n _validateSecure(options);\n _validateHttpOnly(options);\n _validateSameSite(options);\n _validateDomain(options);\n _validatePath(options);\n _validateExpires(options);\n};\n\n/**\n * Validate maxAge option\n */\nconst _validateMaxAge = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.maxAge === undefined) return;\n\n // Validate time string format if it's a string\n if (typeof options.maxAge === 'string') {\n const timeRegex = /^(?<value>\\d+)(?<unit>ms|s|m|h|d)$/;\n if (!timeRegex.test(options.maxAge)) {\n throw new Error(\n `cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: \"${options.maxAge}\"`,\n );\n }\n return;\n }\n\n // Validate number format\n if (typeof options.maxAge !== 'number' || isNaN(options.maxAge)) {\n throw new Error(\n `cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: \"${options.maxAge}\"`,\n );\n }\n\n if (options.maxAge < 0) {\n throw new Error('cookieParser.defaults.maxAge must be 0 or greater');\n }\n\n if (!Number.isInteger(options.maxAge)) {\n throw new Error('cookieParser.defaults.maxAge must be an integer (no decimals)');\n }\n};\n\n/**\n * Validate secure option\n */\nconst _validateSecure = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.secure === undefined) return;\n if (typeof options.secure !== 'boolean') {\n throw new Error('cookieParser.defaults.secure must be a boolean');\n }\n};\n\n/**\n * Validate httpOnly option\n */\nconst _validateHttpOnly = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.httpOnly === undefined) return;\n if (typeof options.httpOnly !== 'boolean') {\n throw new Error('cookieParser.defaults.httpOnly must be a boolean');\n }\n};\n\n/**\n * Validate sameSite option\n */\nconst _validateSameSite = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.sameSite === undefined) return;\n if (!['strict', 'lax', 'none'].includes(options.sameSite)) {\n throw new Error('cookieParser.defaults.sameSite must be one of: \"strict\", \"lax\", \"none\"');\n }\n};\n\n/**\n * Validate domain option\n */\nconst _validateDomain = (options: CookieParserOptions['defaults']): void => {\n if (!options) return;\n if (options.domain === undefined) return;\n\n if (typeof options.domain !== 'string') {\n throw new Error('cookieParser.defaults.domain must be a string');\n }\n\n if (options.domain.length === 0) {\n throw new Error('cookieParser.defaults.domain cannot be empty');\n }\n};\n\n/**\n * Validate path option\n */\nconst _validatePath = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.path === undefined) return;\n\n if (typeof options.path !== 'string') {\n throw new Error('cookieParser.defaults.path must be a string');\n }\n\n if (!options.path.startsWith('/')) {\n throw new Error('cookieParser.defaults.path must start with \"/\"');\n }\n};\n\n/**\n * Validate expires option\n */\nconst _validateExpires = (options: CookieParserOptions['defaults']): void => {\n if (!options || options.expires === undefined) return;\n if (!(options.expires instanceof Date)) {\n throw new Error('cookieParser.defaults.expires must be a Date object');\n }\n};\n\n/**\n * Issue security warnings for risky cookie configurations\n */\nconst _warnCookieConfig = (config: CookieParserOptions, logger: Logger): void => {\n // Warn if cookie parser is disabled\n if (config.enabled === false) {\n logger.warn('[SECURITY WARNING] Cookie parser is disabled. Cookies will not be parsed or validated. Only disable for special use cases.');\n }\n\n // Warn if no secret in production\n const isProduction = process.env.NODE_ENV === 'production';\n if (isProduction && !config.secret) {\n logger.warn(\n '[SECURITY WARNING] No secret provided for cookie signing in production. ' +\n 'Cookies will not be signed and cannot be validated for tampering. Consider using a secret.',\n );\n }\n\n // Warn if default secure is false in production\n if (isProduction && config.defaults?.secure === false) {\n logger.warn(\n '[SECURITY WARNING] cookieParser.defaults.secure is false in production. ' +\n 'Cookies will be sent over HTTP, which is insecure. Always use secure cookies in production.',\n );\n }\n\n // Warn if default httpOnly is false in production\n if (isProduction && config.defaults?.httpOnly === false) {\n logger.warn(\n '[SECURITY WARNING] cookieParser.defaults.httpOnly is false in production. ' +\n 'Cookies will be accessible to JavaScript, which increases XSS risk. ' +\n 'Only disable httpOnly for cookies that need JavaScript access.',\n );\n }\n\n // Warn about SameSite=none without secure\n if (config.defaults?.sameSite === 'none' && config.defaults.secure !== true) {\n logger.warn('[SECURITY WARNING] SameSite=none requires secure=true. Browsers will reject cookies with SameSite=none without secure flag.');\n }\n};\n",
52
51
  "import type { HandlerCallback } from '@typedefs/public/Context.js';\nimport type { CookieParserOptions } from '@typedefs/public/CookieParser.js';\nimport { CookieParser } from '@core/modules/cookieParser/CookieParser.ts';\nimport type { HandlerCallbackGenerics } from '@typedefs/public/HandlerCallbackGenerics.js';\n\n/**\n * Create a global cookie parser hook\n *\n * This middleware parses incoming cookies and makes them available on the context.\n * It also provides cookie helper methods for setting and managing cookies.\n *\n * @param config - Cookie parser configuration\n * @returns Handler callback for cookie parsing\n *\n * @example\n * ```typescript\n * import { cookieParser } from 'yinzerflow';\n *\n * // Basic usage with secret\n * app.use(cookieParser({ secret: process.env.COOKIE_SECRET }));\n *\n * // With default options\n * app.use(cookieParser({\n * secret: process.env.COOKIE_SECRET,\n * defaults: {\n * httpOnly: true,\n * secure: true,\n * sameSite: 'strict'\n * }\n * }));\n *\n * // In your route handler\n * app.get('/api/data', async (ctx) => {\n * // Access unsigned cookies\n * const theme = ctx.request.cookies.get('theme');\n *\n * // Access signed cookies\n * const sessionId = ctx.request.signedCookies.get('sessionId');\n *\n * return { theme, sessionId };\n * });\n * ```\n */\nexport const cookieParserHook =\n <T extends HandlerCallbackGenerics>(config?: CookieParserOptions): HandlerCallback<T> =>\n // Return the hook function\n (context) => {\n // Create cookie parser instance\n const cookieParser = new CookieParser(config);\n\n // Parse incoming cookies from Cookie header\n const cookieHeader = context.request.headers.cookie;\n const parsedCookies = cookieParser.parse(cookieHeader ?? '');\n\n // Initialize cookies maps on request\n context.request.cookies = parsedCookies;\n context.request.signedCookies = new Map<string, string>();\n\n // Parse signed cookies\n if (cookieParser.config.secret) {\n for (const [name, value] of parsedCookies.entries()) {\n if (cookieParser.shouldSign(name)) {\n const unsigned = cookieParser.unsign(name, value);\n if (unsigned !== false) {\n context.request.signedCookies.set(name, unsigned);\n }\n }\n }\n }\n\n // Attach cookie helpers to context\n context.cookies = {\n set: (name: string, value: string, options?: CookieParserOptions['defaults']): void => {\n // Sign the value if configured\n let cookieValue = value;\n if (cookieParser.shouldSign(name)) {\n cookieValue = cookieParser.sign(name, value);\n }\n\n // Build Set-Cookie header\n const setCookieHeader = cookieParser.set(name, cookieValue, options);\n context.response.addHeaders({ 'Set-Cookie': setCookieHeader });\n },\n sign: (name: string, value: string): string => {\n if (!cookieParser.config.secret) {\n throw new Error('Cannot sign cookie: no secret configured');\n }\n return cookieParser.sign(name, value);\n },\n unsign: (name: string, signedValue: string): string | false => {\n if (!cookieParser.config.secret) {\n throw new Error('Cannot unsign cookie: no secret configured');\n }\n return cookieParser.unsign(name, signedValue);\n },\n };\n\n // Continue to next hook/handler\n return void 0;\n };\n",
53
52
  "import { httpHeaders } from '@constants/http.ts';\nimport type { InternalContextImpl } from '@typedefs/internal/InternalContextImpl.js';\nimport type { InternalCorsEnabledOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * Core CORS handling logic\n *\n * This module implements Cross-Origin Resource Sharing (CORS) as a beforeRouting hook.\n * It validates origins, handles preflight requests, and sets appropriate CORS headers.\n *\n * Security features:\n * - Origin validation (never blindly echo back request origin)\n * - Prevents wildcard + credentials combination (CORS spec violation)\n * - Rejects unauthorized preflight requests with 403\n */\nexport class Cors {\n private readonly _normalizedOrigins: Set<string> | null;\n\n constructor(private readonly config: InternalCorsEnabledOptions) {\n // Fail fast: wildcard + credentials is forbidden by CORS spec\n if (config.origin === '*' && config.credentials) {\n throw new Error(\n 'CORS Configuration Error: Cannot use origin: \"*\" with credentials: true. ' +\n 'The CORS specification forbids this combination as it creates security vulnerabilities. ' +\n 'Choose one of these solutions:\\n' +\n ' 1) Set credentials: false (recommended for public APIs)\\n' +\n ' 2) Use specific origins instead of \"*\" (e.g., origin: [\"https://example.com\"])\\n' +\n ' 3) Disable CORS entirely (enabled: false)',\n );\n }\n\n // Pre-normalize array origins for O(1) lookup per request instead of O(m)\n if (Array.isArray(config.origin)) {\n this._normalizedOrigins = new Set(config.origin.map((o) => o.toLowerCase()));\n } else {\n this._normalizedOrigins = null;\n }\n }\n\n /**\n * Handle CORS for an incoming request.\n *\n * For OPTIONS (preflight) requests: validates origin, sets all CORS headers,\n * and short-circuits with the configured success status (default 204).\n * Returns a response object to short-circuit, or undefined if preflightContinue is true.\n *\n * For actual requests: validates origin and sets Access-Control-Allow-Origin +\n * Access-Control-Allow-Credentials headers. Always returns undefined to let\n * normal request processing continue (CORS enforcement is browser-side for\n * non-preflight requests per the CORS specification).\n *\n * @param context - The request context with headers and response object\n * @returns Response object to short-circuit (preflight), or undefined to continue\n */\n handle(context: InternalContextImpl): unknown {\n if (context.request.method === 'OPTIONS') {\n return this._handlePreflightRequest(context);\n }\n\n return void this._handleActualRequest(context);\n }\n\n /**\n * Handle OPTIONS preflight request\n */\n private _handlePreflightRequest(context: InternalContextImpl): unknown {\n // Validate origin is accepted - SECURITY CRITICAL\n const normalizedOrigin = context.request.headers.origin?.toLowerCase() ?? '';\n const isOriginAllowed = this._isOriginAllowed(normalizedOrigin, context);\n\n if (!isOriginAllowed) {\n // Reject unauthorized CORS preflight requests\n context.response.setStatusCode(403);\n return {\n error: 'CORS: Origin not allowed',\n origin: context.request.headers.origin,\n };\n }\n\n // Set response headers ONLY for allowed origins\n context.response.setStatusCode(this.config.optionsSuccessStatus);\n\n // Determine the allowed origin to echo back\n const allowedOrigin = this._resolveAllowedOrigin(context);\n\n // Set common CORS headers (origin + credentials)\n this._setCommonCorsHeaders(context, allowedOrigin);\n\n // Set preflight-specific headers\n context._response._setHeadersIfNotSet({\n [httpHeaders.accessControlAllowMethods]: this.config.methods.join(', '),\n [httpHeaders.accessControlAllowHeaders]:\n typeof this.config.allowedHeaders === 'string' ? this.config.allowedHeaders : this.config.allowedHeaders.join(', '),\n [httpHeaders.accessControlExposeHeaders]: this.config.exposedHeaders.join(', '),\n [httpHeaders.accessControlMaxAge]: this.config.maxAge.toString(),\n });\n\n if (this.config.preflightContinue) {\n // Don't short-circuit, let next handler run\n return undefined;\n }\n\n // Short-circuit with empty body (204 No Content typical response)\n return '';\n }\n\n /**\n * Handle actual (non-preflight) request — validate origin and set CORS headers.\n * For disallowed origins, no CORS headers are set (browser will block the response).\n */\n private _handleActualRequest(context: InternalContextImpl): undefined {\n const normalizedOrigin = context.request.headers.origin?.toLowerCase() ?? '';\n const isOriginAllowed = this._isOriginAllowed(normalizedOrigin, context);\n\n if (isOriginAllowed) {\n const allowedOrigin = this._resolveAllowedOrigin(context);\n this._setCommonCorsHeaders(context, allowedOrigin);\n }\n\n return undefined;\n }\n\n /**\n * Set CORS headers common to both preflight and actual requests\n */\n private _setCommonCorsHeaders(context: InternalContextImpl, allowedOrigin: string): void {\n context._response._setHeadersIfNotSet({\n [httpHeaders.accessControlAllowOrigin]: allowedOrigin,\n [httpHeaders.accessControlAllowCredentials]: this.config.credentials ? 'true' : 'false',\n });\n }\n\n /**\n * Determine the correct origin value for the Access-Control-Allow-Origin header.\n * SECURITY: Never echo back the request origin without prior validation.\n */\n private _resolveAllowedOrigin(context: InternalContextImpl): string {\n if (this.config.origin === '*') {\n return '*';\n }\n\n // For specific origins, echo back the validated request origin\n const requestOrigin = context.request.headers.origin;\n if (requestOrigin) {\n return requestOrigin;\n }\n\n // Fallback: no request origin (shouldn't happen for validated requests)\n if (typeof this.config.origin === 'string') {\n return this.config.origin;\n }\n\n if (Array.isArray(this.config.origin) && this.config.origin.length > 0) {\n const [firstOrigin] = this.config.origin;\n return firstOrigin ?? 'null';\n }\n\n return 'null';\n }\n\n /**\n * Check if a normalized (lowercased) origin is allowed by the CORS configuration\n */\n private _isOriginAllowed(normalizedOrigin: string, context?: InternalContextImpl): boolean {\n if (this.config.origin === '*') return true;\n\n if (typeof this.config.origin === 'function') {\n return Boolean(this.config.origin(normalizedOrigin, context?.request));\n }\n\n if (typeof this.config.origin === 'string') {\n return normalizedOrigin === this.config.origin.toLowerCase();\n }\n\n if (this._normalizedOrigins) {\n return this._normalizedOrigins.has(normalizedOrigin);\n }\n\n if (this.config.origin instanceof RegExp) {\n return this.config.origin.test(normalizedOrigin);\n }\n\n return false;\n }\n}\n",
54
53
  "import type { HandlerCallback } from '@typedefs/public/Context.js';\nimport type { InternalCorsEnabledOptions } from '@typedefs/internal/InternalConfiguration.js';\nimport type { InternalContextImpl } from '@typedefs/internal/InternalContextImpl.js';\nimport { Cors } from '@core/modules/cors/Cors.ts';\n\n/**\n * Creates a CORS beforeRouting hook\n *\n * This hook handles Cross-Origin Resource Sharing (CORS) before routing.\n * It validates origins, handles preflight requests, and sets appropriate headers.\n *\n * @param config - CORS configuration (must be enabled)\n * @returns HandlerCallback that can be registered with app.beforeRouting()\n *\n * @example\n * ```typescript\n * import { YinzerFlow } from 'yinzerflow';\n * import { corsHook } from '@core/modules/cors/corsHooks.ts';\n *\n * const app = new YinzerFlow();\n *\n * // Register CORS as a beforeRouting hook\n * app.beforeRouting([\n * corsHook({\n * enabled: true,\n * origin: ['https://example.com', 'https://app.example.com'],\n * methods: ['GET', 'POST', 'PUT', 'DELETE'],\n * credentials: true,\n * })\n * ]);\n * ```\n */\nexport const corsHook = (config: InternalCorsEnabledOptions): HandlerCallback => {\n const cors = new Cors(config);\n\n return (context) => cors.handle(context as InternalContextImpl);\n};\n",
55
- "import { httpStatusCode } from '@constants/http.ts';\nimport type { InternalCorsEnabledOptions, InternalCorsOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * CORS Configuration defaults and validation\n *\n * Provides sensible defaults and validates CORS configuration for security.\n */\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class\nexport class CorsConfig {\n /**\n * Get default CORS configuration (disabled by default)\n */\n static getDefaults(): InternalCorsOptions {\n return {\n enabled: false,\n };\n }\n\n /**\n * Get default enabled CORS configuration\n * Used when user enables CORS but doesn't provide full config\n */\n static getEnabledDefaults(): InternalCorsEnabledOptions {\n return {\n enabled: true,\n origin: '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowedHeaders: '*',\n exposedHeaders: [],\n credentials: false,\n maxAge: 86400, // 24 hours\n preflightContinue: false,\n optionsSuccessStatus: httpStatusCode.noContent,\n };\n }\n\n /**\n * Merge user configuration with defaults\n */\n static merge(userConfig?: Partial<InternalCorsEnabledOptions>): InternalCorsOptions {\n if (!userConfig || !userConfig.enabled) {\n return CorsConfig.getDefaults();\n }\n\n const defaults = CorsConfig.getEnabledDefaults();\n\n return {\n enabled: true,\n origin: userConfig.origin ?? defaults.origin,\n methods: userConfig.methods ?? defaults.methods,\n allowedHeaders: userConfig.allowedHeaders ?? defaults.allowedHeaders,\n exposedHeaders: userConfig.exposedHeaders ?? defaults.exposedHeaders,\n credentials: userConfig.credentials ?? defaults.credentials,\n maxAge: userConfig.maxAge ?? defaults.maxAge,\n preflightContinue: userConfig.preflightContinue ?? defaults.preflightContinue,\n optionsSuccessStatus: userConfig.optionsSuccessStatus ?? defaults.optionsSuccessStatus,\n };\n }\n\n /**\n * Validate CORS configuration for security issues\n * Throws if configuration is invalid or insecure\n */\n static validate(config: InternalCorsOptions): void {\n if (!config.enabled) return;\n\n // SECURITY: Validate wildcard + credentials combination\n if (config.origin === '*' && config.credentials) {\n throw new Error(\n 'CORS Security Error: origin: \"*\" with credentials: true is forbidden by CORS spec and creates security vulnerabilities. Use specific origins instead.',\n );\n }\n\n // Validate origin is provided when enabled\n if (!config.origin) {\n throw new Error('CORS Configuration Error: origin is required when CORS is enabled.');\n }\n\n // Validate methods array\n if (!Array.isArray(config.methods) || config.methods.length === 0) {\n throw new Error('CORS Configuration Error: methods must be a non-empty array.');\n }\n\n // Validate exposedHeaders array\n if (!Array.isArray(config.exposedHeaders)) {\n throw new Error('CORS Configuration Error: exposedHeaders must be an array.');\n }\n\n // Validate maxAge is positive\n if (typeof config.maxAge !== 'number' || config.maxAge < 0) {\n throw new Error('CORS Configuration Error: maxAge must be a non-negative number.');\n }\n }\n}\n"
54
+ "import { httpStatusCode } from '@constants/http.ts';\nimport type { InternalCorsEnabledOptions, InternalCorsOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * CORS Configuration defaults and validation\n *\n * Provides sensible defaults and validates CORS configuration for security.\n */\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class\nexport class CorsConfig {\n /**\n * Get default CORS configuration (disabled by default)\n */\n static getDefaults(): InternalCorsOptions {\n return {\n enabled: false,\n };\n }\n\n /**\n * Get default enabled CORS configuration\n * Used when user enables CORS but doesn't provide full config\n */\n static getEnabledDefaults(): InternalCorsEnabledOptions {\n return {\n enabled: true,\n origin: '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowedHeaders: '*',\n exposedHeaders: [],\n credentials: false,\n maxAge: 86400, // 24 hours\n preflightContinue: false,\n optionsSuccessStatus: httpStatusCode.noContent,\n };\n }\n\n /**\n * Merge user configuration with defaults\n */\n static merge(userConfig?: Partial<InternalCorsEnabledOptions>): InternalCorsOptions {\n if (!userConfig || !userConfig.enabled) {\n return CorsConfig.getDefaults();\n }\n\n const defaults = CorsConfig.getEnabledDefaults();\n\n return {\n enabled: true,\n origin: userConfig.origin ?? defaults.origin,\n methods: userConfig.methods ?? defaults.methods,\n allowedHeaders: userConfig.allowedHeaders ?? defaults.allowedHeaders,\n exposedHeaders: userConfig.exposedHeaders ?? defaults.exposedHeaders,\n credentials: userConfig.credentials ?? defaults.credentials,\n maxAge: userConfig.maxAge ?? defaults.maxAge,\n preflightContinue: userConfig.preflightContinue ?? defaults.preflightContinue,\n optionsSuccessStatus: userConfig.optionsSuccessStatus ?? defaults.optionsSuccessStatus,\n };\n }\n\n /**\n * Validate CORS configuration for security issues\n * Throws if configuration is invalid or insecure\n */\n static validate(config: InternalCorsOptions): void {\n if (!config.enabled) return;\n\n // SECURITY: Validate wildcard + credentials combination\n if (config.origin === '*' && config.credentials) {\n throw new Error(\n 'CORS Security Error: origin: \"*\" with credentials: true is forbidden by CORS spec and creates security vulnerabilities. Use specific origins instead.',\n );\n }\n\n // Validate origin is provided when enabled\n if (!config.origin) {\n throw new Error('CORS Configuration Error: origin is required when CORS is enabled.');\n }\n\n // Validate methods array\n if (!Array.isArray(config.methods) || config.methods.length === 0) {\n throw new Error('CORS Configuration Error: methods must be a non-empty array.');\n }\n\n // Validate exposedHeaders array\n if (!Array.isArray(config.exposedHeaders)) {\n throw new Error('CORS Configuration Error: exposedHeaders must be an array.');\n }\n\n // Validate maxAge is positive\n if (typeof config.maxAge !== 'number' || config.maxAge < 0) {\n throw new Error('CORS Configuration Error: maxAge must be a non-negative number.');\n }\n }\n}\n",
55
+ "/**\n * Strip control characters, newlines, and Unicode BiDi overrides from attacker-controlled\n * strings before log interpolation. Prevents log injection (fake log entries via \\n),\n * ANSI escape injection, and terminal display manipulation via BiDi overrides.\n */\nconst CONTROL_CHARS_RE = /[\\r\\n\\x00-\\x1f\\x7f\\u200f\\u202a-\\u202e\\u2066-\\u2069]/g;\n\nexport const _sanitizeLogField = (value: string, maxLength = 256): string => value.replace(CONTROL_CHARS_RE, '').slice(0, maxLength);\n",
56
+ "import { createLogger } from '@core/utils/log.ts';\nimport { _convertTimeToMs } from '@core/utils/time.ts';\nimport { _convertBytesToBytes } from '@core/utils/bytes.ts';\nimport { _sanitizeLogField } from '@core/utils/sanitize.ts';\nimport type { InternalDiagnosticsOptions } from '@typedefs/internal/InternalConfiguration.js';\n\n/**\n * Pittsburgh-themed performance phrases for diagnostic output.\n * Moved from the old networkLog.ts `logPerformanceDetails` function.\n */\nconst DIAGNOSTIC_PHRASES = {\n slowRequest: [\n \"that's draggin' n'at\",\n 'slower than a bus on Forbes Ave',\n 'yinz might wanna optimize that',\n 'what a jagoff response time!',\n \"slowin' down a bit there\",\n ],\n largePayload: [\"that's a yuge payload n'at\", \"bigger than a Primanti's sandwich\", \"that's a lot of data, yinz\", 'hefty response there'],\n memory: [\"heap's gettin' full n'at\", 'memory usage update', \"keepin' an eye on the heap\"],\n eventLoop: [\"event loop's laggin' n'at\", \"the loop's stuck in traffic on 376\", 'yinz got a blocking operation'],\n rateLimit: [\"somebody's hammerin' the server n'at\", 'slow down there, jagoff', 'rate limit hit'],\n} as const;\n\nconst _getRandomDiagPhrase = (type: keyof typeof DIAGNOSTIC_PHRASES): string => {\n const phrases = DIAGNOSTIC_PHRASES[type];\n return phrases[Math.floor(Math.random() * phrases.length)] ?? '';\n};\n\n/**\n * Resolved diagnostics config — all thresholds converted to numbers.\n * `false` means the diagnostic is disabled.\n */\ninterface ResolvedConfig {\n slowRequestsMs: number | false;\n largeResponsesBytes: number | false;\n largeRequestsBytes: number | false;\n memoryIntervalMs: number | false;\n eventLoopThresholdMs: number | false;\n rateLimits: boolean;\n}\n\n/**\n * Framework health monitoring — independent of app log level.\n *\n * Diagnostics fire only when thresholds are exceeded. All thresholds default\n * to `false` (disabled). Even with `logging.level: 'off'`, diagnostics still\n * fire because they use their own logger instance always at 'info' level.\n *\n * Three categories:\n * - **Per-request**: Slow requests, large request/response bodies\n * - **Interval**: Memory usage, event loop lag\n * - **Event**: Rate limit hits\n */\nexport class DiagnosticsMonitor {\n _config: ResolvedConfig;\n _personality: boolean;\n _memoryTimer?: ReturnType<typeof setInterval> | undefined;\n _eventLoopTimer?: ReturnType<typeof setTimeout> | undefined;\n _log: ReturnType<typeof createLogger>;\n private _destroyed = false;\n\n constructor(config: InternalDiagnosticsOptions, personality: boolean) {\n this._config = _resolveConfig(config);\n this._personality = personality;\n // Own logger — always at 'info', bypasses the app log level\n this._log = createLogger({ level: 'info', prefix: 'DIAGNOSTIC', personality: false });\n }\n\n /** Append a Pittsburgh phrase only when personality is enabled. */\n _phrase(type: keyof typeof DIAGNOSTIC_PHRASES): string {\n return this._personality ? ` — ${_getRandomDiagPhrase(type)}` : '';\n }\n\n /**\n * Check a completed request against diagnostic thresholds.\n * Called after the response has been written to the socket.\n *\n * @param method - Pre-sanitized HTTP method (caller handles sanitization to avoid double-sanitize)\n * @param path - Pre-sanitized request path\n */\n checkRequest({ duration, reqBytes, resBytes, method, path }: { duration: number; reqBytes: number; resBytes: number; method: string; path: string }): void {\n if (this._config.slowRequestsMs !== false && duration > this._config.slowRequestsMs) {\n this._log.warn(`🐌 Slow request: ${method} ${path} took ${duration}ms (threshold: ${this._config.slowRequestsMs}ms)${this._phrase('slowRequest')}`);\n }\n\n if (this._config.largeResponsesBytes !== false && resBytes > this._config.largeResponsesBytes) {\n this._log.warn(\n `📦 Large response: ${method} ${path} ${resBytes} bytes (threshold: ${this._config.largeResponsesBytes} bytes)${this._phrase('largePayload')}`,\n );\n }\n\n if (this._config.largeRequestsBytes !== false && reqBytes > this._config.largeRequestsBytes) {\n this._log.warn(\n `📦 Large request: ${method} ${path} ${reqBytes} bytes (threshold: ${this._config.largeRequestsBytes} bytes)${this._phrase('largePayload')}`,\n );\n }\n }\n\n /**\n * Called when the rate limiter fires for an IP.\n */\n onRateLimitHit(ip: string, path: string): void {\n if (!this._config.rateLimits) return;\n this._log.warn(`🚫 Rate limit hit: ${ip} on ${_sanitizeLogField(path)}${this._phrase('rateLimit')}`);\n }\n\n /**\n * Start interval-based monitors (memory, event loop).\n * Call once after server is constructed.\n */\n start(): void {\n this._startMemoryMonitor();\n this._startEventLoopMonitor();\n }\n\n /**\n * Clean up all intervals and timers. Call on server close.\n */\n destroy(): void {\n this._destroyed = true;\n if (this._memoryTimer) {\n clearInterval(this._memoryTimer);\n this._memoryTimer = undefined;\n }\n if (this._eventLoopTimer) {\n clearTimeout(this._eventLoopTimer);\n this._eventLoopTimer = undefined;\n }\n }\n\n /**\n * Check if any diagnostics are enabled.\n */\n hasAnyEnabled(): boolean {\n return (\n this._config.slowRequestsMs !== false ||\n this._config.largeResponsesBytes !== false ||\n this._config.largeRequestsBytes !== false ||\n this._config.memoryIntervalMs !== false ||\n this._config.eventLoopThresholdMs !== false ||\n this._config.rateLimits\n );\n }\n\n /**\n * Periodic memory usage logging.\n */\n private _startMemoryMonitor(): void {\n if (this._config.memoryIntervalMs === false) return;\n\n const intervalMs = this._config.memoryIntervalMs;\n this._memoryTimer = setInterval(() => {\n const mem = process.memoryUsage();\n const toMB = (bytes: number): string => (bytes / 1024 / 1024).toFixed(1);\n this._log.info(\n `💾 Heap: ${toMB(mem.heapUsed)}MB / ${toMB(mem.heapTotal)}MB | RSS: ${toMB(mem.rss)}MB | External: ${toMB(mem.external)}MB${this._phrase('memory')}`,\n );\n }, intervalMs);\n\n // Don't prevent process exit\n this._memoryTimer.unref();\n }\n\n /**\n * Event loop lag detection via setTimeout drift.\n * If the callback fires significantly later than scheduled, the event loop is lagging.\n */\n private _startEventLoopMonitor(): void {\n if (this._config.eventLoopThresholdMs === false) return;\n\n const threshold = this._config.eventLoopThresholdMs;\n // Check every 1 second\n const checkIntervalMs = 1000;\n\n const check = (): void => {\n if (this._destroyed) return;\n const start = Date.now();\n this._eventLoopTimer = setTimeout(() => {\n if (this._destroyed) return;\n const lag = Date.now() - start - checkIntervalMs;\n if (lag > threshold) {\n this._log.warn(`⏱️ Event loop lag: ${lag}ms (threshold: ${threshold}ms)${this._phrase('eventLoop')}`);\n }\n check();\n }, checkIntervalMs);\n this._eventLoopTimer.unref();\n };\n\n check();\n }\n}\n\n/**\n * Resolve TimeString/ByteString config values to raw numbers.\n * `false` values pass through as disabled.\n */\nconst _resolveConfig = (config: InternalDiagnosticsOptions): ResolvedConfig => ({\n slowRequestsMs: config.slowRequests === false ? false : _convertTimeToMs(config.slowRequests),\n largeResponsesBytes: config.largeResponses === false ? false : _convertBytesToBytes(config.largeResponses),\n largeRequestsBytes: config.largeRequests === false ? false : _convertBytesToBytes(config.largeRequests),\n memoryIntervalMs: config.memory === false ? false : _convertTimeToMs(config.memory),\n eventLoopThresholdMs: config.eventLoop === false ? false : _convertTimeToMs(config.eventLoop),\n rateLimits: config.rateLimits,\n});\n"
56
57
  ],
57
- "mappings": "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",
58
- "debugId": "F35721FED347557264756E2164756E21",
58
+ "mappings": "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",
59
+ "debugId": "5A273D0FD83BCF6E64756E2164756E21",
59
60
  "names": []
60
61
  }