yinzerflow 0.7.0 → 0.8.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/CHANGELOG.md +22 -0
- package/docs/core/websockets.md +343 -0
- package/index.d.ts +269 -0
- package/index.js +33 -21
- package/index.js.map +16 -8
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,38 +1,50 @@
|
|
|
1
|
-
import{createServer as
|
|
2
|
-
`)?
|
|
1
|
+
import{createServer as D6}from"net";class a{setup;constructor(X){this.setup=X}async handle(X){try{if(await this._handleBeforeRoutingHooks(X))return;let Z=await this._matchRoute(X);if(!Z)return;Object.assign(X.request.params,Z.params);let{handler:$,options:Q}=Z,{beforeHooks:J=[],afterHooks:Y=[]}=Q;if(await this._handleBeforeAllHooks(X))return;if(await this._handleBeforeHooks(X,J))return;let j=await $(X);X._response._setBody(j);for(let K of Y)await K(X);let W=this.setup._hooks._afterAll;for(let K of W){if(!this._shouldRunHook(K.options,X.request.path))continue;await K.handler(X)}if(X.request.method==="HEAD")X._response._setBody(null);X._response._parseResponseIntoString();return}catch(Z){await this.handleError(X,Z)}}async handleError(X,Z){try{let $=this.setup._hooks._onError,Q=await $(X,Z);X._response._setBody(Q),X._response._parseResponseIntoString()}catch($){this.setup._log.error("Your custom error handler threw an error. Check your onError() handler for bugs: ",$),X.response.setStatusCode(500),X._response._setBody({success:!1,message:"Internal Server Error"}),X._response._parseResponseIntoString()}}async _handleBeforeRoutingHooks(X){let Z=this.setup._hooks._beforeRouting;for(let $ of Z){if(!this._shouldRunHook($.options,X.request.path))continue;let Q=await $.handler(X);if(this._applyHookResponse(Q,X))return!0}return!1}async _matchRoute(X){let Z=this.setup._routeRegistry._findRoute(X.request.method,X.request.path);if(!Z){let $=await this.setup._hooks._onNotFound(X);return X._response._setBody($),X._response._parseResponseIntoString(),null}return Z}async _handleBeforeAllHooks(X){let Z=this.setup._hooks._beforeAll;for(let $ of Z){if(!this._shouldRunHook($.options,X.request.path))continue;let Q=await $.handler(X);if(this._applyHookResponse(Q,X))return!0}return!1}async _handleBeforeHooks(X,Z){for(let $ of Z){let Q=await $(X);if(this._applyHookResponse(Q,X))return!0}return!1}_applyHookResponse(X,Z){if(X===void 0)return!1;return Z._response._setBody(X),Z._response._parseResponseIntoString(),!0}_shouldRunHook(X,Z){if(!X)return!0;let{routesToInclude:$=[],routesToExclude:Q=[]}=X;if(Q.some((J)=>this._matchesPattern(Z,J)))return!1;if($.length===0)return!0;return $.some((J)=>this._matchesPattern(Z,J))}_matchesPattern(X,Z){if(Z===X)return!0;if(Z.endsWith("/*")){let $=Z.slice(0,-1);return X.startsWith($)||X===Z.slice(0,-2)}return!1}}var V={reset:"\x1B[0m",cyan:"\x1B[96m",yellow:"\x1B[93m",red:"\x1B[91m",green:"\x1B[92m",magenta:"\x1B[95m",gray:"\x1B[90m"};var P={off:"off",error:"error",warn:"warn",info:"info",debug:"debug"};var L=Symbol("YinzerFlowLogger"),R={off:0,error:1,warn:2,info:3,debug:4},F1={positive:["n'at!","yinz are good!","that's the way!","right on!","lookin' good!","way to go!","keep it up!"],neutral:["n'at","yinz know","just sayin'","that's how it is","what can ya do","it happens"],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"]},T0=(X)=>{let Z=F1[X];return Z[Math.floor(Math.random()*Z.length)]??""},C=(X,Z=2)=>String(X).padStart(Z,"0"),w0=()=>{let X=new Date;return`${X.getFullYear()}-${C(X.getMonth()+1)}-${C(X.getDate())} ${C(X.getHours())}:${C(X.getMinutes())}:${C(X.getSeconds())}.${C(X.getMilliseconds(),3)}`},E1=(X)=>{if(X==="error")return"negative";if(X==="warn")return"neutral";return"positive"},z1=(X,...Z)=>{let{level:$,prefix:Q,personality:J,output:Y}=X,j=w0(),W=V.reset;if(Q==="ACCESS"||Q==="DIAGNOSTIC")W=V.gray;let K=J?` - ${T0(E1($))}`:"";if($==="error"){let G=`${V.red}[${Q}] ❌ [${j}] [ERROR]${V.reset}`;Y.error(`${G}`,`${W}`,...Z,`${V.reset}${K}`);return}if($==="warn"){let G=`${V.yellow}[${Q}] ⚠️ [${j}] [WARN]${V.reset}`;Y.warn(`${G}`,`${W}`,...Z,`${V.reset}${K}`);return}if($==="debug"){let G=`${V.gray}[${Q}] \uD83D\uDD0D [${j}] [DEBUG]${V.reset}`;(Y.debug??Y.info)(`${G}`,`${V.gray}`,...Z,`${V.reset}`);return}if($==="off")return;let U=`${V.cyan}[${Q}] ✅ [${j}] [INFO]${V.reset}`;Y.info(`${U}`,`${W}`,...Z,`${V.reset}${K}`)},O0={info:(...X)=>console.info(...X),warn:(...X)=>console.warn(...X),error:(...X)=>console.error(...X),debug:(...X)=>console.debug(...X)},A1=(X,Z,...$)=>{let{prefix:Q,personality:J,output:Y}=X,j=w0(),W=J?` - ${T0("positive")}`:"",K=`${V.magenta}[${Q}] \uD83D\uDCCA [${j}] [TABLE]${V.reset}`;if(Y.info(`${K}${W}`),console.table(Z),$.length>0)Y.info(`${V.gray}Additional context:${V.reset}`,...$)},_=(X)=>{let Z={level:X?.level??X?.logLevel??P.info,prefix:X?.prefix??"YINZER",personality:X?.personality??!0,logger:X?.logger??null},$=R[Z.level]??R.info,Q=(U,G)=>{if(Z.logger){(U==="debug"?Z.logger.debug??Z.logger.info:Z.logger[U]).call(Z.logger,...G);return}z1({level:U,prefix:Z.prefix,personality:Z.personality,output:O0},...G)};return{info:(...U)=>{if($<R.info)return;Q("info",U)},warn:(...U)=>{if($<R.warn)return;Q("warn",U)},error:(...U)=>{if($<R.error)return;Q("error",U)},debug:(...U)=>{if($<R.debug)return;Q("debug",U)},table:(U,...G)=>{if($<R.info)return;if(Z.logger){Z.logger.info("TABLE:",U,...G);return}A1({prefix:Z.prefix,personality:Z.personality,output:O0},U,...G)},levels:R,[L]:Z}},M=_();var I1={b:1,kb:1024,mb:1048576,gb:1073741824},k=(X)=>{if(typeof X==="number"){if(!Number.isFinite(X)||X<=0)throw Error(`Invalid byte value: "${X}". Must be a positive number`);return X}if(typeof X!=="string")throw Error("Invalid byte format. Expected format: 512b, 256kb, 1mb, 2gb");let Z=/^(?<value>\d+(?:\.\d+)?)(?<unit>b|kb|mb|gb)$/.exec(X);if(!Z?.groups)throw Error(`Invalid byte format: "${X}". Expected format: 512b, 256kb, 1mb, 2gb`);let $=parseFloat(Z.groups.value??"0"),Q=Z.groups.unit??"",J=I1[Q];if(!J)throw Error(`Invalid byte unit: "${Q}". Expected: b, kb, mb, gb`);if($<=0)throw Error(`Invalid byte value: "${$}". Must be a positive number`);return Math.floor($*J)},I=(X)=>{if(X>=1073741824)return`${Math.round(X/1024/1024/1024)}GB`;if(X>=1048576)return`${Math.round(X/1024/1024)}MB`;if(X>=1024)return`${Math.round(X/1024)}KB`;return`${X}B`};var R0=["__proto__","constructor","prototype"],P0=(X,Z,$)=>{let Q=$??M;if(!X||!X.trim()||X.trim()==="\x00")return;let J=Buffer.byteLength(X,"utf8");if(J>Z.maxSize)throw Q.warn("[SECURITY] JSON request body too large",{size:I(J),limit:Z.maxSize}),Error(`Request body too large: ${J} bytes exceeds limit of ${Z.maxSize} bytes`);let Y=null;try{Y=JSON.parse(X)}catch(j){let W=j instanceof Error?j.message:String(j);throw Error(`Invalid JSON syntax: ${W}`)}try{n(Y,{config:Z,logger:Q},1)}catch(j){let W=j instanceof Error?j.message:String(j);throw Error(`JSON security validation failed: ${W}`)}return Y},H1=(X,Z)=>{if(typeof X==="string"&&X.length>Z.maxStringLength)throw Error(`String too long: ${X.length} characters exceeds limit of ${Z.maxStringLength}`)},O1=(X,Z,$)=>{if(X.length>Z.config.maxArrayLength)throw Error(`Array too large: ${X.length} elements exceeds limit of ${Z.config.maxArrayLength}`);for(let Q of X)n(Q,Z,$+1)},T1=(X,Z)=>{if(X.length>Z.config.maxKeys)throw Error(`Object has too many keys: ${X.length} exceeds limit of ${Z.config.maxKeys}`);if(!Z.config.allowPrototypeProperties){for(let $ of X)if(R0.includes($))throw Z.logger.warn("[SECURITY] Prototype pollution attempt detected",{property:$,dangerousProperties:R0}),Error(`Prototype pollution attempt detected: property '${$}' is not allowed`)}},w1=(X,Z,$)=>{let Q=Object.keys(X);for(let J of Q){if(J.length>Z.config.maxStringLength)throw Error(`Object key too long: '${J.substring(0,50)}...' exceeds limit of ${Z.config.maxStringLength}`);let Y=X[J];if(typeof Y==="string"&&Y.length>Z.config.maxStringLength)throw Error(`String value too long: property '${J}' has ${Y.length} characters, exceeds limit of ${Z.config.maxStringLength}`);n(Y,Z,$+1)}},n=(X,Z,$)=>{if($>Z.config.maxDepth)throw Z.logger.warn("[SECURITY] JSON nesting too deep - potential stack overflow attack",{currentDepth:$,maxDepth:Z.config.maxDepth}),Error(`JSON nesting too deep: current depth ${$} exceeds maximum depth of ${Z.config.maxDepth}`);if(X===null||typeof X!=="object"){H1(X,Z.config);return}if(Array.isArray(X)){O1(X,Z,$);return}let Q=Object.keys(X);T1(Q,Z),w1(X,Z,$)};var R1=(X)=>{let Z=X.startsWith(`\r
|
|
2
|
+
`)?X.slice(2):X,$=Z.indexOf(`\r
|
|
3
3
|
\r
|
|
4
|
-
`);if(
|
|
5
|
-
`)?
|
|
6
|
-
`)?
|
|
7
|
-
`),[
|
|
4
|
+
`);if($===-1)return["",""];let Q=Z.slice(0,$),J=Z.slice($+4);return[Q,J]},P1=(X)=>{let Z={name:""},$=/name=(?:"(?<temp2>[^"]*)"|(?<temp1>[^;,\s]+))/i.exec(X),Q=/filename=(?:"(?<temp2>[^"]*)"|(?<temp1>[^;,\s]+))/i.exec(X);if($)Z.name=$[1]??$[2]??"";if(Q){let J=Q[1]??Q[2];if(J)Z.filename=J}return Z},L1=(X)=>{let $=X.split(/\r?\n/).find((Q)=>Q.toLowerCase().startsWith("content-type:"));if(!$)return"application/octet-stream";return $.slice($.indexOf(":")+1).trim().split(";")[0]?.trim()??"application/octet-stream"},_1=(X)=>{return["image/","audio/","video/","application/octet-stream","application/pdf","application/zip","application/x-"].some(($)=>X.toLowerCase().startsWith($))},S1=(X)=>Buffer.isBuffer(X)?X.length:Buffer.byteLength(X,"utf8"),v1=(X,Z,$)=>{if(!Z)return;let Q=$??M;if(X.size>Z.maxFileSize)throw Q.warn("[SECURITY] File upload too large",{filename:X.filename,size:I(X.size),limit:Z.maxFileSize}),Error(`File too large: ${X.filename} is ${X.size} bytes, exceeds limit of ${Z.maxFileSize} bytes`);if(X.filename&&X.filename.length>Z.maxFilenameLength)throw Error(`Filename too long: ${X.filename.length} characters exceeds limit of ${Z.maxFilenameLength}`);if(X.filename){let J=X.filename.toLowerCase().substring(X.filename.lastIndexOf("."));if(Z.blockedExtensions.includes(J))throw Q.warn("[SECURITY] Blocked file type upload attempt",{filename:X.filename,extension:J,blockedExtensions:Z.blockedExtensions}),Error(`File type not allowed: ${J} files are blocked for security reasons`);if(Z.allowedExtensions.length>0&&!Z.allowedExtensions.includes(J))throw Error(`File type not allowed: ${J} is not in the allowed extensions list`)}},x1=({contentDisposition:X,contentSection:Z,headersSection:$,config:Q,logger:J})=>{let Y=L1($),j=Z.endsWith(`\r
|
|
5
|
+
`)?Z.slice(0,-2):Z,W=_1(Y)?Buffer.from(j,"binary"):j,K={filename:X.filename??"",contentType:Y,size:S1(W),content:W};return v1(K,Q,J),K},L0=(X,Z,$)=>{let Q=$?.config,J=$?.logger??M,Y={fields:{},files:[]},j=X.split(`--${Z}`).slice(1),W=0;for(let K of j){if(!K||K.trim()===""||K.trim()==="--")continue;let[U,G]=R1(K);if(!U)continue;let E=U.split(/\r?\n/).find((w)=>w.toLowerCase().startsWith("content-disposition:"));if(!E)continue;let T=P1(E);if(!T.name)continue;if(T.filename!==void 0){if(Q&&Y.files.length>=Q.maxFiles)throw J.warn("[SECURITY] Too many files in upload request",{fileCount:Y.files.length,maxFiles:Q.maxFiles}),Error(`Too many files: maximum of ${Q.maxFiles} files allowed per request`);let w=x1({contentDisposition:T,contentSection:G,headersSection:U,config:Q,logger:J});if(W+=w.size,Q&&W>Q.maxTotalSize)throw J.warn("[SECURITY] Total upload size too large",{totalSize:I(W),limit:Q.maxTotalSize}),Error(`Total file size too large: ${W} bytes exceeds limit of ${Q.maxTotalSize} bytes`);Y.files.push(w)}if(T.filename===void 0){let w=G.endsWith(`\r
|
|
6
|
+
`)?G.slice(0,-2):G;Y.fields[T.name]=w}}return Y};var m={ok:"OK",created:"Created",accepted:"Accepted",noContent:"No Content",movedPermanently:"Moved Permanently",found:"Found",notModified:"Not Modified",badRequest:"Bad Request",unauthorized:"Unauthorized",forbidden:"Forbidden",notFound:"Not Found",methodNotAllowed:"Method Not Allowed",conflict:"Conflict",unsupportedMediaType:"Unsupported Media Type",tooManyRequests:"Too Many Requests",internalServerError:"Internal Server Error"},z={ok:200,created:201,accepted:202,noContent:204,movedPermanently:301,found:302,notModified:304,badRequest:400,unauthorized:401,forbidden:403,notFound:404,methodNotAllowed:405,conflict:409,unsupportedMediaType:415,tooManyRequests:429,internalServerError:500},q={delete:"DELETE",get:"GET",head:"HEAD",post:"POST",put:"PUT",patch:"PATCH",options:"OPTIONS"},A={json:"application/json",html:"text/html",form:"application/x-www-form-urlencoded",multipart:"multipart/form-data",xml:"application/xml",text:"text/plain",csv:"text/csv",yamlApplication:"application/yaml",yamlText:"text/yaml",urlEncodedJson:"application/x-www-form-urlencoded+json"},S={authorization:"Authorization",proxyAuthorization:"Proxy-Authorization",wwwAuthenticate:"WWW-Authenticate",cacheControl:"Cache-Control",etag:"ETag",expires:"Expires",lastModified:"Last-Modified",ifMatch:"If-Match",ifNoneMatch:"If-None-Match",ifModifiedSince:"If-Modified-Since",ifUnmodifiedSince:"If-Unmodified-Since",ifRange:"If-Range",age:"Age",vary:"Vary",contentType:"Content-Type",contentLength:"Content-Length",contentEncoding:"Content-Encoding",contentLanguage:"Content-Language",contentDisposition:"Content-Disposition",contentLocation:"Content-Location",contentRange:"Content-Range",accessControlAllowCredentials:"Access-Control-Allow-Credentials",accessControlAllowHeaders:"Access-Control-Allow-Headers",accessControlAllowMethods:"Access-Control-Allow-Methods",accessControlAllowOrigin:"Access-Control-Allow-Origin",accessControlExposeHeaders:"Access-Control-Expose-Headers",accessControlMaxAge:"Access-Control-Max-Age",accessControlRequestHeaders:"Access-Control-Request-Headers",accessControlRequestMethod:"Access-Control-Request-Method",accept:"Accept",acceptEncoding:"Accept-Encoding",acceptLanguage:"Accept-Language",acceptRanges:"Accept-Ranges",host:"Host",userAgent:"User-Agent",referer:"Referer",origin:"Origin",from:"From",expect:"Expect",location:"Location",server:"Server",date:"Date",allow:"Allow",retryAfter:"Retry-After",range:"Range",contentSecurityPolicy:"Content-Security-Policy",contentSecurityPolicyReportOnly:"Content-Security-Policy-Report-Only",strictTransportSecurity:"Strict-Transport-Security",xContentTypeOptions:"X-Content-Type-Options",xFrameOptions:"X-Frame-Options",xXSSProtection:"X-XSS-Protection",referrerPolicy:"Referrer-Policy",permissionsPolicy:"Permissions-Policy",crossOriginEmbedderPolicy:"Cross-Origin-Embedder-Policy",crossOriginOpenerPolicy:"Cross-Origin-Opener-Policy",crossOriginResourcePolicy:"Cross-Origin-Resource-Policy",cookie:"Cookie",setCookie:"Set-Cookie",connection:"Connection",keepAlive:"Keep-Alive",upgrade:"Upgrade",upgradeInsecureRequests:"Upgrade-Insecure-Requests",transferEncoding:"Transfer-Encoding",te:"TE",trailer:"Trailer",forwarded:"Forwarded",xForwardedFor:"X-Forwarded-For",via:"Via",maxForwards:"Max-Forwards",altSvc:"Alt-Svc",altUsed:"Alt-Used",timingAllowOrigin:"Timing-Allow-Origin",serverTiming:"Server-Timing",refresh:"Refresh",link:"Link",xPoweredBy:"X-Powered-By",xPermittedCrossDomainPolicies:"X-Permitted-Cross-Domain-Policies",reportTo:"Report-To",serviceWorkerAllowed:"Service-Worker-Allowed",sourceMap:"SourceMap",priority:"Priority",secGPC:"Sec-GPC",clearSiteData:"Clear-Site-Data",noVarySearch:"No-Vary-Search"},_0={base64:"base64",binary:"binary",utf8:"utf8"};var C1=(X,Z)=>{if(X.length>Z.maxFields)throw Error(`Too many form fields: ${X.length} exceeds limit of ${Z.maxFields}`)},k1=(X,Z,$)=>{if(X.length>$.maxFieldNameLength)throw Error(`Form field name too long: ${X.length} characters exceeds limit of ${$.maxFieldNameLength}`);if(Z&&Z.length>$.maxFieldLength)throw Error(`Form field value too long: field '${X}' has ${Z.length} characters, exceeds limit of ${$.maxFieldLength}`)},b1=(X,Z,$)=>{if(X.length>$.maxFieldNameLength)throw Error(`Decoded form field name too long: ${X.length} characters exceeds limit of ${$.maxFieldNameLength}`);if(Z.length>$.maxFieldLength)throw Error(`Decoded form field value too long: field '${X}' has ${Z.length} characters, exceeds limit of ${$.maxFieldLength}`)},y1=(X,Z,$)=>{let[Q,J]=X.split("=");if(!Q)return;if($)k1(Q,J,$);try{let Y=decodeURIComponent(Q),j=J?decodeURIComponent(J):"";if($)b1(Y,j,$);Z[Y]=j}catch(Y){if(Y instanceof Error&&Y.message.includes("exceeds limit"))throw Y;Z[Q]=J??""}},S0=(X,Z)=>{let $={},Q=X.split("&");if(Z)C1(Q,Z);for(let J of Q)y1(J,$,Z);return $};var g={JPEG:[255,216,255],PNG:[137,80,78,71],GIF87A:[71,73,70,56,55,97],GIF89A:[71,73,70,56,57,97],BMP:[66,77],TIFF_LE:[73,73,42,0],TIFF_BE:[77,77,0,42],WEBP:[82,73,70,70],ICO:[0,0,1,0],MP3_ID3:[73,68,51],MP3_FRAME:[255,251],WAV:[82,73,70,70],FLAC:[102,76,97,67],OGG:[79,103,103,83],MP4_FTYP:[0,0,0,24,102,116,121,112],MP4_FTYP_ALT:[0,0,0,28,102,116,121,112],AVI:[82,73,70,70],WEBM:[26,69,223,163],PDF:[37,80,68,70],ZIP:[80,75,3,4],ZIP_EMPTY:[80,75,5,6],ZIP_SPANNED:[80,75,7,8],RAR:[82,97,114,33,26,7,0],RAR5:[82,97,114,33,26,7,1,0],SEVENZ:[55,122,188,175,39,28],GZIP:[31,139],EXE:[77,90],ELF:[127,69,76,70],OFFICE_OLD:[208,207,17,224,161,177,26,225]},l=(X,Z)=>{if(X.length<Z.length)return!1;return Z.every(($,Q)=>X[Q]===$)},m1=(X)=>{if(l(X,g.WEBP)&&X.length>=12)return X.subarray(8,12).toString("ascii")==="WEBP";if(l(X,g.WAV)&&X.length>=12)return X.subarray(8,12).toString("ascii")==="WAVE";if(l(X,g.AVI)&&X.length>=12)return X.subarray(8,12).toString("ascii")==="AVI ";return!1},d=(X,Z)=>{if(!X)return h1(Z);let $=X.toLowerCase();if($.startsWith("image/")||$.startsWith("video/")||$.startsWith("audio/")||$==="application/pdf"||$==="application/octet-stream"||$.startsWith("application/zip")||$.startsWith("application/x-"))return"base64";if($.startsWith("text/")||$.startsWith("application/json")||$.startsWith("application/xml")||$.startsWith("application/javascript"))return"utf8";return"binary"},h1=(X)=>{if(Buffer.isBuffer(X))return u1(X)?"base64":"utf8";if(typeof X==="object"&&X!==null)return"utf8";if(typeof X==="string")return"utf8";return"utf8"},u1=(X)=>{if(X.length===0)return!1;let Z=Object.values(g);for(let J of Z)if(l(X,J))return!0;if(m1(X))return!0;let $=X.filter((J)=>J===0).length,Q=X.filter((J)=>J<32&&J!==9&&J!==10&&J!==13).length;return $/X.length>0.1||Q/X.length>0.3};var p1=(X)=>{if(!(X.startsWith("{")&&X.endsWith("}")||X.startsWith("[")&&X.endsWith("]")))return!1;try{return JSON.parse(X),!0}catch{return!1}},f1=(X)=>X.includes("=")&&X.includes("&"),g1=(X)=>X.includes("boundary="),l1=(X)=>typeof X==="object"&&X!==null&&!Buffer.isBuffer(X)&&!(X instanceof Uint8Array)&&!(X instanceof ArrayBuffer)&&!(X instanceof Date),d1=(X)=>X instanceof Date,c1=(X)=>{if(Buffer.isBuffer(X))return X;return Buffer.from(X)},s1=(X)=>{return d(void 0,X)==="base64"?"application/octet-stream":"text/plain"},o=(X)=>{let Z=X.trim();if(p1(Z))return A.json;if(f1(Z))return A.form;if(g1(Z))return A.multipart;return"text/plain"},v0=(X)=>{if(X===null||X===void 0)return"text/plain";if(d1(X))return"text/plain";if(l1(X))return A.json;if(typeof X==="string")return o(X);if(Buffer.isBuffer(X)||X instanceof Uint8Array||X instanceof ArrayBuffer){let Z=c1(X);return s1(Z)}return"text/plain"};var r1=(X,Z,$)=>{let Q=Buffer.byteLength(X,"utf8");if(Z===A.json){if(Q>$.json.maxSize)throw Error(`JSON body too large: ${Q} bytes exceeds limit of ${$.json.maxSize} bytes`)}else if(Z===A.form){if(Q>$.urlEncoded.maxSize)throw Error(`URL-encoded body too large: ${Q} bytes exceeds limit of ${$.urlEncoded.maxSize} bytes`)}else if(Z===A.multipart){if(Q>$.fileUploads.maxTotalSize)throw Error(`Multipart body too large: ${Q} bytes exceeds limit of ${$.fileUploads.maxTotalSize} bytes`)}},x0=(X,Z={})=>{let{headerContentType:$,boundary:Q,config:J,logger:Y}=Z;if(!X||!X.trim())return;let j=$??o(X);if(J)r1(X,j,J);if(j===A.json){if(!J)throw Error("Body parser configuration is required for JSON parsing");return P0(X,J.json,Y)}if(j===A.multipart){if(!Q)throw Error("Invalid multipart form data: missing boundary");let W={};if(J?.fileUploads)W.config=J.fileUploads;if(Y)W.logger=Y;return L0(X,Q,W)}if(j===A.form)return S0(X,J?.urlEncoded);return X};var t=(X,Z)=>{let $=X.indexOf(Z);if($===-1)return[X,""];let Q=X.slice(0,$),J=X.slice($+Z.length);return[Q,J]};var C0=(X)=>{if(!X||!X.trim())return{method:"GET",path:"/",protocol:"HTTP/1.1",headersRaw:"",rawBody:""};let[Z,$]=t(X,`\r
|
|
7
|
+
`),[Q,J,Y]=Z.split(" ",3),[j,W]=t($,`\r
|
|
8
8
|
\r
|
|
9
|
-
`);if(
|
|
9
|
+
`);if(!Q||!Object.values(q).includes(Q))return{method:"GET",path:J??"/",protocol:Y??"HTTP/1.1",headersRaw:j,rawBody:W};return{method:Q,path:J??"/",protocol:Y??"HTTP/1.1",headersRaw:j,rawBody:W}};var k0=(X)=>{if(!X)return{};if(!X.includes("?"))return{};let[,Z]=X.split("?");if(!Z)return{};let $={},Q=Z.split("&");for(let J of Q){let[Y,j]=J.split("=");if(Y)try{let W=decodeURIComponent(Y),K=j?decodeURIComponent(j):"";$[W]=K}catch{$[Y]=j??""}}return $};var i1=[/^(?<classA>10)\./,/^(?<classB>172)\.(?<classBRange>1[6-9]|2[0-9]|3[0-1])\./,/^(?<classC>192)\.(?<classCRange>168)\./,/^(?<linkLocal>169)\.(?<linkLocalRange>254)\./,/^(?<loopback>127)\./,/^(?<ipv6Loopback>::1)$/,/^(?<ipv6LinkLocal>fe80):/i,/^(?<ipv6UniqueLocalFC>fc00):/i,/^(?<ipv6UniqueLocalFD>fd00):/i],b0=(X)=>{if(!X||typeof X!=="string")return!1;let Z=X.replace(/^\[|\]$/g,"");if(/^(?<octet>(?<highByte>25[0-5]|(?<midByte>2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/.test(Z)){let J=Z.split(".");return J.length===4&&J.every((Y)=>{let j=parseInt(Y,10);return j>=0&&j<=255})}if(Z.includes("::")&&(Z.match(/::/g)??[]).length>1)return!1;return/^(?<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}))$/.test(Z)},y0=(X)=>{if(!X)return!1;let Z=X.replace(/^\[|\]$/g,"");return i1.some(($)=>$.test(Z))},m0=(X,Z)=>{if(!X||!Z.length)return!1;if(Z.includes("*"))return!0;return Z.includes(X)},a1=(X,Z)=>{if(!Z.detectSpoofing||X.length<=1)return!1;if(X.length>Z.maxChainLength)return!0;if(new Set(X).size!==X.length)return!0;let Q=X.filter(b0).length;if(Q>0&&Q<X.length)return!0;return!1},n1=(X,Z)=>{if(X.length<=1)return!0;let $=X[X.length-1];return Boolean($&&m0($,Z.trustedProxies))},o1=(X,Z)=>{if(Z==="x-forwarded-for")return X[0];return X[X.length-1]},t1=(X)=>{let{clientIp:Z,headerName:$,ipChain:Q,config:J}=X,Y=y0(Z),j=$==="x-forwarded-for"?m0(Q[Q.length-1]??"",J.trustedProxies):!0;return{ip:Z,isValid:!0,isPrivate:Y,source:$,trusted:j}},e1=()=>({ip:"",isValid:!1,isPrivate:!1,source:"socket",trusted:!1}),X9=(X,Z)=>{for(let $ of Z.headerPreference){let Q=X[$];if(!Q)continue;let J=Q.split(",").map((W)=>W.trim()).filter(Boolean);if(J.length===0)continue;if(a1(J,Z))continue;if($==="x-forwarded-for"&&!n1(J,Z))continue;let Y=o1(J,$);if(!Y||!b0(Y))continue;if(y0(Y)&&!Z.allowPrivateIps)continue;return t1({clientIp:Y,headerName:$,ipChain:J,config:Z})}return e1()},Z9=(X,Z,$={})=>{let J={...X._configuration.ipSecurity,...$},Y=X9(Z,J);if(Y.isValid)return Y;return{ip:"",isValid:!1,isPrivate:!1,source:"socket",trusted:!1}},h0=(X,Z)=>{return Z9(X,Z).ip};var u0=(X)=>{if(!X)return;return/boundary\s*=\s*(?<temp1>[^;,\s]*)/i.exec(X)?.[1]};var p0=(X)=>{if(!X)return{};$9(X);let Z=Q9(X),$=J9(Z);return Y9($)},$9=(X)=>{if(X.split(/\r\n|\r|\n/).length>100)throw Error("Too many headers: maximum 100 allowed")},Q9=(X)=>{let Z={},Q=X.replace(/\r\n|\r|\n/g,`
|
|
10
10
|
`).split(`
|
|
11
|
-
`);for(let
|
|
12
|
-
`))throw Error(`Header value contains invalid line break characters: ${
|
|
11
|
+
`);for(let J of Q){if(!J.trim())continue;let Y=J.indexOf(":");if(Y===-1)continue;let j=J.slice(0,Y).trim(),W=J.slice(Y+1).trim();if(!j)continue;if(!j9(j))throw Error(`Invalid header name: ${j}`);if(j.length>200)throw Error("Header name too long: maximum 200 characters allowed");if(W.length>8192)throw Error("Header value too long: maximum 8192 characters allowed");Z[j.toLowerCase()]=W}return Z},J9=(X)=>{let Z={};for(let[$,Q]of Object.entries(X))Z[$]=W9(Q);return Z},Y9=(X)=>X,j9=(X)=>{return/^[a-zA-Z0-9!#$%&'*+\-.^_`|~]+$/.test(X)},W9=(X)=>{return X.replace(/[\x00-\x08\x0A-\x1F\x7F]/g,"")};class e{_rawRequest;_setup;method;path;protocol;headers;body;query;params;ipAddress;rawBody;cookies=new Map;signedCookies=new Map;constructor(X,Z,$){this._rawRequest=X,this._setup=Z,this.ipAddress=$??"";let{method:Q,path:J,protocol:Y,headers:j,body:W,query:K,params:U,rawBody:G}=this._parseRequestIntoObject();this.method=Q,this.path=J,this.protocol=Y,this.headers=j,this.body=W,this.query=K??{},this.params=U??{},this.rawBody=G;let B=h0(this._setup,j);if(B)this.ipAddress=B}_parseRequestIntoObject(){let X=this._rawRequest.toString(),{method:Z,path:$,protocol:Q,headersRaw:J,rawBody:Y}=C0(X),j=p0(J),W=j["content-type"],K=W?.split(";")[0]?.trim().toLowerCase(),U=u0(W);return{method:Z,path:$,protocol:Q,headers:j,body:x0(Y,{headerContentType:K,boundary:U,config:this._setup._configuration.bodyParser,logger:this._setup._log}),query:k0($),params:{},rawBody:Y}}}var f0=(X,Z)=>{let $=Z?.encoding??"utf8";if(X===null||X===void 0)return"";if(Buffer.isBuffer(X))return X0(X,$);if(X instanceof Uint8Array)return K9(X,$);if(X instanceof ArrayBuffer)return U9(X,$);if(typeof X==="string")return X;if(typeof X==="object")return G9(X);return String(X)},X0=(X,Z)=>{if(Z==="base64")return X.toString("base64");if(Z==="binary")return X.toString("binary");return X.toString("utf8")},K9=(X,Z)=>{let $=Buffer.from(X);return X0($,Z)},U9=(X,Z)=>{let $=Buffer.from(X);return X0($,Z)},G9=(X)=>{try{return JSON.stringify(X)}catch(Z){return String(X)}};var g0=new Map;for(let[X,Z]of Object.entries(z)){let Q=m[X];g0.set(Z,Q)}var l0=(X)=>{let Z=g0.get(X);if(!Z)throw Error(`Unknown status code: ${X}`);return Z};var M9=(X,Z)=>{if(typeof Z!=="string")throw Error(`Header value must be a string, got ${typeof Z}`);if(Z.includes("\r")||Z.includes(`
|
|
12
|
+
`))throw Error(`Header value contains invalid line break characters: ${X}`);let $=[/[\r\n](?:set-cookie|location|authorization|www-authenticate):/i,/\r\n\r\n|\n\n/,/[\r\n]http\/\d\.\d\s+\d+/i];for(let Q of $)if(Q.test(Z))throw Error(`Header value contains suspicious injection pattern: ${X}`)},V9=(X)=>{for(let[Z,$]of Object.entries(X))M9(Z,$)},Z0=(X)=>{let Z={};for(let[$,Q]of Object.entries(X))if(Q!==void 0)Z[$]=Q;return V9(Z),Z};var d0=()=>{let X=new Date,Z=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],$=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],Q=(J)=>J<10?`0${J}`:String(J);return`${Z[X.getUTCDay()]}, ${Q(X.getUTCDate())} ${$[X.getUTCMonth()]} ${X.getUTCFullYear()} ${Q(X.getUTCHours())}:${Q(X.getUTCMinutes())}:${Q(X.getUTCSeconds())} GMT`},c0=d0(),q9=setInterval(()=>{c0=d0()},1000);q9.unref();class $0{_request;_statusCode=z.ok;_status=m.ok;_headers={};_setCookies=[];_body="";_stringBody="";_encoding=_0.utf8;constructor(X){this._request=X,this._setSecurityHeaders()}_parseResponseIntoString(){let X=`${this._request.protocol} ${this._statusCode} ${this._status}`,Z=d(this._headers["content-type"],this._body),$=f0(this._body,{encoding:Z});this._setHeadersIfNotSet({Date:c0,"Content-Length":String(Buffer.byteLength($,"utf8"))}),this._encoding=Z;let Q=Object.entries(this._headers).map(([W,K])=>`${W}: ${K}`),J=this._setCookies.map((W)=>`Set-Cookie: ${W}`),Y=[...Q,...J],j=Y.length>0?`${Y.join(`\r
|
|
13
13
|
`)}\r
|
|
14
|
-
`:"";this._stringBody=`${
|
|
15
|
-
${
|
|
16
|
-
${Z}`}_setHeadersIfNotSet(Q){let X={};for(let[$,W]of Object.entries(Q))if(W!==void 0&&!($ in this._headers))X[$]=W;let Z=i(X);Object.assign(this._headers,Z)}_setBody(Q){if(this._body=Q,!this._headers["content-type"]){let X=z1(Q);this._setHeadersIfNotSet({"Content-Type":X})}}setStatusCode(Q){this._statusCode=Q,this._status=b1(Q)}addHeaders(Q){let X=i(Q);for(let[Z,$]of Object.entries(X))if(Z==="Set-Cookie"){if($)this._setCookies.push($)}else this._headers[Z]=$}removeHeaders(Q){for(let X of Q)delete this._headers[X]}_setSecurityHeaders(){this._setHeadersIfNotSet({"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-XSS-Protection":"1; mode=block","Referrer-Policy":"strict-origin-when-cross-origin"})}}class o{_request;_response;request;response;state={};cookies={set:(Q,X,Z)=>{},sign:(Q,X)=>"",unsign:(Q,X)=>!1};constructor(Q,X,Z){this._request=new r(Q,X,Z),this._response=new n(this._request),this.request=this._request,this.response=this._response}}var N=(Q)=>{if(typeof Q==="number"){if(!Number.isFinite(Q)||Q<=0)throw Error(`Invalid time value: "${Q}". Must be a positive number`);return Q}if(typeof Q!=="string"||Q.length<2)throw Error("Invalid time format. Expected format: 1ms, 1s, 1m, 1h, 1d");let X=Q.includes("ms")?Q.slice(-2):Q.slice(-1),Z=Q.includes("ms")?Q.slice(0,-2):Q.slice(0,-1);if(!["ms","s","m","h","d"].includes(X))throw Error(`Invalid time unit: "${X}". Expected: s (seconds), m (minutes), h (hours), or d (days)`);let $=Number(Z);if(isNaN($)||$<=0)throw Error(`Invalid time value: "${Z}". Must be a positive number`);switch(X){case"ms":return $;case"s":return $*1000;case"m":return $*60*1000;case"h":return $*60*60*1000;case"d":return $*24*60*60*1000;default:throw Error(`Unsupported time unit: "${X}"`)}};var p={json:{maxSize:262144,maxDepth:10,allowPrototypeProperties:!1,maxKeys:1000,maxStringLength:1048576,maxArrayLength:1e4},fileUploads:{maxFileSize:10485760,maxTotalSize:52428800,maxFiles:10,allowedExtensions:[],blockedExtensions:[".exe",".bat",".cmd",".scr",".pif",".com"],maxFilenameLength:255},urlEncoded:{maxSize:1048576,maxFields:1000,maxFieldNameLength:100,maxFieldLength:1048576}},m1={trustedProxies:["127.0.0.1","::1"],allowPrivateIps:!0,headerPreference:["x-forwarded-for","x-real-ip","cf-connecting-ip","x-client-ip","true-client-ip"],maxChainLength:10,detectSpoofing:!0},h1={slowRequests:!1,largeResponses:!1,largeRequests:!1,memory:!1,eventLoop:!1,rateLimits:!1},u1={level:"warn",prefix:"YINZER",personality:!0,requests:!1,diagnostics:h1},n2={port:5000,host:"0.0.0.0",gracefulShutdownTimeout:"15m",cors:{enabled:!1},logging:u1,bodyParser:p,ipSecurity:m1},o2=(Q)=>{if(Q.maxSize<1)throw Error("bodyParser.json.maxSize must be at least 1 byte");if(Q.maxDepth<1)throw Error("bodyParser.json.maxDepth must be at least 1");if(Q.maxKeys<1)throw Error("bodyParser.json.maxKeys must be at least 1");if(Q.maxStringLength<1)throw Error("bodyParser.json.maxStringLength must be at least 1 byte");if(Q.maxArrayLength<1)throw Error("bodyParser.json.maxArrayLength must be at least 1")},t2=(Q)=>{if(Q.maxFileSize<1)throw Error("bodyParser.fileUploads.maxFileSize must be at least 1 byte");if(Q.maxTotalSize<1)throw Error("bodyParser.fileUploads.maxTotalSize must be at least 1 byte");if(Q.maxFiles<1)throw Error("bodyParser.fileUploads.maxFiles must be at least 1");if(Q.maxFilenameLength<1)throw Error("bodyParser.fileUploads.maxFilenameLength must be at least 1 character")},e2=(Q)=>{if(Q.maxSize<1)throw Error("bodyParser.urlEncoded.maxSize must be at least 1 byte");if(Q.maxFields<1)throw Error("bodyParser.urlEncoded.maxFields must be at least 1");if(Q.maxFieldNameLength<1)throw Error("bodyParser.urlEncoded.maxFieldNameLength must be at least 1 character");if(Q.maxFieldLength<1)throw Error("bodyParser.urlEncoded.maxFieldLength must be at least 1 byte")},Q0=(Q)=>{if(!Array.isArray(Q.trustedProxies))throw Error("ipSecurity.trustedProxies must be an array");if(!Array.isArray(Q.headerPreference))throw Error("ipSecurity.headerPreference must be an array");if(Q.headerPreference.length===0)throw Error("ipSecurity.headerPreference must contain at least one header");if(Q.maxChainLength<1)throw Error("ipSecurity.maxChainLength must be at least 1");if(Q.maxChainLength>50)throw Error("ipSecurity.maxChainLength must not exceed 50 to prevent DoS attacks")},X0=(Q)=>{if(Q.allowPrototypeProperties)G.warn("[SECURITY WARNING] bodyParser.json.allowPrototypeProperties is enabled. This allows prototype pollution attacks. Only enable this if you absolutely need it and have other protections in place.");if(Q.maxSize>10485760)G.warn(`[SECURITY WARNING] bodyParser.json.maxSize is set to ${Q.maxSize} bytes (${A(Q.maxSize)}). Large JSON payloads can cause memory exhaustion and DoS attacks. Consider if this size is necessary.`);if(Q.maxDepth>50)G.warn(`[SECURITY WARNING] bodyParser.json.maxDepth is set to ${Q.maxDepth}. Very deep JSON nesting can cause stack overflow attacks. Consider if this depth is necessary.`)},Z0=(Q)=>{if(Q.maxFileSize>104857600)G.warn(`[SECURITY WARNING] bodyParser.fileUploads.maxFileSize is set to ${Q.maxFileSize} bytes (${A(Q.maxFileSize)}). Large file uploads can consume significant server resources.`);if(Q.maxTotalSize>1073741824)G.warn(`[SECURITY WARNING] bodyParser.fileUploads.maxTotalSize is set to ${Q.maxTotalSize} bytes (${A(Q.maxTotalSize)}). Very large total upload sizes can cause memory and disk space exhaustion.`);let X=[".exe",".bat",".cmd",".scr",".pif",".com",".vbs",".jar",".app"],Z=Q.allowedExtensions.filter(($)=>X.includes($.toLowerCase()));if(Z.length>0)G.warn(`[SECURITY WARNING] bodyParser.fileUploads.allowedExtensions includes dangerous file types: ${Z.join(", ")}. This could allow execution of malicious files. Only allow these if absolutely necessary.`);if(Q.blockedExtensions.length===0&&Q.allowedExtensions.length===0)G.warn("[SECURITY WARNING] File uploads have no extension restrictions (no blockedExtensions and no allowedExtensions). Consider adding blockedExtensions or allowedExtensions to improve security.")},$0=(Q)=>{if(Q.trustedProxies.length===0)G.warn("[SECURITY WARNING] ipSecurity.trustedProxies is empty. No proxy headers will be trusted, which may prevent proper client IP detection.");if(Q.maxChainLength>20)G.warn(`[SECURITY WARNING] ipSecurity.maxChainLength is set to ${Q.maxChainLength}. Very long proxy chains can consume significant resources and may indicate amplification attacks.`);if(!Q.detectSpoofing)G.warn("[SECURITY WARNING] ipSecurity.detectSpoofing is disabled. This reduces protection against IP spoofing attacks. Only disable if you have other protective measures.")},W0=(Q,X)=>{if(X?.bodyParser)Q.bodyParser={json:{...p.json,...X.bodyParser.json},fileUploads:{...p.fileUploads,...X.bodyParser.fileUploads},urlEncoded:{...p.urlEncoded,...X.bodyParser.urlEncoded}},K0(Q.bodyParser)},J0=(Q,X)=>{if(X?.ipSecurity)Q.ipSecurity={...m1,...X.ipSecurity},Q0(Q.ipSecurity),$0(Q.ipSecurity)},Y0=(Q,X)=>{if(X?.port!==void 0){let Z=Number(X.port);if(isNaN(Z)||Z<1||Z>65535)throw Error("Invalid port number");Q.port=Z}},K0=(Q)=>{o2(Q.json),t2(Q.fileUploads),e2(Q.urlEncoded),X0(Q.json),Z0(Q.fileUploads)},y1=new Set(Object.values(H)),j0=new Set(["logging","bodyParser","ipSecurity"]),b=(Q)=>{try{Q.converter(Q.value)}catch{throw Error(`logging.diagnostics.${Q.field} must be a valid threshold value (e.g. ${Q.examples}). Got: "${String(Q.value)}"`)}},U0=(Q)=>{if(!y1.has(Q.level))throw Error(`logging.level must be one of: ${[...y1].join(", ")}. Got: "${Q.level}"`);let X=Q.diagnostics;if(X.slowRequests!==!1)b({field:"slowRequests",value:X.slowRequests,converter:N,examples:"'100ms', '1s', '30s'"});if(X.memory!==!1)b({field:"memory",value:X.memory,converter:N,examples:"'100ms', '1s', '30s'"});if(X.eventLoop!==!1)b({field:"eventLoop",value:X.eventLoop,converter:N,examples:"'100ms', '1s', '30s'"});if(X.largeResponses!==!1)b({field:"largeResponses",value:X.largeResponses,converter:_,examples:"'1mb', '256kb', '10mb'"});if(X.largeRequests!==!1)b({field:"largeRequests",value:X.largeRequests,converter:_,examples:"'1mb', '256kb', '10mb'"})},G0=(Q,X)=>{if(X?.logging){let Z={},$=X.logging.logger;if($&&I in $){let W=$[I];Z={level:W.level,prefix:W.prefix,personality:W.personality}}Q.logging={...u1,...Z,...X.logging,diagnostics:{...h1,...X.logging.diagnostics}},U0(Q.logging)}},p1=(Q)=>{let X={...n2};if(Q){for(let Z of Object.keys(Q))if(!j0.has(Z)&&Q[Z]!==void 0)X[Z]=Q[Z]}return G0(X,Q),W0(X,Q),J0(X,Q),Y0(X,Q),X};class t{_beforeRouting;_beforeAll;_afterAll;_onError;_onNotFound;_logger=G;setLogger(Q){this._logger=Q}constructor(){this._beforeRouting=new Set,this._beforeAll=new Set,this._afterAll=new Set,this._onError=(Q,X)=>{return this._logger.error("Error while handling your request: ",X),Q.response.setStatusCode(B.internalServerError),{success:!1,message:"Internal Server Error"}},this._onNotFound=(Q)=>{return Q.response.setStatusCode(B.notFound),{success:!1,message:"404 Not Found"}}}_addBeforeRoutingHooks(Q,X){this._validateHandlersArray(Q,"beforeRouting");for(let Z of Q)this._beforeRouting.add({handler:Z,options:X??{routesToExclude:[],routesToInclude:[]}})}_addBeforeHooks(Q,X){this._validateHandlersArray(Q,"beforeAll");for(let Z of Q)this._beforeAll.add({handler:Z,options:X??{routesToExclude:[],routesToInclude:[]}})}_addAfterHooks(Q,X){this._validateHandlersArray(Q,"afterAll");for(let Z of Q)this._afterAll.add({handler:Z,options:X??{routesToExclude:[],routesToInclude:[]}})}_validateHandlersArray(Q,X){if(!Array.isArray(Q)){let Z=typeof Q;throw Error(`YinzerFlow: ${X}() expects an array of handler functions, but received ${Z}.${Z==="function"?`
|
|
14
|
+
`:"";this._stringBody=`${X}\r
|
|
15
|
+
${j}\r
|
|
16
|
+
${$}`}_setHeadersIfNotSet(X){let Z={};for(let[Q,J]of Object.entries(X))if(J!==void 0&&!(Q in this._headers))Z[Q]=J;let $=Z0(Z);Object.assign(this._headers,$)}_setBody(X){if(this._body=X,!this._headers["content-type"]){let Z=v0(X);this._setHeadersIfNotSet({"Content-Type":Z})}}setStatusCode(X){this._statusCode=X,this._status=l0(X)}addHeaders(X){let Z=Z0(X);for(let[$,Q]of Object.entries(Z))if($==="Set-Cookie"){if(Q)this._setCookies.push(Q)}else this._headers[$]=Q}removeHeaders(X){for(let Z of X)delete this._headers[Z]}_setSecurityHeaders(){this._setHeadersIfNotSet({"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-XSS-Protection":"1; mode=block","Referrer-Policy":"strict-origin-when-cross-origin"})}}class Q0{_request;_response;request;response;state={};cookies={set:(X,Z,$)=>{},sign:(X,Z)=>"",unsign:(X,Z)=>!1};constructor(X,Z,$){this._request=new e(X,Z,$),this._response=new $0(this._request),this.request=this._request,this.response=this._response}}var c={maxPayloadLength:16777216,idleTimeout:120,maxConnectionsPerIp:50,allowedOrigins:[],backpressure:{strategy:"buffer",limit:1048576}},s0=(X)=>{if(X.maxPayloadLength<1)throw Error("websocket.maxPayloadLength must be at least 1 byte");if(X.idleTimeout<0)throw Error("websocket.idleTimeout must be >= 0 (0 = no timeout)");if(X.maxConnectionsPerIp<1)throw Error("websocket.maxConnectionsPerIp must be at least 1");if(X.backpressure.limit<0)throw Error("websocket.backpressure.limit must be >= 0");if(X.backpressure.strategy!=="buffer"&&X.backpressure.strategy!=="drop")throw Error(`websocket.backpressure.strategy must be 'buffer' or 'drop'. Got: "${X.backpressure.strategy}"`)},r0=(X)=>{if(X.maxPayloadLength>67108864)M.warn(`[SECURITY WARNING] websocket.maxPayloadLength is set to ${I(X.maxPayloadLength)}. Very large payloads can cause memory exhaustion. Consider if this size is necessary.`);if(X.idleTimeout===0)M.warn("[SECURITY WARNING] websocket.idleTimeout is 0 (disabled). Idle connections will never be closed automatically, which can lead to resource exhaustion.")};var D=(X)=>{if(typeof X==="number"){if(!Number.isFinite(X)||X<=0)throw Error(`Invalid time value: "${X}". Must be a positive number`);return X}if(typeof X!=="string"||X.length<2)throw Error("Invalid time format. Expected format: 1ms, 1s, 1m, 1h, 1d");let Z=X.includes("ms")?X.slice(-2):X.slice(-1),$=X.includes("ms")?X.slice(0,-2):X.slice(0,-1);if(!["ms","s","m","h","d"].includes(Z))throw Error(`Invalid time unit: "${Z}". Expected: s (seconds), m (minutes), h (hours), or d (days)`);let Q=Number($);if(isNaN(Q)||Q<=0)throw Error(`Invalid time value: "${$}". Must be a positive number`);switch(Z){case"ms":return Q;case"s":return Q*1000;case"m":return Q*60*1000;case"h":return Q*60*60*1000;case"d":return Q*24*60*60*1000;default:throw Error(`Unsupported time unit: "${Z}"`)}};var s={json:{maxSize:262144,maxDepth:10,allowPrototypeProperties:!1,maxKeys:1000,maxStringLength:1048576,maxArrayLength:1e4},fileUploads:{maxFileSize:10485760,maxTotalSize:52428800,maxFiles:10,allowedExtensions:[],blockedExtensions:[".exe",".bat",".cmd",".scr",".pif",".com"],maxFilenameLength:255},urlEncoded:{maxSize:1048576,maxFields:1000,maxFieldNameLength:100,maxFieldLength:1048576}},a0={trustedProxies:["127.0.0.1","::1"],allowPrivateIps:!0,headerPreference:["x-forwarded-for","x-real-ip","cf-connecting-ip","x-client-ip","true-client-ip"],maxChainLength:10,detectSpoofing:!0},n0={slowRequests:!1,largeResponses:!1,largeRequests:!1,memory:!1,eventLoop:!1,rateLimits:!1},o0={level:"warn",prefix:"YINZER",personality:!0,requests:!1,diagnostics:n0},N9={port:5000,host:"0.0.0.0",gracefulShutdownTimeout:"15m",cors:{enabled:!1},logging:o0,bodyParser:s,ipSecurity:a0,websocket:c},D9=(X)=>{if(X.maxSize<1)throw Error("bodyParser.json.maxSize must be at least 1 byte");if(X.maxDepth<1)throw Error("bodyParser.json.maxDepth must be at least 1");if(X.maxKeys<1)throw Error("bodyParser.json.maxKeys must be at least 1");if(X.maxStringLength<1)throw Error("bodyParser.json.maxStringLength must be at least 1 byte");if(X.maxArrayLength<1)throw Error("bodyParser.json.maxArrayLength must be at least 1")},B9=(X)=>{if(X.maxFileSize<1)throw Error("bodyParser.fileUploads.maxFileSize must be at least 1 byte");if(X.maxTotalSize<1)throw Error("bodyParser.fileUploads.maxTotalSize must be at least 1 byte");if(X.maxFiles<1)throw Error("bodyParser.fileUploads.maxFiles must be at least 1");if(X.maxFilenameLength<1)throw Error("bodyParser.fileUploads.maxFilenameLength must be at least 1 character")},F9=(X)=>{if(X.maxSize<1)throw Error("bodyParser.urlEncoded.maxSize must be at least 1 byte");if(X.maxFields<1)throw Error("bodyParser.urlEncoded.maxFields must be at least 1");if(X.maxFieldNameLength<1)throw Error("bodyParser.urlEncoded.maxFieldNameLength must be at least 1 character");if(X.maxFieldLength<1)throw Error("bodyParser.urlEncoded.maxFieldLength must be at least 1 byte")},E9=(X)=>{if(!Array.isArray(X.trustedProxies))throw Error("ipSecurity.trustedProxies must be an array");if(!Array.isArray(X.headerPreference))throw Error("ipSecurity.headerPreference must be an array");if(X.headerPreference.length===0)throw Error("ipSecurity.headerPreference must contain at least one header");if(X.maxChainLength<1)throw Error("ipSecurity.maxChainLength must be at least 1");if(X.maxChainLength>50)throw Error("ipSecurity.maxChainLength must not exceed 50 to prevent DoS attacks")},z9=(X)=>{if(X.allowPrototypeProperties)M.warn("[SECURITY WARNING] bodyParser.json.allowPrototypeProperties is enabled. This allows prototype pollution attacks. Only enable this if you absolutely need it and have other protections in place.");if(X.maxSize>10485760)M.warn(`[SECURITY WARNING] bodyParser.json.maxSize is set to ${X.maxSize} bytes (${I(X.maxSize)}). Large JSON payloads can cause memory exhaustion and DoS attacks. Consider if this size is necessary.`);if(X.maxDepth>50)M.warn(`[SECURITY WARNING] bodyParser.json.maxDepth is set to ${X.maxDepth}. Very deep JSON nesting can cause stack overflow attacks. Consider if this depth is necessary.`)},A9=(X)=>{if(X.maxFileSize>104857600)M.warn(`[SECURITY WARNING] bodyParser.fileUploads.maxFileSize is set to ${X.maxFileSize} bytes (${I(X.maxFileSize)}). Large file uploads can consume significant server resources.`);if(X.maxTotalSize>1073741824)M.warn(`[SECURITY WARNING] bodyParser.fileUploads.maxTotalSize is set to ${X.maxTotalSize} bytes (${I(X.maxTotalSize)}). Very large total upload sizes can cause memory and disk space exhaustion.`);let Z=[".exe",".bat",".cmd",".scr",".pif",".com",".vbs",".jar",".app"],$=X.allowedExtensions.filter((Q)=>Z.includes(Q.toLowerCase()));if($.length>0)M.warn(`[SECURITY WARNING] bodyParser.fileUploads.allowedExtensions includes dangerous file types: ${$.join(", ")}. This could allow execution of malicious files. Only allow these if absolutely necessary.`);if(X.blockedExtensions.length===0&&X.allowedExtensions.length===0)M.warn("[SECURITY WARNING] File uploads have no extension restrictions (no blockedExtensions and no allowedExtensions). Consider adding blockedExtensions or allowedExtensions to improve security.")},I9=(X)=>{if(X.trustedProxies.length===0)M.warn("[SECURITY WARNING] ipSecurity.trustedProxies is empty. No proxy headers will be trusted, which may prevent proper client IP detection.");if(X.maxChainLength>20)M.warn(`[SECURITY WARNING] ipSecurity.maxChainLength is set to ${X.maxChainLength}. Very long proxy chains can consume significant resources and may indicate amplification attacks.`);if(!X.detectSpoofing)M.warn("[SECURITY WARNING] ipSecurity.detectSpoofing is disabled. This reduces protection against IP spoofing attacks. Only disable if you have other protective measures.")},H9=(X,Z)=>{if(Z?.bodyParser)X.bodyParser={json:{...s.json,...Z.bodyParser.json},fileUploads:{...s.fileUploads,...Z.bodyParser.fileUploads},urlEncoded:{...s.urlEncoded,...Z.bodyParser.urlEncoded}},w9(X.bodyParser)},O9=(X,Z)=>{if(Z?.ipSecurity)X.ipSecurity={...a0,...Z.ipSecurity},E9(X.ipSecurity),I9(X.ipSecurity)},T9=(X,Z)=>{if(Z?.port!==void 0){let $=Number(Z.port);if(isNaN($)||$<1||$>65535)throw Error("Invalid port number");X.port=$}},w9=(X)=>{D9(X.json),B9(X.fileUploads),F9(X.urlEncoded),z9(X.json),A9(X.fileUploads)},i0=new Set(Object.values(P)),R9=new Set(["logging","bodyParser","ipSecurity","websocket"]),h=(X)=>{try{X.converter(X.value)}catch{throw Error(`logging.diagnostics.${X.field} must be a valid threshold value (e.g. ${X.examples}). Got: "${String(X.value)}"`)}},P9=(X)=>{if(!i0.has(X.level))throw Error(`logging.level must be one of: ${[...i0].join(", ")}. Got: "${X.level}"`);let Z=X.diagnostics;if(Z.slowRequests!==!1)h({field:"slowRequests",value:Z.slowRequests,converter:D,examples:"'100ms', '1s', '30s'"});if(Z.memory!==!1)h({field:"memory",value:Z.memory,converter:D,examples:"'100ms', '1s', '30s'"});if(Z.eventLoop!==!1)h({field:"eventLoop",value:Z.eventLoop,converter:D,examples:"'100ms', '1s', '30s'"});if(Z.largeResponses!==!1)h({field:"largeResponses",value:Z.largeResponses,converter:k,examples:"'1mb', '256kb', '10mb'"});if(Z.largeRequests!==!1)h({field:"largeRequests",value:Z.largeRequests,converter:k,examples:"'1mb', '256kb', '10mb'"})},L9=(X,Z)=>{if(Z?.logging){let $={},Q=Z.logging.logger;if(Q&&L in Q){let J=Q[L];$={level:J.level,prefix:J.prefix,personality:J.personality}}X.logging={...o0,...$,...Z.logging,diagnostics:{...n0,...Z.logging.diagnostics}},P9(X.logging)}},_9=(X,Z)=>{if(Z?.websocket){let $={...c,...Z.websocket,backpressure:{...c.backpressure,...Z.websocket.backpressure}};X.websocket=$,s0($),r0($)}},t0=(X)=>{let Z={...N9};if(X){for(let $ of Object.keys(X))if(!R9.has($)&&X[$]!==void 0)Z[$]=X[$]}return L9(Z,X),H9(Z,X),O9(Z,X),_9(Z,X),T9(Z,X),Z};class J0{_beforeRouting;_beforeAll;_afterAll;_wsBeforeMessage;_wsAfterMessage;_onError;_onNotFound;_logger=M;setLogger(X){this._logger=X}constructor(){this._beforeRouting=new Set,this._beforeAll=new Set,this._afterAll=new Set,this._wsBeforeMessage=new Set,this._wsAfterMessage=new Set,this._onError=(X,Z)=>{return this._logger.error("Error while handling your request: ",Z),X.response.setStatusCode(z.internalServerError),{success:!1,message:"Internal Server Error"}},this._onNotFound=(X)=>{return X.response.setStatusCode(z.notFound),{success:!1,message:"404 Not Found"}}}_addBeforeRoutingHooks(X,Z){this._validateHandlersArray(X,"beforeRouting");for(let $ of X)this._beforeRouting.add({handler:$,options:Z??{routesToExclude:[],routesToInclude:[]}})}_addBeforeHooks(X,Z){this._validateHandlersArray(X,"beforeAll");for(let $ of X)this._beforeAll.add({handler:$,options:Z??{routesToExclude:[],routesToInclude:[]}})}_addAfterHooks(X,Z){this._validateHandlersArray(X,"afterAll");for(let $ of X)this._afterAll.add({handler:$,options:Z??{routesToExclude:[],routesToInclude:[]}})}_addWsBeforeMessageHooks(X){this._validateHandlersArray(X,"wsBeforeMessage");for(let Z of X)this._wsBeforeMessage.add({handler:Z})}_addWsAfterMessageHooks(X){this._validateHandlersArray(X,"wsAfterMessage");for(let Z of X)this._wsAfterMessage.add({handler:Z})}_validateHandlersArray(X,Z){if(!Array.isArray(X)){let $=typeof X;throw Error(`YinzerFlow: ${Z}() expects an array of handler functions, but received ${$}.${$==="function"?`
|
|
17
17
|
|
|
18
|
-
❌ Incorrect: app.${
|
|
19
|
-
✅ Correct: app.${
|
|
18
|
+
❌ Incorrect: app.${Z}${V.red}(${V.reset}(ctx) => { ... }${V.red})${V.reset}
|
|
19
|
+
✅ Correct: app.${Z}${V.green}([${V.reset}(ctx) => { ... }${V.green}])${V.reset}
|
|
20
20
|
|
|
21
|
-
Note: Wrap your handler function in ${
|
|
21
|
+
Note: Wrap your handler function in ${V.magenta}square brackets${V.reset} to make it an array.
|
|
22
22
|
|
|
23
23
|
`:`
|
|
24
24
|
|
|
25
25
|
Expected: Array<HandlerCallback>
|
|
26
|
-
Received: ${Z}`}`)}if(Q.length===0){this._logger.warn(`${X}() called with empty array. No hooks will be registered.`);return}for(let Z=0;Z<Q.length;Z++){let $=Q[Z];if(typeof $!=="function")throw Error(`YinzerFlow: ${X}() array contains non-function at index ${Z}. Expected: function, received: ${typeof $}`)}}_addOnError(Q){this._onError=Q}_addOnNotFound(Q){this._onNotFound=Q}}var f1=(Q)=>{let X=[],Z=Q.path.replace(/:\w+/g,($)=>{let W=$.slice(1);return X.push(W),"([^/]+)"}).replace(/\//g,"\\/");return{...Q,pattern:new RegExp(`^${Z}$`),paramNames:X,isParameterized:!0}};var e=(Q)=>{let[X]=Q.split("?");if(!X)return"";if([X]=X.split("#"),!X)return"";try{X=decodeURIComponent(X)}catch(Z){G.warn("Failed to decode URL path",{path:X})}if(X=X.startsWith("/")?X:`/${X}`,X=X.replace(/\/\/+/g,"/"),X=q0(X),X.length>1&&X.endsWith("/"))X=X.slice(0,-1);return X},q0=(Q)=>{let X=Q.split("/"),Z=[];for(let W of X){if(W==="."||W===""){if(W===""&&Z.length===0)Z.push(W);continue}if(W===".."){if(Z.length>1)Z.pop()}else Z.push(W)}return Z.join("/")||"/"},Q1=(Q)=>Q.replace(/:\w+/g,":param");var g1=(Q)=>{let X=Q.match(/:\w+/g);if(!X)return;let Z=X.map((W)=>W.slice(1)),$=new Set(Z);if(Z.length!==$.size){let W=Z.filter((J,Y)=>Z.indexOf(J)!==Y);throw Error(`Route ${Q} has duplicate parameter names: ${W.join(", ")}. Parameter names must be unique within a route for clarity and to prevent conflicts.`)}};class X1{_exactRoutes=new Map;_parameterizedRoutes=new Map;_register({method:Q,path:X,handler:Z,options:$}){let W=e(X),J=W.includes(":");if(J)g1(W);if(this._hasExactRoutePattern(Q,W))throw Error(`Route ${W} already exists for method ${Q}`);let Y={method:Q,path:W,handler:Z,options:$,params:{}};if(J)this._storeParameterizedRoute(Q,Y);else this._storeExactRoute(Q,W,Y)}_findRoute(Q,X){let Z=e(X),$=this._exactRoutes.get(Q)?.get(Z);if($)return $;let W=this._findParameterizedRoute(Q,Z);if(W)return W;return}_hasExactRoutePattern(Q,X){if(this._exactRoutes.get(Q)?.has(X))return!0;if(X.includes(":")){let Z=Q1(X),$=this._parameterizedRoutes.get(Q);if($)return $.some((W)=>Q1(W.path)===Z)}else{let Z=this._parameterizedRoutes.get(Q);if(Z)return Z.some(($)=>$.path===X)}return!1}_storeExactRoute(Q,X,Z){if(!this._exactRoutes.has(Q))this._exactRoutes.set(Q,new Map);this._exactRoutes.get(Q)?.set(X,Z)}_storeParameterizedRoute(Q,X){if(!this._parameterizedRoutes.has(Q))this._parameterizedRoutes.set(Q,[]);let Z=f1(X);this._parameterizedRoutes.get(Q)?.push(Z)}_findParameterizedRoute(Q,X){let Z=this._parameterizedRoutes.get(Q);if(!Z)return;for(let $ of Z){let W=X.match($.pattern);if(W){let J={};for(let Y=0;Y<$.paramNames.length;Y++){let K=W[Y+1],U=$.paramNames[Y];if(K!==void 0&&U!==void 0)J[U]=K}return{...$,params:J}}}return}}var w=(Q)=>({beforeHooks:Q?.beforeHooks??[],afterHooks:Q?.afterHooks??[]}),Z1=(Q,X)=>({beforeHooks:[...Q.beforeHooks??[],...X?.beforeHooks??[]],afterHooks:[...X?.afterHooks??[],...Q.afterHooks??[]]}),$1=(Q,X)=>{let Z=Q.endsWith("/")?Q.slice(0,-1):Q,$=X.startsWith("/")?X:`/${X}`;return`${Z}${$}`};class f{_setup;_prefix;_options;constructor(Q,X,Z){this._setup=Q,this._prefix=X,this._options=w(Z)}_createRouteHandler(Q){return(X,Z,$)=>{let W=$1(this._prefix,X),J=Z1(this._options,$);if(this._setup._routeRegistry._register({method:Q,handler:Z,path:W,options:J,params:{}}),Q===M.get)this._setup._routeRegistry._register({method:M.head,handler:Z,path:W,options:J,params:{}})}}get=this._createRouteHandler(M.get);head=this._createRouteHandler(M.head);post=this._createRouteHandler(M.post);put=this._createRouteHandler(M.put);delete=this._createRouteHandler(M.delete);patch=this._createRouteHandler(M.patch);options=this._createRouteHandler(M.options);group(Q,X,Z){let $=$1(this._prefix,Q),W=Z1(this._options,Z),J=new f(this._setup,$,W);return X(J),J}}class W1{_configuration;_routeRegistry=new X1;_hooks=new t;_log=G;constructor(Q){this._configuration=p1(Q)}get(Q,X,Z){let $=w(Z);this._routeRegistry._register({method:M.get,handler:X,path:Q,options:$,params:{}}),this._routeRegistry._register({method:M.head,handler:X,path:Q,options:$,params:{}})}head(Q,X,Z){this._routeRegistry._register({method:M.head,handler:X,path:Q,options:w(Z),params:{}})}post(Q,X,Z){this._routeRegistry._register({method:M.post,handler:X,path:Q,options:w(Z),params:{}})}put(Q,X,Z){this._routeRegistry._register({method:M.put,handler:X,path:Q,options:w(Z),params:{}})}patch(Q,X,Z){this._routeRegistry._register({method:M.patch,handler:X,path:Q,options:w(Z),params:{}})}delete(Q,X,Z){this._routeRegistry._register({method:M.delete,handler:X,path:Q,options:w(Z),params:{}})}options(Q,X,Z){this._routeRegistry._register({method:M.options,handler:X,path:Q,options:w(Z),params:{}})}group(Q,X,Z){let $=new f(this,Q,Z);return X($),$}beforeRouting(Q,X){this._hooks._addBeforeRoutingHooks(Q,X)}beforeAll(Q,X){this._hooks._addBeforeHooks(Q,X)}afterAll(Q,X){this._hooks._addAfterHooks(Q,X)}onError(Q){this._hooks._addOnError(Q)}onNotFound(Q){this._hooks._addOnNotFound(Q)}}var l1={prefix:"ACCESS",level:H.off,personality:!1},d1=(Q)=>{if(Q>=200&&Q<300)return"✅";if(Q>=300&&Q<400)return"\uD83D\uDD04";if(Q>=400&&Q<500)return"❌";if(Q>=500)return"\uD83D\uDCA5";return"❓"};var c1=()=>{let Q=new Map;return{get:async(X)=>Promise.resolve(Q.get(X)),set:async(X,Z)=>{return Q.set(X,Z),Promise.resolve()},delete:async(X)=>{return Q.delete(X),Promise.resolve()},destroy:async()=>{return Q.clear(),Promise.resolve()}}};import{Redis as s1}from"ioredis";var r1=async(Q)=>{let{store:X}=Q;if(X.type!=="redis")throw Error(`Expected Redis store configuration but got: ${JSON.stringify(X)}`);let{client:Z,keyPrefix:$="rate_limit:",maxRetries:W=3,retryDelay:J=1000}=X,Y=N(J),K=!1;return await(async()=>{for(let j=1;j<=W;j++)try{await Z.ping(),K=!0,G.info(`[RedisStore] Successfully connected to Redis (attempt ${j})`);return}catch(q){if(G.warn(`[RedisStore] Redis connection attempt ${j}/${W} failed:`,q),j<W)G.info(`[RedisStore] Retrying connection in ${J}...`),await new Promise((V)=>{setTimeout(V,Y)});else G.error("[RedisStore] All Redis connection attempts failed. Store will operate in degraded mode."),K=!1}})(),{get:async(j)=>D0({client:Z,key:j,keyPrefix:$,connectionHealthy:K}),set:async(j,q)=>B0({client:Z,config:Q,key:j,value:q,keyPrefix:$,connectionHealthy:K}),delete:async(j)=>E0({client:Z,key:j,keyPrefix:$,connectionHealthy:K}),destroy:async()=>w0({client:Z,keyPrefix:$,connectionHealthy:K})}},J1=(Q,X)=>`${X}${Q}`,F0=(Q)=>{try{return JSON.stringify(Q)}catch(X){throw G.error("[RedisStore] Failed to serialize value:",X),Error("Failed to serialize rate limit data")}},M0=(Q)=>{try{return JSON.parse(Q)}catch(X){throw G.error("[RedisStore] Failed to deserialize value:",X),Error("Failed to deserialize rate limit data")}},g=(Q,X,Z)=>{if(Z)G.warn(`[RedisStore] Redis ${Q} failed (connection was healthy):`,X);else G.error(`[RedisStore] Redis ${Q} failed (connection unhealthy):`,X)},V0=async({client:Q,key:X,value:Z,ttlSeconds:$})=>{try{if(Q instanceof s1)await Q.set(X,Z,"EX",$);else await Q.set(X,Z,{EX:$})}catch(W){if(W instanceof Error)throw Error(`Unsupported Redis client or Redis operation failed: ${W.message}`);throw W}},N0=async({client:Q,key:X,value:Z})=>{try{if(Q instanceof s1)await Q.set(X,Z,"KEEPTTL");else await Q.set(X,Z,{KEEPTTL:!0})}catch($){if($ instanceof Error)throw Error(`Unsupported Redis client or Redis operation failed: ${$.message}`);throw $}},D0=async({client:Q,key:X,keyPrefix:Z,connectionHealthy:$})=>{try{let W=J1(X,Z),J=await Q.get(W);if(J===null)return;return M0(J)}catch(W){g("GET",W,$);return}},B0=async({client:Q,config:X,key:Z,value:$,keyPrefix:W,connectionHealthy:J})=>{try{let Y=J1(Z,W),K=F0($);if(await Q.exists(Y))await N0({client:Q,key:Y,value:K});else await V0({client:Q,key:Y,value:K,ttlSeconds:Math.floor(X.window/1000)})}catch(Y){g("SET",Y,J)}},E0=async({client:Q,key:X,keyPrefix:Z,connectionHealthy:$})=>{try{let W=J1(X,Z);await Q.del(W)}catch(W){g("DELETE",W,$)}},w0=async({client:Q,keyPrefix:X,connectionHealthy:Z})=>{try{let $=`${X}*`,W=await Q.keys($);if(W.length>0)await Promise.all(W.map(async(J)=>Q.del(J))),G.info(`[RedisStore] Destroyed ${W.length} rate limit keys`)}catch($){g("DESTROY",$,Z)}};var A0=()=>({memory:()=>c1(),redis:async(Q)=>r1(Q)}),a1=async(Q)=>{let Z=A0()[Q.store.type];if(!Z)throw Error(`Unsupported store type: ${Q.store.type}`);return Z(Q)};class Y1{_config;_store=null;constructor(Q){this._config=Q}async _getStore(){return this._store??=await a1(this._config),this._store}async check(Q){let X=await this._getStore(),Z=this._config.keyGenerator(Q),$=Date.now(),W=await X.get(Z)??{currentWindowCount:0,previousWindowCount:0,windowStart:$};if($-W.windowStart>=this._config.window)W.previousWindowCount=0,W.currentWindowCount=0,W.windowStart=$;let K=($-W.windowStart)/this._config.window,U=W.previousWindowCount*(1-K),j=W.currentWindowCount+U,q=j<this._config.max;if(q)W.currentWindowCount++;await X.set(Z,W);let V=Math.max(0,Math.floor(this._config.max-j-(q?1:0))),D=W.windowStart+this._config.window;return{allowed:q,remaining:V,resetTime:D,totalHits:Math.ceil(j+(q?1:0)),limit:this._config.max}}async destroy(){await(await this._getStore()).destroy()}}var z0=(Q)=>Math.ceil((Q-Date.now())/1000),O0=(Q)=>new Y1(Q);class C{_config;_strategy;constructor(Q){this._config=Q,this._strategy=O0(Q)}async check(Q){return this._strategy.check(Q)}async destroy(){await this._strategy.destroy()}get config(){return this._config}}var K1=(Q,X)=>{if(Q.response.addHeaders({"RateLimit-Limit":String(X.limit),"RateLimit-Remaining":String(X.remaining),"RateLimit-Reset":String(Math.ceil(X.resetTime/1000))}),!X.allowed){let Z=z0(X.resetTime);Q.response.addHeaders({"Retry-After":String(Z)})}};var j1={slidingWindowCounter:"sliding-window-counter"},H0={memory:"memory",redis:"redis"};class x{algorithm;store;window;max;standardHeaders;skipSuccessfulRequests;skipFailedRequests;keyGenerator;handler;constructor(Q,X){this._validateConfig(Q,X??G),this.algorithm=Q?.algorithm??j1.slidingWindowCounter,this.store=Q?.store??{type:"memory"},this.window=N(Q?.window??"15m"),this.max=Q?.max??100,this.standardHeaders=Q?.standardHeaders??!0,this.skipSuccessfulRequests=Q?.skipSuccessfulRequests??!1,this.skipFailedRequests=Q?.skipFailedRequests??!1,this.keyGenerator=Q?.keyGenerator??T0,this.handler=Q?.handler??L0}_validateConfig(Q,X){if(!Q)return;I0(Q),R0(Q,X)}get config(){return{algorithm:this.algorithm,window:this.window,max:this.max,standardHeaders:this.standardHeaders,skipSuccessfulRequests:this.skipSuccessfulRequests,skipFailedRequests:this.skipFailedRequests,keyGenerator:this.keyGenerator,handler:this.handler}}}var I0=(Q)=>{if(Q.max!==void 0){if(typeof Q.max!=="number"||isNaN(Q.max))throw Error("rateLimit.max must be a number");if(Q.max<1)throw Error("rateLimit.max must be at least 1 request per window");if(!Number.isInteger(Q.max))throw Error("rateLimit.max must be an integer (no decimals)")}if(Q.window!==void 0)if(typeof Q.window==="string"){if(!/^(?<value>\d+)(?<unit>s|m|h|d)$/.test(Q.window))throw Error(`rateLimit.window must be a valid time string (e.g., '30s', '15m', '2h', '1d') or milliseconds as a number. Received: "${Q.window}"`);let Z=N(Q.window);if(Z<1000)throw Error(`rateLimit.window must be at least 1000ms (1 second). Received: ${Q.window} (${Z}ms). Very short time windows can cause performance issues and inaccurate rate limiting.`)}else if(typeof Q.window==="number"){if(isNaN(Q.window))throw Error("rateLimit.window must be a valid number when using milliseconds");if(Q.window<1000)throw Error(`rateLimit.window must be at least 1000ms (1 second). Received: ${Q.window}ms. Very short time windows can cause performance issues and inaccurate rate limiting.`);if(!Number.isInteger(Q.window))throw Error("rateLimit.window must be an integer when using milliseconds (no decimals)")}else throw Error('rateLimit.window must be a time string (e.g., "15m") or milliseconds as a number')},R0=(Q,X)=>{if(Q.enabled===!1)X.warn("[SECURITY WARNING] Rate limiting is disabled. This removes DoS protection from your API. Only disable for development or if you have external rate limiting (e.g., API gateway, CDN).");if(Q.max!==void 0&&Q.max>1e4)X.warn(`[SECURITY WARNING] rateLimit.max is set to ${Q.max} requests. Very high rate limits may not provide adequate DoS protection. Consider if this limit is necessary for your use case.`);if(Q.window!==void 0){let Z=N(Q.window),$=3600000;if(Z>3600000){let W=Math.round(Z/3600000);X.warn(`[SECURITY WARNING] rateLimit.window is set to ${typeof Q.window==="string"?Q.window:`${Z}ms`} (${W}h). Very long time windows may allow burst attacks before limits are enforced. Consider shorter windows for better protection.`)}}if(Q.window!==void 0){let Z=N(Q.window);if(Z<1e4&&Q.max!==void 0&&Q.max>100)X.warn(`[PERFORMANCE WARNING] rateLimit.window is set to ${typeof Q.window==="string"?Q.window:`${Z}ms`} with max ${Q.max} requests. Very short time windows with high request counts can cause performance overhead. Consider increasing the window or decreasing max.`)}},L0=(Q)=>{return Q.response.setStatusCode(B.tooManyRequests),{success:!1,message:"Yinz are sending too many requests. Slow down, jagoff!"}},T0=(Q)=>Q.request.ipAddress;var S0=(Q)=>async(X)=>{let Z=new x(Q),$=new C(Z),W=await $.check(X);if($.config.standardHeaders)K1(X,W);if(!W.allowed)return $.config.handler(X);return},i1=(Q,X)=>async(Z)=>{let $=await Q.check(Z);if(Q.config.standardHeaders)K1(Z,$);if(!$.allowed)return X?.(Z.request.ipAddress,Z.request.path),Q.config.handler(Z);return};import{createHmac as n1}from"node:crypto";class y{enabled;secret;signed;defaults;constructor(Q,X){this._validateConfig(Q,X??G),this.enabled=Q?.enabled??!1,this.secret=Q?.secret,this.signed=Q?.signed,this.defaults=Q?.defaults}_validateConfig(Q,X){if(!Q)return;P0(Q),h0(Q,X)}get config(){let Q={enabled:this.enabled};if(this.secret!==void 0)Q.secret=this.secret;if(this.signed!==void 0)Q.signed=this.signed;if(this.defaults!==void 0)Q.defaults=this.defaults;return Q}}var P0=(Q)=>{if(Q.secret!==void 0){if(typeof Q.secret!=="string")throw Error("cookieParser.secret must be a string");if(Q.secret.length<32)throw Error("cookieParser.secret must be at least 32 characters for security. Use a strong, random secret stored in environment variables.")}if(Q.signed!==void 0){if(!Array.isArray(Q.signed))throw Error("cookieParser.signed must be an array of cookie names");for(let X of Q.signed){if(typeof X!=="string")throw Error("cookieParser.signed must be an array of strings (cookie names)");if(X.length===0)throw Error("cookieParser.signed cannot contain empty cookie names")}}if(Q.defaults)_0(Q.defaults)},_0=(Q)=>{if(!Q)return;v0(Q),k0(Q),b0(Q),C0(Q),x0(Q),y0(Q),m0(Q)},v0=(Q)=>{if(!Q||Q.maxAge===void 0)return;if(typeof Q.maxAge==="string"){if(!/^(?<value>\d+)(?<unit>ms|s|m|h|d)$/.test(Q.maxAge))throw Error(`cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: "${Q.maxAge}"`);return}if(typeof Q.maxAge!=="number"||isNaN(Q.maxAge))throw Error(`cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: "${Q.maxAge}"`);if(Q.maxAge<0)throw Error("cookieParser.defaults.maxAge must be 0 or greater");if(!Number.isInteger(Q.maxAge))throw Error("cookieParser.defaults.maxAge must be an integer (no decimals)")},k0=(Q)=>{if(!Q||Q.secure===void 0)return;if(typeof Q.secure!=="boolean")throw Error("cookieParser.defaults.secure must be a boolean")},b0=(Q)=>{if(!Q||Q.httpOnly===void 0)return;if(typeof Q.httpOnly!=="boolean")throw Error("cookieParser.defaults.httpOnly must be a boolean")},C0=(Q)=>{if(!Q||Q.sameSite===void 0)return;if(!["strict","lax","none"].includes(Q.sameSite))throw Error('cookieParser.defaults.sameSite must be one of: "strict", "lax", "none"')},x0=(Q)=>{if(!Q)return;if(Q.domain===void 0)return;if(typeof Q.domain!=="string")throw Error("cookieParser.defaults.domain must be a string");if(Q.domain.length===0)throw Error("cookieParser.defaults.domain cannot be empty")},y0=(Q)=>{if(!Q||Q.path===void 0)return;if(typeof Q.path!=="string")throw Error("cookieParser.defaults.path must be a string");if(!Q.path.startsWith("/"))throw Error('cookieParser.defaults.path must start with "/"')},m0=(Q)=>{if(!Q||Q.expires===void 0)return;if(!(Q.expires instanceof Date))throw Error("cookieParser.defaults.expires must be a Date object")},h0=(Q,X)=>{if(Q.enabled===!1)X.warn("[SECURITY WARNING] Cookie parser is disabled. Cookies will not be parsed or validated. Only disable for special use cases.");let Z=!1;if(Z&&!Q.secret)X.warn("[SECURITY WARNING] No secret provided for cookie signing in production. Cookies will not be signed and cannot be validated for tampering. Consider using a secret.");if(Z&&Q.defaults?.secure===!1)X.warn("[SECURITY WARNING] cookieParser.defaults.secure is false in production. Cookies will be sent over HTTP, which is insecure. Always use secure cookies in production.");if(Z&&Q.defaults?.httpOnly===!1)X.warn("[SECURITY WARNING] cookieParser.defaults.httpOnly is false in production. Cookies will be accessible to JavaScript, which increases XSS risk. Only disable httpOnly for cookies that need JavaScript access.");if(Q.defaults?.sameSite==="none"&&Q.defaults.secure!==!0)X.warn("[SECURITY WARNING] SameSite=none requires secure=true. Browsers will reject cookies with SameSite=none without secure flag.")};class U1{_config;constructor(Q){this._config=new y(Q)}parse(Q){let X=new Map;if(!Q||typeof Q!=="string")return X;let Z=Q.split(";");for(let $ of Z){let W=$.trim();if(!W)continue;let J=W.indexOf("=");if(J===-1)continue;let Y=W.slice(0,J).trim(),K=W.slice(J+1).trim();if(Y&&K)try{X.set(Y,decodeURIComponent(K))}catch(U){continue}}return X}set(Q,X,Z){let $=encodeURIComponent(Q),W=encodeURIComponent(X),J=this._mergeOptions(Z),Y=[`${$}=${W}`];if(J.expires)Y.push(`Expires=${J.expires.toUTCString()}`);if(J.maxAge!==void 0)Y.push(`Max-Age=${J.maxAge}`);if(J.domain)Y.push(`Domain=${J.domain}`);if(J.path)Y.push(`Path=${J.path}`);if(J.secure)Y.push("Secure");if(J.httpOnly)Y.push("HttpOnly");if(J.sameSite)Y.push(`SameSite=${J.sameSite.charAt(0).toUpperCase()+J.sameSite.slice(1)}`);return Y.join("; ")}sign(Q,X){if(!this._config.secret)throw Error("Cannot sign cookie: no secret configured");let Z=n1("sha256",this._config.secret).update(`${Q}=${X}`).digest("base64url").replace(/=/g,"");return`${X}.${Z}`}unsign(Q,X){if(!this._config.secret)throw Error("Cannot unsign cookie: no secret configured");let Z=X.lastIndexOf(".");if(Z===-1)return!1;let $=X.slice(0,Z),W=X.slice(Z+1);return n1("sha256",this._config.secret).update(`${Q}=${$}`).digest("base64url").replace(/=/g,"")===W?$:!1}_mergeOptions(Q){let X={...this._config.defaults,...Q};if(X.maxAge!==void 0&&typeof X.maxAge==="string")X.maxAge=N(X.maxAge)/1000;return X}get config(){return this._config}shouldSign(Q){if(!this._config.secret)return!1;if(this._config.signed===void 0||this._config.signed.length===0)return!0;return this._config.signed.includes(Q)}}var o1=(Q)=>(X)=>{let Z=new U1(Q),$=X.request.headers.cookie,W=Z.parse($??"");if(X.request.cookies=W,X.request.signedCookies=new Map,Z.config.secret){for(let[J,Y]of W.entries())if(Z.shouldSign(J)){let K=Z.unsign(J,Y);if(K!==!1)X.request.signedCookies.set(J,K)}}X.cookies={set:(J,Y,K)=>{let U=Y;if(Z.shouldSign(J))U=Z.sign(J,Y);let j=Z.set(J,U,K);X.response.addHeaders({"Set-Cookie":j})},sign:(J,Y)=>{if(!Z.config.secret)throw Error("Cannot sign cookie: no secret configured");return Z.sign(J,Y)},unsign:(J,Y)=>{if(!Z.config.secret)throw Error("Cannot unsign cookie: no secret configured");return Z.unsign(J,Y)}};return};class G1{config;_normalizedOrigins;constructor(Q){this.config=Q;if(Q.origin==="*"&&Q.credentials)throw Error(`CORS Configuration Error: Cannot use origin: "*" with credentials: true. The CORS specification forbids this combination as it creates security vulnerabilities. Choose one of these solutions:
|
|
26
|
+
Received: ${$}`}`)}if(X.length===0){this._logger.warn(`${Z}() called with empty array. No hooks will be registered.`);return}for(let $=0;$<X.length;$++){let Q=X[$];if(typeof Q!=="function")throw Error(`YinzerFlow: ${Z}() array contains non-function at index ${$}. Expected: function, received: ${typeof Q}`)}}_addOnError(X){this._onError=X}_addOnNotFound(X){this._onNotFound=X}}var e0=(X)=>{let Z=[],$=X.path.replace(/:\w+/g,(Q)=>{let J=Q.slice(1);return Z.push(J),"([^/]+)"}).replace(/\//g,"\\/");return{...X,pattern:new RegExp(`^${$}$`),paramNames:Z,isParameterized:!0}};var b=(X)=>{let[Z]=X.split("?");if(!Z)return"";if([Z]=Z.split("#"),!Z)return"";try{Z=decodeURIComponent(Z)}catch($){M.warn("Failed to decode URL path",{path:Z})}if(Z=Z.startsWith("/")?Z:`/${Z}`,Z=Z.replace(/\/\/+/g,"/"),Z=S9(Z),Z.length>1&&Z.endsWith("/"))Z=Z.slice(0,-1);return Z},S9=(X)=>{let Z=X.split("/"),$=[];for(let J of Z){if(J==="."||J===""){if(J===""&&$.length===0)$.push(J);continue}if(J===".."){if($.length>1)$.pop()}else $.push(J)}return $.join("/")||"/"},Y0=(X)=>X.replace(/:\w+/g,":param");var X1=(X)=>{let Z=X.match(/:\w+/g);if(!Z)return;let $=Z.map((J)=>J.slice(1)),Q=new Set($);if($.length!==Q.size){let J=$.filter((Y,j)=>$.indexOf(Y)!==j);throw Error(`Route ${X} has duplicate parameter names: ${J.join(", ")}. Parameter names must be unique within a route for clarity and to prevent conflicts.`)}};class j0{_exactRoutes=new Map;_parameterizedRoutes=new Map;_register({method:X,path:Z,handler:$,options:Q}){let J=b(Z),Y=J.includes(":");if(Y)X1(J);if(this._hasExactRoutePattern(X,J))throw Error(`Route ${J} already exists for method ${X}`);let j={method:X,path:J,handler:$,options:Q,params:{}};if(Y)this._storeParameterizedRoute(X,j);else this._storeExactRoute(X,J,j)}_findRoute(X,Z){let $=b(Z),Q=this._exactRoutes.get(X)?.get($);if(Q)return Q;let J=this._findParameterizedRoute(X,$);if(J)return J;return}_hasExactRoutePattern(X,Z){if(this._exactRoutes.get(X)?.has(Z))return!0;if(Z.includes(":")){let $=Y0(Z),Q=this._parameterizedRoutes.get(X);if(Q)return Q.some((J)=>Y0(J.path)===$)}else{let $=this._parameterizedRoutes.get(X);if($)return $.some((Q)=>Q.path===Z)}return!1}_storeExactRoute(X,Z,$){if(!this._exactRoutes.has(X))this._exactRoutes.set(X,new Map);this._exactRoutes.get(X)?.set(Z,$)}_storeParameterizedRoute(X,Z){if(!this._parameterizedRoutes.has(X))this._parameterizedRoutes.set(X,[]);let $=e0(Z);this._parameterizedRoutes.get(X)?.push($)}_findParameterizedRoute(X,Z){let $=this._parameterizedRoutes.get(X);if(!$)return;for(let Q of $){let J=Z.match(Q.pattern);if(J){let Y={};for(let j=0;j<Q.paramNames.length;j++){let W=J[j+1],K=Q.paramNames[j];if(W!==void 0&&K!==void 0)Y[K]=W}return{...Q,params:Y}}}return}}var O=(X)=>({beforeHooks:X?.beforeHooks??[],afterHooks:X?.afterHooks??[]}),W0=(X,Z)=>({beforeHooks:[...X.beforeHooks??[],...Z?.beforeHooks??[]],afterHooks:[...Z?.afterHooks??[],...X.afterHooks??[]]}),K0=(X,Z)=>{let $=X.endsWith("/")?X.slice(0,-1):X,Q=Z.startsWith("/")?Z:`/${Z}`;return`${$}${Q}`};class r{_setup;_prefix;_options;constructor(X,Z,$){this._setup=X,this._prefix=Z,this._options=O($)}_createRouteHandler(X){return(Z,$,Q)=>{let J=K0(this._prefix,Z),Y=W0(this._options,Q);if(this._setup._routeRegistry._register({method:X,handler:$,path:J,options:Y,params:{}}),X===q.get)this._setup._routeRegistry._register({method:q.head,handler:$,path:J,options:Y,params:{}})}}get=this._createRouteHandler(q.get);head=this._createRouteHandler(q.head);post=this._createRouteHandler(q.post);put=this._createRouteHandler(q.put);delete=this._createRouteHandler(q.delete);patch=this._createRouteHandler(q.patch);options=this._createRouteHandler(q.options);group(X,Z,$){let Q=K0(this._prefix,X),J=W0(this._options,$),Y=new r(this._setup,Q,J);return Z(Y),Y}}class U0{_exactRoutes=new Map;_parameterizedRoutes=[];_register(X,Z,$){let Q=b(X);if(Q.includes(":")){let Y=[],j=Q.replace(/:\w+/g,(W)=>{return Y.push(W.slice(1)),"([^/]+)"}).replace(/\//g,"\\/");this._parameterizedRoutes.push({path:Q,handlers:Z,options:$,pattern:new RegExp(`^${j}$`),paramNames:Y})}else this._exactRoutes.set(Q,{path:Q,handlers:Z,options:$})}_match(X){let Z=b(X),$=this._exactRoutes.get(Z);if($)return{handlers:$.handlers,options:$.options,params:{}};for(let Q of this._parameterizedRoutes){if(!Q.pattern)continue;let J=Q.pattern.exec(Z);if(J){let Y={};for(let j=0;j<(Q.paramNames?.length??0);j++){let W=Q.paramNames?.[j],K=J[j+1];if(W&&K)Y[W]=K}return{handlers:Q.handlers,options:Q.options,params:Y}}}return}_hasRoutes(){return this._exactRoutes.size>0||this._parameterizedRoutes.length>0}}class G0{_configuration;_routeRegistry=new j0;_hooks=new J0;_wsRouter=new U0;_log=M;constructor(X){this._configuration=t0(X)}get(X,Z,$){let Q=O($);this._routeRegistry._register({method:q.get,handler:Z,path:X,options:Q,params:{}}),this._routeRegistry._register({method:q.head,handler:Z,path:X,options:Q,params:{}})}head(X,Z,$){this._routeRegistry._register({method:q.head,handler:Z,path:X,options:O($),params:{}})}post(X,Z,$){this._routeRegistry._register({method:q.post,handler:Z,path:X,options:O($),params:{}})}put(X,Z,$){this._routeRegistry._register({method:q.put,handler:Z,path:X,options:O($),params:{}})}patch(X,Z,$){this._routeRegistry._register({method:q.patch,handler:Z,path:X,options:O($),params:{}})}delete(X,Z,$){this._routeRegistry._register({method:q.delete,handler:Z,path:X,options:O($),params:{}})}options(X,Z,$){this._routeRegistry._register({method:q.options,handler:Z,path:X,options:O($),params:{}})}group(X,Z,$){let Q=new r(this,X,$);return Z(Q),Q}beforeRouting(X,Z){this._hooks._addBeforeRoutingHooks(X,Z)}beforeAll(X,Z){this._hooks._addBeforeHooks(X,Z)}afterAll(X,Z){this._hooks._addAfterHooks(X,Z)}onError(X){this._hooks._addOnError(X)}onNotFound(X){this._hooks._addOnNotFound(X)}ws(X,Z,$){this._wsRouter._register(X,Z,$)}wsBeforeMessage(X){this._hooks._addWsBeforeMessageHooks(X)}wsAfterMessage(X){this._hooks._addWsAfterMessageHooks(X)}}var Z1={prefix:"ACCESS",level:P.off,personality:!1},$1=(X)=>{if(X>=200&&X<300)return"✅";if(X>=300&&X<400)return"\uD83D\uDD04";if(X>=400&&X<500)return"❌";if(X>=500)return"\uD83D\uDCA5";return"❓"};var Q1=()=>{let X=new Map;return{get:async(Z)=>Promise.resolve(X.get(Z)),set:async(Z,$)=>{return X.set(Z,$),Promise.resolve()},delete:async(Z)=>{return X.delete(Z),Promise.resolve()},destroy:async()=>{return X.clear(),Promise.resolve()}}};import{Redis as J1}from"ioredis";var Y1=async(X)=>{let{store:Z}=X;if(Z.type!=="redis")throw Error(`Expected Redis store configuration but got: ${JSON.stringify(Z)}`);let{client:$,keyPrefix:Q="rate_limit:",maxRetries:J=3,retryDelay:Y=1000}=Z,j=D(Y),W=!1;return await(async()=>{for(let U=1;U<=J;U++)try{await $.ping(),W=!0,M.info(`[RedisStore] Successfully connected to Redis (attempt ${U})`);return}catch(G){if(M.warn(`[RedisStore] Redis connection attempt ${U}/${J} failed:`,G),U<J)M.info(`[RedisStore] Retrying connection in ${Y}...`),await new Promise((B)=>{setTimeout(B,j)});else M.error("[RedisStore] All Redis connection attempts failed. Store will operate in degraded mode."),W=!1}})(),{get:async(U)=>b9({client:$,key:U,keyPrefix:Q,connectionHealthy:W}),set:async(U,G)=>y9({client:$,config:X,key:U,value:G,keyPrefix:Q,connectionHealthy:W}),delete:async(U)=>m9({client:$,key:U,keyPrefix:Q,connectionHealthy:W}),destroy:async()=>h9({client:$,keyPrefix:Q,connectionHealthy:W})}},M0=(X,Z)=>`${Z}${X}`,v9=(X)=>{try{return JSON.stringify(X)}catch(Z){throw M.error("[RedisStore] Failed to serialize value:",Z),Error("Failed to serialize rate limit data")}},x9=(X)=>{try{return JSON.parse(X)}catch(Z){throw M.error("[RedisStore] Failed to deserialize value:",Z),Error("Failed to deserialize rate limit data")}},i=(X,Z,$)=>{if($)M.warn(`[RedisStore] Redis ${X} failed (connection was healthy):`,Z);else M.error(`[RedisStore] Redis ${X} failed (connection unhealthy):`,Z)},C9=async({client:X,key:Z,value:$,ttlSeconds:Q})=>{try{if(X instanceof J1)await X.set(Z,$,"EX",Q);else await X.set(Z,$,{EX:Q})}catch(J){if(J instanceof Error)throw Error(`Unsupported Redis client or Redis operation failed: ${J.message}`);throw J}},k9=async({client:X,key:Z,value:$})=>{try{if(X instanceof J1)await X.set(Z,$,"KEEPTTL");else await X.set(Z,$,{KEEPTTL:!0})}catch(Q){if(Q instanceof Error)throw Error(`Unsupported Redis client or Redis operation failed: ${Q.message}`);throw Q}},b9=async({client:X,key:Z,keyPrefix:$,connectionHealthy:Q})=>{try{let J=M0(Z,$),Y=await X.get(J);if(Y===null)return;return x9(Y)}catch(J){i("GET",J,Q);return}},y9=async({client:X,config:Z,key:$,value:Q,keyPrefix:J,connectionHealthy:Y})=>{try{let j=M0($,J),W=v9(Q);if(await X.exists(j))await k9({client:X,key:j,value:W});else await C9({client:X,key:j,value:W,ttlSeconds:Math.floor(Z.window/1000)})}catch(j){i("SET",j,Y)}},m9=async({client:X,key:Z,keyPrefix:$,connectionHealthy:Q})=>{try{let J=M0(Z,$);await X.del(J)}catch(J){i("DELETE",J,Q)}},h9=async({client:X,keyPrefix:Z,connectionHealthy:$})=>{try{let Q=`${Z}*`,J=await X.keys(Q);if(J.length>0)await Promise.all(J.map(async(Y)=>X.del(Y))),M.info(`[RedisStore] Destroyed ${J.length} rate limit keys`)}catch(Q){i("DESTROY",Q,$)}};var u9=()=>({memory:()=>Q1(),redis:async(X)=>Y1(X)}),j1=async(X)=>{let $=u9()[X.store.type];if(!$)throw Error(`Unsupported store type: ${X.store.type}`);return $(X)};class V0{_config;_store=null;constructor(X){this._config=X}async _getStore(){return this._store??=await j1(this._config),this._store}async check(X){let Z=await this._getStore(),$=this._config.keyGenerator(X),Q=Date.now(),J=await Z.get($)??{currentWindowCount:0,previousWindowCount:0,windowStart:Q};if(Q-J.windowStart>=this._config.window)J.previousWindowCount=0,J.currentWindowCount=0,J.windowStart=Q;let W=(Q-J.windowStart)/this._config.window,K=J.previousWindowCount*(1-W),U=J.currentWindowCount+K,G=U<this._config.max;if(G)J.currentWindowCount++;await Z.set($,J);let B=Math.max(0,Math.floor(this._config.max-U-(G?1:0))),E=J.windowStart+this._config.window;return{allowed:G,remaining:B,resetTime:E,totalHits:Math.ceil(U+(G?1:0)),limit:this._config.max}}async destroy(){await(await this._getStore()).destroy()}}var p9=(X)=>Math.ceil((X-Date.now())/1000),f9=(X)=>new V0(X);class u{_config;_strategy;constructor(X){this._config=X,this._strategy=f9(X)}async check(X){return this._strategy.check(X)}async destroy(){await this._strategy.destroy()}get config(){return this._config}}var q0=(X,Z)=>{if(X.response.addHeaders({"RateLimit-Limit":String(Z.limit),"RateLimit-Remaining":String(Z.remaining),"RateLimit-Reset":String(Math.ceil(Z.resetTime/1000))}),!Z.allowed){let $=p9(Z.resetTime);X.response.addHeaders({"Retry-After":String($)})}};var N0={slidingWindowCounter:"sliding-window-counter"},g9={memory:"memory",redis:"redis"};class p{algorithm;store;window;max;standardHeaders;skipSuccessfulRequests;skipFailedRequests;keyGenerator;handler;constructor(X,Z){this._validateConfig(X,Z??M),this.algorithm=X?.algorithm??N0.slidingWindowCounter,this.store=X?.store??{type:"memory"},this.window=D(X?.window??"15m"),this.max=X?.max??100,this.standardHeaders=X?.standardHeaders??!0,this.skipSuccessfulRequests=X?.skipSuccessfulRequests??!1,this.skipFailedRequests=X?.skipFailedRequests??!1,this.keyGenerator=X?.keyGenerator??s9,this.handler=X?.handler??c9}_validateConfig(X,Z){if(!X)return;l9(X),d9(X,Z)}get config(){return{algorithm:this.algorithm,window:this.window,max:this.max,standardHeaders:this.standardHeaders,skipSuccessfulRequests:this.skipSuccessfulRequests,skipFailedRequests:this.skipFailedRequests,keyGenerator:this.keyGenerator,handler:this.handler}}}var l9=(X)=>{if(X.max!==void 0){if(typeof X.max!=="number"||isNaN(X.max))throw Error("rateLimit.max must be a number");if(X.max<1)throw Error("rateLimit.max must be at least 1 request per window");if(!Number.isInteger(X.max))throw Error("rateLimit.max must be an integer (no decimals)")}if(X.window!==void 0)if(typeof X.window==="string"){if(!/^(?<value>\d+)(?<unit>s|m|h|d)$/.test(X.window))throw Error(`rateLimit.window must be a valid time string (e.g., '30s', '15m', '2h', '1d') or milliseconds as a number. Received: "${X.window}"`);let $=D(X.window);if($<1000)throw Error(`rateLimit.window must be at least 1000ms (1 second). Received: ${X.window} (${$}ms). Very short time windows can cause performance issues and inaccurate rate limiting.`)}else if(typeof X.window==="number"){if(isNaN(X.window))throw Error("rateLimit.window must be a valid number when using milliseconds");if(X.window<1000)throw Error(`rateLimit.window must be at least 1000ms (1 second). Received: ${X.window}ms. Very short time windows can cause performance issues and inaccurate rate limiting.`);if(!Number.isInteger(X.window))throw Error("rateLimit.window must be an integer when using milliseconds (no decimals)")}else throw Error('rateLimit.window must be a time string (e.g., "15m") or milliseconds as a number')},d9=(X,Z)=>{if(X.enabled===!1)Z.warn("[SECURITY WARNING] Rate limiting is disabled. This removes DoS protection from your API. Only disable for development or if you have external rate limiting (e.g., API gateway, CDN).");if(X.max!==void 0&&X.max>1e4)Z.warn(`[SECURITY WARNING] rateLimit.max is set to ${X.max} requests. Very high rate limits may not provide adequate DoS protection. Consider if this limit is necessary for your use case.`);if(X.window!==void 0){let $=D(X.window),Q=3600000;if($>3600000){let J=Math.round($/3600000);Z.warn(`[SECURITY WARNING] rateLimit.window is set to ${typeof X.window==="string"?X.window:`${$}ms`} (${J}h). Very long time windows may allow burst attacks before limits are enforced. Consider shorter windows for better protection.`)}}if(X.window!==void 0){let $=D(X.window);if($<1e4&&X.max!==void 0&&X.max>100)Z.warn(`[PERFORMANCE WARNING] rateLimit.window is set to ${typeof X.window==="string"?X.window:`${$}ms`} with max ${X.max} requests. Very short time windows with high request counts can cause performance overhead. Consider increasing the window or decreasing max.`)}},c9=(X)=>{return X.response.setStatusCode(z.tooManyRequests),{success:!1,message:"Yinz are sending too many requests. Slow down, jagoff!"}},s9=(X)=>X.request.ipAddress;var r9=(X)=>async(Z)=>{let $=new p(X),Q=new u($),J=await Q.check(Z);if(Q.config.standardHeaders)q0(Z,J);if(!J.allowed)return Q.config.handler(Z);return},W1=(X,Z)=>async($)=>{let Q=await X.check($);if(X.config.standardHeaders)q0($,Q);if(!Q.allowed)return Z?.($.request.ipAddress,$.request.path),X.config.handler($);return};import{createHmac as K1}from"node:crypto";class f{enabled;secret;signed;defaults;constructor(X,Z){this._validateConfig(X,Z??M),this.enabled=X?.enabled??!1,this.secret=X?.secret,this.signed=X?.signed,this.defaults=X?.defaults}_validateConfig(X,Z){if(!X)return;i9(X),Q6(X,Z)}get config(){let X={enabled:this.enabled};if(this.secret!==void 0)X.secret=this.secret;if(this.signed!==void 0)X.signed=this.signed;if(this.defaults!==void 0)X.defaults=this.defaults;return X}}var i9=(X)=>{if(X.secret!==void 0){if(typeof X.secret!=="string")throw Error("cookieParser.secret must be a string");if(X.secret.length<32)throw Error("cookieParser.secret must be at least 32 characters for security. Use a strong, random secret stored in environment variables.")}if(X.signed!==void 0){if(!Array.isArray(X.signed))throw Error("cookieParser.signed must be an array of cookie names");for(let Z of X.signed){if(typeof Z!=="string")throw Error("cookieParser.signed must be an array of strings (cookie names)");if(Z.length===0)throw Error("cookieParser.signed cannot contain empty cookie names")}}if(X.defaults)a9(X.defaults)},a9=(X)=>{if(!X)return;n9(X),o9(X),t9(X),e9(X),X6(X),Z6(X),$6(X)},n9=(X)=>{if(!X||X.maxAge===void 0)return;if(typeof X.maxAge==="string"){if(!/^(?<value>\d+)(?<unit>ms|s|m|h|d)$/.test(X.maxAge))throw Error(`cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: "${X.maxAge}"`);return}if(typeof X.maxAge!=="number"||isNaN(X.maxAge))throw Error(`cookieParser.defaults.maxAge must be a valid time string (e.g., '30s', '15m', '2h', '1d') or seconds as a number. Received: "${X.maxAge}"`);if(X.maxAge<0)throw Error("cookieParser.defaults.maxAge must be 0 or greater");if(!Number.isInteger(X.maxAge))throw Error("cookieParser.defaults.maxAge must be an integer (no decimals)")},o9=(X)=>{if(!X||X.secure===void 0)return;if(typeof X.secure!=="boolean")throw Error("cookieParser.defaults.secure must be a boolean")},t9=(X)=>{if(!X||X.httpOnly===void 0)return;if(typeof X.httpOnly!=="boolean")throw Error("cookieParser.defaults.httpOnly must be a boolean")},e9=(X)=>{if(!X||X.sameSite===void 0)return;if(!["strict","lax","none"].includes(X.sameSite))throw Error('cookieParser.defaults.sameSite must be one of: "strict", "lax", "none"')},X6=(X)=>{if(!X)return;if(X.domain===void 0)return;if(typeof X.domain!=="string")throw Error("cookieParser.defaults.domain must be a string");if(X.domain.length===0)throw Error("cookieParser.defaults.domain cannot be empty")},Z6=(X)=>{if(!X||X.path===void 0)return;if(typeof X.path!=="string")throw Error("cookieParser.defaults.path must be a string");if(!X.path.startsWith("/"))throw Error('cookieParser.defaults.path must start with "/"')},$6=(X)=>{if(!X||X.expires===void 0)return;if(!(X.expires instanceof Date))throw Error("cookieParser.defaults.expires must be a Date object")},Q6=(X,Z)=>{if(X.enabled===!1)Z.warn("[SECURITY WARNING] Cookie parser is disabled. Cookies will not be parsed or validated. Only disable for special use cases.");let $=!1;if($&&!X.secret)Z.warn("[SECURITY WARNING] No secret provided for cookie signing in production. Cookies will not be signed and cannot be validated for tampering. Consider using a secret.");if($&&X.defaults?.secure===!1)Z.warn("[SECURITY WARNING] cookieParser.defaults.secure is false in production. Cookies will be sent over HTTP, which is insecure. Always use secure cookies in production.");if($&&X.defaults?.httpOnly===!1)Z.warn("[SECURITY WARNING] cookieParser.defaults.httpOnly is false in production. Cookies will be accessible to JavaScript, which increases XSS risk. Only disable httpOnly for cookies that need JavaScript access.");if(X.defaults?.sameSite==="none"&&X.defaults.secure!==!0)Z.warn("[SECURITY WARNING] SameSite=none requires secure=true. Browsers will reject cookies with SameSite=none without secure flag.")};class D0{_config;constructor(X){this._config=new f(X)}parse(X){let Z=new Map;if(!X||typeof X!=="string")return Z;let $=X.split(";");for(let Q of $){let J=Q.trim();if(!J)continue;let Y=J.indexOf("=");if(Y===-1)continue;let j=J.slice(0,Y).trim(),W=J.slice(Y+1).trim();if(j&&W)try{Z.set(j,decodeURIComponent(W))}catch(K){continue}}return Z}set(X,Z,$){let Q=encodeURIComponent(X),J=encodeURIComponent(Z),Y=this._mergeOptions($),j=[`${Q}=${J}`];if(Y.expires)j.push(`Expires=${Y.expires.toUTCString()}`);if(Y.maxAge!==void 0)j.push(`Max-Age=${Y.maxAge}`);if(Y.domain)j.push(`Domain=${Y.domain}`);if(Y.path)j.push(`Path=${Y.path}`);if(Y.secure)j.push("Secure");if(Y.httpOnly)j.push("HttpOnly");if(Y.sameSite)j.push(`SameSite=${Y.sameSite.charAt(0).toUpperCase()+Y.sameSite.slice(1)}`);return j.join("; ")}sign(X,Z){if(!this._config.secret)throw Error("Cannot sign cookie: no secret configured");let $=K1("sha256",this._config.secret).update(`${X}=${Z}`).digest("base64url").replace(/=/g,"");return`${Z}.${$}`}unsign(X,Z){if(!this._config.secret)throw Error("Cannot unsign cookie: no secret configured");let $=Z.lastIndexOf(".");if($===-1)return!1;let Q=Z.slice(0,$),J=Z.slice($+1);return K1("sha256",this._config.secret).update(`${X}=${Q}`).digest("base64url").replace(/=/g,"")===J?Q:!1}_mergeOptions(X){let Z={...this._config.defaults,...X};if(Z.maxAge!==void 0&&typeof Z.maxAge==="string")Z.maxAge=D(Z.maxAge)/1000;return Z}get config(){return this._config}shouldSign(X){if(!this._config.secret)return!1;if(this._config.signed===void 0||this._config.signed.length===0)return!0;return this._config.signed.includes(X)}}var U1=(X)=>(Z)=>{let $=new D0(X),Q=Z.request.headers.cookie,J=$.parse(Q??"");if(Z.request.cookies=J,Z.request.signedCookies=new Map,$.config.secret){for(let[Y,j]of J.entries())if($.shouldSign(Y)){let W=$.unsign(Y,j);if(W!==!1)Z.request.signedCookies.set(Y,W)}}Z.cookies={set:(Y,j,W)=>{let K=j;if($.shouldSign(Y))K=$.sign(Y,j);let U=$.set(Y,K,W);Z.response.addHeaders({"Set-Cookie":U})},sign:(Y,j)=>{if(!$.config.secret)throw Error("Cannot sign cookie: no secret configured");return $.sign(Y,j)},unsign:(Y,j)=>{if(!$.config.secret)throw Error("Cannot unsign cookie: no secret configured");return $.unsign(Y,j)}};return};class B0{config;_normalizedOrigins;constructor(X){this.config=X;if(X.origin==="*"&&X.credentials)throw Error(`CORS Configuration Error: Cannot use origin: "*" with credentials: true. The CORS specification forbids this combination as it creates security vulnerabilities. Choose one of these solutions:
|
|
27
27
|
1) Set credentials: false (recommended for public APIs)
|
|
28
28
|
2) Use specific origins instead of "*" (e.g., origin: ["https://example.com"])
|
|
29
|
-
3) Disable CORS entirely (enabled: false)`);if(Array.isArray(Q.origin))this._normalizedOrigins=new Set(Q.origin.map((X)=>X.toLowerCase()));else this._normalizedOrigins=null}handle(Q){if(Q.request.method==="OPTIONS")return this._handlePreflightRequest(Q);return void this._handleActualRequest(Q)}_handlePreflightRequest(Q){let X=Q.request.headers.origin?.toLowerCase()??"";if(!this._isOriginAllowed(X,Q))return Q.response.setStatusCode(403),{error:"CORS: Origin not allowed",origin:Q.request.headers.origin};Q.response.setStatusCode(this.config.optionsSuccessStatus);let $=this._resolveAllowedOrigin(Q);if(this._setCommonCorsHeaders(Q,$),Q._response._setHeadersIfNotSet({[L.accessControlAllowMethods]:this.config.methods.join(", "),[L.accessControlAllowHeaders]:typeof this.config.allowedHeaders==="string"?this.config.allowedHeaders:this.config.allowedHeaders.join(", "),[L.accessControlExposeHeaders]:this.config.exposedHeaders.join(", "),[L.accessControlMaxAge]:this.config.maxAge.toString()}),this.config.preflightContinue)return;return""}_handleActualRequest(Q){let X=Q.request.headers.origin?.toLowerCase()??"";if(this._isOriginAllowed(X,Q)){let $=this._resolveAllowedOrigin(Q);this._setCommonCorsHeaders(Q,$)}return}_setCommonCorsHeaders(Q,X){Q._response._setHeadersIfNotSet({[L.accessControlAllowOrigin]:X,[L.accessControlAllowCredentials]:this.config.credentials?"true":"false"})}_resolveAllowedOrigin(Q){if(this.config.origin==="*")return"*";let X=Q.request.headers.origin;if(X)return X;if(typeof this.config.origin==="string")return this.config.origin;if(Array.isArray(this.config.origin)&&this.config.origin.length>0){let[Z]=this.config.origin;return Z??"null"}return"null"}_isOriginAllowed(Q,X){if(this.config.origin==="*")return!0;if(typeof this.config.origin==="function")return Boolean(this.config.origin(Q,X?.request));if(typeof this.config.origin==="string")return Q===this.config.origin.toLowerCase();if(this._normalizedOrigins)return this._normalizedOrigins.has(Q);if(this.config.origin instanceof RegExp)return this.config.origin.test(Q);return!1}}var q1=(Q)=>{let X=new G1(Q);return(Z)=>X.handle(Z)};class v{static getDefaults(){return{enabled:!1}}static getEnabledDefaults(){return{enabled:!0,origin:"*",methods:["GET","POST","PUT","DELETE","PATCH","OPTIONS"],allowedHeaders:"*",exposedHeaders:[],credentials:!1,maxAge:86400,preflightContinue:!1,optionsSuccessStatus:B.noContent}}static merge(Q){if(!Q||!Q.enabled)return v.getDefaults();let X=v.getEnabledDefaults();return{enabled:!0,origin:Q.origin??X.origin,methods:Q.methods??X.methods,allowedHeaders:Q.allowedHeaders??X.allowedHeaders,exposedHeaders:Q.exposedHeaders??X.exposedHeaders,credentials:Q.credentials??X.credentials,maxAge:Q.maxAge??X.maxAge,preflightContinue:Q.preflightContinue??X.preflightContinue,optionsSuccessStatus:Q.optionsSuccessStatus??X.optionsSuccessStatus}}static validate(Q){if(!Q.enabled)return;if(Q.origin==="*"&&Q.credentials)throw Error('CORS Security Error: origin: "*" with credentials: true is forbidden by CORS spec and creates security vulnerabilities. Use specific origins instead.');if(!Q.origin)throw Error("CORS Configuration Error: origin is required when CORS is enabled.");if(!Array.isArray(Q.methods)||Q.methods.length===0)throw Error("CORS Configuration Error: methods must be a non-empty array.");if(!Array.isArray(Q.exposedHeaders))throw Error("CORS Configuration Error: exposedHeaders must be an array.");if(typeof Q.maxAge!=="number"||Q.maxAge<0)throw Error("CORS Configuration Error: maxAge must be a non-negative number.")}}var u0=/[\r\n\x00-\x1f\x7f\u200f\u202a-\u202e\u2066-\u2069]/g,T=(Q,X=256)=>Q.replace(u0,"").slice(0,X);var p0={slowRequest:["that's draggin' n'at","slower than a bus on Forbes Ave","yinz might wanna optimize that","what a jagoff response time!","slowin' down a bit there"],largePayload:["that's a yuge payload n'at","bigger than a Primanti's sandwich","that's a lot of data, yinz","hefty response there"],memory:["heap's gettin' full n'at","memory usage update","keepin' an eye on the heap"],eventLoop:["event loop's laggin' n'at","the loop's stuck in traffic on 376","yinz got a blocking operation"],rateLimit:["somebody's hammerin' the server n'at","slow down there, jagoff","rate limit hit"]},f0=(Q)=>{let X=p0[Q];return X[Math.floor(Math.random()*X.length)]??""};class F1{_config;_personality;_memoryTimer;_eventLoopTimer;_log;_destroyed=!1;constructor(Q,X){this._config=g0(Q),this._personality=X,this._log=R({level:"info",prefix:"DIAGNOSTIC",personality:!1})}_phrase(Q){return this._personality?` — ${f0(Q)}`:""}checkRequest({duration:Q,reqBytes:X,resBytes:Z,method:$,path:W}){if(this._config.slowRequestsMs!==!1&&Q>this._config.slowRequestsMs)this._log.warn(`\uD83D\uDC0C Slow request: ${$} ${W} took ${Q}ms (threshold: ${this._config.slowRequestsMs}ms)${this._phrase("slowRequest")}`);if(this._config.largeResponsesBytes!==!1&&Z>this._config.largeResponsesBytes)this._log.warn(`\uD83D\uDCE6 Large response: ${$} ${W} ${Z} bytes (threshold: ${this._config.largeResponsesBytes} bytes)${this._phrase("largePayload")}`);if(this._config.largeRequestsBytes!==!1&&X>this._config.largeRequestsBytes)this._log.warn(`\uD83D\uDCE6 Large request: ${$} ${W} ${X} bytes (threshold: ${this._config.largeRequestsBytes} bytes)${this._phrase("largePayload")}`)}onRateLimitHit(Q,X){if(!this._config.rateLimits)return;this._log.warn(`\uD83D\uDEAB Rate limit hit: ${Q} on ${T(X)}${this._phrase("rateLimit")}`)}start(){this._startMemoryMonitor(),this._startEventLoopMonitor()}destroy(){if(this._destroyed=!0,this._memoryTimer)clearInterval(this._memoryTimer),this._memoryTimer=void 0;if(this._eventLoopTimer)clearTimeout(this._eventLoopTimer),this._eventLoopTimer=void 0}hasAnyEnabled(){return this._config.slowRequestsMs!==!1||this._config.largeResponsesBytes!==!1||this._config.largeRequestsBytes!==!1||this._config.memoryIntervalMs!==!1||this._config.eventLoopThresholdMs!==!1||this._config.rateLimits}_startMemoryMonitor(){if(this._config.memoryIntervalMs===!1)return;let Q=this._config.memoryIntervalMs;this._memoryTimer=setInterval(()=>{let X=process.memoryUsage(),Z=($)=>($/1024/1024).toFixed(1);this._log.info(`\uD83D\uDCBE Heap: ${Z(X.heapUsed)}MB / ${Z(X.heapTotal)}MB | RSS: ${Z(X.rss)}MB | External: ${Z(X.external)}MB${this._phrase("memory")}`)},Q),this._memoryTimer.unref()}_startEventLoopMonitor(){if(this._config.eventLoopThresholdMs===!1)return;let Q=this._config.eventLoopThresholdMs,X=1000,Z=()=>{if(this._destroyed)return;let $=Date.now();this._eventLoopTimer=setTimeout(()=>{if(this._destroyed)return;let W=Date.now()-$-X;if(W>Q)this._log.warn(`⏱️ Event loop lag: ${W}ms (threshold: ${Q}ms)${this._phrase("eventLoop")}`);Z()},X),this._eventLoopTimer.unref()};Z()}}var g0=(Q)=>({slowRequestsMs:Q.slowRequests===!1?!1:N(Q.slowRequests),largeResponsesBytes:Q.largeResponses===!1?!1:_(Q.largeResponses),largeRequestsBytes:Q.largeRequests===!1?!1:_(Q.largeRequests),memoryIntervalMs:Q.memory===!1?!1:N(Q.memory),eventLoopThresholdMs:Q.eventLoop===!1?!1:N(Q.eventLoop),rateLimits:Q.rateLimits});var d0=65536;class t1 extends W1{_isListening=!1;_server;_globalRateLimiter;_diagnostics;_maxBufferSize;_accessLog;_accessLogEnabled=!1;constructor(Q){super(Q);this._maxBufferSize=Math.max(this._configuration.bodyParser.json.maxSize,this._configuration.bodyParser.urlEncoded.maxSize,this._configuration.bodyParser.fileUploads.maxTotalSize)+d0,this._configureLogging();let X=new x(Q?.rateLimit,this._log);if(Q?.rateLimit?.enabled){this._globalRateLimiter=new C(X);let Z=this._diagnostics?(W,J)=>this._diagnostics?.onRateLimitHit(W,J):void 0,$=i1(this._globalRateLimiter,Z);this.beforeAll([$])}if(Q?.cookieParser?.enabled){let Z=new y(Q.cookieParser,this._log),$=o1(Z.config);this.beforeAll([$])}if(Q?.cors?.enabled){let Z=v.merge(Q.cors);v.validate(Z);let $=q1(Z);this.beforeRouting([$])}if(this._configuration.gracefulShutdownTimeout){let Z=N(this._configuration.gracefulShutdownTimeout);if(Z>0)this._setupGracefulShutdown(Z)}}get log(){return this._log}_configureLogging(){let Q=this._configuration.logging,X=Q.logger;if(X&&I in X)X=X[I].logger??void 0;if(this._log=R({level:Q.level,prefix:Q.prefix,personality:Q.personality,logger:X}),this._hooks.setLogger(this._log),Q.requests)this._accessLog=R({...l1,level:"info",logger:Q.accessLogger}),this._accessLogEnabled=!0;let Z=new F1(Q.diagnostics,Q.personality);if(Z.hasAnyEnabled())this._diagnostics=Z,this._diagnostics.start()}_setupServer(Q,X,Z){if(!this._server)return;this._server.on("error",($)=>{this._log.error(`YinzerFlow server error at ${this._configuration.host}:${this._configuration.port} - ${$.message}`),this._server?.close(),delete this._server,X($)}),this._server.on("listening",()=>{this._isListening=!0,this._log.info(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is up and running`),Q()}),this._server.on("connection",($)=>{this._handleConnection($,Z)})}async _processRequest({data:Q,socket:X,requestHandler:Z,clientAddress:$}){let W=Date.now(),J=new o(Q,this,$);if(await Z.handle(J),!X.destroyed)X.write(J._response._stringBody),X.end();let Y=Date.now()-W;if(this._accessLogEnabled||this._diagnostics){let K=Buffer.byteLength(J._response._stringBody,"utf8"),U=T(J.request.method),j=T(J.request.path);if(this._accessLogEnabled)this._accessLog?.info(`${d1(J._response._statusCode)} ${$} "${U} ${j} ${J.request.protocol}" ${J._response._statusCode} ${K}bytes "${T(J.request.headers.referer??"-")}" "${T(J.request.headers["user-agent"]??"-")}" ${Y}ms`);this._diagnostics?.checkRequest({duration:Y,reqBytes:Q.length,resBytes:K,method:U,path:j})}}_handleRequestError(Q,X,Z){let $=Q instanceof Error?Q.message:"Unknown error";if(this._log.error(`Request processing error from ${X}: ${$}`,Q),!Z.destroyed)Z.destroy()}_dispatchRequest({data:Q,socket:X,requestHandler:Z,clientAddress:$}){this._processRequest({data:Q,socket:X,requestHandler:Z,clientAddress:$}).catch((W)=>this._handleRequestError(W,$,X))}_rejectOversizedRequest({socket:Q,clientAddress:X,totalLength:Z,headersParsed:$}){if($){let W=JSON.stringify({error:"Payload too large",maxSize:this._maxBufferSize,received:Z});Q.write(`HTTP/1.1 413 Payload Too Large\r
|
|
29
|
+
3) Disable CORS entirely (enabled: false)`);if(Array.isArray(X.origin))this._normalizedOrigins=new Set(X.origin.map((Z)=>Z.toLowerCase()));else this._normalizedOrigins=null}handle(X){if(X.request.method==="OPTIONS")return this._handlePreflightRequest(X);return void this._handleActualRequest(X)}_handlePreflightRequest(X){let Z=X.request.headers.origin?.toLowerCase()??"";if(!this._isOriginAllowed(Z,X))return X.response.setStatusCode(403),{error:"CORS: Origin not allowed",origin:X.request.headers.origin};X.response.setStatusCode(this.config.optionsSuccessStatus);let Q=this._resolveAllowedOrigin(X);if(this._setCommonCorsHeaders(X,Q),X._response._setHeadersIfNotSet({[S.accessControlAllowMethods]:this.config.methods.join(", "),[S.accessControlAllowHeaders]:typeof this.config.allowedHeaders==="string"?this.config.allowedHeaders:this.config.allowedHeaders.join(", "),[S.accessControlExposeHeaders]:this.config.exposedHeaders.join(", "),[S.accessControlMaxAge]:this.config.maxAge.toString()}),this.config.preflightContinue)return;return""}_handleActualRequest(X){let Z=X.request.headers.origin?.toLowerCase()??"";if(this._isOriginAllowed(Z,X)){let Q=this._resolveAllowedOrigin(X);this._setCommonCorsHeaders(X,Q)}return}_setCommonCorsHeaders(X,Z){X._response._setHeadersIfNotSet({[S.accessControlAllowOrigin]:Z,[S.accessControlAllowCredentials]:this.config.credentials?"true":"false"})}_resolveAllowedOrigin(X){if(this.config.origin==="*")return"*";let Z=X.request.headers.origin;if(Z)return Z;if(typeof this.config.origin==="string")return this.config.origin;if(Array.isArray(this.config.origin)&&this.config.origin.length>0){let[$]=this.config.origin;return $??"null"}return"null"}_isOriginAllowed(X,Z){if(this.config.origin==="*")return!0;if(typeof this.config.origin==="function")return Boolean(this.config.origin(X,Z?.request));if(typeof this.config.origin==="string")return X===this.config.origin.toLowerCase();if(this._normalizedOrigins)return this._normalizedOrigins.has(X);if(this.config.origin instanceof RegExp)return this.config.origin.test(X);return!1}}var F0=(X)=>{let Z=new B0(X);return($)=>Z.handle($)};class y{static getDefaults(){return{enabled:!1}}static getEnabledDefaults(){return{enabled:!0,origin:"*",methods:["GET","POST","PUT","DELETE","PATCH","OPTIONS"],allowedHeaders:"*",exposedHeaders:[],credentials:!1,maxAge:86400,preflightContinue:!1,optionsSuccessStatus:z.noContent}}static merge(X){if(!X||!X.enabled)return y.getDefaults();let Z=y.getEnabledDefaults();return{enabled:!0,origin:X.origin??Z.origin,methods:X.methods??Z.methods,allowedHeaders:X.allowedHeaders??Z.allowedHeaders,exposedHeaders:X.exposedHeaders??Z.exposedHeaders,credentials:X.credentials??Z.credentials,maxAge:X.maxAge??Z.maxAge,preflightContinue:X.preflightContinue??Z.preflightContinue,optionsSuccessStatus:X.optionsSuccessStatus??Z.optionsSuccessStatus}}static validate(X){if(!X.enabled)return;if(X.origin==="*"&&X.credentials)throw Error('CORS Security Error: origin: "*" with credentials: true is forbidden by CORS spec and creates security vulnerabilities. Use specific origins instead.');if(!X.origin)throw Error("CORS Configuration Error: origin is required when CORS is enabled.");if(!Array.isArray(X.methods)||X.methods.length===0)throw Error("CORS Configuration Error: methods must be a non-empty array.");if(!Array.isArray(X.exposedHeaders))throw Error("CORS Configuration Error: exposedHeaders must be an array.");if(typeof X.maxAge!=="number"||X.maxAge<0)throw Error("CORS Configuration Error: maxAge must be a non-negative number.")}}var J6=/[\r\n\x00-\x1f\x7f\u200f\u202a-\u202e\u2066-\u2069]/g,v=(X,Z=256)=>X.replace(J6,"").slice(0,Z);var Y6={slowRequest:["that's draggin' n'at","slower than a bus on Forbes Ave","yinz might wanna optimize that","what a jagoff response time!","slowin' down a bit there"],largePayload:["that's a yuge payload n'at","bigger than a Primanti's sandwich","that's a lot of data, yinz","hefty response there"],memory:["heap's gettin' full n'at","memory usage update","keepin' an eye on the heap"],eventLoop:["event loop's laggin' n'at","the loop's stuck in traffic on 376","yinz got a blocking operation"],rateLimit:["somebody's hammerin' the server n'at","slow down there, jagoff","rate limit hit"]},j6=(X)=>{let Z=Y6[X];return Z[Math.floor(Math.random()*Z.length)]??""};class E0{_config;_personality;_memoryTimer;_eventLoopTimer;_log;_destroyed=!1;constructor(X,Z){this._config=W6(X),this._personality=Z,this._log=_({level:"info",prefix:"DIAGNOSTIC",personality:!1})}_phrase(X){return this._personality?` — ${j6(X)}`:""}checkRequest({duration:X,reqBytes:Z,resBytes:$,method:Q,path:J}){if(this._config.slowRequestsMs!==!1&&X>this._config.slowRequestsMs)this._log.warn(`\uD83D\uDC0C Slow request: ${Q} ${J} took ${X}ms (threshold: ${this._config.slowRequestsMs}ms)${this._phrase("slowRequest")}`);if(this._config.largeResponsesBytes!==!1&&$>this._config.largeResponsesBytes)this._log.warn(`\uD83D\uDCE6 Large response: ${Q} ${J} ${$} bytes (threshold: ${this._config.largeResponsesBytes} bytes)${this._phrase("largePayload")}`);if(this._config.largeRequestsBytes!==!1&&Z>this._config.largeRequestsBytes)this._log.warn(`\uD83D\uDCE6 Large request: ${Q} ${J} ${Z} bytes (threshold: ${this._config.largeRequestsBytes} bytes)${this._phrase("largePayload")}`)}onRateLimitHit(X,Z){if(!this._config.rateLimits)return;this._log.warn(`\uD83D\uDEAB Rate limit hit: ${X} on ${v(Z)}${this._phrase("rateLimit")}`)}start(){this._startMemoryMonitor(),this._startEventLoopMonitor()}destroy(){if(this._destroyed=!0,this._memoryTimer)clearInterval(this._memoryTimer),this._memoryTimer=void 0;if(this._eventLoopTimer)clearTimeout(this._eventLoopTimer),this._eventLoopTimer=void 0}hasAnyEnabled(){return this._config.slowRequestsMs!==!1||this._config.largeResponsesBytes!==!1||this._config.largeRequestsBytes!==!1||this._config.memoryIntervalMs!==!1||this._config.eventLoopThresholdMs!==!1||this._config.rateLimits}_startMemoryMonitor(){if(this._config.memoryIntervalMs===!1)return;let X=this._config.memoryIntervalMs;this._memoryTimer=setInterval(()=>{let Z=process.memoryUsage(),$=(Q)=>(Q/1024/1024).toFixed(1);this._log.info(`\uD83D\uDCBE Heap: ${$(Z.heapUsed)}MB / ${$(Z.heapTotal)}MB | RSS: ${$(Z.rss)}MB | External: ${$(Z.external)}MB${this._phrase("memory")}`)},X),this._memoryTimer.unref()}_startEventLoopMonitor(){if(this._config.eventLoopThresholdMs===!1)return;let X=this._config.eventLoopThresholdMs,Z=1000,$=()=>{if(this._destroyed)return;let Q=Date.now();this._eventLoopTimer=setTimeout(()=>{if(this._destroyed)return;let J=Date.now()-Q-Z;if(J>X)this._log.warn(`⏱️ Event loop lag: ${J}ms (threshold: ${X}ms)${this._phrase("eventLoop")}`);$()},Z),this._eventLoopTimer.unref()};$()}}var W6=(X)=>({slowRequestsMs:X.slowRequests===!1?!1:D(X.slowRequests),largeResponsesBytes:X.largeResponses===!1?!1:k(X.largeResponses),largeRequestsBytes:X.largeRequests===!1?!1:k(X.largeRequests),memoryIntervalMs:X.memory===!1?!1:D(X.memory),eventLoopThresholdMs:X.eventLoop===!1?!1:D(X.eventLoop),rateLimits:X.rateLimits});import{createHash as U6}from"crypto";var F={continuation:0,text:1,binary:2,close:8,ping:9,pong:10},H={normal:1000,goingAway:1001,protocolError:1002,unsupported:1003,noStatus:1005,abnormal:1006,invalidPayload:1007,policyViolation:1008,tooLarge:1009,missingExtension:1010,internalError:1011},N={connecting:0,open:1,closing:2,closed:3},K6={buffer:"buffer",drop:"drop"},G1="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";var M1=(X)=>{let Z=X.toLowerCase();return Z.includes("upgrade: websocket")&&Z.includes("connection:")&&Z.includes("upgrade")},G6=(X)=>{let Z=/^GET\s+(?<path>\S+)\s+HTTP\/1\.1$/i.exec(X);if(!Z?.groups?.path)return null;let $=Z.groups.path,Q=$.indexOf("?"),J=Q>=0?$.substring(0,Q):$,Y={};if(Q>=0){let j=$.substring(Q+1);for(let W of j.split("&")){let K=W.indexOf("=");if(K>=0)Y[decodeURIComponent(W.substring(0,K))]=decodeURIComponent(W.substring(K+1));else Y[decodeURIComponent(W)]=""}}return{path:J,query:Y}},M6=(X)=>{let Z=new Map;for(let $=1;$<X.length;$++){let Q=X[$];if(!Q)continue;if(Q.indexOf(":")<0)continue;let[Y="",...j]=Q.split(":");Z.set(Y.trim().toLowerCase(),j.join(":").trim())}return Z},V6=(X)=>{try{if(Buffer.from(X,"base64").length!==16)return{valid:!1,reason:"Sec-WebSocket-Key must decode to 16 bytes"}}catch{return{valid:!1,reason:"Sec-WebSocket-Key is not valid base64"}}return{valid:!0}},V1=(X)=>{let Z=X.split(`\r
|
|
30
|
+
`),[$]=Z;if(!$)return{valid:!1,reason:"Empty request"};let Q=G6($);if(!Q)return{valid:!1,reason:"Invalid request line — must be GET with HTTP/1.1"};let{path:J,query:Y}=Q,j=M6(Z),W=j.get("sec-websocket-key");if(!W)return{valid:!1,reason:"Missing Sec-WebSocket-Key header"};let K=V6(W);if(!K.valid)return K;let U=j.get("sec-websocket-version");if(U!=="13")return{valid:!1,reason:`Unsupported Sec-WebSocket-Version: ${U??"missing"}`};let G=j.get("origin"),B=j.get("sec-websocket-protocol"),E=B?B.split(",").map((T)=>T.trim()).filter(Boolean):[];return{valid:!0,key:W,origin:G,path:J,query:Y,protocols:E}},q1=(X)=>U6("sha1").update(X+G1).digest("base64"),N1=(X,Z)=>{let $=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${X}`];if(Z)$.push(`Sec-WebSocket-Protocol: ${Z}`);return $.join(`\r
|
|
31
|
+
`).concat(`\r
|
|
32
|
+
\r
|
|
33
|
+
`)};var q6=F.close,N6=(X,Z)=>{for(let $=0;$<X.length;$++)X[$]=X[$]^Z[$&3]},D1=(X,Z)=>{let $=X.length-Z;if($<2)return null;let Q=X[Z],J=X[Z+1],Y=(Q&128)!==0,j=Q&15,W=(J&128)!==0,K=J&127,U=2;if(K===126){if($<4)return null;K=X.readUInt16BE(Z+2),U=4}else if(K===127){if($<10)return null;if(X.readUInt32BE(Z+2)!==0)throw Error("WebSocket frame payload too large (>4GB not supported)");K=X.readUInt32BE(Z+6),U=10}if(W)U+=4;let G=U+K;if($<G)return null;let B=W?(()=>{let E=Z+U-4,T=X.subarray(E,E+4),w=Buffer.from(X.subarray(Z+U,Z+G));return N6(w,T),w})():Buffer.from(X.subarray(Z+U,Z+G));return{frame:{fin:Y,opcode:j,masked:W,payloadLength:K,payload:B},bytesConsumed:G}},x=(X,Z,$=!0)=>{let Q=Z.length,J=2,Y=void 0;if(Q>=126&&Q<65536)J=4,Y=(W)=>{W.writeUInt16BE(Q,2)};else if(Q>=65536)J=10,Y=(W)=>{W.writeUInt32BE(0,2),W.writeUInt32BE(Q,6)};let j=Buffer.allocUnsafe(J+Q);if(j[0]=($?128:0)|X,Q<126)j[1]=Q;else if(Q<65536)j[1]=126;else j[1]=127;if(Y)Y(j);return Z.copy(j,J),j},z0=(X,Z)=>{let $=Z?(()=>{let Q=Buffer.from(Z,"utf8"),J=Math.min(Q.length,123),Y=Buffer.allocUnsafe(2+J);return Y.writeUInt16BE(X,0),Q.copy(Y,2,0,J),Y})():(()=>{let Q=Buffer.allocUnsafe(2);return Q.writeUInt16BE(X,0),Q})();return x(q6,$)};class A0{_readyState=N.open;_socket;_data;_handlers;_options;_remoteAddress;_receiveBuffer=Buffer.alloc(0);_fragmentBuffers=[];_fragmentOpcode=0;_backpressured=!1;_bufferedAmount=0;_idleTimer;_channelManager;constructor(X,Z,$,Q){this._socket=X,this._data=Z,this._handlers=$,this._options=Q,this._remoteAddress=X.remoteAddress??"unknown",X.on("data",(J)=>this._onSocketData(J)),X.on("close",()=>this._onSocketClose()),X.on("error",(J)=>this._onSocketError(J)),X.on("drain",()=>this._onSocketDrain()),this._resetIdleTimeout()}get readyState(){return this._readyState}get data(){return this._data}get remoteAddress(){return this._remoteAddress}get bufferedAmount(){return this._bufferedAmount}setChannelManager(X){this._channelManager=X}send(X){if(this._readyState!==N.open)return;let Z=typeof X==="string",$=Z?Buffer.from(X,"utf8"):X,Q=Z?F.text:F.binary,J=x(Q,$);this._writeFrame(J)}sendRaw(X){if(this._readyState!==N.open)return;this._writeFrame(X)}close(X=H.normal,Z){if(this._readyState!==N.open)return;this._readyState=N.closing;let $=z0(X,Z);this._socket.write($),setTimeout(()=>{if(this._readyState!==N.closed){if(this._readyState=N.closed,!this._socket.destroyed)this._socket.destroy()}},5000)}ping(X){if(this._readyState!==N.open)return;let Z=X??Buffer.alloc(0),$=x(F.ping,Z);this._socket.write($)}subscribe(X){this._channelManager?.subscribe(this,X)}unsubscribe(X){this._channelManager?.unsubscribe(this,X)}publish(X,Z){return this._channelManager?.publish(X,Z,this)??0}isSubscribed(X){return this._channelManager?.isSubscribed(this,X)??!1}_onSocketData(X){if(this._readyState===N.closed)return;this._resetIdleTimeout(),this._receiveBuffer=this._receiveBuffer.length===0?Buffer.from(X):Buffer.concat([this._receiveBuffer,X]);let Z=0;while(Z<this._receiveBuffer.length){let $=D1(this._receiveBuffer,Z);if($===null)break;let{frame:Q,bytesConsumed:J}=$;if(Z+=J,!Q.masked){this.close(H.protocolError,"Client frames must be masked"),this._destroySocket();return}if(Q.payloadLength>this._options.maxPayloadLength){this.close(H.tooLarge,"Payload too large"),this._destroySocket();return}if(this._handleFrame(Q),this._readyState===N.closed)return}this._receiveBuffer=Z>0?this._receiveBuffer.subarray(Z):this._receiveBuffer}_onSocketClose(){if(this._clearIdleTimeout(),this._channelManager?.unsubscribeAll(this),this._readyState===N.closed)return;let X=this._readyState;if(this._readyState=N.closed,X!==N.closed)this._handlers.close?.(this._asPublic(),H.abnormal,"Connection closed unexpectedly")}_onSocketError(X){if(this._clearIdleTimeout(),this._handlers.error?.(this._asPublic(),X),!this._socket.destroyed)this._socket.destroy()}_onSocketDrain(){this._backpressured=!1,this._bufferedAmount=0,this._handlers.drain?.(this._asPublic())}_handleFrame(X){let{fin:Z,opcode:$,payload:Q}=X;if($>=8){if(!Z){this.close(H.protocolError,"Control frames must not be fragmented"),this._destroySocket();return}if(Q.length>125){this.close(H.protocolError,"Control frame payload exceeds 125 bytes"),this._destroySocket();return}this._handleControlFrame($,Q);return}if($===F.continuation){if(this._fragmentBuffers.push(Q),Z){let J=Buffer.concat(this._fragmentBuffers),Y=this._fragmentOpcode;this._fragmentBuffers=[],this._fragmentOpcode=0,this._deliverMessage(Y,J)}}else if(Z)this._deliverMessage($,Q);else this._fragmentOpcode=$,this._fragmentBuffers=[Q]}_handleControlFrame(X,Z){if(X===F.ping){if(this._readyState===N.open){let $=x(F.pong,Z);this._socket.write($)}return}if(X===F.pong)return;if(X===F.close){let $=Z.length>=2?Z.readUInt16BE(0):H.noStatus,Q=Z.length>2?Z.subarray(2).toString("utf8"):"";if(this._readyState===N.open){this._readyState=N.closed;let J=z0($,Q);this._socket.write(J),this._destroySocket(),this._handlers.close?.(this._asPublic(),$,Q)}else if(this._readyState===N.closing)this._readyState=N.closed,this._destroySocket(),this._handlers.close?.(this._asPublic(),$,Q)}}_deliverMessage(X,Z){if(this._readyState===N.closed)return;let $=X===F.binary,Q=$?Z:Z.toString("utf8");this._handlers.message?.(this._asPublic(),Q,$)}_writeFrame(X){if(this._backpressured){if(this._options.backpressure.strategy==="drop")return;if(this._bufferedAmount+=X.length,this._bufferedAmount>this._options.backpressure.limit){this.close(H.tooLarge,"Send buffer overflow"),this._destroySocket();return}}if(!this._socket.write(X))this._backpressured=!0,this._bufferedAmount+=X.length}_resetIdleTimeout(){if(this._clearIdleTimeout(),this._options.idleTimeout>0)this._idleTimer=setTimeout(()=>{this.close(H.normal,"Idle timeout"),setTimeout(()=>this._destroySocket(),1000)},this._options.idleTimeout*1000)}_clearIdleTimeout(){if(this._idleTimer)clearTimeout(this._idleTimer),this._idleTimer=void 0}_destroySocket(){if(this._clearIdleTimeout(),!this._socket.destroyed)this._socket.destroy()}_asPublic(){return this}}class I0{_channels=new Map;_subscriptions=new Map;subscribe(X,Z){let $=this._channels.get(Z);if(!$)$=new Set,this._channels.set(Z,$);$.add(X);let Q=this._subscriptions.get(X);if(!Q)Q=new Set,this._subscriptions.set(X,Q);Q.add(Z)}unsubscribe(X,Z){let $=this._channels.get(Z);if($){if($.delete(X),$.size===0)this._channels.delete(Z)}let Q=this._subscriptions.get(X);if(Q){if(Q.delete(Z),Q.size===0)this._subscriptions.delete(X)}}unsubscribeAll(X){let Z=this._subscriptions.get(X);if(!Z)return;for(let $ of Z){let Q=this._channels.get($);if(Q){if(Q.delete(X),Q.size===0)this._channels.delete($)}}this._subscriptions.delete(X)}publish(X,Z,$){let Q=this._channels.get(X);if(!Q||Q.size===0)return 0;let J=typeof Z==="string",Y=J?Buffer.from(Z,"utf8"):Z,j=J?F.text:F.binary,W=x(j,Y),K=0;for(let U of Q){if(U===$)continue;U.sendRaw(W),K++}return K}subscriberCount(X){return this._channels.get(X)?.size??0}isSubscribed(X,Z){return this._subscriptions.get(X)?.has(Z)??!1}channels(){return[...this._channels.keys()]}}class H0{_connectionCounts=new Map;validateOrigin(X,Z){if(Z.length===0)return!0;if(!X)return!1;let $=X.toLowerCase();return Z.some((Q)=>Q.toLowerCase()===$)}canConnect(X,Z){return(this._connectionCounts.get(X)??0)<Z}trackConnect(X){let Z=this._connectionCounts.get(X)??0;this._connectionCounts.set(X,Z+1)}trackDisconnect(X){let Z=this._connectionCounts.get(X);if(Z===void 0)return;if(Z<=1)this._connectionCounts.delete(X);else this._connectionCounts.set(X,Z-1)}connectionCount(X){return this._connectionCounts.get(X)??0}}var B6=65536;class B1 extends G0{_isListening=!1;_server;_globalRateLimiter;_diagnostics;_maxBufferSize;_accessLog;_accessLogEnabled=!1;_wsConnections;_wsChannelManager;_wsSecurity;constructor(X){super(X);this._maxBufferSize=Math.max(this._configuration.bodyParser.json.maxSize,this._configuration.bodyParser.urlEncoded.maxSize,this._configuration.bodyParser.fileUploads.maxTotalSize)+B6,this._configureLogging();let Z=new p(X?.rateLimit,this._log);if(X?.rateLimit?.enabled){this._globalRateLimiter=new u(Z);let $=this._diagnostics?(J,Y)=>this._diagnostics?.onRateLimitHit(J,Y):void 0,Q=W1(this._globalRateLimiter,$);this.beforeAll([Q])}if(X?.cookieParser?.enabled){let $=new f(X.cookieParser,this._log),Q=U1($.config);this.beforeAll([Q])}if(X?.cors?.enabled){let $=y.merge(X.cors);y.validate($);let Q=F0($);this.beforeRouting([Q])}if(this._configuration.gracefulShutdownTimeout){let $=D(this._configuration.gracefulShutdownTimeout);if($>0)this._setupGracefulShutdown($)}}get log(){return this._log}_configureLogging(){let X=this._configuration.logging,Z=X.logger;if(Z&&L in Z)Z=Z[L].logger??void 0;if(this._log=_({level:X.level,prefix:X.prefix,personality:X.personality,logger:Z}),this._hooks.setLogger(this._log),X.requests)this._accessLog=_({...Z1,level:"info",logger:X.accessLogger}),this._accessLogEnabled=!0;let $=new E0(X.diagnostics,X.personality);if($.hasAnyEnabled())this._diagnostics=$,this._diagnostics.start()}_setupServer(X,Z,$){if(!this._server)return;this._server.on("error",(Q)=>{this._log.error(`YinzerFlow server error at ${this._configuration.host}:${this._configuration.port} - ${Q.message}`),this._server?.close(),delete this._server,Z(Q)}),this._server.on("listening",()=>{this._isListening=!0,this._log.info(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is up and running`),X()}),this._server.on("connection",(Q)=>{this._handleConnection(Q,$)})}async _processRequest({data:X,socket:Z,requestHandler:$,clientAddress:Q}){let J=Date.now(),Y=new Q0(X,this,Q);if(await $.handle(Y),!Z.destroyed)Z.write(Y._response._stringBody),Z.end();let j=Date.now()-J;if(this._accessLogEnabled||this._diagnostics){let W=Buffer.byteLength(Y._response._stringBody,"utf8"),K=v(Y.request.method),U=v(Y.request.path);if(this._accessLogEnabled)this._accessLog?.info(`${$1(Y._response._statusCode)} ${Q} "${K} ${U} ${Y.request.protocol}" ${Y._response._statusCode} ${W}bytes "${v(Y.request.headers.referer??"-")}" "${v(Y.request.headers["user-agent"]??"-")}" ${j}ms`);this._diagnostics?.checkRequest({duration:j,reqBytes:X.length,resBytes:W,method:K,path:U})}}_handleRequestError(X,Z,$){let Q=X instanceof Error?X.message:"Unknown error";if(this._log.error(`Request processing error from ${Z}: ${Q}`,X),!$.destroyed)$.destroy()}_dispatchRequest({data:X,socket:Z,requestHandler:$,clientAddress:Q}){this._processRequest({data:X,socket:Z,requestHandler:$,clientAddress:Q}).catch((J)=>this._handleRequestError(J,Q,Z))}_rejectOversizedRequest({socket:X,clientAddress:Z,totalLength:$,headersParsed:Q}){if(Q){let J=JSON.stringify({error:"Payload too large",maxSize:this._maxBufferSize,received:$});X.write(`HTTP/1.1 413 Payload Too Large\r
|
|
30
34
|
Content-Type: application/json\r
|
|
31
|
-
Content-Length: ${Buffer.byteLength(
|
|
35
|
+
Content-Length: ${Buffer.byteLength(J,"utf8")}\r
|
|
32
36
|
Connection: close\r
|
|
33
37
|
\r
|
|
34
|
-
${
|
|
38
|
+
${J}`)}this._log.warn(`Request from ${Z} exceeded maximum buffer size (${$} > ${this._maxBufferSize} bytes). Current limits: json=${this._configuration.bodyParser.json.maxSize}, urlEncoded=${this._configuration.bodyParser.urlEncoded.maxSize}, fileUploads=${this._configuration.bodyParser.fileUploads.maxTotalSize}`),X.destroy()}_looksLikeHttp(X,Z,$){if(Z>=1){let Q=X[0]?.[0]??0;if(Q!==71&&Q!==80&&Q!==68&&Q!==72&&Q!==79)return!1}if(Z>=8){let Q=$.subarray(0,8).toString();if(!/^(?:GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s/.test(Q))return!1}return!0}_parseContentLength(X,Z){let $=X.subarray(0,Z).toString(),Q=/content-length:\s*(?<digits>\d+)/i.exec($);return Q?.groups?.digits?parseInt(Q.groups.digits,10):0}_handleConnection(X,Z){let $=X.remoteAddress??"unknown",Q=Date.now(),J=!1;this._log.debug(`New connection from ${$}`);let Y=[],j=0,W={headersParsed:!1,expectedBodyLength:0,headerEndIndex:-1,requestDispatched:!1};X.on("data",(K)=>{this._handleConnectionData({chunk:K,socket:X,requestHandler:Z,clientAddress:$,connectionStartTime:Q,chunks:Y,state:W,hasReceivedData:()=>J,setHasReceivedData:(U)=>{J=U},totalLength:()=>j,setTotalLength:(U)=>{j=U},shouldDispatch:()=>W.requestDispatched})}),X.on("error",(K)=>{this._log.error(`Socket error from ${$}: ${K.message}`,K)}),X.on("close",()=>{let K=Date.now()-Q;if(J){this._log.debug(`Connection closed from ${$} (${K}ms total)`);return}if(K<10)this._log.debug(`${$} quick connectivity check (${K}ms) - health probe`);else this._log.debug(`${$} disconnected without sending data (${K}ms) - potential probe`)})}_handleConnectionData({chunk:X,socket:Z,requestHandler:$,clientAddress:Q,connectionStartTime:J,chunks:Y,state:j,hasReceivedData:W,setHasReceivedData:K,totalLength:U,setTotalLength:G,shouldDispatch:B}){if(!W()){K(!0);let E=Date.now()-J;if(E>100)this._log.debug(`Delayed data from ${Q} (${E}ms connection delay)`)}if(B())return;if(Y.push(X),G(U()+X.length),U()>this._maxBufferSize){this._rejectOversizedRequest({socket:Z,clientAddress:Q,totalLength:U(),headersParsed:j.headersParsed});return}if(!j.headersParsed){this._handleHeaderPhase({chunks:Y,totalLength:U(),state:j,socket:Z,requestHandler:$,clientAddress:Q});return}if(U()-(j.headerEndIndex+4)>=j.expectedBodyLength)j.requestDispatched=!0,this._dispatchRequest({data:Buffer.concat(Y,U()),socket:Z,requestHandler:$,clientAddress:Q})}_handleHeaderPhase({chunks:X,totalLength:Z,state:$,socket:Q,requestHandler:J,clientAddress:Y}){let j=Buffer.concat(X,Z);if($.headerEndIndex=j.indexOf(`\r
|
|
39
|
+
\r
|
|
40
|
+
`),$.headerEndIndex===-1){if(!this._looksLikeHttp(X,Z,j))$.requestDispatched=!0,this._dispatchRequest({data:j,socket:Q,requestHandler:J,clientAddress:Y});return}if($.headersParsed=!0,this._wsRouter._hasRoutes()){let K=j.subarray(0,$.headerEndIndex).toString();if(M1(K)){$.requestDispatched=!0,this._handleWebSocketUpgrade(j,Q,Y);return}}$.expectedBodyLength=this._parseContentLength(j,$.headerEndIndex);let W=$.headerEndIndex+4;if(j.length-W>=$.expectedBodyLength)$.requestDispatched=!0,this._dispatchRequest({data:j,socket:Q,requestHandler:J,clientAddress:Y})}_handleWebSocketUpgrade(X,Z,$){this._handleWebSocketUpgradeAsync(X,Z,$).catch((Q)=>this._handleRequestError(Q,$,Z))}async _handleWebSocketUpgradeAsync(X,Z,$){let Q=X.indexOf(`\r
|
|
41
|
+
\r
|
|
42
|
+
`),J=X.subarray(0,Q).toString(),Y=V1(J);if(!Y.valid){this._sendHttpError(Z,400,Y.reason);return}this._wsSecurity??=new H0;let j=this._configuration.websocket;if(!this._wsSecurity.validateOrigin(Y.origin,j.allowedOrigins)){this._sendHttpError(Z,403,"Origin not allowed");return}if(!this._wsSecurity.canConnect($,j.maxConnectionsPerIp)){this._sendHttpError(Z,429,"Too many WebSocket connections from this IP");return}let W=this._wsRouter._match(Y.path);if(!W){this._sendHttpError(Z,404,"No WebSocket route matches this path");return}let K={headers:this._parseHeadersMap(J),path:Y.path,query:Y.query,params:W.params,remoteAddress:$},U=W.handlers.upgrade?await W.handlers.upgrade(K):void 0;if(U===!1){this._sendHttpError(Z,403,"WebSocket upgrade rejected");return}let G=q1(Y.key);Z.write(N1(G)),Z.removeAllListeners(),this._wsSecurity.trackConnect($),this._setupWsConnection(Z,U,W.handlers,W.options,$,Y.path)}_setupWsConnection(X,Z,$,Q,J,Y){let j=this._mergeWsConnectionOptions(Q),W=this._wrapWsHandlers($),K=new A0(X,Z,W,j);this._wsChannelManager??=new I0,K.setChannelManager(this._wsChannelManager),this._wsConnections??=new Set,this._wsConnections.add(K);let U=W.close;W.close=(G,B,E)=>{this._wsConnections?.delete(K),this._wsSecurity?.trackDisconnect(J),U?.(G,B,E)},this._log.debug(`WebSocket connection established from ${J} on ${Y}`),$.open?.(K)}_mergeWsConnectionOptions(X){let Z=this._configuration.websocket;return{maxPayloadLength:X?.maxPayloadLength??Z.maxPayloadLength,idleTimeout:X?.idleTimeout??Z.idleTimeout,backpressure:{strategy:X?.backpressure?.strategy??Z.backpressure.strategy,limit:X?.backpressure?.limit??Z.backpressure.limit}}}_wrapWsHandlers(X){let Z=this._hooks._wsBeforeMessage.size>0,$=this._hooks._wsAfterMessage.size>0;if(!Z&&!$)return X;return{...X,message:(Q,J,Y)=>{(async()=>{for(let j of this._hooks._wsBeforeMessage){let W=j.handler(Q,J,Y);if(W&&typeof W==="object"&&"catch"in W)await W}X.message?.(Q,J,Y);for(let j of this._hooks._wsAfterMessage){let W=j.handler(Q,J,Y);if(W&&typeof W==="object"&&"catch"in W)await W}})().catch((j)=>{this._log.error("Error in WebSocket message handler hooks:",j)})}}}_parseHeadersMap(X){let Z={},$=X.split(`\r
|
|
43
|
+
`);for(let Q=1;Q<$.length;Q++){let J=$[Q];if(!J)continue;let Y=J.indexOf(":");if(Y<0)continue;Z[J.substring(0,Y).trim().toLowerCase()]=J.substring(Y+1).trim()}return Z}_sendHttpError(X,Z,$){let Q=JSON.stringify({error:$}),Y={400:"Bad Request",403:"Forbidden",404:"Not Found"}[Z]??"Error";X.write(`HTTP/1.1 ${Z} ${Y}\r
|
|
44
|
+
Content-Type: application/json\r
|
|
45
|
+
Content-Length: ${Buffer.byteLength(Q,"utf8")}\r
|
|
46
|
+
Connection: close\r
|
|
35
47
|
\r
|
|
36
|
-
`),
|
|
48
|
+
${Q}`),X.destroy()}publish(X,Z){return this._wsChannelManager?.publish(X,Z)??0}subscriberCount(X){return this._wsChannelManager?.subscriberCount(X)??0}async listen(){if(this._isListening)throw Error("Server is already listening");return new Promise((X,Z)=>{let $=new a(this);this._server=D6(),this._setupServer(X,Z,$),this._server.listen(this._configuration.port,this._configuration.host)})}async close(){if(!this._isListening||!this._server)return;if(this._globalRateLimiter)await this._globalRateLimiter.destroy(),this._globalRateLimiter=void 0;if(this._diagnostics)this._diagnostics.destroy(),this._diagnostics=void 0;if(this._wsConnections?.size){for(let X of this._wsConnections)X.close(1001,"Server shutting down");this._wsConnections.clear()}return new Promise((X)=>{if(!this._server){this._isListening=!1,X();return}this._server.close(()=>{this._isListening=!1,this._log.warn(`YinzerFlow server at ${this._configuration.host}:${this._configuration.port} is shutting down`),X()})})}status(){return{isListening:this._isListening,port:this._configuration.port,host:this._configuration.host}}_setupGracefulShutdown(X){if(X<=0)return;if(process.listenerCount("SIGTERM")===0&&process.listenerCount("SIGINT")===0){let Z=($)=>{this._log.info(`\uD83D\uDED1 Received ${$}, shutting down gracefully in ${this._configuration.gracefulShutdownTimeout}...`),setTimeout(()=>{this.close().then(()=>{this._log.info("✅ Server shut down gracefully"),process.exit(0)}).catch((Q)=>{this._log.error("❌ Error during graceful shutdown:",Q),process.exit(1)})},X)};process.on("SIGTERM",()=>Z("SIGTERM")),process.on("SIGINT",()=>Z("SIGINT"))}}}export{N as wsReadyState,F as wsOpcode,H as wsCloseCode,K6 as wsBackpressureStrategy,g9 as rateLimitStoreType,r9 as rateLimitHook,N0 as rateLimitAlgorithm,P as logLevels,z as httpStatusCode,m as httpStatus,_ as createLogger,F0 as corsHook,V as colors,B1 as YinzerFlow};
|
|
37
49
|
|
|
38
|
-
//# debugId=
|
|
50
|
+
//# debugId=0EF35157C891F4AC64756E2164756E21
|