@je-es/server 0.0.8 → 0.0.9

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/dist/main.d.cts CHANGED
@@ -103,6 +103,20 @@ declare class Logger$1 {
103
103
  statusCode: number
104
104
  }
105
105
 
106
+ interface StaticConfig$1 {
107
+ path : string // URL path prefix (e.g., '/public' or '/static')
108
+ directory : string // Local directory to serve from
109
+ maxAge? : number // Cache control in seconds (default: 3600)
110
+ index? : string[] // Index files (default: ['index.html'])
111
+ dotfiles? : 'allow' | 'deny' | 'ignore' // How to handle dotfiles (default: 'deny')
112
+ etag? : boolean // Enable ETag headers (default: true)
113
+ lastModified? : boolean // Enable Last-Modified headers (default: true)
114
+ immutable? : boolean // Add immutable to cache-control (default: false)
115
+ extensions? : string[] // Try these extensions if file not found (e.g., ['html', 'htm'])
116
+ fallthrough? : boolean // Continue to next handler if file not found (default: false)
117
+ setHeaders? : (ctx: AppContext, path: string) => void // Custom header setter
118
+ }
119
+
106
120
  interface CookieOptions {
107
121
  maxAge? : number
108
122
  expires? : Date
@@ -193,6 +207,9 @@ declare class Logger$1 {
193
207
 
194
208
  logging? : boolean | { level?: 'debug' | 'info' | 'warn' | 'error'; pretty?: boolean }
195
209
 
210
+ // Static file serving
211
+ static? : StaticConfig$1 | StaticConfig$1[]
212
+
196
213
  routes? : RouteDefinition[]
197
214
  middlewares? : AppMiddleware[]
198
215
 
@@ -346,6 +363,58 @@ declare function unique(col: ColumnDefinition): ColumnDefinition;
346
363
  declare function defaultValue(col: ColumnDefinition, value: SqlValue): ColumnDefinition;
347
364
  declare function references(col: ColumnDefinition, table: string, column: string): ColumnDefinition;
348
365
 
366
+ interface StaticConfig {
367
+ path: string;
368
+ directory: string;
369
+ maxAge?: number;
370
+ index?: string[];
371
+ dotfiles?: 'allow' | 'deny' | 'ignore';
372
+ etag?: boolean;
373
+ lastModified?: boolean;
374
+ immutable?: boolean;
375
+ extensions?: string[];
376
+ fallthrough?: boolean;
377
+ setHeaders?: (ctx: AppContext, path: string) => void;
378
+ }
379
+ declare class StaticFileServer {
380
+ private config;
381
+ private resolvedDir;
382
+ private fileCache;
383
+ private readonly CACHE_MAX_SIZE;
384
+ constructor(config: StaticConfig);
385
+ /**
386
+ * Create request handler for static files
387
+ */
388
+ handler(): (ctx: AppContext) => Promise<Response>;
389
+ /**
390
+ * Get URL path pattern for router
391
+ */
392
+ getPathPattern(): string;
393
+ private resolveFilePath;
394
+ private isPathSafe;
395
+ private serveDirectory;
396
+ private serveFile;
397
+ private buildHeaders;
398
+ private generateEtag;
399
+ private getMimeType;
400
+ private handleNotFound;
401
+ /**
402
+ * Clear file cache
403
+ */
404
+ clearCache(): void;
405
+ /**
406
+ * Get cache statistics
407
+ */
408
+ getCacheStats(): {
409
+ entries: number;
410
+ maxSize: number;
411
+ };
412
+ }
413
+ /**
414
+ * Helper function to create static file server
415
+ */
416
+ declare function createStatic(config: StaticConfig): StaticFileServer;
417
+
349
418
  declare function server(config?: ServerConfig): ServerInstance;
350
419
 
351
- export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type ColumnDefinition, type ColumnType, type CookieOptions, type CorsConfig, type CsrfConfig, DB, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, Logger$1 as Logger, type QueryBuilder, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type SqlValue, type TableSchema, TimeoutError, ValidationError, type WhereCondition, blob, column, server as default, defaultValue, integer, notNull, numeric, primaryKey, real, references, server, table, text, unique };
420
+ export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type ColumnDefinition, type ColumnType, type CookieOptions, type CorsConfig, type CsrfConfig, DB, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, Logger$1 as Logger, type QueryBuilder, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type SqlValue, type StaticConfig, StaticFileServer, type TableSchema, TimeoutError, ValidationError, type WhereCondition, blob, column, createStatic, server as default, defaultValue, integer, notNull, numeric, primaryKey, real, references, server, table, text, unique };
package/dist/main.d.ts CHANGED
@@ -103,6 +103,20 @@ declare class Logger$1 {
103
103
  statusCode: number
104
104
  }
105
105
 
