molnos 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  // MolnOS Core - See LICENSE file for copyright and license details.
2
- var D=class{requests=new Map;limit;windowMs;constructor(e=100,t=60){this.limit=e,this.windowMs=t*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(e){let t=Date.now(),r=e||"unknown",s=this.requests.get(r);return(!s||s.resetTime<t)&&(s={count:0,resetTime:t+this.windowMs},this.requests.set(r,s)),s.count++,s.count<=this.limit}getRemainingRequests(e){let t=Date.now(),r=e||"unknown",s=this.requests.get(r);return!s||s.resetTime<t?this.limit:Math.max(0,this.limit-s.count)}getResetTime(e){let t=Date.now(),r=e||"unknown",s=this.requests.get(r);return!s||s.resetTime<t?Math.floor((t+this.windowMs)/1e3):Math.floor(s.resetTime/1e3)}cleanup(){let e=Date.now();for(let[t,r]of this.requests.entries())r.resetTime<e&&this.requests.delete(t)}};import{URL as re}from"url";var q=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(e){return this.globalMiddlewares.push(e),this}get(e,...t){let r=t.pop();return this.register("GET",e,r,t)}post(e,...t){let r=t.pop();return this.register("POST",e,r,t)}put(e,...t){let r=t.pop();return this.register("PUT",e,r,t)}delete(e,...t){let r=t.pop();return this.register("DELETE",e,r,t)}patch(e,...t){let r=t.pop();return this.register("PATCH",e,r,t)}any(e,...t){let r=t.pop(),s=t;return this.register("GET",e,r,s),this.register("POST",e,r,s),this.register("PUT",e,r,s),this.register("DELETE",e,r,s),this.register("PATCH",e,r,s),this.register("OPTIONS",e,r,s),this}options(e,...t){let r=t.pop();return this.register("OPTIONS",e,r,t)}match(e,t){for(let r of this.routes){if(r.method!==e)continue;let s=this.pathPatterns.get(r.path);if(!s)continue;let i=s.pattern.exec(t);if(!i)continue;let n={};return s.paramNames.forEach((o,a)=>{n[o]=i[a+1]||""}),{route:r,params:n}}return null}async handle(e,t){let r=e.method||"GET",s=new re(e.url||"/",`http://${e.headers.host}`),i=s.pathname,n=this.match(r,i);if(!n)return null;let{route:o,params:a}=n,l={};s.searchParams.forEach((c,d)=>{l[d]=c});let u={req:e,res:t,params:a,query:l,body:e.body||{},headers:e.headers,path:i,state:{},raw:()=>t,binary:(c,d="application/octet-stream",h=200)=>({statusCode:h,body:c,headers:{"Content-Type":d,"Content-Length":c.length.toString()},isRaw:!0}),text:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"text/plain"}}),form:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"application/json"}}),html:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"text/html"}}),redirect:(c,d=302)=>({statusCode:d,body:null,headers:{Location:c}}),status:function(c){return{raw:()=>t,binary:(d,h="application/octet-stream")=>({statusCode:c,body:d,headers:{"Content-Type":h,"Content-Length":d.length.toString()},isRaw:!0}),text:d=>({statusCode:c,body:d,headers:{"Content-Type":"text/plain"}}),json:d=>({statusCode:c,body:d,headers:{"Content-Type":"application/json"}}),html:d=>({statusCode:c,body:d,headers:{"Content-Type":"text/html"}}),form:d=>({statusCode:c,body:d,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(d,h=302)=>({statusCode:h,body:null,headers:{Location:d}}),status:d=>this.status(d)}}},f=[...this.globalMiddlewares,...o.middlewares];return this.executeMiddlewareChain(u,f,o.handler)}register(e,t,r,s=[]){return this.routes.push({method:e,path:t,handler:r,middlewares:s}),this.pathPatterns.set(t,this.createPathPattern(t)),this}createPathPattern(e){let t=[],r=e.replace(/\/:[^/]+/g,s=>{let i=s.slice(2);return t.push(i),"/([^/]+)"});return r.endsWith("/*")?(r=`${r.slice(0,-2)}(?:/(.*))?`,t.push("wildcard")):r=r.replace(/\/$/,"/?"),{pattern:new RegExp(`^${r}$`),paramNames:t}}async executeMiddlewareChain(e,t,r){let s=0,i=async()=>{if(s<t.length){let n=t[s++];return n(e,i)}return r(e)};return i()}};var R=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:se(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function se(e){return e==="true"||e===!0}var j=class extends Error{constructor(e){super(e),this.name="ValidationError",this.message=e||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as ie,readFileSync as oe}from"node:fs";var L=class{config={};options=[];validators=[];autoValidate=!0;constructor(e){let t=e?.configFilePath,r=e?.args||[],s=e?.config||{};this.options=e?.options||[],this.validators=e?.validators||[],e?.autoValidate!==void 0&&(this.autoValidate=e.autoValidate),this.config=this.createConfig(t,r,s)}deepMerge(e,t){let r={...e};for(let s in t)t[s]!==void 0&&(t[s]!==null&&typeof t[s]=="object"&&!Array.isArray(t[s])&&s in e&&e[s]!==null&&typeof e[s]=="object"&&!Array.isArray(e[s])?r[s]=this.deepMerge(e[s],t[s]):t[s]!==void 0&&(r[s]=t[s]));return r}setValueAtPath(e,t,r){let s=t.split("."),i=e;for(let o=0;o<s.length-1;o++){let a=s[o];!(a in i)||i[a]===null?i[a]={}:typeof i[a]!="object"&&(i[a]={}),i=i[a]}let n=s[s.length-1];i[n]=r}getValueAtPath(e,t){let r=t.split("."),s=e;for(let i of r){if(s==null)return;s=s[i]}return s}createConfig(e,t=[],r={}){let s={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(s,a.path,a.defaultValue);let i={};if(e&&ie(e))try{let a=oe(e,"utf8");i=JSON.parse(a),console.log(`Loaded configuration from ${e}`)}catch(a){console.error(`Error reading config file: ${a instanceof Error?a.message:String(a)}`)}let n=this.parseCliArgs(t),o=this.deepMerge({},s);return o=this.deepMerge(o,i),o=this.deepMerge(o,r),o=this.deepMerge(o,n),o}parseCliArgs(e){let t={},r=e[0]?.endsWith("node")||e[0]?.endsWith("node.exe")?2:0;for(;r<e.length;){let s=e[r++],i=this.options.find(n=>n.flag===s);if(i)if(i.isFlag)this.setValueAtPath(t,i.path,!0);else if(r<e.length&&!e[r].startsWith("-")){let n=e[r++];if(i.parser)try{n=i.parser(n)}catch(o){console.error(`Error parsing value for ${i.flag}: ${o instanceof Error?o.message:String(o)}`);continue}if(i.validator){let o=i.validator(n);if(o!==!0&&typeof o=="string"){console.error(`Invalid value for ${i.flag}: ${o}`);continue}if(o===!1){console.error(`Invalid value for ${i.flag}`);continue}}this.setValueAtPath(t,i.path,n)}else console.error(`Missing value for option ${s}`)}return t}validate(){for(let e of this.validators){let t=this.getValueAtPath(this.config,e.path),r=e.validator(t,this.config);if(r===!1)throw new j(e.message);if(typeof r=="string")throw new j(r)}}get(){return this.autoValidate&&this.validate(),this.config}getValue(e,t){let r=this.getValueAtPath(this.config,e);return r!==void 0?r:t}setValue(e,t){if(typeof t=="object"&&t!==null&&!Array.isArray(t)){let r=this.getValueAtPath(this.config,e)||{};if(typeof r=="object"&&!Array.isArray(r)){let s=this.deepMerge(r,t);this.setValueAtPath(this.config,e,s);return}}this.setValueAtPath(this.config,e,t)}getHelpText(){let e=`Available configuration options:
2
+ var D=class{requests=new Map;limit;windowMs;constructor(e=100,t=60){this.limit=e,this.windowMs=t*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(e){let t=Date.now(),r=e||"unknown",s=this.requests.get(r);return(!s||s.resetTime<t)&&(s={count:0,resetTime:t+this.windowMs},this.requests.set(r,s)),s.count++,s.count<=this.limit}getRemainingRequests(e){let t=Date.now(),r=e||"unknown",s=this.requests.get(r);return!s||s.resetTime<t?this.limit:Math.max(0,this.limit-s.count)}getResetTime(e){let t=Date.now(),r=e||"unknown",s=this.requests.get(r);return!s||s.resetTime<t?Math.floor((t+this.windowMs)/1e3):Math.floor(s.resetTime/1e3)}cleanup(){let e=Date.now();for(let[t,r]of this.requests.entries())r.resetTime<e&&this.requests.delete(t)}};import{URL as re}from"url";var q=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(e){return this.globalMiddlewares.push(e),this}get(e,...t){let r=t.pop();return this.register("GET",e,r,t)}post(e,...t){let r=t.pop();return this.register("POST",e,r,t)}put(e,...t){let r=t.pop();return this.register("PUT",e,r,t)}delete(e,...t){let r=t.pop();return this.register("DELETE",e,r,t)}patch(e,...t){let r=t.pop();return this.register("PATCH",e,r,t)}any(e,...t){let r=t.pop(),s=t;return this.register("GET",e,r,s),this.register("POST",e,r,s),this.register("PUT",e,r,s),this.register("DELETE",e,r,s),this.register("PATCH",e,r,s),this.register("OPTIONS",e,r,s),this}options(e,...t){let r=t.pop();return this.register("OPTIONS",e,r,t)}match(e,t){for(let r of this.routes){if(r.method!==e)continue;let s=this.pathPatterns.get(r.path);if(!s)continue;let i=s.pattern.exec(t);if(!i)continue;let n={};return s.paramNames.forEach((o,a)=>{n[o]=i[a+1]||""}),{route:r,params:n}}return null}async handle(e,t){let r=e.method||"GET",s=new re(e.url||"/",`http://${e.headers.host}`),i=s.pathname,n=this.match(r,i);if(!n)return null;let{route:o,params:a}=n,l={};s.searchParams.forEach((c,u)=>{l[u]=c});let d={req:e,res:t,params:a,query:l,body:e.body||{},headers:e.headers,path:i,state:{},raw:()=>t,binary:(c,u="application/octet-stream",h=200)=>({statusCode:h,body:c,headers:{"Content-Type":u,"Content-Length":c.length.toString()},isRaw:!0}),text:(c,u=200)=>({statusCode:u,body:c,headers:{"Content-Type":"text/plain"}}),form:(c,u=200)=>({statusCode:u,body:c,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(c,u=200)=>({statusCode:u,body:c,headers:{"Content-Type":"application/json"}}),html:(c,u=200)=>({statusCode:u,body:c,headers:{"Content-Type":"text/html"}}),redirect:(c,u=302)=>({statusCode:u,body:null,headers:{Location:c}}),status:function(c){return{raw:()=>t,binary:(u,h="application/octet-stream")=>({statusCode:c,body:u,headers:{"Content-Type":h,"Content-Length":u.length.toString()},isRaw:!0}),text:u=>({statusCode:c,body:u,headers:{"Content-Type":"text/plain"}}),json:u=>({statusCode:c,body:u,headers:{"Content-Type":"application/json"}}),html:u=>({statusCode:c,body:u,headers:{"Content-Type":"text/html"}}),form:u=>({statusCode:c,body:u,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(u,h=302)=>({statusCode:h,body:null,headers:{Location:u}}),status:u=>this.status(u)}}},f=[...this.globalMiddlewares,...o.middlewares];return this.executeMiddlewareChain(d,f,o.handler)}register(e,t,r,s=[]){return this.routes.push({method:e,path:t,handler:r,middlewares:s}),this.pathPatterns.set(t,this.createPathPattern(t)),this}createPathPattern(e){let t=[],r=e.replace(/\/:[^/]+/g,s=>{let i=s.slice(2);return t.push(i),"/([^/]+)"});return r.endsWith("/*")?(r=`${r.slice(0,-2)}(?:/(.*))?`,t.push("wildcard")):r=r.replace(/\/$/,"/?"),{pattern:new RegExp(`^${r}$`),paramNames:t}}async executeMiddlewareChain(e,t,r){let s=0,i=async()=>{if(s<t.length){let n=t[s++];return n(e,i)}return r(e)};return i()}};var R=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:se(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function se(e){return e==="true"||e===!0}var j=class extends Error{constructor(e){super(e),this.name="ValidationError",this.message=e||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as ie,readFileSync as oe}from"node:fs";var L=class{config={};options=[];validators=[];autoValidate=!0;constructor(e){let t=e?.configFilePath,r=e?.args||[],s=e?.config||{};this.options=e?.options||[],this.validators=e?.validators||[],e?.autoValidate!==void 0&&(this.autoValidate=e.autoValidate),this.config=this.createConfig(t,r,s)}deepMerge(e,t){let r={...e};for(let s in t)t[s]!==void 0&&(t[s]!==null&&typeof t[s]=="object"&&!Array.isArray(t[s])&&s in e&&e[s]!==null&&typeof e[s]=="object"&&!Array.isArray(e[s])?r[s]=this.deepMerge(e[s],t[s]):t[s]!==void 0&&(r[s]=t[s]));return r}setValueAtPath(e,t,r){let s=t.split("."),i=e;for(let o=0;o<s.length-1;o++){let a=s[o];!(a in i)||i[a]===null?i[a]={}:typeof i[a]!="object"&&(i[a]={}),i=i[a]}let n=s[s.length-1];i[n]=r}getValueAtPath(e,t){let r=t.split("."),s=e;for(let i of r){if(s==null)return;s=s[i]}return s}createConfig(e,t=[],r={}){let s={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(s,a.path,a.defaultValue);let i={};if(e&&ie(e))try{let a=oe(e,"utf8");i=JSON.parse(a),console.log(`Loaded configuration from ${e}`)}catch(a){console.error(`Error reading config file: ${a instanceof Error?a.message:String(a)}`)}let n=this.parseCliArgs(t),o=this.deepMerge({},s);return o=this.deepMerge(o,i),o=this.deepMerge(o,r),o=this.deepMerge(o,n),o}parseCliArgs(e){let t={},r=e[0]?.endsWith("node")||e[0]?.endsWith("node.exe")?2:0;for(;r<e.length;){let s=e[r++],i=this.options.find(n=>n.flag===s);if(i)if(i.isFlag)this.setValueAtPath(t,i.path,!0);else if(r<e.length&&!e[r].startsWith("-")){let n=e[r++];if(i.parser)try{n=i.parser(n)}catch(o){console.error(`Error parsing value for ${i.flag}: ${o instanceof Error?o.message:String(o)}`);continue}if(i.validator){let o=i.validator(n);if(o!==!0&&typeof o=="string"){console.error(`Invalid value for ${i.flag}: ${o}`);continue}if(o===!1){console.error(`Invalid value for ${i.flag}`);continue}}this.setValueAtPath(t,i.path,n)}else console.error(`Missing value for option ${s}`)}return t}validate(){for(let e of this.validators){let t=this.getValueAtPath(this.config,e.path),r=e.validator(t,this.config);if(r===!1)throw new j(e.message);if(typeof r=="string")throw new j(r)}}get(){return this.autoValidate&&this.validate(),this.config}getValue(e,t){let r=this.getValueAtPath(this.config,e);return r!==void 0?r:t}setValue(e,t){if(typeof t=="object"&&t!==null&&!Array.isArray(t)){let r=this.getValueAtPath(this.config,e)||{};if(typeof r=="object"&&!Array.isArray(r)){let s=this.deepMerge(r,t);this.setValueAtPath(this.config,e,s);return}}this.setValueAtPath(this.config,e,t)}getHelpText(){let e=`Available configuration options:
3
3
 
4
4
  `;for(let t of this.options)e+=`${t.flag}${t.isFlag?"":" <value>"}
5
5
  `,t.description&&(e+=` ${t.description}
6
6
  `),t.defaultValue!==void 0&&(e+=` Default: ${JSON.stringify(t.defaultValue)}
7
7
  `),e+=`
8
- `;return e}};var O={int:e=>{let t=e.trim();if(!/^[+-]?\d+$/.test(t))throw new Error(`Cannot parse "${e}" as an integer`);let r=Number.parseInt(t,10);if(Number.isNaN(r))throw new Error(`Cannot parse "${e}" as an integer`);return r},float:e=>{let t=e.trim();if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(t)){if(t==="Infinity"||t==="-Infinity")return t==="Infinity"?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY;throw new Error(`Cannot parse "${e}" as a number`)}let r=Number.parseFloat(t);if(Number.isNaN(r))throw new Error(`Cannot parse "${e}" as a number`);return r},boolean:e=>{let t=e.trim().toLowerCase();if(["true","yes","1","y"].includes(t))return!0;if(["false","no","0","n"].includes(t))return!1;throw new Error(`Cannot parse "${e}" as a boolean`)},array:e=>e.split(",").map(t=>t.trim()),json:e=>{try{return JSON.parse(e)}catch{throw new Error(`Cannot parse "${e}" as JSON`)}}};var m=R(),N=e=>({configFilePath:"mikroserve.config.json",args:process.argv,options:[{flag:"--port",path:"port",defaultValue:m.port},{flag:"--host",path:"host",defaultValue:m.host},{flag:"--https",path:"useHttps",defaultValue:m.useHttps,isFlag:!0},{flag:"--http2",path:"useHttp2",defaultValue:m.useHttp2,isFlag:!0},{flag:"--cert",path:"sslCert",defaultValue:m.sslCert},{flag:"--key",path:"sslKey",defaultValue:m.sslKey},{flag:"--ca",path:"sslCa",defaultValue:m.sslCa},{flag:"--ratelimit",path:"rateLimit.enabled",defaultValue:m.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"rateLimit.requestsPerMinute",defaultValue:m.rateLimit.requestsPerMinute},{flag:"--allowed",path:"allowedDomains",defaultValue:m.allowedDomains,parser:O.array},{flag:"--debug",path:"debug",defaultValue:m.debug,isFlag:!0},{flag:"--max-body-size",path:"maxBodySize",defaultValue:m.maxBodySize},{flag:"--request-timeout",path:"requestTimeout",defaultValue:m.requestTimeout}],config:e});function F(e,t){let r=t.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!r)throw new Error("Invalid multipart/form-data: missing boundary");let s=r[1]||r[2],i=Buffer.from(`--${s}`),n=Buffer.from(`--${s}--`),o={},a={},l=ne(e,i);for(let u of l){if(u.length===0||u.equals(n.subarray(i.length)))continue;let f=ae(u);if(!f)continue;let{name:c,filename:d,contentType:h,data:b}=f;if(d){let p={filename:d,contentType:h||"application/octet-stream",data:b,size:b.length};a[c]?Array.isArray(a[c])?a[c].push(p):a[c]=[a[c],p]:a[c]=p}else{let p=b.toString("utf8");o[c]?Array.isArray(o[c])?o[c].push(p):o[c]=[o[c],p]:o[c]=p}}return{fields:o,files:a}}function ne(e,t){let r=[],s=0;for(;s<e.length;){let i=e.indexOf(t,s);if(i===-1)break;s!==i&&r.push(e.subarray(s,i)),s=i+t.length,s<e.length&&e[s]===13&&e[s+1]===10&&(s+=2)}return r}function ae(e){let t=Buffer.from(`\r
8
+ `;return e}};var O={int:e=>{let t=e.trim();if(!/^[+-]?\d+$/.test(t))throw new Error(`Cannot parse "${e}" as an integer`);let r=Number.parseInt(t,10);if(Number.isNaN(r))throw new Error(`Cannot parse "${e}" as an integer`);return r},float:e=>{let t=e.trim();if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(t)){if(t==="Infinity"||t==="-Infinity")return t==="Infinity"?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY;throw new Error(`Cannot parse "${e}" as a number`)}let r=Number.parseFloat(t);if(Number.isNaN(r))throw new Error(`Cannot parse "${e}" as a number`);return r},boolean:e=>{let t=e.trim().toLowerCase();if(["true","yes","1","y"].includes(t))return!0;if(["false","no","0","n"].includes(t))return!1;throw new Error(`Cannot parse "${e}" as a boolean`)},array:e=>e.split(",").map(t=>t.trim()),json:e=>{try{return JSON.parse(e)}catch{throw new Error(`Cannot parse "${e}" as JSON`)}}};var m=R(),N=e=>({configFilePath:"mikroserve.config.json",args:process.argv,options:[{flag:"--port",path:"port",defaultValue:m.port},{flag:"--host",path:"host",defaultValue:m.host},{flag:"--https",path:"useHttps",defaultValue:m.useHttps,isFlag:!0},{flag:"--http2",path:"useHttp2",defaultValue:m.useHttp2,isFlag:!0},{flag:"--cert",path:"sslCert",defaultValue:m.sslCert},{flag:"--key",path:"sslKey",defaultValue:m.sslKey},{flag:"--ca",path:"sslCa",defaultValue:m.sslCa},{flag:"--ratelimit",path:"rateLimit.enabled",defaultValue:m.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"rateLimit.requestsPerMinute",defaultValue:m.rateLimit.requestsPerMinute},{flag:"--allowed",path:"allowedDomains",defaultValue:m.allowedDomains,parser:O.array},{flag:"--debug",path:"debug",defaultValue:m.debug,isFlag:!0},{flag:"--max-body-size",path:"maxBodySize",defaultValue:m.maxBodySize},{flag:"--request-timeout",path:"requestTimeout",defaultValue:m.requestTimeout}],config:e});function F(e,t){let r=t.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!r)throw new Error("Invalid multipart/form-data: missing boundary");let s=r[1]||r[2],i=Buffer.from(`--${s}`),n=Buffer.from(`--${s}--`),o={},a={},l=ne(e,i);for(let d of l){if(d.length===0||d.equals(n.subarray(i.length)))continue;let f=ae(d);if(!f)continue;let{name:c,filename:u,contentType:h,data:b}=f;if(u){let p={filename:u,contentType:h||"application/octet-stream",data:b,size:b.length};a[c]?Array.isArray(a[c])?a[c].push(p):a[c]=[a[c],p]:a[c]=p}else{let p=b.toString("utf8");o[c]?Array.isArray(o[c])?o[c].push(p):o[c]=[o[c],p]:o[c]=p}}return{fields:o,files:a}}function ne(e,t){let r=[],s=0;for(;s<e.length;){let i=e.indexOf(t,s);if(i===-1)break;s!==i&&r.push(e.subarray(s,i)),s=i+t.length,s<e.length&&e[s]===13&&e[s+1]===10&&(s+=2)}return r}function ae(e){let t=Buffer.from(`\r
9
9
  \r
10
10
  `),r=e.indexOf(t);if(r===-1)return null;let s=e.subarray(0,r),i=e.subarray(r+4),o=s.toString("utf8").split(`\r
11
- `),a="",l="",u,f;for(let d of o){let h=d.toLowerCase();if(h.startsWith("content-disposition:")){a=d.substring(20).trim();let b=a.match(/name="([^"]+)"/);b&&(l=b[1]);let p=a.match(/filename="([^"]+)"/);p&&(u=p[1])}else h.startsWith("content-type:")&&(f=d.substring(13).trim())}if(!l)return null;let c=i;return c.length>=2&&c[c.length-2]===13&&c[c.length-1]===10&&(c=c.subarray(0,c.length-2)),{disposition:a,name:l,filename:u,contentType:f,data:c}}import{readFileSync as S}from"fs";import A from"http";import le from"http2";import ce from"https";var M=class{config;rateLimiter;router;shutdownHandlers=[];constructor(e){let t=new L(N(e||{})).get();t.debug&&console.log("Using configuration:",t),this.config=t,this.router=new q;let r=t.rateLimit.requestsPerMinute||R().rateLimit.requestsPerMinute;this.rateLimiter=new D(r,60),t.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(e){return this.router.use(e),this}get(e,...t){return this.router.get(e,...t),this}post(e,...t){return this.router.post(e,...t),this}put(e,...t){return this.router.put(e,...t),this}delete(e,...t){return this.router.delete(e,...t),this}patch(e,...t){return this.router.patch(e,...t),this}any(e,...t){return this.router.any(e,...t),this}options(e,...t){return this.router.options(e,...t),this}start(){let e=this.createServer(),{port:t,host:r}=this.config;return this.setupGracefulShutdown(e),e.listen(t,r,()=>{let s=e.address(),i=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${i}://${s.address!=="::"?s.address:"localhost"}:${s.port}`)}),e}createServer(){let e=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let t={key:S(this.config.sslKey),cert:S(this.config.sslCert),...this.config.sslCa?{ca:S(this.config.sslCa)}:{}};return le.createSecureServer(t,e)}catch(t){throw t.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${t.message}`):t}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let t={key:S(this.config.sslKey),cert:S(this.config.sslCert),...this.config.sslCa?{ca:S(this.config.sslCa)}:{}};return ce.createServer(t,e)}catch(t){throw t.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${t.message}`):t}}return A.createServer(e)}async rateLimitMiddleware(e,t){let r=e.req.socket.remoteAddress||"unknown";return e.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),e.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(r).toString()),e.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(r).toString()),this.rateLimiter.isAllowed(r)?t():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(e,t){let r=Date.now(),s=e.method||"UNKNOWN",i=e.url||"/unknown",n=this.config.debug;try{if(this.setCorsHeaders(t,e),this.setSecurityHeaders(t,this.config.useHttps),n&&console.log(`${s} ${i}`),e.method==="OPTIONS"){if(t instanceof A.ServerResponse)t.statusCode=204,t.end();else{let a=t;a.writeHead(204),a.end()}return}try{e.body=await this.parseBody(e)}catch(a){return n&&console.error("Body parsing error:",a.message),this.respond(t,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let o=await this.router.handle(e,t);return o?o._handled?void 0:this.respond(t,o):this.respond(t,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(o){return console.error("Server error:",o),this.respond(t,{statusCode:500,body:{error:"Internal Server Error",message:n?o.message:"An unexpected error occurred"}})}finally{n&&this.logDuration(r,s,i)}}logDuration(e,t,r){let s=Date.now()-e;console.log(`${t} ${r} completed in ${s}ms`)}async parseBody(e){return new Promise((t,r)=>{let s=[],i=0,n=this.config.maxBodySize,o=!1,a=null,l=this.config.debug,u=e.headers["content-type"]||"";l&&console.log("Content-Type:",u),this.config.requestTimeout>0&&(a=setTimeout(()=>{o||(o=!0,l&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let f=()=>{a&&(clearTimeout(a),a=null)};e.on("data",c=>{if(!o){if(i+=c.length,l&&console.log(`Received chunk: ${c.length} bytes, total size: ${i}`),i>n){o=!0,f(),l&&console.log(`Body size exceeded limit: ${i} > ${n}`),r(new Error("Request body too large"));return}s.push(c)}}),e.on("end",()=>{if(!o){o=!0,f(),l&&console.log(`Request body complete: ${i} bytes`);try{if(s.length>0){let c=Buffer.concat(s);if(u.includes("application/json"))try{let d=c.toString("utf8");t(JSON.parse(d))}catch(d){r(new Error(`Invalid JSON in request body: ${d.message}`))}else if(u.includes("application/x-www-form-urlencoded")){let d=c.toString("utf8"),h={};new URLSearchParams(d).forEach((b,p)=>{h[p]=b}),t(h)}else if(u.includes("multipart/form-data"))try{let d=F(c,u);t(d)}catch(d){r(new Error(`Invalid multipart form data: ${d.message}`))}else this.isBinaryContentType(u)?t(c):t(c.toString("utf8"))}else t({})}catch(c){r(new Error(`Invalid request body: ${c}`))}}}),e.on("error",c=>{o||(o=!0,f(),r(new Error(`Error reading request body: ${c.message}`)))}),e.on("close",()=>{f()})})}isBinaryContentType(e){return["application/octet-stream","application/pdf","application/zip","application/gzip","application/x-tar","application/x-rar-compressed","application/x-7z-compressed","image/","video/","audio/","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument","application/msword","application/vnd.ms-powerpoint"].some(r=>e.includes(r))}setCorsHeaders(e,t){let r=t.headers.origin,{allowedDomains:s=["*"]}=this.config;!r||s.length===0||s.includes("*")?e.setHeader("Access-Control-Allow-Origin","*"):s.includes(r)&&(e.setHeader("Access-Control-Allow-Origin",r),e.setHeader("Vary","Origin")),e.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),e.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(e,t=!1){let r={"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'self'; script-src 'self'; object-src 'none'","X-XSS-Protection":"1; mode=block"};if((t||this.config.useHttp2)&&(r["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),e instanceof A.ServerResponse)Object.entries(r).forEach(([s,i])=>{e.setHeader(s,i)});else{let s=e;Object.entries(r).forEach(([i,n])=>{s.setHeader(i,n)})}}respond(e,t){let r={...t.headers||{}};(i=>typeof i.writeHead=="function"&&typeof i.end=="function")(e)?(e.writeHead(t.statusCode,r),t.body===null||t.body===void 0?e.end():t.isRaw||typeof t.body=="string"?e.end(t.body):e.end(JSON.stringify(t.body))):(console.warn("Unexpected response object type without writeHead/end methods"),e.writeHead?.(t.statusCode,r),t.body===null||t.body===void 0?e.end?.():t.isRaw||typeof t.body=="string"?e.end?.(t.body):e.end?.(JSON.stringify(t.body)))}setupGracefulShutdown(e){let t=o=>{console.log("Shutting down MikroServe server..."),o&&console.error("Error:",o),this.cleanupShutdownHandlers(),e.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(o?1:0))})},r=()=>t(),s=()=>t(),i=o=>t(o),n=o=>t(o);this.shutdownHandlers=[r,s,i,n],process.on("SIGINT",r),process.on("SIGTERM",s),process.on("uncaughtException",i),process.on("unhandledRejection",n)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[e,t,r,s]=this.shutdownHandlers;process.removeListener("SIGINT",e),process.removeListener("SIGTERM",t),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",s),this.shutdownHandlers=[]}}};import{join as P}from"node:path";import{promises as w}from"node:fs";import{getRandomValues as ue}from"node:crypto";var $=class{options;defaultLength=16;defaultOnlyLowerCase=!1;defaultStyle="extended";defaultUrlSafe=!0;constructor(e){if(this.options={},e)for(let[t,r]of Object.entries(e))this.options[t]=this.generateConfig(r)}add(e){if(!e?.name)throw new Error("Missing name for the ID configuration");let t=this.generateConfig(e);this.options[e.name]=t}remove(e){if(!e?.name)throw new Error("Missing name for the ID configuration");delete this.options[e.name]}create(e,t,r,s){let i=this.generateConfig({length:e,style:t,onlyLowerCase:r,urlSafe:s});return this.generateId(i)}custom(e){if(this.options[e])return this.generateId(this.options[e]);throw new Error(`No configuration found with name: ${e}`)}generateConfig(e){return{name:e?.name||"",length:e?.length||this.defaultLength,onlyLowerCase:e?.onlyLowerCase??this.defaultOnlyLowerCase,style:e?.style||this.defaultStyle,urlSafe:e?.urlSafe??this.defaultUrlSafe}}getCharacterSet(e,t,r){if(e==="hex")return t?"0123456789abcdef":"0123456789ABCDEFabcdef";if(e==="alphanumeric")return t?"abcdefghijklmnopqrstuvwxyz0123456789":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";if(e==="extended")return r?t?"abcdefghijklmnopqrstuvwxyz0123456789-._~":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~":t?"abcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:";throw new Error(`Unknown ID style "${e} provided. Must be one of "extended" (default), "alphanumeric", or "hex".`)}generateId(e){let{length:t,onlyLowerCase:r,style:s,urlSafe:i}=e;if(t<0||t===0)throw new Error("ID length cannot be negative");let n=this.getCharacterSet(s,r,i),o=(2<<Math.log(n.length-1)/Math.LN2)-1,a=Math.ceil(1.6*o*t/n.length),l="";for(;l.length<t;){let u=new Uint8Array(a);ue(u);for(let f=0;f<a;f++){let c=u[f]&o;if(c<n.length&&(l+=n[c],l.length===t))break}}return l}};var de=8;function V(){return new $().create(de,"alphanumeric",!1,!0)}var x=class{isSilent;propertyPath="";constructor(e=!1){this.isSilent=e}test(e,t){if(!t)throw new Error("Missing input!");this.updatePropertyPath();let{results:r,errors:s}=this.validate(e.properties,t),i=this.compileErrors(r,s),n=this.isSuccessful(r,i);return{errors:i,success:n}}compileErrors(e,t){let r=e.filter(s=>s.success===!1);return[...t,...r].flatMap(s=>s)}isSuccessful(e,t){return e.every(r=>r.success===!0)&&t.length===0}validate(e,t,r=[],s=[]){let i=e?.additionalProperties??!0,n=e?.required||[];s=this.checkForRequiredKeysErrors(n,t,s),s=this.checkForDisallowedProperties(Object.keys(t),Object.keys(e),s,i);for(let o in e){let a=n.includes(o)&&o!=="required",l=e[o],u=t[o],f=l.additionalProperties??!0;a&&(s=this.checkForRequiredKeysErrors(l.required||[],u,s)),this.isDefined(u)&&(this.handleValidation(o,u,l,r),s=this.checkForDisallowedProperties(Object.keys(u),Object.keys(l),s,f),this.handleNestedObject(u,l,r,s))}return{results:r,errors:s}}updatePropertyPath(e,t=""){if(!e){this.propertyPath="";return}t&&(this.propertyPath=t),this.propertyPath=`${this.propertyPath}.${e}`,this.propertyPath.startsWith(".")&&(this.propertyPath=this.propertyPath.substring(1,this.propertyPath.length))}isDefined(e){return!!(typeof e=="number"&&e===0||e||e===""||typeof e=="boolean")}checkForRequiredKeysErrors(e,t,r){if(!this.areRequiredKeysPresent(e,t)){let s=t?Object.keys(t):[],i=this.findNonOverlappingElements(e,s),n=i.length>0?`Missing the required key: '${i.join(", ")}'!`:`Missing values for required keys: '${s.filter(o=>!t[o]).join(", ")}'!`;r.push({key:"",value:t,success:!1,error:n})}return r}checkForDisallowedProperties(e,t,r,s){if(!s){let i=this.findNonOverlappingElements(e,t);i.length>0&&r.push({key:`${t}`,value:e,success:!1,error:`Has additional (disallowed) properties: '${i.join(", ")}'!`})}return r}handleValidation(e,t,r,s){this.updatePropertyPath(e);let i=this.validateProperty(this.propertyPath,r,t);s.push(...i);let n=(a,l)=>{a.forEach(u=>{let f=this.validateProperty(this.propertyPath,l.items,u);s.push(...f)}),this.updatePropertyPath()},o=a=>{let l=Object.keys(a),u=this.propertyPath;l.forEach(f=>{if(this.updatePropertyPath(f,u),this.isArray(a[f])&&r[f]?.items!=null)n(a[f],r[f]);else{let c=this.validateProperty(this.propertyPath,r[f],a[f]);s.push(...c)}})};this.isArray(t)&&r.items!=null?n(t,r):this.isObject(t)?o(t):this.updatePropertyPath()}handleNestedObject(e,t,r,s){if(this.isObject(e)){let i=this.getNestedObjects(e);for(let n of i){let o=t[n],a=e[n];o&&typeof a=="object"&&this.validate(o,a,r,s)}}}getNestedObjects(e){return Object.keys(e).filter(t=>{if(this.isObject(e))return t})}findNonOverlappingElements(e,t){return e.filter(r=>!t.includes(r))}areRequiredKeysPresent(e,t=[]){return e.every(r=>Object.keys(t).includes(r)?this.isDefined(t[r]):!1)}validateProperty(e,t,r){return this.validateInput(t,r).map(i=>{let{success:n,error:o}=i;return{key:e,value:r,success:n,error:o??""}})}validateInput(e,t){if(e){let r=[{condition:()=>e.type,validator:()=>this.isCorrectType(e.type,t),error:"Invalid type"},{condition:()=>e.format,validator:()=>this.isCorrectFormat(e.format,t),error:"Invalid format"},{condition:()=>e.minLength,validator:()=>this.isMinimumLength(e.minLength,t),error:"Length too short"},{condition:()=>e.maxLength,validator:()=>this.isMaximumLength(e.maxLength,t),error:"Length too long"},{condition:()=>e.minValue,validator:()=>this.isMinimumValue(e.minValue,t),error:"Value too small"},{condition:()=>e.maxValue,validator:()=>this.isMaximumValue(e.maxValue,t),error:"Value too large"},{condition:()=>e.matchesPattern,validator:()=>this.matchesPattern(e.matchesPattern,t),error:"Pattern does not match"}],s=[];for(let i of r)i.condition()&&!i.validator()&&s.push({success:!1,error:i.error});return s}else this.isSilent||console.warn(`Missing property '${e}' for match '${t}'. Skipping...`);return[{success:!0}]}isCorrectType(e,t){return Array.isArray(e)||(e=[e]),e.some(r=>{switch(r){case"string":return typeof t=="string";case"number":return typeof t=="number"&&!isNaN(t);case"boolean":return typeof t=="boolean";case"object":return this.isObject(t);case"array":return this.isArray(t)}})}isObject(e){return e!==null&&!this.isArray(e)&&typeof e=="object"&&e instanceof Object&&Object.prototype.toString.call(e)==="[object Object]"}isArray(e){return Array.isArray(e)}isCorrectFormat(e,t){switch(e){case"alphanumeric":return new RegExp(/^[a-zA-Z0-9]+$/).test(t);case"numeric":return new RegExp(/^-?\d+(\.\d+)?$/).test(t);case"email":return new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/).test(t);case"date":return new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(t);case"url":return new RegExp(/^(https?):\/\/[^\s$.?#].[^\s]*$/).test(t);case"hexColor":return new RegExp(/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i).test(t)}}isMinimumLength(e,t){return Array.isArray(t)?t.length>=e:t?.toString().length>=e}isMaximumLength(e,t){return Array.isArray(t)?t.length<=e:t.toString().length<=e}isMinimumValue(e,t){return t>=e}isMaximumValue(e,t){return t<=e}matchesPattern(e,t){return new RegExp(e).test(t)}schemaFrom(e){let t={properties:{additionalProperties:!1,required:[]}};for(let r in e){let s=e[r];t.properties.required.push(r),Array.isArray(s)?t.properties[r]=this.generateArraySchema(s):typeof s=="object"&&s!==null?t.properties[r]=this.generateNestedObjectSchema(s):t.properties[r]=this.generatePropertySchema(s)}return t}generateArraySchema(e){let t={type:"array"},r=e.filter(s=>s);if(r.length>0){let s=r[0];r.every(n=>typeof n==typeof s)?typeof s=="object"&&!Array.isArray(s)?t.items=this.generateNestedObjectSchema(s):t.items=this.generatePropertySchema(s):console.warn("All elements in array are not of the same type. Unable to generate a schema for these elements.")}return t}generateNestedObjectSchema(e){let t={type:"object",additionalProperties:!1,required:[]};for(let r in e){let s=e[r];t.required.push(r),typeof s=="object"&&!Array.isArray(s)&&s!==null?t[r]=this.generateNestedObjectSchema(s):t[r]=this.generatePropertySchema(s)}return t}generatePropertySchema(e){let t=typeof e,r={type:t};return t==="string"&&(r.minLength=1),r}};async function I(e){return e.body||{}}function g(e,t="An error occurred"){let r=e?.message||e||t,s=e?.cause?.statusCode||e?.statusCode||400;return{message:r,status:s}}function U(e,t,r={}){let s=Array.isArray(t)?t:[t];if(!e||!e.roles||!Array.isArray(e.roles)){let o=new Error("Unauthorized: User or roles missing");throw o.cause={statusCode:403},o}let i=e.roles.flatMap(o=>o?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(u=>typeof u=="string"?u:u&&typeof u=="object"&&u.actions?u.actions:[]))||[]);if(!s.every(o=>i.includes(o)||i.includes("*")?!0:i.some(a=>{if(a.endsWith(".*")){let l=a.slice(0,-2);return o.startsWith(`${l}.`)}return!1}))){let o=new Error("Unauthorized");throw o.cause={statusCode:403},o}return!0}function B(e,t,r,s,i){let n=e.find(l=>l.service===t);if(!n){let l=new Error(`Function bindings do not include access to service: ${t}`);throw l.cause={statusCode:403},l}if(!n.permissions||n.permissions.length===0)return;for(let l of n.permissions)if(!(l.resource&&l.resource!==r)){if(!l.resource||!l.actions||l.actions.length===0)return;if(l.actions.includes(s)){if(l.targets&&l.targets.length>0){if(!i)return;if(!l.targets.includes(i))continue}return}}let o=i?`:${i}`:"",a=new Error(`Function bindings do not allow: ${t}.${r}.${s}${o}`);throw a.cause={statusCode:403},a}async function y(e,t,r,s,i,n,o){if(!t)return null;let a=e.state.user;if(!a){let u=e.headers.authorization;if(!u){let c=new Error("Unauthorized: No authentication provided");throw c.cause={statusCode:401},c}let f=u.replace(/^Bearer\s+/i,"");if(a=await t.getUserFromToken(f),!a){let c=new Error("Unauthorized: Invalid token");throw c.cause={statusCode:401},c}}U(a,r,{});let l=e.headers["x-function-bindings"];if(l)try{let u=Array.isArray(l)?l[0]:l,f=JSON.parse(u);B(f,s,i,n,o)}catch(u){if(u.cause?.statusCode===403)throw u;let f=new Error("Invalid function bindings header");throw f.cause={statusCode:400},f}return a}var z={properties:{service:{type:"string",minLength:1},level:{type:"string",enum:["info","warn","error","debug"]},message:{type:"string",minLength:1},metadata:{type:"object"}},required:["service","level","message"],additionalProperties:!1};var he=new x(!0);async function G(e,t,r){try{let s=await I(e),i=he.test(z,s);if(!i.success)return e.json({error:"Invalid input",details:i.errors},400);await y(e,r,"observability.event.create","observability","event","create");let{service:n,level:o,message:a,metadata:l}=s;if(!n)return e.json({error:"service is required"},400);if(!o)return e.json({error:"level is required"},400);if(!["info","warn","error","debug"].includes(o))return e.json({error:"level must be one of: info, warn, error, debug"},400);if(!a)return e.json({error:"message is required"},400);let u=await t.log({service:n,level:o,message:a,metadata:l});return e.json({success:!0,event:u},201)}catch(s){let{message:i,status:n}=g(s,"Error logging event");return e.json({error:i},n)}}async function W(e,t,r){try{await y(e,r,"observability.event.read","observability","event","read");let s=e.query.startTime?parseInt(e.query.startTime,10):void 0,i=e.query.endTime?parseInt(e.query.endTime,10):void 0,n=e.query.service||void 0,o=e.query.level||void 0,a=e.query.search||void 0,l=e.query.limit?parseInt(e.query.limit,10):1e3,u=e.query.offset?parseInt(e.query.offset,10):0,f=await t.query({startTime:s,endTime:i,service:n,level:o,search:a,limit:l,offset:u});return e.json({success:!0,count:f.length,events:f},200)}catch(s){let{message:i,status:n}=g(s,"Error querying events");return e.json({error:i},n)}}async function _(e,t,r){try{await y(e,r,"observability.stats.read","observability","stats","read");let s=await t.getStats();return e.json({success:!0,stats:s},200)}catch(s){let{message:i,status:n}=g(s,"Error getting statistics");return e.json({error:i},n)}}var J={properties:{olderThanDays:{type:"number",minimum:1}},required:["olderThanDays"],additionalProperties:!1};var me=new x(!0);async function X(e,t,r){try{let s=await I(e),i=me.test(J,s);if(!i.success)return e.json({error:"Invalid input",details:i.errors},400);await y(e,r,"observability.event.delete","observability","event","delete");let{olderThanDays:n}=s;if(!n)return e.json({error:"olderThanDays is required"},400);let o=await t.deleteOldLogs(n);return e.json({success:!0,deletedCount:o},200)}catch(s){let{message:i,status:n}=g(s,"Error cleaning up logs");return e.json({error:i},n)}}async function Z(e,t,r){try{return await y(e,r,"observability.buffer.write","observability","buffer","write"),await t.flush(),e.json({success:!0},200)}catch(s){let{message:i,status:n}=g(s,"Error flushing buffer");return e.json({error:i},n)}}import v from"node:os";async function Q(e,t){try{await y(e,t,"observability.metrics.read","observability","metrics","read");let r={timestamp:Date.now(),system:{loadAvg:v.loadavg()[0],uptime:v.uptime(),platform:v.platform(),arch:v.arch()},memory:{total:v.totalmem(),free:v.freemem(),used:v.totalmem()-v.freemem(),usagePercent:Number(((v.totalmem()-v.freemem())/v.totalmem()*100).toFixed(2))},process:{heapUsed:process.memoryUsage().heapUsed,heapTotal:process.memoryUsage().heapTotal,external:process.memoryUsage().external,rss:process.memoryUsage().rss,uptime:process.uptime(),pid:process.pid}};return e.json({success:!0,metrics:r},200)}catch(r){let{message:s,status:i}=g(r,"Error getting system metrics");return e.json({error:s},i)}}import{readFileSync as ge,existsSync as ye}from"node:fs";function E(){let e=process.env.MOLNOS_RUNTIME_CONFIG,t={molnos:{dataPath:"data",rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:"molnos-default-signed-url-secret"},server:{host:"127.0.0.1",port:3e3},identity:{apiUrl:"http://127.0.0.1:3000"},context:{apiUrl:"http://127.0.0.1:3000"},services:{storage:{host:"127.0.0.1",port:3001,url:"http://127.0.0.1:3001"},functions:{host:"127.0.0.1",port:3002,url:"http://127.0.0.1:3002"},sites:{host:"127.0.0.1",port:3003,url:"http://127.0.0.1:3003"},databases:{host:"127.0.0.1",port:3004,url:"http://127.0.0.1:3004"},observability:{host:"127.0.0.1",port:3005,url:"http://127.0.0.1:3005"}}};if(!e||!ye(e))return t;try{let r=ge(e,"utf-8");return JSON.parse(r)}catch(r){return console.error("[loadRuntimeConfig] Failed to load runtime config:",r),t}}function K(e,t){let r={enabled:!1,requestsPerMinute:0};if(!t)return r;let s=t.services?.[e];return s||(t.global?t.global:r)}function Y(e,t){let r=e?.api?.port;if(!r)throw new Error('Missing "port" input when create API service!');let s=E(),i=e?.dataPath||s.molnos.dataPath,n=e?.api?.host||s.server.host,o=e?.identityApiUrl!==void 0?e.identityApiUrl:s.identity.apiUrl,a=e?.contextApiUrl!==void 0?e.contextApiUrl:s.context.apiUrl,l=e?.debug||!1,u;return t&&(u=K(t,s.molnos.rateLimit)),{dataPath:i,api:{port:r,host:n},...o?{identityApiUrl:o}:{},...a?{contextApiUrl:a}:{},...u?{rateLimit:u}:{},debug:l}}var T=class{baseUrl;authToken;constructor(t,r){this.baseUrl=t.replace(/\/$/,""),this.authToken=r}async createCustomRole(t,r,s,i){let n=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:t,name:r,description:s,permissions:i})});if(!n.ok){let o=`Failed to create role: ${n.statusText}`;try{o=(await n.json()).error||o}catch{o=await n.text().catch(()=>n.statusText)||o}throw new Error(o)}}async updateRole(t,r){let s=await fetch(`${this.baseUrl}/identity/roles/${t}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!s.ok){let i=await s.json();throw new Error(i.error||`Failed to update role: ${s.statusText}`)}}async deleteRole(t){let r=await fetch(`${this.baseUrl}/identity/roles/${t}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(t,r,s){let i=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:t,description:r,roles:s})});if(!i.ok){let o=`Failed to create service account: ${i.statusText}`;try{o=(await i.json()).error||o}catch{o=await i.text().catch(()=>i.statusText)||o}throw new Error(o)}let n=await i.json();return{id:n.id,apiKey:n.apiKey}}async deleteIdentity(t){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${t}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete service account: ${r.statusText}`)}}async getUserFromToken(t){try{let r=await fetch(`${this.baseUrl}/identity/whoami`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});return r.ok?await r.json():null}catch{return null}}getHeaders(){let t={"Content-Type":"application/json"};return this.authToken&&(t.Authorization=`Bearer ${this.authToken}`),t}};function ee(e){return e?new T(e):null}var k=class{logDir;currentLogFile;writeBuffer=[];bufferSize;flushInterval=null;constructor(t){this.logDir=t.path||"observability-data",this.bufferSize=t.bufferSize||100,this.currentLogFile=this.getLogFileName()}async start(){await w.mkdir(this.logDir,{recursive:!0}),this.flushInterval=setInterval(()=>{this.writeBuffer.length>0&&this.flush().catch(t=>console.error("Failed to flush log buffer:",t))},5e3)}async stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),await this.flush()}getLogFileName(){let t=new Date().toISOString().split("T")[0];return P(this.logDir,`logs-${t}.ndjson`)}async log(t){let r={id:this.generateId(),timestamp:Date.now(),service:t.service,level:t.level,message:t.message,metadata:t.metadata};return this.writeBuffer.push(r),this.writeBuffer.length>=this.bufferSize&&await this.flush(),r}async flush(){if(this.writeBuffer.length===0)return;let t=this.getLogFileName(),r=[...this.writeBuffer];this.writeBuffer=[],t!==this.currentLogFile&&(this.currentLogFile=t);let s=`${r.map(i=>JSON.stringify(i)).join(`
11
+ `),a="",l="",d,f;for(let u of o){let h=u.toLowerCase();if(h.startsWith("content-disposition:")){a=u.substring(20).trim();let b=a.match(/name="([^"]+)"/);b&&(l=b[1]);let p=a.match(/filename="([^"]+)"/);p&&(d=p[1])}else h.startsWith("content-type:")&&(f=u.substring(13).trim())}if(!l)return null;let c=i;return c.length>=2&&c[c.length-2]===13&&c[c.length-1]===10&&(c=c.subarray(0,c.length-2)),{disposition:a,name:l,filename:d,contentType:f,data:c}}import{readFileSync as x}from"fs";import A from"http";import le from"http2";import ce from"https";var M=class{config;rateLimiter;router;shutdownHandlers=[];constructor(e){let t=new L(N(e||{})).get();t.debug&&console.log("Using configuration:",t),this.config=t,this.router=new q;let r=t.rateLimit.requestsPerMinute||R().rateLimit.requestsPerMinute;this.rateLimiter=new D(r,60),t.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(e){return this.router.use(e),this}get(e,...t){return this.router.get(e,...t),this}post(e,...t){return this.router.post(e,...t),this}put(e,...t){return this.router.put(e,...t),this}delete(e,...t){return this.router.delete(e,...t),this}patch(e,...t){return this.router.patch(e,...t),this}any(e,...t){return this.router.any(e,...t),this}options(e,...t){return this.router.options(e,...t),this}start(){let e=this.createServer(),{port:t,host:r}=this.config;return this.setupGracefulShutdown(e),e.listen(t,r,()=>{let s=e.address(),i=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${i}://${s.address!=="::"?s.address:"localhost"}:${s.port}`)}),e}createServer(){let e=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let t={key:x(this.config.sslKey),cert:x(this.config.sslCert),...this.config.sslCa?{ca:x(this.config.sslCa)}:{}};return le.createSecureServer(t,e)}catch(t){throw t.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${t.message}`):t}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let t={key:x(this.config.sslKey),cert:x(this.config.sslCert),...this.config.sslCa?{ca:x(this.config.sslCa)}:{}};return ce.createServer(t,e)}catch(t){throw t.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${t.message}`):t}}return A.createServer(e)}async rateLimitMiddleware(e,t){let r=e.req.socket.remoteAddress||"unknown";return e.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),e.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(r).toString()),e.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(r).toString()),this.rateLimiter.isAllowed(r)?t():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(e,t){let r=Date.now(),s=e.method||"UNKNOWN",i=e.url||"/unknown",n=this.config.debug;try{if(this.setCorsHeaders(t,e),this.setSecurityHeaders(t,this.config.useHttps),n&&console.log(`${s} ${i}`),e.method==="OPTIONS"){if(t instanceof A.ServerResponse)t.statusCode=204,t.end();else{let a=t;a.writeHead(204),a.end()}return}try{e.body=await this.parseBody(e)}catch(a){return n&&console.error("Body parsing error:",a.message),this.respond(t,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let o=await this.router.handle(e,t);return o?o._handled?void 0:this.respond(t,o):this.respond(t,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(o){return console.error("Server error:",o),this.respond(t,{statusCode:500,body:{error:"Internal Server Error",message:n?o.message:"An unexpected error occurred"}})}finally{n&&this.logDuration(r,s,i)}}logDuration(e,t,r){let s=Date.now()-e;console.log(`${t} ${r} completed in ${s}ms`)}async parseBody(e){return new Promise((t,r)=>{let s=[],i=0,n=this.config.maxBodySize,o=!1,a=null,l=this.config.debug,d=e.headers["content-type"]||"";l&&console.log("Content-Type:",d),this.config.requestTimeout>0&&(a=setTimeout(()=>{o||(o=!0,l&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let f=()=>{a&&(clearTimeout(a),a=null)};e.on("data",c=>{if(!o){if(i+=c.length,l&&console.log(`Received chunk: ${c.length} bytes, total size: ${i}`),i>n){o=!0,f(),l&&console.log(`Body size exceeded limit: ${i} > ${n}`),r(new Error("Request body too large"));return}s.push(c)}}),e.on("end",()=>{if(!o){o=!0,f(),l&&console.log(`Request body complete: ${i} bytes`);try{if(s.length>0){let c=Buffer.concat(s);if(d.includes("application/json"))try{let u=c.toString("utf8");t(JSON.parse(u))}catch(u){r(new Error(`Invalid JSON in request body: ${u.message}`))}else if(d.includes("application/x-www-form-urlencoded")){let u=c.toString("utf8"),h={};new URLSearchParams(u).forEach((b,p)=>{h[p]=b}),t(h)}else if(d.includes("multipart/form-data"))try{let u=F(c,d);t(u)}catch(u){r(new Error(`Invalid multipart form data: ${u.message}`))}else this.isBinaryContentType(d)?t(c):t(c.toString("utf8"))}else t({})}catch(c){r(new Error(`Invalid request body: ${c}`))}}}),e.on("error",c=>{o||(o=!0,f(),r(new Error(`Error reading request body: ${c.message}`)))}),e.on("close",()=>{f()})})}isBinaryContentType(e){return["application/octet-stream","application/pdf","application/zip","application/gzip","application/x-tar","application/x-rar-compressed","application/x-7z-compressed","image/","video/","audio/","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument","application/msword","application/vnd.ms-powerpoint"].some(r=>e.includes(r))}setCorsHeaders(e,t){let r=t.headers.origin,{allowedDomains:s=["*"]}=this.config;!r||s.length===0||s.includes("*")?e.setHeader("Access-Control-Allow-Origin","*"):s.includes(r)&&(e.setHeader("Access-Control-Allow-Origin",r),e.setHeader("Vary","Origin")),e.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),e.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(e,t=!1){let r={"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'self'; script-src 'self'; object-src 'none'","X-XSS-Protection":"1; mode=block"};if((t||this.config.useHttp2)&&(r["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),e instanceof A.ServerResponse)Object.entries(r).forEach(([s,i])=>{e.setHeader(s,i)});else{let s=e;Object.entries(r).forEach(([i,n])=>{s.setHeader(i,n)})}}respond(e,t){let r={...t.headers||{}};(i=>typeof i.writeHead=="function"&&typeof i.end=="function")(e)?(e.writeHead(t.statusCode,r),t.body===null||t.body===void 0?e.end():t.isRaw||typeof t.body=="string"?e.end(t.body):e.end(JSON.stringify(t.body))):(console.warn("Unexpected response object type without writeHead/end methods"),e.writeHead?.(t.statusCode,r),t.body===null||t.body===void 0?e.end?.():t.isRaw||typeof t.body=="string"?e.end?.(t.body):e.end?.(JSON.stringify(t.body)))}setupGracefulShutdown(e){let t=o=>{console.log("Shutting down MikroServe server..."),o&&console.error("Error:",o),this.cleanupShutdownHandlers(),e.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(o?1:0))})},r=()=>t(),s=()=>t(),i=o=>t(o),n=o=>t(o);this.shutdownHandlers=[r,s,i,n],process.on("SIGINT",r),process.on("SIGTERM",s),process.on("uncaughtException",i),process.on("unhandledRejection",n)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[e,t,r,s]=this.shutdownHandlers;process.removeListener("SIGINT",e),process.removeListener("SIGTERM",t),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",s),this.shutdownHandlers=[]}}};import{join as P}from"node:path";import{promises as w}from"node:fs";var E=class{isSilent;propertyPath="";constructor(e=!1){this.isSilent=e}test(e,t){if(!t)throw new Error("Missing input!");this.updatePropertyPath();let{results:r,errors:s}=this.validate(e.properties,t),i=this.compileErrors(r,s),n=this.isSuccessful(r,i);return{errors:i,success:n}}compileErrors(e,t){let r=e.filter(s=>s.success===!1);return[...t,...r].flatMap(s=>s)}isSuccessful(e,t){return e.every(r=>r.success===!0)&&t.length===0}validate(e,t,r=[],s=[]){let i=e?.additionalProperties??!0,n=e?.required||[];s=this.checkForRequiredKeysErrors(n,t,s),s=this.checkForDisallowedProperties(Object.keys(t),Object.keys(e),s,i);for(let o in e){let a=n.includes(o)&&o!=="required",l=e[o],d=t[o],f=l.additionalProperties??!0;a&&(s=this.checkForRequiredKeysErrors(l.required||[],d,s)),this.isDefined(d)&&(this.handleValidation(o,d,l,r),s=this.checkForDisallowedProperties(Object.keys(d),Object.keys(l),s,f),this.handleNestedObject(d,l,r,s))}return{results:r,errors:s}}updatePropertyPath(e,t=""){if(!e){this.propertyPath="";return}t&&(this.propertyPath=t),this.propertyPath=`${this.propertyPath}.${e}`,this.propertyPath.startsWith(".")&&(this.propertyPath=this.propertyPath.substring(1,this.propertyPath.length))}isDefined(e){return!!(typeof e=="number"&&e===0||e||e===""||typeof e=="boolean")}checkForRequiredKeysErrors(e,t,r){if(!this.areRequiredKeysPresent(e,t)){let s=t?Object.keys(t):[],i=this.findNonOverlappingElements(e,s),n=i.length>0?`Missing the required key: '${i.join(", ")}'!`:`Missing values for required keys: '${s.filter(o=>!t[o]).join(", ")}'!`;r.push({key:"",value:t,success:!1,error:n})}return r}checkForDisallowedProperties(e,t,r,s){if(!s){let i=this.findNonOverlappingElements(e,t);i.length>0&&r.push({key:`${t}`,value:e,success:!1,error:`Has additional (disallowed) properties: '${i.join(", ")}'!`})}return r}handleValidation(e,t,r,s){this.updatePropertyPath(e);let i=this.validateProperty(this.propertyPath,r,t);s.push(...i);let n=(a,l)=>{a.forEach(d=>{let f=this.validateProperty(this.propertyPath,l.items,d);s.push(...f)}),this.updatePropertyPath()},o=a=>{let l=Object.keys(a),d=this.propertyPath;l.forEach(f=>{if(this.updatePropertyPath(f,d),this.isArray(a[f])&&r[f]?.items!=null)n(a[f],r[f]);else{let c=this.validateProperty(this.propertyPath,r[f],a[f]);s.push(...c)}})};this.isArray(t)&&r.items!=null?n(t,r):this.isObject(t)?o(t):this.updatePropertyPath()}handleNestedObject(e,t,r,s){if(this.isObject(e)){let i=this.getNestedObjects(e);for(let n of i){let o=t[n],a=e[n];o&&typeof a=="object"&&this.validate(o,a,r,s)}}}getNestedObjects(e){return Object.keys(e).filter(t=>{if(this.isObject(e))return t})}findNonOverlappingElements(e,t){return e.filter(r=>!t.includes(r))}areRequiredKeysPresent(e,t=[]){return e.every(r=>Object.keys(t).includes(r)?this.isDefined(t[r]):!1)}validateProperty(e,t,r){return this.validateInput(t,r).map(i=>{let{success:n,error:o}=i;return{key:e,value:r,success:n,error:o??""}})}validateInput(e,t){if(e){let r=[{condition:()=>e.type,validator:()=>this.isCorrectType(e.type,t),error:"Invalid type"},{condition:()=>e.format,validator:()=>this.isCorrectFormat(e.format,t),error:"Invalid format"},{condition:()=>e.minLength,validator:()=>this.isMinimumLength(e.minLength,t),error:"Length too short"},{condition:()=>e.maxLength,validator:()=>this.isMaximumLength(e.maxLength,t),error:"Length too long"},{condition:()=>e.minValue,validator:()=>this.isMinimumValue(e.minValue,t),error:"Value too small"},{condition:()=>e.maxValue,validator:()=>this.isMaximumValue(e.maxValue,t),error:"Value too large"},{condition:()=>e.matchesPattern,validator:()=>this.matchesPattern(e.matchesPattern,t),error:"Pattern does not match"}],s=[];for(let i of r)i.condition()&&!i.validator()&&s.push({success:!1,error:i.error});return s}else this.isSilent||console.warn(`Missing property '${e}' for match '${t}'. Skipping...`);return[{success:!0}]}isCorrectType(e,t){return Array.isArray(e)||(e=[e]),e.some(r=>{switch(r){case"string":return typeof t=="string";case"number":return typeof t=="number"&&!isNaN(t);case"boolean":return typeof t=="boolean";case"object":return this.isObject(t);case"array":return this.isArray(t)}})}isObject(e){return e!==null&&!this.isArray(e)&&typeof e=="object"&&e instanceof Object&&Object.prototype.toString.call(e)==="[object Object]"}isArray(e){return Array.isArray(e)}isCorrectFormat(e,t){switch(e){case"alphanumeric":return new RegExp(/^[a-zA-Z0-9]+$/).test(t);case"numeric":return new RegExp(/^-?\d+(\.\d+)?$/).test(t);case"email":return new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/).test(t);case"date":return new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(t);case"url":return new RegExp(/^(https?):\/\/[^\s$.?#].[^\s]*$/).test(t);case"hexColor":return new RegExp(/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i).test(t)}}isMinimumLength(e,t){return Array.isArray(t)?t.length>=e:t?.toString().length>=e}isMaximumLength(e,t){return Array.isArray(t)?t.length<=e:t.toString().length<=e}isMinimumValue(e,t){return t>=e}isMaximumValue(e,t){return t<=e}matchesPattern(e,t){return new RegExp(e).test(t)}schemaFrom(e){let t={properties:{additionalProperties:!1,required:[]}};for(let r in e){let s=e[r];t.properties.required.push(r),Array.isArray(s)?t.properties[r]=this.generateArraySchema(s):typeof s=="object"&&s!==null?t.properties[r]=this.generateNestedObjectSchema(s):t.properties[r]=this.generatePropertySchema(s)}return t}generateArraySchema(e){let t={type:"array"},r=e.filter(s=>s);if(r.length>0){let s=r[0];r.every(n=>typeof n==typeof s)?typeof s=="object"&&!Array.isArray(s)?t.items=this.generateNestedObjectSchema(s):t.items=this.generatePropertySchema(s):console.warn("All elements in array are not of the same type. Unable to generate a schema for these elements.")}return t}generateNestedObjectSchema(e){let t={type:"object",additionalProperties:!1,required:[]};for(let r in e){let s=e[r];t.required.push(r),typeof s=="object"&&!Array.isArray(s)&&s!==null?t[r]=this.generateNestedObjectSchema(s):t[r]=this.generatePropertySchema(s)}return t}generatePropertySchema(e){let t=typeof e,r={type:t};return t==="string"&&(r.minLength=1),r}};async function I(e){return e.body||{}}function g(e,t="An error occurred"){let r=e?.message||e||t,s=e?.cause?.statusCode||e?.statusCode||400;return{message:r,status:s}}function V(e,t,r={}){let s=Array.isArray(t)?t:[t];if(!e||!e.roles||!Array.isArray(e.roles)){let o=new Error("Unauthorized: User or roles missing");throw o.cause={statusCode:403},o}let i=e.roles.flatMap(o=>o?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(d=>typeof d=="string"?d:d&&typeof d=="object"&&d.actions?d.actions:[]))||[]);if(!s.every(o=>i.includes(o)||i.includes("*")?!0:i.some(a=>{if(a.endsWith(".*")){let l=a.slice(0,-2);return o.startsWith(`${l}.`)}return!1}))){let o=new Error("Unauthorized");throw o.cause={statusCode:403},o}return!0}function U(e,t,r,s,i){let n=e.find(l=>l.service===t);if(!n){let l=new Error(`Function bindings do not include access to service: ${t}`);throw l.cause={statusCode:403},l}if(!n.permissions||n.permissions.length===0)return;for(let l of n.permissions)if(!(l.resource&&l.resource!==r)){if(!l.resource||!l.actions||l.actions.length===0)return;if(l.actions.includes(s)){if(l.targets&&l.targets.length>0){if(!i)return;if(!l.targets.includes(i))continue}return}}let o=i?`:${i}`:"",a=new Error(`Function bindings do not allow: ${t}.${r}.${s}${o}`);throw a.cause={statusCode:403},a}import{readFileSync as ue,existsSync as de}from"node:fs";function S(){let e=process.env.MOLNOS_RUNTIME_CONFIG,t={molnos:{dataPath:"data",rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:"molnos-default-signed-url-secret"},server:{host:"127.0.0.1",port:3e3},identity:{apiUrl:"http://127.0.0.1:3000"},context:{apiUrl:"http://127.0.0.1:3000"},services:{storage:{host:"127.0.0.1",port:3001,url:"http://127.0.0.1:3001"},functions:{host:"127.0.0.1",port:3002,url:"http://127.0.0.1:3002"},sites:{host:"127.0.0.1",port:3003,url:"http://127.0.0.1:3003"},databases:{host:"127.0.0.1",port:3004,url:"http://127.0.0.1:3004"},observability:{host:"127.0.0.1",port:3005,url:"http://127.0.0.1:3005"}}};if(!e||!de(e))return t;try{let r=ue(e,"utf-8");return JSON.parse(r)}catch(r){return console.error("[loadRuntimeConfig] Failed to load runtime config:",r),t}}async function y(e,t,r,s,i,n,o){if(!t)return null;let a=e.headers["x-molnos-service-token"];if(a){let f=S();if(f.internalServiceSecret&&a===f.internalServiceSecret)return null}let l=e.state.user;if(!l){let f=e.headers.authorization;if(!f){let u=new Error("Unauthorized: No authentication provided");throw u.cause={statusCode:401},u}let c=f.replace(/^Bearer\s+/i,"");if(l=await t.getUserFromToken(c),!l){let u=new Error("Unauthorized: Invalid token");throw u.cause={statusCode:401},u}}V(l,r,{});let d=e.headers["x-function-bindings"];if(d)try{let f=Array.isArray(d)?d[0]:d,c=JSON.parse(f);U(c,s,i,n,o)}catch(f){if(f.cause?.statusCode===403)throw f;let c=new Error("Invalid function bindings header");throw c.cause={statusCode:400},c}return l}var B={properties:{service:{type:"string",minLength:1},level:{type:"string",enum:["info","warn","error","debug"]},message:{type:"string",minLength:1},metadata:{type:"object"}},required:["service","level","message"],additionalProperties:!1};var he=new E(!0);async function z(e,t,r){try{let s=await I(e),i=he.test(B,s);if(!i.success)return e.json({error:"Invalid input",details:i.errors},400);await y(e,r,"observability.event.create","observability","event","create");let{service:n,level:o,message:a,metadata:l}=s;if(!n)return e.json({error:"service is required"},400);if(!o)return e.json({error:"level is required"},400);if(!["info","warn","error","debug"].includes(o))return e.json({error:"level must be one of: info, warn, error, debug"},400);if(!a)return e.json({error:"message is required"},400);let d=await t.log({service:n,level:o,message:a,metadata:l});return e.json({success:!0,event:d},201)}catch(s){let{message:i,status:n}=g(s,"Error logging event");return e.json({error:i},n)}}async function G(e,t,r){try{await y(e,r,"observability.event.read","observability","event","read");let s=e.query.startTime?parseInt(e.query.startTime,10):void 0,i=e.query.endTime?parseInt(e.query.endTime,10):void 0,n=e.query.service||void 0,o=e.query.level||void 0,a=e.query.search||void 0,l=e.query.limit?parseInt(e.query.limit,10):1e3,d=e.query.offset?parseInt(e.query.offset,10):0,f=await t.query({startTime:s,endTime:i,service:n,level:o,search:a,limit:l,offset:d});return e.json({success:!0,count:f.length,events:f},200)}catch(s){let{message:i,status:n}=g(s,"Error querying events");return e.json({error:i},n)}}async function W(e,t,r){try{await y(e,r,"observability.stats.read","observability","stats","read");let s=await t.getStats();return e.json({success:!0,stats:s},200)}catch(s){let{message:i,status:n}=g(s,"Error getting statistics");return e.json({error:i},n)}}var _={properties:{olderThanDays:{type:"number",minimum:1}},required:["olderThanDays"],additionalProperties:!1};var me=new E(!0);async function J(e,t,r){try{let s=await I(e),i=me.test(_,s);if(!i.success)return e.json({error:"Invalid input",details:i.errors},400);await y(e,r,"observability.event.delete","observability","event","delete");let{olderThanDays:n}=s;if(!n)return e.json({error:"olderThanDays is required"},400);let o=await t.deleteOldLogs(n);return e.json({success:!0,deletedCount:o},200)}catch(s){let{message:i,status:n}=g(s,"Error cleaning up logs");return e.json({error:i},n)}}async function X(e,t,r){try{return await y(e,r,"observability.buffer.write","observability","buffer","write"),await t.flush(),e.json({success:!0},200)}catch(s){let{message:i,status:n}=g(s,"Error flushing buffer");return e.json({error:i},n)}}import v from"node:os";async function Z(e,t){try{await y(e,t,"observability.metrics.read","observability","metrics","read");let r={timestamp:Date.now(),system:{loadAvg:v.loadavg()[0],uptime:v.uptime(),platform:v.platform(),arch:v.arch()},memory:{total:v.totalmem(),free:v.freemem(),used:v.totalmem()-v.freemem(),usagePercent:Number(((v.totalmem()-v.freemem())/v.totalmem()*100).toFixed(2))},process:{heapUsed:process.memoryUsage().heapUsed,heapTotal:process.memoryUsage().heapTotal,external:process.memoryUsage().external,rss:process.memoryUsage().rss,uptime:process.uptime(),pid:process.pid}};return e.json({success:!0,metrics:r},200)}catch(r){let{message:s,status:i}=g(r,"Error getting system metrics");return e.json({error:s},i)}}import{getRandomValues as ge}from"node:crypto";var $=class{options;defaultLength=16;defaultOnlyLowerCase=!1;defaultStyle="extended";defaultUrlSafe=!0;constructor(e){if(this.options={},e)for(let[t,r]of Object.entries(e))this.options[t]=this.generateConfig(r)}add(e){if(!e?.name)throw new Error("Missing name for the ID configuration");let t=this.generateConfig(e);this.options[e.name]=t}remove(e){if(!e?.name)throw new Error("Missing name for the ID configuration");delete this.options[e.name]}create(e,t,r,s){let i=this.generateConfig({length:e,style:t,onlyLowerCase:r,urlSafe:s});return this.generateId(i)}custom(e){if(this.options[e])return this.generateId(this.options[e]);throw new Error(`No configuration found with name: ${e}`)}generateConfig(e){return{name:e?.name||"",length:e?.length||this.defaultLength,onlyLowerCase:e?.onlyLowerCase??this.defaultOnlyLowerCase,style:e?.style||this.defaultStyle,urlSafe:e?.urlSafe??this.defaultUrlSafe}}getCharacterSet(e,t,r){if(e==="hex")return t?"0123456789abcdef":"0123456789ABCDEFabcdef";if(e==="alphanumeric")return t?"abcdefghijklmnopqrstuvwxyz0123456789":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";if(e==="extended")return r?t?"abcdefghijklmnopqrstuvwxyz0123456789-._~":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~":t?"abcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:";throw new Error(`Unknown ID style "${e} provided. Must be one of "extended" (default), "alphanumeric", or "hex".`)}generateId(e){let{length:t,onlyLowerCase:r,style:s,urlSafe:i}=e;if(t<0||t===0)throw new Error("ID length cannot be negative");let n=this.getCharacterSet(s,r,i),o=(2<<Math.log(n.length-1)/Math.LN2)-1,a=Math.ceil(1.6*o*t/n.length),l="";for(;l.length<t;){let d=new Uint8Array(a);ge(d);for(let f=0;f<a;f++){let c=d[f]&o;if(c<n.length&&(l+=n[c],l.length===t))break}}return l}};var ye=8;function Q(){return new $().create(ye,"alphanumeric",!1,!0)}function K(e,t){let r={enabled:!1,requestsPerMinute:0};if(!t)return r;let s=t.services?.[e];return s||(t.global?t.global:r)}function Y(e,t){let r=e?.api?.port;if(!r)throw new Error('Missing "port" input when create API service!');let s=S(),i=e?.dataPath||s.molnos.dataPath,n=e?.api?.host||s.server.host,o=e?.identityApiUrl!==void 0?e.identityApiUrl:s.identity.apiUrl,a=e?.contextApiUrl!==void 0?e.contextApiUrl:s.context.apiUrl,l=e?.debug||!1,d;return t&&(d=K(t,s.molnos.rateLimit)),{dataPath:i,api:{port:r,host:n},...o?{identityApiUrl:o}:{},...a?{contextApiUrl:a}:{},...d?{rateLimit:d}:{},debug:l}}var T=class{baseUrl;authToken;constructor(t,r){this.baseUrl=t.replace(/\/$/,""),this.authToken=r}async createCustomRole(t,r,s,i){let n=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:t,name:r,description:s,permissions:i})});if(!n.ok){let o=`Failed to create role: ${n.statusText}`;try{o=(await n.json()).error||o}catch{o=await n.text().catch(()=>n.statusText)||o}throw new Error(o)}}async updateRole(t,r){let s=await fetch(`${this.baseUrl}/identity/roles/${t}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!s.ok){let i=await s.json();throw new Error(i.error||`Failed to update role: ${s.statusText}`)}}async deleteRole(t){let r=await fetch(`${this.baseUrl}/identity/roles/${t}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(t,r,s){let i=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:t,description:r,roles:s})});if(!i.ok){let o=`Failed to create service account: ${i.statusText}`;try{o=(await i.json()).error||o}catch{o=await i.text().catch(()=>i.statusText)||o}throw new Error(o)}let n=await i.json();return{id:n.id,apiKey:n.apiKey}}async deleteIdentity(t){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${t}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete service account: ${r.statusText}`)}}async getUserFromToken(t){try{let r=await fetch(`${this.baseUrl}/identity/whoami`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});return r.ok?await r.json():null}catch{return null}}getHeaders(){let t={"Content-Type":"application/json"};return this.authToken&&(t.Authorization=`Bearer ${this.authToken}`),t}};function ee(e){return e?new T(e):null}var k=class{logDir;currentLogFile;writeBuffer=[];bufferSize;flushInterval=null;constructor(t){this.logDir=t.path||"observability-data",this.bufferSize=t.bufferSize||100,this.currentLogFile=this.getLogFileName()}async start(){await w.mkdir(this.logDir,{recursive:!0}),this.flushInterval=setInterval(()=>{this.writeBuffer.length>0&&this.flush().catch(t=>console.error("Failed to flush log buffer:",t))},5e3)}async stop(){this.flushInterval&&(clearInterval(this.flushInterval),this.flushInterval=null),await this.flush()}getLogFileName(){let t=new Date().toISOString().split("T")[0];return P(this.logDir,`logs-${t}.ndjson`)}async log(t){let r={id:this.generateId(),timestamp:Date.now(),service:t.service,level:t.level,message:t.message,metadata:t.metadata};return this.writeBuffer.push(r),this.writeBuffer.length>=this.bufferSize&&await this.flush(),r}async flush(){if(this.writeBuffer.length===0)return;let t=this.getLogFileName(),r=[...this.writeBuffer];this.writeBuffer=[],t!==this.currentLogFile&&(this.currentLogFile=t);let s=`${r.map(i=>JSON.stringify(i)).join(`
12
12
  `)}
13
- `;await w.appendFile(this.currentLogFile,s,"utf8")}generateId(){return V()}async query(t={}){let{startTime:r,endTime:s,service:i,level:n,search:o,limit:a=1e3,offset:l=0}=t,u=await this.getLogFilesInRange(r,s),f=o?.toLowerCase(),c=[],d=0;for(let h of u){let b=P(this.logDir,h);try{let te=(await w.readFile(b,"utf8")).trim().split(`
14
- `);for(let H of te)if(H)try{let C=JSON.parse(H);if(r&&C.timestamp<r||s&&C.timestamp>s||i&&!C.service.startsWith(i)||n&&C.level!==n||f&&!C.message?.toLowerCase().includes(f)&&!C.service?.toLowerCase().includes(f))continue;if(d<l){d++;continue}if(c.push(C),c.length>=a)return c}catch{}}catch{}}return c}async getLogFilesInRange(t,r){try{let i=(await w.readdir(this.logDir)).filter(o=>o.startsWith("logs-")&&o.endsWith(".ndjson"));return!t&&!r?i.sort().reverse():i.filter(o=>{let a=o.replace("logs-","").replace(".ndjson",""),l=new Date(a).getTime();return!(t&&l<t-864e5||r&&l>r+864e5)}).sort().reverse()}catch{return[]}}async getStats(){try{let r=(await w.readdir(this.logDir)).filter(i=>i.startsWith("logs-")&&i.endsWith(".ndjson")),s=await Promise.all(r.map(async i=>{let n=P(this.logDir,i),o=await w.stat(n);return{name:i,size:o.size,date:i.replace("logs-","").replace(".ndjson","")}}));return{totalLogFiles:r.length,logFiles:s.sort((i,n)=>n.date.localeCompare(i.date))}}catch{return{totalLogFiles:0,logFiles:[]}}}async deleteOldLogs(t){let r=Date.now()-t*864e5,i=(await w.readdir(this.logDir)).filter(o=>o.startsWith("logs-")&&o.endsWith(".ndjson")),n=0;for(let o of i){let a=o.replace("logs-","").replace(".ndjson","");if(new Date(a).getTime()<r){let u=P(this.logDir,o);await w.unlink(u),n++}}return n}};async function ve(e){let t=e.dataPath||"data",r=P(t,"observability"),s=new k({path:r,bufferSize:100});await s.start();let i=ee(e.identityApiUrl),n=E(),o=new M({port:e.api.port||n.services.observability.port,host:e.api.host||n.services.observability.host,rateLimit:e.rateLimit||{enabled:!1,requestsPerMinute:0}});return o.post("/events",async l=>G(l,s,i)),o.get("/events",async l=>W(l,s,i)),o.get("/stats",async l=>_(l,s,i)),o.delete("/events/cleanup",async l=>X(l,s,i)),o.post("/flush",async l=>Z(l,s,i)),o.get("/metrics/system",async l=>Q(l,i)),{server:o.start(),observability:s}}if(import.meta.url===`file://${process.argv[1]}`){let e=E(),r=process.argv.slice(2).find(n=>n.startsWith("--port=")),s=r?parseInt(r.split("=")[1],10):e.services.observability.port,i=Y({api:{port:s}},"observability");ve(i)}export{k as ObservabilityService,ve as startServer};
13
+ `;await w.appendFile(this.currentLogFile,s,"utf8")}generateId(){return Q()}async query(t={}){let{startTime:r,endTime:s,service:i,level:n,search:o,limit:a=1e3,offset:l=0}=t,d=await this.getLogFilesInRange(r,s),f=o?.toLowerCase(),c=[],u=0;for(let h of d){let b=P(this.logDir,h);try{let te=(await w.readFile(b,"utf8")).trim().split(`
14
+ `);for(let H of te)if(H)try{let C=JSON.parse(H);if(r&&C.timestamp<r||s&&C.timestamp>s||i&&!C.service.startsWith(i)||n&&C.level!==n||f&&!C.message?.toLowerCase().includes(f)&&!C.service?.toLowerCase().includes(f))continue;if(u<l){u++;continue}if(c.push(C),c.length>=a)return c}catch{}}catch{}}return c}async getLogFilesInRange(t,r){try{let i=(await w.readdir(this.logDir)).filter(o=>o.startsWith("logs-")&&o.endsWith(".ndjson"));return!t&&!r?i.sort().reverse():i.filter(o=>{let a=o.replace("logs-","").replace(".ndjson",""),l=new Date(a).getTime();return!(t&&l<t-864e5||r&&l>r+864e5)}).sort().reverse()}catch{return[]}}async getStats(){try{let r=(await w.readdir(this.logDir)).filter(i=>i.startsWith("logs-")&&i.endsWith(".ndjson")),s=await Promise.all(r.map(async i=>{let n=P(this.logDir,i),o=await w.stat(n);return{name:i,size:o.size,date:i.replace("logs-","").replace(".ndjson","")}}));return{totalLogFiles:r.length,logFiles:s.sort((i,n)=>n.date.localeCompare(i.date))}}catch{return{totalLogFiles:0,logFiles:[]}}}async deleteOldLogs(t){let r=Date.now()-t*864e5,i=(await w.readdir(this.logDir)).filter(o=>o.startsWith("logs-")&&o.endsWith(".ndjson")),n=0;for(let o of i){let a=o.replace("logs-","").replace(".ndjson","");if(new Date(a).getTime()<r){let d=P(this.logDir,o);await w.unlink(d),n++}}return n}};async function ve(e){let t=e.dataPath||"data",r=P(t,"observability"),s=new k({path:r,bufferSize:100});await s.start();let i=ee(e.identityApiUrl),n=S(),o=new M({port:e.api.port||n.services.observability.port,host:e.api.host||n.services.observability.host,rateLimit:e.rateLimit||{enabled:!1,requestsPerMinute:0}});return o.post("/events",async l=>z(l,s,i)),o.get("/events",async l=>G(l,s,i)),o.get("/stats",async l=>W(l,s,i)),o.delete("/events/cleanup",async l=>J(l,s,i)),o.post("/flush",async l=>X(l,s,i)),o.get("/metrics/system",async l=>Z(l,i)),{server:o.start(),observability:s}}if(import.meta.url===`file://${process.argv[1]}`){let e=S(),r=process.argv.slice(2).find(n=>n.startsWith("--port=")),s=r?parseInt(r.split("=")[1],10):e.services.observability.port,i=Y({api:{port:s}},"observability");ve(i)}export{k as ObservabilityService,ve as startServer};
package/dist/sites.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // MolnOS Core - See LICENSE file for copyright and license details.
2
- var U=class{requests=new Map;limit;windowMs;constructor(t=100,e=60){this.limit=t,this.windowMs=e*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return(!s||s.resetTime<e)&&(s={count:0,resetTime:e+this.windowMs},this.requests.set(r,s)),s.count++,s.count<=this.limit}getRemainingRequests(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return!s||s.resetTime<e?this.limit:Math.max(0,this.limit-s.count)}getResetTime(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return!s||s.resetTime<e?Math.floor((e+this.windowMs)/1e3):Math.floor(s.resetTime/1e3)}cleanup(){let t=Date.now();for(let[e,r]of this.requests.entries())r.resetTime<t&&this.requests.delete(e)}};import{URL as pt}from"url";var _=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(t){return this.globalMiddlewares.push(t),this}get(t,...e){let r=e.pop();return this.register("GET",t,r,e)}post(t,...e){let r=e.pop();return this.register("POST",t,r,e)}put(t,...e){let r=e.pop();return this.register("PUT",t,r,e)}delete(t,...e){let r=e.pop();return this.register("DELETE",t,r,e)}patch(t,...e){let r=e.pop();return this.register("PATCH",t,r,e)}any(t,...e){let r=e.pop(),s=e;return this.register("GET",t,r,s),this.register("POST",t,r,s),this.register("PUT",t,r,s),this.register("DELETE",t,r,s),this.register("PATCH",t,r,s),this.register("OPTIONS",t,r,s),this}options(t,...e){let r=e.pop();return this.register("OPTIONS",t,r,e)}match(t,e){for(let r of this.routes){if(r.method!==t)continue;let s=this.pathPatterns.get(r.path);if(!s)continue;let o=s.pattern.exec(e);if(!o)continue;let n={};return s.paramNames.forEach((i,a)=>{n[i]=o[a+1]||""}),{route:r,params:n}}return null}async handle(t,e){let r=t.method||"GET",s=new pt(t.url||"/",`http://${t.headers.host}`),o=s.pathname,n=this.match(r,o);if(!n)return null;let{route:i,params:a}=n,l={};s.searchParams.forEach((c,d)=>{l[d]=c});let u={req:t,res:e,params:a,query:l,body:t.body||{},headers:t.headers,path:o,state:{},raw:()=>e,binary:(c,d="application/octet-stream",h=200)=>({statusCode:h,body:c,headers:{"Content-Type":d,"Content-Length":c.length.toString()},isRaw:!0}),text:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"text/plain"}}),form:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"application/json"}}),html:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"text/html"}}),redirect:(c,d=302)=>({statusCode:d,body:null,headers:{Location:c}}),status:function(c){return{raw:()=>e,binary:(d,h="application/octet-stream")=>({statusCode:c,body:d,headers:{"Content-Type":h,"Content-Length":d.length.toString()},isRaw:!0}),text:d=>({statusCode:c,body:d,headers:{"Content-Type":"text/plain"}}),json:d=>({statusCode:c,body:d,headers:{"Content-Type":"application/json"}}),html:d=>({statusCode:c,body:d,headers:{"Content-Type":"text/html"}}),form:d=>({statusCode:c,body:d,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(d,h=302)=>({statusCode:h,body:null,headers:{Location:d}}),status:d=>this.status(d)}}},p=[...this.globalMiddlewares,...i.middlewares];return this.executeMiddlewareChain(u,p,i.handler)}register(t,e,r,s=[]){return this.routes.push({method:t,path:e,handler:r,middlewares:s}),this.pathPatterns.set(e,this.createPathPattern(e)),this}createPathPattern(t){let e=[],r=t.replace(/\/:[^/]+/g,s=>{let o=s.slice(2);return e.push(o),"/([^/]+)"});return r.endsWith("/*")?(r=`${r.slice(0,-2)}(?:/(.*))?`,e.push("wildcard")):r=r.replace(/\/$/,"/?"),{pattern:new RegExp(`^${r}$`),paramNames:e}}async executeMiddlewareChain(t,e,r){let s=0,o=async()=>{if(s<e.length){let n=e[s++];return n(t,o)}return r(t)};return o()}};var E=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:ht(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function ht(t){return t==="true"||t===!0}var k=class extends Error{constructor(t){super(t),this.name="ValidationError",this.message=t||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as ft,readFileSync as mt}from"node:fs";var H=class{config={};options=[];validators=[];autoValidate=!0;constructor(t){let e=t?.configFilePath,r=t?.args||[],s=t?.config||{};this.options=t?.options||[],this.validators=t?.validators||[],t?.autoValidate!==void 0&&(this.autoValidate=t.autoValidate),this.config=this.createConfig(e,r,s)}deepMerge(t,e){let r={...t};for(let s in e)e[s]!==void 0&&(e[s]!==null&&typeof e[s]=="object"&&!Array.isArray(e[s])&&s in t&&t[s]!==null&&typeof t[s]=="object"&&!Array.isArray(t[s])?r[s]=this.deepMerge(t[s],e[s]):e[s]!==void 0&&(r[s]=e[s]));return r}setValueAtPath(t,e,r){let s=e.split("."),o=t;for(let i=0;i<s.length-1;i++){let a=s[i];!(a in o)||o[a]===null?o[a]={}:typeof o[a]!="object"&&(o[a]={}),o=o[a]}let n=s[s.length-1];o[n]=r}getValueAtPath(t,e){let r=e.split("."),s=t;for(let o of r){if(s==null)return;s=s[o]}return s}createConfig(t,e=[],r={}){let s={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(s,a.path,a.defaultValue);let o={};if(t&&ft(t))try{let a=mt(t,"utf8");o=JSON.parse(a),console.log(`Loaded configuration from ${t}`)}catch(a){console.error(`Error reading config file: ${a instanceof Error?a.message:String(a)}`)}let n=this.parseCliArgs(e),i=this.deepMerge({},s);return i=this.deepMerge(i,o),i=this.deepMerge(i,r),i=this.deepMerge(i,n),i}parseCliArgs(t){let e={},r=t[0]?.endsWith("node")||t[0]?.endsWith("node.exe")?2:0;for(;r<t.length;){let s=t[r++],o=this.options.find(n=>n.flag===s);if(o)if(o.isFlag)this.setValueAtPath(e,o.path,!0);else if(r<t.length&&!t[r].startsWith("-")){let n=t[r++];if(o.parser)try{n=o.parser(n)}catch(i){console.error(`Error parsing value for ${o.flag}: ${i instanceof Error?i.message:String(i)}`);continue}if(o.validator){let i=o.validator(n);if(i!==!0&&typeof i=="string"){console.error(`Invalid value for ${o.flag}: ${i}`);continue}if(i===!1){console.error(`Invalid value for ${o.flag}`);continue}}this.setValueAtPath(e,o.path,n)}else console.error(`Missing value for option ${s}`)}return e}validate(){for(let t of this.validators){let e=this.getValueAtPath(this.config,t.path),r=t.validator(e,this.config);if(r===!1)throw new k(t.message);if(typeof r=="string")throw new k(r)}}get(){return this.autoValidate&&this.validate(),this.config}getValue(t,e){let r=this.getValueAtPath(this.config,t);return r!==void 0?r:e}setValue(t,e){if(typeof e=="object"&&e!==null&&!Array.isArray(e)){let r=this.getValueAtPath(this.config,t)||{};if(typeof r=="object"&&!Array.isArray(r)){let s=this.deepMerge(r,e);this.setValueAtPath(this.config,t,s);return}}this.setValueAtPath(this.config,t,e)}getHelpText(){let t=`Available configuration options:
2
+ var U=class{requests=new Map;limit;windowMs;constructor(t=100,e=60){this.limit=t,this.windowMs=e*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return(!s||s.resetTime<e)&&(s={count:0,resetTime:e+this.windowMs},this.requests.set(r,s)),s.count++,s.count<=this.limit}getRemainingRequests(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return!s||s.resetTime<e?this.limit:Math.max(0,this.limit-s.count)}getResetTime(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return!s||s.resetTime<e?Math.floor((e+this.windowMs)/1e3):Math.floor(s.resetTime/1e3)}cleanup(){let t=Date.now();for(let[e,r]of this.requests.entries())r.resetTime<t&&this.requests.delete(e)}};import{URL as pt}from"url";var _=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(t){return this.globalMiddlewares.push(t),this}get(t,...e){let r=e.pop();return this.register("GET",t,r,e)}post(t,...e){let r=e.pop();return this.register("POST",t,r,e)}put(t,...e){let r=e.pop();return this.register("PUT",t,r,e)}delete(t,...e){let r=e.pop();return this.register("DELETE",t,r,e)}patch(t,...e){let r=e.pop();return this.register("PATCH",t,r,e)}any(t,...e){let r=e.pop(),s=e;return this.register("GET",t,r,s),this.register("POST",t,r,s),this.register("PUT",t,r,s),this.register("DELETE",t,r,s),this.register("PATCH",t,r,s),this.register("OPTIONS",t,r,s),this}options(t,...e){let r=e.pop();return this.register("OPTIONS",t,r,e)}match(t,e){for(let r of this.routes){if(r.method!==t)continue;let s=this.pathPatterns.get(r.path);if(!s)continue;let o=s.pattern.exec(e);if(!o)continue;let n={};return s.paramNames.forEach((i,a)=>{n[i]=o[a+1]||""}),{route:r,params:n}}return null}async handle(t,e){let r=t.method||"GET",s=new pt(t.url||"/",`http://${t.headers.host}`),o=s.pathname,n=this.match(r,o);if(!n)return null;let{route:i,params:a}=n,l={};s.searchParams.forEach((c,d)=>{l[d]=c});let u={req:t,res:e,params:a,query:l,body:t.body||{},headers:t.headers,path:o,state:{},raw:()=>e,binary:(c,d="application/octet-stream",h=200)=>({statusCode:h,body:c,headers:{"Content-Type":d,"Content-Length":c.length.toString()},isRaw:!0}),text:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"text/plain"}}),form:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"application/json"}}),html:(c,d=200)=>({statusCode:d,body:c,headers:{"Content-Type":"text/html"}}),redirect:(c,d=302)=>({statusCode:d,body:null,headers:{Location:c}}),status:function(c){return{raw:()=>e,binary:(d,h="application/octet-stream")=>({statusCode:c,body:d,headers:{"Content-Type":h,"Content-Length":d.length.toString()},isRaw:!0}),text:d=>({statusCode:c,body:d,headers:{"Content-Type":"text/plain"}}),json:d=>({statusCode:c,body:d,headers:{"Content-Type":"application/json"}}),html:d=>({statusCode:c,body:d,headers:{"Content-Type":"text/html"}}),form:d=>({statusCode:c,body:d,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(d,h=302)=>({statusCode:h,body:null,headers:{Location:d}}),status:d=>this.status(d)}}},p=[...this.globalMiddlewares,...i.middlewares];return this.executeMiddlewareChain(u,p,i.handler)}register(t,e,r,s=[]){return this.routes.push({method:t,path:e,handler:r,middlewares:s}),this.pathPatterns.set(e,this.createPathPattern(e)),this}createPathPattern(t){let e=[],r=t.replace(/\/:[^/]+/g,s=>{let o=s.slice(2);return e.push(o),"/([^/]+)"});return r.endsWith("/*")?(r=`${r.slice(0,-2)}(?:/(.*))?`,e.push("wildcard")):r=r.replace(/\/$/,"/?"),{pattern:new RegExp(`^${r}$`),paramNames:e}}async executeMiddlewareChain(t,e,r){let s=0,o=async()=>{if(s<e.length){let n=e[s++];return n(t,o)}return r(t)};return o()}};var E=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:ht(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function ht(t){return t==="true"||t===!0}var L=class extends Error{constructor(t){super(t),this.name="ValidationError",this.message=t||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as ft,readFileSync as mt}from"node:fs";var H=class{config={};options=[];validators=[];autoValidate=!0;constructor(t){let e=t?.configFilePath,r=t?.args||[],s=t?.config||{};this.options=t?.options||[],this.validators=t?.validators||[],t?.autoValidate!==void 0&&(this.autoValidate=t.autoValidate),this.config=this.createConfig(e,r,s)}deepMerge(t,e){let r={...t};for(let s in e)e[s]!==void 0&&(e[s]!==null&&typeof e[s]=="object"&&!Array.isArray(e[s])&&s in t&&t[s]!==null&&typeof t[s]=="object"&&!Array.isArray(t[s])?r[s]=this.deepMerge(t[s],e[s]):e[s]!==void 0&&(r[s]=e[s]));return r}setValueAtPath(t,e,r){let s=e.split("."),o=t;for(let i=0;i<s.length-1;i++){let a=s[i];!(a in o)||o[a]===null?o[a]={}:typeof o[a]!="object"&&(o[a]={}),o=o[a]}let n=s[s.length-1];o[n]=r}getValueAtPath(t,e){let r=e.split("."),s=t;for(let o of r){if(s==null)return;s=s[o]}return s}createConfig(t,e=[],r={}){let s={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(s,a.path,a.defaultValue);let o={};if(t&&ft(t))try{let a=mt(t,"utf8");o=JSON.parse(a),console.log(`Loaded configuration from ${t}`)}catch(a){console.error(`Error reading config file: ${a instanceof Error?a.message:String(a)}`)}let n=this.parseCliArgs(e),i=this.deepMerge({},s);return i=this.deepMerge(i,o),i=this.deepMerge(i,r),i=this.deepMerge(i,n),i}parseCliArgs(t){let e={},r=t[0]?.endsWith("node")||t[0]?.endsWith("node.exe")?2:0;for(;r<t.length;){let s=t[r++],o=this.options.find(n=>n.flag===s);if(o)if(o.isFlag)this.setValueAtPath(e,o.path,!0);else if(r<t.length&&!t[r].startsWith("-")){let n=t[r++];if(o.parser)try{n=o.parser(n)}catch(i){console.error(`Error parsing value for ${o.flag}: ${i instanceof Error?i.message:String(i)}`);continue}if(o.validator){let i=o.validator(n);if(i!==!0&&typeof i=="string"){console.error(`Invalid value for ${o.flag}: ${i}`);continue}if(i===!1){console.error(`Invalid value for ${o.flag}`);continue}}this.setValueAtPath(e,o.path,n)}else console.error(`Missing value for option ${s}`)}return e}validate(){for(let t of this.validators){let e=this.getValueAtPath(this.config,t.path),r=t.validator(e,this.config);if(r===!1)throw new L(t.message);if(typeof r=="string")throw new L(r)}}get(){return this.autoValidate&&this.validate(),this.config}getValue(t,e){let r=this.getValueAtPath(this.config,t);return r!==void 0?r:e}setValue(t,e){if(typeof e=="object"&&e!==null&&!Array.isArray(e)){let r=this.getValueAtPath(this.config,t)||{};if(typeof r=="object"&&!Array.isArray(r)){let s=this.deepMerge(r,e);this.setValueAtPath(this.config,t,s);return}}this.setValueAtPath(this.config,t,e)}getHelpText(){let t=`Available configuration options:
3
3
 
4
4
  `;for(let e of this.options)t+=`${e.flag}${e.isFlag?"":" <value>"}
5
5
  `,e.description&&(t+=` ${e.description}
@@ -8,4 +8,4 @@ var U=class{requests=new Map;limit;windowMs;constructor(t=100,e=60){this.limit=t
8
8
  `;return t}};var V={int:t=>{let e=t.trim();if(!/^[+-]?\d+$/.test(e))throw new Error(`Cannot parse "${t}" as an integer`);let r=Number.parseInt(e,10);if(Number.isNaN(r))throw new Error(`Cannot parse "${t}" as an integer`);return r},float:t=>{let e=t.trim();if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(e)){if(e==="Infinity"||e==="-Infinity")return e==="Infinity"?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY;throw new Error(`Cannot parse "${t}" as a number`)}let r=Number.parseFloat(e);if(Number.isNaN(r))throw new Error(`Cannot parse "${t}" as a number`);return r},boolean:t=>{let e=t.trim().toLowerCase();if(["true","yes","1","y"].includes(e))return!0;if(["false","no","0","n"].includes(e))return!1;throw new Error(`Cannot parse "${t}" as a boolean`)},array:t=>t.split(",").map(e=>e.trim()),json:t=>{try{return JSON.parse(t)}catch{throw new Error(`Cannot parse "${t}" as JSON`)}}};var g=E(),G=t=>({configFilePath:"mikroserve.config.json",args:process.argv,options:[{flag:"--port",path:"port",defaultValue:g.port},{flag:"--host",path:"host",defaultValue:g.host},{flag:"--https",path:"useHttps",defaultValue:g.useHttps,isFlag:!0},{flag:"--http2",path:"useHttp2",defaultValue:g.useHttp2,isFlag:!0},{flag:"--cert",path:"sslCert",defaultValue:g.sslCert},{flag:"--key",path:"sslKey",defaultValue:g.sslKey},{flag:"--ca",path:"sslCa",defaultValue:g.sslCa},{flag:"--ratelimit",path:"rateLimit.enabled",defaultValue:g.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"rateLimit.requestsPerMinute",defaultValue:g.rateLimit.requestsPerMinute},{flag:"--allowed",path:"allowedDomains",defaultValue:g.allowedDomains,parser:V.array},{flag:"--debug",path:"debug",defaultValue:g.debug,isFlag:!0},{flag:"--max-body-size",path:"maxBodySize",defaultValue:g.maxBodySize},{flag:"--request-timeout",path:"requestTimeout",defaultValue:g.requestTimeout}],config:t});function J(t,e){let r=e.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!r)throw new Error("Invalid multipart/form-data: missing boundary");let s=r[1]||r[2],o=Buffer.from(`--${s}`),n=Buffer.from(`--${s}--`),i={},a={},l=gt(t,o);for(let u of l){if(u.length===0||u.equals(n.subarray(o.length)))continue;let p=yt(u);if(!p)continue;let{name:c,filename:d,contentType:h,data:m}=p;if(d){let f={filename:d,contentType:h||"application/octet-stream",data:m,size:m.length};a[c]?Array.isArray(a[c])?a[c].push(f):a[c]=[a[c],f]:a[c]=f}else{let f=m.toString("utf8");i[c]?Array.isArray(i[c])?i[c].push(f):i[c]=[i[c],f]:i[c]=f}}return{fields:i,files:a}}function gt(t,e){let r=[],s=0;for(;s<t.length;){let o=t.indexOf(e,s);if(o===-1)break;s!==o&&r.push(t.subarray(s,o)),s=o+e.length,s<t.length&&t[s]===13&&t[s+1]===10&&(s+=2)}return r}function yt(t){let e=Buffer.from(`\r
9
9
  \r
10
10
  `),r=t.indexOf(e);if(r===-1)return null;let s=t.subarray(0,r),o=t.subarray(r+4),i=s.toString("utf8").split(`\r
11
- `),a="",l="",u,p;for(let d of i){let h=d.toLowerCase();if(h.startsWith("content-disposition:")){a=d.substring(20).trim();let m=a.match(/name="([^"]+)"/);m&&(l=m[1]);let f=a.match(/filename="([^"]+)"/);f&&(u=f[1])}else h.startsWith("content-type:")&&(p=d.substring(13).trim())}if(!l)return null;let c=o;return c.length>=2&&c[c.length-2]===13&&c[c.length-1]===10&&(c=c.subarray(0,c.length-2)),{disposition:a,name:l,filename:u,contentType:p,data:c}}import{readFileSync as b}from"fs";import F from"http";import wt from"http2";import vt from"https";var N=class{config;rateLimiter;router;shutdownHandlers=[];constructor(t){let e=new H(G(t||{})).get();e.debug&&console.log("Using configuration:",e),this.config=e,this.router=new _;let r=e.rateLimit.requestsPerMinute||E().rateLimit.requestsPerMinute;this.rateLimiter=new U(r,60),e.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(t){return this.router.use(t),this}get(t,...e){return this.router.get(t,...e),this}post(t,...e){return this.router.post(t,...e),this}put(t,...e){return this.router.put(t,...e),this}delete(t,...e){return this.router.delete(t,...e),this}patch(t,...e){return this.router.patch(t,...e),this}any(t,...e){return this.router.any(t,...e),this}options(t,...e){return this.router.options(t,...e),this}start(){let t=this.createServer(),{port:e,host:r}=this.config;return this.setupGracefulShutdown(t),t.listen(e,r,()=>{let s=t.address(),o=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${o}://${s.address!=="::"?s.address:"localhost"}:${s.port}`)}),t}createServer(){let t=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let e={key:b(this.config.sslKey),cert:b(this.config.sslCert),...this.config.sslCa?{ca:b(this.config.sslCa)}:{}};return wt.createSecureServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let e={key:b(this.config.sslKey),cert:b(this.config.sslCert),...this.config.sslCa?{ca:b(this.config.sslCa)}:{}};return vt.createServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}return F.createServer(t)}async rateLimitMiddleware(t,e){let r=t.req.socket.remoteAddress||"unknown";return t.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),t.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(r).toString()),t.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(r).toString()),this.rateLimiter.isAllowed(r)?e():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(t,e){let r=Date.now(),s=t.method||"UNKNOWN",o=t.url||"/unknown",n=this.config.debug;try{if(this.setCorsHeaders(e,t),this.setSecurityHeaders(e,this.config.useHttps),n&&console.log(`${s} ${o}`),t.method==="OPTIONS"){if(e instanceof F.ServerResponse)e.statusCode=204,e.end();else{let a=e;a.writeHead(204),a.end()}return}try{t.body=await this.parseBody(t)}catch(a){return n&&console.error("Body parsing error:",a.message),this.respond(e,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let i=await this.router.handle(t,e);return i?i._handled?void 0:this.respond(e,i):this.respond(e,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(i){return console.error("Server error:",i),this.respond(e,{statusCode:500,body:{error:"Internal Server Error",message:n?i.message:"An unexpected error occurred"}})}finally{n&&this.logDuration(r,s,o)}}logDuration(t,e,r){let s=Date.now()-t;console.log(`${e} ${r} completed in ${s}ms`)}async parseBody(t){return new Promise((e,r)=>{let s=[],o=0,n=this.config.maxBodySize,i=!1,a=null,l=this.config.debug,u=t.headers["content-type"]||"";l&&console.log("Content-Type:",u),this.config.requestTimeout>0&&(a=setTimeout(()=>{i||(i=!0,l&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let p=()=>{a&&(clearTimeout(a),a=null)};t.on("data",c=>{if(!i){if(o+=c.length,l&&console.log(`Received chunk: ${c.length} bytes, total size: ${o}`),o>n){i=!0,p(),l&&console.log(`Body size exceeded limit: ${o} > ${n}`),r(new Error("Request body too large"));return}s.push(c)}}),t.on("end",()=>{if(!i){i=!0,p(),l&&console.log(`Request body complete: ${o} bytes`);try{if(s.length>0){let c=Buffer.concat(s);if(u.includes("application/json"))try{let d=c.toString("utf8");e(JSON.parse(d))}catch(d){r(new Error(`Invalid JSON in request body: ${d.message}`))}else if(u.includes("application/x-www-form-urlencoded")){let d=c.toString("utf8"),h={};new URLSearchParams(d).forEach((m,f)=>{h[f]=m}),e(h)}else if(u.includes("multipart/form-data"))try{let d=J(c,u);e(d)}catch(d){r(new Error(`Invalid multipart form data: ${d.message}`))}else this.isBinaryContentType(u)?e(c):e(c.toString("utf8"))}else e({})}catch(c){r(new Error(`Invalid request body: ${c}`))}}}),t.on("error",c=>{i||(i=!0,p(),r(new Error(`Error reading request body: ${c.message}`)))}),t.on("close",()=>{p()})})}isBinaryContentType(t){return["application/octet-stream","application/pdf","application/zip","application/gzip","application/x-tar","application/x-rar-compressed","application/x-7z-compressed","image/","video/","audio/","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument","application/msword","application/vnd.ms-powerpoint"].some(r=>t.includes(r))}setCorsHeaders(t,e){let r=e.headers.origin,{allowedDomains:s=["*"]}=this.config;!r||s.length===0||s.includes("*")?t.setHeader("Access-Control-Allow-Origin","*"):s.includes(r)&&(t.setHeader("Access-Control-Allow-Origin",r),t.setHeader("Vary","Origin")),t.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),t.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(t,e=!1){let r={"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'self'; script-src 'self'; object-src 'none'","X-XSS-Protection":"1; mode=block"};if((e||this.config.useHttp2)&&(r["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),t instanceof F.ServerResponse)Object.entries(r).forEach(([s,o])=>{t.setHeader(s,o)});else{let s=t;Object.entries(r).forEach(([o,n])=>{s.setHeader(o,n)})}}respond(t,e){let r={...e.headers||{}};(o=>typeof o.writeHead=="function"&&typeof o.end=="function")(t)?(t.writeHead(e.statusCode,r),e.body===null||e.body===void 0?t.end():e.isRaw||typeof e.body=="string"?t.end(e.body):t.end(JSON.stringify(e.body))):(console.warn("Unexpected response object type without writeHead/end methods"),t.writeHead?.(e.statusCode,r),e.body===null||e.body===void 0?t.end?.():e.isRaw||typeof e.body=="string"?t.end?.(e.body):t.end?.(JSON.stringify(e.body)))}setupGracefulShutdown(t){let e=i=>{console.log("Shutting down MikroServe server..."),i&&console.error("Error:",i),this.cleanupShutdownHandlers(),t.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(i?1:0))})},r=()=>e(),s=()=>e(),o=i=>e(i),n=i=>e(i);this.shutdownHandlers=[r,s,o,n],process.on("SIGINT",r),process.on("SIGTERM",s),process.on("uncaughtException",o),process.on("unhandledRejection",n)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[t,e,r,s]=this.shutdownHandlers;process.removeListener("SIGINT",t),process.removeListener("SIGTERM",e),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",s),this.shutdownHandlers=[]}}};import{dirname as jt,join as y}from"node:path";import{existsSync as Et,rmSync as L}from"node:fs";import{isAbsolute as It,normalize as Rt}from"node:path";import{mkdir as q,writeFile as Tt,readFile as At,stat as ct}from"node:fs/promises";import{randomBytes as $t}from"node:crypto";function w(t,e="An error occurred"){let r=t?.message||t||e,s=t?.cause?.statusCode||t?.statusCode||400;return{message:r,status:s}}function W(t,e,r={}){let s=Array.isArray(e)?e:[e];if(!t||!t.roles||!Array.isArray(t.roles)){let i=new Error("Unauthorized: User or roles missing");throw i.cause={statusCode:403},i}let o=t.roles.flatMap(i=>i?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(u=>typeof u=="string"?u:u&&typeof u=="object"&&u.actions?u.actions:[]))||[]);if(!s.every(i=>o.includes(i)||o.includes("*")?!0:o.some(a=>{if(a.endsWith(".*")){let l=a.slice(0,-2);return i.startsWith(`${l}.`)}return!1}))){let i=new Error("Unauthorized");throw i.cause={statusCode:403},i}return!0}function X(t,e,r,s,o){let n=t.find(l=>l.service===e);if(!n){let l=new Error(`Function bindings do not include access to service: ${e}`);throw l.cause={statusCode:403},l}if(!n.permissions||n.permissions.length===0)return;for(let l of n.permissions)if(!(l.resource&&l.resource!==r)){if(!l.resource||!l.actions||l.actions.length===0)return;if(l.actions.includes(s)){if(l.targets&&l.targets.length>0){if(!o)return;if(!l.targets.includes(o))continue}return}}let i=o?`:${o}`:"",a=new Error(`Function bindings do not allow: ${e}.${r}.${s}${i}`);throw a.cause={statusCode:403},a}async function v(t,e,r,s,o,n,i){if(!e)return null;let a=t.state.user;if(!a){let u=t.headers.authorization;if(!u){let c=new Error("Unauthorized: No authentication provided");throw c.cause={statusCode:401},c}let p=u.replace(/^Bearer\s+/i,"");if(a=await e.getUserFromToken(p),!a){let c=new Error("Unauthorized: Invalid token");throw c.cause={statusCode:401},c}}W(a,r,{});let l=t.headers["x-function-bindings"];if(l)try{let u=Array.isArray(l)?l[0]:l,p=JSON.parse(u);X(p,s,o,n,i)}catch(u){if(u.cause?.statusCode===403)throw u;let p=new Error("Invalid function bindings header");throw p.cause={statusCode:400},p}return a}async function Z(t,e,r,s){try{await v(t,r,"sites.project.list","sites","project","list");let o=t.headers["x-forwarded-proto"]||"http",n=t.headers.host||"localhost:3003",i=`${o}://${n}`,a=await e.listProjects(i),l=t.query.context;if(l&&s){let u=t.headers.authorization?.replace(/^Bearer\s+/i,""),p=await s.listResourcesByType(l,"site",u);a=a.filter(c=>p.includes(c.projectId))}return t.json({success:!0,count:a.length,projects:a},200)}catch(o){let{message:n,status:i}=w(o,"Error listing projects");return t.json({error:n},i)}}var D=class{isSilent;propertyPath="";constructor(t=!1){this.isSilent=t}test(t,e){if(!e)throw new Error("Missing input!");this.updatePropertyPath();let{results:r,errors:s}=this.validate(t.properties,e),o=this.compileErrors(r,s),n=this.isSuccessful(r,o);return{errors:o,success:n}}compileErrors(t,e){let r=t.filter(s=>s.success===!1);return[...e,...r].flatMap(s=>s)}isSuccessful(t,e){return t.every(r=>r.success===!0)&&e.length===0}validate(t,e,r=[],s=[]){let o=t?.additionalProperties??!0,n=t?.required||[];s=this.checkForRequiredKeysErrors(n,e,s),s=this.checkForDisallowedProperties(Object.keys(e),Object.keys(t),s,o);for(let i in t){let a=n.includes(i)&&i!=="required",l=t[i],u=e[i],p=l.additionalProperties??!0;a&&(s=this.checkForRequiredKeysErrors(l.required||[],u,s)),this.isDefined(u)&&(this.handleValidation(i,u,l,r),s=this.checkForDisallowedProperties(Object.keys(u),Object.keys(l),s,p),this.handleNestedObject(u,l,r,s))}return{results:r,errors:s}}updatePropertyPath(t,e=""){if(!t){this.propertyPath="";return}e&&(this.propertyPath=e),this.propertyPath=`${this.propertyPath}.${t}`,this.propertyPath.startsWith(".")&&(this.propertyPath=this.propertyPath.substring(1,this.propertyPath.length))}isDefined(t){return!!(typeof t=="number"&&t===0||t||t===""||typeof t=="boolean")}checkForRequiredKeysErrors(t,e,r){if(!this.areRequiredKeysPresent(t,e)){let s=e?Object.keys(e):[],o=this.findNonOverlappingElements(t,s),n=o.length>0?`Missing the required key: '${o.join(", ")}'!`:`Missing values for required keys: '${s.filter(i=>!e[i]).join(", ")}'!`;r.push({key:"",value:e,success:!1,error:n})}return r}checkForDisallowedProperties(t,e,r,s){if(!s){let o=this.findNonOverlappingElements(t,e);o.length>0&&r.push({key:`${e}`,value:t,success:!1,error:`Has additional (disallowed) properties: '${o.join(", ")}'!`})}return r}handleValidation(t,e,r,s){this.updatePropertyPath(t);let o=this.validateProperty(this.propertyPath,r,e);s.push(...o);let n=(a,l)=>{a.forEach(u=>{let p=this.validateProperty(this.propertyPath,l.items,u);s.push(...p)}),this.updatePropertyPath()},i=a=>{let l=Object.keys(a),u=this.propertyPath;l.forEach(p=>{if(this.updatePropertyPath(p,u),this.isArray(a[p])&&r[p]?.items!=null)n(a[p],r[p]);else{let c=this.validateProperty(this.propertyPath,r[p],a[p]);s.push(...c)}})};this.isArray(e)&&r.items!=null?n(e,r):this.isObject(e)?i(e):this.updatePropertyPath()}handleNestedObject(t,e,r,s){if(this.isObject(t)){let o=this.getNestedObjects(t);for(let n of o){let i=e[n],a=t[n];i&&typeof a=="object"&&this.validate(i,a,r,s)}}}getNestedObjects(t){return Object.keys(t).filter(e=>{if(this.isObject(t))return e})}findNonOverlappingElements(t,e){return t.filter(r=>!e.includes(r))}areRequiredKeysPresent(t,e=[]){return t.every(r=>Object.keys(e).includes(r)?this.isDefined(e[r]):!1)}validateProperty(t,e,r){return this.validateInput(e,r).map(o=>{let{success:n,error:i}=o;return{key:t,value:r,success:n,error:i??""}})}validateInput(t,e){if(t){let r=[{condition:()=>t.type,validator:()=>this.isCorrectType(t.type,e),error:"Invalid type"},{condition:()=>t.format,validator:()=>this.isCorrectFormat(t.format,e),error:"Invalid format"},{condition:()=>t.minLength,validator:()=>this.isMinimumLength(t.minLength,e),error:"Length too short"},{condition:()=>t.maxLength,validator:()=>this.isMaximumLength(t.maxLength,e),error:"Length too long"},{condition:()=>t.minValue,validator:()=>this.isMinimumValue(t.minValue,e),error:"Value too small"},{condition:()=>t.maxValue,validator:()=>this.isMaximumValue(t.maxValue,e),error:"Value too large"},{condition:()=>t.matchesPattern,validator:()=>this.matchesPattern(t.matchesPattern,e),error:"Pattern does not match"}],s=[];for(let o of r)o.condition()&&!o.validator()&&s.push({success:!1,error:o.error});return s}else this.isSilent||console.warn(`Missing property '${t}' for match '${e}'. Skipping...`);return[{success:!0}]}isCorrectType(t,e){return Array.isArray(t)||(t=[t]),t.some(r=>{switch(r){case"string":return typeof e=="string";case"number":return typeof e=="number"&&!isNaN(e);case"boolean":return typeof e=="boolean";case"object":return this.isObject(e);case"array":return this.isArray(e)}})}isObject(t){return t!==null&&!this.isArray(t)&&typeof t=="object"&&t instanceof Object&&Object.prototype.toString.call(t)==="[object Object]"}isArray(t){return Array.isArray(t)}isCorrectFormat(t,e){switch(t){case"alphanumeric":return new RegExp(/^[a-zA-Z0-9]+$/).test(e);case"numeric":return new RegExp(/^-?\d+(\.\d+)?$/).test(e);case"email":return new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/).test(e);case"date":return new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(e);case"url":return new RegExp(/^(https?):\/\/[^\s$.?#].[^\s]*$/).test(e);case"hexColor":return new RegExp(/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i).test(e)}}isMinimumLength(t,e){return Array.isArray(e)?e.length>=t:e?.toString().length>=t}isMaximumLength(t,e){return Array.isArray(e)?e.length<=t:e.toString().length<=t}isMinimumValue(t,e){return e>=t}isMaximumValue(t,e){return e<=t}matchesPattern(t,e){return new RegExp(t).test(e)}schemaFrom(t){let e={properties:{additionalProperties:!1,required:[]}};for(let r in t){let s=t[r];e.properties.required.push(r),Array.isArray(s)?e.properties[r]=this.generateArraySchema(s):typeof s=="object"&&s!==null?e.properties[r]=this.generateNestedObjectSchema(s):e.properties[r]=this.generatePropertySchema(s)}return e}generateArraySchema(t){let e={type:"array"},r=t.filter(s=>s);if(r.length>0){let s=r[0];r.every(n=>typeof n==typeof s)?typeof s=="object"&&!Array.isArray(s)?e.items=this.generateNestedObjectSchema(s):e.items=this.generatePropertySchema(s):console.warn("All elements in array are not of the same type. Unable to generate a schema for these elements.")}return e}generateNestedObjectSchema(t){let e={type:"object",additionalProperties:!1,required:[]};for(let r in t){let s=t[r];e.required.push(r),typeof s=="object"&&!Array.isArray(s)&&s!==null?e[r]=this.generateNestedObjectSchema(s):e[r]=this.generatePropertySchema(s)}return e}generatePropertySchema(t){let e=typeof t,r={type:e};return e==="string"&&(r.minLength=1),r}};async function K(t){return t.body||{}}var Y={properties:{files:{type:"array",minItems:1,items:{type:"object",properties:{path:{type:"string",minLength:1},content:{type:"string",minLength:1}},required:["path","content"]}},projectId:{type:"string",pattern:"^[a-z0-9-]+$",maxLength:64},context:{type:"string",minLength:1}},required:["files"],additionalProperties:!1};var Ct=new D(!0);async function Q(t,e,r){try{await v(t,r,"sites.project.create","sites","project","create");let s=await K(t),o=Ct.test(Y,s);if(!o.success)return t.json({error:"Invalid input",details:o.errors},400);let{files:n,projectId:i}=s;if(!n)return t.json({error:"files array is required"},400);let a=await e.uploadProject(n,i);return t.json({success:!0,projectId:a},200)}catch(s){let{message:o,status:n}=w(s,"Error uploading project");return t.json({error:o},n)}}async function tt(t,e,r){try{let s=t.params.projectId;return s?!/^[a-z0-9-]+$/.test(s)||s.length>64?t.json({error:"Invalid projectId format"},400):(await v(t,r,"sites.project.delete","sites","project","delete",s),await e.projectExists(s)?(await e.deleteProject(s),t.json({success:!0,projectId:s},200)):t.json({error:"Project not found"},404)):t.json({error:"projectId parameter is required"},400)}catch(s){let{message:o,status:n}=w(s,"Error deleting project");return t.json({error:o},n)}}async function et(t,e,r){try{let s=t.params.projectId;if(!s)return t.json({error:"projectId parameter is required"},400);if(!/^[a-z0-9-]+$/.test(s)||s.length>64)return t.json({error:"Invalid projectId format"},400);if(await v(t,r,"sites.project.download","sites","project","download",s),!await e.projectExists(s))return t.json({error:"Project not found"},404);let n=await e.getProjectFiles(s);return t.json({success:!0,projectId:s,files:n},200)}catch(s){let{message:o,status:n}=w(s,"Error downloading project");return t.json({error:o},n)}}import{extname as bt}from"node:path";function rt(t){let e=bt(t).toLowerCase();return{".html":"text/html",".css":"text/css",".js":"application/javascript",".json":"application/json",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".txt":"text/plain",".pdf":"application/pdf",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".eot":"application/vnd.ms-fontobject",".otf":"font/otf",".xml":"application/xml",".mp4":"video/mp4",".webm":"video/webm",".mp3":"audio/mpeg",".wav":"audio/wav"}[e]||"application/octet-stream"}async function st(t,e){try{let r=t.params.projectId;if(!r)return t.json({error:"projectId is required"},400);let n=new URL(t.req.url||"","http://localhost").pathname.split(`/projects/${r}/`)[1]||""||"index.html";if(!await e.fileExists(r,n))return t.text("Not Found",404);let a=await e.getFile(r,n),l=rt(n);return t.binary(a,l)}catch(r){let{message:s,status:o}=w(r,"Error serving file");return t.json({error:s},o)}}var S=class extends Error{constructor(e){super(),this.name="ValidationError",this.message=e||"Invalid input",this.cause={statusCode:400}}};var I=class extends Error{constructor(e){super(),this.name="InvalidPathError",this.message=e||"Invalid file path",this.cause={statusCode:400}}},x=class extends Error{constructor(e){super(),this.name="InvalidFormatError",this.message=e||"Invalid format",this.cause={statusCode:400}}},R=class extends Error{constructor(e){super(),this.name="FileTooLargeError",this.message=e||"File exceeds maximum size",this.cause={statusCode:413}}},T=class extends Error{constructor(e){super(),this.name="ProjectTooLargeError",this.message=e||"Project exceeds maximum size",this.cause={statusCode:413}}},A=class extends Error{constructor(e){super(),this.name="InvalidContentError",this.message=e||"Invalid content",this.cause={statusCode:400}}};function $(t){return t&&typeof t=="number"&&t>0?t*1024*1024:0}import{readFileSync as St,existsSync as Pt}from"node:fs";function j(){let t=process.env.MOLNOS_RUNTIME_CONFIG,e={molnos:{dataPath:"data",rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:"molnos-default-signed-url-secret"},server:{host:"127.0.0.1",port:3e3},identity:{apiUrl:"http://127.0.0.1:3000"},context:{apiUrl:"http://127.0.0.1:3000"},services:{storage:{host:"127.0.0.1",port:3001,url:"http://127.0.0.1:3001"},functions:{host:"127.0.0.1",port:3002,url:"http://127.0.0.1:3002"},sites:{host:"127.0.0.1",port:3003,url:"http://127.0.0.1:3003"},databases:{host:"127.0.0.1",port:3004,url:"http://127.0.0.1:3004"},observability:{host:"127.0.0.1",port:3005,url:"http://127.0.0.1:3005"}}};if(!t||!Pt(t))return e;try{let r=St(t,"utf-8");return JSON.parse(r)}catch(r){return console.error("[loadRuntimeConfig] Failed to load runtime config:",r),e}}function ot(t,e){let r={enabled:!1,requestsPerMinute:0};if(!e)return r;let s=e.services?.[t];return s||(e.global?e.global:r)}function it(t,e){let r=t?.api?.port;if(!r)throw new Error('Missing "port" input when create API service!');let s=j(),o=t?.dataPath||s.molnos.dataPath,n=t?.api?.host||s.server.host,i=t?.identityApiUrl!==void 0?t.identityApiUrl:s.identity.apiUrl,a=t?.contextApiUrl!==void 0?t.contextApiUrl:s.context.apiUrl,l=t?.debug||!1,u;return e&&(u=ot(e,s.molnos.rateLimit)),{dataPath:o,api:{port:r,host:n},...i?{identityApiUrl:i}:{},...a?{contextApiUrl:a}:{},...u?{rateLimit:u}:{},debug:l}}var M=class{baseUrl;authToken;constructor(e,r){this.baseUrl=e.replace(/\/$/,""),this.authToken=r}async createCustomRole(e,r,s,o){let n=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:e,name:r,description:s,permissions:o})});if(!n.ok){let i=`Failed to create role: ${n.statusText}`;try{i=(await n.json()).error||i}catch{i=await n.text().catch(()=>n.statusText)||i}throw new Error(i)}}async updateRole(e,r){let s=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!s.ok){let o=await s.json();throw new Error(o.error||`Failed to update role: ${s.statusText}`)}}async deleteRole(e){let r=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(e,r,s){let o=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:e,description:r,roles:s})});if(!o.ok){let i=`Failed to create service account: ${o.statusText}`;try{i=(await o.json()).error||i}catch{i=await o.text().catch(()=>o.statusText)||i}throw new Error(i)}let n=await o.json();return{id:n.id,apiKey:n.apiKey}}async deleteIdentity(e){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete service account: ${r.statusText}`)}}async getUserFromToken(e){try{let r=await fetch(`${this.baseUrl}/identity/whoami`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});return r.ok?await r.json():null}catch{return null}}getHeaders(){let e={"Content-Type":"application/json"};return this.authToken&&(e.Authorization=`Bearer ${this.authToken}`),e}};function nt(t){return t?new M(t):null}var O=class{baseUrl;constructor(e){this.baseUrl=e.replace(/\/$/,"")}async listResourcesByType(e,r,s){let o=`${this.baseUrl}/contexts/${encodeURIComponent(e)}`;try{let n={"Content-Type":"application/json"};s&&(n.Authorization=`Bearer ${s}`);let i=await fetch(o,{method:"GET",headers:n});if(!i.ok)return[];let l=(await i.json()).context?.resources||{};switch(r){case"function":return l.functions||[];case"database":return l.databases||[];case"storage":return l.storage||[];case"site":return l.sites||[];default:return[]}}catch{return[]}}};function at(t){return t?new O(t):null}var Mt=10,Ot=100,lt=$(Mt),ut=$(Ot),Lt="index.html";function C(t){return/^[a-z0-9-]{4,40}$/.test(t)}function B(t){if(!t)throw new S("File path is required");let e=Rt(t.replace(/^\/+/,""));if(e.includes("..")||It(e))throw new I("Invalid file path");return e===""||e==="/"?Lt:e}var z=class{rootPath;constructor(e){this.rootPath=e.path||"sites-data"}async start(){await q(this.rootPath,{recursive:!0})}generateProjectId(e=8){return $t(Math.ceil(e/2)).toString("hex").slice(0,e)}async uploadProject(e,r=null){if(!e||!Array.isArray(e))throw new S("Invalid request format. Expected array of files in the request body.");r||(r=this.generateProjectId());let s=y(this.rootPath,r);if(!C(r))throw new x("Invalid project ID format");Et(s)&&L(s,{recursive:!0,force:!0}),await q(s,{recursive:!0});let o=0;for(let n of e){if(!n.path||!n.content)continue;let i=await this.saveFile(s,n.path,n.content);if(o+=i,o>ut)throw L(s,{recursive:!0,force:!0}),new T(`Project exceeds maximum size of ${ut/1024/1024}MB`)}if(e.length===0||o===0)throw L(s,{recursive:!0,force:!0}),new S("No valid files were provided");return r}async saveFile(e,r,s){let o=B(r),n=y(e,o),i=jt(n);await q(i,{recursive:!0});let a;try{a=Buffer.from(s,"base64")}catch(l){throw new A(`Invalid content for file "${o}": ${l.message}`)}if(a.length>lt)throw new R(`File "${o}" exceeds maximum size of ${lt/1024/1024}MB`);return await Tt(n,a),a.length}async getFile(e,r){if(!C(e))throw new x("Invalid project ID format");let s=B(r),o=y(this.rootPath,e,s);return await At(o)}async fileExists(e,r){try{if(!C(e))return!1;let s=B(r),o=y(this.rootPath,e,s);return(await ct(o)).isFile()}catch{return!1}}async projectExists(e){try{if(!C(e))return!1;let r=y(this.rootPath,e);return(await ct(r)).isDirectory()}catch{return!1}}async getProjectFiles(e){if(!C(e))throw new x("Invalid project ID format");let r=y(this.rootPath,e),{readdir:s,readFile:o}=await import("node:fs/promises"),n=[],i=async(a,l)=>{let u=await s(a,{withFileTypes:!0});for(let p of u){let c=y(a,p.name);if(p.isDirectory())await i(c,l);else if(p.isFile()){let d=await o(c),h=c.slice(l.length+1);n.push({path:h,content:d.toString("base64")})}}};return await i(r,r),n}async deleteProject(e){if(!C(e))throw new x("Invalid project ID format");let r=y(this.rootPath,e);L(r,{recursive:!0,force:!0})}async listProjects(e){try{let{readdir:r,stat:s}=await import("node:fs/promises"),o=await r(this.rootPath,{withFileTypes:!0}),n=[];for(let i of o)if(i.isDirectory()&&C(i.name)){let a=y(this.rootPath,i.name),l=await s(a),u=0,p=0,c=async h=>{let m=await r(h,{withFileTypes:!0});for(let f of m){let P=y(h,f.name);if(f.isDirectory())await c(P);else if(f.isFile()){u++;let dt=await s(P);p+=dt.size}}};await c(a);let d=h=>{if(h===0)return"0 B";let m=1024,f=["B","KB","MB","GB"],P=Math.floor(Math.log(h)/Math.log(m));return`${(h/m**P).toFixed(1)} ${f[P]}`};n.push({projectId:i.name,name:i.name,url:`${e}/projects/${i.name}/`,status:"active",files:u,size:d(p),lastDeployed:l.mtime.toISOString(),createdAt:l.birthtime.toISOString()})}return n}catch{return[]}}};async function kt(t){let e=t.dataPath||"data",r=y(e,"sites"),s=new z({path:r});await s.start();let o=nt(t.identityApiUrl),n=at(t.contextApiUrl),i=j(),a=new N({port:t.api.port||i.services.sites.port,host:t.api.host||i.services.sites.host,maxBodySize:$(1e3),rateLimit:t.rateLimit||{enabled:!1,requestsPerMinute:0}});return a.get("/projects",async u=>Z(u,s,o,n)),a.post("/projects",async u=>Q(u,s,o)),a.delete("/projects/:projectId",async u=>tt(u,s,o)),a.get("/projects/:projectId/download",async u=>et(u,s,o)),a.get("/projects/:projectId",async u=>{let p=u.params.projectId;return u.redirect(`/projects/${p}/`,301)}),a.get("/projects/:projectId/*",async u=>st(u,s)),a.start()}if(import.meta.url===`file://${process.argv[1]}`){let t=j(),r=process.argv.slice(2).find(n=>n.startsWith("--port=")),s=r?parseInt(r.split("=")[1],10):t.services.sites.port,o=it({api:{port:s}},"sites");kt(o)}export{z as SitesService,kt as startServer};
11
+ `),a="",l="",u,p;for(let d of i){let h=d.toLowerCase();if(h.startsWith("content-disposition:")){a=d.substring(20).trim();let m=a.match(/name="([^"]+)"/);m&&(l=m[1]);let f=a.match(/filename="([^"]+)"/);f&&(u=f[1])}else h.startsWith("content-type:")&&(p=d.substring(13).trim())}if(!l)return null;let c=o;return c.length>=2&&c[c.length-2]===13&&c[c.length-1]===10&&(c=c.subarray(0,c.length-2)),{disposition:a,name:l,filename:u,contentType:p,data:c}}import{readFileSync as S}from"fs";import F from"http";import wt from"http2";import vt from"https";var N=class{config;rateLimiter;router;shutdownHandlers=[];constructor(t){let e=new H(G(t||{})).get();e.debug&&console.log("Using configuration:",e),this.config=e,this.router=new _;let r=e.rateLimit.requestsPerMinute||E().rateLimit.requestsPerMinute;this.rateLimiter=new U(r,60),e.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(t){return this.router.use(t),this}get(t,...e){return this.router.get(t,...e),this}post(t,...e){return this.router.post(t,...e),this}put(t,...e){return this.router.put(t,...e),this}delete(t,...e){return this.router.delete(t,...e),this}patch(t,...e){return this.router.patch(t,...e),this}any(t,...e){return this.router.any(t,...e),this}options(t,...e){return this.router.options(t,...e),this}start(){let t=this.createServer(),{port:e,host:r}=this.config;return this.setupGracefulShutdown(t),t.listen(e,r,()=>{let s=t.address(),o=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${o}://${s.address!=="::"?s.address:"localhost"}:${s.port}`)}),t}createServer(){let t=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let e={key:S(this.config.sslKey),cert:S(this.config.sslCert),...this.config.sslCa?{ca:S(this.config.sslCa)}:{}};return wt.createSecureServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let e={key:S(this.config.sslKey),cert:S(this.config.sslCert),...this.config.sslCa?{ca:S(this.config.sslCa)}:{}};return vt.createServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}return F.createServer(t)}async rateLimitMiddleware(t,e){let r=t.req.socket.remoteAddress||"unknown";return t.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),t.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(r).toString()),t.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(r).toString()),this.rateLimiter.isAllowed(r)?e():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(t,e){let r=Date.now(),s=t.method||"UNKNOWN",o=t.url||"/unknown",n=this.config.debug;try{if(this.setCorsHeaders(e,t),this.setSecurityHeaders(e,this.config.useHttps),n&&console.log(`${s} ${o}`),t.method==="OPTIONS"){if(e instanceof F.ServerResponse)e.statusCode=204,e.end();else{let a=e;a.writeHead(204),a.end()}return}try{t.body=await this.parseBody(t)}catch(a){return n&&console.error("Body parsing error:",a.message),this.respond(e,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let i=await this.router.handle(t,e);return i?i._handled?void 0:this.respond(e,i):this.respond(e,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(i){return console.error("Server error:",i),this.respond(e,{statusCode:500,body:{error:"Internal Server Error",message:n?i.message:"An unexpected error occurred"}})}finally{n&&this.logDuration(r,s,o)}}logDuration(t,e,r){let s=Date.now()-t;console.log(`${e} ${r} completed in ${s}ms`)}async parseBody(t){return new Promise((e,r)=>{let s=[],o=0,n=this.config.maxBodySize,i=!1,a=null,l=this.config.debug,u=t.headers["content-type"]||"";l&&console.log("Content-Type:",u),this.config.requestTimeout>0&&(a=setTimeout(()=>{i||(i=!0,l&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let p=()=>{a&&(clearTimeout(a),a=null)};t.on("data",c=>{if(!i){if(o+=c.length,l&&console.log(`Received chunk: ${c.length} bytes, total size: ${o}`),o>n){i=!0,p(),l&&console.log(`Body size exceeded limit: ${o} > ${n}`),r(new Error("Request body too large"));return}s.push(c)}}),t.on("end",()=>{if(!i){i=!0,p(),l&&console.log(`Request body complete: ${o} bytes`);try{if(s.length>0){let c=Buffer.concat(s);if(u.includes("application/json"))try{let d=c.toString("utf8");e(JSON.parse(d))}catch(d){r(new Error(`Invalid JSON in request body: ${d.message}`))}else if(u.includes("application/x-www-form-urlencoded")){let d=c.toString("utf8"),h={};new URLSearchParams(d).forEach((m,f)=>{h[f]=m}),e(h)}else if(u.includes("multipart/form-data"))try{let d=J(c,u);e(d)}catch(d){r(new Error(`Invalid multipart form data: ${d.message}`))}else this.isBinaryContentType(u)?e(c):e(c.toString("utf8"))}else e({})}catch(c){r(new Error(`Invalid request body: ${c}`))}}}),t.on("error",c=>{i||(i=!0,p(),r(new Error(`Error reading request body: ${c.message}`)))}),t.on("close",()=>{p()})})}isBinaryContentType(t){return["application/octet-stream","application/pdf","application/zip","application/gzip","application/x-tar","application/x-rar-compressed","application/x-7z-compressed","image/","video/","audio/","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument","application/msword","application/vnd.ms-powerpoint"].some(r=>t.includes(r))}setCorsHeaders(t,e){let r=e.headers.origin,{allowedDomains:s=["*"]}=this.config;!r||s.length===0||s.includes("*")?t.setHeader("Access-Control-Allow-Origin","*"):s.includes(r)&&(t.setHeader("Access-Control-Allow-Origin",r),t.setHeader("Vary","Origin")),t.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),t.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(t,e=!1){let r={"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'self'; script-src 'self'; object-src 'none'","X-XSS-Protection":"1; mode=block"};if((e||this.config.useHttp2)&&(r["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),t instanceof F.ServerResponse)Object.entries(r).forEach(([s,o])=>{t.setHeader(s,o)});else{let s=t;Object.entries(r).forEach(([o,n])=>{s.setHeader(o,n)})}}respond(t,e){let r={...e.headers||{}};(o=>typeof o.writeHead=="function"&&typeof o.end=="function")(t)?(t.writeHead(e.statusCode,r),e.body===null||e.body===void 0?t.end():e.isRaw||typeof e.body=="string"?t.end(e.body):t.end(JSON.stringify(e.body))):(console.warn("Unexpected response object type without writeHead/end methods"),t.writeHead?.(e.statusCode,r),e.body===null||e.body===void 0?t.end?.():e.isRaw||typeof e.body=="string"?t.end?.(e.body):t.end?.(JSON.stringify(e.body)))}setupGracefulShutdown(t){let e=i=>{console.log("Shutting down MikroServe server..."),i&&console.error("Error:",i),this.cleanupShutdownHandlers(),t.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(i?1:0))})},r=()=>e(),s=()=>e(),o=i=>e(i),n=i=>e(i);this.shutdownHandlers=[r,s,o,n],process.on("SIGINT",r),process.on("SIGTERM",s),process.on("uncaughtException",o),process.on("unhandledRejection",n)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[t,e,r,s]=this.shutdownHandlers;process.removeListener("SIGINT",t),process.removeListener("SIGTERM",e),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",s),this.shutdownHandlers=[]}}};import{dirname as jt,join as y}from"node:path";import{existsSync as Et,rmSync as k}from"node:fs";import{isAbsolute as It,normalize as Rt}from"node:path";import{mkdir as q,writeFile as Tt,readFile as At,stat as ct}from"node:fs/promises";import{randomBytes as $t}from"node:crypto";function w(t,e="An error occurred"){let r=t?.message||t||e,s=t?.cause?.statusCode||t?.statusCode||400;return{message:r,status:s}}function W(t,e,r={}){let s=Array.isArray(e)?e:[e];if(!t||!t.roles||!Array.isArray(t.roles)){let i=new Error("Unauthorized: User or roles missing");throw i.cause={statusCode:403},i}let o=t.roles.flatMap(i=>i?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(u=>typeof u=="string"?u:u&&typeof u=="object"&&u.actions?u.actions:[]))||[]);if(!s.every(i=>o.includes(i)||o.includes("*")?!0:o.some(a=>{if(a.endsWith(".*")){let l=a.slice(0,-2);return i.startsWith(`${l}.`)}return!1}))){let i=new Error("Unauthorized");throw i.cause={statusCode:403},i}return!0}function X(t,e,r,s,o){let n=t.find(l=>l.service===e);if(!n){let l=new Error(`Function bindings do not include access to service: ${e}`);throw l.cause={statusCode:403},l}if(!n.permissions||n.permissions.length===0)return;for(let l of n.permissions)if(!(l.resource&&l.resource!==r)){if(!l.resource||!l.actions||l.actions.length===0)return;if(l.actions.includes(s)){if(l.targets&&l.targets.length>0){if(!o)return;if(!l.targets.includes(o))continue}return}}let i=o?`:${o}`:"",a=new Error(`Function bindings do not allow: ${e}.${r}.${s}${i}`);throw a.cause={statusCode:403},a}import{readFileSync as xt,existsSync as Ct}from"node:fs";function x(){let t=process.env.MOLNOS_RUNTIME_CONFIG,e={molnos:{dataPath:"data",rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:"molnos-default-signed-url-secret"},server:{host:"127.0.0.1",port:3e3},identity:{apiUrl:"http://127.0.0.1:3000"},context:{apiUrl:"http://127.0.0.1:3000"},services:{storage:{host:"127.0.0.1",port:3001,url:"http://127.0.0.1:3001"},functions:{host:"127.0.0.1",port:3002,url:"http://127.0.0.1:3002"},sites:{host:"127.0.0.1",port:3003,url:"http://127.0.0.1:3003"},databases:{host:"127.0.0.1",port:3004,url:"http://127.0.0.1:3004"},observability:{host:"127.0.0.1",port:3005,url:"http://127.0.0.1:3005"}}};if(!t||!Ct(t))return e;try{let r=xt(t,"utf-8");return JSON.parse(r)}catch(r){return console.error("[loadRuntimeConfig] Failed to load runtime config:",r),e}}async function v(t,e,r,s,o,n,i){if(!e)return null;let a=t.headers["x-molnos-service-token"];if(a){let p=x();if(p.internalServiceSecret&&a===p.internalServiceSecret)return null}let l=t.state.user;if(!l){let p=t.headers.authorization;if(!p){let d=new Error("Unauthorized: No authentication provided");throw d.cause={statusCode:401},d}let c=p.replace(/^Bearer\s+/i,"");if(l=await e.getUserFromToken(c),!l){let d=new Error("Unauthorized: Invalid token");throw d.cause={statusCode:401},d}}W(l,r,{});let u=t.headers["x-function-bindings"];if(u)try{let p=Array.isArray(u)?u[0]:u,c=JSON.parse(p);X(c,s,o,n,i)}catch(p){if(p.cause?.statusCode===403)throw p;let c=new Error("Invalid function bindings header");throw c.cause={statusCode:400},c}return l}async function Z(t,e,r,s){try{await v(t,r,"sites.project.list","sites","project","list");let o=t.headers["x-forwarded-proto"]||"http",n=t.headers.host||"localhost:3003",i=`${o}://${n}`,a=await e.listProjects(i),l=t.query.context;if(l&&s){let u=t.headers.authorization?.replace(/^Bearer\s+/i,""),p=await s.listResourcesByType(l,"site",u);a=a.filter(c=>p.includes(c.projectId))}return t.json({success:!0,count:a.length,projects:a},200)}catch(o){let{message:n,status:i}=w(o,"Error listing projects");return t.json({error:n},i)}}var D=class{isSilent;propertyPath="";constructor(t=!1){this.isSilent=t}test(t,e){if(!e)throw new Error("Missing input!");this.updatePropertyPath();let{results:r,errors:s}=this.validate(t.properties,e),o=this.compileErrors(r,s),n=this.isSuccessful(r,o);return{errors:o,success:n}}compileErrors(t,e){let r=t.filter(s=>s.success===!1);return[...e,...r].flatMap(s=>s)}isSuccessful(t,e){return t.every(r=>r.success===!0)&&e.length===0}validate(t,e,r=[],s=[]){let o=t?.additionalProperties??!0,n=t?.required||[];s=this.checkForRequiredKeysErrors(n,e,s),s=this.checkForDisallowedProperties(Object.keys(e),Object.keys(t),s,o);for(let i in t){let a=n.includes(i)&&i!=="required",l=t[i],u=e[i],p=l.additionalProperties??!0;a&&(s=this.checkForRequiredKeysErrors(l.required||[],u,s)),this.isDefined(u)&&(this.handleValidation(i,u,l,r),s=this.checkForDisallowedProperties(Object.keys(u),Object.keys(l),s,p),this.handleNestedObject(u,l,r,s))}return{results:r,errors:s}}updatePropertyPath(t,e=""){if(!t){this.propertyPath="";return}e&&(this.propertyPath=e),this.propertyPath=`${this.propertyPath}.${t}`,this.propertyPath.startsWith(".")&&(this.propertyPath=this.propertyPath.substring(1,this.propertyPath.length))}isDefined(t){return!!(typeof t=="number"&&t===0||t||t===""||typeof t=="boolean")}checkForRequiredKeysErrors(t,e,r){if(!this.areRequiredKeysPresent(t,e)){let s=e?Object.keys(e):[],o=this.findNonOverlappingElements(t,s),n=o.length>0?`Missing the required key: '${o.join(", ")}'!`:`Missing values for required keys: '${s.filter(i=>!e[i]).join(", ")}'!`;r.push({key:"",value:e,success:!1,error:n})}return r}checkForDisallowedProperties(t,e,r,s){if(!s){let o=this.findNonOverlappingElements(t,e);o.length>0&&r.push({key:`${e}`,value:t,success:!1,error:`Has additional (disallowed) properties: '${o.join(", ")}'!`})}return r}handleValidation(t,e,r,s){this.updatePropertyPath(t);let o=this.validateProperty(this.propertyPath,r,e);s.push(...o);let n=(a,l)=>{a.forEach(u=>{let p=this.validateProperty(this.propertyPath,l.items,u);s.push(...p)}),this.updatePropertyPath()},i=a=>{let l=Object.keys(a),u=this.propertyPath;l.forEach(p=>{if(this.updatePropertyPath(p,u),this.isArray(a[p])&&r[p]?.items!=null)n(a[p],r[p]);else{let c=this.validateProperty(this.propertyPath,r[p],a[p]);s.push(...c)}})};this.isArray(e)&&r.items!=null?n(e,r):this.isObject(e)?i(e):this.updatePropertyPath()}handleNestedObject(t,e,r,s){if(this.isObject(t)){let o=this.getNestedObjects(t);for(let n of o){let i=e[n],a=t[n];i&&typeof a=="object"&&this.validate(i,a,r,s)}}}getNestedObjects(t){return Object.keys(t).filter(e=>{if(this.isObject(t))return e})}findNonOverlappingElements(t,e){return t.filter(r=>!e.includes(r))}areRequiredKeysPresent(t,e=[]){return t.every(r=>Object.keys(e).includes(r)?this.isDefined(e[r]):!1)}validateProperty(t,e,r){return this.validateInput(e,r).map(o=>{let{success:n,error:i}=o;return{key:t,value:r,success:n,error:i??""}})}validateInput(t,e){if(t){let r=[{condition:()=>t.type,validator:()=>this.isCorrectType(t.type,e),error:"Invalid type"},{condition:()=>t.format,validator:()=>this.isCorrectFormat(t.format,e),error:"Invalid format"},{condition:()=>t.minLength,validator:()=>this.isMinimumLength(t.minLength,e),error:"Length too short"},{condition:()=>t.maxLength,validator:()=>this.isMaximumLength(t.maxLength,e),error:"Length too long"},{condition:()=>t.minValue,validator:()=>this.isMinimumValue(t.minValue,e),error:"Value too small"},{condition:()=>t.maxValue,validator:()=>this.isMaximumValue(t.maxValue,e),error:"Value too large"},{condition:()=>t.matchesPattern,validator:()=>this.matchesPattern(t.matchesPattern,e),error:"Pattern does not match"}],s=[];for(let o of r)o.condition()&&!o.validator()&&s.push({success:!1,error:o.error});return s}else this.isSilent||console.warn(`Missing property '${t}' for match '${e}'. Skipping...`);return[{success:!0}]}isCorrectType(t,e){return Array.isArray(t)||(t=[t]),t.some(r=>{switch(r){case"string":return typeof e=="string";case"number":return typeof e=="number"&&!isNaN(e);case"boolean":return typeof e=="boolean";case"object":return this.isObject(e);case"array":return this.isArray(e)}})}isObject(t){return t!==null&&!this.isArray(t)&&typeof t=="object"&&t instanceof Object&&Object.prototype.toString.call(t)==="[object Object]"}isArray(t){return Array.isArray(t)}isCorrectFormat(t,e){switch(t){case"alphanumeric":return new RegExp(/^[a-zA-Z0-9]+$/).test(e);case"numeric":return new RegExp(/^-?\d+(\.\d+)?$/).test(e);case"email":return new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/).test(e);case"date":return new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(e);case"url":return new RegExp(/^(https?):\/\/[^\s$.?#].[^\s]*$/).test(e);case"hexColor":return new RegExp(/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i).test(e)}}isMinimumLength(t,e){return Array.isArray(e)?e.length>=t:e?.toString().length>=t}isMaximumLength(t,e){return Array.isArray(e)?e.length<=t:e.toString().length<=t}isMinimumValue(t,e){return e>=t}isMaximumValue(t,e){return e<=t}matchesPattern(t,e){return new RegExp(t).test(e)}schemaFrom(t){let e={properties:{additionalProperties:!1,required:[]}};for(let r in t){let s=t[r];e.properties.required.push(r),Array.isArray(s)?e.properties[r]=this.generateArraySchema(s):typeof s=="object"&&s!==null?e.properties[r]=this.generateNestedObjectSchema(s):e.properties[r]=this.generatePropertySchema(s)}return e}generateArraySchema(t){let e={type:"array"},r=t.filter(s=>s);if(r.length>0){let s=r[0];r.every(n=>typeof n==typeof s)?typeof s=="object"&&!Array.isArray(s)?e.items=this.generateNestedObjectSchema(s):e.items=this.generatePropertySchema(s):console.warn("All elements in array are not of the same type. Unable to generate a schema for these elements.")}return e}generateNestedObjectSchema(t){let e={type:"object",additionalProperties:!1,required:[]};for(let r in t){let s=t[r];e.required.push(r),typeof s=="object"&&!Array.isArray(s)&&s!==null?e[r]=this.generateNestedObjectSchema(s):e[r]=this.generatePropertySchema(s)}return e}generatePropertySchema(t){let e=typeof t,r={type:e};return e==="string"&&(r.minLength=1),r}};async function K(t){return t.body||{}}var Y={properties:{files:{type:"array",minItems:1,items:{type:"object",properties:{path:{type:"string",minLength:1},content:{type:"string",minLength:1}},required:["path","content"]}},projectId:{type:"string",pattern:"^[a-z0-9-]+$",maxLength:64},context:{type:"string",minLength:1}},required:["files"],additionalProperties:!1};var St=new D(!0);async function Q(t,e,r){try{await v(t,r,"sites.project.create","sites","project","create");let s=await K(t),o=St.test(Y,s);if(!o.success)return t.json({error:"Invalid input",details:o.errors},400);let{files:n,projectId:i}=s;if(!n)return t.json({error:"files array is required"},400);let a=await e.uploadProject(n,i);return t.json({success:!0,projectId:a},200)}catch(s){let{message:o,status:n}=w(s,"Error uploading project");return t.json({error:o},n)}}async function tt(t,e,r){try{let s=t.params.projectId;return s?!/^[a-z0-9-]+$/.test(s)||s.length>64?t.json({error:"Invalid projectId format"},400):(await v(t,r,"sites.project.delete","sites","project","delete",s),await e.projectExists(s)?(await e.deleteProject(s),t.json({success:!0,projectId:s},200)):t.json({error:"Project not found"},404)):t.json({error:"projectId parameter is required"},400)}catch(s){let{message:o,status:n}=w(s,"Error deleting project");return t.json({error:o},n)}}async function et(t,e,r){try{let s=t.params.projectId;if(!s)return t.json({error:"projectId parameter is required"},400);if(!/^[a-z0-9-]+$/.test(s)||s.length>64)return t.json({error:"Invalid projectId format"},400);if(await v(t,r,"sites.project.download","sites","project","download",s),!await e.projectExists(s))return t.json({error:"Project not found"},404);let n=await e.getProjectFiles(s);return t.json({success:!0,projectId:s,files:n},200)}catch(s){let{message:o,status:n}=w(s,"Error downloading project");return t.json({error:o},n)}}import{extname as Pt}from"node:path";function rt(t){let e=Pt(t).toLowerCase();return{".html":"text/html",".css":"text/css",".js":"application/javascript",".json":"application/json",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".txt":"text/plain",".pdf":"application/pdf",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".eot":"application/vnd.ms-fontobject",".otf":"font/otf",".xml":"application/xml",".mp4":"video/mp4",".webm":"video/webm",".mp3":"audio/mpeg",".wav":"audio/wav"}[e]||"application/octet-stream"}async function st(t,e){try{let r=t.params.projectId;if(!r)return t.json({error:"projectId is required"},400);let n=new URL(t.req.url||"","http://localhost").pathname.split(`/projects/${r}/`)[1]||""||"index.html";if(!await e.fileExists(r,n))return t.text("Not Found",404);let a=await e.getFile(r,n),l=rt(n);return t.binary(a,l)}catch(r){let{message:s,status:o}=w(r,"Error serving file");return t.json({error:s},o)}}var P=class extends Error{constructor(e){super(),this.name="ValidationError",this.message=e||"Invalid input",this.cause={statusCode:400}}};var I=class extends Error{constructor(e){super(),this.name="InvalidPathError",this.message=e||"Invalid file path",this.cause={statusCode:400}}},C=class extends Error{constructor(e){super(),this.name="InvalidFormatError",this.message=e||"Invalid format",this.cause={statusCode:400}}},R=class extends Error{constructor(e){super(),this.name="FileTooLargeError",this.message=e||"File exceeds maximum size",this.cause={statusCode:413}}},T=class extends Error{constructor(e){super(),this.name="ProjectTooLargeError",this.message=e||"Project exceeds maximum size",this.cause={statusCode:413}}},A=class extends Error{constructor(e){super(),this.name="InvalidContentError",this.message=e||"Invalid content",this.cause={statusCode:400}}};function $(t){return t&&typeof t=="number"&&t>0?t*1024*1024:0}function ot(t,e){let r={enabled:!1,requestsPerMinute:0};if(!e)return r;let s=e.services?.[t];return s||(e.global?e.global:r)}function it(t,e){let r=t?.api?.port;if(!r)throw new Error('Missing "port" input when create API service!');let s=x(),o=t?.dataPath||s.molnos.dataPath,n=t?.api?.host||s.server.host,i=t?.identityApiUrl!==void 0?t.identityApiUrl:s.identity.apiUrl,a=t?.contextApiUrl!==void 0?t.contextApiUrl:s.context.apiUrl,l=t?.debug||!1,u;return e&&(u=ot(e,s.molnos.rateLimit)),{dataPath:o,api:{port:r,host:n},...i?{identityApiUrl:i}:{},...a?{contextApiUrl:a}:{},...u?{rateLimit:u}:{},debug:l}}var M=class{baseUrl;authToken;constructor(e,r){this.baseUrl=e.replace(/\/$/,""),this.authToken=r}async createCustomRole(e,r,s,o){let n=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:e,name:r,description:s,permissions:o})});if(!n.ok){let i=`Failed to create role: ${n.statusText}`;try{i=(await n.json()).error||i}catch{i=await n.text().catch(()=>n.statusText)||i}throw new Error(i)}}async updateRole(e,r){let s=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!s.ok){let o=await s.json();throw new Error(o.error||`Failed to update role: ${s.statusText}`)}}async deleteRole(e){let r=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(e,r,s){let o=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:e,description:r,roles:s})});if(!o.ok){let i=`Failed to create service account: ${o.statusText}`;try{i=(await o.json()).error||i}catch{i=await o.text().catch(()=>o.statusText)||i}throw new Error(i)}let n=await o.json();return{id:n.id,apiKey:n.apiKey}}async deleteIdentity(e){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete service account: ${r.statusText}`)}}async getUserFromToken(e){try{let r=await fetch(`${this.baseUrl}/identity/whoami`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});return r.ok?await r.json():null}catch{return null}}getHeaders(){let e={"Content-Type":"application/json"};return this.authToken&&(e.Authorization=`Bearer ${this.authToken}`),e}};function nt(t){return t?new M(t):null}var O=class{baseUrl;constructor(e){this.baseUrl=e.replace(/\/$/,"")}async listResourcesByType(e,r,s){let o=`${this.baseUrl}/contexts/${encodeURIComponent(e)}`;try{let n={"Content-Type":"application/json"};s&&(n.Authorization=`Bearer ${s}`);let i=await fetch(o,{method:"GET",headers:n});if(!i.ok)return[];let l=(await i.json()).context?.resources||{};switch(r){case"function":return l.functions||[];case"database":return l.databases||[];case"storage":return l.storage||[];case"site":return l.sites||[];default:return[]}}catch{return[]}}};function at(t){return t?new O(t):null}var Mt=10,Ot=100,lt=$(Mt),ut=$(Ot),kt="index.html";function b(t){return/^[a-z0-9-]{4,40}$/.test(t)}function B(t){if(!t)throw new P("File path is required");let e=Rt(t.replace(/^\/+/,""));if(e.includes("..")||It(e))throw new I("Invalid file path");return e===""||e==="/"?kt:e}var z=class{rootPath;constructor(e){this.rootPath=e.path||"sites-data"}async start(){await q(this.rootPath,{recursive:!0})}generateProjectId(e=8){return $t(Math.ceil(e/2)).toString("hex").slice(0,e)}async uploadProject(e,r=null){if(!e||!Array.isArray(e))throw new P("Invalid request format. Expected array of files in the request body.");r||(r=this.generateProjectId());let s=y(this.rootPath,r);if(!b(r))throw new C("Invalid project ID format");Et(s)&&k(s,{recursive:!0,force:!0}),await q(s,{recursive:!0});let o=0;for(let n of e){if(!n.path||!n.content)continue;let i=await this.saveFile(s,n.path,n.content);if(o+=i,o>ut)throw k(s,{recursive:!0,force:!0}),new T(`Project exceeds maximum size of ${ut/1024/1024}MB`)}if(e.length===0||o===0)throw k(s,{recursive:!0,force:!0}),new P("No valid files were provided");return r}async saveFile(e,r,s){let o=B(r),n=y(e,o),i=jt(n);await q(i,{recursive:!0});let a;try{a=Buffer.from(s,"base64")}catch(l){throw new A(`Invalid content for file "${o}": ${l.message}`)}if(a.length>lt)throw new R(`File "${o}" exceeds maximum size of ${lt/1024/1024}MB`);return await Tt(n,a),a.length}async getFile(e,r){if(!b(e))throw new C("Invalid project ID format");let s=B(r),o=y(this.rootPath,e,s);return await At(o)}async fileExists(e,r){try{if(!b(e))return!1;let s=B(r),o=y(this.rootPath,e,s);return(await ct(o)).isFile()}catch{return!1}}async projectExists(e){try{if(!b(e))return!1;let r=y(this.rootPath,e);return(await ct(r)).isDirectory()}catch{return!1}}async getProjectFiles(e){if(!b(e))throw new C("Invalid project ID format");let r=y(this.rootPath,e),{readdir:s,readFile:o}=await import("node:fs/promises"),n=[],i=async(a,l)=>{let u=await s(a,{withFileTypes:!0});for(let p of u){let c=y(a,p.name);if(p.isDirectory())await i(c,l);else if(p.isFile()){let d=await o(c),h=c.slice(l.length+1);n.push({path:h,content:d.toString("base64")})}}};return await i(r,r),n}async deleteProject(e){if(!b(e))throw new C("Invalid project ID format");let r=y(this.rootPath,e);k(r,{recursive:!0,force:!0})}async listProjects(e){try{let{readdir:r,stat:s}=await import("node:fs/promises"),o=await r(this.rootPath,{withFileTypes:!0}),n=[];for(let i of o)if(i.isDirectory()&&b(i.name)){let a=y(this.rootPath,i.name),l=await s(a),u=0,p=0,c=async h=>{let m=await r(h,{withFileTypes:!0});for(let f of m){let j=y(h,f.name);if(f.isDirectory())await c(j);else if(f.isFile()){u++;let dt=await s(j);p+=dt.size}}};await c(a);let d=h=>{if(h===0)return"0 B";let m=1024,f=["B","KB","MB","GB"],j=Math.floor(Math.log(h)/Math.log(m));return`${(h/m**j).toFixed(1)} ${f[j]}`};n.push({projectId:i.name,name:i.name,url:`${e}/projects/${i.name}/`,status:"active",files:u,size:d(p),lastDeployed:l.mtime.toISOString(),createdAt:l.birthtime.toISOString()})}return n}catch{return[]}}};async function Lt(t){let e=t.dataPath||"data",r=y(e,"sites"),s=new z({path:r});await s.start();let o=nt(t.identityApiUrl),n=at(t.contextApiUrl),i=x(),a=new N({port:t.api.port||i.services.sites.port,host:t.api.host||i.services.sites.host,maxBodySize:$(1e3),rateLimit:t.rateLimit||{enabled:!1,requestsPerMinute:0}});return a.get("/projects",async u=>Z(u,s,o,n)),a.post("/projects",async u=>Q(u,s,o)),a.delete("/projects/:projectId",async u=>tt(u,s,o)),a.get("/projects/:projectId/download",async u=>et(u,s,o)),a.get("/projects/:projectId",async u=>{let p=u.params.projectId;return u.redirect(`/projects/${p}/`,301)}),a.get("/projects/:projectId/*",async u=>st(u,s)),a.start()}if(import.meta.url===`file://${process.argv[1]}`){let t=x(),r=process.argv.slice(2).find(n=>n.startsWith("--port=")),s=r?parseInt(r.split("=")[1],10):t.services.sites.port,o=it({api:{port:s}},"sites");Lt(o)}export{z as SitesService,Lt as startServer};