Skip to content

Instantly share code, notes, and snippets.

@mikaelvesavuori
Last active April 1, 2025 17:39
Show Gist options
  • Save mikaelvesavuori/361b64a3edec772d614ce85d1096d1bc to your computer and use it in GitHub Desktop.
Save mikaelvesavuori/361b64a3edec772d614ce85d1096d1bc to your computer and use it in GitHub Desktop.
MikroDB
#!/usr/bin/env node
// MikroDB - See LICENSE file for copyright and license details.
var W=class extends Error{constructor(i){super(i),this.name="ValidationError",this.message=i||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as ie,readFileSync as ne}from"node:fs";var x=class{config={};options=[];validators=[];autoValidate=!0;constructor(i){let e=i?.configFilePath,t=i?.args||[],r=i?.config||{};this.options=i?.options||[],this.validators=i?.validators||[],i?.autoValidate!==void 0&&(this.autoValidate=i.autoValidate),this.config=this.createConfig(e,t,r)}deepMerge(i,e){let t={...i};for(let r in e)e[r]!==void 0&&(e[r]!==null&&typeof e[r]=="object"&&!Array.isArray(e[r])&&r in i&&i[r]!==null&&typeof i[r]=="object"&&!Array.isArray(i[r])?t[r]=this.deepMerge(i[r],e[r]):e[r]!==void 0&&(t[r]=e[r]));return t}setValueAtPath(i,e,t){let r=e.split("."),s=i;for(let o=0;o<r.length-1;o++){let a=r[o];!(a in s)||s[a]===null?s[a]={}:typeof s[a]!="object"&&(s[a]={}),s=s[a]}let n=r[r.length-1];s[n]=t}getValueAtPath(i,e){let t=e.split("."),r=i;for(let s of t){if(r==null)return;r=r[s]}return r}createConfig(i,e=[],t={}){let r={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(r,a.path,a.defaultValue);let s={};if(i&&ie(i))try{let a=ne(i,"utf8");s=JSON.parse(a),console.log(`Loaded configuration from ${i}`)}catch(a){console.error(`Error reading config file: ${a instanceof Error?a.message:String(a)}`)}let n=this.parseCliArgs(e),o=this.deepMerge({},r);return o=this.deepMerge(o,s),o=this.deepMerge(o,t),o=this.deepMerge(o,n),o}parseCliArgs(i){let e={},t=i[0]?.endsWith("node")||i[0]?.endsWith("node.exe")?2:0;for(;t<i.length;){let r=i[t++],s=this.options.find(n=>n.flag===r);if(s)if(s.isFlag)this.setValueAtPath(e,s.path,!0);else if(t<i.length&&!i[t].startsWith("-")){let n=i[t++];if(s.parser)try{n=s.parser(n)}catch(o){console.error(`Error parsing value for ${s.flag}: ${o instanceof Error?o.message:String(o)}`);continue}if(s.validator){let o=s.validator(n);if(o!==!0&&typeof o=="string"){console.error(`Invalid value for ${s.flag}: ${o}`);continue}if(o===!1){console.error(`Invalid value for ${s.flag}`);continue}}this.setValueAtPath(e,s.path,n)}else console.error(`Missing value for option ${r}`)}return e}validate(){for(let i of this.validators){let e=this.getValueAtPath(this.config,i.path),t=i.validator(e,this.config);if(t===!1)throw new W(i.message);if(typeof t=="string")throw new W(t)}}get(){return this.autoValidate&&this.validate(),this.config}getValue(i,e){let t=this.getValueAtPath(this.config,i);return t!==void 0?t:e}setValue(i,e){if(typeof e=="object"&&e!==null&&!Array.isArray(e)){let t=this.getValueAtPath(this.config,i)||{};if(typeof t=="object"&&!Array.isArray(t)){let r=this.deepMerge(t,e);this.setValueAtPath(this.config,i,r);return}}this.setValueAtPath(this.config,i,e)}getHelpText(){let i=`Available configuration options:
`;for(let e of this.options)i+=`${e.flag}${e.isFlag?"":" <value>"}
`,e.description&&(i+=` ${e.description}
`),e.defaultValue!==void 0&&(i+=` Default: ${JSON.stringify(e.defaultValue)}
`),i+=`
`;return i}};var V={int:i=>{let e=i.trim();if(!/^[+-]?\d+$/.test(e))throw new Error(`Cannot parse "${i}" as an integer`);let t=Number.parseInt(e,10);if(Number.isNaN(t))throw new Error(`Cannot parse "${i}" as an integer`);return t},float:i=>{let e=i.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 "${i}" as a number`)}let t=Number.parseFloat(e);if(Number.isNaN(t))throw new Error(`Cannot parse "${i}" as a number`);return t},boolean:i=>{let e=i.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 "${i}" as a boolean`)},array:i=>i.split(",").map(e=>e.trim()),json:i=>{try{return JSON.parse(i)}catch{throw new Error(`Cannot parse "${i}" as JSON`)}}};var U=class{requests=new Map;limit;windowMs;constructor(i=100,e=60){this.limit=i,this.windowMs=e*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(i){let e=Date.now(),t=i||"unknown",r=this.requests.get(t);return(!r||r.resetTime<e)&&(r={count:0,resetTime:e+this.windowMs},this.requests.set(t,r)),r.count++,r.count<=this.limit}getRemainingRequests(i){let e=Date.now(),t=i||"unknown",r=this.requests.get(t);return!r||r.resetTime<e?this.limit:Math.max(0,this.limit-r.count)}getResetTime(i){let e=Date.now(),t=i||"unknown",r=this.requests.get(t);return!r||r.resetTime<e?Math.floor((e+this.windowMs)/1e3):Math.floor(r.resetTime/1e3)}cleanup(){let i=Date.now();for(let[e,t]of this.requests.entries())t.resetTime<i&&this.requests.delete(e)}};import{URL as oe}from"node:url";var _=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(i){return this.globalMiddlewares.push(i),this}register(i,e,t,r=[]){return this.routes.push({method:i,path:e,handler:t,middlewares:r}),this.pathPatterns.set(e,this.createPathPattern(e)),this}get(i,...e){let t=e.pop();return this.register("GET",i,t,e)}post(i,...e){let t=e.pop();return this.register("POST",i,t,e)}put(i,...e){let t=e.pop();return this.register("PUT",i,t,e)}delete(i,...e){let t=e.pop();return this.register("DELETE",i,t,e)}patch(i,...e){let t=e.pop();return this.register("PATCH",i,t,e)}options(i,...e){let t=e.pop();return this.register("OPTIONS",i,t,e)}match(i,e){for(let t of this.routes){if(t.method!==i)continue;let r=this.pathPatterns.get(t.path);if(!r)continue;let s=r.pattern.exec(e);if(!s)continue;let n={};return r.paramNames.forEach((o,a)=>{n[o]=s[a+1]||""}),{route:t,params:n}}return null}createPathPattern(i){let e=[],t=i.replace(/\/:[^/]+/g,r=>{let s=r.slice(2);return e.push(s),"/([^/]+)"}).replace(/\/$/,"/?");return{pattern:new RegExp(`^${t}$`),paramNames:e}}async handle(i,e){let t=i.method||"GET",r=new oe(i.url||"/",`http://${i.headers.host}`),s=r.pathname,n=this.match(t,s);if(!n)return null;let{route:o,params:a}=n,l={};r.searchParams.forEach((h,u)=>{l[u]=h});let c={req:i,res:e,params:a,query:l,body:i.body||{},headers:i.headers,path:s,state:{},raw:()=>e,binary:(h,u="application/octet-stream",d=200)=>({statusCode:d,body:h,headers:{"Content-Type":u,"Content-Length":h.length.toString()},isRaw:!0}),text:(h,u=200)=>({statusCode:u,body:h,headers:{"Content-Type":"text/plain"}}),form:(h,u=200)=>({statusCode:u,body:h,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(h,u=200)=>({statusCode:u,body:h,headers:{"Content-Type":"application/json"}}),html:(h,u=200)=>({statusCode:u,body:h,headers:{"Content-Type":"text/html"}}),redirect:(h,u=302)=>({statusCode:u,body:null,headers:{Location:h}}),status:function(h){return{raw:()=>e,binary:(u,d="application/octet-stream")=>({statusCode:h,body:u,headers:{"Content-Type":d,"Content-Length":u.length.toString()},isRaw:!0}),text:u=>({statusCode:h,body:u,headers:{"Content-Type":"text/plain"}}),json:u=>({statusCode:h,body:u,headers:{"Content-Type":"application/json"}}),html:u=>({statusCode:h,body:u,headers:{"Content-Type":"text/html"}}),form:u=>({statusCode:h,body:u,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(u,d=302)=>({statusCode:d,body:null,headers:{Location:u}}),status:u=>this.status(u)}}},f=[...this.globalMiddlewares,...o.middlewares];return this.executeMiddlewareChain(c,f,o.handler)}async executeMiddlewareChain(i,e,t){let r=0,s=async()=>{if(r<e.length){let n=e[r++];return n(i,s)}return t(i)};return s()}};var k=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:ae(process.env.DEBUG)||!1,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function ae(i){return i==="true"||i===!0}var m=k(),K=i=>({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:V.array},{flag:"--debug",path:"debug",defaultValue:m.debug,isFlag:!0}],config:i});import{readFileSync as b}from"node:fs";import $ from"node:http";import le from"node:http2";import ce from"node:https";var D=class{config;rateLimiter;router;constructor(i){let e=new x(K(i||{})).get();e.debug&&console.log("Using configuration:",e),this.config=e,this.router=new _;let t=e.rateLimit.requestsPerMinute||k().rateLimit.requestsPerMinute;this.rateLimiter=new U(t,60),e.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(i){return this.router.use(i),this}get(i,...e){return this.router.get(i,...e),this}post(i,...e){return this.router.post(i,...e),this}put(i,...e){return this.router.put(i,...e),this}delete(i,...e){return this.router.delete(i,...e),this}patch(i,...e){return this.router.patch(i,...e),this}options(i,...e){return this.router.options(i,...e),this}start(){let i=this.createServer(),{port:e,host:t}=this.config;return this.setupGracefulShutdown(i),i.listen(e,t,()=>{let r=i.address(),s=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${s}://${r.address!=="::"?r.address:"localhost"}:${r.port}`)}),i}createServer(){let i=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let e={key:b(this.config.sslKey),cert:b(this.config.sslCert),...this.config.sslCa?{ca:b(this.config.sslCa)}:{}};return le.createSecureServer(e,i)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let e={key:b(this.config.sslKey),cert:b(this.config.sslCert),...this.config.sslCa?{ca:b(this.config.sslCa)}:{}};return ce.createServer(e,i)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}return $.createServer(i)}async rateLimitMiddleware(i,e){let t=i.req.socket.remoteAddress||"unknown";return i.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),i.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(t).toString()),i.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(t).toString()),this.rateLimiter.isAllowed(t)?e():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(i,e){let t=Date.now(),r=i.method||"UNKNOWN",s=i.url||"/unknown",n=this.config.debug;try{if(this.setCorsHeaders(e,i),this.setSecurityHeaders(e,this.config.useHttps),n&&console.log(`${r} ${s}`),i.method==="OPTIONS"){if(e instanceof $.ServerResponse)e.statusCode=204,e.end();else{let a=e;a.writeHead(204),a.end()}return}try{i.body=await this.parseBody(i)}catch(a){return n&&console.error("Body parsing error:",a.message),this.respond(e,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let o=await this.router.handle(i,e);return o?o._handled?void 0:this.respond(e,o):this.respond(e,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(o){return console.error("Server error:",o),this.respond(e,{statusCode:500,body:{error:"Internal Server Error",message:n?o.message:"An unexpected error occurred"}})}finally{n&&this.logDuration(t,r,s)}}logDuration(i,e,t){let r=Date.now()-i;console.log(`${e} ${t} completed in ${r}ms`)}async parseBody(i){return new Promise((e,t)=>{let r=[],s=0,n=1024*1024,o=!1,a=this.config.debug,l=i.headers["content-type"]||"";a&&console.log("Content-Type:",l),i.on("data",c=>{if(s+=c.length,a&&console.log(`Received chunk: ${c.length} bytes, total size: ${s}`),s>n&&!o){o=!0,a&&console.log(`Body size exceeded limit: ${s} > ${n}`),t(new Error("Request body too large"));return}o||r.push(c)}),i.on("end",()=>{if(!o){a&&console.log(`Request body complete: ${s} bytes`);try{if(r.length>0){let c=Buffer.concat(r).toString("utf8");if(l.includes("application/json"))try{e(JSON.parse(c))}catch(f){t(new Error(`Invalid JSON in request body: ${f.message}`))}else if(l.includes("application/x-www-form-urlencoded")){let f={};new URLSearchParams(c).forEach((h,u)=>{f[u]=h}),e(f)}else e(c)}else e({})}catch(c){t(new Error(`Invalid request body: ${c}`))}}}),i.on("error",c=>{o||t(new Error(`Error reading request body: ${c.message}`))})})}setCorsHeaders(i,e){let t=e.headers.origin,{allowedDomains:r=["*"]}=this.config;!t||r.length===0||r.includes("*")?i.setHeader("Access-Control-Allow-Origin","*"):r.includes(t)&&(i.setHeader("Access-Control-Allow-Origin",t),i.setHeader("Vary","Origin")),i.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),i.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(i,e=!1){let t={"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)&&(t["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),i instanceof $.ServerResponse)Object.entries(t).forEach(([r,s])=>{i.setHeader(r,s)});else{let r=i;Object.entries(t).forEach(([s,n])=>{r.setHeader(s,n)})}}respond(i,e){let t={...e.headers||{}};(s=>typeof s.writeHead=="function"&&typeof s.end=="function")(i)?(i.writeHead(e.statusCode,t),e.body===null||e.body===void 0?i.end():e.isRaw||typeof e.body=="string"?i.end(e.body):i.end(JSON.stringify(e.body))):(console.warn("Unexpected response object type without writeHead/end methods"),i.writeHead?.(e.statusCode,t),e.body===null||e.body===void 0?i.end?.():e.isRaw||typeof e.body=="string"?i.end?.(e.body):i.end?.(JSON.stringify(e.body)))}setupGracefulShutdown(i){let e=t=>{console.log("Shutting down MikroServe server..."),t&&console.error("Error:",t),i.close(()=>{console.log("Server closed successfully"),setImmediate(()=>process.exit(t?1:0))})};process.on("SIGINT",()=>e()),process.on("SIGTERM",()=>e()),process.on("uncaughtException",e),process.on("unhandledRejection",e)}};import{join as Te}from"node:path";import{existsSync as M}from"node:fs";import{readFile as J,unlink as ue,writeFile as X}from"node:fs/promises";var p=()=>Date.now(),z=(i,e)=>e==="D"||i.length===0||i.join(" ")==="null"?null:G(i),G=i=>{try{return JSON.parse(i.join(" "))}catch{return}};function O(i){return i==="true"||i===!0}function q(i){let e=(o,a)=>{if(o){if(a==="json")return G(o)||o;if(a==="number")return Number.parseInt(o,10)}},t=e(i?.filter,"json"),r=e(i?.sort,"json"),s=e(i?.limit,"number"),n=e(i?.offset,"number");if(!(!t&&!r&&!s&&!n))return{filter:t,sort:r,limit:s,offset:n}}var w=class extends Error{constructor(e){super(),this.name="NotFoundError",this.message=e||"Resource not found",this.cause={statusCode:404}}},g=class extends Error{constructor(e){super(),this.name="CheckpointError",this.message=e,this.cause={statusCode:500}}};var A=class{checkpointInterval;defaultCheckpointIntervalMs=10*1e3;isCheckpointing=!1;lastCheckpointTime=0;walFile;checkpointTimer=null;wal;table;constructor(e){let{table:t,wal:r,walFile:s,checkpointIntervalMs:n}=e;this.defaultCheckpointIntervalMs=n||this.defaultCheckpointIntervalMs,this.table=t,this.wal=r,this.walFile=s,this.checkpointInterval=n,this.lastCheckpointTime=p()}async start(){let e=`${this.walFile}.checkpoint`;if(M(e)){console.log("Incomplete checkpoint detected, running recovery...");try{let t=await J(e,"utf8");console.log(`Incomplete checkpoint from: ${new Date(Number.parseInt(t))}`),await this.checkpoint(!0)}catch(t){throw new w(`Error reading checkpoint file: ${t}`)}}this.checkpointTimer=setInterval(async()=>{try{await this.checkpoint()}catch(t){throw new g(`Checkpoint interval failed: ${t}`)}},this.checkpointInterval)}stop(){this.checkpointTimer&&(clearInterval(this.checkpointTimer),this.checkpointTimer=null)}async checkpoint(e=!1){if(this.isCheckpointing)return;let t=p();if(!(!e&&t-this.lastCheckpointTime<this.checkpointInterval)){this.isCheckpointing=!0;try{await this.wal.flushWAL();let r=`${this.walFile}.checkpoint`;await X(r,t.toString(),"utf8");let s=await this.getTablesFromWAL();await this.persistTables(s);try{await X(this.walFile,"","utf8"),process.env.DEBUG==="true"&&console.log("WAL truncated successfully")}catch(n){throw new g(`Failed to truncate WAL: ${n}`)}M(r)&&await ue(r),this.wal.clearPositions(),this.lastCheckpointTime=t,process.env.DEBUG==="true"&&console.log("Checkpoint complete")}catch(r){throw new g(`Checkpoint failed: ${r}`)}finally{this.isCheckpointing=!1}}}async getTablesFromWAL(){let e=new Set;if(!M(this.walFile))return e;try{let t=await J(this.walFile,"utf8");if(!t.trim())return e;let r=t.trim().split(`
`);for(let s of r){if(!s.trim())continue;let n=s.split(" ");n.length>=3&&e.add(n[2])}}catch(t){throw new g(`Error reading WAL file: ${t}`)}return e}async persistTables(e){let t=Array.from(e).map(async r=>{try{await this.table.flushTableToDisk(r),console.log(`Checkpointed table "${r}"`)}catch(s){throw new g(`Failed to checkpoint table "${r}": ${s.message}`)}});await Promise.all(t)}};import{existsSync as ee,mkdirSync as we}from"node:fs";import{readFile as ve,writeFile as Ee}from"node:fs/promises";import{join as N}from"node:path";import{EventEmitter as he}from"node:events";var P=class{emitter;targets={};options;constructor(i){this.emitter=new he,i?.maxListeners===0?this.emitter.setMaxListeners(0):this.emitter.setMaxListeners(i?.maxListeners||10),this.options={errorHandler:i?.errorHandler||(e=>console.error(e))}}addTarget(i){return(Array.isArray(i)?i:[i]).map(r=>this.targets[r.name]?(console.error(`Target with name '${r.name}' already exists.`),!1):(this.targets[r.name]={name:r.name,url:r.url,headers:r.headers||{},events:r.events||[]},!0)).every(r=>r===!0)}updateTarget(i,e){if(!this.targets[i])return console.error(`Target with name '${i}' does not exist.`),!1;let t=this.targets[i];return e.url!==void 0&&(t.url=e.url),e.headers&&(t.headers={...t.headers,...e.headers}),e.events&&(t.events=e.events),!0}removeTarget(i){return this.targets[i]?(delete this.targets[i],!0):(console.error(`Target with name '${i}' does not exist.`),!1)}addEventToTarget(i,e){if(!this.targets[i])return console.error(`Target with name '${i}' does not exist.`),!1;let t=Array.isArray(e)?e:[e],r=this.targets[i];return t.forEach(s=>{r.events.includes(s)||r.events.push(s)}),!0}on(i,e){return this.emitter.on(i,e),this}off(i,e){return this.emitter.off(i,e),this}once(i,e){return this.emitter.once(i,e),this}async emit(i,e){let t={success:!0,errors:[]},r=(o,a,l)=>({target:o,event:a,error:l}),s=Object.values(this.targets).filter(o=>o.events.includes(i)||o.events.includes("*"));s.filter(o=>!o.url).forEach(o=>{try{this.emitter.emit(i,e)}catch(a){let l=a instanceof Error?a:new Error(String(a));t.errors.push({target:o.name,event:i,error:l}),this.options.errorHandler(l,i,e),t.success=!1}});let n=s.filter(o=>o.url);if(n.length>0){let o=n.map(async a=>{try{let l=await fetch(a.url,{method:"POST",headers:{"Content-Type":"application/json",...a.headers},body:JSON.stringify({eventName:i,data:e})});if(!l.ok){let c=`HTTP error! Status: ${l.status}: ${l.statusText}`,f=new Error(c);t.errors.push(r(a.name,i,f)),this.options.errorHandler(f,i,e),t.success=!1}}catch(l){let c=l instanceof Error?l:new Error(String(l));t.errors.push(r(a.name,i,c)),this.options.errorHandler(c,i,e),t.success=!1}});await Promise.allSettled(o)}return t}async handleIncomingEvent(i){try{let{eventName:e,data:t}=typeof i=="string"?JSON.parse(i):i;process.nextTick(()=>{try{this.emitter.emit(e,t)}catch(r){this.options.errorHandler(r instanceof Error?r:new Error(String(r)),e,t)}})}catch(e){throw this.options.errorHandler(e instanceof Error?e:new Error(String(e)),"parse_event"),e}}createMiddleware(){return async(i,e,t)=>{if(i.method!=="POST"){t&&t();return}if(i.body)try{await this.handleIncomingEvent(i.body),e.statusCode=202,e.end()}catch(r){e.statusCode=400,e.end(JSON.stringify({error:"Invalid event format"})),t&&t(r)}else{let r="";i.on("data",s=>r+=s.toString()),i.on("end",async()=>{try{await this.handleIncomingEvent(r),e.statusCode=202,e.end()}catch(s){e.statusCode=400,e.end(JSON.stringify({error:"Invalid event format"})),t&&t(s)}})}}}};var I=class{cacheLimit;tableAccessTimes=new Map;constructor(e={}){this.cacheLimit=e.cacheLimit??20}trackTableAccess(e){this.tableAccessTimes.set(e,p())}findTablesForEviction(e){if(e<=this.cacheLimit)return[];let t=e-this.cacheLimit,r=Array.from(this.tableAccessTimes.entries()).sort((s,n)=>s[1]-n[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=p(),r=[];for(let[s,n]of e.entries())n.x&&n.x<t&&r.push([s,n]);return r}clear(){this.tableAccessTimes.clear()}};import{createCipheriv as fe,createDecipheriv as pe,randomBytes as de,scryptSync as me}from"node:crypto";var v=class{algo="aes-256-gcm";KEY_LENGTH=32;IV_LENGTH=12;generateKey(e,t){return me(`${t}#${e}`,t,this.KEY_LENGTH)}generateIV(){return de(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=fe(this.algo,t,r),n=Buffer.concat([s.update(e,"utf8"),s.final()]),o=s.getAuthTag();return{iv:r,encrypted:n,authTag:o}}decrypt(e,t){let{iv:r,encrypted:s,authTag:n}=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=pe(this.algo,t,r);return o.setAuthTag(n),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++],n=e.subarray(t,t+s);t+=s;let o=e[t++],a=e.subarray(t,t+o);t+=o;let l=e.subarray(t);return{iv:n,authTag:a,encrypted:l}}toHex(e){return e.toString("hex")}toUtf8(e){return e.toString("utf8")}toBuffer(e){return Buffer.from(e,"hex")}};var E=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,n=p();for(let o=0;o<t&&s+26<=e.length;o++){let a=e.readUInt16LE(s);s+=2;let l=e.readUInt32LE(s);s+=4;let c=e.readUInt32LE(s);s+=4;let f=Number(e.readBigUInt64LE(s));s+=8;let h=Number(e.readBigUInt64LE(s));if(s+=8,s+a+l>e.length)break;if(h&&h<=n){s+=a+l;continue}let u=e.toString("utf8",s,s+a);s+=a;let d=e.slice(s,s+l);s+=l;let y=this.decodeValue(d);r.set(u,{value:y,version:c,timestamp:f,expiration:h||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"),n=Buffer.alloc(4);n.writeUInt32LE(s.length,0),t.push(n);for(let[o,a]of s){if(o===null||typeof o!="string")continue;let l=Buffer.from(o),c=this.encodeValue(a.value),f=Buffer.alloc(2);f.writeUInt16LE(l.length,0),t.push(f);let h=Buffer.alloc(4);h.writeUInt32LE(c.length,0),t.push(h);let u=Buffer.alloc(4);u.writeUInt32LE(a.version||0,0),t.push(u);let d=Buffer.alloc(8);d.writeBigUInt64LE(BigInt(a.timestamp||0),0),t.push(d);let y=Buffer.alloc(8);y.writeBigUInt64LE(BigInt(a.expiration||0),0),t.push(y),t.push(l),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 n=Buffer.alloc(9);return n[0]=7,n.writeBigInt64LE(BigInt(e.getTime()),1),n}let t=Object.keys(e),r=[],s=Buffer.alloc(5);s[0]=6,s.writeUInt32LE(t.length,1),r.push(s);for(let n of t){let o=Buffer.from(n,"utf8"),a=Buffer.alloc(2);a.writeUInt16LE(o.length,0),r.push(a),r.push(o),r.push(this.encodeValue(e[n]))}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),n=5;for(let o=0;o<r;o++){let{value:a,bytesRead:l}=this.decodeValueWithSize(e,n);s[o]=a,n+=l}return s}case 6:{let r=e.readUInt32LE(1),s={},n=5;for(let o=0;o<r;o++){let a=e.readUInt16LE(n);n+=2;let l=e.toString("utf8",n,n+a);n+=a;let{value:c,bytesRead:f}=this.decodeValueWithSize(e,n);s[l]=c,n+=f}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),n=new Array(s),o=t+5;for(let a=0;a<s;a++){let l=this.decodeValueWithSize(e,o);n[a]=l.value,o+=l.bytesRead}return{value:n,bytesRead:o-t}}case 6:{let s=e.readUInt32LE(t+1),n={},o=t+5;for(let a=0;a<s;a++){let l=e.readUInt16LE(o);o+=2;let c=e.toString("utf8",o,o+l);o+=l;let f=this.decodeValueWithSize(e,o);n[c]=f.value,o+=f.bytesRead}return{value:n,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}}}};var L=class{async query(e,t,r=50){let s=new Map;for(let[n,o]of e.entries())if((!t||(typeof t=="function"?t(o.value):this.evaluateFilter(o.value,t)))&&(s.set(n,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 n=new RegExp(s);return typeof e=="string"&&n.test(e)}catch(n){return console.error("Invalid regex pattern:",n),!1}case"contains":return Array.isArray(e)&&e.includes(s);case"containsAll":return Array.isArray(e)&&Array.isArray(s)&&s.every(n=>e.includes(n));case"containsAny":return Array.isArray(e)&&Array.isArray(s)&&s.some(n=>e.includes(n));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 n=r.split("."),o=e;for(let a of n)if(o=o?.[a],o==null)return!1;if(!this.evaluateCondition(o,s))return!1}else if(s&&typeof s=="object"&&!("operator"in s)){let n=e[r];if(n==null||!this.evaluateFilter(n,s))return!1}else{let n=e[r];if(!this.evaluateCondition(n,s))return!1}return!0}};import{existsSync as Y,statSync as H,writeFileSync as ge}from"node:fs";import{appendFile as ye,readFile as Q}from"node:fs/promises";var B=class{walFile;walInterval;walBuffer=[];maxWalBufferEntries=Number.parseInt(process.env.MAX_WAL_BUFFER_ENTRIES||"100");maxWalBufferSize=Number.parseInt(process.env.MAX_WAL_BUFFER_SIZE||(1024*1024*.01).toString());maxWalSizeBeforeCheckpoint=Number.parseInt(process.env.MAX_WAL_BUFFER_SIZE||(1024*1024*.01).toString());lastProcessedEntryCount=new Map;checkpointCallback=null;constructor(e,t){this.walFile=e,this.walInterval=t,this.start()}setCheckpointCallback(e){this.checkpointCallback=e}start(){Y(this.walFile)||ge(this.walFile,"","utf-8"),setInterval(async()=>{try{this.walBuffer.length>0&&await this.flushWAL()}catch(e){console.error("WAL flush interval failed:",e)}},this.walInterval)}checkWalFileExists(){if(!Y(this.walFile))throw new w(`WAL file "${this.walFile}" does not exist`)}async loadWAL(e){this.checkWalFileExists();let t=[];if((H(this.walFile)?.size||0)===0)return t;try{let n=(await Q(this.walFile,"utf8")).trim().split(`
`),o=p();for(let a=0;a<n.length;a++){if(!n[a].trim())continue;let c=this.lastProcessedEntryCount.get(e||"")||0;if(e&&a<c)continue;let[f,h,u,d,y,R,...re]=n[a].split(" ");if(e&&u!==e)continue;let se=Number(d.split(":")[1]),F=y==="0"?null:Number(y.split(":")[1]);if(F&&F<o)continue;let j=z(re,h);j!==void 0&&(e&&this.lastProcessedEntryCount.set(e,a+1),h==="W"?t.push({operation:"W",tableName:u,key:R,data:{value:j,version:se,timestamp:o,expiration:F}}):h==="D"&&t.push({operation:"D",tableName:u,key:R}))}return t}catch(s){return console.error(e?`Failed to replay WAL for table "${e}": ${s.message}`:`Failed to replay WAL: ${s.message}`),t}}async hasNewWALEntriesForTable(e){this.checkWalFileExists();try{let t=await Q(this.walFile,"utf8");if(!t.trim())return!1;let r=t.trim().split(`
`),s=this.lastProcessedEntryCount.get(e)||0;if(s>=r.length)return!1;for(let n=s;n<r.length;n++){let o=r[n];if(!o.trim())continue;let a=o.split(" ");if(a.length>=3&&a[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 ye(this.walFile,e.join(""),"utf8");let t=H(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,n,o=0){this.checkWalFileExists();let l=`${p()} ${t} ${e} v:${n} x:${o} ${r} ${JSON.stringify(s)}
`;this.walBuffer.push(l),this.walBuffer.length>=this.maxWalBufferEntries&&await this.flushWAL(),this.walBuffer.reduce((h,u)=>h+u.length,0)>=this.maxWalBufferSize&&await this.flushWAL();let f=H(this.walFile);f.size>this.maxWalSizeBeforeCheckpoint&&(process.env.DEBUG==="true"&&console.log(`WAL size (${f.size}) exceeds limit (${this.maxWalSizeBeforeCheckpoint}), triggering checkpoint`),this.checkpointCallback&&setImmediate(async()=>{try{await this.checkpointCallback()}catch(h){console.error("Error during automatic checkpoint:",h)}}))}clearPositions(){this.lastProcessedEntryCount.clear()}};var T=()=>({db:{dbName:"mikrodb",databaseDirectory:"mikrodb",walFileName:"wal.log",walInterval:2e3,encryptionKey:"",maxWriteOpsBeforeFlush:100,debug:O(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:O(process.env.DEBUG)||!1}});import{writeFile as be}from"node:fs/promises";async function Z(i,e,t){let r=new v,n=new E().toBinaryBuffer(e);if(!n){console.log("Buffer is empty, skipping...");return}if(t)try{let o=r.generateKey(t,"salt"),a=n.toString("binary"),l=r.generateIV(),c=r.encrypt(a,o,l);n=r.serialize(c)}catch(o){console.error("Encryption failed:",o)}await be(i,n)}var S=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):T().db.maxWriteOpsBeforeFlush;encryptionKey;cache;encryption;persistence;query;wal;mikroEvent;constructor(e,t){let{databaseDirectory:r,walFileName:s,walInterval:n}=e;this.databaseDirectory=r,this.walFile=N(this.databaseDirectory,s),this.encryptionKey=e.encryptionKey?e.encryptionKey:null,ee(this.databaseDirectory)||we(this.databaseDirectory),this.cache=new I,this.encryption=new v,this.persistence=new E,this.query=new L,this.wal=new B(this.walFile,n),this.mikroEvent=new P,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?.forEach(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 n=t.filter(o=>o.tableName===s);e&&!this.hasTable(s)?await this.loadTable(s):this.createTable(s);for(let o of n)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=N(this.databaseDirectory,e);if(!ee(t)){this.createTable(e);return}let r=await ve(t),s=r;if(this.encryptionKey&&r.length>0&&r[0]===1)try{let o=this.encryption.deserialize(r),a=this.encryption.generateKey(this.encryptionKey,"salt"),l=this.encryption.decrypt(o,a);s=Buffer.from(l,"binary")}catch(o){console.error(`Failed to decrypt ${e}:`,o)}let n=this.persistence.readTableFromBinaryBuffer(s);this.data.set(e,n),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 n=this.getTable(t),o=await this.query.query(n,s.filter,s.limit);if(s.sort&&(o=o.sort(s.sort)),s.offset!=null||s.limit!=null){let a=s.offset||0,l=s.limit?a+s.limit:void 0;o=o.slice(a,l)}return o}async write(e,t={}){let{concurrencyLimit:r=10,flushImmediately:s=!1}=t,n=Array.isArray(e)?e:[e],o=n.length,a=0;for(;a<o;){let l=n.slice(a,a+r),c=l.map(h=>this.writeItem(h,!1));if((await Promise.all(c)).includes(!1))return!1;a+=l.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:n,expectedVersion:o=null,expiration:a=0}=e;await this.setActiveTable(r);let{success:l,newVersion:c}=this.getItemVersion(r,s,o);return l?(await this.wal.appendToWAL(r,"W",s,n,c,a),this.setItem(r,s,{value:n,v:c,t:p(),x:a}),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:n,currentVersion:o,expiration:a}=this.getItemVersion(e,t,r);return n?(await this.wal.appendToWAL(e,"D",t,null,o,a),await this.deleteItem(e,t),s&&await this.flush(),!0):!1}getItemVersion(e,t,r){let s=this.getItem(e,t),n=s?s.version:0,o=n+1,a=s&&s.expiration||0,l=!0;return r!==null&&n!==r&&(console.log(`Version mismatch for ${e}:${t}. Expected ${r}, found ${n}`),l=!1),{success:l,currentRecord:s,currentVersion:n,newVersion:o,expiration:a}}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:n}=await this.mikroEvent.emit(r,{operation:r,table:e,key:t});s||console.error("Error when emitting events:",n)}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 n=JSON.parse(s);e.has(n.tableName)||e.set(n.tableName,new Map),e.get(n.tableName).set(n.key,n.record);let a="item.written",{success:l,errors:c}=await this.mikroEvent.emit(a,{operation:a,table:n.tableName,key:n.key,record:n.record});l||console.error("Error when emitting events:",c)}let r=Array.from(e.entries()).map(async([s])=>{let n=this.getTable(s),o=N(this.databaseDirectory,s);await Z(o,n,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,n]of r){await this.wal.appendToWAL(e,"D",s,null,n.v,n.x),t.delete(s);let o="item.expired",{success:a,errors:l}=await this.mikroEvent.emit(o,{operation:o,table:e,key:s,record:n});a||console.error("Error when emitting events:",l)}}}async dump(e){e&&await this.setActiveTable(e);let t=this.getAll(this.activeTable);await Ee(`${this.databaseDirectory}/${this.activeTable}_dump.json`,JSON.stringify(t),"utf8")}getWAL(){return this.wal}getPersistence(){return this.persistence}};var C=class{table;checkpoint;constructor(e){let t=T(),r=e?.databaseDirectory||t.db.databaseDirectory,s=e?.walFileName||t.db.walFileName,n=e?.walInterval||t.db.walInterval,o=e?.encryptionKey||t.db.encryptionKey,a=e?.maxWriteOpsBeforeFlush||t.db.maxWriteOpsBeforeFlush,l=e?.events||{};e?.debug&&(process.env.DEBUG="true"),e?.maxWriteOpsBeforeFlush&&(process.env.MAX_WRITE_OPS_BEFORE_FLUSH=a.toString()),this.table=new S({databaseDirectory:r,walFileName:s,walInterval:n,encryptionKey:o},l);let c=this.table.getWAL();this.table.getWAL().setCheckpointCallback(()=>this.checkpoint.checkpoint(!0)),this.checkpoint=new A({table:this.table,wal:c,walFile:Te(r,s),checkpointIntervalMs:n}),this.checkpoint.start().catch(f=>console.error("Failed to start checkpoint service:",f))}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(){await this.flush()}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()}};async function te(i){let e=new C({...i.db,events:i?.events});await e.start();let t=new D(i.server);return t.get("/table",async r=>{let s=r.req.body;if(!s.tableName)return r.json({statusCode:400,body:"tableName is required"});let n=await e.getTableSize(s.tableName);return n||r.json({statusCode:404,body:null}),r.json({statusCode:200,body:n})}),t.post("/get",async r=>{let s=r.req.body;if(!s.tableName)return r.json({statusCode:400,body:"tableName is required"});let n={tableName:s.tableName,key:s.key},o=q(s?.options);o&&(n.options=o);let a=await e.get(n);return a||r.json({statusCode:404,body:null}),r.json({statusCode:200,body:a})}),t.post("/write",async r=>{let s=r.req.body;if(!s.tableName||s.value===void 0)return r.json({statusCode:400,body:"tableName and value are required"});let n={tableName:s.tableName,key:s.key,value:s.value,expectedVersion:s.expectedVersion,expiration:s.expiration},o={concurrencyLimit:s.concurrencyLimit,flushImmediately:s.flushImmediately},a=await e.write(n,o);return r.json({statusCode:200,body:a})}),t.delete("/delete",async r=>{let s=r.params;if(!s.tableName||!s.key)return r.json({statusCode:400,body:"tableName and key are required"});let n={tableName:s.tableName,key:s.key},o=await e.delete(n);return r.json({statusCode:200,body:o})}),t.start(),t}async function xe(){let e=process.argv[1]?.includes("node_modules/.bin/mikrodb"),t=(process.argv[2]||"")==="--force";if(e||t){console.log("\u{1F5C2}\uFE0F Welcome to MikroDB! \u2728");try{let r=T(),s=new x({configFilePath:"mikrodb.config.json",args:process.argv,options:[{flag:"--db",path:"db.dbName",defaultValue:r.db.dbName},{flag:"--dir",path:"db.databaseDirectory",defaultValue:r.db.databaseDirectory},{flag:"--wal",path:"db.walFileName",defaultValue:r.db.walFileName},{flag:"--interval",path:"db.walInterval",defaultValue:r.db.walInterval},{flag:"--encryptionKey",path:"db.encryptionKey",defaultValue:r.db.encryptionKey},{flag:"--maxWrites",path:"db.maxWriteOpsBeforeFlush",defaultValue:r.db.maxWriteOpsBeforeFlush},{flag:"--debug",path:"db.debug",isFlag:!0,defaultValue:r.db.debug},{path:"events",defaultValue:r.events},{flag:"--port",path:"server.port",defaultValue:r.server.port},{flag:"--host",path:"server.host",defaultValue:r.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:r.server.useHttps},{flag:"--http2",path:"server.useHttp2",isFlag:!0,defaultValue:r.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:r.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:r.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:r.server.sslCa},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:r.server.debug}]}).get();te(s)}catch(r){console.error(r)}}}xe();export{C as MikroDB};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment