molnos 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +20 -0
- package/README.md +31 -16
- package/VERSION +1 -0
- package/dist/databases.mjs +10 -1
- package/dist/functions.mjs +14 -5
- package/dist/molnos_core.mjs +37 -38
- package/dist/observability.mjs +12 -3
- package/dist/sites.mjs +10 -1
- package/dist/storage.mjs +10 -1
- package/oss-licenses.txt +97 -0
- package/package.json +14 -9
- package/sbom.json +1 -0
package/dist/molnos_core.mjs
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
// MolnOS Core - See LICENSE file for copyright and license details.
|
|
2
|
-
import
|
|
2
|
+
import _,{createHash as Ss,createHmac as bs,scryptSync as Ar,randomBytes as Ir,createCipheriv as Er,createDecipheriv as kr}from"crypto";import{URL as Pr}from"url";var Te=class extends Error{constructor(t){super(t),this.name="ValidationError",this.message=t||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as gr,readFileSync as yr}from"node:fs";var z=class{config={};options=[];validators=[];autoValidate=!0;constructor(t){let e=t?.configFilePath,r=t?.args||[],i=t?.config||{};this.options=t?.options||[],this.validators=t?.validators||[],t?.autoValidate!==void 0&&(this.autoValidate=t.autoValidate),this.config=this.createConfig(e,r,i)}deepMerge(t,e){let r={...t};for(let i in e)e[i]!==void 0&&(e[i]!==null&&typeof e[i]=="object"&&!Array.isArray(e[i])&&i in t&&t[i]!==null&&typeof t[i]=="object"&&!Array.isArray(t[i])?r[i]=this.deepMerge(t[i],e[i]):e[i]!==void 0&&(r[i]=e[i]));return r}setValueAtPath(t,e,r){let i=e.split("."),s=t;for(let n=0;n<i.length-1;n++){let a=i[n];!(a in s)||s[a]===null?s[a]={}:typeof s[a]!="object"&&(s[a]={}),s=s[a]}let o=i[i.length-1];s[o]=r}getValueAtPath(t,e){let r=e.split("."),i=t;for(let s of r){if(i==null)return;i=i[s]}return i}createConfig(t,e=[],r={}){let i={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(i,a.path,a.defaultValue);let s={};if(t&&gr(t))try{let a=yr(t,"utf8");s=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 o=this.parseCliArgs(e),n=this.deepMerge({},i);return n=this.deepMerge(n,s),n=this.deepMerge(n,r),n=this.deepMerge(n,o),n}parseCliArgs(t){let e={},r=t[0]?.endsWith("node")||t[0]?.endsWith("node.exe")?2:0;for(;r<t.length;){let i=t[r++],s=this.options.find(o=>o.flag===i);if(s)if(s.isFlag)this.setValueAtPath(e,s.path,!0);else if(r<t.length&&!t[r].startsWith("-")){let o=t[r++];if(s.parser)try{o=s.parser(o)}catch(n){console.error(`Error parsing value for ${s.flag}: ${n instanceof Error?n.message:String(n)}`);continue}if(s.validator){let n=s.validator(o);if(n!==!0&&typeof n=="string"){console.error(`Invalid value for ${s.flag}: ${n}`);continue}if(n===!1){console.error(`Invalid value for ${s.flag}`);continue}}this.setValueAtPath(e,s.path,o)}else console.error(`Missing value for option ${i}`)}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 Te(t.message);if(typeof r=="string")throw new Te(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 i=this.deepMerge(r,e);this.setValueAtPath(this.config,t,i);return}}this.setValueAtPath(this.config,t,e)}getHelpText(){let t=`Available configuration options:
|
|
3
3
|
|
|
4
|
-
`;for(let
|
|
5
|
-
`,
|
|
6
|
-
`),
|
|
7
|
-
`),
|
|
8
|
-
`;return
|
|
9
|
-
`);if(
|
|
10
|
-
`)})}parseCapabilities(
|
|
11
|
-
`);this.serverCapabilities=[];for(let r=1;r<
|
|
4
|
+
`;for(let e of this.options)t+=`${e.flag}${e.isFlag?"":" <value>"}
|
|
5
|
+
`,e.description&&(t+=` ${e.description}
|
|
6
|
+
`),e.defaultValue!==void 0&&(t+=` Default: ${JSON.stringify(e.defaultValue)}
|
|
7
|
+
`),t+=`
|
|
8
|
+
`;return t}};var q={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`)}}};import{EventEmitter as Tr}from"events";var ze=class extends Error{constructor(t){super(),this.name="ValidationError",this.message=t,this.cause={statusCode:400}}};import{existsSync as vr,readFileSync as wr}from"fs";var qe=class{config;defaults={configFilePath:"mikromail.config.json",args:[]};constructor(t){let e=t?.config||{},r=t?.configFilePath||this.defaults.configFilePath,i=t?.args||this.defaults.args;this.config=this.create(r,i,e)}create(t,e,r){let i={host:"",user:"",password:"",port:465,secure:!0,debug:!1,maxRetries:2,skipEmailValidation:!1,skipMXRecordCheck:!1},s={};if(vr(t))try{let n=wr(t,"utf8");s=JSON.parse(n),console.log(`Loaded configuration from ${t}`)}catch(n){console.error(`Error reading config file: ${n instanceof Error?n.message:String(n)}`)}let o=this.parseCliArgs(e);return{...i,...r,...s,...o}}parseCliArgs(t){let e={};for(let r=2;r<t.length;r++)switch(t[r]){case"--host":r+1<t.length&&(e.host=t[++r]);break;case"--user":r+1<t.length&&(e.user=t[++r]);break;case"--password":r+1<t.length&&(e.password=t[++r]);break;case"--port":if(r+1<t.length){let s=Number.parseInt(t[++r],10);Number.isNaN(s)||(e.port=s)}break;case"--secure":e.secure=!0;break;case"--debug":e.debug=!0;break;case"--retries":if(r+1<t.length){let s=Number.parseInt(t[++r],10);Number.isNaN(s)||(e.maxRetries=s)}break}return e}validate(){if(!this.config.host)throw new ze("Host value not found")}get(){return this.validate(),this.config}};import{promises as Sr}from"dns";function oe(t){try{let[e,r]=t.split("@");if(!e||e.length>64||e.startsWith(".")||e.endsWith(".")||e.includes("..")||!/^[a-zA-Z0-9!#$%&'*+\-/=?^_`{|}~.]+$/.test(e)||!r||r.length>255)return!1;if(r.startsWith("[")&&r.endsWith("]")){let s=r.slice(1,-1);return s.startsWith("IPv6:")?!0:/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(s)}if(r.startsWith(".")||r.endsWith(".")||r.includes(".."))return!1;let i=r.split(".");if(i.length<2||i[i.length-1].length<2)return!1;for(let s of i)if(!s||s.length>63||!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(s))return!1;return!0}catch{return!1}}async function br(t){try{let e=await Sr.resolveMx(t);return!!e&&e.length>0}catch{return!1}}async function _e(t){try{let e=t.split("@")[1];return e?await br(e):!1}catch{return!1}}import{Buffer as ne}from"buffer";import Re from"crypto";import xr from"net";import Cr from"os";import We from"tls";var Ke=class{config;socket;connected;lastCommand;serverCapabilities;secureMode;retryCount;maxEmailSize=10485760;constructor(t){this.config={host:t.host,user:t.user,password:t.password,port:t.port??(t.secure?465:587),secure:t.secure??!0,debug:t.debug??!1,timeout:t.timeout??1e4,clientName:t.clientName??Cr.hostname(),maxRetries:t.maxRetries??3,retryDelay:t.retryDelay??1e3,skipAuthentication:t.skipAuthentication||!1,skipEmailValidation:t.skipEmailValidation||!1,skipMXRecordCheck:t.skipMXRecordCheck||!1},this.socket=null,this.connected=!1,this.lastCommand="",this.serverCapabilities=[],this.secureMode=this.config.secure,this.retryCount=0}log(t,e=!1){this.config.debug&&console.log(`${e?"SMTP ERROR: ":"SMTP: "}${t}`)}async connect(){return new Promise((t,e)=>{let r=setTimeout(()=>{e(new Error(`Connection timeout after ${this.config.timeout}ms`)),this.socket?.destroy()},this.config.timeout);try{this.config.secure?this.createTLSConnection(r,t,e):this.createPlainConnection(r,t,e)}catch(i){clearTimeout(r),this.log(`Failed to create socket: ${i.message}`,!0),e(i)}})}createTLSConnection(t,e,r){this.socket=We.connect({host:this.config.host,port:this.config.port,rejectUnauthorized:!0,minVersion:"TLSv1.2",ciphers:"HIGH:!aNULL:!MD5:!RC4"}),this.setupSocketEventHandlers(t,e,r)}createPlainConnection(t,e,r){this.socket=xr.createConnection({host:this.config.host,port:this.config.port}),this.setupSocketEventHandlers(t,e,r)}setupSocketEventHandlers(t,e,r){this.socket&&(this.socket.once("error",i=>{clearTimeout(t),this.log(`Connection error: ${i.message}`,!0),r(new Error(`SMTP connection error: ${i.message}`))}),this.socket.once("connect",()=>{this.log("Connected to SMTP server"),clearTimeout(t),this.socket.once("data",i=>{let s=i.toString().trim();this.log(`Server greeting: ${s}`),s.startsWith("220")?(this.connected=!0,this.secureMode=this.config.secure,e()):(r(new Error(`Unexpected server greeting: ${s}`)),this.socket.destroy())})}),this.socket.once("close",i=>{this.connected?this.log(`Connection closed${i?" with error":""}`):(clearTimeout(t),r(new Error("Connection closed before initialization completed"))),this.connected=!1}))}async upgradeToTLS(){if(!(!this.socket||this.secureMode))return new Promise((t,e)=>{let i={socket:this.socket,host:this.config.host,rejectUnauthorized:!0,minVersion:"TLSv1.2",ciphers:"HIGH:!aNULL:!MD5:!RC4"},s=We.connect(i);s.once("error",o=>{this.log(`TLS upgrade error: ${o.message}`,!0),e(new Error(`STARTTLS error: ${o.message}`))}),s.once("secureConnect",()=>{this.log("Connection upgraded to TLS"),s.authorized?(this.socket=s,this.secureMode=!0,t()):e(new Error(`TLS certificate verification failed: ${s.authorizationError}`))})})}async sendCommand(t,e,r=this.config.timeout){if(!this.socket||!this.connected)throw new Error("Not connected to SMTP server");return new Promise((i,s)=>{let o=setTimeout(()=>{this.socket?.removeListener("data",a),s(new Error(`Command timeout after ${r}ms: ${t}`))},r),n="",a=c=>{n+=c.toString();let d=n.split(`\r
|
|
9
|
+
`);if(d.length>0&&d[d.length-1]===""){let p=d[d.length-2]||"",u=/^(\d{3})(.?)/.exec(p);u?.[1]&&u[2]!=="-"&&(this.socket?.removeListener("data",a),clearTimeout(o),this.log(`SMTP Response: ${n.trim()}`),u[1]===e.toString()?i(n.trim()):s(new Error(`SMTP Error: ${n.trim()}`)))}};this.socket.on("data",a),t.startsWith("AUTH PLAIN")||t.startsWith("AUTH LOGIN")||this.lastCommand==="AUTH LOGIN"&&!t.startsWith("AUTH")?this.log("SMTP Command: [Credentials hidden]"):this.log(`SMTP Command: ${t}`),this.lastCommand=t,this.socket.write(`${t}\r
|
|
10
|
+
`)})}parseCapabilities(t){let e=t.split(`\r
|
|
11
|
+
`);this.serverCapabilities=[];for(let r=1;r<e.length;r++){let i=e[r];if(i.match(/^\d{3}/)&&i.charAt(3)===" "){let s=i.substr(4).toUpperCase();this.serverCapabilities.push(s)}}this.log(`Server capabilities: ${this.serverCapabilities.join(", ")}`)}getBestAuthMethod(){if(this.serverCapabilities.map(e=>e.split(" ")[0]).includes("AUTH")){let e=this.serverCapabilities.find(r=>r.startsWith("AUTH "));if(e){let r=e.split(" ").slice(1);if(r.includes("CRAM-MD5"))return"CRAM-MD5";if(r.includes("LOGIN"))return"LOGIN";if(r.includes("PLAIN"))return"PLAIN"}}return"PLAIN"}async authenticate(){switch(this.getBestAuthMethod()){case"CRAM-MD5":await this.authenticateCramMD5();break;case"LOGIN":await this.authenticateLogin();break;default:await this.authenticatePlain();break}}async authenticatePlain(){let t=ne.from(`\0${this.config.user}\0${this.config.password}`).toString("base64");await this.sendCommand(`AUTH PLAIN ${t}`,235)}async authenticateLogin(){await this.sendCommand("AUTH LOGIN",334),await this.sendCommand(ne.from(this.config.user).toString("base64"),334),await this.sendCommand(ne.from(this.config.password).toString("base64"),235)}async authenticateCramMD5(){let t=await this.sendCommand("AUTH CRAM-MD5",334),e=ne.from(t.substr(4),"base64").toString("utf8"),r=Re.createHmac("md5",this.config.password);r.update(e);let i=r.digest("hex"),s=`${this.config.user} ${i}`,o=ne.from(s).toString("base64");await this.sendCommand(o,235)}generateMessageId(){let t=Re.randomBytes(16).toString("hex"),e=this.config.user.split("@")[1]||"localhost";return`<${t}@${e}>`}generateBoundary(){return`----=_NextPart_${Re.randomBytes(12).toString("hex")}`}encodeHeaderValue(t){return/^[\x00-\x7F]*$/.test(t)?t:`=?UTF-8?Q?${t.replace(/[^\x00-\x7F]/g,e=>{let r=e.charCodeAt(0).toString(16).toUpperCase();return`=${r.length<2?`0${r}`:r}`})}?=`}sanitizeHeader(t){let e=t.replace(/[\r\n\t]+/g," ").replace(/\s{2,}/g," ").trim();return this.encodeHeaderValue(e)}createEmailHeaders(t){let e=this.generateMessageId(),r=new Date().toUTCString(),i=t.from||this.config.user,s=Array.isArray(t.to)?t.to.join(", "):t.to,o=[`From: ${this.sanitizeHeader(i)}`,`To: ${this.sanitizeHeader(s)}`,`Subject: ${this.sanitizeHeader(t.subject)}`,`Message-ID: ${e}`,`Date: ${r}`,"MIME-Version: 1.0"];if(t.cc){let n=Array.isArray(t.cc)?t.cc.join(", "):t.cc;o.push(`Cc: ${this.sanitizeHeader(n)}`)}if(t.replyTo&&o.push(`Reply-To: ${this.sanitizeHeader(t.replyTo)}`),t.headers)for(let[n,a]of Object.entries(t.headers))/^[a-zA-Z0-9-]+$/.test(n)&&(/^(from|to|cc|bcc|subject|date|message-id)$/i.test(n)||o.push(`${n}: ${this.sanitizeHeader(a)}`));return o}createMultipartEmail(t){let{text:e,html:r}=t,i=this.createEmailHeaders(t),s=this.generateBoundary();return r&&e?(i.push(`Content-Type: multipart/alternative; boundary="${s}"`),`${i.join(`\r
|
|
12
12
|
`)}\r
|
|
13
13
|
\r
|
|
14
|
-
--${
|
|
14
|
+
--${s}\r
|
|
15
15
|
Content-Type: text/plain; charset=utf-8\r
|
|
16
16
|
\r
|
|
17
|
-
${
|
|
17
|
+
${e||""}\r
|
|
18
18
|
\r
|
|
19
|
-
--${
|
|
19
|
+
--${s}\r
|
|
20
20
|
Content-Type: text/html; charset=utf-8\r
|
|
21
21
|
\r
|
|
22
22
|
${r||""}\r
|
|
23
23
|
\r
|
|
24
|
-
--${
|
|
25
|
-
`):(
|
|
24
|
+
--${s}--\r
|
|
25
|
+
`):(i.push("Content-Type: text/html; charset=utf-8"),r?`${i.join(`\r
|
|
26
26
|
`)}\r
|
|
27
27
|
\r
|
|
28
|
-
${r}`:`${
|
|
28
|
+
${r}`:`${i.join(`\r
|
|
29
29
|
`)}\r
|
|
30
30
|
\r
|
|
31
|
-
${
|
|
32
|
-
.`,250);let c=/Message-ID: (.*)/i.exec(a);return{success:!0,messageId:c?c[1].trim():void 0,message:"Email sent successfully"}}catch(a){let c=a.message;if(this.log(`Error sending email: ${c}`,!0),c.includes("5.")||c.includes("Authentication failed")||c.includes("certificate")||this.retryCount>=this.config.maxRetries)return{success:!1,error:c};try{this.connected&&await this.sendCommand("RSET",250)}catch{}this.socket?.end(),this.connected=!1}return{success:!1,error:"Maximum retry count exceeded"}}async close(){try{this.connected&&await this.sendCommand("QUIT",221)}catch(
|
|
33
|
-
Click this link to login: ${
|
|
31
|
+
${e||""}`)}async smtpHandshake(){let t=await this.sendCommand(`EHLO ${this.config.clientName}`,250);if(this.parseCapabilities(t),!this.secureMode&&this.serverCapabilities.includes("STARTTLS")){await this.sendCommand("STARTTLS",220),await this.upgradeToTLS();let e=await this.sendCommand(`EHLO ${this.config.clientName}`,250);this.parseCapabilities(e)}this.config.skipAuthentication?this.log("Authentication skipped (testing mode)"):await this.authenticate()}async sendEmail(t){let e=t.from||this.config.user,{to:r,subject:i}=t,s=t.text||"",o=t.html||"";if(!e||!r||!i||!s&&!o)return{success:!1,error:"Missing required email parameters (from, to, subject, and either text or html)"};let n=Array.isArray(t.to)?t.to:[t.to];if(this.config.skipEmailValidation)this.log("Email validation skipped (testing mode)");else{if(!oe(e))return{success:!1,error:"Invalid email address format"};for(let a of n)if(!oe(a))return{success:!1,error:`Invalid recipient email address format: ${a}`}}for(this.retryCount=0;this.retryCount<=this.config.maxRetries;this.retryCount++)try{this.retryCount>0&&(this.log(`Retrying email send (attempt ${this.retryCount} of ${this.config.maxRetries})...`),await new Promise(p=>setTimeout(p,this.config.retryDelay))),this.connected||(await this.connect(),await this.smtpHandshake()),await this.sendCommand(`MAIL FROM:<${e}>`,250);for(let p of n)await this.sendCommand(`RCPT TO:<${p}>`,250);if(t.cc){let p=Array.isArray(t.cc)?t.cc:[t.cc];for(let u of p)oe(u)&&await this.sendCommand(`RCPT TO:<${u}>`,250)}if(t.bcc){let p=Array.isArray(t.bcc)?t.bcc:[t.bcc];for(let u of p)oe(u)&&await this.sendCommand(`RCPT TO:<${u}>`,250)}await this.sendCommand("DATA",354);let a=this.createMultipartEmail(t);if(a.length>this.maxEmailSize)return{success:!1,error:"Email size exceeds maximum allowed"};await this.sendCommand(`${a}\r
|
|
32
|
+
.`,250);let c=/Message-ID: (.*)/i.exec(a);return{success:!0,messageId:c?c[1].trim():void 0,message:"Email sent successfully"}}catch(a){let c=a.message;if(this.log(`Error sending email: ${c}`,!0),c.includes("5.")||c.includes("Authentication failed")||c.includes("certificate")||this.retryCount>=this.config.maxRetries)return{success:!1,error:c};try{this.connected&&await this.sendCommand("RSET",250)}catch{}this.socket?.end(),this.connected=!1}return{success:!1,error:"Maximum retry count exceeded"}}async close(){try{this.connected&&await this.sendCommand("QUIT",221)}catch(t){this.log(`Error during QUIT: ${t.message}`,!0)}finally{this.socket&&(this.socket.destroy(),this.socket=null,this.connected=!1)}}};var je=class{smtpClient;config;constructor(t){let e=new qe(t).get(),r=new Ke(e);this.smtpClient=r,this.config=e}async send(t){try{let e=Array.isArray(t.to)?t.to:[t.to];if(!this.config.skipMXRecordCheck)for(let i of e)await _e(i)||console.error(`Warning: No MX records found for recipient domain: ${i}`);let r=await this.smtpClient.sendEmail(t);r.success?console.log(`Message ID: ${r.messageId}`):console.error(`Failed to send email: ${r.error}`),await this.smtpClient.close()}catch(e){console.error("Error in email sending process:",e.message)}}};var $e=()=>{let t=Rr(process.env.DEBUG)||!1;return{auth:{jwtSecret:process.env.AUTH_JWT_SECRET||"your-jwt-secret",magicLinkExpirySeconds:900,jwtExpirySeconds:3600,refreshTokenExpirySeconds:604800,maxActiveSessions:3,appUrl:process.env.APP_URL||"http://localhost:3000",templates:null,debug:t},email:{emailSubject:"Your Secure Login Link",user:process.env.EMAIL_USER||"",host:process.env.EMAIL_HOST||"",password:process.env.EMAIL_PASSWORD||"",port:465,secure:!0,maxRetries:2,debug:t},storage:{databaseDirectory:"mikroauth",encryptionKey:process.env.STORAGE_KEY||"",debug:t},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"],debug:t}}};function Rr(t){return t==="true"||t===!0}var jr=class{algorithm="HS256";secret="HS256";constructor(t){if(process.env.NODE_ENV==="production"&&(!t||t.length<32||t===$e().auth.jwtSecret))throw new Error("Production environment requires a strong JWT secret (min 32 chars)");this.secret=t}sign(t,e={}){let r={alg:this.algorithm,typ:"JWT"},i=Math.floor(Date.now()/1e3),s={...t,iat:i};e.exp!==void 0&&(s.exp=i+e.exp),e.notBefore!==void 0&&(s.nbf=i+e.notBefore),e.issuer&&(s.iss=e.issuer),e.audience&&(s.aud=e.audience),e.subject&&(s.sub=e.subject),e.jwtid&&(s.jti=e.jwtid);let o=this.base64UrlEncode(JSON.stringify(r)),n=this.base64UrlEncode(JSON.stringify(s)),a=`${o}.${n}`,c=this.createSignature(a);return`${a}.${c}`}verify(t,e={}){let r=this.decode(t);if(r.header.alg!==this.algorithm)throw new Error(`Invalid algorithm. Expected ${this.algorithm}, got ${r.header.alg}`);let[i,s]=t.split("."),o=`${i}.${s}`;if(this.createSignature(o)!==r.signature)throw new Error("Invalid signature");let n=r.payload,a=Math.floor(Date.now()/1e3),c=e.clockTolerance||0;if(n.exp!==void 0&&n.exp+c<a)throw new Error("Token expired");if(n.nbf!==void 0&&n.nbf-c>a)throw new Error("Token not yet valid");if(e.issuer&&n.iss!==e.issuer)throw new Error("Invalid issuer");if(e.audience&&n.aud!==e.audience)throw new Error("Invalid audience");if(e.subject&&n.sub!==e.subject)throw new Error("Invalid subject");return n}decode(t){let e=t.split(".");if(e.length!==3)throw new Error("Invalid token format");try{let[r,i,s]=e,o=JSON.parse(this.base64UrlDecode(r)),n=JSON.parse(this.base64UrlDecode(i));return{header:o,payload:n,signature:s}}catch{throw new Error("Failed to decode token")}}createSignature(t){let e=_.createHmac("sha256",this.secret).update(t).digest();return this.base64UrlEncode(e)}base64UrlEncode(t){let e;return typeof t=="string"?e=Buffer.from(t):e=t,e.toString("base64").replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}base64UrlDecode(t){let e=t.replace(/-/g,"+").replace(/_/g,"/");switch(e.length%4){case 0:break;case 2:e+="==";break;case 3:e+="=";break;default:throw new Error("Invalid base64 string")}return Buffer.from(e,"base64").toString()}},$r=class{templates;constructor(t){t?this.templates=t:this.templates=Mr}getText(t,e,r){return this.templates.textVersion(t,e,r).trim()}getHtml(t,e,r){return this.templates.htmlVersion(t,e,r).trim()}},Mr={textVersion:(t,e,r)=>`
|
|
33
|
+
Click this link to login: ${t}
|
|
34
34
|
|
|
35
35
|
Security Information:
|
|
36
|
-
- Expires in ${
|
|
36
|
+
- Expires in ${e} minutes
|
|
37
37
|
- Can only be used once
|
|
38
38
|
- Should only be used by you
|
|
39
39
|
|
|
40
40
|
If you didn't request this link, please ignore this email.
|
|
41
|
-
`,htmlVersion:(e,
|
|
41
|
+
`,htmlVersion:(t,e,r)=>`
|
|
42
42
|
<!DOCTYPE html>
|
|
43
43
|
<html>
|
|
44
44
|
<head>
|
|
@@ -85,7 +85,7 @@ If you didn't request this link, please ignore this email.
|
|
|
85
85
|
<div class="container">
|
|
86
86
|
<h2>Your Secure Login Link</h2>
|
|
87
87
|
<p>Click the button below to log in to your account:</p>
|
|
88
|
-
<a href="${
|
|
88
|
+
<a href="${t}" class="button">Login to Your Account</a>
|
|
89
89
|
|
|
90
90
|
<p>
|
|
91
91
|
Hello, this is a test email! Hall\xE5, MikroMail has international support for, among others, espa\xF1ol, fran\xE7ais, portugu\xEAs, \u4E2D\u6587, \u65E5\u672C\u8A9E, and \u0420\u0443\u0441\u0441\u043A\u0438\u0439!
|
|
@@ -94,7 +94,7 @@ If you didn't request this link, please ignore this email.
|
|
|
94
94
|
<div class="security-info">
|
|
95
95
|
<h3>Security Information:</h3>
|
|
96
96
|
<ul>
|
|
97
|
-
<li>This link expires in ${
|
|
97
|
+
<li>This link expires in ${e} minutes</li>
|
|
98
98
|
<li>Can only be used once</li>
|
|
99
99
|
<li>Should only be used by you</li>
|
|
100
100
|
</ul>
|
|
@@ -108,23 +108,22 @@ If you didn't request this link, please ignore this email.
|
|
|
108
108
|
</div>
|
|
109
109
|
</body>
|
|
110
110
|
</html>
|
|
111
|
-
`},si=class{constructor(e){this.options=e}sentEmails=[];async sendMail(e){this.sentEmails.push(e),this.options?.logToConsole&&(console.log("Email sent:"),console.log(`From: ${e.from}`),console.log(`To: ${e.to}`),console.log(`Subject: ${e.subject}`),console.log(`Text: ${e.text}`)),this.options?.onSend&&this.options.onSend(e)}getSentEmails(){return[...this.sentEmails]}clearSentEmails(){this.sentEmails=[]}},ii=class{data=new Map;collections=new Map;expiryEmitter=new Zs;expiryCheckInterval;constructor(e=1e3){this.expiryCheckInterval=setInterval(()=>this.checkExpiredItems(),e)}destroy(){clearInterval(this.expiryCheckInterval),this.data.clear(),this.collections.clear(),this.expiryEmitter.removeAllListeners()}checkExpiredItems(){let e=Date.now();for(let[t,r]of this.data.entries())r.expiry&&r.expiry<e&&(this.data.delete(t),this.expiryEmitter.emit("expired",t));for(let[t,r]of this.collections.entries())r.expiry&&r.expiry<e&&(this.collections.delete(t),this.expiryEmitter.emit("expired",t))}async set(e,t,r){let s=r?Date.now()+r*1e3:null;this.data.set(e,{value:t,expiry:s})}async get(e){let t=this.data.get(e);return t?t.expiry&&t.expiry<Date.now()?(this.data.delete(e),null):t.value:null}async delete(e){this.data.delete(e),this.collections.delete(e)}async addToCollection(e,t,r){this.collections.has(e)||this.collections.set(e,{items:[],expiry:r?Date.now()+r*1e3:null});let s=this.collections.get(e);s&&(r&&(s.expiry=Date.now()+r*1e3),s.items.push(t))}async removeFromCollection(e,t){let r=this.collections.get(e);r&&(r.items=r.items.filter(s=>s!==t))}async getCollection(e){let t=this.collections.get(e);return t?[...t.items]:[]}async getCollectionSize(e){let t=this.collections.get(e);return t?t.items.length:0}async removeOldestFromCollection(e){let t=this.collections.get(e);return!t||t.items.length===0?null:t.items.shift()||null}async findKeys(e){let t=e.replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${t}$`),s=Array.from(this.data.keys()).filter(o=>r.test(o)),i=Array.from(this.collections.keys()).filter(o=>r.test(o));return[...new Set([...s,...i])]}};function Ut(e){if(!e||e.trim()===""||(e.match(/@/g)||[]).length!==1)return!1;let[t,r]=e.split("@");return!(!t||!r||e.includes("..")||!oi(t)||!ni(r))}function oi(e){return e.startsWith('"')&&e.endsWith('"')?!e.slice(1,-1).includes('"'):e.length>64||e.startsWith(".")||e.endsWith(".")?!1:/^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$/.test(e)}function ni(e){if(e.startsWith("[")&&e.endsWith("]")){let r=e.slice(1,-1);return r.startsWith("IPv6:")?ci(r.slice(5)):ai(r)}let t=e.split(".");if(t.length===0)return!1;for(let r of t)if(!r||r.length>63||!/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(r))return!1;if(t.length>1){let r=t[t.length-1];if(!/^[a-zA-Z]{2,}$/.test(r))return!1}return!0}function ai(e){return/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/.test(e)}function ci(e){if(!/^[a-fA-F0-9:]+$/.test(e))return!1;let t=e.split(":");return!(t.length<2||t.length>8)}var w=it(),li=e=>{let t={configFilePath:"mikroauth.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:w.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:w.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:w.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:w.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:w.auth.maxActiveSessions},{flag:"--appUrl",path:"auth.appUrl",defaultValue:w.auth.appUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:w.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:w.email.host},{flag:"--emailUser",path:"email.user",defaultValue:w.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:w.email.password},{flag:"--emailPort",path:"email.port",defaultValue:w.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:w.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:w.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:w.email.debug},{flag:"--dir",path:"storage.databaseDirectory",defaultValue:w.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:w.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:w.storage.debug},{flag:"--port",path:"server.port",defaultValue:w.server.port},{flag:"--host",path:"server.host",defaultValue:w.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:w.server.useHttps},{flag:"--https",path:"server.useHttp2",isFlag:!0,defaultValue:w.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:w.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:w.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:w.server.sslCa},{flag:"--ratelimit",path:"server.rateLimit.enabled",defaultValue:w.server.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"server.rateLimit.requestsPerMinute",defaultValue:w.server.rateLimit.requestsPerMinute},{flag:"--allowed",path:"server.allowedDomains",defaultValue:w.server.allowedDomains,parser:J.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:w.server.debug}]};return e&&(t.config=e),t},Ae={linkSent:"If a matching account was found, a magic link has been sent.",revokedSuccess:"All other sessions revoked successfully.",logoutSuccess:"Logged out successfully."},Bt=class{config;email;storage;jwtService;templates;constructor(e,t,r){let s=new N(li({auth:e.auth,email:e.email})).get();s.auth.debug&&console.log("Using configuration:",s),this.config=s,this.email=t||new si,this.storage=r||new ii,this.jwtService=new ei(s.auth.jwtSecret),this.templates=new ti(s?.auth.templates),this.checkIfUsingDefaultCredentialsInProduction()}checkIfUsingDefaultCredentialsInProduction(){process.env.NODE_ENV==="production"&&this.config.auth.jwtSecret===it().auth.jwtSecret&&(console.error("WARNING: Using default secrets in production environment!"),process.exit(1))}generateToken(e){let t=Date.now().toString(),r=X.randomBytes(32).toString("hex");return X.createHash("sha256").update(`${e}:${t}:${r}`).digest("hex")}generateJsonWebToken(e){return this.jwtService.sign({sub:e.id,email:e.email,username:e.username,role:e.role,exp:Math.floor(Date.now()/1e3)+3600*24})}generateRefreshToken(){return X.randomBytes(40).toString("hex")}async trackSession(e,t,r){let s=`sessions:${e}`;if(await this.storage.getCollectionSize(s)>=this.config.auth.maxActiveSessions){let i=await this.storage.removeOldestFromCollection(s);i&&await this.storage.delete(`refresh:${i}`)}await this.storage.addToCollection(s,t,this.config.auth.refreshTokenExpirySeconds),await this.storage.set(`refresh:${t}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds)}generateMagicLinkUrl(e){let{token:t,email:r,appUrl:s}=e,i=s||this.config.auth.appUrl;try{return new Ys(i),`${i}?token=${encodeURIComponent(t)}&email=${encodeURIComponent(r)}`}catch{throw new Error("Invalid base URL configuration")}}async createMagicLink(e){let{email:t,ip:r,metadata:s,appUrl:i,subject:o}=e;if(!Ut(t))throw new Error("Valid email required");try{let n=this.generateToken(t),a=`magic_link:${n}`,c={email:t,ipAddress:r||"unknown",createdAt:Date.now()};await this.storage.set(a,JSON.stringify(c),this.config.auth.magicLinkExpirySeconds);let l=await this.storage.findKeys("magic_link:*");for(let h of l){if(h===a)continue;let y=await this.storage.get(h);if(y)try{JSON.parse(y).email===t&&await this.storage.delete(h)}catch{}}let u=this.generateMagicLinkUrl({token:n,email:t,appUrl:i}),f=Math.ceil(this.config.auth.magicLinkExpirySeconds/60);return await this.email.sendMail({from:this.config.email.user,to:t,subject:o||this.config.email.emailSubject,text:this.templates.getText(u,f,s),html:this.templates.getHtml(u,f,s)}),{message:Ae.linkSent}}catch(n){throw console.error(`Failed to process magic link request: ${n}`),new Error("Failed to process magic link request")}}async createToken(e){let{email:t,username:r,role:s,ip:i}=e;if(!Ut(t))throw new Error("Valid email required");try{let o=X.randomBytes(16).toString("hex"),n=this.generateRefreshToken(),a=Date.now(),c={sub:t,username:r,role:s,jti:o,lastLogin:a,metadata:{ip:i||"unknown"},exp:Math.floor(Date.now()/1e3)+3600*24},l=this.jwtService.sign(c,{exp:this.config.auth.jwtExpirySeconds}),u={email:t,username:r,role:s,ipAddress:i||"unknown",tokenId:o,createdAt:a,lastLogin:a};return await this.trackSession(t,n,u),{accessToken:l,refreshToken:n,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(o){throw console.error("Token creation error:",o),new Error("Token creation failed")}}async verifyToken(e){let{token:t,email:r}=e;try{let s=`magic_link:${t}`,i=await this.storage.get(s);if(!i)throw new Error("Invalid or expired token");let o=JSON.parse(i);if(o.email!==r)throw new Error("Email mismatch");let n=o.username,a=o.role;await this.storage.delete(s);let c=X.randomBytes(16).toString("hex"),l=this.generateRefreshToken(),u={sub:r,username:n,role:a,jti:c,lastLogin:o.createdAt,metadata:{ip:o.ipAddress},exp:Math.floor(Date.now()/1e3)+3600*24},f=this.jwtService.sign(u,{exp:this.config.auth.jwtExpirySeconds});return await this.trackSession(r,l,{...o,tokenId:c,createdAt:Date.now()}),{accessToken:f,refreshToken:l,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(s){throw console.error("Token verification error:",s),new Error("Verification failed")}}async refreshAccessToken(e){try{let t=await this.storage.get(`refresh:${e}`);if(!t)throw new Error("Invalid or expired refresh token");let r=JSON.parse(t),s=r.email;if(!s)throw new Error("Invalid refresh token data");let i=r.username,o=r.role,n=X.randomBytes(16).toString("hex"),a={sub:s,username:i,role:o,jti:n,lastLogin:r.lastLogin||r.createdAt,metadata:{ip:r.ipAddress}},c=this.jwtService.sign(a,{exp:this.config.auth.jwtExpirySeconds});return r.lastUsed=Date.now(),await this.storage.set(`refresh:${e}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds),{accessToken:c,refreshToken:e,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(t){throw console.error("Token refresh error:",t),new Error("Token refresh failed")}}verify(e){try{return this.jwtService.verify(e)}catch{throw new Error("Invalid token")}}async logout(e){try{if(!e||typeof e!="string")throw new Error("Refresh token is required");let t=await this.storage.get(`refresh:${e}`);if(!t)return{message:Ae.logoutSuccess};let r=JSON.parse(t).email;if(!r)throw new Error("Invalid refresh token data");await this.storage.delete(`refresh:${e}`);let s=`sessions:${r}`;return await this.storage.removeFromCollection(s,e),{message:Ae.logoutSuccess}}catch(t){throw console.error("Logout error:",t),new Error("Logout failed")}}async getSessions(e){try{if(!e.user?.email)throw new Error("User not authenticated");let t=e.user.email,r=e.body?.refreshToken,s=`sessions:${t}`,i=(await this.storage.getCollection(s)).map(async n=>{try{let a=await this.storage.get(`refresh:${n}`);if(!a)return await this.storage.removeFromCollection(s,n),null;let c=JSON.parse(a);return{id:`${n.substring(0,8)}...`,createdAt:c.createdAt||0,lastLogin:c.lastLogin||c.createdAt||0,lastUsed:c.lastUsed||c.createdAt||0,metadata:{ip:c.ipAddress},isCurrentSession:n===r}}catch{return await this.storage.removeFromCollection(s,n),null}}),o=(await Promise.all(i)).filter(Boolean);return o.sort((n,a)=>a.createdAt-n.createdAt),{sessions:o}}catch(t){throw console.error("Get sessions error:",t),new Error("Failed to fetch sessions")}}async revokeSessions(e){try{if(!e.user?.email)throw new Error("User not authenticated");let t=e.user.email,r=e.body?.refreshToken,s=`sessions:${t}`,i=await this.storage.getCollection(s);for(let o of i)r&&o===r||await this.storage.delete(`refresh:${o}`);return await this.storage.delete(s),r&&await this.storage.get(`refresh:${r}`)&&await this.storage.addToCollection(s,r,this.config.auth.refreshTokenExpirySeconds),{message:Ae.revokedSuccess}}catch(t){throw console.error("Revoke sessions error:",t),new Error("Failed to revoke sessions")}}authenticate(e,t){try{let r=e.headers?.authorization;if(!r||!r.startsWith("Bearer "))throw new Error("Authentication required");let s=r.split(" ")[1];try{let i=this.verify(s);e.user={email:i.sub},t()}catch{throw new Error("Invalid or expired token")}}catch(r){t(r)}}},Ht=class{db;PREFIX_KV="kv:";PREFIX_COLLECTION="coll:";TABLE_NAME="mikroauth";constructor(e){this.db=e}async start(){await this.db.start()}async close(){await this.db.close()}async set(e,t,r){let s=`${this.PREFIX_KV}${e}`,i=r?Date.now()+r*1e3:void 0;await this.db.write({tableName:this.TABLE_NAME,key:s,value:t,expiration:i})}async get(e){let t=`${this.PREFIX_KV}${e}`;return await this.db.get({tableName:this.TABLE_NAME,key:t})||null}async delete(e){let t=`${this.PREFIX_KV}${e}`;await this.db.delete({tableName:this.TABLE_NAME,key:t})}async addToCollection(e,t,r){let s=`${this.PREFIX_COLLECTION}${e}`,i=await this.db.get({tableName:this.TABLE_NAME,key:s}),o=[];i&&(o=JSON.parse(i)),o.includes(t)||o.push(t);let n=r?Date.now()+r*1e3:void 0;await this.db.write({tableName:this.TABLE_NAME,key:s,value:JSON.stringify(o),expiration:n})}async removeFromCollection(e,t){let r=`${this.PREFIX_COLLECTION}${e}`,s=await this.db.get({tableName:this.TABLE_NAME,key:r});if(!s)return;let i=JSON.parse(s);i=i.filter(o=>o!==t),await this.db.write({tableName:this.TABLE_NAME,key:r,value:JSON.stringify(i)})}async getCollection(e){let t=`${this.PREFIX_COLLECTION}${e}`,r=await this.db.get({tableName:this.TABLE_NAME,key:t});return r?JSON.parse(r):[]}async getCollectionSize(e){return(await this.getCollection(e)).length}async removeOldestFromCollection(e){let t=`${this.PREFIX_COLLECTION}${e}`,r=await this.db.get({tableName:this.TABLE_NAME,key:t});if(!r)return null;let s=JSON.parse(r);if(s.length===0)return null;let i=s.shift();return await this.db.write({tableName:this.TABLE_NAME,key:t,value:JSON.stringify(s)}),i}async findKeys(e){let t=e.replace(/\./g,"\\.").replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${t}$`);return(await this.db.get({tableName:this.TABLE_NAME})).filter(s=>{let i=s[0];return typeof i=="string"&&i.startsWith(this.PREFIX_KV)}).map(s=>s[0].substring(this.PREFIX_KV.length)).filter(s=>r.test(s))}},Ft=class{email;sender;constructor(e){this.sender=e.user,this.email=new st({config:e})}async sendMail(e){await this.email.send({from:this.sender,to:e.to,cc:e.cc,bcc:e.bcc,subject:e.subject,text:e.text,html:e.html})}};var ke=class extends Error{constructor(e){super(),this.name="NotFoundError",this.message=e||"Resource not found",this.cause={statusCode:404}}},pe=class extends Error{constructor(e){super(),this.name="CheckpointError",this.message=e,this.cause={statusCode:500}}};var L=()=>Date.now(),Vt=(e,t)=>t==="D"||e.length===0||e.join(" ")==="null"?null:Nt(e),Nt=e=>{try{return JSON.parse(e.join(" "))}catch{return}};function ot(e){return e==="true"||e===!0}function _t(e){let t=(n,a)=>{if(n){if(a==="json")return Nt(n)||n;if(a==="number")return Number.parseInt(n,10)}},r=t(e?.filter,"json"),s=t(e?.sort,"json"),i=t(e?.limit,"number"),o=t(e?.offset,"number");if(!(!r&&!s&&!i&&!o))return{filter:r,sort:s,limit:i,offset:o}}import{existsSync as nt}from"fs";import{readFile as Wt,unlink as ui,writeFile as qt}from"fs/promises";var zt=class{checkpointInterval;defaultCheckpointIntervalMs=10*1e3;isCheckpointing=!1;lastCheckpointTime=0;walFile;checkpointTimer=null;wal;table;constructor(e){let{table:t,wal:r,walFile:s,checkpointIntervalMs:i}=e;this.defaultCheckpointIntervalMs=i||this.defaultCheckpointIntervalMs,this.table=t,this.wal=r,this.walFile=s,this.checkpointInterval=i,this.lastCheckpointTime=L()}async start(){let e=`${this.walFile}.checkpoint`;if(nt(e)){console.log("Incomplete checkpoint detected, running recovery...");try{let t=await Wt(e,"utf8");console.log(`Incomplete checkpoint from: ${new Date(Number.parseInt(t))}`),await this.checkpoint(!0)}catch(t){throw new ke(`Error reading checkpoint file: ${t}`)}}this.checkpointTimer=setInterval(async()=>{try{await this.checkpoint()}catch(t){throw new pe(`Checkpoint interval failed: ${t}`)}},this.checkpointInterval)}stop(){this.checkpointTimer&&(clearInterval(this.checkpointTimer),this.checkpointTimer=null),this.isCheckpointing=!1}async checkpoint(e=!1){if(this.isCheckpointing){process.env.DEBUG==="true"&&console.log("Checkpoint already in progress, skipping");return}let t=L();if(!e&&t-this.lastCheckpointTime<this.checkpointInterval)return;this.isCheckpointing=!0;let r=`${this.walFile}.checkpoint`;try{if(nt(r)){process.env.DEBUG==="true"&&console.log("Checkpoint file exists, another process is checkpointing");return}await this.wal.flushWAL(),await qt(r,t.toString(),"utf8");let s=await this.getTablesFromWAL();await this.persistTables(s),await qt(this.walFile,"","utf8"),process.env.DEBUG==="true"&&console.log("WAL truncated successfully");try{await ui(r)}catch(i){if(i.code!=="ENOENT")throw new pe(`Failed to delete checkpoint file: ${i}`)}this.wal.clearPositions(),this.lastCheckpointTime=t,process.env.DEBUG==="true"&&console.log("Checkpoint complete")}catch(s){throw new pe(`Checkpoint failed: ${s}`)}finally{this.isCheckpointing=!1}}async getTablesFromWAL(){let e=new Set;if(!nt(this.walFile))return e;try{let t=await Wt(this.walFile,"utf8");if(!t.trim())return e;let r=t.trim().split(`
|
|
112
|
-
|
|
113
|
-
`),
|
|
114
|
-
`),s=this.lastProcessedEntryCount.get(e)||0;if(s>=r.length)return!1;for(let i=s;i<r.length;i++){let o=r[i];if(!o.trim())continue;let n=o.split(" ");if(n.length>=3&&n[2]===e)return!0}return!1}catch(t){return console.error(`Error checking WAL for ${e}:`,t),!0}}async flushWAL(){if(this.checkWalFileExists(),this.walBuffer.length===0)return;let e=[...this.walBuffer];this.walBuffer=[];try{await hi(this.walFile,e.join(""),"utf8");let t=Kt(this.walFile);t.size>this.maxWalSizeBeforeCheckpoint&&(process.env.DEBUG==="true"&&console.log(`WAL size (${t.size}) exceeds limit (${this.maxWalSizeBeforeCheckpoint}), triggering checkpoint`),this.checkpointCallback&&setImmediate(async()=>{try{await this.checkpointCallback()}catch(r){console.error("Error during automatic checkpoint:",r)}}))}catch(t){throw console.error(`Failed to flush WAL: ${t.message}`),this.walBuffer=[...e,...this.walBuffer],t}}async appendToWAL(e,t,r,s,i,o=0){this.checkWalFileExists();let a=`${L()} ${t} ${e} v:${i} x:${o} ${r} ${JSON.stringify(s)}
|
|
115
|
-
`;this.walBuffer.push(a),this.walBuffer.length>=this.maxWalBufferEntries&&await this.flushWAL(),this.walBuffer.reduce((l,u)=>l+u.length,0)>=this.maxWalBufferSize&&await this.flushWAL()}clearPositions(){this.lastProcessedEntryCount.clear()}};var se=()=>({db:{dbName:"mikrodb",databaseDirectory:"mikrodb",walFileName:"wal.log",walInterval:2e3,encryptionKey:"",maxWriteOpsBeforeFlush:100,debug:ot(process.env.DEBUG)||!1},events:{},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"],debug:ot(process.env.DEBUG)||!1}});import{createCipheriv as fi,createDecipheriv as pi,randomBytes as mi,scryptSync as gi}from"crypto";var Te=class{algo="aes-256-gcm";KEY_LENGTH=32;IV_LENGTH=12;generateKey(e,t){return gi(`${t}#${e}`,t,this.KEY_LENGTH)}generateIV(){return mi(this.IV_LENGTH)}encrypt(e,t,r){if(t.length!==this.KEY_LENGTH)throw new Error(`Invalid key length: ${t.length} bytes. Expected: ${this.KEY_LENGTH} bytes`);if(r.length!==this.IV_LENGTH)throw new Error(`Invalid IV length: ${r.length} bytes. Expected: ${this.IV_LENGTH} bytes`);let s=fi(this.algo,t,r),i=Buffer.concat([s.update(e,"utf8"),s.final()]),o=s.getAuthTag();return{iv:r,encrypted:i,authTag:o}}decrypt(e,t){let{iv:r,encrypted:s,authTag:i}=e;if(t.length!==this.KEY_LENGTH)throw new Error(`Invalid key length: ${t.length} bytes. Expected: ${this.KEY_LENGTH} bytes`);if(r.length!==this.IV_LENGTH)throw new Error(`Invalid IV length: ${r.length} bytes. Expected: ${this.IV_LENGTH} bytes`);let o=pi(this.algo,t,r);return o.setAuthTag(i),Buffer.concat([o.update(s),o.final()]).toString("utf8")}serialize(e){return Buffer.concat([Buffer.from([1]),Buffer.from([e.iv.length]),e.iv,Buffer.from([e.authTag.length]),e.authTag,e.encrypted])}deserialize(e){let t=0,r=e[t++];if(r!==1)throw new Error(`Unsupported encryption format version: ${r}`);let s=e[t++],i=e.subarray(t,t+s);t+=s;let o=e[t++],n=e.subarray(t,t+o);t+=o;let a=e.subarray(t);return{iv:i,authTag:n,encrypted:a}}toHex(e){return e.toString("hex")}toUtf8(e){return e.toString("utf8")}toBuffer(e){return Buffer.from(e,"hex")}};var Re=class{readTableFromBinaryBuffer(e){if(e.length<8||e[0]!==77||e[1]!==68||e[2]!==66||e[3]!==1)throw new Error("Invalid table file format");let t=e.readUInt32LE(4),r=new Map,s=8,i=L();for(let o=0;o<t&&s+26<=e.length;o++){let n=e.readUInt16LE(s);s+=2;let a=e.readUInt32LE(s);s+=4;let c=e.readUInt32LE(s);s+=4;let l=Number(e.readBigUInt64LE(s));s+=8;let u=Number(e.readBigUInt64LE(s));if(s+=8,s+n+a>e.length)break;if(u&&u<=i){s+=n+a;continue}let f=e.toString("utf8",s,s+n);s+=n;let h=e.slice(s,s+a);s+=a;let y=this.decodeValue(h);r.set(f,{value:y,version:c,timestamp:l,expiration:u||null})}return r}toBinaryBuffer(e){let t=[],r=Buffer.from([77,68,66,1]);t.push(r);let s=Array.from(e.entries()).filter(([o])=>typeof o=="string"),i=Buffer.alloc(4);i.writeUInt32LE(s.length,0),t.push(i);for(let[o,n]of s){if(o===null||typeof o!="string")continue;let a=Buffer.from(o),c=this.encodeValue(n.value),l=Buffer.alloc(2);l.writeUInt16LE(a.length,0),t.push(l);let u=Buffer.alloc(4);u.writeUInt32LE(c.length,0),t.push(u);let f=Buffer.alloc(4);f.writeUInt32LE(n.version||0,0),t.push(f);let h=Buffer.alloc(8);h.writeBigUInt64LE(BigInt(n.timestamp||0),0),t.push(h);let y=Buffer.alloc(8);y.writeBigUInt64LE(BigInt(n.expiration||0),0),t.push(y),t.push(a),t.push(c)}return Buffer.concat(t)}encodeValue(e){if(e==null)return Buffer.from([0]);if(typeof e=="boolean")return Buffer.from([1,e?1:0]);if(typeof e=="number"){if(Number.isInteger(e)&&e>=-2147483648&&e<=2147483647){let r=Buffer.alloc(5);return r[0]=2,r.writeInt32LE(e,1),r}let t=Buffer.alloc(9);return t[0]=3,t.writeDoubleLE(e,1),t}if(typeof e=="string"){let t=Buffer.from(e,"utf8"),r=Buffer.alloc(5+t.length);return r[0]=4,r.writeUInt32LE(t.length,1),t.copy(r,5),r}if(Array.isArray(e)){let t=[],r=Buffer.alloc(5);r[0]=5,r.writeUInt32LE(e.length,1),t.push(r);for(let s of e)t.push(this.encodeValue(s));return Buffer.concat(t)}if(typeof e=="object"){if(e instanceof Date){let i=Buffer.alloc(9);return i[0]=7,i.writeBigInt64LE(BigInt(e.getTime()),1),i}let t=Object.keys(e),r=[],s=Buffer.alloc(5);s[0]=6,s.writeUInt32LE(t.length,1),r.push(s);for(let i of t){let o=Buffer.from(i,"utf8"),n=Buffer.alloc(2);n.writeUInt16LE(o.length,0),r.push(n),r.push(o),r.push(this.encodeValue(e[i]))}return Buffer.concat(r)}return this.encodeValue(String(e))}decodeValue(e){if(e.length===0)return null;let t=e[0];switch(t){case 0:return null;case 1:return e[1]===1;case 2:return e.readInt32LE(1);case 3:return e.readDoubleLE(1);case 4:{let r=e.readUInt32LE(1);return e.toString("utf8",5,5+r)}case 5:{let r=e.readUInt32LE(1),s=new Array(r),i=5;for(let o=0;o<r;o++){let{value:n,bytesRead:a}=this.decodeValueWithSize(e,i);s[o]=n,i+=a}return s}case 6:{let r=e.readUInt32LE(1),s={},i=5;for(let o=0;o<r;o++){let n=e.readUInt16LE(i);i+=2;let a=e.toString("utf8",i,i+n);i+=n;let{value:c,bytesRead:l}=this.decodeValueWithSize(e,i);s[a]=c,i+=l}return s}case 7:return new Date(Number(e.readBigInt64LE(1)));default:return console.warn(`Unknown type byte: ${t}`),null}}decodeValueWithSize(e,t=0){if(t>=e.length)return{value:null,bytesRead:0};let r=e[t];switch(r){case 0:return{value:null,bytesRead:1};case 1:return{value:e[t+1]===1,bytesRead:2};case 2:return{value:e.readInt32LE(t+1),bytesRead:5};case 3:return{value:e.readDoubleLE(t+1),bytesRead:9};case 4:{let s=e.readUInt32LE(t+1);return{value:e.toString("utf8",t+5,t+5+s),bytesRead:5+s}}case 5:{let s=e.readUInt32LE(t+1),i=new Array(s),o=t+5;for(let n=0;n<s;n++){let a=this.decodeValueWithSize(e,o);i[n]=a.value,o+=a.bytesRead}return{value:i,bytesRead:o-t}}case 6:{let s=e.readUInt32LE(t+1),i={},o=t+5;for(let n=0;n<s;n++){let a=e.readUInt16LE(o);o+=2;let c=e.toString("utf8",o,o+a);o+=a;let l=this.decodeValueWithSize(e,o);i[c]=l.value,o+=l.bytesRead}return{value:i,bytesRead:o-t}}case 7:return{value:new Date(Number(e.readBigInt64LE(t+1))),bytesRead:9};default:return console.warn(`Unknown type byte: ${r} at offset ${t}`),{value:null,bytesRead:1}}}};import{writeFile as yi,rename as vi,unlink as wi}from"fs/promises";async function Yt(e,t,r){let s=new Te,o=new Re().toBinaryBuffer(t);if(!o){console.log("Buffer is empty, skipping...");return}if(r)try{let l=s.generateKey(r,"salt"),u=o.toString("binary"),f=s.generateIV(),h=s.encrypt(u,l,f);o=s.serialize(h)}catch(l){console.error("Encryption failed:",l)}let n=Date.now(),a=Math.random().toString(36).substring(7),c=`${e}.tmp.${n}.${a}`;try{await yi(c,o),await vi(c,e)}catch(l){try{await wi(c)}catch{}throw l}}var Zt=class{cacheLimit;tableAccessTimes=new Map;constructor(e={}){this.cacheLimit=e.cacheLimit??20}trackTableAccess(e){this.tableAccessTimes.set(e,L())}findTablesForEviction(e){if(e<=this.cacheLimit)return[];let t=e-this.cacheLimit,r=Array.from(this.tableAccessTimes.entries()).sort((s,i)=>s[1]-i[1]).slice(0,t).map(([s])=>s);for(let s of r)this.tableAccessTimes.delete(s);return r}removeTable(e){this.tableAccessTimes.delete(e)}findExpiredItems(e){let t=L(),r=[];for(let[s,i]of e.entries())i.x&&i.x<t&&r.push([s,i]);return r}clear(){this.tableAccessTimes.clear()}};var Qt=class{async query(e,t,r=50){let s=new Map;for(let[i,o]of e.entries())if((!t||(typeof t=="function"?t(o.value):this.evaluateFilter(o.value,t)))&&(s.set(i,o.value),r&&s.size>=r))break;return Array.from(s.values())}evaluateCondition(e,t){if(!t||typeof t!="object"||!t.operator)return e===t;let{operator:r,value:s}=t;switch(r){case"eq":return e===s;case"neq":return e!==s;case"gt":return e>s;case"gte":return e>=s;case"lt":return e<s;case"lte":return e<=s;case"in":return Array.isArray(s)&&s.includes(e);case"nin":return Array.isArray(s)&&!s.includes(e);case"like":return typeof e=="string"&&typeof s=="string"&&e.toLowerCase().includes(s.toLowerCase());case"between":return Array.isArray(s)&&s.length===2&&e>=s[0]&&e<=s[1];case"regex":try{let i=new RegExp(s);return typeof e=="string"&&i.test(e)}catch(i){return console.error("Invalid regex pattern:",i),!1}case"contains":return Array.isArray(e)&&e.includes(s);case"containsAll":return Array.isArray(e)&&Array.isArray(s)&&s.every(i=>e.includes(i));case"containsAny":return Array.isArray(e)&&Array.isArray(s)&&s.some(i=>e.includes(i));case"size":return Array.isArray(e)&&e.length===s;default:return!1}}evaluateFilter(e,t){if(e==null)return!1;if("$or"in t)return t.$or.some(r=>this.evaluateFilter(e,r));for(let[r,s]of Object.entries(t))if(!r.startsWith("$"))if(r.includes(".")){let i=r.split("."),o=e;for(let n of i)if(o=o?.[n],o==null)return!1;if(!this.evaluateCondition(o,s))return!1}else if(s&&typeof s=="object"&&!("operator"in s)){let i=e[r];if(i==null||!this.evaluateFilter(i,s))return!1}else{let i=e[r];if(!this.evaluateCondition(i,s))return!1}return!0}};import{existsSync as er,mkdirSync as bi}from"fs";import{readFile as Ei,writeFile as Ci}from"fs/promises";import{join as ct}from"path";import{EventEmitter as Si}from"events";var at=class{emitter;targets={};options;constructor(e){this.emitter=new Si,e?.maxListeners===0?this.emitter.setMaxListeners(0):this.emitter.setMaxListeners(e?.maxListeners||10),this.options={errorHandler:e?.errorHandler||(t=>console.error(t)),isSilent:e?.isSilent||!1}}addTarget(e){return(Array.isArray(e)?e:[e]).map(s=>this.targets[s.name]?(this.options.isSilent||console.error(`Target with name '${s.name}' already exists.`),!1):(this.targets[s.name]={name:s.name,url:s.url,headers:s.headers||{},events:s.events||[]},!0)).every(s=>s===!0)}updateTarget(e,t){if(!this.targets[e])return console.error(`Target with name '${e}' does not exist.`),!1;let r=this.targets[e];return t.url!==void 0&&(r.url=t.url),t.headers&&(r.headers={...r.headers,...t.headers}),t.events&&(r.events=t.events),!0}removeTarget(e){return this.targets[e]?(delete this.targets[e],!0):(console.error(`Target with name '${e}' does not exist.`),!1)}addEventToTarget(e,t){if(!this.targets[e])return console.error(`Target with name '${e}' does not exist.`),!1;let r=Array.isArray(t)?t:[t],s=this.targets[e];return r.forEach(i=>{s.events.includes(i)||s.events.push(i)}),!0}on(e,t){return this.emitter.on(e,t),this}off(e,t){return this.emitter.off(e,t),this}once(e,t){return this.emitter.once(e,t),this}async emit(e,t){let r={success:!0,errors:[]},s=(n,a,c)=>({target:n,event:a,error:c}),i=Object.values(this.targets).filter(n=>n.events.includes(e)||n.events.includes("*"));i.filter(n=>!n.url).forEach(n=>{try{this.emitter.emit(e,t)}catch(a){let c=a instanceof Error?a:new Error(String(a));r.errors.push({target:n.name,event:e,error:c}),this.options.errorHandler(c,e,t),r.success=!1}});let o=i.filter(n=>n.url);if(o.length>0){let n=o.map(async a=>{try{let c=await fetch(a.url,{method:"POST",headers:{"Content-Type":"application/json",...a.headers},body:JSON.stringify({eventName:e,data:t})});if(!c.ok){let l=`HTTP error! Status: ${c.status}: ${c.statusText}`,u=new Error(l);r.errors.push(s(a.name,e,u)),this.options.errorHandler(u,e,t),r.success=!1}}catch(c){let l=c instanceof Error?c:new Error(String(c));r.errors.push(s(a.name,e,l)),this.options.errorHandler(l,e,t),r.success=!1}});await Promise.allSettled(n)}return r}async handleIncomingEvent(e){try{let{eventName:t,data:r}=typeof e=="string"?JSON.parse(e):e;process.nextTick(()=>{try{this.emitter.emit(t,r)}catch(s){this.options.errorHandler(s instanceof Error?s:new Error(String(s)),t,r)}})}catch(t){throw this.options.errorHandler(t instanceof Error?t:new Error(String(t)),"parse_event"),t}}createMiddleware(){return async(e,t,r)=>{if(e.method!=="POST"){r&&r();return}if(e.body)try{await this.handleIncomingEvent(e.body),t.statusCode=202,t.end()}catch(s){t.statusCode=400,t.end(JSON.stringify({error:"Invalid event format"})),r&&r(s)}else{let s="";e.on("data",i=>s+=i.toString()),e.on("end",async()=>{try{await this.handleIncomingEvent(s),t.statusCode=202,t.end()}catch(i){t.statusCode=400,t.end(JSON.stringify({error:"Invalid event format"})),r&&r(i)}})}}}};var tr=class{databaseDirectory;walFile;activeTable=null;data=new Map;writeBuffer=[];maxWriteOpsBeforeFlush=process.env.MAX_WRITE_OPS_BEFORE_FLUSH?Number.parseInt(process.env.MAX_WRITE_OPS_BEFORE_FLUSH,10):se().db.maxWriteOpsBeforeFlush;encryptionKey;cache;encryption;persistence;query;wal;mikroEvent;constructor(e,t){let{databaseDirectory:r,walFileName:s,walInterval:i}=e;this.databaseDirectory=r,this.walFile=ct(this.databaseDirectory,s),this.encryptionKey=e.encryptionKey?e.encryptionKey:null,er(this.databaseDirectory)||bi(this.databaseDirectory),this.cache=new Zt,this.encryption=new Te,this.persistence=new Re,this.query=new Qt,this.wal=new Xt(this.walFile,i),this.mikroEvent=new at,this.setupEvents(t)}async start(){await this.applyWALEntries()}setupEvents(e){e?.targets?.forEach(t=>{this.mikroEvent.addTarget({name:t.name,url:t.url,headers:t.headers,events:t.events})}),e?.listeners?.map(t=>this.mikroEvent.on(t.event,t.handler))}async setActiveTable(e){this.activeTable!==e&&(this.hasTable(e)||await this.loadTable(e),await this.applyWALEntries(e),await this.evictTablesIfNeeded(),this.activeTable=e)}async applyWALEntries(e){let t=await this.wal.loadWAL(e);if(t.length===0)return;let r=e?[e]:[...new Set(t.map(s=>s.tableName))];for(let s of r){let i=t.filter(o=>o.tableName===s);e&&!this.hasTable(s)?await this.loadTable(s):this.createTable(s);for(let o of i)o.operation==="W"&&o.data?this.setItem(s,o.key,o.data):o.operation==="D"&&await this.deleteItem(s,o.key)}}async loadTable(e){if(this.hasTable(e))return;let t=ct(this.databaseDirectory,e);if(!er(t)){this.createTable(e);return}let r=await Ei(t),s=r;if(this.encryptionKey&&r.length>0&&r[0]===1)try{let o=this.encryption.deserialize(r),n=this.encryption.generateKey(this.encryptionKey,"salt"),a=this.encryption.decrypt(o,n);s=Buffer.from(a,"binary")}catch(o){console.error(`Failed to decrypt ${e}:`,o)}if(r.length<8){console.warn(`Table file ${e} is too small, recreating...`),this.createTable(e);return}let i=this.persistence.readTableFromBinaryBuffer(s);this.data.set(e,i),this.data.size>this.cache.cacheLimit&&setImmediate(()=>this.evictTablesIfNeeded())}async get(e){let{tableName:t}=e,r=e.key,s=e.options;if(await this.setActiveTable(t),!s)return r?this.getItem(t,r)?.value:[...this.getAll(t)];let i=this.getTable(t),o=await this.query.query(i,s.filter,s.limit);if(s.sort&&(o=o.sort(s.sort)),s.offset!=null||s.limit!=null){let n=s.offset||0,a=s.limit?n+s.limit:void 0;o=o.slice(n,a)}return o}async write(e,t={}){let{concurrencyLimit:r=10,flushImmediately:s=!1}=t,i=Array.isArray(e)?e:[e],o=i.length,n=0;for(;n<o;){let a=i.slice(n,n+r),c=a.map(u=>this.writeItem(u,!1));if((await Promise.all(c)).includes(!1))return!1;n+=a.length,this.writeBuffer.length>=this.maxWriteOpsBeforeFlush&&await this.flushWrites()}return(s||o>=0)&&await this.flush(),!0}async writeItem(e,t=!1){let{tableName:r,key:s,value:i,expectedVersion:o=null,expiration:n=0}=e;await this.setActiveTable(r);let{success:a,newVersion:c}=this.getItemVersion(r,s,o);return a?(await this.wal.appendToWAL(r,"W",s,i,c,n),this.setItem(r,s,{value:i,v:c,t:L(),x:n}),this.addToWriteBuffer(r,s),t&&await this.flush(),!0):!1}async delete(e,t,r=null,s=!1){if(await this.setActiveTable(e),!this.hasTable(e)||!this.hasKey(e,t))return console.log(`Key ${t} not found in table ${e}`),!1;let{success:i,currentVersion:o,expiration:n}=this.getItemVersion(e,t,r);return i?(await this.wal.appendToWAL(e,"D",t,null,o,n),await this.deleteItem(e,t),s&&await this.flush(),!0):!1}getItemVersion(e,t,r){let s=this.getItem(e,t),i=s?s.version:0,o=i+1,n=s&&s.expiration||0,a=!0;return r!==null&&i!==r&&(console.log(`Version mismatch for ${e}:${t}. Expected ${r}, found ${i}`),a=!1),{success:a,currentRecord:s,currentVersion:i,newVersion:o,expiration:n}}createTable(e){this.trackTableAccess(e),this.hasTable(e)||this.data.set(e,new Map)}getTable(e){return this.trackTableAccess(e),this.hasTable(e)?this.data.get(e):new Map}async getTableSize(e){return await this.setActiveTable(e),this.trackTableAccess(e),this.data.get(e)?.size}async deleteTable(e){this.trackTableAccess(e),this.data.delete(e);let t="table.deleted",{success:r,errors:s}=await this.mikroEvent.emit(t,{operation:t,table:e});r||console.error("Error when emitting events:",s)}hasTable(e){return this.trackTableAccess(e),this.data.has(e)}hasKey(e,t){return this.trackTableAccess(e),this.data.get(e)?.has(t)}getItem(e,t){this.trackTableAccess(e);let r=this.data.get(e)?.get(t);if(r){if(r?.x!==0&&Date.now()>r?.x){this.deleteItem(e,t);return}return{value:r.value,version:r.v,timestamp:r.t,expiration:r.x}}}getAll(e){this.trackTableAccess(e);let t=this.data.get(e);return t?Array.from(t):[]}setItem(e,t,r){this.trackTableAccess(e),this.createTable(e),this.data.get(e)?.set(t,r)}async deleteItem(e,t){this.data.get(e)?.delete(t);let r="item.deleted",{success:s,errors:i}=await this.mikroEvent.emit(r,{operation:r,table:e,key:t});s||console.error("Error when emitting events:",i)}addToWriteBuffer(e,t){let r=this.getItem(e,t);this.writeBuffer.push(JSON.stringify({tableName:e,key:t,record:r}))}trackTableAccess(e){this.cache.trackTableAccess(e)}async flush(){await this.flushWAL(),await this.flushWrites()}async flushWAL(){await this.wal.flushWAL()}async flushWrites(){if(this.writeBuffer.length!==0)try{let e=new Map,t=[...this.writeBuffer];for(let s of t){let i=JSON.parse(s);e.has(i.tableName)||e.set(i.tableName,new Map),e.get(i.tableName).set(i.key,i.record);let n="item.written",{success:a,errors:c}=await this.mikroEvent.emit(n,{operation:n,table:i.tableName,key:i.key,record:i.record});a||console.error("Error when emitting events:",c)}let r=Array.from(e.entries()).map(async([s])=>{let i=this.getTable(s),o=ct(this.databaseDirectory,s);await Yt(o,i,this.encryptionKey)});await Promise.all(r),this.writeBuffer=this.writeBuffer.slice(t.length)}catch(e){console.error(`Failed to flush writes: ${e.message}`)}}async flushTableToDisk(e){await this.setActiveTable(e);let t=this.getTable(e);if(t.size!==0){for(let[r,s]of t.entries())this.addToWriteBuffer(e,r);await this.flushWrites()}}async evictTablesIfNeeded(){let e=this.cache.findTablesForEviction(this.data.size);for(let t of e)await this.flushTableToDisk(t),this.data.delete(t)}async cleanupExpiredItems(){for(let[e,t]of this.data.entries()){let r=this.cache.findExpiredItems(t);for(let[s,i]of r){await this.wal.appendToWAL(e,"D",s,null,i.v,i.x),t.delete(s);let o="item.expired",{success:n,errors:a}=await this.mikroEvent.emit(o,{operation:o,table:e,key:s,record:i});n||console.error("Error when emitting events:",a)}}}async dump(e){e&&await this.setActiveTable(e);let t=this.getAll(this.activeTable);await Ci(`${this.databaseDirectory}/${this.activeTable}_dump.json`,JSON.stringify(t),"utf8")}getWAL(){return this.wal}getPersistence(){return this.persistence}};import{join as xi}from"path";var me=class{table;checkpoint;constructor(e){let t=se(),r=e?.databaseDirectory||t.db.databaseDirectory,s=e?.walFileName||t.db.walFileName,i=e?.walInterval||t.db.walInterval,o=e?.encryptionKey||t.db.encryptionKey,n=e?.maxWriteOpsBeforeFlush||t.db.maxWriteOpsBeforeFlush,a=e?.events||{};e?.debug&&(process.env.DEBUG="true"),e?.maxWriteOpsBeforeFlush&&(process.env.MAX_WRITE_OPS_BEFORE_FLUSH=n.toString()),this.table=new tr({databaseDirectory:r,walFileName:s,walInterval:i,encryptionKey:o},a);let c=this.table.getWAL();this.table.getWAL().setCheckpointCallback(()=>this.checkpoint.checkpoint(!0)),this.checkpoint=new zt({table:this.table,wal:c,walFile:xi(r,s),checkpointIntervalMs:i})}async start(){await this.table.start(),await this.checkpoint.start()}async get(e){return await this.table.get(e)}async getTableSize(e){return await this.table.getTableSize(e)}async write(e,t){return await this.table.write(e,t)}async delete(e){let{tableName:t,key:r}=e,s=e?.expectedVersion||null;return await this.table.delete(t,r,s)}async deleteTable(e){return await this.table.deleteTable(e)}async close(){this.checkpoint&&this.checkpoint.stop(),this.table?.getWAL()&&this.table.getWAL().stop();try{await this.flush()}catch(e){console.error("Error flushing during close:",e)}}async flush(){await this.table.flush()}async flushWAL(){await this.table.flushWAL()}async dump(e){await this.table.dump(e)}async cleanupExpiredItems(){await this.table.cleanupExpiredItems()}};var rr=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 Ii}from"node:url";var sr=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 o={};return s.paramNames.forEach((n,a)=>{o[n]=i[a+1]||""}),{route:r,params:o}}return null}async handle(e,t){let r=e.method||"GET",s=new Ii(e.url||"/",`http://${e.headers.host}`),i=s.pathname,o=this.match(r,i);if(!o)return null;let{route:n,params:a}=o,c={};s.searchParams.forEach((f,h)=>{c[h]=f});let l={req:e,res:t,params:a,query:c,body:e.body||{},headers:e.headers,path:i,state:{},raw:()=>t,binary:(f,h="application/octet-stream",y=200)=>({statusCode:y,body:f,headers:{"Content-Type":h,"Content-Length":f.length.toString()},isRaw:!0}),text:(f,h=200)=>({statusCode:h,body:f,headers:{"Content-Type":"text/plain"}}),form:(f,h=200)=>({statusCode:h,body:f,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(f,h=200)=>({statusCode:h,body:f,headers:{"Content-Type":"application/json"}}),html:(f,h=200)=>({statusCode:h,body:f,headers:{"Content-Type":"text/html"}}),redirect:(f,h=302)=>({statusCode:h,body:null,headers:{Location:f}}),status:function(f){return{raw:()=>t,binary:(h,y="application/octet-stream")=>({statusCode:f,body:h,headers:{"Content-Type":y,"Content-Length":h.length.toString()},isRaw:!0}),text:h=>({statusCode:f,body:h,headers:{"Content-Type":"text/plain"}}),json:h=>({statusCode:f,body:h,headers:{"Content-Type":"application/json"}}),html:h=>({statusCode:f,body:h,headers:{"Content-Type":"text/html"}}),form:h=>({statusCode:f,body:h,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(h,y=302)=>({statusCode:y,body:null,headers:{Location:h}}),status:h=>this.status(h)}}},u=[...this.globalMiddlewares,...n.middlewares];return this.executeMiddlewareChain(l,u,n.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 o=t[s++];return o(e,i)}return r(e)};return i()}};var Pe=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:Ai(process.env.DEBUG)||!1,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function Ai(e){return e==="true"||e===!0}var M=Pe(),ir=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:J.array},{flag:"--debug",path:"debug",defaultValue:M.debug,isFlag:!0}],config:e});import{readFileSync as ie}from"node:fs";import lt from"node:http";import ki from"node:http2";import Ti from"node:https";var ut=class{config;rateLimiter;router;constructor(e){let t=new N(ir(e||{})).get();t.debug&&console.log("Using configuration:",t),this.config=t,this.router=new sr;let r=t.rateLimit.requestsPerMinute||Pe().rateLimit.requestsPerMinute;this.rateLimiter=new rr(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:ie(this.config.sslKey),cert:ie(this.config.sslCert),...this.config.sslCa?{ca:ie(this.config.sslCa)}:{}};return ki.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:ie(this.config.sslKey),cert:ie(this.config.sslCert),...this.config.sslCa?{ca:ie(this.config.sslCa)}:{}};return Ti.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 lt.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",o=this.config.debug;try{if(this.setCorsHeaders(t,e),this.setSecurityHeaders(t,this.config.useHttps),o&&console.log(`${s} ${i}`),e.method==="OPTIONS"){if(t instanceof lt.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 o&&console.error("Body parsing error:",a.message),this.respond(t,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let n=await this.router.handle(e,t);return n?n._handled?void 0:this.respond(t,n):this.respond(t,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(n){return console.error("Server error:",n),this.respond(t,{statusCode:500,body:{error:"Internal Server Error",message:o?n.message:"An unexpected error occurred"}})}finally{o&&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,o=1024*1024,n=!1,a=this.config.debug,c=e.headers["content-type"]||"";a&&console.log("Content-Type:",c),e.on("data",l=>{if(i+=l.length,a&&console.log(`Received chunk: ${l.length} bytes, total size: ${i}`),i>o&&!n){n=!0,a&&console.log(`Body size exceeded limit: ${i} > ${o}`),r(new Error("Request body too large"));return}n||s.push(l)}),e.on("end",()=>{if(!n){a&&console.log(`Request body complete: ${i} bytes`);try{if(s.length>0){let l=Buffer.concat(s).toString("utf8");if(c.includes("application/json"))try{t(JSON.parse(l))}catch(u){r(new Error(`Invalid JSON in request body: ${u.message}`))}else if(c.includes("application/x-www-form-urlencoded")){let u={};new URLSearchParams(l).forEach((f,h)=>{u[h]=f}),t(u)}else t(l)}else t({})}catch(l){r(new Error(`Invalid request body: ${l}`))}}}),e.on("error",l=>{n||r(new Error(`Error reading request body: ${l.message}`))})})}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 lt.ServerResponse)Object.entries(r).forEach(([s,i])=>{e.setHeader(s,i)});else{let s=e;Object.entries(r).forEach(([i,o])=>{s.setHeader(i,o)})}}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=r=>{console.log("Shutting down MikroServe server..."),r&&console.error("Error:",r),e.close(()=>{console.log("Server closed successfully"),setImmediate(()=>process.exit(r?1:0))})};process.on("SIGINT",()=>t()),process.on("SIGTERM",()=>t()),process.on("uncaughtException",t),process.on("unhandledRejection",t)}};async function or(e){let t=new me({...e.db,events:e?.events});await t.start();let r=new ut(e.server);return r.get("/table",async s=>{let i=s.req.body;if(!i.tableName)return s.json({statusCode:400,body:"tableName is required"});let o=await t.getTableSize(i.tableName);return o||s.json({statusCode:404,body:null}),s.json({statusCode:200,body:o})}),r.post("/get",async s=>{let i=s.req.body;if(!i.tableName)return s.json({statusCode:400,body:"tableName is required"});let o={tableName:i.tableName,key:i.key},n=_t(i?.options);n&&(o.options=n);let a=await t.get(o);return a||s.json({statusCode:404,body:null}),s.json({statusCode:200,body:a})}),r.post("/write",async s=>{let i=s.req.body;if(!i.tableName||i.value===void 0)return s.json({statusCode:400,body:"tableName and value are required"});let o={tableName:i.tableName,key:i.key,value:i.value,expectedVersion:i.expectedVersion,expiration:i.expiration},n={concurrencyLimit:i.concurrencyLimit,flushImmediately:i.flushImmediately},a=await t.write(o,n);return s.json({statusCode:200,body:a})}),r.delete("/delete",async s=>{let i=s.params;if(!i.tableName||!i.key)return s.json({statusCode:400,body:"tableName and key are required"});let o={tableName:i.tableName,key:i.key},n=await t.delete(o);return s.json({statusCode:200,body:n})}),r.start(),r}async function Ri(){let t=process.argv[1]?.includes("node_modules/.bin/mikrodb"),r=(process.argv[2]||"")==="--force";if(t||r){console.log("\u{1F5C2}\uFE0F Welcome to MikroDB! \u2728");try{let s=se(),i=new N({configFilePath:"mikrodb.config.json",args:process.argv,options:[{flag:"--db",path:"db.dbName",defaultValue:s.db.dbName},{flag:"--dir",path:"db.databaseDirectory",defaultValue:s.db.databaseDirectory},{flag:"--wal",path:"db.walFileName",defaultValue:s.db.walFileName},{flag:"--interval",path:"db.walInterval",defaultValue:s.db.walInterval},{flag:"--encryptionKey",path:"db.encryptionKey",defaultValue:s.db.encryptionKey},{flag:"--maxWrites",path:"db.maxWriteOpsBeforeFlush",defaultValue:s.db.maxWriteOpsBeforeFlush},{flag:"--debug",path:"db.debug",isFlag:!0,defaultValue:s.db.debug},{path:"events",defaultValue:s.events},{flag:"--port",path:"server.port",defaultValue:s.server.port},{flag:"--host",path:"server.host",defaultValue:s.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:s.server.useHttps},{flag:"--http2",path:"server.useHttp2",isFlag:!0,defaultValue:s.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:s.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:s.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:s.server.sslCa},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:s.server.debug}]}).get();or(i)}catch(s){console.error(s)}}}Ri();import{join as Wo}from"node:path";import Do from"node:http";import{join as Rt}from"node:path";var dt=(e,t,r)=>(s,i)=>{let o=-1;return n(0);async function n(a){if(a<=o)throw new Error("next() called multiple times");o=a;let c,l=!1,u;if(e[a]?(u=e[a][0][0],s.req.routeIndex=a):u=a===e.length&&i||void 0,u)try{c=await u(s,()=>n(a+1))}catch(f){if(f instanceof Error&&t)s.error=f,c=await t(f,s),l=!0;else throw f}else s.finalized===!1&&r&&(c=await r(s));return c&&(s.finalized===!1||l)&&(s.res=c),s}};var nr=Symbol();var ar=async(e,t=Object.create(null))=>{let{all:r=!1,dot:s=!1}=t,o=(e instanceof je?e.raw.headers:e.headers).get("Content-Type");return o?.startsWith("multipart/form-data")||o?.startsWith("application/x-www-form-urlencoded")?Pi(e,{all:r,dot:s}):{}};async function Pi(e,t){let r=await e.formData();return r?ji(r,t):{}}function ji(e,t){let r=Object.create(null);return e.forEach((s,i)=>{t.all||i.endsWith("[]")?Li(r,i,s):r[i]=s}),t.dot&&Object.entries(r).forEach(([s,i])=>{s.includes(".")&&($i(r,s,i),delete r[s])}),r}var Li=(e,t,r)=>{e[t]!==void 0?Array.isArray(e[t])?e[t].push(r):e[t]=[e[t],r]:t.endsWith("[]")?e[t]=[r]:e[t]=r},$i=(e,t,r)=>{let s=e,i=t.split(".");i.forEach((o,n)=>{n===i.length-1?s[o]=r:((!s[o]||typeof s[o]!="object"||Array.isArray(s[o])||s[o]instanceof File)&&(s[o]=Object.create(null)),s=s[o])})};var ft=e=>{let t=e.split("/");return t[0]===""&&t.shift(),t},cr=e=>{let{groups:t,path:r}=Mi(e),s=ft(r);return Oi(s,t)},Mi=e=>{let t=[];return e=e.replace(/\{[^}]+\}/g,(r,s)=>{let i=`@${s}`;return t.push([i,r]),i}),{groups:t,path:e}},Oi=(e,t)=>{for(let r=t.length-1;r>=0;r--){let[s]=t[r];for(let i=e.length-1;i>=0;i--)if(e[i].includes(s)){e[i]=e[i].replace(s,t[r][1]);break}}return e},Le={},lr=(e,t)=>{if(e==="*")return"*";let r=e.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);if(r){let s=`${e}#${t}`;return Le[s]||(r[2]?Le[s]=t&&t[0]!==":"&&t[0]!=="*"?[s,r[1],new RegExp(`^${r[2]}(?=/${t})`)]:[e,r[1],new RegExp(`^${r[2]}$`)]:Le[s]=[e,r[1],!0]),Le[s]}return null},$e=(e,t)=>{try{return t(e)}catch{return e.replace(/(?:%[0-9A-Fa-f]{2})+/g,r=>{try{return t(r)}catch{return r}})}},Di=e=>$e(e,decodeURI),pt=e=>{let t=e.url,r=t.indexOf("/",t.indexOf(":")+4),s=r;for(;s<t.length;s++){let i=t.charCodeAt(s);if(i===37){let o=t.indexOf("?",s),n=t.slice(r,o===-1?void 0:o);return Di(n.includes("%25")?n.replace(/%25/g,"%2525"):n)}else if(i===63)break}return t.slice(r,s)};var ur=e=>{let t=pt(e);return t.length>1&&t.at(-1)==="/"?t.slice(0,-1):t},Y=(e,t,...r)=>(r.length&&(t=Y(t,...r)),`${e?.[0]==="/"?"":"/"}${e}${t==="/"?"":`${e?.at(-1)==="/"?"":"/"}${t?.[0]==="/"?t.slice(1):t}`}`),Me=e=>{if(e.charCodeAt(e.length-1)!==63||!e.includes(":"))return null;let t=e.split("/"),r=[],s="";return t.forEach(i=>{if(i!==""&&!/\:/.test(i))s+="/"+i;else if(/\:/.test(i))if(/\?/.test(i)){r.length===0&&s===""?r.push("/"):r.push(s);let o=i.replace("?","");s+="/"+o,r.push(s)}else s+="/"+i}),r.filter((i,o,n)=>n.indexOf(i)===o)},ht=e=>/[%+]/.test(e)?(e.indexOf("+")!==-1&&(e=e.replace(/\+/g," ")),e.indexOf("%")!==-1?$e(e,mt):e):e,dr=(e,t,r)=>{let s;if(!r&&t&&!/[%+]/.test(t)){let n=e.indexOf("?",8);if(n===-1)return;for(e.startsWith(t,n+1)||(n=e.indexOf(`&${t}`,n+1));n!==-1;){let a=e.charCodeAt(n+t.length+1);if(a===61){let c=n+t.length+2,l=e.indexOf("&",c);return ht(e.slice(c,l===-1?void 0:l))}else if(a==38||isNaN(a))return"";n=e.indexOf(`&${t}`,n+1)}if(s=/[%+]/.test(e),!s)return}let i={};s??=/[%+]/.test(e);let o=e.indexOf("?",8);for(;o!==-1;){let n=e.indexOf("&",o+1),a=e.indexOf("=",o);a>n&&n!==-1&&(a=-1);let c=e.slice(o+1,a===-1?n===-1?void 0:n:a);if(s&&(c=ht(c)),o=n,c==="")continue;let l;a===-1?l="":(l=e.slice(a+1,n===-1?void 0:n),s&&(l=ht(l))),r?(i[c]&&Array.isArray(i[c])||(i[c]=[]),i[c].push(l)):i[c]??=l}return t?i[t]:i},hr=dr,fr=(e,t)=>dr(e,t,!0),mt=decodeURIComponent;var pr=e=>$e(e,mt),je=class{raw;#t;#e;routeIndex=0;path;bodyCache={};constructor(e,t="/",r=[[]]){this.raw=e,this.path=t,this.#e=r,this.#t={}}param(e){return e?this.#r(e):this.#o()}#r(e){let t=this.#e[0][this.routeIndex][1][e],r=this.#i(t);return r&&/\%/.test(r)?pr(r):r}#o(){let e={},t=Object.keys(this.#e[0][this.routeIndex][1]);for(let r of t){let s=this.#i(this.#e[0][this.routeIndex][1][r]);s!==void 0&&(e[r]=/\%/.test(s)?pr(s):s)}return e}#i(e){return this.#e[1]?this.#e[1][e]:e}query(e){return hr(this.url,e)}queries(e){return fr(this.url,e)}header(e){if(e)return this.raw.headers.get(e)??void 0;let t={};return this.raw.headers.forEach((r,s)=>{t[s]=r}),t}async parseBody(e){return this.bodyCache.parsedBody??=await ar(this,e)}#s=e=>{let{bodyCache:t,raw:r}=this,s=t[e];if(s)return s;let i=Object.keys(t)[0];return i?t[i].then(o=>(i==="json"&&(o=JSON.stringify(o)),new Response(o)[e]())):t[e]=r[e]()};json(){return this.#s("text").then(e=>JSON.parse(e))}text(){return this.#s("text")}arrayBuffer(){return this.#s("arrayBuffer")}blob(){return this.#s("blob")}formData(){return this.#s("formData")}addValidatedData(e,t){this.#t[e]=t}valid(e){return this.#t[e]}get url(){return this.raw.url}get method(){return this.raw.method}get[nr](){return this.#e}get matchedRoutes(){return this.#e[0].map(([[,e]])=>e)}get routePath(){return this.#e[0].map(([[,e]])=>e)[this.routeIndex].path}};var mr={Stringify:1,BeforeStream:2,Stream:3},Ui=(e,t)=>{let r=new String(e);return r.isEscaped=!0,r.callbacks=t,r};var gt=async(e,t,r,s,i)=>{typeof e=="object"&&!(e instanceof String)&&(e instanceof Promise||(e=e.toString()),e instanceof Promise&&(e=await e));let o=e.callbacks;if(!o?.length)return Promise.resolve(e);i?i[0]+=e:i=[e];let n=Promise.all(o.map(a=>a({phase:t,buffer:i,context:s}))).then(a=>Promise.all(a.filter(Boolean).map(c=>gt(c,t,!1,s,i))).then(()=>i[0]));return r?Ui(await n,o):n};var Bi="text/plain; charset=UTF-8",yt=(e,t)=>({"Content-Type":e,...t}),gr=class{#t;#e;env={};#r;finalized=!1;error;#o;#i;#s;#u;#c;#l;#a;#d;#h;constructor(e,t){this.#t=e,t&&(this.#i=t.executionCtx,this.env=t.env,this.#l=t.notFoundHandler,this.#h=t.path,this.#d=t.matchResult)}get req(){return this.#e??=new je(this.#t,this.#h,this.#d),this.#e}get event(){if(this.#i&&"respondWith"in this.#i)return this.#i;throw Error("This context has no FetchEvent")}get executionCtx(){if(this.#i)return this.#i;throw Error("This context has no ExecutionContext")}get res(){return this.#s||=new Response(null,{headers:this.#a??=new Headers})}set res(e){if(this.#s&&e){e=new Response(e.body,e);for(let[t,r]of this.#s.headers.entries())if(t!=="content-type")if(t==="set-cookie"){let s=this.#s.headers.getSetCookie();e.headers.delete("set-cookie");for(let i of s)e.headers.append("set-cookie",i)}else e.headers.set(t,r)}this.#s=e,this.finalized=!0}render=(...e)=>(this.#c??=t=>this.html(t),this.#c(...e));setLayout=e=>this.#u=e;getLayout=()=>this.#u;setRenderer=e=>{this.#c=e};header=(e,t,r)=>{this.finalized&&(this.#s=new Response(this.#s.body,this.#s));let s=this.#s?this.#s.headers:this.#a??=new Headers;t===void 0?s.delete(e):r?.append?s.append(e,t):s.set(e,t)};status=e=>{this.#o=e};set=(e,t)=>{this.#r??=new Map,this.#r.set(e,t)};get=e=>this.#r?this.#r.get(e):void 0;get var(){return this.#r?Object.fromEntries(this.#r):{}}#n(e,t,r){let s=this.#s?new Headers(this.#s.headers):this.#a??new Headers;if(typeof t=="object"&&"headers"in t){let o=t.headers instanceof Headers?t.headers:new Headers(t.headers);for(let[n,a]of o)n.toLowerCase()==="set-cookie"?s.append(n,a):s.set(n,a)}if(r)for(let[o,n]of Object.entries(r))if(typeof n=="string")s.set(o,n);else{s.delete(o);for(let a of n)s.append(o,a)}let i=typeof t=="number"?t:t?.status??this.#o;return new Response(e,{status:i,headers:s})}newResponse=(...e)=>this.#n(...e);body=(e,t,r)=>this.#n(e,t,r);text=(e,t,r)=>!this.#a&&!this.#o&&!t&&!r&&!this.finalized?new Response(e):this.#n(e,t,yt(Bi,r));json=(e,t,r)=>this.#n(JSON.stringify(e),t,yt("application/json",r));html=(e,t,r)=>{let s=i=>this.#n(i,t,yt("text/html; charset=UTF-8",r));return typeof e=="object"?gt(e,mr.Stringify,!1,{}).then(s):s(e)};redirect=(e,t)=>{let r=String(e);return this.header("Location",/[^\x00-\xFF]/.test(r)?encodeURI(r):r),this.newResponse(null,t??302)};notFound=()=>(this.#l??=()=>new Response,this.#l(this))};var x="ALL",yr="all",vr=["get","post","put","delete","options","patch"],Oe="Can not add a route since the matcher is already built.",De=class extends Error{};var wr="__COMPOSED_HANDLER";var Hi=e=>e.text("404 Not Found",404),Sr=(e,t)=>{if("getResponse"in e){let r=e.getResponse();return t.newResponse(r.body,r)}return console.error(e),t.text("Internal Server Error",500)},br=class Er{get;post;put;delete;options;patch;all;on;use;router;getPath;_basePath="/";#t="/";routes=[];constructor(t={}){[...vr,yr].forEach(o=>{this[o]=(n,...a)=>(typeof n=="string"?this.#t=n:this.#o(o,this.#t,n),a.forEach(c=>{this.#o(o,this.#t,c)}),this)}),this.on=(o,n,...a)=>{for(let c of[n].flat()){this.#t=c;for(let l of[o].flat())a.map(u=>{this.#o(l.toUpperCase(),this.#t,u)})}return this},this.use=(o,...n)=>(typeof o=="string"?this.#t=o:(this.#t="*",n.unshift(o)),n.forEach(a=>{this.#o(x,this.#t,a)}),this);let{strict:s,...i}=t;Object.assign(this,i),this.getPath=s??!0?t.getPath??pt:ur}#e(){let t=new Er({router:this.router,getPath:this.getPath});return t.errorHandler=this.errorHandler,t.#r=this.#r,t.routes=this.routes,t}#r=Hi;errorHandler=Sr;route(t,r){let s=this.basePath(t);return r.routes.map(i=>{let o;r.errorHandler===Sr?o=i.handler:(o=async(n,a)=>(await dt([],r.errorHandler)(n,()=>i.handler(n,a))).res,o[wr]=i.handler),s.#o(i.method,i.path,o)}),this}basePath(t){let r=this.#e();return r._basePath=Y(this._basePath,t),r}onError=t=>(this.errorHandler=t,this);notFound=t=>(this.#r=t,this);mount(t,r,s){let i,o;s&&(typeof s=="function"?o=s:(o=s.optionHandler,s.replaceRequest===!1?i=c=>c:i=s.replaceRequest));let n=o?c=>{let l=o(c);return Array.isArray(l)?l:[l]}:c=>{let l;try{l=c.executionCtx}catch{}return[c.env,l]};i||=(()=>{let c=Y(this._basePath,t),l=c==="/"?0:c.length;return u=>{let f=new URL(u.url);return f.pathname=f.pathname.slice(l)||"/",new Request(f,u)}})();let a=async(c,l)=>{let u=await r(i(c.req.raw),...n(c));if(u)return u;await l()};return this.#o(x,Y(t,"*"),a),this}#o(t,r,s){t=t.toUpperCase(),r=Y(this._basePath,r);let i={basePath:this._basePath,path:r,method:t,handler:s};this.router.add(t,r,[s,i]),this.routes.push(i)}#i(t,r){if(t instanceof Error)return this.errorHandler(t,r);throw t}#s(t,r,s,i){if(i==="HEAD")return(async()=>new Response(null,await this.#s(t,r,s,"GET")))();let o=this.getPath(t,{env:s}),n=this.router.match(i,o),a=new gr(t,{path:o,matchResult:n,env:s,executionCtx:r,notFoundHandler:this.#r});if(n[0].length===1){let l;try{l=n[0][0][0][0](a,async()=>{a.res=await this.#r(a)})}catch(u){return this.#i(u,a)}return l instanceof Promise?l.then(u=>u||(a.finalized?a.res:this.#r(a))).catch(u=>this.#i(u,a)):l??this.#r(a)}let c=dt(n[0],this.errorHandler,this.#r);return(async()=>{try{let l=await c(a);if(!l.finalized)throw new Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return l.res}catch(l){return this.#i(l,a)}})()}fetch=(t,...r)=>this.#s(t,r[1],r[0],t.method);request=(t,r,s,i)=>t instanceof Request?this.fetch(r?new Request(t,r):t,s,i):(t=t.toString(),this.fetch(new Request(/^https?:\/\//.test(t)?t:`http://localhost${Y("/",t)}`,r),s,i));fire=()=>{addEventListener("fetch",t=>{t.respondWith(this.#s(t.request,t,void 0,t.request.method))})}};var Ue=[];function vt(e,t){let r=this.buildAllMatchers(),s=((i,o)=>{let n=r[i]||r[x],a=n[2][o];if(a)return a;let c=o.match(n[0]);if(!c)return[[],Ue];let l=c.indexOf("",1);return[n[1][l],c]});return this.match=s,s(e,t)}var Be="[^/]+",ge=".*",ye="(?:|/.*)",Z=Symbol(),Fi=new Set(".\\+*[^]$()");function Vi(e,t){return e.length===1?t.length===1?e<t?-1:1:-1:t.length===1||e===ge||e===ye?1:t===ge||t===ye?-1:e===Be?1:t===Be?-1:e.length===t.length?e<t?-1:1:t.length-e.length}var Cr=class wt{#t;#e;#r=Object.create(null);insert(t,r,s,i,o){if(t.length===0){if(this.#t!==void 0)throw Z;if(o)return;this.#t=r;return}let[n,...a]=t,c=n==="*"?a.length===0?["","",ge]:["","",Be]:n==="/*"?["","",ye]:n.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/),l;if(c){let u=c[1],f=c[2]||Be;if(u&&c[2]&&(f===".*"||(f=f.replace(/^\((?!\?:)(?=[^)]+\)$)/,"(?:"),/\((?!\?:)/.test(f))))throw Z;if(l=this.#r[f],!l){if(Object.keys(this.#r).some(h=>h!==ge&&h!==ye))throw Z;if(o)return;l=this.#r[f]=new wt,u!==""&&(l.#e=i.varIndex++)}!o&&u!==""&&s.push([u,l.#e])}else if(l=this.#r[n],!l){if(Object.keys(this.#r).some(u=>u.length>1&&u!==ge&&u!==ye))throw Z;if(o)return;l=this.#r[n]=new wt}l.insert(a,r,s,i,o)}buildRegExpStr(){let r=Object.keys(this.#r).sort(Vi).map(s=>{let i=this.#r[s];return(typeof i.#e=="number"?`(${s})@${i.#e}`:Fi.has(s)?`\\${s}`:s)+i.buildRegExpStr()});return typeof this.#t=="number"&&r.unshift(`#${this.#t}`),r.length===0?"":r.length===1?r[0]:"(?:"+r.join("|")+")"}};var xr=class{#t={varIndex:0};#e=new Cr;insert(e,t,r){let s=[],i=[];for(let n=0;;){let a=!1;if(e=e.replace(/\{[^}]+\}/g,c=>{let l=`@\\${n}`;return i[n]=[l,c],n++,a=!0,l}),!a)break}let o=e.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let n=i.length-1;n>=0;n--){let[a]=i[n];for(let c=o.length-1;c>=0;c--)if(o[c].indexOf(a)!==-1){o[c]=o[c].replace(a,i[n][1]);break}}return this.#e.insert(o,t,s,this.#t,r),s}buildRegExp(){let e=this.#e.buildRegExpStr();if(e==="")return[/^$/,[],[]];let t=0,r=[],s=[];return e=e.replace(/#(\d+)|@(\d+)|\.\*\$/g,(i,o,n)=>o!==void 0?(r[++t]=Number(o),"$()"):(n!==void 0&&(s[Number(n)]=++t),"")),[new RegExp(`^${e}`),r,s]}};var Ni=[/^$/,[],Object.create(null)],Ir=Object.create(null);function Ar(e){return Ir[e]??=new RegExp(e==="*"?"":`^${e.replace(/\/\*$|([.\\+*[^\]$()])/g,(t,r)=>r?`\\${r}`:"(?:|/.*)")}$`)}function _i(){Ir=Object.create(null)}function Wi(e){let t=new xr,r=[];if(e.length===0)return Ni;let s=e.map(l=>[!/\*|\/:/.test(l[0]),...l]).sort(([l,u],[f,h])=>l?1:f?-1:u.length-h.length),i=Object.create(null);for(let l=0,u=-1,f=s.length;l<f;l++){let[h,y,C]=s[l];h?i[y]=[C.map(([I])=>[I,Object.create(null)]),Ue]:u++;let g;try{g=t.insert(y,u,h)}catch(I){throw I===Z?new De(y):I}h||(r[u]=C.map(([I,T])=>{let H=Object.create(null);for(T-=1;T>=0;T--){let[d,E]=g[T];H[d]=E}return[I,H]}))}let[o,n,a]=t.buildRegExp();for(let l=0,u=r.length;l<u;l++)for(let f=0,h=r[l].length;f<h;f++){let y=r[l][f]?.[1];if(!y)continue;let C=Object.keys(y);for(let g=0,I=C.length;g<I;g++)y[C[g]]=a[y[C[g]]]}let c=[];for(let l in n)c[l]=r[n[l]];return[o,c,i]}function oe(e,t){if(e){for(let r of Object.keys(e).sort((s,i)=>i.length-s.length))if(Ar(r).test(t))return[...e[r]]}}var He=class{name="RegExpRouter";#t;#e;constructor(){this.#t={[x]:Object.create(null)},this.#e={[x]:Object.create(null)}}add(e,t,r){let s=this.#t,i=this.#e;if(!s||!i)throw new Error(Oe);s[e]||[s,i].forEach(a=>{a[e]=Object.create(null),Object.keys(a[x]).forEach(c=>{a[e][c]=[...a[x][c]]})}),t==="/*"&&(t="*");let o=(t.match(/\/:/g)||[]).length;if(/\*$/.test(t)){let a=Ar(t);e===x?Object.keys(s).forEach(c=>{s[c][t]||=oe(s[c],t)||oe(s[x],t)||[]}):s[e][t]||=oe(s[e],t)||oe(s[x],t)||[],Object.keys(s).forEach(c=>{(e===x||e===c)&&Object.keys(s[c]).forEach(l=>{a.test(l)&&s[c][l].push([r,o])})}),Object.keys(i).forEach(c=>{(e===x||e===c)&&Object.keys(i[c]).forEach(l=>a.test(l)&&i[c][l].push([r,o]))});return}let n=Me(t)||[t];for(let a=0,c=n.length;a<c;a++){let l=n[a];Object.keys(i).forEach(u=>{(e===x||e===u)&&(i[u][l]||=[...oe(s[u],l)||oe(s[x],l)||[]],i[u][l].push([r,o-c+a+1]))})}}match=vt;buildAllMatchers(){let e=Object.create(null);return Object.keys(this.#e).concat(Object.keys(this.#t)).forEach(t=>{e[t]||=this.#r(t)}),this.#t=this.#e=void 0,_i(),e}#r(e){let t=[],r=e===x;return[this.#t,this.#e].forEach(s=>{let i=s[e]?Object.keys(s[e]).map(o=>[o,s[e][o]]):[];i.length!==0?(r||=!0,t.push(...i)):e!==x&&t.push(...Object.keys(s[x]).map(o=>[o,s[x][o]]))}),r?Wi(t):null}};var St=class{name="SmartRouter";#t=[];#e=[];constructor(e){this.#t=e.routers}add(e,t,r){if(!this.#e)throw new Error(Oe);this.#e.push([e,t,r])}match(e,t){if(!this.#e)throw new Error("Fatal error");let r=this.#t,s=this.#e,i=r.length,o=0,n;for(;o<i;o++){let a=r[o];try{for(let c=0,l=s.length;c<l;c++)a.add(...s[c]);n=a.match(e,t)}catch(c){if(c instanceof De)continue;throw c}this.match=a.match.bind(a),this.#t=[a],this.#e=void 0;break}if(o===i)throw new Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,n}get activeRouter(){if(this.#e||this.#t.length!==1)throw new Error("No active router has been determined yet.");return this.#t[0]}};var ve=Object.create(null),kr=class Tr{#t;#e;#r;#o=0;#i=ve;constructor(t,r,s){if(this.#e=s||Object.create(null),this.#t=[],t&&r){let i=Object.create(null);i[t]={handler:r,possibleKeys:[],score:0},this.#t=[i]}this.#r=[]}insert(t,r,s){this.#o=++this.#o;let i=this,o=cr(r),n=[];for(let a=0,c=o.length;a<c;a++){let l=o[a],u=o[a+1],f=lr(l,u),h=Array.isArray(f)?f[0]:l;if(h in i.#e){i=i.#e[h],f&&n.push(f[1]);continue}i.#e[h]=new Tr,f&&(i.#r.push(f),n.push(f[1])),i=i.#e[h]}return i.#t.push({[t]:{handler:s,possibleKeys:n.filter((a,c,l)=>l.indexOf(a)===c),score:this.#o}}),i}#s(t,r,s,i){let o=[];for(let n=0,a=t.#t.length;n<a;n++){let c=t.#t[n],l=c[r]||c[x],u={};if(l!==void 0&&(l.params=Object.create(null),o.push(l),s!==ve||i&&i!==ve))for(let f=0,h=l.possibleKeys.length;f<h;f++){let y=l.possibleKeys[f],C=u[l.score];l.params[y]=i?.[y]&&!C?i[y]:s[y]??i?.[y],u[l.score]=!0}}return o}search(t,r){let s=[];this.#i=ve;let o=[this],n=ft(r),a=[];for(let c=0,l=n.length;c<l;c++){let u=n[c],f=c===l-1,h=[];for(let y=0,C=o.length;y<C;y++){let g=o[y],I=g.#e[u];I&&(I.#i=g.#i,f?(I.#e["*"]&&s.push(...this.#s(I.#e["*"],t,g.#i)),s.push(...this.#s(I,t,g.#i))):h.push(I));for(let T=0,H=g.#r.length;T<H;T++){let d=g.#r[T],E=g.#i===ve?{}:{...g.#i};if(d==="*"){let j=g.#e["*"];j&&(s.push(...this.#s(j,t,g.#i)),j.#i=E,h.push(j));continue}let[k,R,D]=d;if(!u&&!(D instanceof RegExp))continue;let P=g.#e[k],$=n.slice(c).join("/");if(D instanceof RegExp){let j=D.exec($);if(j){if(E[R]=j[0],s.push(...this.#s(P,t,g.#i,E)),Object.keys(P.#e).length){P.#i=E;let q=j[0].match(/\//)?.length??0;(a[q]||=[]).push(P)}continue}}(D===!0||D.test(u))&&(E[R]=u,f?(s.push(...this.#s(P,t,E,g.#i)),P.#e["*"]&&s.push(...this.#s(P.#e["*"],t,E,g.#i))):(P.#i=E,h.push(P)))}}o=h.concat(a.shift()??[])}return s.length>1&&s.sort((c,l)=>c.score-l.score),[s.map(({handler:c,params:l})=>[c,l])]}};var bt=class{name="TrieRouter";#t;constructor(){this.#t=new kr}add(e,t,r){let s=Me(t);if(s){for(let i=0,o=s.length;i<o;i++)this.#t.insert(e,s[i],r);return}this.#t.insert(e,t,r)}match(e,t){return this.#t.search(e,t)}};var Et=class extends br{constructor(e={}){super(e),this.router=e.router??new St({routers:[new He,new bt]})}};var Rr=e=>{let r={...{origin:"*",allowMethods:["GET","HEAD","PUT","POST","DELETE","PATCH"],allowHeaders:[],exposeHeaders:[]},...e},s=(o=>typeof o=="string"?o==="*"?()=>o:n=>o===n?n:null:typeof o=="function"?o:n=>o.includes(n)?n:null)(r.origin),i=(o=>typeof o=="function"?o:Array.isArray(o)?()=>o:()=>[])(r.allowMethods);return async function(n,a){function c(u,f){n.res.headers.set(u,f)}let l=await s(n.req.header("origin")||"",n);if(l&&c("Access-Control-Allow-Origin",l),r.credentials&&c("Access-Control-Allow-Credentials","true"),r.exposeHeaders?.length&&c("Access-Control-Expose-Headers",r.exposeHeaders.join(",")),n.req.method==="OPTIONS"){r.origin!=="*"&&c("Vary","Origin"),r.maxAge!=null&&c("Access-Control-Max-Age",r.maxAge.toString());let u=await i(n.req.header("origin")||"",n);u.length&&c("Access-Control-Allow-Methods",u.join(","));let f=r.allowHeaders;if(!f?.length){let h=n.req.header("Access-Control-Request-Headers");h&&(f=h.split(/\s*,\s*/))}return f?.length&&(c("Access-Control-Allow-Headers",f.join(",")),n.res.headers.append("Vary","Access-Control-Request-Headers")),n.res.headers.delete("Content-Length"),n.res.headers.delete("Content-Type"),new Response(null,{headers:n.res.headers,status:204,statusText:"No Content"})}await a(),r.origin!=="*"&&n.header("Vary","Origin",{append:!0})}};import{createServer as qi}from"http";import{Http2ServerRequest as zi}from"http2";import{Http2ServerRequest as Ct}from"http2";import{Readable as Pr}from"stream";import io from"crypto";var Q=class extends Error{constructor(e,t){super(e,t),this.name="RequestError"}},Gi=e=>e instanceof Q?e:new Q(e.message,{cause:e}),Ki=global.Request,we=class extends Ki{constructor(e,t){typeof e=="object"&&ce in e&&(e=e[ce]()),typeof t?.body?.getReader<"u"&&(t.duplex??="half"),super(e,t)}},Ji=e=>{let t=[],r=e.rawHeaders;for(let s=0;s<r.length;s+=2){let{[s]:i,[s+1]:o}=r;i.charCodeAt(0)!==58&&t.push([i,o])}return new Headers(t)},Lr=Symbol("wrapBodyStream"),Xi=(e,t,r,s,i)=>{let o={method:e,headers:r,signal:i.signal};if(e==="TRACE"){o.method="GET";let n=new we(t,o);return Object.defineProperty(n,"method",{get(){return"TRACE"}}),n}if(!(e==="GET"||e==="HEAD"))if("rawBody"in s&&s.rawBody instanceof Buffer)o.body=new ReadableStream({start(n){n.enqueue(s.rawBody),n.close()}});else if(s[Lr]){let n;o.body=new ReadableStream({async pull(a){try{n||=Pr.toWeb(s).getReader();let{done:c,value:l}=await n.read();c?a.close():a.enqueue(l)}catch(c){a.error(c)}}})}else o.body=Pr.toWeb(s);return new we(t,o)},ce=Symbol("getRequestCache"),Yi=Symbol("requestCache"),Fe=Symbol("incomingKey"),Ve=Symbol("urlKey"),Zi=Symbol("headersKey"),ae=Symbol("abortControllerKey"),Qi=Symbol("getAbortController"),Ne={get method(){return this[Fe].method||"GET"},get url(){return this[Ve]},get headers(){return this[Zi]||=Ji(this[Fe])},[Qi](){return this[ce](),this[ae]},[ce](){return this[ae]||=new AbortController,this[Yi]||=Xi(this.method,this[Ve],this.headers,this[Fe],this[ae])}};["body","bodyUsed","cache","credentials","destination","integrity","mode","redirect","referrer","referrerPolicy","signal","keepalive"].forEach(e=>{Object.defineProperty(Ne,e,{get(){return this[ce]()[e]}})});["arrayBuffer","blob","clone","formData","json","text"].forEach(e=>{Object.defineProperty(Ne,e,{value:function(){return this[ce]()[e]()}})});Object.setPrototypeOf(Ne,we.prototype);var eo=(e,t)=>{let r=Object.create(Ne);r[Fe]=e;let s=e.url||"";if(s[0]!=="/"&&(s.startsWith("http://")||s.startsWith("https://"))){if(e instanceof Ct)throw new Q("Absolute URL for :path is not allowed in HTTP/2");try{let a=new URL(s);r[Ve]=a.href}catch(a){throw new Q("Invalid absolute URL",{cause:a})}return r}let i=(e instanceof Ct?e.authority:e.headers.host)||t;if(!i)throw new Q("Missing host header");let o;if(e instanceof Ct){if(o=e.scheme,!(o==="http"||o==="https"))throw new Q("Unsupported scheme")}else o=e.socket&&e.socket.encrypted?"https":"http";let n=new URL(`${o}://${i}${s}`);if(n.hostname.length!==i.length&&n.hostname!==i.replace(/:\d+$/,""))throw new Q("Invalid host header");return r[Ve]=n.href,r},jr=Symbol("responseCache"),ne=Symbol("getResponseCache"),ee=Symbol("cache"),It=global.Response,Se=class $r{#t;#e;[ne](){return delete this[ee],this[jr]||=new It(this.#t,this.#e)}constructor(t,r){let s;if(this.#t=t,r instanceof $r){let i=r[jr];if(i){this.#e=i,this[ne]();return}else this.#e=r.#e,s=new Headers(r.#e.headers)}else this.#e=r;(typeof t=="string"||typeof t?.getReader<"u"||t instanceof Blob||t instanceof Uint8Array)&&(s||=r?.headers||{"content-type":"text/plain; charset=UTF-8"},this[ee]=[r?.status||200,t,s])}get headers(){let t=this[ee];return t?(t[2]instanceof Headers||(t[2]=new Headers(t[2])),t[2]):this[ne]().headers}get status(){return this[ee]?.[0]??this[ne]().status}get ok(){let t=this.status;return t>=200&&t<300}};["body","bodyUsed","redirected","statusText","trailers","type","url"].forEach(e=>{Object.defineProperty(Se.prototype,e,{get(){return this[ne]()[e]}})});["arrayBuffer","blob","clone","formData","json","text"].forEach(e=>{Object.defineProperty(Se.prototype,e,{value:function(){return this[ne]()[e]()}})});Object.setPrototypeOf(Se,It);Object.setPrototypeOf(Se.prototype,It.prototype);async function to(e){return Promise.race([e,Promise.resolve().then(()=>Promise.resolve(void 0))])}function Mr(e,t,r){let s=a=>{e.cancel(a).catch(()=>{})};return t.on("close",s),t.on("error",s),(r??e.read()).then(n,i),e.closed.finally(()=>{t.off("close",s),t.off("error",s)});function i(a){a&&t.destroy(a)}function o(){e.read().then(n,i)}function n({done:a,value:c}){try{if(a)t.end();else if(!t.write(c))t.once("drain",o);else return e.read().then(n,i)}catch(l){i(l)}}}function ro(e,t){if(e.locked)throw new TypeError("ReadableStream is locked.");return t.destroyed?void 0:Mr(e.getReader(),t)}var Or=e=>{let t={};e instanceof Headers||(e=new Headers(e??void 0));let r=[];for(let[s,i]of e)s==="set-cookie"?r.push(i):t[s]=i;return r.length>0&&(t["set-cookie"]=r),t["content-type"]??="text/plain; charset=UTF-8",t},so="x-hono-already-sent",oo=global.fetch;typeof global.crypto>"u"&&(global.crypto=io);global.fetch=(e,t)=>(t={compress:!1,...t},oo(e,t));var At=Symbol("outgoingEnded"),no=()=>new Response(null,{status:400}),Dr=e=>new Response(null,{status:e instanceof Error&&(e.name==="TimeoutError"||e.constructor.name==="TimeoutError")?504:500}),xt=(e,t)=>{let r=e instanceof Error?e:new Error("unknown error",{cause:e});r.code==="ERR_STREAM_PREMATURE_CLOSE"?console.info("The user aborted a request."):(console.error(e),t.headersSent||t.writeHead(500,{"Content-Type":"text/plain"}),t.end(`Error: ${r.message}`),t.destroy(r))},Ur=e=>{"flushHeaders"in e&&e.writable&&e.flushHeaders()},Br=async(e,t)=>{let[r,s,i]=e[ee];i instanceof Headers&&(i=Or(i)),typeof s=="string"?i["Content-Length"]=Buffer.byteLength(s):s instanceof Uint8Array?i["Content-Length"]=s.byteLength:s instanceof Blob&&(i["Content-Length"]=s.size),t.writeHead(r,i),typeof s=="string"||s instanceof Uint8Array?t.end(s):s instanceof Blob?t.end(new Uint8Array(await s.arrayBuffer())):(Ur(t),await ro(s,t)?.catch(o=>xt(o,t))),t[At]?.()},ao=e=>typeof e.then=="function",co=async(e,t,r={})=>{if(ao(e))if(r.errorHandler)try{e=await e}catch(i){let o=await r.errorHandler(i);if(!o)return;e=o}else e=await e.catch(Dr);if(ee in e)return Br(e,t);let s=Or(e.headers);if(e.body){let i=e.body.getReader(),o=[],n=!1,a;if(s["transfer-encoding"]!=="chunked"){let c=2;for(let l=0;l<c;l++){a||=i.read();let u=await to(a).catch(f=>{console.error(f),n=!0});if(!u){if(l===1){await new Promise(f=>setTimeout(f)),c=3;continue}break}if(a=void 0,u.value&&o.push(u.value),u.done){n=!0;break}}n&&!("content-length"in s)&&(s["content-length"]=o.reduce((l,u)=>l+u.length,0))}t.writeHead(e.status,s),o.forEach(c=>{t.write(c)}),n?t.end():(o.length===0&&Ur(t),await Mr(i,t,a))}else s[so]||(t.writeHead(e.status,s),t.end());t[At]?.()},lo=(e,t={})=>{let r=t.autoCleanupIncoming??!0;return t.overrideGlobalObjects!==!1&&global.Request!==we&&(Object.defineProperty(global,"Request",{value:we}),Object.defineProperty(global,"Response",{value:Se})),async(s,i)=>{let o,n;try{n=eo(s,t.hostname);let a=!r||s.method==="GET"||s.method==="HEAD";if(a||(s[Lr]=!0,s.on("end",()=>{a=!0}),s instanceof zi&&(i[At]=()=>{a||setTimeout(()=>{a||setTimeout(()=>{s.destroy(),i.destroy()})})})),i.on("close",()=>{n[ae]&&(s.errored?n[ae].abort(s.errored.toString()):i.writableFinished||n[ae].abort("Client connection prematurely closed.")),a||setTimeout(()=>{a||setTimeout(()=>{s.destroy()})})}),o=e(n,{incoming:s,outgoing:i}),ee in o)return Br(o,i)}catch(a){if(o)return xt(a,i);if(t.errorHandler){if(o=await t.errorHandler(n?a:Gi(a)),!o)return}else n?o=Dr(a):o=no()}try{return await co(o,i,t)}catch(a){return xt(a,i)}}},uo=e=>{let t=e.fetch,r=lo(t,{hostname:e.hostname,overrideGlobalObjects:e.overrideGlobalObjects,autoCleanupIncoming:e.autoCleanupIncoming});return(e.createServer||qi)(e.serverOptions||{},r)},Hr=(e,t)=>{let r=uo(e);return r.listen(e?.port??3e3,e.hostname,()=>{let s=r.address();t&&t(s)}),r};import{getRandomValues as ho}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 o=this.getCharacterSet(s,r,i),n=(2<<Math.log(o.length-1)/Math.LN2)-1,a=Math.ceil(1.6*n*t/o.length),c="";for(;c.length<t;){let l=new Uint8Array(a);ho(l);for(let u=0;u<a;u++){let f=l[u]&n;if(f<o.length&&(c+=o[f],c.length===t))break}}return c}};import fo from"node:crypto";function Vr(e,t,r){if(!r)return!1;let s=typeof e=="string"?[e]:e,i=typeof t=="string"?[t]:t;if(r.roles?.some(a=>a==="administrator"||a.id==="administrator"))return!0;let n=(r.roles||[]).flatMap(a=>typeof a=="string"?[]:(a.policies||[]).flatMap(l=>l?.permissions&&l?.targets?{permissions:l.permissions,targets:l.targets}:[]));for(let a of s)for(let c of i)if(!n.some(u=>Fr(u?.permissions,a)&&Fr(u?.targets,c)))return!1;return!0}function Fr(e,t){return!e||e.length===0?!1:e.some(r=>{if(r==="*"||r===t)return!0;if(r.endsWith("*")&&r!=="*"){let s=r.slice(0,-1);return t.startsWith(s)}return!1})}var b=class extends Error{constructor(t){super(),this.name="ValidationError",this.message=t||"Invalid input",this.cause={statusCode:400}}},be=class extends Error{constructor(t){super(),this.name="IdentityAlreadyExistsError",this.message=t||"Identity already exists",this.cause={statusCode:400}}},G=class extends Error{constructor(t){super(),this.name="NotFoundError",this.message=t||"Resource not found",this.cause={statusCode:404}}},_e=class extends Error{constructor(t){super(),this.name="InvalidInputError",this.message=t||"Invalid input",this.cause={statusCode:400}}};var We=class extends Error{constructor(t){super(),this.name="ConfigurationError",this.message=t||"Invalid configuration",this.cause={statusCode:400}}};var le=class extends Error{constructor(t){super(),this.name="PermissionDeniedError",this.message=t||"Permission denied",this.cause={statusCode:403}}},K=class extends Error{constructor(t){super(),this.name="AlreadyExistsError",this.message=t||"Resource already exists",this.cause={statusCode:409}}},qe=class extends Error{constructor(t){super(),this.name="PortInUseError",this.message=t||"Port already in use",this.cause={statusCode:409}}},Ee=class extends Error{constructor(t){super(),this.name="RoleNotFoundError",this.message=t||"Role not found",this.cause={statusCode:404}}},ze=class extends Error{constructor(t){super(),this.name="ProtectedResourceError",this.message=t||"Cannot modify protected resource",this.cause={statusCode:403}}},Ge=class extends Error{constructor(t){super(),this.name="ServiceRequestError",this.message=t||"Service request failed",this.cause={statusCode:502}}};var te=class{id;name;type;roles;metadata;constructor(t){let{id:r,name:s,type:i,roles:o,metadata:n}=this.createIdentity(t);this.id=r,this.name=s,this.type=i,this.roles=o,this.metadata=n}createIdentity(t){let r=t?.id||this.createId(),s=t?.name||"",i=t?.type||"service_account",o=t?.metadata||{},n=t?.roles||["user"];return t&&this.validate({id:r,name:s,type:i,metadata:o,roles:n}),{id:r,name:s,type:i,metadata:{...o,createdAt:new Date().toISOString()},roles:n}}changeName(t){this.name=t}changeEmail(t){this.metadata||(this.metadata={}),this.metadata.email=t}updateMetadata(t){let r=this.metadata?JSON.parse(JSON.stringify(this.metadata)):{};this.metadata={...r,...t}}updateRoles(t){this.validateRoles(t),this.roles=t}createId(){return new _().create()}isValidRoleId(t){return typeof t!="string"||t.length===0?!1:/^[a-z0-9]+(-[a-z0-9]+)*$/.test(t)}isValidType(t){return["user","service_account"].includes(t)}validate(t){let r=t.id||"",s=t.name||"",i=t.type||"",o=t.metadata||{},n=t.roles||[];if(!r)throw new b("Missing ID");if(!s)throw new b("Missing name");if(!i||!this.isValidType(i))throw new b("Missing or invalid type");if(i==="user"&&!o?.email)throw new b("Missing email for user identity");if(!n||n.length===0)throw new b("Must have at least one role");this.validateRoles(n)}validateRoles(t){(t||[]).forEach(r=>{let s=r.id||r;if(!this.isValidRoleId(s))throw new b(`Invalid role ID '${s}'`)})}can(t,r,s){return Vr(t,r,s)}fromDTO(t){return this.validate(t),this.id=t.id,this.name=t.name,this.type=t.type,this.metadata=t.metadata||{},this.roles=t.roles,this}toDTO(){return{id:this.id,name:this.name,type:this.type,metadata:this.metadata,roles:this.roles}}};function Nr(){return[{id:"administrator",name:"Administrator",description:"Full system administrator with all permissions",policies:[{permissions:["*"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!0,audit_level:"high"}}},{id:"user",name:"User",description:"Read-only access to resources",policies:[{permissions:["*.read"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"low"}}}]}var Ke=class{mikroAuth;db;tableName="identity_service";initialUser;roles;identities;serviceAccountTokens;constructor(t,r,s){this.mikroAuth=t,this.db=r,this.initialUser=s,this.roles=[],this.identities=[],this.serviceAccountTokens=new Map}async start(){await this.loadState(),this.roles.find(t=>t.id==="administrator")||this.createBaseRoles(),this.identities.length===0&&this.createInitialUser()}createBaseRoles(){let t=Nr();for(let r of t)this.roles.push(r)}async createInitialUser(){let t=this.initialUser.email,r=this.initialUser.userName,s=await this.addUser(t,r,["administrator"]);return await this.mikroAuth.createMagicLink({email:t}),s}async getUserByEmail(t){let r=this.identities.find(s=>s.type==="user"&&s.metadata?.email===t&&s.metadata?.active!==!1);if(r)return this.enrichIdentityWithRoles(r)}async addUser(t,r,s=["user"],i=!0){if((await this.getUsers()).find(c=>c.metadata.email===t))throw new be(`User with email ${t} already exists`);let a=new te({name:r,type:"user",metadata:{email:t,active:i},roles:s}).toDTO();return this.identities.push(a),await this.saveState(),this.enrichIdentityWithRoles(a)}async addServiceAccount(t,r,s){if((await this.getServiceAccounts()).find(c=>c.name===t))throw new be(`Service account with name ${t} already exists`);let n=new te({name:t,type:"service_account",metadata:{description:r},roles:s}).toDTO(),a=this.generateServiceAccountToken();return this.serviceAccountTokens.set(a,n.id),this.identities.push(n),await this.saveState(),{...this.enrichIdentityWithRoles(n),apiKey:a}}async rotateServiceAccountKey(t){let r=await this.getIdentityById(t);if(!r||r.type!=="service_account")return;for(let[i,o]of this.serviceAccountTokens.entries())o===t&&this.serviceAccountTokens.delete(i);let s=this.generateServiceAccountToken();return this.serviceAccountTokens.set(s,t),await this.saveState(),s}async deleteIdentity(t){for(let[s,i]of this.serviceAccountTokens.entries())i===t&&this.serviceAccountTokens.delete(s);let r=this.identities.filter(s=>s.id!==t);this.identities=r,await this.saveState()}async updateIdentity(t,r){let s=await this.getIdentityById(t);if(!s)return;let i=new te().fromDTO(s);r?.name&&i.changeName(r.name),r?.email&&i.changeEmail(r.email),r?.roles&&i.updateRoles(r.roles),r?.metadata&&i.updateMetadata(r.metadata);let o=i.toDTO();return this.updateIdentityList(o),await this.saveState(),this.enrichIdentityWithRoles(o)}updateIdentityList(t){if(!t)throw new b("Cannot update identity list: updatedIdentity is null or undefined");let r=JSON.parse(JSON.stringify(this.identities)),s=r.find(i=>i.id===t.id);s&&Object.assign(s,t),this.identities=r}generateServiceAccountToken(){return`sa.${new _().create()}.${fo.randomBytes(32).toString("hex")}`}async getUserFromToken(t){if(t&&t.startsWith("Bearer ")){let r=t.split(" ")[1];if(r.startsWith("sa.")){let i=this.serviceAccountTokens.get(r);if(i){let o=this.identities.find(n=>n.id===i&&n.type==="service_account");if(o)return this.enrichIdentityWithRoles(o)}}else try{let i=this.mikroAuth.verify(r),o=i.email||i.sub;return this.getUserByEmail(o)}catch{let o=new Error("Invalid token");throw o.cause={statusCode:401},o}}}async getIdentityById(t){return this.identities.find(r=>r.id===t)}async getUserById(t){let r=await this.getIdentityById(t);if(!(!r||r.type!=="user"))return this.enrichIdentityWithRoles(r)}async getServiceAccountById(t){let r=await this.getIdentityById(t);if(!(!r||r.type!=="service_account"))return this.enrichIdentityWithRoles(r)}async updateUser(t,r){let s=await this.getIdentityById(t);if(!(!s||s.type!=="user"))return this.updateIdentity(t,r)}async updateServiceAccount(t,r){let s=await this.getIdentityById(t);if(!s||s.type!=="service_account")return;let i={...r};return r.description!==void 0&&(i.metadata={...i.metadata||{},description:r.description},delete i.description),this.updateIdentity(t,i)}async deleteUser(t){let r=await this.getIdentityById(t);return!r||r.type!=="user"?!1:(await this.deleteIdentity(t),!0)}async deleteServiceAccount(t){let r=await this.getIdentityById(t);return!r||r.type!=="service_account"?!1:(await this.deleteIdentity(t),!0)}enrichIdentityWithRoles(t){let r=JSON.parse(JSON.stringify(t));return t.roles?r.roles=t.roles.map(s=>this.roles.find(i=>i.id===s)).filter(Boolean):r.roles=[],r}async getIdentities(){return this.identities}async getUsers(){return this.identities.filter(t=>t.type==="user")}async getServiceAccounts(){return this.identities.filter(t=>t.type==="service_account")}can(t,r,s){return new te(s).can(t,r,s)}async createCustomRole(t,r,s,i,o){if(this.roles.find(l=>l.id===t))throw new K(`Role with ID ${t} already exists`);let c={id:t,name:r,description:s,policies:[{permissions:i,targets:["*"]}],constraints:o||{assumable_by:{identities:[],roles:[],services:["functions"]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"medium"}}};this.roles.push(c),await this.saveState()}async updateRole(t,r){let s=this.roles.find(i=>i.id===t);if(!s)throw new Ee(`Role with ID ${t} not found`);r.name!==void 0&&(s.name=r.name),r.description!==void 0&&(s.description=r.description),r.permissions!==void 0&&(s.policies=[{permissions:r.permissions,targets:["*"]}]),r.constraints!==void 0&&(s.constraints=r.constraints),await this.saveState()}async deleteRole(t){let r=this.roles.findIndex(s=>s.id===t);if(r===-1)throw new Ee(`Role with ID ${t} not found`);if(t==="administrator"||t==="user")throw new ze("Cannot delete base roles");this.roles.splice(r,1),await this.saveState()}async getRoles(){return this.roles}async getRole(t){return this.roles.find(r=>r.id===t)}async loadState(){let t=await this.load("identities");t&&(this.identities=t);let r=await this.load("roles");r&&(this.roles=r);let s=await this.load("serviceAccountTokens");if(s){let i=new Map(Object.entries(s));s&&Object.keys(s).length>0&&(this.serviceAccountTokens=i)}}async saveState(){let t=Object.fromEntries(this.serviceAccountTokens);await this.write("identities",this.identities),await this.write("roles",this.roles),await this.write("serviceAccountTokens",t)}async load(t){return await this.db.get({tableName:this.tableName,key:t})}async write(t,r){await this.db.write({tableName:this.tableName,key:t,value:r})}};import{existsSync as Co}from"node:fs";function re(e){if(!e||typeof e!="string")throw new Error("Table name must be a non-empty string");if(e.length>255)throw new Error("Table name must not exceed 255 characters");if(e.includes("/")||e.includes("\\"))throw new Error("Table name must not contain path separators");if(e.includes(".."))throw new Error('Table name must not contain ".."');if(e.startsWith("."))throw new Error('Table name must not start with "."');if(e.includes("\0"))throw new Error("Table name must not contain null bytes");if(["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"].includes(e.toUpperCase()))throw new Error(`Table name "${e}" is reserved by the filesystem`)}function Je(e){if(e==null)throw new Error("Key must be defined");if(typeof e!="string")throw new Error("Key must be a string");if(e.length===0)throw new Error("Key must not be empty");if(e.length>1024)throw new Error("Key must not exceed 1024 characters");if(e.includes("\0"))throw new Error("Key must not contain null bytes")}function _r(e){if(e===void 0)throw new Error("Value must not be undefined (use null instead)");let t=typeof e;if(t==="function")throw new Error("Value must be JSON-serializable: functions are not supported");if(t==="symbol")throw new Error("Value must be JSON-serializable: symbols are not supported");try{if(JSON.stringify(e)===void 0)throw new Error("Value must be JSON-serializable: value cannot be serialized")}catch(r){throw new Error(`Value must be JSON-serializable: ${r instanceof Error?r.message:String(r)}`)}}function Xe(e){if(!e.deflate&&!e.inflate)throw new Error("Dictionary must provide either deflate or inflate mapping");if(e.deflate&&e.inflate)throw new Error("Dictionary should provide only one of deflate or inflate (not both). The inverse will be auto-generated.");return e.deflate?{deflate:e.deflate,inflate:Wr(e.deflate)}:{deflate:Wr(e.inflate),inflate:e.inflate}}function Wr(e){let t={};for(let[r,s]of Object.entries(e))t[s]=r;return t}function ue(e,t){if(e==null||typeof e!="object")return e;if(Array.isArray(e))return e.map(s=>ue(s,t));let r={};for(let[s,i]of Object.entries(e)){let o=t[s]||s;r[o]=ue(i,t)}return r}import{existsSync as kt,mkdirSync as po,readdirSync as mo,openSync as go,closeSync as yo}from"fs";import{readFile as vo,writeFile as wo,rename as So,unlink as qr,open as bo}from"fs/promises";import{join as Tt,dirname as Eo}from"path";var Ce=class{data=new Map;databaseDirectory;dictionaries=new Map;useFsync;constructor(e){this.databaseDirectory=e.databaseDirectory,this.useFsync=e.durableWrites??!1,e.dictionaries&&Object.entries(e.dictionaries).forEach(([t,r])=>{let s=Xe(r);this.dictionaries.set(t,s)}),kt(this.databaseDirectory)||po(this.databaseDirectory,{recursive:!0})}async start(){try{let e=mo(this.databaseDirectory);for(let t of e)!t.endsWith(".tmp")&&!t.startsWith(".")&&await this.loadTable(t)}catch(e){throw console.error("Failed to start database:",e),e}}async write(e,t,r,s,i){if(re(e),Je(t),_r(r),i&&!this.dictionaries.has(i))throw new Error(`Dictionary "${i}" not found. Available dictionaries: ${Array.from(this.dictionaries.keys()).join(", ")||"none"}`);try{this.data.has(e)||this.data.set(e,new Map);let o=this.data.get(e),a=(o.get(t)?.version||0)+1,c={value:r,version:a,timestamp:Date.now(),expiration:s||null,dictionaryName:i||void 0};return o.set(t,c),await this.persistTable(e),!0}catch(o){return console.error(`Write failed for ${e}:${t}:`,o),!1}}async get(e,t){re(e),t!==void 0&&Je(t);try{this.data.has(e)||await this.loadTable(e);let r=this.data.get(e);if(!r)return t?void 0:[];if(t!==void 0){let o=r.get(t);if(!o)return;if(this.isExpired(o)){r.delete(t),await this.persistTable(e);return}return o.value}let s=[],i=[];for(let[o,n]of r.entries())this.isExpired(n)?i.push(o):s.push([o,n.value]);if(i.length>0){for(let o of i)r.delete(o);await this.persistTable(e)}return s}catch(r){return console.error(`Read failed for ${e}:${t}:`,r),t?void 0:[]}}async delete(e,t){re(e),Je(t);try{this.data.has(e)||await this.loadTable(e);let r=this.data.get(e);if(!r||!r.has(t))return!1;let s=r.get(t);return s?this.isExpired(s)?(r.delete(t),await this.persistTable(e),!1):(r.delete(t),await this.persistTable(e),!0):!1}catch(r){return console.error(`Delete failed for ${e}:${t}:`,r),!1}}async getTableSize(e){re(e);try{this.data.has(e)||await this.loadTable(e);let t=this.data.get(e);if(!t)return 0;let r=0,s=[];for(let[i,o]of t.entries())this.isExpired(o)?s.push(i):r++;if(s.length>0){for(let i of s)t.delete(i);await this.persistTable(e)}return r}catch(t){return console.error(`Get table size failed for ${e}:`,t),0}}isExpired(e){return e.expiration===null?!1:Date.now()>e.expiration}async cleanupExpired(e){re(e);try{this.data.has(e)||await this.loadTable(e);let t=this.data.get(e);if(!t)return 0;let r=[];for(let[s,i]of t.entries())this.isExpired(i)&&r.push(s);for(let s of r)t.delete(s);return r.length>0&&await this.persistTable(e),r.length}catch(t){return console.error(`Cleanup failed for ${e}:`,t),0}}async cleanupAllExpired(){let e=0;for(let t of this.data.keys())e+=await this.cleanupExpired(t);return e}async deleteTable(e){re(e);try{this.data.delete(e);let t=Tt(this.databaseDirectory,e);return kt(t)&&await qr(t),!0}catch(t){return console.error(`Delete table failed for ${e}:`,t),!1}}listTables(){return Array.from(this.data.keys())}async flush(){try{let e=Array.from(this.data.keys()).map(t=>this.persistTable(t));await Promise.all(e)}catch(e){throw console.error("Flush failed:",e),e}}async close(){await this.flush()}addDictionary(e,t){let r=Xe(t);this.dictionaries.set(e,r)}removeDictionary(e){return this.dictionaries.delete(e)}listDictionaries(){return Array.from(this.dictionaries.keys())}async loadTable(e){let t=Tt(this.databaseDirectory,e);if(!kt(t)){this.data.set(e,new Map);return}try{let r=await vo(t);if(r.length===0){this.data.set(e,new Map);return}let s=this.deserializeTable(r);this.data.set(e,s)}catch(r){console.error(`Failed to load table ${e}:`,r),this.data.set(e,new Map)}}async persistTable(e){let t=this.data.get(e);if(!t)return;let r=this.serializeTable(t),s=Tt(this.databaseDirectory,e),i=`${s}.tmp.${Date.now()}.${Math.random().toString(36).substring(7)}`;try{if(await wo(i,r),this.useFsync){let o=await bo(i,"r+");try{await o.sync()}finally{await o.close()}}if(await So(i,s),this.useFsync){let o=Eo(s),n=go(o,"r");try{yo(n)}catch{}}}catch(o){try{await qr(i)}catch{}throw o}}serializeTable(e){let t=Array.from(e.entries()).map(([r,s])=>{let i=s.dictionaryName?this.dictionaries.get(s.dictionaryName):void 0,o={d:i?ue(s.value,i.deflate):s.value,v:s.version,t:s.timestamp,x:s.expiration};return s.dictionaryName&&(o.n=s.dictionaryName),[r,o]});return Buffer.from(JSON.stringify(t),"utf8")}deserializeTable(e){let r=JSON.parse(e.toString("utf8")).map(([s,i])=>{let o=i.n,n=o?this.dictionaries.get(o):void 0;return[s,{value:n?ue(i.d,n.inflate):i.d,version:i.v,timestamp:i.t,expiration:i.x,dictionaryName:o||void 0}]});return new Map(r)}};import{spawn as xo}from"node:child_process";import{createServer as Io}from"node:net";var zr={storage:{name:"storage",path:"./storage.mjs",port:3001,prefix:"/storage",args:["--force"]},functions:{name:"functions",path:"./functions.mjs",port:3002,prefix:"/functions",args:["--force"]},sites:{name:"sites",path:"./sites.mjs",port:3003,prefix:"/sites",args:["--force"]},databases:{name:"databases",path:"./databases.mjs",port:3004,prefix:"/databases",args:["--force"]},observability:{name:"observability",path:"./observability.mjs",port:3005,prefix:"/observability",args:["--force"]}},Ye=class{db;services=new Map;tableName="molnosmanagement";environmentVariables={};setEnvironmentVariables(t){this.environmentVariables={...this.environmentVariables,...t}}async isPortInUse(t){return new Promise(r=>{let s=Io();s.once("error",i=>{i.code==="EADDRINUSE"?r(!0):r(!1)}),s.once("listening",()=>{s.close(),r(!1)}),s.listen(t,"localhost")})}constructor(t){this.db=new Ce({databaseDirectory:t.dbPath})}getServiceDefinition(t){let r=t.toLowerCase(),s=zr[r];if(!s)throw new G(`Unknown service: ${t}. Valid services are: ${Object.keys(zr).join(", ")}`);return s}async start(){await this.db.start();let t=await this.db.get(this.tableName,"services")||[];for(let r of t)r.restartPolicy&&(r.restartPolicy.attempts=0),r.healthCheck&&(r.healthCheck.consecutiveFailures=0,r.healthCheck.consecutiveSuccesses=0,r.healthCheck.healthy=!0),this.services.set(r.name,r),r.active&&await this.startService(r.name)}async stopAllServices(){let t=[];for(let[r,s]of this.services.entries())(s.active||s.process)&&(console.log(`[ManagementService] Stopping ${r}...`),t.push(this.stopService(r)));await Promise.all(t),console.log("[ManagementService] All services stopped")}async shutdown(){console.log("[ManagementService] External shutdown requested"),await this.stopAllServices()}validateServiceConfig(t){if(!t.name||t.name.trim()==="")throw new b("Service name is required");if(!t.path||t.path.trim()==="")throw new b("Service path is required");if(!Co(t.path))throw new We(`Service path does not exist: ${t.path}`);if(!t.port||t.port<1||t.port>65535)throw new b(`Invalid port: ${t.port}`);if(!t.prefix||!t.prefix.startsWith("/"))throw new b("Service prefix must start with /")}cleanServiceForStorage(t){let{process:r,...s}=t;if(s.healthCheck){let{timer:i,consecutiveFailures:o,consecutiveSuccesses:n,healthy:a,...c}=s.healthCheck;s.healthCheck=c}if(s.restartPolicy){let{attempts:i,...o}=s.restartPolicy;s.restartPolicy=o}return s}async registerService(t,r){let s;typeof t=="string"?s={...this.getServiceDefinition(t),...r}:s=t,this.validateServiceConfig(s);let i={...s,active:!1},o=await this.getServices(),n=[],a=[];if(o.forEach(l=>{n.push(l.name),a.push(l.port)}),a.includes(i.port))throw new qe(`Port ${i.port} already in use`);if(n.includes(i.name))throw new K(`Service with name '${i.name}' already exists`);i.restartPolicy&&(i.restartPolicy.attempts=0),i.healthCheck&&(i.healthCheck.consecutiveFailures=0,i.healthCheck.consecutiveSuccesses=0,i.healthCheck.healthy=!0),this.services.set(i.name,i);let c=await this.db.get(this.tableName,"services")||[];return c.push(this.cleanServiceForStorage(i)),await this.db.write(this.tableName,"services",c),i.name}async startService(t){let r=this.services.get(t);if(!r)return!1;if(r.process&&r.active)return!0;if(await this.isPortInUse(r.port))return console.error(`[ManagementService] Cannot start ${t}: Port ${r.port} is already in use. Please kill the process using this port.`),!1;try{let i={stdio:"pipe",detached:!1,env:{...process.env,...this.environmentVariables}},o=r.args||[],n=`--port=${r.port}`,a=xo("node",[r.path,n,...o],i);r.process=a,r.restartPolicy&&(r.restartPolicy.attempts=0);let c=!1;return a.stdout.on("data",l=>{let u=l.toString();console.log(`[${t}] ${u}`)}),a.stderr.on("data",l=>{let u=l.toString();console.error(`[${t}] ERROR: ${u}`),(u.includes("EADDRINUSE")||u.includes("address already in use"))&&(c=!0)}),a.on("exit",l=>{r.active=!1,r.process=void 0,c&&console.error(`[ManagementService] Service ${t} failed to start due to startup error`),this.handleServiceExit(r,l||0)}),await new Promise(l=>setTimeout(l,500)),r.process&&!r.process.killed?(r.active=!0,await this.updateServiceStatus(t,!0),r.healthCheck&&this.startHealthCheck(r),!0):(console.error(`[ManagementService] Service ${t} exited immediately after spawn`),!1)}catch(i){return console.error(`[ManagementService] Failed to start ${t}:`,i),!1}}startHealthCheck(t){let r=t.healthCheck;if(!r)return;let s=async()=>{if(t.healthCheck)try{let i=new AbortController,o=setTimeout(()=>i.abort(),t.healthCheck.timeoutMs),n=await fetch(`http://localhost:${t.port}${t.healthCheck.path}`,{signal:i.signal});clearTimeout(o),n.ok&&t.healthCheck?(t.healthCheck.consecutiveSuccesses=(t.healthCheck.consecutiveSuccesses||0)+1,t.healthCheck.consecutiveFailures=0,t.healthCheck.consecutiveSuccesses>=t.healthCheck.successThreshold&&(t.healthCheck.healthy=!0)):this.handleHealthCheckFailure(t)}catch{this.handleHealthCheckFailure(t)}};r.timer=setInterval(s,r.intervalMs)}handleHealthCheckFailure(t){t.healthCheck&&(t.healthCheck.consecutiveFailures=(t.healthCheck.consecutiveFailures||0)+1,t.healthCheck.consecutiveSuccesses=0,t.healthCheck.consecutiveFailures>=t.healthCheck.failureThreshold&&(t.healthCheck.healthy=!1,t.healthCheck.restartOnFailure&&this.restartService(t.name).catch(()=>{})))}async handleServiceExit(t,r){if(await this.updateServiceStatus(t.name,!1),!t.restartPolicy||t.restartPolicy.type==="never"||t.restartPolicy.type==="on-failure"&&r===0||t.restartPolicy.maxAttempts>0&&(t.restartPolicy.attempts||0)>=t.restartPolicy.maxAttempts)return;t.restartPolicy.attempts=(t.restartPolicy.attempts||0)+1;let s=t.restartPolicy.backoffMs;setTimeout(()=>{this.startService(t.name).catch(()=>{})},s)}async stopService(t){let r=this.services.get(t);if(!r)return!1;if(r.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),!r.process)return r.active&&(r.active=!1,await this.updateServiceStatus(t,!1)),!0;try{let s=i=>{try{return process.kill(i,0),!0}catch{return!1}};if(r.process?.pid){let i=r.process.pid;if(s(i))try{process.kill(i,"SIGTERM");let o=setTimeout(()=>{try{s(i)&&process.kill(i,"SIGKILL")}catch{}},5*1e3);await new Promise(n=>{if(!r.process){clearTimeout(o),n();return}let a=setTimeout(()=>{clearTimeout(o),n()},6*1e3);r.process.once("exit",()=>{clearTimeout(o),clearTimeout(a),n()})})}catch(o){if(o.code!=="ESRCH")throw o}}return r.active=!1,r.process=void 0,await this.updateServiceStatus(t,!1),!0}catch{return r.active&&(r.active=!1,r.process=void 0,await this.updateServiceStatus(t,!1)),!1}}async restartService(t){return await this.stopService(t),await new Promise(r=>setTimeout(r,500)),this.startService(t)}async updateServiceStatus(t,r){let s=await this.db.get(this.tableName,"services")||[],i=s.findIndex(o=>o.name===t);if(i>=0){let o=this.services.get(t);o?(o.active=r,s[i]=this.cleanServiceForStorage(o)):s[i].active=r,await this.db.write(this.tableName,"services",s)}}async getService(t){return(await this.getServices()).find(s=>s.name===t)}async getServices(){return(await this.db.get(this.tableName,"services")||[]).map(({process:r,...s})=>s)}async getServiceStats(t){let r=this.services.get(t);return r?{name:r.name,active:r.active,health:r.healthCheck?{healthy:r.healthCheck.healthy||!1,consecutiveSuccesses:r.healthCheck.consecutiveSuccesses||0,consecutiveFailures:r.healthCheck.consecutiveFailures||0}:null,restarts:r.restartPolicy&&r.restartPolicy.attempts||0}:null}async updateService(t,r){let s=this.services.get(t);if(!s)return!1;let i=s.active;Object.assign(s,r);let o=await this.db.get(this.tableName,"services")||[],n=o.findIndex(a=>a.name===t);return n>=0?(o[n]=this.cleanServiceForStorage(s),await this.db.write(this.tableName,"services",o),i!==s.active&&(s.active?await this.startService(t):await this.stopService(t)),!0):!1}async removeService(t){let r=this.services.get(t);r?.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),await this.stopService(t),this.services.delete(t);let i=(await this.db.get(this.tableName,"services")||[]).filter(o=>o.name!==t);return await this.db.write(this.tableName,"services",i),!0}};var W=class{id;name;description;redirectUris;metadata;owners;constructor(t){let r=this.createApplication(t);this.id=r.id,this.name=r.name,this.description=r.description,this.redirectUris=r.redirectUris,this.metadata=r.metadata,this.owners=r.owners}createApplication(t){let r=t?.id||this.createId(),s=t?.name||"",i=t?.description,o=t?.redirectUris||[],n=t?.owners||[],a=new Date().toISOString(),c={...t?.metadata||{},createdAt:t?.metadata?.createdAt||a,updatedAt:a,createdBy:t?.metadata?.createdBy||n[0]||""};return t&&this.validate({id:r,name:s,description:i,redirectUris:o,metadata:c,owners:n}),{id:r,name:s,description:i,redirectUris:o,metadata:c,owners:n}}update(t){t.name!==void 0&&(this.name=t.name),t.description!==void 0&&(this.description=t.description),t.redirectUris!==void 0&&(this.validateRedirectUris(t.redirectUris),this.redirectUris=t.redirectUris),t.owners!==void 0&&(this.owners=t.owners),t.metadata!==void 0?this.metadata={...this.metadata,...t.metadata,updatedAt:new Date().toISOString()}:this.metadata.updatedAt=new Date().toISOString(),this.validate(this.toDTO())}isValidRedirectUri(t){return this.redirectUris.includes(t)}isOwner(t){return this.owners.includes(t)}createId(){return new _().create()}validate(t){let{id:r,name:s,redirectUris:i,metadata:o,owners:n}=t;if(!r)throw new b("Missing ID");if(!s||s.trim().length===0)throw new b("Missing or invalid name");if(!o)throw new b("Missing metadata");if(!n||n.length===0)throw new b("Application must have at least one owner");this.validateRedirectUris(i)}validateRedirectUris(t){if(!t||t.length===0)throw new b("At least one redirect URI is required");for(let r of t)try{let s=new URL(r);if(s.protocol!=="https:"&&s.protocol!=="http:"&&s.protocol!=="custom:")throw new b(`Invalid redirect URI protocol: ${r}. Must use https:, http: (localhost only), or custom: (for native apps)`);s.protocol==="http:"&&!s.hostname.match(/^(localhost|127\.0\.0\.1|\[::1\])$/)&&console.warn(`Warning: HTTP redirect URI detected for non-localhost: ${r}. HTTPS is recommended for production.`)}catch{throw new b(`Invalid redirect URI format: ${r}`)}}fromDTO(t){return this.validate(t),this.id=t.id,this.name=t.name,this.description=t.description,this.redirectUris=t.redirectUris,this.metadata=t.metadata,this.owners=t.owners,this}toDTO(){return{id:this.id,name:this.name,description:this.description,redirectUris:this.redirectUris,metadata:this.metadata,owners:this.owners}}};var Ze=class{db;tableName="molnosapplications";key="applications";applications;constructor(t){this.db=new Ce({databaseDirectory:t.dbPath}),this.applications=[]}async start(){await this.db.start(),await this.loadState()}async loadState(){let t=await this.db.get(this.tableName,this.key)||[];this.applications=t,console.log(`[ApplicationService] Loaded ${this.applications.length} applications`)}async saveState(){await this.db.write(this.tableName,this.key,this.applications)}async createApplication(t,r){let s=t.owners.includes(r)?t.owners:[...t.owners,r],o=new W({...t,owners:s,metadata:{...t.metadata,createdBy:r}}).toDTO();if(this.applications.find(a=>a.name===o.name))throw new K(`Application with name '${o.name}' already exists`);return this.applications.push(o),await this.saveState(),o}async getApplication(t,r){let s=this.applications.find(o=>o.id===t);if(!s)throw new G(`Application with ID '${t}' not found`);let i=new W().fromDTO(s);if(r&&!i.isOwner(r))throw new le("Only application owners can view application details");return i.toDTO()}async getApplicationById(t){return this.applications.find(s=>s.id===t)||null}async listApplications(t){return this.applications.filter(r=>r.owners.includes(t)).map(r=>new W().fromDTO(r).toDTO())}async updateApplication(t,r,s){let i=this.applications.findIndex(a=>a.id===t);if(i===-1)throw new G(`Application with ID '${t}' not found`);let o=this.applications[i],n=new W().fromDTO(o);if(!n.isOwner(s))throw new le("Only application owners can update the application");if(r.owners&&r.owners.length===0)throw new b("Application must have at least one owner");if(r.owners&&!r.owners.includes(s)&&o.owners.length===1&&o.owners[0]===s)throw new b("Cannot remove yourself as the last owner of the application");return n.update(r),this.applications[i]=n.toDTO(),await this.saveState(),this.applications[i]}async deleteApplication(t,r){let s=this.applications.findIndex(n=>n.id===t);if(s===-1)throw new G(`Application with ID '${t}' not found`);let i=this.applications[s];if(!new W().fromDTO(i).isOwner(r))throw new le("Only application owners can delete the application");this.applications.splice(s,1),await this.saveState()}async validateRedirectUri(t,r){let s=await this.getApplicationById(t);return s?new W().fromDTO(s).isValidRedirectUri(r):!1}getAllApplicationsInternal(){return this.applications}};async function A(e){try{return await e.req.json()}catch{return{}}}function m(e,t="An error occurred"){let r=e?.message||e||t,s=e?.cause?.statusCode||e?.statusCode||400;return{message:r,status:s}}async function Gr(e,t,r,s){try{let i=await A(e),{email:o,redirectUrl:n,applicationId:a}=i;if(!o)return e.json({error:"Email is required"},400);let c=o.trim().toLowerCase(),l;if(n){if(!a)return e.json({error:"applicationId is required when redirectUrl is provided"},400);if(!await s.validateRedirectUri(a,n))return e.json({error:"Invalid redirectUrl or applicationId. The redirect URI must be registered for this application."},400);l=(await s.getApplicationById(a))?.name}let u=!1;return await t.getUserByEmail(c)&&(u=!0),u&&await r.createMagicLink({email:c,subject:`Sign In To ${l||"MolnOS"}`,appUrl:n,metadata:{appName:l,redirectUrl:n,applicationId:a}}),e.json({success:!0,message:"If a matching account was found, a magic link has been sent."})}catch(i){let{message:o,status:n}=m(i,"Error during login");return e.text(o,n)}}async function Kr(e,t,r){try{let s=e.req.query("token"),i=e.req.query("email");if(!s)return e.json({error:"Token is required as query parameter"},400);if(!i)return e.json({error:"Email is required as query parameter"},400);try{let o=await t.verifyToken({token:s,email:i});if(!o)return e.json({error:"Invalid or expired token"},401);let n=o.metadata;if(n?.redirectUrl&&n?.applicationId)if(await r.validateRedirectUri(n.applicationId,n.redirectUrl)){let c=new URL(n.redirectUrl);return c.searchParams.set("access_token",o.accessToken),c.searchParams.set("refresh_token",o.refreshToken),c.searchParams.set("expires_in",o.expiresIn.toString()),e.redirect(c.toString(),302)}else return console.warn(`Redirect URL validation failed for applicationId: ${n.applicationId}`),e.json({...o,warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);return e.json(o,200)}catch{return e.json({error:"Invalid or expired token"},401)}}catch(s){let{message:i,status:o}=m(s,"Error verifying token");return e.text(i,o)}}async function Jr(e,t){try{let s=(await A(e)).refreshToken;if(!s)return e.json({error:"Value for refreshToken is required"},400);try{let i=await t.refreshAccessToken(s);return e.json(i,200)}catch{return e.json({error:"Invalid or expired token"},401)}}catch(r){let{message:s,status:i}=m(r,"Error refreshing token");return e.text(s,i)}}async function Xr(e){try{let t=e.get("user");return e.json(t)}catch(t){let{message:r,status:s}=m(t,"Error getting user info");return e.text(r,s)}}function p(e,t,r={}){let s=Array.isArray(t)?t:[t];if(!e||!e.roles||!Array.isArray(e.roles)){let n=new Error("Unauthorized: User or roles missing");throw n.cause={statusCode:403},n}let i=e.roles.flatMap(n=>n?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(l=>typeof l=="string"?l:l&&typeof l=="object"&&l.actions?l.actions:[]))||[]);if(!s.every(n=>i.includes(n)||i.includes("*")?!0:i.some(a=>{if(a.endsWith(".*")){let c=a.slice(0,-2);return n.startsWith(`${c}.`)}return!1}))){let n=new Error("Unauthorized");throw n.cause={statusCode:403},n}return!0}async function Yr(e,t){try{let r=e.get("user");p(r,"identity.identities.get",{});let s=await t.getIdentities();return e.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting identities");return e.text(s,i)}}var O=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),o=this.isSuccessful(r,i);return{errors:i,success:o}}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,o=e?.required||[];s=this.checkForRequiredKeysErrors(o,t,s),s=this.checkForDisallowedProperties(Object.keys(t),Object.keys(e),s,i);for(let n in e){let a=o.includes(n)&&n!=="required",c=e[n],l=t[n],u=c.additionalProperties??!0;a&&(s=this.checkForRequiredKeysErrors(c.required||[],l,s)),this.isDefined(l)&&(this.handleValidation(n,l,c,r),s=this.checkForDisallowedProperties(Object.keys(l),Object.keys(c),s,u),this.handleNestedObject(l,c,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),o=i.length>0?`Missing the required key: '${i.join(", ")}'!`:`Missing values for required keys: '${s.filter(n=>!t[n]).join(", ")}'!`;r.push({key:"",value:t,success:!1,error:o})}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 o=(a,c)=>{a.forEach(l=>{let u=this.validateProperty(this.propertyPath,c.items,l);s.push(...u)}),this.updatePropertyPath()},n=a=>{let c=Object.keys(a),l=this.propertyPath;c.forEach(u=>{if(this.updatePropertyPath(u,l),this.isArray(a[u])&&r[u]?.items!=null)o(a[u],r[u]);else{let f=this.validateProperty(this.propertyPath,r[u],a[u]);s.push(...f)}})};this.isArray(t)&&r.items!=null?o(t,r):this.isObject(t)?n(t):this.updatePropertyPath()}handleNestedObject(e,t,r,s){if(this.isObject(e)){let i=this.getNestedObjects(e);for(let o of i){let n=t[o],a=e[o];n&&typeof a=="object"&&this.validate(n,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:o,error:n}=i;return{key:e,value:r,success:o,error:n??""}})}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(o=>typeof o==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}};var Zr={properties:{email:{type:"string",format:"email",minLength:3},name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}},verified:{type:"boolean"}},required:["email","name","roles"],additionalProperties:!1};var ko=new O;async function Qr(e,t){try{let r=e.get("user"),s=await A(e),i=ko.test(Zr,s);if(!i.success)return e.json({error:"Invalid input",details:i.errors},400);p(r,"identity.user.create",{});let{email:o,name:n,roles:a,verified:c}=s,l=await t.addUser(o,n,a,c);return e.json(l,201)}catch(r){let{message:s,status:i}=m(r,"Error creating user");return e.text(s,i)}}async function es(e,t){try{let r=e.get("user");p(r,"identity.user.get",{});let s=await t.getUsers();return e.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting users");return e.text(s,i)}}function S(e){return e.req.param()}async function ts(e,t){try{let r=e.get("user"),s=S(e),{userId:i}=s;p(r,"identity.user.get",{userId:i});let o=await t.getUserById(i);return o?e.json(o):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting user");return e.text(s,i)}}var rs={properties:{name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var Ro=new O;async function ss(e,t){try{let r=e.get("user"),s=S(e),{userId:i}=s,o=await A(e),n=Ro.test(rs,o);if(!n.success)return e.json({error:"Invalid input",details:n.errors},400);p(r,"identity.user.update",{userId:i});let{name:a,roles:c}=o,l=await t.updateUser(i,{name:a,roles:c});return l?e.json(l):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error updating user");return e.text(s,i)}}async function is(e,t){try{let r=e.get("user"),s=S(e),{userId:i}=s;return p(r,"identity.user.delete",{userId:i}),await t.deleteUser(i)?e.body(null,204):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error deleting user");return e.text(s,i)}}async function os(e,t){try{let r=e.get("user"),s=await A(e);if(!s.name||!s.description||!s.roles||!Array.isArray(s.roles))return e.json({error:"Invalid input: name, description, and roles are required"},400);if(!["user","administrator"].every(c=>s.roles.includes(c)||!s.roles.includes(c)))return e.json({error:"Invalid input: roles must be user or administrator"},400);p(r,"identity.service-account.create",{});let{name:i,description:o,roles:n}=s,a=await t.addServiceAccount(i,o,n);return e.json(a,201)}catch(r){let{message:s,status:i}=m(r,"Error creating service account");return e.text(s,i)}}async function ns(e,t){try{let r=e.get("user");p(r,"identity.service-account.get",{});let s=await t.getServiceAccounts();return e.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting service accounts");return e.text(s,i)}}async function as(e,t){try{let r=e.get("user"),s=S(e),{serviceAccountId:i}=s;p(r,"identity.service-account.get",{serviceAccountId:i});let o=await t.getServiceAccountById(i);return o?e.json(o):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting service account");return e.text(s,i)}}var cs={properties:{name:{type:"string",minLength:1,maxLength:100},description:{type:"string",minLength:1,maxLength:500},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var jo=new O;async function ls(e,t){try{let r=e.get("user"),s=S(e),{serviceAccountId:i}=s,o=await A(e),n=jo.test(cs,o);if(!n.success)return e.json({error:"Invalid input",details:n.errors},400);p(r,"identity.service-account.update",{serviceAccountId:i});let{name:a,description:c,roles:l}=o,u=await t.updateServiceAccount(i,{name:a,description:c,roles:l});return u?e.json(u):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error updating service account");return e.text(s,i)}}async function us(e,t){try{let r=e.get("user"),s=S(e),{serviceAccountId:i}=s;return p(r,"identity.service-account.delete",{serviceAccountId:i}),await t.deleteServiceAccount(i)?e.body(null,204):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error deleting service account");return e.text(s,i)}}async function ds(e,t){try{let r=e.get("user"),s=S(e),{serviceAccountId:i}=s;p(r,"identity.service-account.update",{serviceAccountId:i});let o=await t.rotateServiceAccountKey(i);return o?e.json({apiKey:o}):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error rotating service account key");return e.text(s,i)}}async function hs(e,t){try{let r=e.get("user");p(r,"identity.role.get",{});let s=await t.getRoles();return e.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting roles");return e.text(s,i)}}async function fs(e,t){try{let r=e.get("user");p(r,"identity.role.get",{});let s=e.req.param("roleId");if(!s)return e.text("Missing role ID",400);let i=await t.getRole(s);return i?e.json(i):e.text("Role not found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting role");return e.text(s,i)}}async function ps(e,t){try{let r=e.get("user");p(r,"identity.role.create",{});let s=await e.req.json(),{roleId:i,name:o,description:n,permissions:a,constraints:c}=s;if(!i||!o||!n)return e.text("Missing required fields: roleId, name, description",400);if(!a||!Array.isArray(a))return e.text("Permissions must be a non-empty array",400);await t.createCustomRole(i,o,n,a,c);let l=await t.getRole(i);return e.json(l,201)}catch(r){let{message:s,status:i}=m(r,"Error creating role");return e.text(s,i)}}async function ms(e,t){try{let r=e.get("user");p(r,"identity.role.update",{});let s=e.req.param("roleId");if(!s)return e.text("Missing role ID",400);let i=await e.req.json(),{name:o,description:n,permissions:a,constraints:c}=i;await t.updateRole(s,{name:o,description:n,permissions:a,constraints:c});let l=await t.getRole(s);return e.json(l)}catch(r){let{message:s,status:i}=m(r,"Error updating role");return e.text(s,i)}}async function gs(e,t){try{let r=e.get("user");p(r,"identity.role.delete",{});let s=e.req.param("roleId");return s?s==="administrator"||s==="user"?e.text("Cannot delete base roles",403):(await t.deleteRole(s),e.json({message:"Role deleted successfully"})):e.text("Missing role ID",400)}catch(r){let{message:s,status:i}=m(r,"Error deleting role");return e.text(s,i)}}async function ys(e,t){try{let r=e.get("user");p(r,"management.services.get",{});let s=await t.getServices();return e.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting services");return e.text(s,i)}}async function vs(e,t){try{let r=e.get("user"),s=S(e),{serviceName:i}=s;p(r,"management.service.get",{serviceName:i});let o=await t.getService(i);return o?e.json(o):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting service");return e.text(s,i)}}var ws={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var $o=new O;async function Ss(e,t){try{let r=e.get("user"),s=await A(e),i=$o.test(ws,s);if(!i.success)return e.json({error:"Invalid input",details:i.errors},400);if(p(r,"management.service.create",{}),["storage","functions","sites","databases","observability"].includes(s.name.toLowerCase())){let{name:a,...c}=s;await t.registerService(a,c)}else{if(!s.path||!s.port||!s.prefix)return e.json({error:"Custom services require: name, path, port, and prefix"},400);await t.registerService(s)}return e.text("",201)}catch(r){let{message:s,status:i}=m(r,"Error creating service");return e.text(s,i)}}var bs={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var Oo=new O;async function Es(e,t){try{let r=e.get("user"),s=S(e),i=await A(e),o=i.name,n=Oo.test(bs,i);return n.success?(p(r,"management.service.update",{serviceName:o}),o!==s.serviceName?e.json({error:"Service name in body must match path parameter"},400):(await t.updateService(o,i),e.body(null,204))):e.json({error:"Invalid input",details:n.errors},400)}catch(r){let{message:s,status:i}=m(r,"Error updating service");return e.text(s,i)}}async function Cs(e,t){try{let r=e.get("user"),s=S(e),{serviceName:i}=s;return p(r,"management.service.delete",{serviceName:i}),await t.getService(i)?(await t.removeService(i),e.body(null,204)):e.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error deleting service");return e.text(s,i)}}async function xs(e,t){try{let r=e.get("user"),s=S(e),{serviceName:i}=s;if(p(r,"management.service.start",{serviceName:i}),!await t.getService(i))return e.text("Not Found",404);let n=await t.startService(i);return e.json({serviceName:i,isStarted:n})}catch(r){let{message:s,status:i}=m(r,"Error starting service");return e.text(s,i)}}async function Is(e,t){try{let r=e.get("user"),s=S(e),{serviceName:i}=s;if(p(r,"management.service.stop",{serviceName:i}),!await t.getService(i))return e.text("Not Found",404);let n=await t.stopService(i);return e.json({serviceName:i,isStopped:n})}catch(r){let{message:s,status:i}=m(r,"Error stopping service");return e.text(s,i)}}async function As(e,t){try{let r=e.get("user"),s=S(e),{serviceName:i}=s;if(p(r,"management.service.get",{serviceName:i}),!await t.getService(i))return e.text("Not Found",404);let n=new URL("http://localhost:3005/events");n.searchParams.set("service",i);let a=e.req.query();a.startTime&&n.searchParams.set("startTime",a.startTime),a.endTime&&n.searchParams.set("endTime",a.endTime),a.level&&n.searchParams.set("level",a.level),a.limit&&n.searchParams.set("limit",a.limit),a.offset&&n.searchParams.set("offset",a.offset);let c=await fetch(n.toString());if(!c.ok)throw new Ge(`Observability service error: ${c.statusText}`);let l=await c.json();return e.json({serviceName:i,logs:l.events,count:l.count})}catch(r){let{message:s,status:i}=m(r,"Error getting service logs");return e.text(s,i)}}async function ks(e,t){try{let r=e.get("user"),s=S(e),{serviceName:i}=s;if(p(r,"management.service.start",{serviceName:i}),!await t.getService(i))return e.text("Not Found",404);let n=await t.restartService(i);return e.json({serviceName:i,restarted:n})}catch(r){let{message:s,status:i}=m(r,"Error restarting service");return e.text(s,i)}}function Ts(e,t,r,s,i){let o=e.find(c=>c.service===t);if(!o){let c=new Error(`Function bindings do not include access to service: ${t}`);throw c.cause={statusCode:403},c}if(!o.permissions||o.permissions.length===0)return;for(let c of o.permissions)if(!(c.resource&&c.resource!==r)){if(!c.resource||!c.actions||c.actions.length===0)return;if(c.actions.includes(s)){if(c.targets&&c.targets.length>0){if(!i)return;if(!c.targets.includes(i))continue}return}}let n=i?`:${i}`:"",a=new Error(`Function bindings do not allow: ${t}.${r}.${s}${n}`);throw a.cause={statusCode:403},a}async function B(e,t,r,s,i,o,n){if(!t)return null;let a=e.get("user");if(!a){let l=e.req.header("authorization");if(!l){let f=new Error("Unauthorized: No authentication provided");throw f.cause={statusCode:401},f}let u=l.replace(/^Bearer\s+/i,"");if(a=await t.getUserFromToken(u),!a){let f=new Error("Unauthorized: Invalid token");throw f.cause={statusCode:401},f}}p(a,r,{});let c=e.req.header("x-function-bindings");if(c)try{let l=JSON.parse(c);Ts(l,s,i,o,n)}catch(l){if(l.cause?.statusCode===403)throw l;let u=new Error("Invalid function bindings header");throw u.cause={statusCode:400},u}return a}async function Rs(e,t,r){try{let s=await A(e),i=e.get("requestingIdentity");if(!i)return e.json({error:"Authentication required"},401);let{name:o,description:n,redirectUris:a,metadata:c}=s;if(!o||!a)return e.json({error:"Missing required fields: name and redirectUris are required"},400);await B(e,r,"applications.application.create","applications","application","create",o);let l={name:o,description:n,redirectUris:Array.isArray(a)?a:[a],metadata:c,owners:[i.id]},u=await t.createApplication(l,i.id);return e.json(u,201)}catch(s){let{message:i,status:o}=m(s,"Error creating application");return e.text(i,o)}}async function Ps(e,t,r){try{let s=e.req.param("id"),i=e.get("requestingIdentity");if(!s)return e.json({error:"Application ID is required"},400);await B(e,r,"applications.application.read","applications","application","read",s);let o=await t.getApplication(s,i?.id);return e.json(o,200)}catch(s){let{message:i,status:o}=m(s,"Error retrieving application");return e.text(i,o)}}async function js(e,t,r){try{let s=e.get("requestingIdentity");if(!s)return e.json({error:"Authentication required"},401);await B(e,r,"applications.application.list","applications","application","list");let i=await t.listApplications(s.id);return e.json({applications:i,total:i.length},200)}catch(s){let{message:i,status:o}=m(s,"Error listing applications");return e.text(i,o)}}async function Ls(e,t,r){try{let s=e.req.param("id"),i=await A(e),o=e.get("requestingIdentity");if(!o)return e.json({error:"Authentication required"},401);if(!s)return e.json({error:"Application ID is required"},400);await B(e,r,"applications.application.update","applications","application","update",s);let n=await t.updateApplication(s,i,o.id);return e.json(n,200)}catch(s){let{message:i,status:o}=m(s,"Error updating application");return e.text(i,o)}}async function $s(e,t,r){try{let s=e.req.param("id"),i=e.get("requestingIdentity");return i?s?(await B(e,r,"applications.application.delete","applications","application","delete",s),await t.deleteApplication(s,i.id),e.json({success:!0,message:"Application deleted"},200)):e.json({error:"Application ID is required"},400):e.json({error:"Authentication required"},401)}catch(s){let{message:i,status:o}=m(s,"Error deleting application");return e.text(i,o)}}function Ms(e,t,r){if(r.startsWith("/databases/")){r.includes("/table")||r.includes("/get")?p(e,"databases.table.get",{}):r.includes("/write")||r.includes("/update")||t==="PUT"||t==="PATCH"?p(e,"databases.table.update",{}):r.includes("/delete")||t==="DELETE"?p(e,"databases.table.delete",{}):t==="POST"&&p(e,"databases.table.create",{});return}if(r.startsWith("/functions/")){r.includes("/deploy")||t==="POST"?p(e,"functions.function.create",{}):r.includes("/run/")?p(e,"functions.function.execute",{}):t==="PUT"||t==="PATCH"?p(e,"functions.function.update",{}):t==="DELETE"?p(e,"functions.function.delete",{}):t==="GET"&&p(e,"functions.function.get",{});return}if(r.startsWith("/storage/")){let s=r.match(/^\/storage\/buckets\/[^/]+$/),i=r.includes("/objects");s?t==="POST"?p(e,"storage.bucket.create",{}):t==="PATCH"||t==="PUT"?p(e,"storage.bucket.update",{}):t==="DELETE"?p(e,"storage.bucket.delete",{}):t==="GET"&&p(e,"storage.bucket.get",{}):i||r.includes("/upload")||r.includes("/download")||r.includes("/get")?r.includes("/upload")||t==="POST"?p(e,"storage.object.create",{}):r.includes("/download")||r.includes("/get")?p(e,"storage.object.get",{}):t==="PUT"||t==="PATCH"?p(e,"storage.object.update",{}):t==="DELETE"?p(e,"storage.object.delete",{}):t==="GET"&&p(e,"storage.object.get",{}):t==="GET"&&p(e,"storage.bucket.get",{});return}if(r.startsWith("/sites/")){if(r.match(/^\/sites\/projects\/[^/]+/)&&t==="GET")return;r.includes("/deploy")||t==="POST"?p(e,"sites.site.create",{}):t==="PUT"||t==="PATCH"?p(e,"sites.site.update",{}):t==="DELETE"?p(e,"sites.site.delete",{}):t==="GET"&&p(e,"sites.site.get",{});return}if(r.startsWith("/observability/")){t==="POST"?p(e,"observability.metrics.create",{}):t==="GET"&&p(e,"observability.metrics.get",{});return}}async function Os(e){let{auth:t,db:r,config:s,molnos:i}=e,o=new Ke(t,r,i.initialUser);await o.start();let n=Rt(i.dataPath,"applications"),a=new Ze({dbPath:n});await a.start();let c=Rt(i.dataPath,"management"),l=new Ye({dbPath:c});await l.start();let u=!1,f=Rt(i.dataPath,".runtime-config.json");l.setEnvironmentVariables({IDENTITY_API_URL:`http://${s.host||"localhost"}:${s.port||3e3}`,MOLNOS_RUNTIME_CONFIG:f});let h=new Et;h.use("/*",async(d,E)=>u?d.text("Service Unavailable - Server is shutting down",503):E()),h.use("/*",Rr({origin:d=>d,allowMethods:["GET","POST","PUT","PATCH","DELETE","OPTIONS"],allowHeaders:["Content-Type","Authorization"],exposeHeaders:["Content-Length"],credentials:!0})),h.get("/health",async d=>d.text("OK")),h.post("/auth/login",d=>Gr(d,o,t,a)),h.get("/auth/verify",d=>Kr(d,t,a)),h.post("/auth/refresh",d=>Jr(d,t)),h.post("/management/service",g,d=>Ss(d,l)),h.get("/management/services",g,d=>ys(d,l)),h.get("/management/service/:serviceName",g,d=>vs(d,l)),h.put("/management/service/:serviceName",g,d=>Es(d,l)),h.delete("/management/service/:serviceName",g,d=>Cs(d,l)),h.get("/management/service/:serviceName/start",g,d=>xs(d,l)),h.get("/management/service/:serviceName/stop",g,d=>Is(d,l)),h.get("/management/service/:serviceName/logs",g,d=>As(d,l)),h.get("/management/service/:serviceName/restart",g,d=>ks(d,l)),h.get("/identity/whoami",g,d=>Xr(d)),h.get("/identity/identities",g,d=>Yr(d,o)),h.post("/identity/users",g,d=>Qr(d,o)),h.get("/identity/users",g,d=>es(d,o)),h.get("/identity/users/:userId",g,d=>ts(d,o)),h.patch("/identity/users/:userId",g,d=>ss(d,o)),h.delete("/identity/users/:userId",g,d=>is(d,o)),h.post("/identity/service-accounts",g,d=>os(d,o)),h.get("/identity/service-accounts",g,d=>ns(d,o)),h.get("/identity/service-accounts/:serviceAccountId",g,d=>as(d,o)),h.patch("/identity/service-accounts/:serviceAccountId",g,d=>ls(d,o)),h.delete("/identity/service-accounts/:serviceAccountId",g,d=>us(d,o)),h.post("/identity/service-accounts/:serviceAccountId/rotate-key",g,d=>ds(d,o)),h.post("/identity/roles",g,d=>ps(d,o)),h.get("/identity/roles",g,d=>hs(d,o)),h.get("/identity/roles/:roleId",g,d=>fs(d,o)),h.patch("/identity/roles/:roleId",g,d=>ms(d,o)),h.delete("/identity/roles/:roleId",g,d=>gs(d,o)),h.post("/applications",g,d=>Rs(d,a,o)),h.get("/applications",g,d=>js(d,a,o)),h.get("/applications/:id",g,d=>Ps(d,a,o)),h.patch("/applications/:id",g,d=>Ls(d,a,o)),h.delete("/applications/:id",g,d=>$s(d,a,o)),h.all("/databases/*",g,C,async d=>d.text("Not Found",404)),h.all("/functions/*",C,async d=>d.text("Not Found",404)),h.get("/sites/projects/:projectId/*",C,async d=>d.text("Not Found",404)),h.all("/sites/*",g,C,async d=>d.text("Not Found",404));async function y(d,E){let k=d.req.method,D=new URL(d.req.url).pathname.match(/^\/storage\/buckets\/([^/]+)/);if(D&&(k==="GET"||k==="HEAD")){let P=D[1],j=(await l.getServices()).find(q=>q.name==="storage");if(j?.active)try{let q=await fetch(`http://localhost:${j.port}/buckets/${P}`);if(q.ok&&(await q.json()).public===!0)return E()}catch{}}return g(d,E)}h.all("/storage/*",y,C,async d=>d.text("Not Found",404)),h.all("/observability/*",g,C,async d=>d.text("Not Found",404));async function C(d,E){try{let k=new URL(d.req.url),R=k.pathname,D=k.search,P=d.req.method,$,j=0,q=await l.getServices();for(let F of q)F.active&&R.startsWith(F.prefix)&&F.prefix.length>j&&($=F,j=F.prefix.length);if(!$)return E();let xe=d.get("user");if(!(R.match(/^\/sites\/projects\/[^/]+/)&&P==="GET")&&xe&&Ms(xe,P,R),$.healthCheck&&$.healthCheck.healthy===!1)return d.json({error:"Service unavailable (unhealthy)"},503);let Pt=(R.substring($.prefix.length)||"/")+D;console.log(`Proxying to http://localhost:${$.port}${Pt}`);let U=null;if(["POST","PUT","PATCH","DELETE"].includes(d.req.method))if((d.req.header("content-type")||"").includes("application/json"))try{let z=await d.req.json();U=JSON.stringify(z)}catch{U=null}else try{let z=await d.req.arrayBuffer();U=Buffer.from(z)}catch{U=null}return new Promise(F=>{let z={host:`localhost:${$.port}`};d.req.raw.headers.forEach((V,de)=>{de.toLowerCase()!=="host"&&(z[de]=V)}),U&&(typeof U=="string"?z["content-length"]=Buffer.byteLength(U).toString():z["content-length"]=U.length.toString());let Vs={method:d.req.method,hostname:"localhost",port:$.port,path:Pt,headers:z},Qe=Do.request(Vs,V=>{let de=[];V.on("data",et=>{de.push(et)}),V.on("end",()=>{let et=Buffer.concat(de),jt={};Object.entries(V.headers).forEach(([Ns,Ie])=>{Ie!==void 0&&(jt[Ns]=Array.isArray(Ie)?Ie.join(", "):Ie)}),F(d.body(et,V.statusCode||200,jt))})});Qe.on("error",V=>{console.log("Proxy error:",V.message),F(d.json({error:"Proxy Error",message:V.message},502))}),U&&Qe.write(U),Qe.end()})}catch(k){return I(d,k)}}async function g(d,E){try{let k=d.req.header("authorization");if(!k)return d.text("Unauthorized",401);let R=await o.getUserFromToken(k);return R?(d.set("user",R),d.set("requestingIdentity",R),E()):d.text("Unauthorized",401)}catch(k){return I(d,k)}}function I(d,E){let k=E.message||E,R=E?.cause?.statusCode||400;return d.json({error:k},R)}let T=Hr({fetch:h.fetch,port:s.port||3e3,hostname:s.host||"localhost"});console.log(`Server started on ${s.host||"localhost"}:${s.port||3e3}`);async function H(){if(u){console.log("[Server] Shutdown already in progress...");return}u=!0,console.log("[Server] Initiating graceful shutdown...");try{console.log("[Server] Stopping HTTP server..."),await new Promise((d,E)=>{T.close(k=>{k?E(k):d()})}),console.log("[Server] Stopping managed services..."),await l.shutdown(),console.log("[Server] Closing database connections..."),await r.close(),console.log("[Server] Graceful shutdown complete")}catch(d){throw console.error("[Server] Error during shutdown:",d),d}}return{server:T,shutdown:H,processManager:l,identity:o}}var Uo="molnosid",Bo={name:Uo,length:12,urlSafe:!0},Ds=()=>{let e=Ho(process.env.DEBUG)||!1;return{auth:{jwtSecret:process.env.AUTH_JWT_SECRET||"your-jwt-secret",magicLinkExpirySeconds:900,jwtExpirySeconds:900,refreshTokenExpirySeconds:10080*60,maxActiveSessions:3,consoleUrl:process.env.CONSOLE_URL||"http://127.0.0.1:8000",debug:e},email:{emailSubject:"Your Secure Login Link",user:process.env.EMAIL_USER||"",host:process.env.EMAIL_HOST||"",password:process.env.EMAIL_PASSWORD||"",port:465,secure:!0,maxRetries:2,debug:e},storage:{databaseDirectory:"molnosdb",encryptionKey:process.env.STORAGE_KEY||"",debug:e},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"localhost",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",allowedDomains:["http://127.0.0.1:8080"],debug:e},molnos:{dataPath:process.env.DATA_PATH||"data",initialUser:{id:process.env.INITIAL_USER_ID||new _().add(Bo),userName:process.env.INITIAL_USER_NAME||"",email:process.env.INITIAL_USER_EMAIL||""}}}};function Ho(e){return e==="true"||e===!0}var v=Ds(),Us={configFilePath:"molnos.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:v.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:v.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:v.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:v.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:v.auth.maxActiveSessions},{flag:"--consoleUrl",path:"auth.consoleUrl",defaultValue:v.auth.consoleUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:v.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:v.email.host},{flag:"--emailUser",path:"email.user",defaultValue:v.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:v.email.password},{flag:"--emailPort",path:"email.port",defaultValue:v.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:v.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:v.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:v.email.debug},{flag:"--db",path:"storage.databaseDirectory",defaultValue:v.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:v.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:v.storage.debug},{flag:"--port",path:"server.port",defaultValue:v.server.port},{flag:"--host",path:"server.host",defaultValue:v.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:v.server.useHttps},{flag:"--http2",path:"server.useHttp2",isFlag:!0,defaultValue:v.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:v.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:v.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:v.server.sslCa},{flag:"--allowed",path:"server.allowedDomains",defaultValue:v.server.allowedDomains,parser:J.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:v.server.debug},{flag:"--data-path",path:"dataPath",defaultValue:v.molnos.dataPath},{flag:"--initialUserId",path:"molnos.initialUser.id",defaultValue:v.molnos.initialUser.id},{flag:"--initialUserName",path:"molnos.initialUser.userName",defaultValue:v.molnos.initialUser.userName},{flag:"--initialUserEmail",path:"molnos.initialUser.email",defaultValue:v.molnos.initialUser.email}]};function Bs(e){return{...e,auth:{...e.auth,templates:{textVersion:(t,r,s)=>{let i=new URL(t),o=i.searchParams.get("token")||"",n=i.searchParams.get("email")||"",a=s?.appName||"MolnOS",c=s?.redirectUrl&&s?.applicationId,l=c?`Sign in to ${a}`:"Sign in to MolnOS",u=c?`You're signing in to ${a}. Click the link below to complete the authentication process.`:"Click the link below to sign in to your account.";return`
|
|
116
|
-
${l}
|
|
111
|
+
`},Or=class{constructor(t){this.options=t}sentEmails=[];async sendMail(t){this.sentEmails.push(t),this.options?.logToConsole&&(console.log("Email sent:"),console.log(`From: ${t.from}`),console.log(`To: ${t.to}`),console.log(`Subject: ${t.subject}`),console.log(`Text: ${t.text}`)),this.options?.onSend&&this.options.onSend(t)}getSentEmails(){return[...this.sentEmails]}clearSentEmails(){this.sentEmails=[]}},Ur=class{data=new Map;collections=new Map;expiryEmitter=new Tr;expiryCheckInterval;constructor(t=1e3){this.expiryCheckInterval=setInterval(()=>this.checkExpiredItems(),t)}destroy(){clearInterval(this.expiryCheckInterval),this.data.clear(),this.collections.clear(),this.expiryEmitter.removeAllListeners()}checkExpiredItems(){let t=Date.now();for(let[e,r]of this.data.entries())r.expiry&&r.expiry<t&&(this.data.delete(e),this.expiryEmitter.emit("expired",e));for(let[e,r]of this.collections.entries())r.expiry&&r.expiry<t&&(this.collections.delete(e),this.expiryEmitter.emit("expired",e))}async set(t,e,r){let i=r?Date.now()+r*1e3:null;this.data.set(t,{value:e,expiry:i})}async get(t){let e=this.data.get(t);return e?e.expiry&&e.expiry<Date.now()?(this.data.delete(t),null):e.value:null}async delete(t){this.data.delete(t),this.collections.delete(t)}async addToCollection(t,e,r){this.collections.has(t)||this.collections.set(t,{items:[],expiry:r?Date.now()+r*1e3:null});let i=this.collections.get(t);i&&(r&&(i.expiry=Date.now()+r*1e3),i.items.push(e))}async removeFromCollection(t,e){let r=this.collections.get(t);r&&(r.items=r.items.filter(i=>i!==e))}async getCollection(t){let e=this.collections.get(t);return e?[...e.items]:[]}async getCollectionSize(t){let e=this.collections.get(t);return e?e.items.length:0}async removeOldestFromCollection(t){let e=this.collections.get(t);return!e||e.items.length===0?null:e.items.shift()||null}async findKeys(t){let e=t.replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${e}$`),i=Array.from(this.data.keys()).filter(o=>r.test(o)),s=Array.from(this.collections.keys()).filter(o=>r.test(o));return[...new Set([...i,...s])]}};function Ge(t){if(!t||t.trim()===""||(t.match(/@/g)||[]).length!==1)return!1;let[e,r]=t.split("@");return!(!e||!r||t.includes("..")||!Lr(e)||!Hr(r))}function Lr(t){return t.startsWith('"')&&t.endsWith('"')?!t.slice(1,-1).includes('"'):t.length>64||t.startsWith(".")||t.endsWith(".")?!1:/^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$/.test(t)}function Hr(t){if(t.startsWith("[")&&t.endsWith("]")){let r=t.slice(1,-1);return r.startsWith("IPv6:")?Vr(r.slice(5)):Dr(r)}let e=t.split(".");if(e.length===0)return!1;for(let r of e)if(!r||r.length>63||!/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(r))return!1;if(e.length>1){let r=e[e.length-1];if(!/^[a-zA-Z]{2,}$/.test(r))return!1}return!0}function Dr(t){return/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/.test(t)}function Vr(t){if(!/^[a-fA-F0-9:]+$/.test(t))return!1;let e=t.split(":");return!(e.length<2||e.length>8)}var b=$e(),Nr=t=>{let e={configFilePath:"mikroauth.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:b.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:b.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:b.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:b.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:b.auth.maxActiveSessions},{flag:"--appUrl",path:"auth.appUrl",defaultValue:b.auth.appUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:b.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:b.email.host},{flag:"--emailUser",path:"email.user",defaultValue:b.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:b.email.password},{flag:"--emailPort",path:"email.port",defaultValue:b.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:b.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:b.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:b.email.debug},{flag:"--dir",path:"storage.databaseDirectory",defaultValue:b.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:b.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:b.storage.debug},{flag:"--port",path:"server.port",defaultValue:b.server.port},{flag:"--host",path:"server.host",defaultValue:b.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:b.server.useHttps},{flag:"--https",path:"server.useHttp2",isFlag:!0,defaultValue:b.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:b.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:b.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:b.server.sslCa},{flag:"--ratelimit",path:"server.rateLimit.enabled",defaultValue:b.server.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"server.rateLimit.requestsPerMinute",defaultValue:b.server.rateLimit.requestsPerMinute},{flag:"--allowed",path:"server.allowedDomains",defaultValue:b.server.allowedDomains,parser:q.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:b.server.debug}]};return t&&(e.config=t),e},de={linkSent:"If a matching account was found, a magic link has been sent.",revokedSuccess:"All other sessions revoked successfully.",logoutSuccess:"Logged out successfully."},Je=class{config;email;storage;jwtService;templates;constructor(t,e,r){let i=new z(Nr({auth:t.auth,email:t.email})).get();i.auth.debug&&console.log("Using configuration:",i),this.config=i,this.email=e||new Or,this.storage=r||new Ur,this.jwtService=new jr(i.auth.jwtSecret),this.templates=new $r(i?.auth.templates),this.checkIfUsingDefaultCredentialsInProduction()}checkIfUsingDefaultCredentialsInProduction(){process.env.NODE_ENV==="production"&&this.config.auth.jwtSecret===$e().auth.jwtSecret&&(console.error("WARNING: Using default secrets in production environment!"),process.exit(1))}generateToken(t){let e=Date.now().toString(),r=_.randomBytes(32).toString("hex");return _.createHash("sha256").update(`${t}:${e}:${r}`).digest("hex")}generateJsonWebToken(t){return this.jwtService.sign({sub:t.id,email:t.email,username:t.username,role:t.role,exp:Math.floor(Date.now()/1e3)+3600*24})}generateRefreshToken(){return _.randomBytes(40).toString("hex")}async trackSession(t,e,r){let i=`sessions:${t}`;if(await this.storage.getCollectionSize(i)>=this.config.auth.maxActiveSessions){let s=await this.storage.removeOldestFromCollection(i);s&&await this.storage.delete(`refresh:${s}`)}await this.storage.addToCollection(i,e,this.config.auth.refreshTokenExpirySeconds),await this.storage.set(`refresh:${e}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds)}generateMagicLinkUrl(t){let{token:e,email:r,appUrl:i}=t,s=i||this.config.auth.appUrl;try{return new Pr(s),`${s}?token=${encodeURIComponent(e)}&email=${encodeURIComponent(r)}`}catch{throw new Error("Invalid base URL configuration")}}async createMagicLink(t){let{email:e,ip:r,metadata:i,appUrl:s,subject:o}=t;if(!Ge(e))throw new Error("Valid email required");try{let n=this.generateToken(e),a=`magic_link:${n}`,c={email:e,ipAddress:r||"unknown",createdAt:Date.now()};await this.storage.set(a,JSON.stringify(c),this.config.auth.magicLinkExpirySeconds);let d=await this.storage.findKeys("magic_link:*");for(let f of d){if(f===a)continue;let v=await this.storage.get(f);if(v)try{JSON.parse(v).email===e&&await this.storage.delete(f)}catch{}}let p=this.generateMagicLinkUrl({token:n,email:e,appUrl:s}),u=Math.ceil(this.config.auth.magicLinkExpirySeconds/60);return await this.email.sendMail({from:this.config.email.user,to:e,subject:o||this.config.email.emailSubject,text:this.templates.getText(p,u,i),html:this.templates.getHtml(p,u,i)}),{message:de.linkSent}}catch(n){throw console.error(`Failed to process magic link request: ${n}`),new Error("Failed to process magic link request")}}async createToken(t){let{email:e,username:r,role:i,ip:s}=t;if(!Ge(e))throw new Error("Valid email required");try{let o=_.randomBytes(16).toString("hex"),n=this.generateRefreshToken(),a=Date.now(),c={sub:e,username:r,role:i,jti:o,lastLogin:a,metadata:{ip:s||"unknown"},exp:Math.floor(Date.now()/1e3)+3600*24},d=this.jwtService.sign(c,{exp:this.config.auth.jwtExpirySeconds}),p={email:e,username:r,role:i,ipAddress:s||"unknown",tokenId:o,createdAt:a,lastLogin:a};return await this.trackSession(e,n,p),{accessToken:d,refreshToken:n,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(o){throw console.error("Token creation error:",o),new Error("Token creation failed")}}async verifyToken(t){let{token:e,email:r}=t;try{let i=`magic_link:${e}`,s=await this.storage.get(i);if(!s)throw new Error("Invalid or expired token");let o=JSON.parse(s);if(o.email!==r)throw new Error("Email mismatch");let n=o.username,a=o.role;await this.storage.delete(i);let c=_.randomBytes(16).toString("hex"),d=this.generateRefreshToken(),p={sub:r,username:n,role:a,jti:c,lastLogin:o.createdAt,metadata:{ip:o.ipAddress},exp:Math.floor(Date.now()/1e3)+3600*24},u=this.jwtService.sign(p,{exp:this.config.auth.jwtExpirySeconds});return await this.trackSession(r,d,{...o,tokenId:c,createdAt:Date.now()}),{accessToken:u,refreshToken:d,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(i){throw console.error("Token verification error:",i),new Error("Verification failed")}}async refreshAccessToken(t){try{let e=await this.storage.get(`refresh:${t}`);if(!e)throw new Error("Invalid or expired refresh token");let r=JSON.parse(e),i=r.email;if(!i)throw new Error("Invalid refresh token data");let s=r.username,o=r.role,n=_.randomBytes(16).toString("hex"),a={sub:i,username:s,role:o,jti:n,lastLogin:r.lastLogin||r.createdAt,metadata:{ip:r.ipAddress}},c=this.jwtService.sign(a,{exp:this.config.auth.jwtExpirySeconds});return r.lastUsed=Date.now(),await this.storage.set(`refresh:${t}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds),{accessToken:c,refreshToken:t,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(e){throw console.error("Token refresh error:",e),new Error("Token refresh failed")}}verify(t){try{return this.jwtService.verify(t)}catch{throw new Error("Invalid token")}}async logout(t){try{if(!t||typeof t!="string")throw new Error("Refresh token is required");let e=await this.storage.get(`refresh:${t}`);if(!e)return{message:de.logoutSuccess};let r=JSON.parse(e).email;if(!r)throw new Error("Invalid refresh token data");await this.storage.delete(`refresh:${t}`);let i=`sessions:${r}`;return await this.storage.removeFromCollection(i,t),{message:de.logoutSuccess}}catch(e){throw console.error("Logout error:",e),new Error("Logout failed")}}async getSessions(t){try{if(!t.user?.email)throw new Error("User not authenticated");let e=t.user.email,r=t.body?.refreshToken,i=`sessions:${e}`,s=(await this.storage.getCollection(i)).map(async n=>{try{let a=await this.storage.get(`refresh:${n}`);if(!a)return await this.storage.removeFromCollection(i,n),null;let c=JSON.parse(a);return{id:`${n.substring(0,8)}...`,createdAt:c.createdAt||0,lastLogin:c.lastLogin||c.createdAt||0,lastUsed:c.lastUsed||c.createdAt||0,metadata:{ip:c.ipAddress},isCurrentSession:n===r}}catch{return await this.storage.removeFromCollection(i,n),null}}),o=(await Promise.all(s)).filter(Boolean);return o.sort((n,a)=>a.createdAt-n.createdAt),{sessions:o}}catch(e){throw console.error("Get sessions error:",e),new Error("Failed to fetch sessions")}}async revokeSessions(t){try{if(!t.user?.email)throw new Error("User not authenticated");let e=t.user.email,r=t.body?.refreshToken,i=`sessions:${e}`,s=await this.storage.getCollection(i);for(let o of s)r&&o===r||await this.storage.delete(`refresh:${o}`);return await this.storage.delete(i),r&&await this.storage.get(`refresh:${r}`)&&await this.storage.addToCollection(i,r,this.config.auth.refreshTokenExpirySeconds),{message:de.revokedSuccess}}catch(e){throw console.error("Revoke sessions error:",e),new Error("Failed to revoke sessions")}}authenticate(t,e){try{let r=t.headers?.authorization;if(!r||!r.startsWith("Bearer "))throw new Error("Authentication required");let i=r.split(" ")[1];try{let s=this.verify(i);t.user={email:s.sub},e()}catch{throw new Error("Invalid or expired token")}}catch(r){e(r)}}},Fr=class{key;algorithm="aes-256-gcm";keyLength=32;constructor(t){this.key=Ar(t,"mikroauth-salt",this.keyLength)}encrypt(t){let e=Ir(12),r=Er(this.algorithm,this.key,e),i=Buffer.concat([r.update(t,"utf8"),r.final()]),s=r.getAuthTag();return`${e.toString("hex")}:${s.toString("hex")}:${i.toString("hex")}`}decrypt(t){let e=t.split(":");if(e.length!==3)throw new Error("Invalid encrypted data format");let[r,i,s]=e,o=Buffer.from(r,"hex"),n=Buffer.from(i,"hex"),a=Buffer.from(s,"hex"),c=kr(this.algorithm,this.key,o);return c.setAuthTag(n),Buffer.concat([c.update(a),c.final()]).toString("utf8")}},Xe=class{db;encryption;PREFIX_KV="kv:";PREFIX_COLLECTION="coll:";TABLE_NAME="mikroauth";constructor(t,e){this.db=t,e&&(this.encryption=new Fr(e))}async start(){await this.db.start()}async close(){await this.db.close()}async set(t,e,r){let i=`${this.PREFIX_KV}${t}`,s=this.encryption?this.encryption.encrypt(e):e,o=r?Date.now()+r*1e3:void 0;await this.db.write(this.TABLE_NAME,i,s,o)}async get(t){let e=`${this.PREFIX_KV}${t}`,r=await this.db.get(this.TABLE_NAME,e);return r?this.encryption?this.encryption.decrypt(r):r:null}async delete(t){let e=`${this.PREFIX_KV}${t}`;await this.db.delete(this.TABLE_NAME,e)}async addToCollection(t,e,r){let i=`${this.PREFIX_COLLECTION}${t}`,s=await this.db.get(this.TABLE_NAME,i),o=[];if(s){let d=this.encryption?this.encryption.decrypt(s):s;o=JSON.parse(d)}o.includes(e)||o.push(e);let n=JSON.stringify(o),a=this.encryption?this.encryption.encrypt(n):n,c=r?Date.now()+r*1e3:void 0;await this.db.write(this.TABLE_NAME,i,a,c)}async removeFromCollection(t,e){let r=`${this.PREFIX_COLLECTION}${t}`,i=await this.db.get(this.TABLE_NAME,r);if(!i)return;let s=this.encryption?this.encryption.decrypt(i):i,o=JSON.parse(s);o=o.filter(c=>c!==e);let n=JSON.stringify(o),a=this.encryption?this.encryption.encrypt(n):n;await this.db.write(this.TABLE_NAME,r,a)}async getCollection(t){let e=`${this.PREFIX_COLLECTION}${t}`,r=await this.db.get(this.TABLE_NAME,e);if(!r)return[];let i=this.encryption?this.encryption.decrypt(r):r;return JSON.parse(i)}async getCollectionSize(t){return(await this.getCollection(t)).length}async removeOldestFromCollection(t){let e=`${this.PREFIX_COLLECTION}${t}`,r=await this.db.get(this.TABLE_NAME,e);if(!r)return null;let i=this.encryption?this.encryption.decrypt(r):r,s=JSON.parse(i);if(s.length===0)return null;let o=s.shift(),n=JSON.stringify(s),a=this.encryption?this.encryption.encrypt(n):n;return await this.db.write(this.TABLE_NAME,e,a),o}async findKeys(t){let e=t.replace(/\./g,"\\.").replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${e}$`),i=await this.db.get(this.TABLE_NAME);return Array.isArray(i)?i.filter(s=>{let o=s[0];return typeof o=="string"&&o.startsWith(this.PREFIX_KV)}).map(s=>s[0].substring(this.PREFIX_KV.length)).filter(s=>r.test(s)):[]}},Ye=class{email;sender;constructor(t){this.sender=t.user,this.email=new je({config:t})}async sendMail(t){await this.email.send({from:this.sender,to:t.to,cc:t.cc,bcc:t.bcc,subject:t.subject,text:t.text,html:t.html})}};function ue(t){if(!t.deflate&&!t.inflate)throw new Error("Dictionary must provide either deflate or inflate mapping");if(t.deflate&&t.inflate)throw new Error("Dictionary should provide only one of deflate or inflate (not both). The inverse will be auto-generated.");return t.deflate?{deflate:t.deflate,inflate:Ze(t.deflate)}:{deflate:Ze(t.inflate),inflate:t.inflate}}function Ze(t){let e={};for(let[r,i]of Object.entries(t))e[i]=r;return e}function Z(t,e){if(t==null||typeof t!="object")return t;if(Array.isArray(t))return t.map(i=>Z(i,e));let r={};for(let[i,s]of Object.entries(t)){let o=e[i]||i;r[o]=Z(s,e)}return r}function W(t){if(!t||typeof t!="string")throw new Error("Table name must be a non-empty string");if(t.length>255)throw new Error("Table name must not exceed 255 characters");if(t.includes("/")||t.includes("\\"))throw new Error("Table name must not contain path separators");if(t.includes(".."))throw new Error('Table name must not contain ".."');if(t.startsWith("."))throw new Error('Table name must not start with "."');if(t.includes("\0"))throw new Error("Table name must not contain null bytes");if(["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"].includes(t.toUpperCase()))throw new Error(`Table name "${t}" is reserved by the filesystem`)}function pe(t){if(t==null)throw new Error("Key must be defined");if(typeof t!="string")throw new Error("Key must be a string");if(t.length===0)throw new Error("Key must not be empty");if(t.length>1024)throw new Error("Key must not exceed 1024 characters");if(t.includes("\0"))throw new Error("Key must not contain null bytes")}function Qe(t){if(t===void 0)throw new Error("Value must not be undefined (use null instead)");let e=typeof t;if(e==="function")throw new Error("Value must be JSON-serializable: functions are not supported");if(e==="symbol")throw new Error("Value must be JSON-serializable: symbols are not supported");try{if(JSON.stringify(t)===void 0)throw new Error("Value must be JSON-serializable: value cannot be serialized")}catch(r){throw new Error(`Value must be JSON-serializable: ${r instanceof Error?r.message:String(r)}`)}}import{existsSync as Me,mkdirSync as Br,readdirSync as zr,openSync as qr,closeSync as _r}from"fs";import{readFile as Wr,writeFile as Kr,rename as Gr,unlink as et,open as Jr}from"fs/promises";import{join as Oe,dirname as Xr}from"path";var K=class{data=new Map;databaseDirectory;dictionaries=new Map;useFsync;constructor(t){this.databaseDirectory=t.databaseDirectory,this.useFsync=t.durableWrites??!1,t.dictionaries&&Object.entries(t.dictionaries).forEach(([e,r])=>{let i=ue(r);this.dictionaries.set(e,i)}),Me(this.databaseDirectory)||Br(this.databaseDirectory,{recursive:!0})}async start(){try{let t=zr(this.databaseDirectory);for(let e of t)!e.endsWith(".tmp")&&!e.startsWith(".")&&await this.loadTable(e)}catch(t){throw console.error("Failed to start database:",t),t}}async write(t,e,r,i,s){if(W(t),pe(e),Qe(r),s&&!this.dictionaries.has(s))throw new Error(`Dictionary "${s}" not found. Available dictionaries: ${Array.from(this.dictionaries.keys()).join(", ")||"none"}`);try{this.data.has(t)||this.data.set(t,new Map);let o=this.data.get(t),a=(o.get(e)?.version||0)+1,c={value:r,version:a,timestamp:Date.now(),expiration:i||null,dictionaryName:s||void 0};return o.set(e,c),await this.persistTable(t),!0}catch(o){return console.error(`Write failed for ${t}:${e}:`,o),!1}}async get(t,e){W(t),e!==void 0&&pe(e);try{this.data.has(t)||await this.loadTable(t);let r=this.data.get(t);if(!r)return e?void 0:[];if(e!==void 0){let o=r.get(e);if(!o)return;if(this.isExpired(o)){r.delete(e),await this.persistTable(t);return}return o.value}let i=[],s=[];for(let[o,n]of r.entries())this.isExpired(n)?s.push(o):i.push([o,n.value]);if(s.length>0){for(let o of s)r.delete(o);await this.persistTable(t)}return i}catch(r){return console.error(`Read failed for ${t}:${e}:`,r),e?void 0:[]}}async delete(t,e){W(t),pe(e);try{this.data.has(t)||await this.loadTable(t);let r=this.data.get(t);if(!r||!r.has(e))return!1;let i=r.get(e);return i?this.isExpired(i)?(r.delete(e),await this.persistTable(t),!1):(r.delete(e),await this.persistTable(t),!0):!1}catch(r){return console.error(`Delete failed for ${t}:${e}:`,r),!1}}async getTableSize(t){W(t);try{this.data.has(t)||await this.loadTable(t);let e=this.data.get(t);if(!e)return 0;let r=0,i=[];for(let[s,o]of e.entries())this.isExpired(o)?i.push(s):r++;if(i.length>0){for(let s of i)e.delete(s);await this.persistTable(t)}return r}catch(e){return console.error(`Get table size failed for ${t}:`,e),0}}isExpired(t){return t.expiration===null?!1:Date.now()>t.expiration}async cleanupExpired(t){W(t);try{this.data.has(t)||await this.loadTable(t);let e=this.data.get(t);if(!e)return 0;let r=[];for(let[i,s]of e.entries())this.isExpired(s)&&r.push(i);for(let i of r)e.delete(i);return r.length>0&&await this.persistTable(t),r.length}catch(e){return console.error(`Cleanup failed for ${t}:`,e),0}}async cleanupAllExpired(){let t=0;for(let e of this.data.keys())t+=await this.cleanupExpired(e);return t}async deleteTable(t){W(t);try{this.data.delete(t);let e=Oe(this.databaseDirectory,t);return Me(e)&&await et(e),!0}catch(e){return console.error(`Delete table failed for ${t}:`,e),!1}}listTables(){return Array.from(this.data.keys())}async flush(){try{let t=Array.from(this.data.keys()).map(e=>this.persistTable(e));await Promise.all(t)}catch(t){throw console.error("Flush failed:",t),t}}async close(){await this.flush()}addDictionary(t,e){let r=ue(e);this.dictionaries.set(t,r)}removeDictionary(t){return this.dictionaries.delete(t)}listDictionaries(){return Array.from(this.dictionaries.keys())}async loadTable(t){let e=Oe(this.databaseDirectory,t);if(!Me(e)){this.data.set(t,new Map);return}try{let r=await Wr(e);if(r.length===0){this.data.set(t,new Map);return}let i=this.deserializeTable(r);this.data.set(t,i)}catch(r){console.error(`Failed to load table ${t}:`,r),this.data.set(t,new Map)}}async persistTable(t){let e=this.data.get(t);if(!e)return;let r=this.serializeTable(e),i=Oe(this.databaseDirectory,t),s=`${i}.tmp.${Date.now()}.${Math.random().toString(36).substring(7)}`;try{if(await Kr(s,r),this.useFsync){let o=await Jr(s,"r+");try{await o.sync()}finally{await o.close()}}if(await Gr(s,i),this.useFsync){let o=Xr(i),n=qr(o,"r");try{_r(n)}catch{}}}catch(o){try{await et(s)}catch{}throw o}}serializeTable(t){let e=Array.from(t.entries()).map(([r,i])=>{let s=i.dictionaryName?this.dictionaries.get(i.dictionaryName):void 0,o={d:s?Z(i.value,s.deflate):i.value,v:i.version,t:i.timestamp,x:i.expiration};return i.dictionaryName&&(o.n=i.dictionaryName),[r,o]});return Buffer.from(JSON.stringify(e),"utf8")}deserializeTable(t){let r=JSON.parse(t.toString("utf8")).map(([i,s])=>{let o=s.n,n=o?this.dictionaries.get(o):void 0;return[i,{value:n?Z(s.d,n.inflate):s.d,version:s.v,timestamp:s.t,expiration:s.x,dictionaryName:o||void 0}]});return new Map(r)}};import{join as ki}from"node:path";import wi from"node:http";import{join as De}from"node:path";var tt=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",i=this.requests.get(r);return(!i||i.resetTime<e)&&(i={count:0,resetTime:e+this.windowMs},this.requests.set(r,i)),i.count++,i.count<=this.limit}getRemainingRequests(t){let e=Date.now(),r=t||"unknown",i=this.requests.get(r);return!i||i.resetTime<e?this.limit:Math.max(0,this.limit-i.count)}getResetTime(t){let e=Date.now(),r=t||"unknown",i=this.requests.get(r);return!i||i.resetTime<e?Math.floor((e+this.windowMs)/1e3):Math.floor(i.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 Yr}from"url";var rt=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(),i=e;return this.register("GET",t,r,i),this.register("POST",t,r,i),this.register("PUT",t,r,i),this.register("DELETE",t,r,i),this.register("PATCH",t,r,i),this.register("OPTIONS",t,r,i),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 i=this.pathPatterns.get(r.path);if(!i)continue;let s=i.pattern.exec(e);if(!s)continue;let o={};return i.paramNames.forEach((n,a)=>{o[n]=s[a+1]||""}),{route:r,params:o}}return null}async handle(t,e){let r=t.method||"GET",i=new Yr(t.url||"/",`http://${t.headers.host}`),s=i.pathname,o=this.match(r,s);if(!o)return null;let{route:n,params:a}=o,c={};i.searchParams.forEach((u,f)=>{c[f]=u});let d={req:t,res:e,params:a,query:c,body:t.body||{},headers:t.headers,path:s,state:{},raw:()=>e,binary:(u,f="application/octet-stream",v=200)=>({statusCode:v,body:u,headers:{"Content-Type":f,"Content-Length":u.length.toString()},isRaw:!0}),text:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"text/plain"}}),form:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"application/json"}}),html:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"text/html"}}),redirect:(u,f=302)=>({statusCode:f,body:null,headers:{Location:u}}),status:function(u){return{raw:()=>e,binary:(f,v="application/octet-stream")=>({statusCode:u,body:f,headers:{"Content-Type":v,"Content-Length":f.length.toString()},isRaw:!0}),text:f=>({statusCode:u,body:f,headers:{"Content-Type":"text/plain"}}),json:f=>({statusCode:u,body:f,headers:{"Content-Type":"application/json"}}),html:f=>({statusCode:u,body:f,headers:{"Content-Type":"text/html"}}),form:f=>({statusCode:u,body:f,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(f,v=302)=>({statusCode:v,body:null,headers:{Location:f}}),status:f=>this.status(f)}}},p=[...this.globalMiddlewares,...n.middlewares];return this.executeMiddlewareChain(d,p,n.handler)}register(t,e,r,i=[]){return this.routes.push({method:t,path:e,handler:r,middlewares:i}),this.pathPatterns.set(e,this.createPathPattern(e)),this}createPathPattern(t){let e=[],r=t.replace(/\/:[^/]+/g,i=>{let s=i.slice(2);return e.push(s),"/([^/]+)"});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 i=0,s=async()=>{if(i<e.length){let o=e[i++];return o(t,s)}return r(t)};return s()}};var he=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:Zr(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function Zr(t){return t==="true"||t===!0}var R=he(),it=t=>({configFilePath:"mikroserve.config.json",args:process.argv,options:[{flag:"--port",path:"port",defaultValue:R.port},{flag:"--host",path:"host",defaultValue:R.host},{flag:"--https",path:"useHttps",defaultValue:R.useHttps,isFlag:!0},{flag:"--http2",path:"useHttp2",defaultValue:R.useHttp2,isFlag:!0},{flag:"--cert",path:"sslCert",defaultValue:R.sslCert},{flag:"--key",path:"sslKey",defaultValue:R.sslKey},{flag:"--ca",path:"sslCa",defaultValue:R.sslCa},{flag:"--ratelimit",path:"rateLimit.enabled",defaultValue:R.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"rateLimit.requestsPerMinute",defaultValue:R.rateLimit.requestsPerMinute},{flag:"--allowed",path:"allowedDomains",defaultValue:R.allowedDomains,parser:q.array},{flag:"--debug",path:"debug",defaultValue:R.debug,isFlag:!0},{flag:"--max-body-size",path:"maxBodySize",defaultValue:R.maxBodySize},{flag:"--request-timeout",path:"requestTimeout",defaultValue:R.requestTimeout}],config:t});function st(t,e){let r=e.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!r)throw new Error("Invalid multipart/form-data: missing boundary");let i=r[1]||r[2],s=Buffer.from(`--${i}`),o=Buffer.from(`--${i}--`),n={},a={},c=Qr(t,s);for(let d of c){if(d.length===0||d.equals(o.subarray(s.length)))continue;let p=ei(d);if(!p)continue;let{name:u,filename:f,contentType:v,data:T}=p;if(f){let S={filename:f,contentType:v||"application/octet-stream",data:T,size:T.length};a[u]?Array.isArray(a[u])?a[u].push(S):a[u]=[a[u],S]:a[u]=S}else{let S=T.toString("utf8");n[u]?Array.isArray(n[u])?n[u].push(S):n[u]=[n[u],S]:n[u]=S}}return{fields:n,files:a}}function Qr(t,e){let r=[],i=0;for(;i<t.length;){let s=t.indexOf(e,i);if(s===-1)break;i!==s&&r.push(t.subarray(i,s)),i=s+e.length,i<t.length&&t[i]===13&&t[i+1]===10&&(i+=2)}return r}function ei(t){let e=Buffer.from(`\r
|
|
112
|
+
\r
|
|
113
|
+
`),r=t.indexOf(e);if(r===-1)return null;let i=t.subarray(0,r),s=t.subarray(r+4),n=i.toString("utf8").split(`\r
|
|
114
|
+
`),a="",c="",d,p;for(let f of n){let v=f.toLowerCase();if(v.startsWith("content-disposition:")){a=f.substring(20).trim();let T=a.match(/name="([^"]+)"/);T&&(c=T[1]);let S=a.match(/filename="([^"]+)"/);S&&(d=S[1])}else v.startsWith("content-type:")&&(p=f.substring(13).trim())}if(!c)return null;let u=s;return u.length>=2&&u[u.length-2]===13&&u[u.length-1]===10&&(u=u.subarray(0,u.length-2)),{disposition:a,name:c,filename:d,contentType:p,data:u}}import{readFileSync as Q}from"fs";import Ue from"http";import ti from"http2";import ri from"https";var Le=class{config;rateLimiter;router;shutdownHandlers=[];constructor(t){let e=new z(it(t||{})).get();e.debug&&console.log("Using configuration:",e),this.config=e,this.router=new rt;let r=e.rateLimit.requestsPerMinute||he().rateLimit.requestsPerMinute;this.rateLimiter=new tt(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 i=t.address(),s=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${s}://${i.address!=="::"?i.address:"localhost"}:${i.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:Q(this.config.sslKey),cert:Q(this.config.sslCert),...this.config.sslCa?{ca:Q(this.config.sslCa)}:{}};return ti.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:Q(this.config.sslKey),cert:Q(this.config.sslCert),...this.config.sslCa?{ca:Q(this.config.sslCa)}:{}};return ri.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 Ue.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(),i=t.method||"UNKNOWN",s=t.url||"/unknown",o=this.config.debug;try{if(this.setCorsHeaders(e,t),this.setSecurityHeaders(e,this.config.useHttps),o&&console.log(`${i} ${s}`),t.method==="OPTIONS"){if(e instanceof Ue.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 o&&console.error("Body parsing error:",a.message),this.respond(e,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let n=await this.router.handle(t,e);return n?n._handled?void 0:this.respond(e,n):this.respond(e,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(n){return console.error("Server error:",n),this.respond(e,{statusCode:500,body:{error:"Internal Server Error",message:o?n.message:"An unexpected error occurred"}})}finally{o&&this.logDuration(r,i,s)}}logDuration(t,e,r){let i=Date.now()-t;console.log(`${e} ${r} completed in ${i}ms`)}async parseBody(t){return new Promise((e,r)=>{let i=[],s=0,o=this.config.maxBodySize,n=!1,a=null,c=this.config.debug,d=t.headers["content-type"]||"";c&&console.log("Content-Type:",d),this.config.requestTimeout>0&&(a=setTimeout(()=>{n||(n=!0,c&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let p=()=>{a&&(clearTimeout(a),a=null)};t.on("data",u=>{if(!n){if(s+=u.length,c&&console.log(`Received chunk: ${u.length} bytes, total size: ${s}`),s>o){n=!0,p(),c&&console.log(`Body size exceeded limit: ${s} > ${o}`),r(new Error("Request body too large"));return}i.push(u)}}),t.on("end",()=>{if(!n){n=!0,p(),c&&console.log(`Request body complete: ${s} bytes`);try{if(i.length>0){let u=Buffer.concat(i);if(d.includes("application/json"))try{let f=u.toString("utf8");e(JSON.parse(f))}catch(f){r(new Error(`Invalid JSON in request body: ${f.message}`))}else if(d.includes("application/x-www-form-urlencoded")){let f=u.toString("utf8"),v={};new URLSearchParams(f).forEach((T,S)=>{v[S]=T}),e(v)}else if(d.includes("multipart/form-data"))try{let f=st(u,d);e(f)}catch(f){r(new Error(`Invalid multipart form data: ${f.message}`))}else this.isBinaryContentType(d)?e(u):e(u.toString("utf8"))}else e({})}catch(u){r(new Error(`Invalid request body: ${u}`))}}}),t.on("error",u=>{n||(n=!0,p(),r(new Error(`Error reading request body: ${u.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:i=["*"]}=this.config;!r||i.length===0||i.includes("*")?t.setHeader("Access-Control-Allow-Origin","*"):i.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 Ue.ServerResponse)Object.entries(r).forEach(([i,s])=>{t.setHeader(i,s)});else{let i=t;Object.entries(r).forEach(([s,o])=>{i.setHeader(s,o)})}}respond(t,e){let r={...e.headers||{}};(s=>typeof s.writeHead=="function"&&typeof s.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=n=>{console.log("Shutting down MikroServe server..."),n&&console.error("Error:",n),this.cleanupShutdownHandlers(),t.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(n?1:0))})},r=()=>e(),i=()=>e(),s=n=>e(n),o=n=>e(n);this.shutdownHandlers=[r,i,s,o],process.on("SIGINT",r),process.on("SIGTERM",i),process.on("uncaughtException",s),process.on("unhandledRejection",o)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[t,e,r,i]=this.shutdownHandlers;process.removeListener("SIGINT",t),process.removeListener("SIGTERM",e),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",i),this.shutdownHandlers=[]}}};import{getRandomValues as ii}from"node:crypto";var D=class{options;defaultLength=16;defaultOnlyLowerCase=!1;defaultStyle="extended";defaultUrlSafe=!0;constructor(t){if(this.options={},t)for(let[e,r]of Object.entries(t))this.options[e]=this.generateConfig(r)}add(t){if(!t?.name)throw new Error("Missing name for the ID configuration");let e=this.generateConfig(t);this.options[t.name]=e}remove(t){if(!t?.name)throw new Error("Missing name for the ID configuration");delete this.options[t.name]}create(t,e,r,i){let s=this.generateConfig({length:t,style:e,onlyLowerCase:r,urlSafe:i});return this.generateId(s)}custom(t){if(this.options[t])return this.generateId(this.options[t]);throw new Error(`No configuration found with name: ${t}`)}generateConfig(t){return{name:t?.name||"",length:t?.length||this.defaultLength,onlyLowerCase:t?.onlyLowerCase??this.defaultOnlyLowerCase,style:t?.style||this.defaultStyle,urlSafe:t?.urlSafe??this.defaultUrlSafe}}getCharacterSet(t,e,r){if(t==="hex")return e?"0123456789abcdef":"0123456789ABCDEFabcdef";if(t==="alphanumeric")return e?"abcdefghijklmnopqrstuvwxyz0123456789":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";if(t==="extended")return r?e?"abcdefghijklmnopqrstuvwxyz0123456789-._~":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~":e?"abcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:";throw new Error(`Unknown ID style "${t} provided. Must be one of "extended" (default), "alphanumeric", or "hex".`)}generateId(t){let{length:e,onlyLowerCase:r,style:i,urlSafe:s}=t;if(e<0||e===0)throw new Error("ID length cannot be negative");let o=this.getCharacterSet(i,r,s),n=(2<<Math.log(o.length-1)/Math.LN2)-1,a=Math.ceil(1.6*n*e/o.length),c="";for(;c.length<e;){let d=new Uint8Array(a);ii(d);for(let p=0;p<a;p++){let u=d[p]&n;if(u<o.length&&(c+=o[u],c.length===e))break}}return c}};import si from"node:crypto";function nt(t,e,r){if(!r)return!1;let i=typeof t=="string"?[t]:t,s=typeof e=="string"?[e]:e;if(r.roles?.some(a=>a==="administrator"||a.id==="administrator"))return!0;let n=(r.roles||[]).flatMap(a=>typeof a=="string"?[]:(a.policies||[]).flatMap(d=>d?.permissions&&d?.targets?{permissions:d.permissions,targets:d.targets}:[]));for(let a of i)for(let c of s)if(!n.some(p=>ot(p?.permissions,a)&&ot(p?.targets,c)))return!1;return!0}function ot(t,e){return!t||t.length===0?!1:t.some(r=>{if(r==="*"||r===e)return!0;if(r.endsWith("*")&&r!=="*"){let i=r.slice(0,-1);return e.startsWith(i)}return!1})}var C=class extends Error{constructor(e){super(),this.name="ValidationError",this.message=e||"Invalid input",this.cause={statusCode:400}}},ae=class extends Error{constructor(e){super(),this.name="IdentityAlreadyExistsError",this.message=e||"Identity already exists",this.cause={statusCode:400}}},F=class extends Error{constructor(e){super(),this.name="NotFoundError",this.message=e||"Resource not found",this.cause={statusCode:404}}},me=class extends Error{constructor(e){super(),this.name="InvalidInputError",this.message=e||"Invalid input",this.cause={statusCode:400}}};var fe=class extends Error{constructor(e){super(),this.name="ConfigurationError",this.message=e||"Invalid configuration",this.cause={statusCode:400}}};var ee=class extends Error{constructor(e){super(),this.name="PermissionDeniedError",this.message=e||"Permission denied",this.cause={statusCode:403}}},B=class extends Error{constructor(e){super(),this.name="AlreadyExistsError",this.message=e||"Resource already exists",this.cause={statusCode:409}}},ge=class extends Error{constructor(e){super(),this.name="PortInUseError",this.message=e||"Port already in use",this.cause={statusCode:409}}},ce=class extends Error{constructor(e){super(),this.name="RoleNotFoundError",this.message=e||"Role not found",this.cause={statusCode:404}}},ye=class extends Error{constructor(e){super(),this.name="ProtectedResourceError",this.message=e||"Cannot modify protected resource",this.cause={statusCode:403}}},ve=class extends Error{constructor(e){super(),this.name="ServiceRequestError",this.message=e||"Service request failed",this.cause={statusCode:502}}};var G=class{id;name;type;roles;metadata;constructor(e){let{id:r,name:i,type:s,roles:o,metadata:n}=this.createIdentity(e);this.id=r,this.name=i,this.type=s,this.roles=o,this.metadata=n}createIdentity(e){let r=e?.id||this.createId(),i=e?.name||"",s=e?.type||"service_account",o=e?.metadata||{},n=e?.roles||["user"];return e&&this.validate({id:r,name:i,type:s,metadata:o,roles:n}),{id:r,name:i,type:s,metadata:{...o,createdAt:new Date().toISOString()},roles:n}}changeName(e){this.name=e}changeEmail(e){this.metadata||(this.metadata={}),this.metadata.email=e}updateMetadata(e){let r=this.metadata?JSON.parse(JSON.stringify(this.metadata)):{};this.metadata={...r,...e}}updateRoles(e){this.validateRoles(e),this.roles=e}createId(){return new D().create()}isValidRoleId(e){return typeof e!="string"||e.length===0?!1:/^[a-z0-9]+(-[a-z0-9]+)*$/.test(e)}isValidType(e){return["user","service_account"].includes(e)}validate(e){let r=e.id||"",i=e.name||"",s=e.type||"",o=e.metadata||{},n=e.roles||[];if(!r)throw new C("Missing ID");if(!i)throw new C("Missing name");if(!s||!this.isValidType(s))throw new C("Missing or invalid type");if(s==="user"&&!o?.email)throw new C("Missing email for user identity");if(!n||n.length===0)throw new C("Must have at least one role");this.validateRoles(n)}validateRoles(e){(e||[]).forEach(r=>{let i=r.id||r;if(!this.isValidRoleId(i))throw new C(`Invalid role ID '${i}'`)})}can(e,r,i){return nt(e,r,i)}fromDTO(e){return this.validate(e),this.id=e.id,this.name=e.name,this.type=e.type,this.metadata=e.metadata||{},this.roles=e.roles,this}toDTO(){return{id:this.id,name:this.name,type:this.type,metadata:this.metadata,roles:this.roles}}};function at(){return[{id:"administrator",name:"Administrator",description:"Full system administrator with all permissions",policies:[{permissions:["*"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!0,audit_level:"high"}}},{id:"user",name:"User",description:"Read-only access to resources",policies:[{permissions:["*.read"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"low"}}}]}var we=class{mikroAuth;db;tableName="identity_service";initialUser;roles;identities;serviceAccountTokens;constructor(e,r,i){this.mikroAuth=e,this.db=r,this.initialUser=i,this.roles=[],this.identities=[],this.serviceAccountTokens=new Map}async start(){await this.loadState(),this.roles.find(e=>e.id==="administrator")||this.createBaseRoles(),this.identities.length===0&&this.createInitialUser()}createBaseRoles(){let e=at();for(let r of e)this.roles.push(r)}async createInitialUser(){let e=this.initialUser.email,r=this.initialUser.userName,i=await this.addUser(e,r,["administrator"]);return await this.mikroAuth.createMagicLink({email:e}),i}async getUserByEmail(e){let r=this.identities.find(i=>i.type==="user"&&i.metadata?.email===e&&i.metadata?.active!==!1);if(r)return this.enrichIdentityWithRoles(r)}async addUser(e,r,i=["user"],s=!0){if((await this.getUsers()).find(c=>c.metadata.email===e))throw new ae(`User with email ${e} already exists`);let a=new G({name:r,type:"user",metadata:{email:e,active:s},roles:i}).toDTO();return this.identities.push(a),await this.saveState(),this.enrichIdentityWithRoles(a)}async addServiceAccount(e,r,i){if((await this.getServiceAccounts()).find(c=>c.name===e))throw new ae(`Service account with name ${e} already exists`);let n=new G({name:e,type:"service_account",metadata:{description:r},roles:i}).toDTO(),a=this.generateServiceAccountToken();return this.serviceAccountTokens.set(a,n.id),this.identities.push(n),await this.saveState(),{...this.enrichIdentityWithRoles(n),apiKey:a}}async rotateServiceAccountKey(e){let r=await this.getIdentityById(e);if(!r||r.type!=="service_account")return;for(let[s,o]of this.serviceAccountTokens.entries())o===e&&this.serviceAccountTokens.delete(s);let i=this.generateServiceAccountToken();return this.serviceAccountTokens.set(i,e),await this.saveState(),i}async deleteIdentity(e){for(let[i,s]of this.serviceAccountTokens.entries())s===e&&this.serviceAccountTokens.delete(i);let r=this.identities.filter(i=>i.id!==e);this.identities=r,await this.saveState()}async updateIdentity(e,r){let i=await this.getIdentityById(e);if(!i)return;let s=new G().fromDTO(i);r?.name&&s.changeName(r.name),r?.email&&s.changeEmail(r.email),r?.roles&&s.updateRoles(r.roles),r?.metadata&&s.updateMetadata(r.metadata);let o=s.toDTO();return this.updateIdentityList(o),await this.saveState(),this.enrichIdentityWithRoles(o)}updateIdentityList(e){if(!e)throw new C("Cannot update identity list: updatedIdentity is null or undefined");let r=JSON.parse(JSON.stringify(this.identities)),i=r.find(s=>s.id===e.id);i&&Object.assign(i,e),this.identities=r}generateServiceAccountToken(){return`sa.${new D().create()}.${si.randomBytes(32).toString("hex")}`}async getUserFromToken(e){if(e&&e.startsWith("Bearer ")){let r=e.split(" ")[1];if(r.startsWith("sa.")){let s=this.serviceAccountTokens.get(r);if(s){let o=this.identities.find(n=>n.id===s&&n.type==="service_account");if(o)return this.enrichIdentityWithRoles(o)}}else try{let s=this.mikroAuth.verify(r),o=s.email||s.sub;return this.getUserByEmail(o)}catch{let o=new Error("Invalid token");throw o.cause={statusCode:401},o}}}async getIdentityById(e){return this.identities.find(r=>r.id===e)}async getUserById(e){let r=await this.getIdentityById(e);if(!(!r||r.type!=="user"))return this.enrichIdentityWithRoles(r)}async getServiceAccountById(e){let r=await this.getIdentityById(e);if(!(!r||r.type!=="service_account"))return this.enrichIdentityWithRoles(r)}async updateUser(e,r){let i=await this.getIdentityById(e);if(!(!i||i.type!=="user"))return this.updateIdentity(e,r)}async updateServiceAccount(e,r){let i=await this.getIdentityById(e);if(!i||i.type!=="service_account")return;let s={...r};return r.description!==void 0&&(s.metadata={...s.metadata||{},description:r.description},delete s.description),this.updateIdentity(e,s)}async deleteUser(e){let r=await this.getIdentityById(e);return!r||r.type!=="user"?!1:(await this.deleteIdentity(e),!0)}async deleteServiceAccount(e){let r=await this.getIdentityById(e);return!r||r.type!=="service_account"?!1:(await this.deleteIdentity(e),!0)}enrichIdentityWithRoles(e){let r=JSON.parse(JSON.stringify(e));return e.roles?r.roles=e.roles.map(i=>this.roles.find(s=>s.id===i)).filter(Boolean):r.roles=[],r}async getIdentities(){return this.identities}async getUsers(){return this.identities.filter(e=>e.type==="user")}async getServiceAccounts(){return this.identities.filter(e=>e.type==="service_account")}can(e,r,i){return new G(i).can(e,r,i)}async createCustomRole(e,r,i,s,o){if(this.roles.find(d=>d.id===e))throw new B(`Role with ID ${e} already exists`);let c={id:e,name:r,description:i,policies:[{permissions:s,targets:["*"]}],constraints:o||{assumable_by:{identities:[],roles:[],services:["functions"]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"medium"}}};this.roles.push(c),await this.saveState()}async updateRole(e,r){let i=this.roles.find(s=>s.id===e);if(!i)throw new ce(`Role with ID ${e} not found`);r.name!==void 0&&(i.name=r.name),r.description!==void 0&&(i.description=r.description),r.permissions!==void 0&&(i.policies=[{permissions:r.permissions,targets:["*"]}]),r.constraints!==void 0&&(i.constraints=r.constraints),await this.saveState()}async deleteRole(e){let r=this.roles.findIndex(i=>i.id===e);if(r===-1)throw new ce(`Role with ID ${e} not found`);if(e==="administrator"||e==="user")throw new ye("Cannot delete base roles");this.roles.splice(r,1),await this.saveState()}async getRoles(){return this.roles}async getRole(e){return this.roles.find(r=>r.id===e)}async loadState(){let e=await this.load("identities");e&&(this.identities=e);let r=await this.load("roles");r&&(this.roles=r);let i=await this.load("serviceAccountTokens");if(i){let s=new Map(Object.entries(i));i&&Object.keys(i).length>0&&(this.serviceAccountTokens=s)}}async saveState(){let e=Object.fromEntries(this.serviceAccountTokens);await this.write("identities",this.identities),await this.write("roles",this.roles),await this.write("serviceAccountTokens",e)}async load(e){return await this.db.get(this.tableName,e)}async write(e,r){await this.db.write(this.tableName,e,r)}};import{existsSync as oi}from"node:fs";import{spawn as ni}from"node:child_process";import{createServer as ai}from"node:net";var ct={storage:{name:"storage",path:"./storage.mjs",port:3001,prefix:"/storage",args:["--force"]},functions:{name:"functions",path:"./functions.mjs",port:3002,prefix:"/functions",args:["--force"]},sites:{name:"sites",path:"./sites.mjs",port:3003,prefix:"/sites",args:["--force"]},databases:{name:"databases",path:"./databases.mjs",port:3004,prefix:"/databases",args:["--force"]},observability:{name:"observability",path:"./observability.mjs",port:3005,prefix:"/observability",args:["--force"]}},Se=class{db;services=new Map;tableName="molnosmanagement";environmentVariables={};setEnvironmentVariables(e){this.environmentVariables={...this.environmentVariables,...e}}async isPortInUse(e){return new Promise(r=>{let i=ai();i.once("error",s=>{s.code==="EADDRINUSE"?r(!0):r(!1)}),i.once("listening",()=>{i.close(),r(!1)}),i.listen(e,"127.0.0.1")})}constructor(e){this.db=new K({databaseDirectory:e.dbPath})}getServiceDefinition(e){let r=e.toLowerCase(),i=ct[r];if(!i)throw new F(`Unknown service: ${e}. Valid services are: ${Object.keys(ct).join(", ")}`);return i}async start(){await this.db.start();let e=await this.db.get(this.tableName,"services")||[];for(let r of e)r.restartPolicy&&(r.restartPolicy.attempts=0),r.healthCheck&&(r.healthCheck.consecutiveFailures=0,r.healthCheck.consecutiveSuccesses=0,r.healthCheck.healthy=!0),this.services.set(r.name,r),r.active&&await this.startService(r.name)}async stopAllServices(){let e=[];for(let[r,i]of this.services.entries())(i.active||i.process)&&e.push(this.stopService(r));await Promise.all(e)}async shutdown(){await this.stopAllServices()}validateServiceConfig(e){if(!e.name||e.name.trim()==="")throw new C("Service name is required");if(!e.path||e.path.trim()==="")throw new C("Service path is required");if(!oi(e.path))throw new fe(`Service path does not exist: ${e.path}`);if(!e.port||e.port<1||e.port>65535)throw new C(`Invalid port: ${e.port}`);if(!e.prefix||!e.prefix.startsWith("/"))throw new C("Service prefix must start with /")}cleanServiceForStorage(e){let{process:r,...i}=e;if(i.healthCheck){let{timer:s,consecutiveFailures:o,consecutiveSuccesses:n,healthy:a,...c}=i.healthCheck;i.healthCheck=c}if(i.restartPolicy){let{attempts:s,...o}=i.restartPolicy;i.restartPolicy=o}return i}async registerService(e,r){let i;typeof e=="string"?i={...this.getServiceDefinition(e),...r}:i=e,this.validateServiceConfig(i);let s={...i,active:!1},o=await this.getServices(),n=[],a=[];if(o.forEach(d=>{n.push(d.name),a.push(d.port)}),a.includes(s.port))throw new ge(`Port ${s.port} already in use`);if(n.includes(s.name))throw new B(`Service with name '${s.name}' already exists`);s.restartPolicy&&(s.restartPolicy.attempts=0),s.healthCheck&&(s.healthCheck.consecutiveFailures=0,s.healthCheck.consecutiveSuccesses=0,s.healthCheck.healthy=!0),this.services.set(s.name,s);let c=await this.db.get(this.tableName,"services")||[];return c.push(this.cleanServiceForStorage(s)),await this.db.write(this.tableName,"services",c),s.name}async startService(e){let r=this.services.get(e);if(!r)return!1;if(r.process&&r.active)return!0;if(await this.isPortInUse(r.port))return console.error(`[ManagementService] Cannot start ${e}: Port ${r.port} is already in use. Please kill the process using this port.`),!1;try{let s={stdio:"pipe",detached:!1,env:{...process.env,...this.environmentVariables}},o=r.args||[],n=`--port=${r.port}`,a=ni("node",[r.path,n,...o],s);r.process=a,r.restartPolicy&&(r.restartPolicy.attempts=0);let c=!1;return a.stdout.on("data",d=>{let p=d.toString();console.log(`[${e}] ${p}`)}),a.stderr.on("data",d=>{let p=d.toString();console.error(`[${e}] ERROR: ${p}`),(p.includes("EADDRINUSE")||p.includes("address already in use"))&&(c=!0)}),a.on("exit",d=>{r.active=!1,r.process=void 0,c&&console.error(`[ManagementService] Service ${e} failed to start due to startup error`),this.handleServiceExit(r,d||0)}),await new Promise(d=>setTimeout(d,500)),r.process&&!r.process.killed?(r.active=!0,await this.updateServiceStatus(e,!0),r.healthCheck&&this.startHealthCheck(r),!0):(console.error(`[ManagementService] Service ${e} exited immediately after spawn`),!1)}catch(s){return console.error(`[ManagementService] Failed to start ${e}:`,s),!1}}startHealthCheck(e){let r=e.healthCheck;if(!r)return;let i=async()=>{if(e.healthCheck)try{let s=new AbortController,o=setTimeout(()=>s.abort(),e.healthCheck.timeoutMs),n=await fetch(`http://127.0.0.1:${e.port}${e.healthCheck.path}`,{signal:s.signal});clearTimeout(o),n.ok&&e.healthCheck?(e.healthCheck.consecutiveSuccesses=(e.healthCheck.consecutiveSuccesses||0)+1,e.healthCheck.consecutiveFailures=0,e.healthCheck.consecutiveSuccesses>=e.healthCheck.successThreshold&&(e.healthCheck.healthy=!0)):this.handleHealthCheckFailure(e)}catch{this.handleHealthCheckFailure(e)}};r.timer=setInterval(i,r.intervalMs)}handleHealthCheckFailure(e){e.healthCheck&&(e.healthCheck.consecutiveFailures=(e.healthCheck.consecutiveFailures||0)+1,e.healthCheck.consecutiveSuccesses=0,e.healthCheck.consecutiveFailures>=e.healthCheck.failureThreshold&&(e.healthCheck.healthy=!1,e.healthCheck.restartOnFailure&&this.restartService(e.name).catch(()=>{})))}async handleServiceExit(e,r){if(await this.updateServiceStatus(e.name,!1),!e.restartPolicy||e.restartPolicy.type==="never"||e.restartPolicy.type==="on-failure"&&r===0||e.restartPolicy.maxAttempts>0&&(e.restartPolicy.attempts||0)>=e.restartPolicy.maxAttempts)return;e.restartPolicy.attempts=(e.restartPolicy.attempts||0)+1;let i=e.restartPolicy.backoffMs;setTimeout(()=>{this.startService(e.name).catch(()=>{})},i)}async stopService(e){let r=this.services.get(e);if(!r)return!1;if(r.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),!r.process)return r.active&&(r.active=!1,await this.updateServiceStatus(e,!1)),!0;try{let i=s=>{try{return process.kill(s,0),!0}catch{return!1}};if(r.process?.pid){let s=r.process.pid;if(i(s))try{process.kill(s,"SIGTERM");let o=setTimeout(()=>{try{i(s)&&process.kill(s,"SIGKILL")}catch{}},5*1e3);await new Promise(n=>{if(!r.process){clearTimeout(o),n();return}let a=setTimeout(()=>{clearTimeout(o),n()},6*1e3);r.process.once("exit",()=>{clearTimeout(o),clearTimeout(a),n()})})}catch(o){if(o.code!=="ESRCH")throw o}}return r.active=!1,r.process=void 0,await this.updateServiceStatus(e,!1),!0}catch{return r.active&&(r.active=!1,r.process=void 0,await this.updateServiceStatus(e,!1)),!1}}async restartService(e){return await this.stopService(e),await new Promise(r=>setTimeout(r,500)),this.startService(e)}async updateServiceStatus(e,r){let i=await this.db.get(this.tableName,"services")||[],s=i.findIndex(o=>o.name===e);if(s>=0){let o=this.services.get(e);o?(o.active=r,i[s]=this.cleanServiceForStorage(o)):i[s].active=r,await this.db.write(this.tableName,"services",i)}}async getService(e){return(await this.getServices()).find(i=>i.name===e)}async getServices(){return(await this.db.get(this.tableName,"services")||[]).map(({process:r,...i})=>i)}async getServiceStats(e){let r=this.services.get(e);return r?{name:r.name,active:r.active,health:r.healthCheck?{healthy:r.healthCheck.healthy||!1,consecutiveSuccesses:r.healthCheck.consecutiveSuccesses||0,consecutiveFailures:r.healthCheck.consecutiveFailures||0}:null,restarts:r.restartPolicy&&r.restartPolicy.attempts||0}:null}async updateService(e,r){let i=this.services.get(e);if(!i)return!1;let s=i.active;Object.assign(i,r);let o=await this.db.get(this.tableName,"services")||[],n=o.findIndex(a=>a.name===e);return n>=0?(o[n]=this.cleanServiceForStorage(i),await this.db.write(this.tableName,"services",o),s!==i.active&&(i.active?await this.startService(e):await this.stopService(e)),!0):!1}async removeService(e){let r=this.services.get(e);r?.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),await this.stopService(e),this.services.delete(e);let s=(await this.db.get(this.tableName,"services")||[]).filter(o=>o.name!==e);return await this.db.write(this.tableName,"services",s),!0}};var V=class{id;name;description;redirectUris;metadata;owners;constructor(e){let r=this.createApplication(e);this.id=r.id,this.name=r.name,this.description=r.description,this.redirectUris=r.redirectUris,this.metadata=r.metadata,this.owners=r.owners}createApplication(e){let r=e?.id||this.createId(),i=e?.name||"",s=e?.description,o=e?.redirectUris||[],n=e?.owners||[],a=new Date().toISOString(),c={...e?.metadata||{},createdAt:e?.metadata?.createdAt||a,updatedAt:a,createdBy:e?.metadata?.createdBy||n[0]||""};return e&&this.validate({id:r,name:i,description:s,redirectUris:o,metadata:c,owners:n}),{id:r,name:i,description:s,redirectUris:o,metadata:c,owners:n}}update(e){e.name!==void 0&&(this.name=e.name),e.description!==void 0&&(this.description=e.description),e.redirectUris!==void 0&&(this.validateRedirectUris(e.redirectUris),this.redirectUris=e.redirectUris),e.owners!==void 0&&(this.owners=e.owners),e.metadata!==void 0?this.metadata={...this.metadata,...e.metadata,updatedAt:new Date().toISOString()}:this.metadata.updatedAt=new Date().toISOString(),this.validate(this.toDTO())}isValidRedirectUri(e){return this.redirectUris.includes(e)}isOwner(e){return this.owners.includes(e)}createId(){return new D().create()}validate(e){let{id:r,name:i,redirectUris:s,metadata:o,owners:n}=e;if(!r)throw new C("Missing ID");if(!i||i.trim().length===0)throw new C("Missing or invalid name");if(!o)throw new C("Missing metadata");if(!n||n.length===0)throw new C("Application must have at least one owner");this.validateRedirectUris(s)}validateRedirectUris(e){if(!e||e.length===0)throw new C("At least one redirect URI is required");for(let r of e)try{let i=new URL(r);if(i.protocol!=="https:"&&i.protocol!=="http:"&&i.protocol!=="custom:")throw new C(`Invalid redirect URI protocol: ${r}. Must use https:, http: (localhost only), or custom: (for native apps)`);i.protocol==="http:"&&!i.hostname.match(/^(localhost|127\.0\.0\.1|\[::1\])$/)&&console.warn(`Warning: HTTP redirect URI detected for non-localhost: ${r}. HTTPS is recommended for production.`)}catch{throw new C(`Invalid redirect URI format: ${r}`)}}fromDTO(e){return this.validate(e),this.id=e.id,this.name=e.name,this.description=e.description,this.redirectUris=e.redirectUris,this.metadata=e.metadata,this.owners=e.owners,this}toDTO(){return{id:this.id,name:this.name,description:this.description,redirectUris:this.redirectUris,metadata:this.metadata,owners:this.owners}}};var be=class{db;tableName="molnosapplications";key="applications";applications;constructor(e){this.db=new K({databaseDirectory:e.dbPath}),this.applications=[]}async start(){await this.db.start(),await this.loadState()}async loadState(){let e=await this.db.get(this.tableName,this.key)||[];this.applications=e}async saveState(){await this.db.write(this.tableName,this.key,this.applications)}async createApplication(e,r){let i=e.owners.includes(r)?e.owners:[...e.owners,r],o=new V({...e,owners:i,metadata:{...e.metadata,createdBy:r}}).toDTO();if(this.applications.find(a=>a.name===o.name))throw new B(`Application with name '${o.name}' already exists`);return this.applications.push(o),await this.saveState(),o}async getApplication(e,r){let i=this.applications.find(o=>o.id===e);if(!i)throw new F(`Application with ID '${e}' not found`);let s=new V().fromDTO(i);if(r&&!s.isOwner(r))throw new ee("Only application owners can view application details");return s.toDTO()}async getApplicationById(e){return this.applications.find(i=>i.id===e)||null}async listApplications(e){return this.applications.filter(r=>r.owners.includes(e)).map(r=>new V().fromDTO(r).toDTO())}async updateApplication(e,r,i){let s=this.applications.findIndex(a=>a.id===e);if(s===-1)throw new F(`Application with ID '${e}' not found`);let o=this.applications[s],n=new V().fromDTO(o);if(!n.isOwner(i))throw new ee("Only application owners can update the application");if(r.owners&&r.owners.length===0)throw new C("Application must have at least one owner");if(r.owners&&!r.owners.includes(i)&&o.owners.length===1&&o.owners[0]===i)throw new C("Cannot remove yourself as the last owner of the application");return n.update(r),this.applications[s]=n.toDTO(),await this.saveState(),this.applications[s]}async deleteApplication(e,r){let i=this.applications.findIndex(n=>n.id===e);if(i===-1)throw new F(`Application with ID '${e}' not found`);let s=this.applications[i];if(!new V().fromDTO(s).isOwner(r))throw new ee("Only application owners can delete the application");this.applications.splice(i,1),await this.saveState()}async validateRedirectUri(e,r){let i=await this.getApplicationById(e);return i?new V().fromDTO(i).isValidRedirectUri(r):!1}getAllApplicationsInternal(){return this.applications}};async function I(t){return t.body||{}}function m(t,e="An error occurred"){let r=t?.message||t||e,i=t?.cause?.statusCode||t?.statusCode||400;return{message:r,status:i}}async function lt(t,e,r,i){try{let s=await I(t),{email:o,redirectUrl:n,applicationId:a}=s;if(!o)return t.json({error:"Email is required"},400);let c=o.trim().toLowerCase(),d;if(n){if(!a)return t.json({error:"applicationId is required when redirectUrl is provided"},400);if(!await i.validateRedirectUri(a,n))return t.json({error:"Invalid redirectUrl or applicationId. The redirect URI must be registered for this application."},400);d=(await i.getApplicationById(a))?.name}let p=!1;return await e.getUserByEmail(c)&&(p=!0),p&&await r.createMagicLink({email:c,subject:`Sign In To ${d||"MolnOS"}`,appUrl:n,metadata:{appName:d,redirectUrl:n,applicationId:a}}),t.json({success:!0,message:"If a matching account was found, a magic link has been sent."})}catch(s){let{message:o,status:n}=m(s,"Error during login");return t.json({error:o},n)}}async function dt(t,e,r){try{let i=t.query.token,s=t.query.email;if(!i)return t.json({error:"Token is required as query parameter"},400);if(!s)return t.json({error:"Email is required as query parameter"},400);try{let o=await e.verifyToken({token:i,email:s});if(!o)return t.json({error:"Invalid or expired token"},401);let n=o.metadata;if(n?.redirectUrl&&n?.applicationId)if(await r.validateRedirectUri(n.applicationId,n.redirectUrl)){let c=new URL(n.redirectUrl);return c.searchParams.set("access_token",o.accessToken),c.searchParams.set("refresh_token",o.refreshToken),c.searchParams.set("expires_in",o.expiresIn.toString()),t.redirect(c.toString(),302)}else return console.warn(`Redirect URL validation failed for applicationId: ${n.applicationId}`),t.json({...o,warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);return t.json(o,200)}catch{return t.json({error:"Invalid or expired token"},401)}}catch(i){let{message:s,status:o}=m(i,"Error verifying token");return t.json({error:s},o)}}async function ut(t,e){try{let i=(await I(t)).refreshToken;if(!i)return t.json({error:"Value for refreshToken is required"},400);try{let s=await e.refreshAccessToken(i);return t.json(s,200)}catch{return t.json({error:"Invalid or expired token"},401)}}catch(r){let{message:i,status:s}=m(r,"Error refreshing token");return t.json({error:i},s)}}async function pt(t){try{let e=t.state.user;return t.json(e)}catch(e){let{message:r,status:i}=m(e,"Error getting user info");return t.json({error:r},i)}}function h(t,e,r={}){let i=Array.isArray(e)?e:[e];if(!t||!t.roles||!Array.isArray(t.roles)){let n=new Error("Unauthorized: User or roles missing");throw n.cause={statusCode:403},n}let s=t.roles.flatMap(n=>n?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(d=>typeof d=="string"?d:d&&typeof d=="object"&&d.actions?d.actions:[]))||[]);if(!i.every(n=>s.includes(n)||s.includes("*")?!0:s.some(a=>{if(a.endsWith(".*")){let c=a.slice(0,-2);return n.startsWith(`${c}.`)}return!1}))){let n=new Error("Unauthorized");throw n.cause={statusCode:403},n}return!0}async function ht(t,e){try{let r=t.state.user;h(r,"identity.identities.get",{});let i=await e.getIdentities();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting identities");return t.json({error:i},s)}}var O=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:i}=this.validate(t.properties,e),s=this.compileErrors(r,i),o=this.isSuccessful(r,s);return{errors:s,success:o}}compileErrors(t,e){let r=t.filter(i=>i.success===!1);return[...e,...r].flatMap(i=>i)}isSuccessful(t,e){return t.every(r=>r.success===!0)&&e.length===0}validate(t,e,r=[],i=[]){let s=t?.additionalProperties??!0,o=t?.required||[];i=this.checkForRequiredKeysErrors(o,e,i),i=this.checkForDisallowedProperties(Object.keys(e),Object.keys(t),i,s);for(let n in t){let a=o.includes(n)&&n!=="required",c=t[n],d=e[n],p=c.additionalProperties??!0;a&&(i=this.checkForRequiredKeysErrors(c.required||[],d,i)),this.isDefined(d)&&(this.handleValidation(n,d,c,r),i=this.checkForDisallowedProperties(Object.keys(d),Object.keys(c),i,p),this.handleNestedObject(d,c,r,i))}return{results:r,errors:i}}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 i=e?Object.keys(e):[],s=this.findNonOverlappingElements(t,i),o=s.length>0?`Missing the required key: '${s.join(", ")}'!`:`Missing values for required keys: '${i.filter(n=>!e[n]).join(", ")}'!`;r.push({key:"",value:e,success:!1,error:o})}return r}checkForDisallowedProperties(t,e,r,i){if(!i){let s=this.findNonOverlappingElements(t,e);s.length>0&&r.push({key:`${e}`,value:t,success:!1,error:`Has additional (disallowed) properties: '${s.join(", ")}'!`})}return r}handleValidation(t,e,r,i){this.updatePropertyPath(t);let s=this.validateProperty(this.propertyPath,r,e);i.push(...s);let o=(a,c)=>{a.forEach(d=>{let p=this.validateProperty(this.propertyPath,c.items,d);i.push(...p)}),this.updatePropertyPath()},n=a=>{let c=Object.keys(a),d=this.propertyPath;c.forEach(p=>{if(this.updatePropertyPath(p,d),this.isArray(a[p])&&r[p]?.items!=null)o(a[p],r[p]);else{let u=this.validateProperty(this.propertyPath,r[p],a[p]);i.push(...u)}})};this.isArray(e)&&r.items!=null?o(e,r):this.isObject(e)?n(e):this.updatePropertyPath()}handleNestedObject(t,e,r,i){if(this.isObject(t)){let s=this.getNestedObjects(t);for(let o of s){let n=e[o],a=t[o];n&&typeof a=="object"&&this.validate(n,a,r,i)}}}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(s=>{let{success:o,error:n}=s;return{key:t,value:r,success:o,error:n??""}})}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"}],i=[];for(let s of r)s.condition()&&!s.validator()&&i.push({success:!1,error:s.error});return i}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 i=t[r];e.properties.required.push(r),Array.isArray(i)?e.properties[r]=this.generateArraySchema(i):typeof i=="object"&&i!==null?e.properties[r]=this.generateNestedObjectSchema(i):e.properties[r]=this.generatePropertySchema(i)}return e}generateArraySchema(t){let e={type:"array"},r=t.filter(i=>i);if(r.length>0){let i=r[0];r.every(o=>typeof o==typeof i)?typeof i=="object"&&!Array.isArray(i)?e.items=this.generateNestedObjectSchema(i):e.items=this.generatePropertySchema(i):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 i=t[r];e.required.push(r),typeof i=="object"&&!Array.isArray(i)&&i!==null?e[r]=this.generateNestedObjectSchema(i):e[r]=this.generatePropertySchema(i)}return e}generatePropertySchema(t){let e=typeof t,r={type:e};return e==="string"&&(r.minLength=1),r}};var mt={properties:{email:{type:"string",format:"email",minLength:3},name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}},verified:{type:"boolean"}},required:["email","name","roles"],additionalProperties:!1};var li=new O;async function ft(t,e){try{let r=t.state.user,i=await I(t),s=li.test(mt,i);if(!s.success)return t.json({error:"Invalid input",details:s.errors},400);h(r,"identity.user.create",{});let{email:o,name:n,roles:a,verified:c}=i,d=await e.addUser(o,n,a,c);return t.json(d,201)}catch(r){let{message:i,status:s}=m(r,"Error creating user");return t.json({error:i},s)}}async function gt(t,e){try{let r=t.state.user;h(r,"identity.user.get",{});let i=await e.getUsers();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting users");return t.json({error:i},s)}}function x(t){return t.params}async function yt(t,e){try{let r=t.state.user,i=x(t),{userId:s}=i;h(r,"identity.user.get",{userId:s});let o=await e.getUserById(s);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting user");return t.json({error:i},s)}}var vt={properties:{name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var ui=new O;async function wt(t,e){try{let r=t.state.user,i=x(t),{userId:s}=i,o=await I(t),n=ui.test(vt,o);if(!n.success)return t.json({error:"Invalid input",details:n.errors},400);h(r,"identity.user.update",{userId:s});let{name:a,roles:c}=o,d=await e.updateUser(s,{name:a,roles:c});return d?t.json(d):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error updating user");return t.json({error:i},s)}}async function St(t,e){try{let r=t.state.user,i=x(t),{userId:s}=i;return h(r,"identity.user.delete",{userId:s}),await e.deleteUser(s)?t.body(null,204):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error deleting user");return t.json({error:i},s)}}async function bt(t,e){try{let r=t.state.user,i=await I(t);if(!i.name||!i.description||!i.roles||!Array.isArray(i.roles))return t.json({error:"Invalid input: name, description, and roles are required"},400);if(!["user","administrator"].every(c=>i.roles.includes(c)||!i.roles.includes(c)))return t.json({error:"Invalid input: roles must be user or administrator"},400);h(r,"identity.service-account.create",{});let{name:s,description:o,roles:n}=i,a=await e.addServiceAccount(s,o,n);return t.json(a,201)}catch(r){let{message:i,status:s}=m(r,"Error creating service account");return t.json({error:i},s)}}async function xt(t,e){try{let r=t.state.user;h(r,"identity.service-account.get",{});let i=await e.getServiceAccounts();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting service accounts");return t.json({error:i},s)}}async function Ct(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i;h(r,"identity.service-account.get",{serviceAccountId:s});let o=await e.getServiceAccountById(s);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting service account");return t.json({error:i},s)}}var At={properties:{name:{type:"string",minLength:1,maxLength:100},description:{type:"string",minLength:1,maxLength:500},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var hi=new O;async function It(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i,o=await I(t),n=hi.test(At,o);if(!n.success)return t.json({error:"Invalid input",details:n.errors},400);h(r,"identity.service-account.update",{serviceAccountId:s});let{name:a,description:c,roles:d}=o,p=await e.updateServiceAccount(s,{name:a,description:c,roles:d});return p?t.json(p):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error updating service account");return t.json({error:i},s)}}async function Et(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i;return h(r,"identity.service-account.delete",{serviceAccountId:s}),await e.deleteServiceAccount(s)?t.body(null,204):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error deleting service account");return t.json({error:i},s)}}async function kt(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i;h(r,"identity.service-account.update",{serviceAccountId:s});let o=await e.rotateServiceAccountKey(s);return o?t.json({apiKey:o}):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error rotating service account key");return t.json({error:i},s)}}async function Pt(t,e){try{let r=t.state.user;h(r,"identity.role.get",{});let i=await e.getRoles();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting roles");return t.json({error:i},s)}}async function Tt(t,e){try{let r=t.state.user;h(r,"identity.role.get",{});let i=t.params.roleId;if(!i)return t.text("Missing role ID",400);let s=await e.getRole(i);return s?t.json(s):t.text("Role not found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting role");return t.json({error:i},s)}}async function Rt(t,e){try{let r=t.state.user;h(r,"identity.role.create",{});let i=await I(t),{roleId:s,name:o,description:n,permissions:a,constraints:c}=i;if(!s||!o||!n)return t.text("Missing required fields: roleId, name, description",400);if(!a||!Array.isArray(a))return t.text("Permissions must be a non-empty array",400);await e.createCustomRole(s,o,n,a,c);let d=await e.getRole(s);return t.json(d,201)}catch(r){let{message:i,status:s}=m(r,"Error creating role");return t.json({error:i},s)}}async function jt(t,e){try{let r=t.state.user;h(r,"identity.role.update",{});let i=t.params.roleId;if(!i)return t.text("Missing role ID",400);let s=await I(t),{name:o,description:n,permissions:a,constraints:c}=s;await e.updateRole(i,{name:o,description:n,permissions:a,constraints:c});let d=await e.getRole(i);return t.json(d)}catch(r){let{message:i,status:s}=m(r,"Error updating role");return t.json({error:i},s)}}async function $t(t,e){try{let r=t.state.user;h(r,"identity.role.delete",{});let i=t.params.roleId;return i?i==="administrator"||i==="user"?t.text("Cannot delete base roles",403):(await e.deleteRole(i),t.json({message:"Role deleted successfully"})):t.text("Missing role ID",400)}catch(r){let{message:i,status:s}=m(r,"Error deleting role");return t.json({error:i},s)}}async function Mt(t,e){try{let r=t.state.user;h(r,"management.services.get",{});let i=await e.getServices();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting services");return t.json({error:i},s)}}async function Ot(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;h(r,"management.service.get",{serviceName:s});let o=await e.getService(s);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting service");return t.json({error:i},s)}}var Ut={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var fi=new O;async function Lt(t,e){try{let r=t.state.user,i=await I(t),s=fi.test(Ut,i);if(!s.success)return t.json({error:"Invalid input",details:s.errors},400);if(h(r,"management.service.create",{}),["storage","functions","sites","databases","observability"].includes(i.name.toLowerCase())){let{name:a,...c}=i;await e.registerService(a,c)}else{if(!i.path||!i.port||!i.prefix)return t.json({error:"Custom services require: name, path, port, and prefix"},400);await e.registerService(i)}return t.text("",201)}catch(r){let{message:i,status:s}=m(r,"Error creating service");return t.json({error:i},s)}}var Ht={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var yi=new O;async function Dt(t,e){try{let r=t.state.user,i=x(t),s=await I(t),o=s.name,n=yi.test(Ht,s);return n.success?(h(r,"management.service.update",{serviceName:o}),o!==i.serviceName?t.json({error:"Service name in body must match path parameter"},400):(await e.updateService(o,s),t.body(null,204))):t.json({error:"Invalid input",details:n.errors},400)}catch(r){let{message:i,status:s}=m(r,"Error updating service");return t.json({error:i},s)}}async function Vt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;return h(r,"management.service.delete",{serviceName:s}),await e.getService(s)?(await e.removeService(s),t.body(null,204)):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error deleting service");return t.json({error:i},s)}}async function Nt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.start",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=await e.startService(s);return t.json({serviceName:s,isStarted:n})}catch(r){let{message:i,status:s}=m(r,"Error starting service");return t.json({error:i},s)}}async function Ft(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.stop",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=await e.stopService(s);return t.json({serviceName:s,isStopped:n})}catch(r){let{message:i,status:s}=m(r,"Error stopping service");return t.json({error:i},s)}}async function Bt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.get",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=new URL("http://127.0.0.1:3005/events");n.searchParams.set("service",s),t.query.startTime&&n.searchParams.set("startTime",t.query.startTime),t.query.endTime&&n.searchParams.set("endTime",t.query.endTime),t.query.level&&n.searchParams.set("level",t.query.level),t.query.limit&&n.searchParams.set("limit",t.query.limit),t.query.offset&&n.searchParams.set("offset",t.query.offset);let a=await fetch(n.toString());if(!a.ok)throw new ve(`Observability service error: ${a.statusText}`);let c=await a.json();return t.json({serviceName:s,logs:c.events,count:c.count})}catch(r){let{message:i,status:s}=m(r,"Error getting service logs");return t.json({error:i},s)}}async function zt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.start",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=await e.restartService(s);return t.json({serviceName:s,restarted:n})}catch(r){let{message:i,status:s}=m(r,"Error restarting service");return t.json({error:i},s)}}function qt(t,e,r,i,s){let o=t.find(c=>c.service===e);if(!o){let c=new Error(`Function bindings do not include access to service: ${e}`);throw c.cause={statusCode:403},c}if(!o.permissions||o.permissions.length===0)return;for(let c of o.permissions)if(!(c.resource&&c.resource!==r)){if(!c.resource||!c.actions||c.actions.length===0)return;if(c.actions.includes(i)){if(c.targets&&c.targets.length>0){if(!s)return;if(!c.targets.includes(s))continue}return}}let n=s?`:${s}`:"",a=new Error(`Function bindings do not allow: ${e}.${r}.${i}${n}`);throw a.cause={statusCode:403},a}async function H(t,e,r,i,s,o,n){if(!e)return null;let a=t.state.user;if(!a){let d=t.headers.authorization;if(!d){let u=new Error("Unauthorized: No authentication provided");throw u.cause={statusCode:401},u}let p=d.replace(/^Bearer\s+/i,"");if(a=await e.getUserFromToken(p),!a){let u=new Error("Unauthorized: Invalid token");throw u.cause={statusCode:401},u}}h(a,r,{});let c=t.headers["x-function-bindings"];if(c)try{let d=Array.isArray(c)?c[0]:c,p=JSON.parse(d);qt(p,i,s,o,n)}catch(d){if(d.cause?.statusCode===403)throw d;let p=new Error("Invalid function bindings header");throw p.cause={statusCode:400},p}return a}async function _t(t,e,r){try{let i=await I(t),s=t.state.requestingIdentity;if(!s)return t.json({error:"Authentication required"},401);let{name:o,description:n,redirectUris:a,metadata:c}=i;if(!o||!a)return t.json({error:"Missing required fields: name and redirectUris are required"},400);await H(t,r,"applications.application.create","applications","application","create",o);let d={name:o,description:n,redirectUris:Array.isArray(a)?a:[a],metadata:c,owners:[s.id]},p=await e.createApplication(d,s.id);return t.json(p,201)}catch(i){let{message:s,status:o}=m(i,"Error creating application");return t.json({error:s},o)}}async function Wt(t,e,r){try{let i=t.params.id,s=t.state.requestingIdentity;if(!i)return t.json({error:"Application ID is required"},400);await H(t,r,"applications.application.read","applications","application","read",i);let o=await e.getApplication(i,s?.id);return t.json(o,200)}catch(i){let{message:s,status:o}=m(i,"Error retrieving application");return t.json({error:s},o)}}async function Kt(t,e,r){try{let i=t.state.requestingIdentity;if(!i)return t.json({error:"Authentication required"},401);await H(t,r,"applications.application.list","applications","application","list");let s=await e.listApplications(i.id);return t.json({applications:s,total:s.length},200)}catch(i){let{message:s,status:o}=m(i,"Error listing applications");return t.json({error:s},o)}}async function Gt(t,e,r){try{let i=t.params.id,s=await I(t),o=t.state.requestingIdentity;if(!o)return t.json({error:"Authentication required"},401);if(!i)return t.json({error:"Application ID is required"},400);await H(t,r,"applications.application.update","applications","application","update",i);let n=await e.updateApplication(i,s,o.id);return t.json(n,200)}catch(i){let{message:s,status:o}=m(i,"Error updating application");return t.json({error:s},o)}}async function Jt(t,e,r){try{let i=t.params.id,s=t.state.requestingIdentity;return s?i?(await H(t,r,"applications.application.delete","applications","application","delete",i),await e.deleteApplication(i,s.id),t.json({success:!0,message:"Application deleted"},200)):t.json({error:"Application ID is required"},400):t.json({error:"Authentication required"},401)}catch(i){let{message:s,status:o}=m(i,"Error deleting application");return t.json({error:s},o)}}async function Xt(t,e,r,i){try{let s=t.headers["x-forwarded-for"],o=(Array.isArray(s)?s[0]:s)?.split(",")[0]?.trim()||t.headers["x-real-ip"]||"unknown";if(!r.checkRateLimit(`oauth:${o}`)){let p=r.getRateLimitInfo(`oauth:${o}`);return t.json({error:"Too many authentication attempts. Please try again later.",retryAfter:Math.ceil((p.reset-Date.now())/1e3)},429)}let n=Array.isArray(t.query.redirect_uri)?t.query.redirect_uri[0]:t.query.redirect_uri,a=Array.isArray(t.query.application_id)?t.query.application_id[0]:t.query.application_id,c=r.generateState(t,i,n,a),d=e.getAuthorizationUrl(c);return t.redirect(d,302)}catch(s){let{message:o,status:n}=m(s,"Error initiating OAuth flow");return t.json({error:o},n)}}async function Yt(t,e,r,i,s,o){try{let n=t.headers["x-forwarded-for"],a=Array.isArray(n)?n[0]:n,c=t.headers["x-real-ip"],d=Array.isArray(c)?c[0]:c,p=a?.split(",")[0]?.trim()||d||"unknown";if(!r.checkRateLimit(`oauth:${p}`)){let T=r.getRateLimitInfo(`oauth:${p}`);return t.json({error:"Too many authentication attempts. Please try again later.",retryAfter:Math.ceil((T.reset-Date.now())/1e3)},429)}let u=r.validateCallback(t,o);if(!u.valid)return t.json({error:u.error||"Invalid OAuth callback"},400);let f=await e.handleCallback(u.code,p),v=await i.getUserByEmail(f.user.email);if(!v)return t.json({error:"User not found. You must be invited before signing in with OAuth."},403);if(await i.updateUser(v.id,{lastLogin:new Date().toISOString()}),u.redirectUrl){if(u.applicationId&&!await s.validateRedirectUri(u.applicationId,u.redirectUrl))return console.warn(`Redirect URL validation failed for applicationId: ${u.applicationId}`),t.json({success:!0,accessToken:f.accessToken,refreshToken:f.refreshToken,expiresIn:f.expiresIn,tokenType:f.tokenType,user:{id:v.id,email:f.user.email,name:f.user.name,username:f.user.username},warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);let T=new URL(u.redirectUrl);return T.searchParams.set("access_token",f.accessToken),T.searchParams.set("refresh_token",f.refreshToken),T.searchParams.set("expires_in",f.expiresIn.toString()),t.redirect(T.toString(),302)}return t.json({success:!0,accessToken:f.accessToken,refreshToken:f.refreshToken,expiresIn:f.expiresIn,tokenType:f.tokenType,user:{id:v.id,email:f.user.email,name:f.user.name,username:f.user.username}})}catch(n){let{message:a,status:c}=m(n,"OAuth authentication failed");return t.json({error:a},c)}}async function Zt(t,e){try{let r=Array.from(e.values()).map(i=>i.getPublicInfo());return t.json({providers:r,count:r.length})}catch(r){let{message:i,status:s}=m(r,"Error listing OAuth providers");return t.json({error:i},s)}}var xe=class{constructor(e,r){this.config=e;this.mikroAuth=r}getAuthorizationUrl(e){let r=Array.isArray(this.config.scopes)?this.config.scopes.join(" "):this.config.scopes,i=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:"code",scope:r,state:e,...this.config.authorizationParams});return`${this.config.authorizationUrl}?${i}`}async exchangeCodeForTokens(e){let r=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,code:e,grant_type:"authorization_code",redirect_uri:this.config.redirectUri}),i=await fetch(this.config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json",...this.config.tokenHeaders},body:r.toString()});if(!i.ok){let s=await i.text();throw new Error(`Token exchange failed: ${i.status} ${i.statusText} - ${s}`)}return i.json()}async fetchUserInfo(e){let r=await fetch(this.config.userInfoUrl,{headers:{Authorization:`Bearer ${e}`,Accept:"application/json",...this.config.userInfoHeaders}});if(!r.ok){let i=await r.text();throw new Error(`User info fetch failed: ${r.status} ${r.statusText} - ${i}`)}return r.json()}mapUserInfo(e){return this.config.userMapping?this.config.userMapping(e):{id:e.sub||e.id?.toString(),email:e.email,name:e.name||e.preferred_username,username:e.preferred_username||e.username||e.email?.split("@")[0]}}async handleCallback(e,r){let i=await this.exchangeCodeForTokens(e),s=await this.fetchUserInfo(i.access_token),o=this.mapUserInfo(s);if(!o.email)throw new Error("OAuth provider did not return user email");let n=await this.mikroAuth.createToken({email:o.email,username:o.username||o.name,role:"user",ip:r});return{accessToken:n.accessToken,refreshToken:n.refreshToken,expiresIn:n.exp,tokenType:n.tokenType,user:o}}getPublicInfo(){return{id:this.config.id,name:this.config.name,loginUrl:`/auth/oauth/${this.config.id}`}}};import vi from"node:crypto";var Ce=class{stateStore=new Map;rateLimitStore=new Map;stateExpiryMs;rateLimitMaxAttempts;rateLimitWindowMs;stateCleanupInterval;rateLimitCleanupInterval;constructor(e){this.stateExpiryMs=(e?.stateExpirySeconds||600)*1e3,this.rateLimitMaxAttempts=e?.rateLimiting?.maxAttempts||10,this.rateLimitWindowMs=e?.rateLimiting?.windowMs||900*1e3,this.startCleanupTasks()}generateState(e,r,i,s){let o=vi.randomBytes(32).toString("hex"),n=this.getClientIp(e),a=e.headers["user-agent"]||"";return this.stateStore.set(o,{expires:Date.now()+this.stateExpiryMs,providerId:r,ip:n,userAgent:a,redirectUrl:i,applicationId:s}),o}validateState(e,r,i,s){let o=this.stateStore.get(e);return o?o.expires<Date.now()?(this.stateStore.delete(e),!1):o.providerId!==r||o.ip!==i||s&&o.userAgent&&o.userAgent!==s?!1:(this.stateStore.delete(e),!0):!1}checkRateLimit(e){let r=Date.now(),i=this.rateLimitStore.get(e);return!i||i.resetAt<r?(this.rateLimitStore.set(e,{count:1,resetAt:r+this.rateLimitWindowMs}),!0):i.count>=this.rateLimitMaxAttempts?!1:(i.count++,!0)}getRateLimitInfo(e){let r=this.rateLimitStore.get(e),i=Date.now();return!r||r.resetAt<i?{limit:this.rateLimitMaxAttempts,remaining:this.rateLimitMaxAttempts-1,reset:i+this.rateLimitWindowMs}:{limit:this.rateLimitMaxAttempts,remaining:Math.max(0,this.rateLimitMaxAttempts-r.count),reset:r.resetAt}}validateCallback(e,r){let i=Array.isArray(e.query.code)?e.query.code[0]:e.query.code,s=Array.isArray(e.query.state)?e.query.state[0]:e.query.state,o=Array.isArray(e.query.error)?e.query.error[0]:e.query.error,n=Array.isArray(e.query.error_description)?e.query.error_description[0]:e.query.error_description;if(o)return{valid:!1,error:`OAuth provider error: ${n||o}`};if(!i||!s)return{valid:!1,error:"Missing code or state parameter"};let a=this.stateStore.get(s),c=this.getClientIp(e),d=e.headers["user-agent"];return this.validateState(s,r,c,d)?{valid:!0,code:i,state:s,redirectUrl:a?.redirectUrl,applicationId:a?.applicationId}:{valid:!1,error:"Invalid or expired state token (CSRF protection failed)"}}getClientIp(e){let r=e.headers["x-forwarded-for"],i=Array.isArray(r)?r[0]:r,s=e.headers["x-real-ip"],o=Array.isArray(s)?s[0]:s;return i?.split(",")[0]?.trim()||o||"unknown"}startCleanupTasks(){this.stateCleanupInterval=setInterval(()=>{this.cleanupExpiredStates()},300*1e3),this.rateLimitCleanupInterval=setInterval(()=>{this.cleanupExpiredRateLimits()},600*1e3)}cleanupExpiredStates(){let e=Date.now(),r=0;for(let[i,s]of this.stateStore.entries())s.expires<e&&(this.stateStore.delete(i),r++);r>0&&console.log(`[OAuthSecurity] Cleaned up ${r} expired state tokens`)}cleanupExpiredRateLimits(){let e=Date.now(),r=0;for(let[i,s]of this.rateLimitStore.entries())s.resetAt<e&&(this.rateLimitStore.delete(i),r++);r>0&&console.log(`[OAuthSecurity] Cleaned up ${r} expired rate limit records`)}shutdown(){this.stateCleanupInterval&&clearInterval(this.stateCleanupInterval),this.rateLimitCleanupInterval&&clearInterval(this.rateLimitCleanupInterval)}getStats(){return{stateCount:this.stateStore.size,rateLimitCount:this.rateLimitStore.size}}};var Qt={google:{id:"google",name:"Google",authorizationUrl:"https://accounts.google.com/o/oauth2/v2/auth",tokenUrl:"https://oauth2.googleapis.com/token",userInfoUrl:"https://www.googleapis.com/oauth2/v2/userinfo",scopes:"openid email profile",authorizationParams:{access_type:"offline",prompt:"consent"},userMapping:t=>({id:t.sub,email:t.email,name:t.name,username:t.email.split("@")[0]})},github:{id:"github",name:"GitHub",authorizationUrl:"https://github.com/login/oauth/authorize",tokenUrl:"https://github.com/login/oauth/access_token",userInfoUrl:"https://api.github.com/user",scopes:"user:email read:user",tokenHeaders:{Accept:"application/json"},userInfoHeaders:{Accept:"application/vnd.github.v3+json"},userMapping:t=>({id:t.id.toString(),email:t.email,name:t.name||t.login,username:t.login})},microsoft:{id:"microsoft",name:"Microsoft",authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",userInfoUrl:"https://graph.microsoft.com/v1.0/me",scopes:"openid email profile User.Read",userMapping:t=>({id:t.id,email:t.mail||t.userPrincipalName,name:t.displayName,username:t.userPrincipalName?.split("@")[0]})},gitlab:{id:"gitlab",name:"GitLab",authorizationUrl:"https://gitlab.com/oauth/authorize",tokenUrl:"https://gitlab.com/oauth/token",userInfoUrl:"https://gitlab.com/api/v4/user",scopes:"read_user email",userMapping:t=>({id:t.id.toString(),email:t.email,name:t.name,username:t.username})}};function er(t){let e=[];if(t.presets)for(let[r,i]of Object.entries(t.presets)){let s=Qt[r];if(!s){console.warn(`[OAuth] Unknown preset provider: ${r}`);continue}e.push({...s,clientId:i.clientId,clientSecret:i.clientSecret,redirectUri:i.redirectUri,scopes:i.scopes||s.scopes,authorizationParams:{...s.authorizationParams,...i.authorizationParams}})}return t.custom&&e.push(...t.custom),e}async function tr(t,e){let r=await e(),i=r.headers||{};return i["X-Frame-Options"]="DENY",i["X-Content-Type-Options"]="nosniff",i["X-XSS-Protection"]="1; mode=block",i["Referrer-Policy"]="strict-origin-when-cross-origin",i["Content-Security-Policy"]="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'",i["Permissions-Policy"]="geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()",process.env.NODE_ENV==="production"&&(i["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),{...r,headers:i}}async function He(t,e){return process.env.NODE_ENV==="development"||process.env.NODE_ENV==="test"||(t.headers["x-forwarded-proto"]||t.headers["x-forwarded-protocol"])==="https"||t.req.url?.startsWith("https://")?e():t.json({error:"HTTPS required",message:"OAuth authentication requires HTTPS. Please use https:// instead of http://"},403)}function rr(t,e,r){if(r.startsWith("/databases/")){r.includes("/table")||r.includes("/get")?h(t,"databases.table.get",{}):r.includes("/write")||r.includes("/update")||e==="PUT"||e==="PATCH"?h(t,"databases.table.update",{}):r.includes("/delete")||e==="DELETE"?h(t,"databases.table.delete",{}):e==="POST"&&h(t,"databases.table.create",{});return}if(r.startsWith("/functions/")){r.includes("/deploy")||e==="POST"?h(t,"functions.function.create",{}):r.includes("/run/")?h(t,"functions.function.execute",{}):e==="PUT"||e==="PATCH"?h(t,"functions.function.update",{}):e==="DELETE"?h(t,"functions.function.delete",{}):e==="GET"&&h(t,"functions.function.get",{});return}if(r.startsWith("/storage/")){let i=r.match(/^\/storage\/buckets\/[^/]+$/),s=r.includes("/objects");i?e==="POST"?h(t,"storage.bucket.create",{}):e==="PATCH"||e==="PUT"?h(t,"storage.bucket.update",{}):e==="DELETE"?h(t,"storage.bucket.delete",{}):e==="GET"&&h(t,"storage.bucket.get",{}):s||r.includes("/upload")||r.includes("/download")||r.includes("/get")?r.includes("/upload")||e==="POST"?h(t,"storage.object.create",{}):r.includes("/download")||r.includes("/get")?h(t,"storage.object.get",{}):e==="PUT"||e==="PATCH"?h(t,"storage.object.update",{}):e==="DELETE"?h(t,"storage.object.delete",{}):e==="GET"&&h(t,"storage.object.get",{}):e==="GET"&&h(t,"storage.bucket.get",{});return}if(r.startsWith("/sites/")){if(r.match(/^\/sites\/projects\/[^/]+/)&&e==="GET")return;r.includes("/deploy")||e==="POST"?h(t,"sites.site.create",{}):e==="PUT"||e==="PATCH"?h(t,"sites.site.update",{}):e==="DELETE"?h(t,"sites.site.delete",{}):e==="GET"&&h(t,"sites.site.get",{});return}if(r.startsWith("/observability/")){e==="POST"?h(t,"observability.metrics.create",{}):e==="GET"&&h(t,"observability.metrics.get",{});return}}var Ae=class{baseUrl;authToken;constructor(e,r){this.baseUrl=e.replace(/\/$/,""),this.authToken=r}async createCustomRole(e,r,i,s){let o=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:e,name:r,description:i,permissions:s})});if(!o.ok){let n=`Failed to create role: ${o.statusText}`;try{n=(await o.json()).error||n}catch{n=await o.text().catch(()=>o.statusText)||n}throw new Error(n)}}async updateRole(e,r){let i=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!i.ok){let s=await i.json();throw new Error(s.error||`Failed to update role: ${i.statusText}`)}}async deleteRole(e){let r=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let i=await r.json();throw new Error(i.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(e,r,i){let s=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:e,description:r,roles:i})});if(!s.ok){let n=`Failed to create service account: ${s.statusText}`;try{n=(await s.json()).error||n}catch{n=await s.text().catch(()=>s.statusText)||n}throw new Error(n)}let o=await s.json();return{id:o.id,apiKey:o.apiKey}}async deleteIdentity(e){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let i=await r.json();throw new Error(i.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 ir(t){return t?new Ae(t):null}function sr(t){return t&&typeof t=="number"&&t>0?t*1024*1024:0}async function or(t){let{auth:e,db:r,config:i,molnos:s,oauth:o}=t,n=new we(e,r,s.initialUser);await n.start();let c=`${i.useHttps||i.useHttp2?"https":"http"}://${i.host||"127.0.0.1"}:${i.port||3e3}`,d=ir(c),p,u;if(o){p=new Ce(o),u=new Map;let l=er(o);for(let P of l){let k=new xe(P,e);u.set(P.id,k)}}let f=De(s.dataPath,"applications"),v=new be({dbPath:f});await v.start();let T=De(s.dataPath,"management"),S=new Se({dbPath:T});await S.start();let Ie=!1,ur=De(s.dataPath,".runtime-config.json");S.setEnvironmentVariables({MOLNOS_RUNTIME_CONFIG:ur});let g=new Le({port:i.port||3e3,host:i.host||"127.0.0.1",allowedDomains:i.allowedDomains,maxBodySize:sr(1e3)});g.use(async(l,P)=>Ie?l.text("Service Unavailable - Server is shutting down",503):P()),g.use(tr),g.get("/health",async l=>l.text("OK"));let E=async l=>l.text("Not Found",404);if(g.post("/auth/login",l=>lt(l,n,e,v)),g.get("/auth/verify",l=>dt(l,e,v)),g.post("/auth/refresh",l=>ut(l,e)),u&&p){g.get("/auth/oauth/providers",l=>Zt(l,u));for(let[l,P]of u.entries())g.get(`/auth/oauth/${l}`,He,k=>Xt(k,P,p,l)),g.get(`/auth/oauth/${l}/callback`,He,k=>Yt(k,P,p,n,v,l))}g.post("/management/service",y,l=>Lt(l,S)),g.get("/management/services",y,l=>Mt(l,S)),g.get("/management/service/:serviceName",y,l=>Ot(l,S)),g.put("/management/service/:serviceName",y,l=>Dt(l,S)),g.delete("/management/service/:serviceName",y,l=>Vt(l,S)),g.get("/management/service/:serviceName/start",y,l=>Nt(l,S)),g.get("/management/service/:serviceName/stop",y,l=>Ft(l,S)),g.get("/management/service/:serviceName/logs",y,l=>Bt(l,S)),g.get("/management/service/:serviceName/restart",y,l=>zt(l,S)),g.get("/identity/whoami",y,l=>pt(l)),g.get("/identity/identities",y,l=>ht(l,n)),g.post("/identity/users",y,l=>ft(l,n)),g.get("/identity/users",y,l=>gt(l,n)),g.get("/identity/users/:userId",y,l=>yt(l,n)),g.patch("/identity/users/:userId",y,l=>wt(l,n)),g.delete("/identity/users/:userId",y,l=>St(l,n)),g.post("/identity/service-accounts",y,l=>bt(l,n)),g.get("/identity/service-accounts",y,l=>xt(l,n)),g.get("/identity/service-accounts/:serviceAccountId",y,l=>Ct(l,n)),g.patch("/identity/service-accounts/:serviceAccountId",y,l=>It(l,n)),g.delete("/identity/service-accounts/:serviceAccountId",y,l=>Et(l,n)),g.post("/identity/service-accounts/:serviceAccountId/rotate-key",y,l=>kt(l,n)),g.post("/identity/roles",y,l=>Rt(l,n)),g.get("/identity/roles",y,l=>Pt(l,n)),g.get("/identity/roles/:roleId",y,l=>Tt(l,n)),g.patch("/identity/roles/:roleId",y,l=>jt(l,n)),g.delete("/identity/roles/:roleId",y,l=>$t(l,n)),g.post("/applications",y,l=>_t(l,v,d)),g.get("/applications",y,l=>Kt(l,v,d)),g.get("/applications/:id",y,l=>Wt(l,v,d)),g.patch("/applications/:id",y,l=>Gt(l,v,d)),g.delete("/applications/:id",y,l=>Jt(l,v,d)),g.get("/databases/*",y,A,E),g.post("/databases/*",y,A,E),g.put("/databases/*",y,A,E),g.patch("/databases/*",y,A,E),g.delete("/databases/*",y,A,E),g.get("/functions/*",A,E),g.post("/functions/*",A,E),g.put("/functions/*",A,E),g.patch("/functions/*",A,E),g.delete("/functions/*",A,E),g.get("/sites/projects/:projectId/*",A,async l=>l.text("Not Found",404)),g.get("/sites/*",y,A,E),g.post("/sites/*",y,A,E),g.put("/sites/*",y,A,E),g.patch("/sites/*",y,A,E),g.delete("/sites/*",y,A,E);async function te(l,P){let k=l.req.method;if(l.path.match(/^\/storage\/buckets\/[^/]+\/objects\/.+/)&&(k==="GET"||k==="HEAD")){let U=l.headers.authorization;if(U)try{let J=await n.getUserFromToken(U);J&&(l.state.user=J,l.state.requestingIdentity=J)}catch{}return P()}return y(l,P)}g.get("/storage/*",te,A,E),g.post("/storage/*",te,A,E),g.put("/storage/*",te,A,E),g.patch("/storage/*",te,A,E),g.delete("/storage/*",te,A,E),g.get("/observability/*",y,A,E),g.post("/observability/*",y,A,E),g.put("/observability/*",y,A,E),g.patch("/observability/*",y,A,E),g.delete("/observability/*",y,A,E);async function A(l,P){try{let k=l.path,N=Object.keys(l.query).length>0?`?${new URLSearchParams(l.query).toString()}`:"",re=l.req.method||"",U,J=0,hr=await S.getServices();for(let $ of hr)$.active&&k.startsWith($.prefix)&&$.prefix.length>J&&(U=$,J=$.prefix.length);if(!U)return P();let Fe=l.state.user;if(!(k.match(/^\/sites\/projects\/[^/]+.*/)&&re==="GET")&&Fe&&rr(Fe,re,k),U.healthCheck&&U.healthCheck.healthy===!1)return l.json({error:"Service unavailable (unhealthy)"},503);let mr=(k.substring(U.prefix.length)||"/")+N,j=null,Ee=null;if(["POST","PUT","PATCH","DELETE"].includes(re)){let $=l.headers["content-type"]||"";if($.includes("application/json"))try{let X=await I(l);j=JSON.stringify(X)}catch{j=null}else if($.includes("multipart/form-data"))try{j=JSON.stringify(l.body),Ee="application/json"}catch{j=null}else try{j=Buffer.isBuffer(l.body)?l.body:Buffer.from(l.body)}catch{j=null}}return new Promise($=>{let X={...l.headers,host:`127.0.0.1:${U.port}`};Ee&&(X["content-type"]=Ee),j&&(typeof j=="string"?X["content-length"]=Buffer.byteLength(j).toString():X["content-length"]=j.length.toString());let fr={method:re,hostname:"127.0.0.1",port:U.port,path:mr,headers:X},ie=wi.request(fr,M=>{let Be=[];M.on("data",Y=>{Be.push(Y)}),M.on("end",()=>{let Y=Buffer.concat(Be),ke={};Object.entries(M.headers).forEach(([Pe,le])=>{le!==void 0&&(ke[Pe]=Array.isArray(le)?le.join(", "):le)});let se=M.headers["content-type"]||"",L;if(se.includes("application/json"))try{let Pe=JSON.parse(Y.toString("utf-8"));L=l.json(Pe,M.statusCode||200)}catch{L=l.text(Y.toString("utf-8"),M.statusCode||200)}else se.startsWith("text/")||se.includes("javascript")||se.includes("xml")?L=l.text(Y.toString("utf-8"),M.statusCode||200):(L=l.binary(Y,se||"application/octet-stream"),M.statusCode&&M.statusCode!==200&&(L.statusCode=M.statusCode));L.headers?L.headers={...L.headers,...ke}:L.headers=ke,$(L)})});ie.on("error",M=>{$(l.json({error:"Proxy Error",message:M.message},502))}),ie.on("timeout",()=>{ie.destroy(),$(l.json({error:"Gateway Timeout"},504))}),j&&ie.write(j),ie.end()})}catch(k){return Ve(l,k)}}async function y(l,P){try{let k=l.headers.authorization;if(!k)return l.text("Unauthorized",401);let N=await n.getUserFromToken(k);return N?(l.state.user=N,l.state.requestingIdentity=N,P()):l.text("Unauthorized",401)}catch(k){return Ve(l,k)}}function Ve(l,P){let k=P.message||P,N=P?.cause?.statusCode||400;return l.json({error:k},N)}let Ne=g.start();async function pr(){Ie||(Ie=!0,await new Promise((l,P)=>{Ne.close(k=>{k?P(k):l()})}),console.log("[Server] Stopping managed services..."),await S.shutdown(),p&&p.shutdown(),await r.close())}return{server:Ne,shutdown:pr,processManager:S,identityService:n}}var Si="molnosid",bi={name:Si,length:12,urlSafe:!0},nr=()=>{let t=xi(process.env.DEBUG)||!1;return{auth:{jwtSecret:process.env.AUTH_JWT_SECRET||"your-jwt-secret",magicLinkExpirySeconds:900,jwtExpirySeconds:900,refreshTokenExpirySeconds:10080*60,maxActiveSessions:3,consoleUrl:process.env.CONSOLE_URL||"http://localhost:8000",debug:t},email:{emailSubject:"Sign In To MolnOS",user:process.env.EMAIL_USER||"",host:process.env.EMAIL_HOST||"",password:process.env.EMAIL_PASSWORD||"",port:465,secure:!0,maxRetries:2,debug:t},storage:{databaseDirectory:"molnosdb",encryptionKey:process.env.STORAGE_KEY||"",debug:t},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"127.0.0.1",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",allowedDomains:["http://localhost:8000"],debug:t},molnos:{dataPath:process.env.DATA_PATH||"data",initialUser:{id:process.env.INITIAL_USER_ID||new D().add(bi),userName:process.env.INITIAL_USER_NAME||"",email:process.env.INITIAL_USER_EMAIL||""},rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:process.env.SIGNED_URL_SECRET||"molnos-default-signed-url-secret"}}};function xi(t){return t==="true"||t===!0}var w=nr(),ar={configFilePath:"molnos.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:w.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:w.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:w.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:w.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:w.auth.maxActiveSessions},{flag:"--consoleUrl",path:"auth.consoleUrl",defaultValue:w.auth.consoleUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:w.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:w.email.host},{flag:"--emailUser",path:"email.user",defaultValue:w.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:w.email.password},{flag:"--emailPort",path:"email.port",defaultValue:w.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:w.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:w.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:w.email.debug},{flag:"--db",path:"storage.databaseDirectory",defaultValue:w.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:w.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:w.storage.debug},{flag:"--port",path:"server.port",defaultValue:w.server.port},{flag:"--host",path:"server.host",defaultValue:w.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:w.server.useHttps},{flag:"--http2",path:"server.useHttp2",isFlag:!0,defaultValue:w.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:w.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:w.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:w.server.sslCa},{flag:"--allowed",path:"server.allowedDomains",defaultValue:w.server.allowedDomains,parser:q.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:w.server.debug},{flag:"--data-path",path:"dataPath",defaultValue:w.molnos.dataPath},{flag:"--initialUserId",path:"molnos.initialUser.id",defaultValue:w.molnos.initialUser.id},{flag:"--initialUserName",path:"molnos.initialUser.userName",defaultValue:w.molnos.initialUser.userName},{flag:"--initialUserEmail",path:"molnos.initialUser.email",defaultValue:w.molnos.initialUser.email}]};function cr(t){return{...t,auth:{...t.auth,templates:{textVersion:(e,r,i)=>{let s=new URL(e),o=s.searchParams.get("token")||"",n=s.searchParams.get("email")||"",a=i?.appName||"MolnOS",c=i?.redirectUrl&&i?.applicationId,d=c?`Sign in to ${a}`:"Sign in to MolnOS",p=c?`You're signing in to ${a}. Click the link below to complete the authentication process.`:"Click the link below to sign in to your account.";return`
|
|
115
|
+
${d}
|
|
117
116
|
|
|
118
|
-
${
|
|
117
|
+
${p}
|
|
119
118
|
|
|
120
|
-
${
|
|
119
|
+
${e}
|
|
121
120
|
|
|
122
121
|
${c?"":`For CLI/Terminal users:
|
|
123
122
|
Run: molnos auth verify ${o} ${n}
|
|
124
123
|
`}
|
|
125
124
|
This link expires in ${r} minutes and can only be used once.
|
|
126
125
|
If you didn't request this, please ignore this email.
|
|
127
|
-
`},htmlVersion:(
|
|
126
|
+
`},htmlVersion:(e,r,i)=>{let s=new URL(e),o=s.searchParams.get("token")||"",n=s.searchParams.get("email")||"",a=i?.appName||"MolnOS",c=i?.redirectUrl&&i?.applicationId,d=c?`Sign in to ${a}`:"Sign in to MolnOS",p=c?`You're signing in to <strong>${a}</strong>. Click the button below to complete the authentication process. You'll be redirected to the application after verification.`:"Click the button below to sign in to your account.",u=c?"":`
|
|
128
127
|
<div style="margin-top: 2rem; padding: 1rem; background-color: #f8f8f8; border-radius: 6px; border-left: 3px solid #3e63dd;">
|
|
129
128
|
<p style="font-size: 0.85rem; font-weight: 600; margin: 0 0 0.5rem 0; color: #202020;">CLI/Terminal Users</p>
|
|
130
129
|
<p style="font-size: 0.8rem; line-height: 1.5; margin: 0 0 0.75rem 0; color: #646464;">Run this command in your terminal (click to select, then copy):</p>
|
|
@@ -136,17 +135,17 @@ If you didn't request this, please ignore this email.
|
|
|
136
135
|
<head>
|
|
137
136
|
<meta charset="utf-8">
|
|
138
137
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
139
|
-
<title>${
|
|
138
|
+
<title>${d}</title>
|
|
140
139
|
</head>
|
|
141
140
|
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
|
|
142
141
|
<div style="max-width: 600px; margin: 0 auto; padding: 2rem;">
|
|
143
|
-
<h1 style="font-size: 1.5rem; font-weight: 600; margin: 0 0 1rem 0; color: #202020;">${
|
|
144
|
-
<p style="font-size: 0.9rem; line-height: 1.5; margin: 0 0 1.5rem 0; color: #646464;">${
|
|
145
|
-
<a href="${
|
|
146
|
-
${
|
|
142
|
+
<h1 style="font-size: 1.5rem; font-weight: 600; margin: 0 0 1rem 0; color: #202020;">${d}</h1>
|
|
143
|
+
<p style="font-size: 0.9rem; line-height: 1.5; margin: 0 0 1.5rem 0; color: #646464;">${p}</p>
|
|
144
|
+
<a href="${e}" style="display: inline-block; padding: 0.75rem 1.5rem; background-color: #3e63dd; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 0.9rem; font-weight: 500;">Sign in</a>
|
|
145
|
+
${u}
|
|
147
146
|
<p style="font-size: 0.8rem; line-height: 1.5; margin: 1.5rem 0 0 0; color: #8d8d8d;">This link expires in ${r} minutes and can only be used once. If you didn't request this, please ignore this email.</p>
|
|
148
147
|
</div>
|
|
149
148
|
</body>
|
|
150
149
|
</html>
|
|
151
|
-
`}}}}}function
|
|
152
|
-
[MolnOS] Received ${
|
|
150
|
+
`}}}}}function lr(t){let e=[],r=c=>e.push(c);return t?.email?.host||r("Missing email.host value"),t?.email?.user||r("Missing email.user value"),t?.email?.password||r("Missing email.password value"),t?.molnos?.initialUser?.email||r("Missing molnos.initialUser.email value"),t?.molnos?.dataPath||r("Missing molnos.dataPath value"),{success:e.length===0,errors:e}}import{writeFileSync as Ci,mkdirSync as Ai,existsSync as Ii}from"node:fs";import{join as Ei}from"node:path";function dr(t){let e=t.molnos.dataPath||"data";Ii(e)||Ai(e,{recursive:!0});let r=Ei(e,".runtime-config.json"),i=t.server.host||"127.0.0.1",s={storage:{host:i,port:3001,url:`http://${i}:3001`},functions:{host:i,port:3002,url:`http://${i}:3002`},sites:{host:i,port:3003,url:`http://${i}:3003`},databases:{host:i,port:3004,url:`http://${i}:3004`},observability:{host:i,port:3005,url:`http://${i}:3005`}},o={molnos:{dataPath:t.molnos.dataPath,rateLimit:t.molnos.rateLimit,signedUrlSecret:t.molnos.signedUrlSecret},server:{host:t.server.host,port:t.server.port},identity:{apiUrl:`http://${i}:${t.server.port}`},services:s,storage:t.storage};return Ci(r,JSON.stringify(o,null,2),"utf-8"),r}async function Pi(){let t=Ti(),e=cr(t),r=lr(e);if(!r.success)throw console.error(`Found configuration validation errors: ${JSON.stringify(r.errors)}`),new me("Invalid configuration provided. Please see the validation errors and fix these and then retry again.");dr(e);let i=e.molnos.dataPath||"data",s=new K({databaseDirectory:ki(i,"molnosdb")});await s.start();let o=new Xe(s),n=new Ye(e.email),a={...e,auth:{...e.auth,appUrl:e.auth.consoleUrl}},c=new Je(a,n,o),d=await or({config:e.server,molnos:e.molnos,auth:c,db:s,oauth:e.oauth}),p=async u=>{console.log(`
|
|
151
|
+
[MolnOS] Received ${u}, initiating graceful shutdown...`);try{await d.shutdown(),console.log("[MolnOS] Shutdown complete"),process.exit(0)}catch(f){console.error("[MolnOS] Error during shutdown:",f),process.exit(1)}};process.on("SIGINT",()=>p("SIGINT")),process.on("SIGTERM",()=>p("SIGTERM")),process.on("SIGHUP",()=>p("SIGHUP")),process.on("uncaughtException",async u=>{console.error("[MolnOS] Uncaught exception:",u);try{await d.shutdown()}catch{}process.exit(1)}),process.on("unhandledRejection",async(u,f)=>{console.error("[MolnOS] Unhandled rejection at:",f,"reason:",u);try{await d.shutdown()}catch{}process.exit(1)})}function Ti(){return new z(ar).get()}Pi();export{Pi as start};
|