106
+ interface StaticConfig$1 {
107
+ path : string // URL path prefix (e.g., '/public' or '/static')
108
+ directory : string // Local directory to serve from
109
+ maxAge? : number // Cache control in seconds (default: 3600)
110
+ index? : string[] // Index files (default: ['index.html'])
111
+ dotfiles? : 'allow' | 'deny' | 'ignore' // How to handle dotfiles (default: 'deny')
112
+ etag? : boolean // Enable ETag headers (default: true)
113
+ lastModified? : boolean // Enable Last-Modified headers (default: true)
114
+ immutable? : boolean // Add immutable to cache-control (default: false)
115
+ extensions? : string[] // Try these extensions if file not found (e.g., ['html', 'htm'])
116
+ fallthrough? : boolean // Continue to next handler if file not found (default: false)
117
+ setHeaders? : (ctx: AppContext, path: string) => void // Custom header setter
118
+ }
119
+
106
120
  interface CookieOptions {
107
121
  maxAge? : number
108
122
  expires? : Date
@@ -193,6 +207,9 @@ declare class Logger$1 {
193
207
 
194
208
  logging? : boolean | { level?: 'debug' | 'info' | 'warn' | 'error'; pretty?: boolean }
195
209
 
210
+ // Static file serving
211
+ static? : StaticConfig$1 | StaticConfig$1[]
212
+
196
213
  routes? : RouteDefinition[]
197
214
  middlewares? : AppMiddleware[]
198
215
 
@@ -346,6 +363,58 @@ declare function unique(col: ColumnDefinition): ColumnDefinition;
346
363
  declare function defaultValue(col: ColumnDefinition, value: SqlValue): ColumnDefinition;
347
364
  declare function references(col: ColumnDefinition, table: string, column: string): ColumnDefinition;
348
365
 
366
+ interface StaticConfig {
367
+ path: string;
368
+ directory: string;
369
+ maxAge?: number;
370
+ index?: string[];
371
+ dotfiles?: 'allow' | 'deny' | 'ignore';
372
+ etag?: boolean;
373
+ lastModified?: boolean;
374
+ immutable?: boolean;
375
+ extensions?: string[];
376
+ fallthrough?: boolean;
377
+ setHeaders?: (ctx: AppContext, path: string) => void;
378
+ }
379
+ declare class StaticFileServer {
380
+ private config;
381
+ private resolvedDir;
382
+ private fileCache;
383
+ private readonly CACHE_MAX_SIZE;
384
+ constructor(config: StaticConfig);
385
+ /**
386
+ * Create request handler for static files
387
+ */
388
+ handler(): (ctx: AppContext) => Promise<Response>;
389
+ /**
390
+ * Get URL path pattern for router
391
+ */
392
+ getPathPattern(): string;
393
+ private resolveFilePath;
394
+ private isPathSafe;
395
+ private serveDirectory;
396
+ private serveFile;
397
+ private buildHeaders;
398
+ private generateEtag;
399
+ private getMimeType;
400
+ private handleNotFound;
401
+ /**
402
+ * Clear file cache
403
+ */
404
+ clearCache(): void;
405
+ /**
406
+ * Get cache statistics
407
+ */
408
+ getCacheStats(): {
409
+ entries: number;
410
+ maxSize: number;
411
+ };
412
+ }
413
+ /**
414
+ * Helper function to create static file server
415
+ */
416
+ declare function createStatic(config: StaticConfig): StaticFileServer;
417
+
349
418
  declare function server(config?: ServerConfig): ServerInstance;
350
419
 
351
- export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type ColumnDefinition, type ColumnType, type CookieOptions, type CorsConfig, type CsrfConfig, DB, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, Logger$1 as Logger, type QueryBuilder, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type SqlValue, type TableSchema, TimeoutError, ValidationError, type WhereCondition, blob, column, server as default, defaultValue, integer, notNull, numeric, primaryKey, real, references, server, table, text, unique };
420
+ export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type ColumnDefinition, type ColumnType, type CookieOptions, type CorsConfig, type CsrfConfig, DB, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, Logger$1 as Logger, type QueryBuilder, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type SqlValue, type StaticConfig, StaticFileServer, type TableSchema, TimeoutError, ValidationError, type WhereCondition, blob, column, createStatic, server as default, defaultValue, integer, notNull, numeric, primaryKey, real, references, server, table, text, unique };
package/dist/main.js CHANGED
@@ -1,10 +1,3 @@
1
- import {Database}from'bun:sqlite';import ce from'crypto';var L=class{constructor(e=":memory:"){this.schemas=new Map;this.currentQuery="";this.currentParams=[];this.db=new Database(e),this.db.exec("PRAGMA foreign_keys = ON");}close(){this.db.close();}defineSchema(e){this.schemas.set(e.name,e);let r=this.generateCreateTableSQL(e);if(this.db.exec(r),e.indexes)for(let t of e.indexes){let o=`CREATE ${t.unique?"UNIQUE":""} INDEX IF NOT EXISTS ${t.name} ON ${e.name} (${t.columns.join(", ")})`;this.db.exec(o);}}getSchema(e){return this.schemas.get(e)}listTables(){return this.db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all().map(r=>r.name)}dropTable(e){this.db.exec(`DROP TABLE IF EXISTS ${e}`),this.schemas.delete(e);}query(){return this.reset(),this.createQueryBuilder()}find(e,r){let t=Object.entries(r).map(([n,o])=>({column:n,operator:"=",value:o}));return this.query().select().from(e).where(t).execute()}findOne(e,r){return this.query().select().from(e).where(Object.entries(r).map(([t,n])=>({column:t,operator:"=",value:n}))).limit(1).executeOne()}findById(e,r){return this.findOne(e,{id:r})}all(e){return this.query().select().from(e).execute()}insert(e,r){this.query().insert(e,r).execute();let t=this.db.query("SELECT last_insert_rowid() as id").get();return this.findById(e,t.id)}update(e,r,t){return this.query().update(e,t).where({column:"id",operator:"=",value:r}).execute(),this.findById(e,r)}delete(e,r){this.query().delete(e).where({column:"id",operator:"=",value:r}).execute();return true}transaction(e){this.db.exec("BEGIN TRANSACTION");try{e(this),this.db.exec("COMMIT");}catch(r){throw this.db.exec("ROLLBACK"),r}}exec(e){this.db.exec(e);}raw(e,r=[]){return this.db.query(e).all(...r)}rawOne(e,r=[]){return this.db.query(e).get(...r)}reset(){this.currentQuery="",this.currentParams=[];}createQueryBuilder(){let e={_select:["*"],_from:"",_where:[],_orderBy:"",_limit:null,_offset:null,_isInsert:false,_isUpdate:false,_isDelete:false,_insertData:null,_updateData:null},r=this;return e.select=function(t){return this._select=t||["*"],this},e.from=function(t){return this._from=t,this},e.where=function(t){let o=(Array.isArray(t)?t:[t]).map(u=>{if(u.operator==="IS NULL"||u.operator==="IS NOT NULL")return `${u.column} ${u.operator}`;if(u.operator==="IN"&&Array.isArray(u.value)){let a=u.value.map(()=>"?").join(", ");return u.value.forEach(d=>{r.currentParams.push(d);}),`${u.column} IN (${a})`}else return r.currentParams.push(u.value),`${u.column} ${u.operator} ?`});return this._where.push(...o),this},e.and=function(t){return this.where(t)},e.or=function(t){if(t.operator==="IS NULL"||t.operator==="IS NOT NULL")this._where.push(`OR ${t.column} ${t.operator}`);else if(t.operator==="IN"&&Array.isArray(t.value)){let n=t.value.map(()=>"?").join(", ");t.value.forEach(o=>{r.currentParams.push(o);}),this._where.push(`OR ${t.column} IN (${n})`);}else r.currentParams.push(t.value),this._where.push(`OR ${t.column} ${t.operator} ?`);return this},e.orderBy=function(t,n="ASC"){return this._orderBy=`ORDER BY ${t} ${n}`,this},e.limit=function(t){return this._limit=t,this},e.offset=function(t){return this._offset=t,this},e.insert=function(t,n){return this._isInsert=true,this._from=t,this._insertData=n,this},e.update=function(t,n){return this._isUpdate=true,this._from=t,this._updateData=n,this},e.delete=function(t){return this._isDelete=true,this._from=t,this},e.raw=function(t,n=[]){return r.currentQuery=t,r.currentParams=n,this},e.execute=function(){let t="";if(this._isInsert&&this._insertData){let u=Object.keys(this._insertData),a=u.map(()=>"?").join(", ");t=`INSERT INTO ${this._from} (${u.join(", ")}) VALUES (${a})`,r.currentParams=Object.values(this._insertData);}else if(this._isUpdate&&this._updateData){let u=Object.keys(this._updateData).map(d=>`${d} = ?`),a=Object.values(this._updateData);r.currentParams=[...a,...r.currentParams],t=`UPDATE ${this._from} SET ${u.join(", ")}`,this._where.length>0&&(t+=` WHERE ${this._where.join(" AND ")}`);}else this._isDelete?(t=`DELETE FROM ${this._from}`,this._where.length>0&&(t+=` WHERE ${this._where.join(" AND ")}`)):(t=`SELECT ${this._select.join(", ")} FROM ${this._from}`,this._where.length>0&&(t+=` WHERE ${this._where.join(" AND ")}`),this._orderBy&&(t+=` ${this._orderBy}`),this._limit!==null&&(t+=` LIMIT ${this._limit}`),this._offset!==null&&(t+=` OFFSET ${this._offset}`));!t&&r.currentQuery&&(t=r.currentQuery);let o=r.db.query(t).all(...r.currentParams);return r.reset(),o},e.executeOne=function(){let t=this.execute();return t.length>0?t[0]:null},e.executeRaw=function(t,n=[]){return r.db.query(t).all(...n)},e}generateCreateTableSQL(e){let r=e.columns.map(t=>{let n=`${t.name} ${t.type}`;return t.primaryKey&&(n+=" PRIMARY KEY",t.autoIncrement&&(n+=" AUTOINCREMENT")),t.notNull&&!t.primaryKey&&(n+=" NOT NULL"),t.unique&&(n+=" UNIQUE"),t.default!==void 0&&(typeof t.default=="string"?n+=` DEFAULT '${t.default}'`:t.default===null?n+=" DEFAULT NULL":n+=` DEFAULT ${t.default}`),t.references&&(n+=` REFERENCES ${t.references.table}(${t.references.column})`),n});return `CREATE TABLE IF NOT EXISTS ${e.name} (${r.join(", ")})`}};function Y(s,e){return {name:s,columns:e}}function Z(s,e){return {name:s,type:e}}function ee(s){return {name:s,type:"INTEGER"}}function te(s){return {name:s,type:"TEXT"}}function re(s){return {name:s,type:"REAL"}}function ne(s){return {name:s,type:"BLOB"}}function se(s){return {name:s,type:"NUMERIC"}}function oe(s,e=false){return {...s,primaryKey:true,autoIncrement:e}}function ie(s){return {...s,notNull:true}}function ae(s){return {...s,unique:true}}function ue(s,e){return {...s,default:e}}function le(s,e,r){return {...s,references:{table:e,column:r}}}var _=class{constructor(){this.routes=new Map;this.regexRoutes=[];}match(e,r){let t=`${e}:${r}`;if(this.routes.has(t))return {handler:this.routes.get(t),params:{}};for(let n of this.regexRoutes)if(n.method===e){let o=r.match(n.pattern);if(o?.groups)return {handler:n.handler,params:o.groups}}return null}getAll(){let e=Array.from(this.routes.entries()).map(([t,n])=>{let o=t.indexOf(":"),u=t.substring(0,o),a=t.substring(o+1);return {method:u,path:a,handler:n}}),r=this.regexRoutes.map(t=>{let n=t.key.indexOf(":");return {method:t.method,path:t.key.substring(n+1),handler:t.handler}});return [...e,...r]}clear(){this.routes.clear(),this.regexRoutes=[];}remove(e,r){let t=`${e}:${r}`;if(this.routes.has(t))return this.routes.delete(t),true;let n=this.regexRoutes.findIndex(o=>o.key===t);return n>=0?(this.regexRoutes.splice(n,1),true):false}register(e,r,t,n={}){let o=`${e}:${r}`;if(r.includes(":")){let u=this.pathToRegex(r),a=this.regexRoutes.findIndex(m=>m.key===o),d={pattern:u,method:e,handler:t,key:o};a>=0?this.regexRoutes[a]=d:this.regexRoutes.push(d);}else this.routes.set(o,t);}pathToRegex(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/:(\w+)/g,"(?<$1>[^/]+)");return new RegExp(`^${t}$`)}};var N=class{constructor(){this.rateLimitStore=new Map;this.csrfTokens=new Map;this.requestLog=new Map;this.MAX_REQUEST_LOG_SIZE=1e3;}checkRateLimit(e,r,t){let n=Date.now(),o=this.rateLimitStore.get(e);return o?n<o.reset?o.count>=r?false:(o.count++,true):(this.rateLimitStore.set(e,{count:1,reset:n+t}),true):(this.rateLimitStore.set(e,{count:1,reset:n+t}),true)}cleanupRateLimit(){let e=Date.now();for(let[r,t]of this.rateLimitStore.entries())e>t.reset&&this.rateLimitStore.delete(r);}generateCsrfToken(e,r=36e5){let t=ce.randomBytes(32).toString("hex");return this.csrfTokens.set(t,{sessionId:e,expires:Date.now()+r}),t}validateCsrfToken(e,r){let t=this.csrfTokens.get(e);return t?Date.now()>t.expires?(this.csrfTokens.delete(e),false):t.sessionId===r?(this.csrfTokens.delete(e),true):false:false}cleanupCsrfTokens(){let e=Date.now();for(let[r,t]of this.csrfTokens.entries())e>t.expires&&this.csrfTokens.delete(r);}sanitizeHtml(e){return e?e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;"):""}sanitizeSql(e){return e?e.replace(/\\/g,"\\\\").replace(/;/g,"").replace(/'/g,"''").replace(/"/g,'\\"').replace(/\x00/g,""):""}logRequest(e,r,t,n,o,u){if(this.requestLog.set(e,{timestamp:new Date().toISOString(),method:r,path:t,ip:n,status:o,duration:u}),this.requestLog.size>this.MAX_REQUEST_LOG_SIZE){let{value:a}=this.requestLog.keys().next()||{value:null};a&&this.requestLog.delete(a);}}getRequestLog(e){return this.requestLog.get(e)}getAllRequestLogs(){return Array.from(this.requestLog.values())}clearAll(){this.rateLimitStore.clear(),this.csrfTokens.clear(),this.requestLog.clear();}getStats(){return {rateLimitEntries:this.rateLimitStore.size,csrfTokens:this.csrfTokens.size,requestLogs:this.requestLog.size}}};var k=class{constructor(e="info",r=false){this.level=1;this.pretty=false;this.levels={debug:0,info:1,warn:2,error:3,fatal:4};this.colors={reset:"\x1B[0m",gray:"\x1B[90m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",magenta:"\x1B[35m",bold:"\x1B[1m"};this.level=this.levels[e]??1,this.pretty=r;}debug(e,r){this.log("debug",this.levels.debug,e,r);}info(e,r){this.log("info",this.levels.info,e,r);}warn(e,r){this.log("warn",this.levels.warn,e,r);}error(e,r){this.log("error",this.levels.error,e,r);}fatal(e,r){this.log("fatal",this.levels.fatal,e,r),process.env.NODE_ENV;}log(e,r,t,n){if(r<this.level)return;let o=t??{};if(this.pretty)this.prettyLog(e,o,n);else {let a={timestamp:new Date().toISOString(),level:e.toUpperCase(),message:n||"No message",...o},d=JSON.stringify(a);e==="error"||e==="fatal"?console.error(d):e==="warn"?console.warn(d):console.log(d);}}prettyLog(e,r,t){let n=this.colors,o=new Date().toLocaleTimeString("en-US",{hour12:false});if(r.method&&r.path&&r.status!==void 0){let p=this.colorizeMethod(r.method),w=this.colorizeStatus(r.status),b=r.duration?`${r.duration}ms`:"",l=r.path;console.log(`${n.gray}${o}${n.reset} ${p} ${n.bold}${l}${n.reset} ${w} ${n.gray}${b}${n.reset}`);return}if(t==="Route added"&&r.method&&r.path){let p=Array.isArray(r.method)?r.method.join("|"):r.method;console.log(`${n.gray}${o}${n.reset} ${n.cyan}\u2192${n.reset} ${n.bold}${p.padEnd(6)}${n.reset} ${r.path}`);return}if(t==="\u2714 Database connected"){console.log(`${n.gray}${o}${n.reset} ${n.green}\u2713${n.reset} Database connected ${n.gray}(${r.name})${n.reset}`);return}if(t==="Server started"){console.log(`${n.gray}${o}${n.reset} ${n.green}\u2713${n.reset} Server started at ${n.cyan}${r.url}${n.reset}`);return}let u=this.getLevelIcon(e),a=this.getLevelColor(e),d=`${n.gray}${o}${n.reset} ${a}${u}${n.reset} `;t&&(d+=`${t} `);let m=Object.keys(r).filter(p=>!["timestamp","level","message"].includes(p));if(m.length>0){let p=m.map(w=>{let b=r[w];return typeof b=="string"||typeof b=="number"?`${n.gray}${w}:${n.reset}${b}`:null}).filter(Boolean);p.length>0&&(d+=n.gray+p.join(" ")+n.reset);}console.log(d);}colorizeMethod(e){let r=this.colors,t=e.toUpperCase();switch(t){case "GET":return `${r.green}${t}${r.reset}`;case "POST":return `${r.cyan}${t}${r.reset}`;case "PUT":return `${r.yellow}${t}${r.reset}`;case "DELETE":return `${r.red}${t}${r.reset}`;case "PATCH":return `${r.magenta}${t}${r.reset}`;default:return `${r.gray}${t}${r.reset}`}}colorizeStatus(e){if(!e)return "";let r=this.colors,t=e.toString();return e>=200&&e<300?`${r.green}${t}${r.reset}`:e>=300&&e<400?`${r.cyan}${t}${r.reset}`:e>=400&&e<500?`${r.yellow}${t}${r.reset}`:e>=500?`${r.red}${t}${r.reset}`:`${r.gray}${t}${r.reset}`}getLevelIcon(e){switch(e){case "debug":return "\u25C6";case "info":return "\u2192";case "warn":return "\u26A0";case "error":return "\u2716";case "fatal":return "\u2620";default:return "\u2022"}}getLevelColor(e){let r=this.colors;switch(e){case "debug":return r.gray;case "info":return r.cyan;case "warn":return r.yellow;case "error":return r.red;case "fatal":return r.red+r.bold;default:return r.reset}}};var E=class extends Error{constructor(r,t=500,n){super(r);this.message=r;this.statusCode=t;this.code=n;this.name="AppError";}},T=class extends E{constructor(r,t){super(r,400,"VALIDATION_ERROR");this.issues=t;this.name="ValidationError";}},W=class extends E{constructor(e){super(e,500,"DATABASE_ERROR"),this.name="DatabaseError";}},P=class extends E{constructor(e="Request timeout"){super(e,408,"TIMEOUT_ERROR"),this.name="TimeoutError";}},G=class extends E{constructor(e="Too many requests"){super(e,429,"RATE_LIMIT_ERROR"),this.name="RateLimitError";}};var B=new N,q=new _;function pe(s={}){let e=Number(s.port)||3e3,r=s.hostname||"localhost",t=s.maxRequestSize||10*1024*1024,n=s.requestTimeout||3e4,o=s.gracefulShutdownTimeout||1e4,u=typeof s.logging=="object"?s.logging:{},a=s.logging?new k(u.level||"info",u.pretty):null,d=new Map,m=[],p=new Set,w=setInterval(()=>{B.cleanupRateLimit(),B.cleanupCsrfTokens();},120*1e3);async function b(i){let f=Date.now(),c=crypto.randomUUID(),C=new URL(i.url).pathname,v=i.method.toUpperCase(),y=me(i);p.add(c);try{let x=i.headers.get("content-length");if(x&&parseInt(x)>t)return a?.warn({requestId:c,size:x,ip:y},"Request too large"),new Response(JSON.stringify({error:"Payload too large"}),{status:413,headers:{"Content-Type":"application/json"}});let I=fe(i,s);if(v==="OPTIONS")return new Response(null,{status:204,headers:I});if(s.security&&typeof s.security=="object"&&s.security.rateLimit){let S=typeof s.security.rateLimit=="object"?s.security.rateLimit:{},A=S.max||100,M=S.windowMs||6e4,F=S.keyGenerator?S.keyGenerator({request:i,ip:y}):y;if(!B.checkRateLimit(F,A,M))return a?.warn({requestId:c,ip:y,key:F},"Rate limit exceeded"),new Response(JSON.stringify({error:S.message||"Too many requests"}),{status:429,headers:{"Content-Type":"application/json"}})}let V=null;["POST","PUT","PATCH"].includes(v)&&(V=await ge(i,a,t));let H=d.get("default"),j=q.match(v,C);if(!j){let S=X(y,i,{},H,a,c);return a?.warn({requestId:c,method:v,path:C,ip:y},"Route not found"),S.json({error:"Not Found",path:C},404)}let U=X(y,i,j.params||{},H,a,c);U.body=V,U.request=i;let Q=new AbortController,K=new Promise((S,A)=>{let M=setTimeout(()=>{Q.abort(),A(new P("Request timeout"));},n);Q.signal.addEventListener("abort",()=>clearTimeout(M));}),D=await Promise.race([j.handler(U),K]),$=new Headers(D.headers);I.forEach((S,A)=>{$.has(A)||$.set(A,S);}),$.set("X-Request-ID",c),$.set("X-Content-Type-Options","nosniff"),$.set("X-Frame-Options","DENY"),$.set("X-XSS-Protection","1; mode=block"),$.set("Referrer-Policy","strict-origin-when-cross-origin");let z=Date.now()-f;return B.logRequest(c,v,C,y,D.status,z),a?.info({requestId:c,method:v,path:C,status:D.status,duration:z,ip:y},"Request completed"),new Response(D.body,{status:D.status,headers:$})}catch(x){if(x instanceof E)return a?.warn({error:x.message,requestId:c,ip:y},`App error: ${x.message}`),new Response(JSON.stringify({error:x.message,code:x.code,requestId:c}),{status:x.statusCode,headers:{"Content-Type":"application/json"}});a?.error({error:String(x),requestId:c,ip:y},"Unhandled error");let I=process.env.NODE_ENV==="production"?"Internal Server Error":x.message;return new Response(JSON.stringify({error:I,requestId:c}),{status:500,headers:{"Content-Type":"application/json"}})}finally{p.delete(c);}}let l={method:"GET",path:"/health",handler:i=>i.json({status:"healthy",timestamp:new Date().toISOString(),uptime:process.uptime(),activeRequests:p.size})},g={method:"GET",path:"/readiness",handler:i=>{let f=d.size>0,c=f||d.size===0;return i.json({ready:c,checks:{database:f?"connected":"not configured",activeRequests:p.size},timestamp:new Date().toISOString()},c?200:503)}};s.routes&&s.routes.forEach(i=>{m.push(i),(Array.isArray(i.method)?i.method:[i.method]).forEach(c=>{q.register(c,i.path,i.handler,i);});}),m.push(l,g),q.register("GET","/health",l.handler,l),q.register("GET","/readiness",g.handler,g);let h=null,R={app:null,logger:a,db:d,bunServer:null,async start(){if(s.database){let f=Array.isArray(s.database)?s.database:[s.database];for(let c of f){let O=c.name||"default";try{if(typeof c.connection=="string"){let C=new L(c.connection);if(c.schema&&typeof c.schema=="object")for(let[v,y]of Object.entries(c.schema))y&&typeof y=="object"&&C.defineSchema(y);d.set(O,C),a?.info({name:O,connection:c.connection},"\u2714 Database connected");}else throw new Error(`Database connection must be a string path (got ${typeof c.connection})`)}catch(C){throw a?.error({error:String(C),name:O},"Failed to connect to database"),C}}}h=Bun.serve({port:e,hostname:r,fetch:b}),R.bunServer=h;let i=`http://${r}:${e}`;console.log(`\u2192 URL: ${i}
2
- \u2192 Environment: ${process.env.NODE_ENV||"development"}
3
- \u2192 Routes: ${m.length.toString()}
4
- \u2192 Database: ${d.size>0?"\u2714 Connected":"\u274C None"}
5
- \u2192 Security: ${s.security?"\u2714 ENABLED":"\u274C Disabled"}
6
-
7
- \u{1F50D} Health: ${i}/health
8
- \u{1F50D} Ready: ${i}/readiness
9
- `),a?.info({url:i},"Server started");},async stop(){if(a?.info("Stopping server..."),p.size>0){a?.info({count:p.size},"Waiting for active requests...");let i=Date.now()+o;for(;p.size>0&&Date.now()<i;)await new Promise(f=>setTimeout(f,100));p.size>0&&a?.warn({count:p.size},"Force closing with active requests");}if(clearInterval(w),s.onShutdown)try{await s.onShutdown();}catch(i){a?.error({error:String(i)},"Error in shutdown handler");}for(let[i,f]of d.entries())try{f&&typeof f.close=="function"&&f.close(),a?.info({name:i},"Database closed");}catch(c){a?.error({error:String(c),name:i},"Error closing database");}h&&(h.stop(),a?.info("Bun server stopped")),a?.info("Server stopped successfully");},addRoute(i){m.push(i),(Array.isArray(i.method)?i.method:[i.method]).forEach(c=>{q.register(c,i.path,i.handler,i);}),a?.info({method:i.method,path:i.path},"Route added");},addRoutes(i){i.forEach(f=>this.addRoute(f));},getRoutes(){return m}};return R}async function ge(s,e,r){let t=s.headers.get("content-type")||"";try{if(t.includes("application/json")){let n=await s.text();if(n.length>r)throw new T("Payload too large");if(!n.trim())return {};try{return JSON.parse(n)}catch(o){throw e?.warn({error:String(o),bodyPreview:n.substring(0,100)},"Invalid JSON in request body"),new T("Invalid JSON in request body")}}if(t.includes("application/x-www-form-urlencoded")){let n=await s.text();if(n.length>r)throw new T("Payload too large");return Object.fromEntries(new URLSearchParams(n))}if(t.includes("multipart/form-data"))return await s.formData()}catch(n){throw n instanceof T?n:(e?.error({error:String(n)},"Error parsing request body"),new T("Failed to parse request body"))}return {}}function he(s){let e=new Map;if(!s)return e;let r=s.split(";");for(let t of r){let[n,...o]=t.trim().split("=");if(n){let u=o.join("=");e.set(n,u?decodeURIComponent(u):"");}}return e}function X(s,e,r,t,n,o){let u=new URL(e.url),a=Object.fromEntries(u.searchParams),d=e.headers,m=200,p=new Map,w=he(d.get("cookie")||""),b={ip:s,request:e,params:r,query:a,headers:d,db:t,logger:n,requestId:o,get statusCode(){return m},set statusCode(l){m=l;},body:null,json(l,g){return new Response(JSON.stringify(l),{status:g??m,headers:{"Content-Type":"application/json",...this._setCookieHeaders()}})},text(l,g){return new Response(l,{status:g??m,headers:{"Content-Type":"text/plain",...this._setCookieHeaders()}})},html(l,g){return new Response(l,{status:g??m,headers:{"Content-Type":"text/html; charset=utf-8",...this._setCookieHeaders()}})},redirect(l,g=302){return new Response(null,{status:g,headers:{Location:l,...this._setCookieHeaders()}})},file(l,g="application/octet-stream"){let h=Bun.file(l);return new Response(h,{headers:{"Content-Type":g,...this._setCookieHeaders()}})},setCookie(l,g,h={}){let R=`${l}=${encodeURIComponent(g)}`;return h.maxAge!==void 0&&(R+=`; Max-Age=${h.maxAge}`),h.expires&&(R+=`; Expires=${h.expires.toUTCString()}`),h.path&&(R+=`; Path=${h.path}`),h.domain&&(R+=`; Domain=${h.domain}`),h.secure&&(R+="; Secure"),h.httpOnly&&(R+="; HttpOnly"),h.sameSite&&(R+=`; SameSite=${h.sameSite}`),p.set(l,R),b},getCookie(l){return w.get(l)},deleteCookie(l,g={}){return b.setCookie(l,"",{...g,maxAge:0,path:g.path||"/"})},setHeader(l,g){return d.set(l,g),b},getHeader(l){return d.get(l)||void 0},status(l){return m=l,b},_setCookieHeaders(){let l={};return p.size>0&&(l["Set-Cookie"]=Array.from(p.values())),l}};return b}function me(s){let e=s.headers.get("x-forwarded-for");if(e)return e.split(",").map(n=>n.trim())[0]||"unknown";let r=s.headers.get("x-real-ip");return r||"unknown"}function fe(s,e){let r=new Headers;if(!e.security||typeof e.security!="object"||!e.security.cors)return r;let t=typeof e.security.cors=="object"?e.security.cors:{},n=s.headers.get("Origin");if(n){typeof t.origin=="function"?t.origin(n)&&r.set("Access-Control-Allow-Origin",n):Array.isArray(t.origin)?t.origin.includes(n)&&r.set("Access-Control-Allow-Origin",n):typeof t.origin=="string"?r.set("Access-Control-Allow-Origin",t.origin):r.set("Access-Control-Allow-Origin",n);let o=t.methods||["GET","POST","PUT","DELETE","PATCH","OPTIONS"];r.set("Access-Control-Allow-Methods",o.join(", "));let u=t.allowedHeaders||["Content-Type","Authorization","X-Requested-With"];r.set("Access-Control-Allow-Headers",u.join(", ")),t.credentials&&r.set("Access-Control-Allow-Credentials","true"),t.maxAge&&r.set("Access-Control-Max-Age",t.maxAge.toString());}return r}var ve=pe;export{E as AppError,L as DB,W as DatabaseError,k as Logger,G as RateLimitError,_ as Router,N as SecurityManager,P as TimeoutError,T as ValidationError,ne as blob,Z as column,ve as default,ue as defaultValue,ee as integer,ie as notNull,se as numeric,oe as primaryKey,re as real,le as references,pe as server,Y as table,te as text,ae as unique};//# sourceMappingURL=main.js.map
1
+ import {Database}from'bun:sqlite';import me from'crypto';import {resolve,join,relative,extname}from'path';import {existsSync,statSync}from'fs';var _=class{constructor(e=":memory:"){this.schemas=new Map;this.currentQuery="";this.currentParams=[];this.db=new Database(e),this.db.exec("PRAGMA foreign_keys = ON");}close(){this.db.close();}defineSchema(e){this.schemas.set(e.name,e);let r=this.generateCreateTableSQL(e);if(this.db.exec(r),e.indexes)for(let t of e.indexes){let i=`CREATE ${t.unique?"UNIQUE":""} INDEX IF NOT EXISTS ${t.name} ON ${e.name} (${t.columns.join(", ")})`;this.db.exec(i);}}getSchema(e){return this.schemas.get(e)}listTables(){return this.db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all().map(r=>r.name)}dropTable(e){this.db.exec(`DROP TABLE IF EXISTS ${e}`),this.schemas.delete(e);}query(){return this.reset(),this.createQueryBuilder()}find(e,r){let t=Object.entries(r).map(([n,i])=>({column:n,operator:"=",value:i}));return this.query().select().from(e).where(t).execute()}findOne(e,r){return this.query().select().from(e).where(Object.entries(r).map(([t,n])=>({column:t,operator:"=",value:n}))).limit(1).executeOne()}findById(e,r){return this.findOne(e,{id:r})}all(e){return this.query().select().from(e).execute()}insert(e,r){this.query().insert(e,r).execute();let t=this.db.query("SELECT last_insert_rowid() as id").get();return this.findById(e,t.id)}update(e,r,t){return this.query().update(e,t).where({column:"id",operator:"=",value:r}).execute(),this.findById(e,r)}delete(e,r){this.query().delete(e).where({column:"id",operator:"=",value:r}).execute();return true}transaction(e){this.db.exec("BEGIN TRANSACTION");try{e(this),this.db.exec("COMMIT");}catch(r){throw this.db.exec("ROLLBACK"),r}}exec(e){this.db.exec(e);}raw(e,r=[]){return this.db.query(e).all(...r)}rawOne(e,r=[]){return this.db.query(e).get(...r)}reset(){this.currentQuery="",this.currentParams=[];}createQueryBuilder(){let e={_select:["*"],_from:"",_where:[],_orderBy:"",_limit:null,_offset:null,_isInsert:false,_isUpdate:false,_isDelete:false,_insertData:null,_updateData:null},r=this;return e.select=function(t){return this._select=t||["*"],this},e.from=function(t){return this._from=t,this},e.where=function(t){let i=(Array.isArray(t)?t:[t]).map(o=>{if(o.operator==="IS NULL"||o.operator==="IS NOT NULL")return `${o.column} ${o.operator}`;if(o.operator==="IN"&&Array.isArray(o.value)){let a=o.value.map(()=>"?").join(", ");return o.value.forEach(d=>{r.currentParams.push(d);}),`${o.column} IN (${a})`}else return r.currentParams.push(o.value),`${o.column} ${o.operator} ?`});return this._where.push(...i),this},e.and=function(t){return this.where(t)},e.or=function(t){if(t.operator==="IS NULL"||t.operator==="IS NOT NULL")this._where.push(`OR ${t.column} ${t.operator}`);else if(t.operator==="IN"&&Array.isArray(t.value)){let n=t.value.map(()=>"?").join(", ");t.value.forEach(i=>{r.currentParams.push(i);}),this._where.push(`OR ${t.column} IN (${n})`);}else r.currentParams.push(t.value),this._where.push(`OR ${t.column} ${t.operator} ?`);return this},e.orderBy=function(t,n="ASC"){return this._orderBy=`ORDER BY ${t} ${n}`,this},e.limit=function(t){return this._limit=t,this},e.offset=function(t){return this._offset=t,this},e.insert=function(t,n){return this._isInsert=true,this._from=t,this._insertData=n,this},e.update=function(t,n){return this._isUpdate=true,this._from=t,this._updateData=n,this},e.delete=function(t){return this._isDelete=true,this._from=t,this},e.raw=function(t,n=[]){return r.currentQuery=t,r.currentParams=n,this},e.execute=function(){let t="";if(this._isInsert&&this._insertData){let o=Object.keys(this._insertData),a=o.map(()=>"?").join(", ");t=`INSERT INTO ${this._from} (${o.join(", ")}) VALUES (${a})`,r.currentParams=Object.values(this._insertData);}else if(this._isUpdate&&this._updateData){let o=Object.keys(this._updateData).map(d=>`${d} = ?`),a=Object.values(this._updateData);r.currentParams=[...a,...r.currentParams],t=`UPDATE ${this._from} SET ${o.join(", ")}`,this._where.length>0&&(t+=` WHERE ${this._where.join(" AND ")}`);}else this._isDelete?(t=`DELETE FROM ${this._from}`,this._where.length>0&&(t+=` WHERE ${this._where.join(" AND ")}`)):(t=`SELECT ${this._select.join(", ")} FROM ${this._from}`,this._where.length>0&&(t+=` WHERE ${this._where.join(" AND ")}`),this._orderBy&&(t+=` ${this._orderBy}`),this._limit!==null&&(t+=` LIMIT ${this._limit}`),this._offset!==null&&(t+=` OFFSET ${this._offset}`));!t&&r.currentQuery&&(t=r.currentQuery);let i=r.db.query(t).all(...r.currentParams);return r.reset(),i},e.executeOne=function(){let t=this.execute();return t.length>0?t[0]:null},e.executeRaw=function(t,n=[]){return r.db.query(t).all(...n)},e}generateCreateTableSQL(e){let r=e.columns.map(t=>{let n=`${t.name} ${t.type}`;return t.primaryKey&&(n+=" PRIMARY KEY",t.autoIncrement&&(n+=" AUTOINCREMENT")),t.notNull&&!t.primaryKey&&(n+=" NOT NULL"),t.unique&&(n+=" UNIQUE"),t.default!==void 0&&(typeof t.default=="string"?n+=` DEFAULT '${t.default}'`:t.default===null?n+=" DEFAULT NULL":n+=` DEFAULT ${t.default}`),t.references&&(n+=` REFERENCES ${t.references.table}(${t.references.column})`),n});return `CREATE TABLE IF NOT EXISTS ${e.name} (${r.join(", ")})`}};function se(s,e){return {name:s,columns:e}}function ie(s,e){return {name:s,type:e}}function oe(s){return {name:s,type:"INTEGER"}}function ae(s){return {name:s,type:"TEXT"}}function ue(s){return {name:s,type:"REAL"}}function le(s){return {name:s,type:"BLOB"}}function ce(s){return {name:s,type:"NUMERIC"}}function de(s,e=false){return {...s,primaryKey:true,autoIncrement:e}}function pe(s){return {...s,notNull:true}}function fe(s){return {...s,unique:true}}function ge(s,e){return {...s,default:e}}function he(s,e,r){return {...s,references:{table:e,column:r}}}var k=class{constructor(){this.routes=new Map;this.regexRoutes=[];}match(e,r){let t=`${e}:${r}`;if(this.routes.has(t))return {handler:this.routes.get(t),params:{}};for(let n of this.regexRoutes)if(n.method===e){let i=r.match(n.pattern);if(i){let o=i.groups||{};return {handler:n.handler,params:o}}}return null}getAll(){let e=Array.from(this.routes.entries()).map(([t,n])=>{let i=t.indexOf(":"),o=t.substring(0,i),a=t.substring(i+1);return {method:o,path:a,handler:n}}),r=this.regexRoutes.map(t=>{let n=t.key.indexOf(":");return {method:t.method,path:t.key.substring(n+1),handler:t.handler}});return [...e,...r]}clear(){this.routes.clear(),this.regexRoutes=[];}remove(e,r){let t=`${e}:${r}`;if(this.routes.has(t))return this.routes.delete(t),true;let n=this.regexRoutes.findIndex(i=>i.key===t);return n>=0?(this.regexRoutes.splice(n,1),true):false}register(e,r,t,n={}){let i=`${e}:${r}`;if(r.includes(":")||r.includes("*")){let o=this.pathToRegex(r),a=this.regexRoutes.findIndex(h=>h.key===i),d={pattern:o,method:e,handler:t,key:i};a>=0?this.regexRoutes[a]=d:this.regexRoutes.push(d);}else this.routes.set(i,t);}pathToRegex(e){let r=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return r=r.replace(/:(\w+)/g,"(?<$1>[^/]+)"),r=r.replace(/\*/g,".*"),new RegExp(`^${r}$`)}};var P=class{constructor(){this.rateLimitStore=new Map;this.csrfTokens=new Map;this.requestLog=new Map;this.MAX_REQUEST_LOG_SIZE=1e3;}checkRateLimit(e,r,t){let n=Date.now(),i=this.rateLimitStore.get(e);return i?n<i.reset?i.count>=r?false:(i.count++,true):(this.rateLimitStore.set(e,{count:1,reset:n+t}),true):(this.rateLimitStore.set(e,{count:1,reset:n+t}),true)}cleanupRateLimit(){let e=Date.now();for(let[r,t]of this.rateLimitStore.entries())e>t.reset&&this.rateLimitStore.delete(r);}generateCsrfToken(e,r=36e5){let t=me.randomBytes(32).toString("hex");return this.csrfTokens.set(t,{sessionId:e,expires:Date.now()+r}),t}validateCsrfToken(e,r){let t=this.csrfTokens.get(e);return t?Date.now()>t.expires?(this.csrfTokens.delete(e),false):t.sessionId===r?(this.csrfTokens.delete(e),true):false:false}cleanupCsrfTokens(){let e=Date.now();for(let[r,t]of this.csrfTokens.entries())e>t.expires&&this.csrfTokens.delete(r);}sanitizeHtml(e){return e?e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;"):""}sanitizeSql(e){return e?e.replace(/\\/g,"\\\\").replace(/;/g,"").replace(/'/g,"''").replace(/"/g,'\\"').replace(/\x00/g,""):""}logRequest(e,r,t,n,i,o){if(this.requestLog.set(e,{timestamp:new Date().toISOString(),method:r,path:t,ip:n,status:i,duration:o}),this.requestLog.size>this.MAX_REQUEST_LOG_SIZE){let{value:a}=this.requestLog.keys().next()||{value:null};a&&this.requestLog.delete(a);}}getRequestLog(e){return this.requestLog.get(e)}getAllRequestLogs(){return Array.from(this.requestLog.values())}clearAll(){this.rateLimitStore.clear(),this.csrfTokens.clear(),this.requestLog.clear();}getStats(){return {rateLimitEntries:this.rateLimitStore.size,csrfTokens:this.csrfTokens.size,requestLogs:this.requestLog.size}}};var j=class{constructor(e="info",r=false){this.level=1;this.pretty=false;this.levels={debug:0,info:1,warn:2,error:3,fatal:4};this.colors={reset:"\x1B[0m",gray:"\x1B[90m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",red:"\x1B[31m",magenta:"\x1B[35m",bold:"\x1B[1m"};this.level=this.levels[e]??1,this.pretty=r;}debug(e,r){this.log("debug",this.levels.debug,e,r);}info(e,r){this.log("info",this.levels.info,e,r);}warn(e,r){this.log("warn",this.levels.warn,e,r);}error(e,r){this.log("error",this.levels.error,e,r);}fatal(e,r){this.log("fatal",this.levels.fatal,e,r),process.env.NODE_ENV;}log(e,r,t,n){if(r<this.level)return;let i,o;if(typeof t=="string"?(o=t,i={}):(i=t??{},o=n),this.pretty)this.prettyLog(e,i,o);else {let d={timestamp:new Date().toISOString(),level:e.toUpperCase(),message:o||"No message",...i},h=JSON.stringify(d);e==="error"||e==="fatal"?console.error(h):e==="warn"?console.warn(h):console.log(h);}}prettyLog(e,r,t){let n=this.colors,i=new Date().toLocaleTimeString("en-US",{hour12:false});if(r.method&&r.path&&r.status!==void 0){let f=this.colorizeMethod(r.method),x=this.colorizeStatus(r.status),C=r.duration?`${r.duration}ms`:"",l=r.path;console.log(`${n.gray}${i}${n.reset} ${f} ${n.bold}${l}${n.reset} ${x} ${n.gray}${C}${n.reset}`);return}if(t==="Route added"&&r.method&&r.path){let f=Array.isArray(r.method)?r.method.join("|"):r.method;console.log(`${n.gray}${i}${n.reset} ${n.cyan}\u2192${n.reset} ${n.bold}${f.padEnd(6)}${n.reset} ${r.path}`);return}if(t==="\u2714 Database connected"){console.log(`${n.gray}${i}${n.reset} ${n.green}\u2713${n.reset} Database connected ${n.gray}(${r.name})${n.reset}`);return}if(t==="Server started"){console.log(`${n.gray}${i}${n.reset} ${n.green}\u2713${n.reset} Server started at ${n.cyan}${r.url}${n.reset}`);return}let o=this.getLevelIcon(e),a=this.getLevelColor(e),d=`${n.gray}${i}${n.reset} ${a}${o}${n.reset} `;t&&(d+=`${t} `);let h=Object.keys(r).filter(f=>!["timestamp","level","message"].includes(f));if(h.length>0){let f=h.map(x=>{let C=r[x];return typeof C=="string"||typeof C=="number"?`${n.gray}${x}:${n.reset}${C}`:null}).filter(Boolean);f.length>0&&(d+=n.gray+f.join(" ")+n.reset);}console.log(d);}colorizeMethod(e){let r=this.colors,t=e.toUpperCase();switch(t){case "GET":return `${r.green}${t}${r.reset}`;case "POST":return `${r.cyan}${t}${r.reset}`;case "PUT":return `${r.yellow}${t}${r.reset}`;case "DELETE":return `${r.red}${t}${r.reset}`;case "PATCH":return `${r.magenta}${t}${r.reset}`;default:return `${r.gray}${t}${r.reset}`}}colorizeStatus(e){if(!e)return "";let r=this.colors,t=e.toString();return e>=200&&e<300?`${r.green}${t}${r.reset}`:e>=300&&e<400?`${r.cyan}${t}${r.reset}`:e>=400&&e<500?`${r.yellow}${t}${r.reset}`:e>=500?`${r.red}${t}${r.reset}`:`${r.gray}${t}${r.reset}`}getLevelIcon(e){switch(e){case "debug":return "-";case "info":return "-";case "warn":return "-";case "error":return "-";case "fatal":return "-";default:return "-"}}getLevelColor(e){let r=this.colors;switch(e){case "debug":return r.gray;case "info":return r.cyan;case "warn":return r.yellow;case "error":return r.red;case "fatal":return r.red+r.bold;default:return r.reset}}};var T=class extends Error{constructor(r,t=500,n){super(r);this.message=r;this.statusCode=t;this.code=n;this.name="AppError";}},D=class extends T{constructor(r,t){super(r,400,"VALIDATION_ERROR");this.issues=t;this.name="ValidationError";}},Y=class extends T{constructor(e){super(e,500,"DATABASE_ERROR"),this.name="DatabaseError";}},H=class extends T{constructor(e="Request timeout"){super(e,408,"TIMEOUT_ERROR"),this.name="TimeoutError";}},Z=class extends T{constructor(e="Too many requests"){super(e,429,"RATE_LIMIT_ERROR"),this.name="RateLimitError";}};var I=class{constructor(e){this.fileCache=new Map;this.CACHE_MAX_SIZE=1e3;if(!existsSync(e.directory))throw new Error(`Static directory does not exist: ${e.directory}`);if(!statSync(e.directory).isDirectory())throw new Error(`Static path is not a directory: ${e.directory}`);this.resolvedDir=resolve(e.directory),this.config={path:e.path,directory:e.directory,maxAge:e.maxAge??3600,index:e.index??["index.html"],dotfiles:e.dotfiles??"deny",etag:e.etag??true,lastModified:e.lastModified??true,immutable:e.immutable??false,extensions:e.extensions??[],fallthrough:e.fallthrough??false,setHeaders:e.setHeaders};}handler(){return async e=>{let r=e.request.url,n=new URL(r).pathname;n.startsWith(this.config.path)&&(n=n.slice(this.config.path.length));try{n=decodeURIComponent(n);}catch{return e.json({error:"Invalid URL encoding"},400)}if(n.includes("..")||n.includes("\\"))return e.json({error:"Forbidden"},403);if(this.config.dotfiles!=="allow"&&n.split("/").some(a=>a.startsWith(".")))return this.config.dotfiles==="deny"?e.json({error:"Forbidden"},403):this.handleNotFound(e);let i=this.resolveFilePath(n);if(!i)return this.handleNotFound(e);if(!this.isPathSafe(i))return e.json({error:"Forbidden"},403);if(!existsSync(i))return this.handleNotFound(e);let o=statSync(i);return o.isDirectory()?this.serveDirectory(e,i,n):this.serveFile(e,i,o)}}getPathPattern(){return `${this.config.path}/*`}resolveFilePath(e){e.startsWith("/")&&(e=e.slice(1));let r=join(this.resolvedDir,e);if(!existsSync(r)&&this.config.extensions.length>0)for(let t of this.config.extensions){let n=`${r}.${t}`;if(existsSync(n))return n}return r}isPathSafe(e){return !relative(this.resolvedDir,resolve(e)).startsWith("..")&&!resolve(e).startsWith("..")}async serveDirectory(e,r,t){for(let n of this.config.index){let i=join(r,n);if(existsSync(i)){let o=statSync(i);if(o.isFile())return this.serveFile(e,i,o)}}return this.handleNotFound(e)}async serveFile(e,r,t){let n=e.request.method.toUpperCase();if(n!=="GET"&&n!=="HEAD")return e.json({error:"Method not allowed"},405);let i=r,o=this.fileCache.get(i);if(o&&o.mtime!==t.mtimeMs&&(o=void 0),!o){if(o={etag:this.generateEtag(t),lastModified:new Date(t.mtime),size:t.size,mtime:t.mtimeMs},this.fileCache.size>=this.CACHE_MAX_SIZE){let x=this.fileCache.keys().next().value;x&&this.fileCache.delete(x);}this.fileCache.set(i,o);}let a=e.request.headers.get("if-none-match"),d=e.request.headers.get("if-modified-since");if(this.config.etag&&a===o.etag)return new Response(null,{status:304,headers:this.buildHeaders(r,o)});if(this.config.lastModified&&d){let x=new Date(d);if(o.lastModified<=x)return new Response(null,{status:304,headers:this.buildHeaders(r,o)})}let h=Bun.file(r),f=this.buildHeaders(r,o);return this.config.setHeaders&&this.config.setHeaders(e,r),n==="HEAD"?new Response(null,{status:200,headers:f}):new Response(h,{status:200,headers:f})}buildHeaders(e,r){let t=new Headers,n=this.getMimeType(e);if(t.set("Content-Type",n),t.set("Content-Length",r.size.toString()),this.config.etag&&t.set("ETag",r.etag),this.config.lastModified&&t.set("Last-Modified",r.lastModified.toUTCString()),this.config.maxAge>0){let i=`public, max-age=${this.config.maxAge}`;this.config.immutable&&(i+=", immutable"),t.set("Cache-Control",i);}else t.set("Cache-Control","no-cache");return t.set("Accept-Ranges","bytes"),t}generateEtag(e){return `"${e.size.toString(16)}-${e.mtimeMs.toString(16)}"`}getMimeType(e){let r=extname(e).toLowerCase();return we[r]||"application/octet-stream"}handleNotFound(e){return this.config.fallthrough?e.json({error:"Not Found"},404):e.json({error:"Not Found"},404)}clearCache(){this.fileCache.clear();}getCacheStats(){return {entries:this.fileCache.size,maxSize:this.CACHE_MAX_SIZE}}};function Ce(s){return new I(s)}var we={".html":"text/html; charset=utf-8",".htm":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".txt":"text/plain; charset=utf-8",".xml":"text/xml; charset=utf-8",".csv":"text/csv; charset=utf-8",".md":"text/markdown; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".jsonld":"application/ld+json",".map":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".ico":"image/x-icon",".webp":"image/webp",".avif":"image/avif",".bmp":"image/bmp",".tiff":"image/tiff",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".otf":"font/otf",".eot":"application/vnd.ms-fontobject",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".m4a":"audio/mp4",".aac":"audio/aac",".flac":"audio/flac",".mp4":"video/mp4",".webm":"video/webm",".ogv":"video/ogg",".mov":"video/quicktime",".avi":"video/x-msvideo",".mkv":"video/x-matroska",".pdf":"application/pdf",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".zip":"application/zip",".rar":"application/x-rar-compressed",".7z":"application/x-7z-compressed",".tar":"application/x-tar",".gz":"application/gzip",".wasm":"application/wasm",".manifest":"text/cache-manifest",".webmanifest":"application/manifest+json"};var U=new P,v=new k;function Se(s={}){let e=Number(s.port)||3e3,r=s.hostname||"localhost",t=s.maxRequestSize||10*1024*1024,n=s.requestTimeout||3e4,i=s.gracefulShutdownTimeout||1e4,o=typeof s.logging=="object"?s.logging:{},a=s.logging?new j(o.level||"info",o.pretty):null,d=new Map,h=[],f=new Set,x=setInterval(()=>{U.cleanupRateLimit(),U.cleanupCsrfTokens();},120*1e3);async function C(u,c){let g=Date.now(),p=crypto.randomUUID(),L=new URL(u.url).pathname,$=u.method.toUpperCase(),R=$e(u,c);f.add(p);try{let w=u.headers.get("content-length");if(w&&parseInt(w)>t)return a?.warn({requestId:p,size:w,ip:R},"Request too large"),new Response(JSON.stringify({error:"Payload too large"}),{status:413,headers:{"Content-Type":"application/json"}});let M=Ee(u,s);if($==="OPTIONS")return new Response(null,{status:204,headers:M});if(s.security&&typeof s.security=="object"&&s.security.rateLimit){let E=typeof s.security.rateLimit=="object"?s.security.rateLimit:{},q=E.max||100,V=E.windowMs||6e4,J=E.keyGenerator?E.keyGenerator({request:u,ip:R}):R;if(!U.checkRateLimit(J,q,V))return a?.warn({requestId:p,ip:R,key:J},"Rate limit exceeded"),new Response(JSON.stringify({error:E.message||"Too many requests"}),{status:429,headers:{"Content-Type":"application/json"}})}let W=null;["POST","PUT","PATCH"].includes($)&&(W=await Re(u,a,t));let G=d.get("default"),B=v.match($,L);if(!B){let E=te(R,u,{},G,a,p);return a?.warn({requestId:p,method:$,path:L,ip:R},"Route not found"),E.json({error:"Not Found",path:L},404)}let z=te(R,u,B.params||{},G,a,p);z.body=W,z.request=u;let X=new AbortController,re=new Promise((E,q)=>{let V=setTimeout(()=>{X.abort(),q(new H("Request timeout"));},n);X.signal.addEventListener("abort",()=>clearTimeout(V));}),O=await Promise.race([B.handler(z),re]),A=new Headers(O.headers);M.forEach((E,q)=>{A.has(q)||A.set(q,E);}),A.set("X-Request-ID",p),A.set("X-Content-Type-Options","nosniff"),A.set("X-Frame-Options","DENY"),A.set("X-XSS-Protection","1; mode=block"),A.set("Referrer-Policy","strict-origin-when-cross-origin");let K=Date.now()-g;return U.logRequest(p,$,L,R,O.status,K),a?.info({requestId:p,method:$,path:L,status:O.status,duration:K,ip:R},"Request completed"),new Response(O.body,{status:O.status,headers:A})}catch(w){if(w instanceof T)return a?.warn({error:w.message,requestId:p,ip:R},`App error: ${w.message}`),new Response(JSON.stringify({error:w.message,code:w.code,requestId:p}),{status:w.statusCode,headers:{"Content-Type":"application/json"}});a?.error({error:String(w),requestId:p,ip:R},"Unhandled error");let M=process.env.NODE_ENV==="production"?"Internal Server Error":w.message;return new Response(JSON.stringify({error:M,requestId:p}),{status:500,headers:{"Content-Type":"application/json"}})}finally{f.delete(p);}}let l={method:"GET",path:"/health",handler:u=>u.json({status:"healthy",timestamp:new Date().toISOString(),uptime:process.uptime(),activeRequests:f.size})},m={method:"GET",path:"/readiness",handler:u=>{let c=d.size>0,g=c||d.size===0;return u.json({ready:g,checks:{database:c?"connected":"not configured",activeRequests:f.size},timestamp:new Date().toISOString()},g?200:503)}};if(s.routes&&s.routes.forEach(u=>{h.push(u),(Array.isArray(u.method)?u.method:[u.method]).forEach(g=>{v.register(g,u.path,u.handler,u);});}),s.static){let u=Array.isArray(s.static)?s.static:[s.static];for(let c of u)try{let p=new I(c).handler(),b={method:"GET",path:c.path==="/"?"/*":`${c.path}/*`,handler:p};h.push(b),c.path==="/"?(v.register("GET","/",p,b),v.register("HEAD","/",p,b),v.register("GET","/*",p,b),v.register("HEAD","/*",p,b)):(v.register("GET",`${c.path}/*`,p,b),v.register("HEAD",`${c.path}/*`,p,b)),a?.info({urlPath:c.path,directory:c.directory,maxAge:c.maxAge||3600},"\u2714 Static file serving enabled");}catch(g){throw a?.error({error:String(g),path:c.path},"Failed to initialize static file server"),g}}h.push(l,m),v.register("GET","/health",l.handler,l),v.register("GET","/readiness",m.handler,m);let y=null,S={app:null,logger:a,db:d,bunServer:null,async start(){if(s.database){let c=Array.isArray(s.database)?s.database:[s.database];for(let g of c){let p=g.name||"default";try{if(typeof g.connection=="string"){let b=new _(g.connection);if(g.schema&&typeof g.schema=="object")for(let[L,$]of Object.entries(g.schema))$&&typeof $=="object"&&b.defineSchema($);d.set(p,b),a?.info({name:p,connection:g.connection},"\u2714 Database connected");}else throw new Error(`Database connection must be a string path (got ${typeof g.connection})`)}catch(b){throw a?.error({error:String(b),name:p},"Failed to connect to database"),b}}}y=Bun.serve({port:e,hostname:r,fetch:(c,g)=>C(c,g)}),S.bunServer=y;let u=`http://${r}:${e}`;a?.info({url:u},"Server started");},async stop(){if(a?.info("Stopping server..."),f.size>0){a?.info({count:f.size},"Waiting for active requests...");let u=Date.now()+i;for(;f.size>0&&Date.now()<u;)await new Promise(c=>setTimeout(c,100));f.size>0&&a?.warn({count:f.size},"Force closing with active requests");}if(clearInterval(x),s.onShutdown)try{await s.onShutdown();}catch(u){a?.error({error:String(u)},"Error in shutdown handler");}for(let[u,c]of d.entries())try{c&&typeof c.close=="function"&&c.close(),a?.info({name:u},"Database closed");}catch(g){a?.error({error:String(g),name:u},"Error closing database");}y&&(y.stop(),a?.info("Bun server stopped")),a?.info("Server stopped successfully");},addRoute(u){h.push(u),(Array.isArray(u.method)?u.method:[u.method]).forEach(g=>{v.register(g,u.path,u.handler,u);}),a?.info({method:u.method,path:u.path},"Route added");},addRoutes(u){u.forEach(c=>this.addRoute(c));},getRoutes(){return h}};return S}async function Re(s,e,r){let t=s.headers.get("content-type")||"";try{if(t.includes("application/json")){let n=await s.text();if(n.length>r)throw new D("Payload too large");if(!n.trim())return {};try{return JSON.parse(n)}catch(i){throw e?.warn({error:String(i),bodyPreview:n.substring(0,100)},"Invalid JSON in request body"),new D("Invalid JSON in request body")}}if(t.includes("application/x-www-form-urlencoded")){let n=await s.text();if(n.length>r)throw new D("Payload too large");return Object.fromEntries(new URLSearchParams(n))}if(t.includes("multipart/form-data"))return await s.formData()}catch(n){throw n instanceof D?n:(e?.error({error:String(n)},"Error parsing request body"),new D("Failed to parse request body"))}return {}}function ve(s){let e=new Map;if(!s)return e;let r=s.split(";");for(let t of r){let[n,...i]=t.trim().split("=");if(n){let o=i.join("=");e.set(n,o?decodeURIComponent(o):"");}}return e}function te(s,e,r,t,n,i){let o=new URL(e.url),a=Object.fromEntries(o.searchParams),d=e.headers,h=200,f=new Map,x=ve(d.get("cookie")||""),C={ip:s,request:e,params:r,query:a,headers:d,db:t,logger:n,requestId:i,get statusCode(){return h},set statusCode(l){h=l;},body:null,json(l,m){return new Response(JSON.stringify(l),{status:m??h,headers:{"Content-Type":"application/json",...this._setCookieHeaders()}})},text(l,m){return new Response(l,{status:m??h,headers:{"Content-Type":"text/plain",...this._setCookieHeaders()}})},html(l,m){return new Response(l,{status:m??h,headers:{"Content-Type":"text/html; charset=utf-8",...this._setCookieHeaders()}})},redirect(l,m=302){return new Response(null,{status:m,headers:{Location:l,...this._setCookieHeaders()}})},file(l,m="application/octet-stream"){let y=Bun.file(l);return new Response(y,{headers:{"Content-Type":m,...this._setCookieHeaders()}})},setCookie(l,m,y={}){let S=`${l}=${encodeURIComponent(m)}`;return y.maxAge!==void 0&&(S+=`; Max-Age=${y.maxAge}`),y.expires&&(S+=`; Expires=${y.expires.toUTCString()}`),y.path&&(S+=`; Path=${y.path}`),y.domain&&(S+=`; Domain=${y.domain}`),y.secure&&(S+="; Secure"),y.httpOnly&&(S+="; HttpOnly"),y.sameSite&&(S+=`; SameSite=${y.sameSite}`),f.set(l,S),C},getCookie(l){return x.get(l)},deleteCookie(l,m={}){return C.setCookie(l,"",{...m,maxAge:0,path:m.path||"/"})},setHeader(l,m){return d.set(l,m),C},getHeader(l){return d.get(l)||void 0},status(l){return h=l,C},_setCookieHeaders(){let l={};return f.size>0&&(l["Set-Cookie"]=Array.from(f.values())),l}};return C}function $e(s,e){let r=s.headers.get("x-forwarded-for");if(r)return r.split(",").map(i=>i.trim())[0]||"unknown";let t=s.headers.get("x-real-ip");if(t)return t;if(e)try{let n=e.requestIP(s);if(n?.address)return n.address}catch{}return "unknown"}function Ee(s,e){let r=new Headers;if(!e.security||typeof e.security!="object"||!e.security.cors)return r;let t=typeof e.security.cors=="object"?e.security.cors:{},n=s.headers.get("Origin");if(n){typeof t.origin=="function"?t.origin(n)&&r.set("Access-Control-Allow-Origin",n):Array.isArray(t.origin)?t.origin.includes(n)&&r.set("Access-Control-Allow-Origin",n):typeof t.origin=="string"?r.set("Access-Control-Allow-Origin",t.origin):r.set("Access-Control-Allow-Origin",n);let i=t.methods||["GET","POST","PUT","DELETE","PATCH","OPTIONS"];r.set("Access-Control-Allow-Methods",i.join(", "));let o=t.allowedHeaders||["Content-Type","Authorization","X-Requested-With"];r.set("Access-Control-Allow-Headers",o.join(", ")),t.credentials&&r.set("Access-Control-Allow-Credentials","true"),t.maxAge&&r.set("Access-Control-Max-Age",t.maxAge.toString());}return r}var Ue=Se;
2
+ export{T as AppError,_ as DB,Y as DatabaseError,j as Logger,Z as RateLimitError,k as Router,P as SecurityManager,I as StaticFileServer,H as TimeoutError,D as ValidationError,le as blob,ie as column,Ce as createStatic,Ue as default,ge as defaultValue,oe as integer,pe as notNull,ce as numeric,de as primaryKey,ue as real,he as references,Se as server,se as table,ae as text,fe as unique};//# sourceMappingURL=main.js.map
10
3
  //# sourceMappingURL=main.js.map