@je-es/server 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DB } from '@je-es/sdb';
2
- export { ColumnDefinition, ColumnType, DB, QueryBuilder, SqlValue, TableSchema, WhereCondition, blob, column, defaultValue, integer, notNull, numeric, primaryKey, real, references, table, text, unique } from '@je-es/sdb';
2
+ export { ColumnDefinition, ColumnType, DB, QueryBuilder, SqlValue, TableSchema, WhereCondition, blob, column, defaultValue, index, integer, notNull, numeric, primaryKey, real, references, table, text, unique } from '@je-es/sdb';
3
3
  export { Logger } from '@je-es/slog';
4
4
 
5
5
  declare class I18nManager {
@@ -12,14 +12,21 @@ declare class I18nManager {
12
12
  /**
13
13
  * Load translations for a specific language
14
14
  * @param lang Language code (e.g., 'en', 'ar', 'fr')
15
- * @param translations Translation object
15
+ * @param translations Translation object (can be nested)
16
16
  */
17
- loadLanguage(lang: string, translations: Record<string, string>): void;
17
+ loadLanguage(lang: string, translations: Record<string, any>): void;
18
+ /**
19
+ * Flatten nested object into dot notation
20
+ * @param obj Nested object
21
+ * @param prefix Current prefix
22
+ * @returns Flattened object with dot notation keys
23
+ */
24
+ private flattenObject;
18
25
  /**
19
26
  * Load all translations from static files
20
27
  * @param translations Object with language codes as keys and translation objects as values
21
28
  */
22
- loadTranslations(translations: Record<string, Record<string, string>>): void;
29
+ loadTranslations(translations: Record<string, Record<string, any>>): void;
23
30
  /**
24
31
  * Set the current language
25
32
  * @param lang Language code
@@ -39,19 +46,19 @@ declare class I18nManager {
39
46
  *
40
47
  * @example
41
48
  * // Simple translation
42
- * t('app.name') // => "JE-ES Server"
49
+ * t('button.login') // => "Login" or "دخـول"
43
50
  *
44
51
  * @example
45
52
  * // With parameters
46
- * t('validation.invalid', { field: 'email' })
47
- * // => "Invalid value for email"
53
+ * t('nav.credits', { count: '100' })
54
+ * // => "Available Credits: 100"
48
55
  *
49
56
  * @example
50
57
  * // With nested translation keys as parameters
51
- * t('message.validation', { error: 'validation.required' })
52
- * // => "Message: This field is required"
58
+ * t('language.switching_to', { language: 'button.login' })
59
+ * // => "Switching to Login..."
53
60
  *
54
- * @param key Translation key (dot-notation)
61
+ * @param key Translation key (dot-notation for nested keys)
55
62
  * @param params Optional parameters for replacement
56
63
  * @param defaultValue Optional default value
57
64
  * @returns Translated string with replaced parameters
@@ -90,7 +97,7 @@ declare function initI18n(config?: I18nConfig): I18nManager;
90
97
  declare function getI18n(): I18nManager;
91
98
  /**
92
99
  * Global translation function
93
- * @param key Translation key
100
+ * @param key Translation key (supports dot notation for nested keys)
94
101
  * @param params Optional parameters
95
102
  * @param defaultValue Optional default value
96
103
  * @returns Translated string
@@ -132,6 +139,7 @@ declare function getSupportedLanguages(): string[];
132
139
  request : Request;
133
140
  params : Record<string, string>;
134
141
  query : Record<string, string>;
142
+
135
143
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
144
  body : any;
137
145
  headers : Headers;
@@ -142,6 +150,8 @@ declare function getSupportedLanguages(): string[];
142
150
  user? : unknown;
143
151
  requestId : string;
144
152
 
153
+ state : Record<string, unknown>;
154
+
145
155
  // Response methods
146
156
  json (data: unknown, status?: number): Response;
147
157
  text (data: string, status?: number): Response;
package/dist/main.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DB } from '@je-es/sdb';
2
- export { ColumnDefinition, ColumnType, DB, QueryBuilder, SqlValue, TableSchema, WhereCondition, blob, column, defaultValue, integer, notNull, numeric, primaryKey, real, references, table, text, unique } from '@je-es/sdb';
2
+ export { ColumnDefinition, ColumnType, DB, QueryBuilder, SqlValue, TableSchema, WhereCondition, blob, column, defaultValue, index, integer, notNull, numeric, primaryKey, real, references, table, text, unique } from '@je-es/sdb';
3
3
  export { Logger } from '@je-es/slog';
4
4
 
5
5
  declare class I18nManager {
@@ -12,14 +12,21 @@ declare class I18nManager {
12
12
  /**
13
13
  * Load translations for a specific language
14
14
  * @param lang Language code (e.g., 'en', 'ar', 'fr')
15
- * @param translations Translation object
15
+ * @param translations Translation object (can be nested)
16
16
  */
17
- loadLanguage(lang: string, translations: Record<string, string>): void;
17
+ loadLanguage(lang: string, translations: Record<string, any>): void;
18
+ /**
19
+ * Flatten nested object into dot notation
20
+ * @param obj Nested object
21
+ * @param prefix Current prefix
22
+ * @returns Flattened object with dot notation keys
23
+ */
24
+ private flattenObject;
18
25
  /**
19
26
  * Load all translations from static files
20
27
  * @param translations Object with language codes as keys and translation objects as values
21
28
  */
22
- loadTranslations(translations: Record<string, Record<string, string>>): void;
29
+ loadTranslations(translations: Record<string, Record<string, any>>): void;
23
30
  /**
24
31
  * Set the current language
25
32
  * @param lang Language code
@@ -39,19 +46,19 @@ declare class I18nManager {
39
46
  *
40
47
  * @example
41
48
  * // Simple translation
42
- * t('app.name') // => "JE-ES Server"
49
+ * t('button.login') // => "Login" or "دخـول"
43
50
  *
44
51
  * @example
45
52
  * // With parameters
46
- * t('validation.invalid', { field: 'email' })
47
- * // => "Invalid value for email"
53
+ * t('nav.credits', { count: '100' })
54
+ * // => "Available Credits: 100"
48
55
  *
49
56
  * @example
50
57
  * // With nested translation keys as parameters
51
- * t('message.validation', { error: 'validation.required' })
52
- * // => "Message: This field is required"
58
+ * t('language.switching_to', { language: 'button.login' })
59
+ * // => "Switching to Login..."
53
60
  *
54
- * @param key Translation key (dot-notation)
61
+ * @param key Translation key (dot-notation for nested keys)
55
62
  * @param params Optional parameters for replacement
56
63
  * @param defaultValue Optional default value
57
64
  * @returns Translated string with replaced parameters
@@ -90,7 +97,7 @@ declare function initI18n(config?: I18nConfig): I18nManager;
90
97
  declare function getI18n(): I18nManager;
91
98
  /**
92
99
  * Global translation function
93
- * @param key Translation key
100
+ * @param key Translation key (supports dot notation for nested keys)
94
101
  * @param params Optional parameters
95
102
  * @param defaultValue Optional default value
96
103
  * @returns Translated string
@@ -132,6 +139,7 @@ declare function getSupportedLanguages(): string[];
132
139
  request : Request;
133
140
  params : Record<string, string>;
134
141
  query : Record<string, string>;
142
+
135
143
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
144
  body : any;
137
145
  headers : Headers;
@@ -142,6 +150,8 @@ declare function getSupportedLanguages(): string[];
142
150
  user? : unknown;
143
151
  requestId : string;
144
152
 
153
+ state : Record<string, unknown>;
154
+
145
155
  // Response methods
146
156
  json (data: unknown, status?: number): Response;
147
157
  text (data: string, status?: number): Response;
package/dist/main.js CHANGED
@@ -1,3 +1,3 @@
1
- import*as de from'@je-es/sdb';export{DB,blob,column,defaultValue,integer,notNull,numeric,primaryKey,real,references,table,text,unique}from'@je-es/sdb';import ge from'crypto';import {Logger}from'@je-es/slog';export{Logger}from'@je-es/slog';import {resolve,join,relative,extname}from'path';import {existsSync,statSync}from'fs';var V=class{constructor(){this.routes=new Map;this.regexRoutes=[];}match(e,t){let n=`${e}:${t}`;if(this.routes.has(n)){let r=this.routes.get(n);return {handler:r.handler,params:{},metadata:r.metadata}}for(let r of this.regexRoutes)if(r.method===e){let i=t.match(r.pattern);if(i){let u=i.groups||{};return {handler:r.handler,params:u,metadata:r.metadata}}}return null}getAll(){let e=Array.from(this.routes.entries()).map(([n,r])=>{let i=n.indexOf(":"),u=n.substring(0,i),a=n.substring(i+1);return {method:u,path:a,handler:r.handler}}),t=this.regexRoutes.map(n=>{let r=n.key.indexOf(":");return {method:n.method,path:n.key.substring(r+1),handler:n.handler}});return [...e,...t]}clear(){this.routes.clear(),this.regexRoutes=[];}remove(e,t){let n=`${e}:${t}`;if(this.routes.has(n))return this.routes.delete(n),true;let r=this.regexRoutes.findIndex(i=>i.key===n);return r>=0?(this.regexRoutes.splice(r,1),true):false}register(e,t,n,r={}){let i=`${e}:${t}`;if(t.includes(":")||t.includes("*")){let u=this.pathToRegex(t),a=this.regexRoutes.findIndex(w=>w.key===i),f={pattern:u,method:e,handler:n,key:i,metadata:r};a>=0?this.regexRoutes[a]=f:this.regexRoutes.push(f);}else this.routes.set(i,{handler:n,metadata:r});}pathToRegex(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return t=t.replace(/:(\w+)/g,"(?<$1>[^/]+)"),t=t.replace(/\*/g,".*"),new RegExp(`^${t}$`)}};var X=class{constructor(){this.rateLimitStore=new Map;this.csrfTokens=new Map;this.requestLog=new Map;this.MAX_REQUEST_LOG_SIZE=1e3;}checkRateLimit(e,t,n){let r=Date.now(),i=this.rateLimitStore.get(e);return i?r<i.reset?i.count>=t?false:(i.count++,true):(this.rateLimitStore.set(e,{count:1,reset:r+n}),true):(this.rateLimitStore.set(e,{count:1,reset:r+n}),true)}cleanupRateLimit(){let e=Date.now();for(let[t,n]of this.rateLimitStore.entries())e>n.reset&&this.rateLimitStore.delete(t);}generateCsrfToken(e,t=36e5){let n=ge.randomBytes(32).toString("hex");return this.csrfTokens.set(n,{sessionId:e,expires:Date.now()+t}),n}validateCsrfToken(e,t){let n=this.csrfTokens.get(e);return n?Date.now()>n.expires?(this.csrfTokens.delete(e),false):n.sessionId===t?(this.csrfTokens.delete(e),true):false:false}cleanupCsrfTokens(){let e=Date.now();for(let[t,n]of this.csrfTokens.entries())e>n.expires&&this.csrfTokens.delete(t);}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(/\u0000/g,""):""}logRequest(e,t,n,r,i,u){if(this.requestLog.set(e,{timestamp:new Date().toISOString(),method:t,path:n,ip:r,status:i,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 D=class extends Error{constructor(t,n=500,r){super(t);this.message=t;this.statusCode=n;this.code=r;this.name="AppError";}},I=class extends D{constructor(t,n){super(t,400,"VALIDATION_ERROR");this.issues=n;this.name="ValidationError";}},ie=class extends D{constructor(e){super(e,500,"DATABASE_ERROR"),this.name="DatabaseError";}},J=class extends D{constructor(e="Request timeout"){super(e,408,"TIMEOUT_ERROR"),this.name="TimeoutError";}},ae=class extends D{constructor(e="Too many requests"){super(e,429,"RATE_LIMIT_ERROR"),this.name="RateLimitError";}};var q=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 t=e.request.url,r=new URL(t).pathname;r.startsWith(this.config.path)&&(r=r.slice(this.config.path.length));try{r=decodeURIComponent(r);}catch{return e.json({error:"Invalid URL encoding"},400)}if(r.includes("..")||r.includes("\\"))return e.json({error:"Forbidden"},403);if(this.config.dotfiles!=="allow"&&r.split("/").some(a=>a.startsWith(".")))return this.config.dotfiles==="deny"?e.json({error:"Forbidden"},403):this.handleNotFound(e);let i=this.resolveFilePath(r);if(!i)return this.handleNotFound(e);if(!this.isPathSafe(i))return e.json({error:"Forbidden"},403);if(!existsSync(i))return this.handleNotFound(e);let u=statSync(i);return u.isDirectory()?this.serveDirectory(e,i,r):this.serveFile(e,i,u)}}getPathPattern(){return `${this.config.path}/*`}resolveFilePath(e){e.startsWith("/")&&(e=e.slice(1));let t=join(this.resolvedDir,e);if(!existsSync(t)&&this.config.extensions.length>0)for(let n of this.config.extensions){let r=`${t}.${n}`;if(existsSync(r))return r}return t}isPathSafe(e){return !relative(this.resolvedDir,resolve(e)).startsWith("..")&&!resolve(e).startsWith("..")}async serveDirectory(e,t,n){for(let r of this.config.index){let i=join(t,r);if(existsSync(i)){let u=statSync(i);if(u.isFile())return this.serveFile(e,i,u)}}return this.handleNotFound(e)}async serveFile(e,t,n){let r=e.request.method.toUpperCase();if(r!=="GET"&&r!=="HEAD")return e.json({error:"Method not allowed"},405);let i=t,u=this.fileCache.get(i);if(u&&u.mtime!==n.mtimeMs&&(u=void 0),!u){if(u={etag:this.generateEtag(n),lastModified:new Date(n.mtime),size:n.size,mtime:n.mtimeMs},this.fileCache.size>=this.CACHE_MAX_SIZE){let h=this.fileCache.keys().next().value;h&&this.fileCache.delete(h);}this.fileCache.set(i,u);}let a=e.request.headers.get("if-none-match"),f=e.request.headers.get("if-modified-since");if(this.config.etag&&a===u.etag)return new Response(null,{status:304,headers:this.buildHeaders(t,u)});if(this.config.lastModified&&f){let h=new Date(f);if(u.lastModified<=h)return new Response(null,{status:304,headers:this.buildHeaders(t,u)})}let w=Bun.file(t),C=this.buildHeaders(t,u);return this.config.setHeaders&&this.config.setHeaders(e,t),r==="HEAD"?new Response(null,{status:200,headers:C}):new Response(w,{status:200,headers:C})}buildHeaders(e,t){let n=new Headers,r=this.getMimeType(e);if(n.set("Content-Type",r),n.set("Content-Length",t.size.toString()),this.config.etag&&n.set("ETag",t.etag),this.config.lastModified&&n.set("Last-Modified",t.lastModified.toUTCString()),this.config.maxAge>0){let i=`public, max-age=${this.config.maxAge}`;this.config.immutable&&(i+=", immutable"),n.set("Cache-Control",i);}else n.set("Cache-Control","no-cache");return n.set("Accept-Ranges","bytes"),n}generateEtag(e){return `"${e.size.toString(16)}-${e.mtimeMs.toString(16)}"`}getMimeType(e){let t=extname(e).toLowerCase();return ye[t]||"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 me(o){return new q(o)}var ye={".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=class{constructor(e){this.translations={};this.currentLanguage="en";this.defaultLanguage="en";this.supportedLanguages=new Set(["en"]);this.cachePath="";e&&(this.defaultLanguage=e.defaultLanguage||"en",this.currentLanguage=e.defaultLanguage||"en",this.cachePath=e.staticPath||"static/i18n",e.supportedLanguages&&(this.supportedLanguages=new Set(e.supportedLanguages)));}loadLanguage(e,t){this.translations[e]||(this.translations[e]={}),this.translations[e]={...this.translations[e],...t},this.supportedLanguages.add(e);}loadTranslations(e){Object.entries(e).forEach(([t,n])=>{this.loadLanguage(t,n);});}setLanguage(e){this.supportedLanguages.has(e)?this.currentLanguage=e:this.supportedLanguages.has(this.defaultLanguage)&&(this.currentLanguage=this.defaultLanguage);}getLanguage(){return this.currentLanguage}getSupportedLanguages(){return Array.from(this.supportedLanguages)}t(e,t,n){let r=this.currentLanguage,i=this.getTranslation(e,n);return t&&Object.entries(t).forEach(([u,a])=>{let f=this.translations[r]?.[a]||this.translations[this.defaultLanguage]?.[a]||a;i=i.replace(new RegExp(`\\{${u}\\}`,"g"),f);}),i}getTranslation(e,t){let n=this.currentLanguage;return this.translations[n]?.[e]?this.translations[n]?.[e]||this.translations[this.defaultLanguage]?.[e]:(console.warn(`Translation key not found: ${e}`),t||e)}tLang(e,t,n){let r=this.currentLanguage;this.setLanguage(t);let i=this.t(e,n);return this.currentLanguage=r,i}getTranslations(){return this.translations[this.currentLanguage]||{}}hasKey(e){return !!(this.translations[this.currentLanguage]?.[e]||this.translations[this.defaultLanguage]?.[e])}},O=null;function te(o){return O||(O=new U(o)),O}function B(){return O||(O=new U),O}function be(o,e,t){return B().t(o,e,t)}function Re(o){B().setLanguage(o);}function xe(){return B().getLanguage()}function we(){return B().getSupportedLanguages()}var W=new X,T=new V;function Se(o={}){let e=Number(o.port)||3e3,t=o.hostname||"localhost",n=o.maxRequestSize||10*1024*1024,r=o.requestTimeout||3e4,i=o.gracefulShutdownTimeout||1e4,u=typeof o.logging=="object"?o.logging:{},a=o.logging?new Logger(u.level||"info",u.pretty):null,f=null;if(o.i18n){let s=typeof o.i18n=="object"?o.i18n:{};f=te({defaultLanguage:s.defaultLanguage||"en",supportedLanguages:s.supportedLanguages||["en","ar","fr"],staticPath:s.staticPath||"static/i18n"});}let w=new Map,C=[],h=new Set,z=setInterval(()=>{W.cleanupRateLimit(),W.cleanupCsrfTokens();},120*1e3);async function K(s,c){let g=Date.now(),d=crypto.randomUUID(),x=new URL(s.url).pathname,v=s.method.toUpperCase(),b=Ae(s,c);h.add(d);try{let R=s.headers.get("content-length");if(R&&parseInt(R)>n)return a?.warn({requestId:d,size:R,ip:b},"Request too large"),new Response(JSON.stringify({error:"Payload too large"}),{status:413,headers:{"Content-Type":"application/json"}});let M=Te(s,o);if(v==="OPTIONS")return new Response(null,{status:204,headers:M});if(o.security&&typeof o.security=="object"&&o.security.rateLimit){let k=typeof o.security.rateLimit=="object"?o.security.rateLimit:{},j=k.max||100,Q=k.windowMs||6e4,oe=k.keyGenerator?k.keyGenerator({request:s,ip:b}):b;if(!W.checkRateLimit(oe,j,Q))return a?.warn({requestId:d,ip:b,key:oe},"Rate limit exceeded"),new Response(JSON.stringify({error:k.message||"Too many requests"}),{status:429,headers:{"Content-Type":"application/json"}})}let L=null;["POST","PUT","PATCH"].includes(v)&&(L=await ve(s,a,n));let E=w.get("default"),$=Object.fromEntries(new URL(s.url).searchParams).lang||s.headers.get("Accept-Language")?.split(",")[0]?.split("-")[0]||"en";f&&!f.getSupportedLanguages().includes($)&&($=f.getLanguage()),f&&f.setLanguage($);let F=T.match(v,x);if(!F){let k=ce(b,s,{},E,a,d,f,$);return a?.warn({requestId:d,method:v,path:x,ip:b},"Route not found"),k.json({error:"Not Found",path:x},404)}let G=ce(b,s,F.params||{},E,a,d,f,$);G.body=L,G.request=s;let ne=new AbortController,le=new Promise((k,j)=>{let Q=setTimeout(()=>{ne.abort(),j(new J("Request timeout"));},r);ne.signal.addEventListener("abort",()=>clearTimeout(Q));}),re=F.metadata?.middlewares||[],Z;re.length>0?Z=P(G,re,F.handler):Z=Promise.resolve(F.handler(G));let N=await Promise.race([Z,le]),H=new Headers(N.headers);M.forEach((k,j)=>{H.has(j)||H.set(j,k);}),H.set("X-Request-ID",d),H.set("X-Content-Type-Options","nosniff"),H.set("X-Frame-Options","DENY"),H.set("X-XSS-Protection","1; mode=block"),H.set("Referrer-Policy","strict-origin-when-cross-origin");let se=Date.now()-g;return W.logRequest(d,v,x,b,N.status,se),a?.info({requestId:d,method:v,path:x,status:N.status,duration:se,ip:b},"Request completed"),new Response(N.body,{status:N.status,headers:H})}catch(R){if(R instanceof D)return a?.warn({error:R.message,requestId:d,ip:b},`App error: ${R.message}`),new Response(JSON.stringify({error:R.message,code:R.code,requestId:d}),{status:R.statusCode,headers:{"Content-Type":"application/json"}});a?.error({error:String(R),requestId:d,ip:b},"Unhandled error");let M=process.env.NODE_ENV==="production"?"Internal Server Error":R.message;return new Response(JSON.stringify({error:M,requestId:d}),{status:500,headers:{"Content-Type":"application/json"}})}finally{h.delete(d);}}async function P(s,c,g){let d=0,p=null,x=s.json.bind(s),v=s.text.bind(s),b=s.html.bind(s),R=s.redirect.bind(s);s.json=function(L,E){let A=x(L,E);return p=A,A},s.text=function(L,E){let A=v(L,E);return p=A,A},s.html=function(L,E){let A=b(L,E);return p=A,A},s.redirect=function(L,E){let A=R(L,E);return p=A,A};async function M(){if(!p&&d<c.length){let L=c[d];d++,await L(s,M);}}return await M(),s.json=x,s.text=v,s.html=b,s.redirect=R,p||g(s)}let l={method:"GET",path:"/health",handler:s=>s.json({status:"healthy",timestamp:new Date().toISOString(),uptime:process.uptime(),activeRequests:h.size})},m={method:"GET",path:"/readiness",handler:s=>{let c=w.size>0,g=c||w.size===0;return s.json({ready:g,checks:{database:c?"connected":"not configured",activeRequests:h.size},timestamp:new Date().toISOString()},g?200:503)}};if(o.routes&&o.routes.forEach(s=>{C.push(s),(Array.isArray(s.method)?s.method:[s.method]).forEach(g=>{T.register(g,s.path,s.handler,s);});}),o.static){let s=Array.isArray(o.static)?o.static:[o.static];for(let c of s)try{let d=new q(c).handler(),p={method:"GET",path:c.path==="/"?"/*":`${c.path}/*`,handler:d};C.push(p),c.path==="/"?(T.register("GET","/",d,p),T.register("HEAD","/",d,p),T.register("GET","/*",d,p),T.register("HEAD","/*",d,p)):(T.register("GET",`${c.path}/*`,d,p),T.register("HEAD",`${c.path}/*`,d,p));}catch(g){throw a?.error({error:String(g),path:c.path},"Failed to initialize static file server"),g}}C.push(l,m),T.register("GET","/health",l.handler,l),T.register("GET","/readiness",m.handler,m);let y=null,S={app:null,logger:a,db:w,bunServer:null,async start(){if(f&&o.i18n){let c=typeof o.i18n=="object"?o.i18n:{},g=c.staticPath||"static/i18n",d=c.supportedLanguages||["en","ar","fr"];try{for(let p of d){let x=`${g}/${p}.json`,v=Bun.file(x);if(await v.exists()){let b=await v.json();f.loadLanguage(p,b);}}a?.info({languages:f.getSupportedLanguages()},"i18n translations loaded");}catch(p){a?.warn({error:String(p)},"Failed to load i18n translations");}}if(o.database){let c=Array.isArray(o.database)?o.database:[o.database];for(let g of c){let d=g.name||"default";try{if(typeof g.connection=="string"){let p=new de.DB(g.connection);if(g.schema&&typeof g.schema=="object")for(let[,x]of Object.entries(g.schema))x&&typeof x=="object"&&p.defineSchema(x);w.set(d,p),a?.info({name:d,connection:g.connection},"\u2714 Database connected");}else throw new Error(`Database connection must be a string path (got ${typeof g.connection})`)}catch(p){throw a?.error({error:String(p),name:d},"Failed to connect to database"),p}}}y=Bun.serve({port:e,hostname:t,fetch:(c,g)=>K(c,g)}),S.bunServer=y;let s=`http://${t}:${e}`;if(a?.info({url:s},"\u2714 Server started"),o.onStartup)try{await o.onStartup(S);}catch(c){a?.error({error:String(c)},"Error in startup handler");}if(o.onReady)try{await o.onReady(S,w);}catch(c){a?.error({error:String(c)},"Error in ready handler");}},async stop(){if(a?.info("Stopping server..."),h.size>0){a?.info({count:h.size},"Waiting for active requests...");let s=Date.now()+i;for(;h.size>0&&Date.now()<s;)await new Promise(c=>setTimeout(c,100));h.size>0&&a?.warn({count:h.size},"Force closing with active requests");}if(clearInterval(z),o.onShutdown)try{await o.onShutdown();}catch(s){a?.error({error:String(s)},"Error in shutdown handler");}for(let[s,c]of w.entries())try{c&&typeof c.close=="function"&&c.close(),a?.info({name:s},"Database closed");}catch(g){a?.error({error:String(g),name:s},"Error closing database");}y&&typeof y.stop=="function"&&(y.stop(),a?.info("Bun server stopped")),a?.info("Server stopped successfully");},addRoute(s){C.push(s),(Array.isArray(s.method)?s.method:[s.method]).forEach(g=>{T.register(g,s.path,s.handler,s);}),a?.info({method:s.method,path:s.path},"Route added");},addRoutes(s){s.forEach(c=>this.addRoute(c));},getRoutes(){return C}};return S}async function ve(o,e,t){let n=o.headers.get("content-type")||"";try{if(n.includes("application/json")){let r=await o.text();if(r.length>t)throw new I("Payload too large");if(!r.trim())return {};try{return JSON.parse(r)}catch(i){throw e?.warn({error:String(i),bodyPreview:r.substring(0,100)},"Invalid JSON in request body"),new I("Invalid JSON in request body")}}if(n.includes("application/x-www-form-urlencoded")){let r=await o.text();if(r.length>t)throw new I("Payload too large");return Object.fromEntries(new URLSearchParams(r))}if(n.includes("multipart/form-data"))return await o.formData()}catch(r){throw r instanceof I?r:(e?.error({error:String(r)},"Error parsing request body"),new I("Failed to parse request body"))}return {}}function Le(o){let e=new Map;if(!o)return e;let t=o.split(";");for(let n of t){let[r,...i]=n.trim().split("=");if(r){let u=i.join("=");e.set(r,u?decodeURIComponent(u):"");}}return e}function ce(o,e,t,n,r,i,u=null,a="en"){let f=new URL(e.url),w=Object.fromEntries(f.searchParams),C=e.headers,h=200,z=new Map,K=Le(C.get("cookie")||""),P={ip:o,request:e,params:t,query:w,headers:C,db:n,logger:r,i18n:u,lang:a,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}`),z.set(l,S),P},getCookie(l){return K.get(l)},deleteCookie(l,m={}){return P.setCookie(l,"",{...m,maxAge:0,path:m.path||"/"})},setHeader(l,m){return C.set(l,m),P},getHeader(l){return C.get(l)||void 0},status(l){return h=l,P},_setCookieHeaders(){let l={};return z.size>0&&(l["Set-Cookie"]=Array.from(z.values())),l}};return P}function Ae(o,e){let t=o.headers.get("x-forwarded-for");if(t)return t.split(",").map(i=>i.trim())[0]||"unknown";let n=o.headers.get("x-real-ip");if(n)return n;if(e)try{let i=e.requestIP?.(o);if(i?.address)return i.address}catch{}return "unknown"}function Te(o,e){let t=new Headers;if(!e.security||typeof e.security!="object"||!e.security.cors)return t;let n=typeof e.security.cors=="object"?e.security.cors:{},r=o.headers.get("Origin");if(r){typeof n.origin=="function"?n.origin(r)&&t.set("Access-Control-Allow-Origin",r):Array.isArray(n.origin)?n.origin.includes(r)&&t.set("Access-Control-Allow-Origin",r):typeof n.origin=="string"?t.set("Access-Control-Allow-Origin",n.origin):t.set("Access-Control-Allow-Origin",r);let i=n.methods||["GET","POST","PUT","DELETE","PATCH","OPTIONS"];t.set("Access-Control-Allow-Methods",i.join(", "));let u=n.allowedHeaders||["Content-Type","Authorization","X-Requested-With"];t.set("Access-Control-Allow-Headers",u.join(", ")),n.credentials&&t.set("Access-Control-Allow-Credentials","true"),n.maxAge&&t.set("Access-Control-Max-Age",n.maxAge.toString());}return t}var _e=Se;
2
- export{D as AppError,ie as DatabaseError,U as I18nManager,ae as RateLimitError,V as Router,X as SecurityManager,q as StaticFileServer,J as TimeoutError,I as ValidationError,me as createStatic,_e as default,xe as getCurrentLanguage,B as getI18n,we as getSupportedLanguages,te as initI18n,Se as server,Re as setLanguage,be as t};//# sourceMappingURL=main.js.map
1
+ import*as le from'@je-es/sdb';export{DB,blob,column,defaultValue,index,integer,notNull,numeric,primaryKey,real,references,table,text,unique}from'@je-es/sdb';import ge from'crypto';import {Logger}from'@je-es/slog';export{Logger}from'@je-es/slog';import {resolve,join,relative,extname}from'path';import {existsSync,statSync}from'fs';var V=class{constructor(){this.routes=new Map;this.regexRoutes=[];}match(e,t){let n=`${e}:${t}`;if(this.routes.has(n)){let r=this.routes.get(n);return {handler:r.handler,params:{},metadata:r.metadata}}for(let r of this.regexRoutes)if(r.method===e){let i=t.match(r.pattern);if(i){let u=i.groups||{};return {handler:r.handler,params:u,metadata:r.metadata}}}return null}getAll(){let e=Array.from(this.routes.entries()).map(([n,r])=>{let i=n.indexOf(":"),u=n.substring(0,i),a=n.substring(i+1);return {method:u,path:a,handler:r.handler}}),t=this.regexRoutes.map(n=>{let r=n.key.indexOf(":");return {method:n.method,path:n.key.substring(r+1),handler:n.handler}});return [...e,...t]}clear(){this.routes.clear(),this.regexRoutes=[];}remove(e,t){let n=`${e}:${t}`;if(this.routes.has(n))return this.routes.delete(n),true;let r=this.regexRoutes.findIndex(i=>i.key===n);return r>=0?(this.regexRoutes.splice(r,1),true):false}register(e,t,n,r={}){let i=`${e}:${t}`;if(t.includes(":")||t.includes("*")){let u=this.pathToRegex(t),a=this.regexRoutes.findIndex(w=>w.key===i),f={pattern:u,method:e,handler:n,key:i,metadata:r};a>=0?this.regexRoutes[a]=f:this.regexRoutes.push(f);}else this.routes.set(i,{handler:n,metadata:r});}pathToRegex(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return t=t.replace(/:(\w+)/g,"(?<$1>[^/]+)"),t=t.replace(/\*/g,".*"),new RegExp(`^${t}$`)}};var X=class{constructor(){this.rateLimitStore=new Map;this.csrfTokens=new Map;this.requestLog=new Map;this.MAX_REQUEST_LOG_SIZE=1e3;}checkRateLimit(e,t,n){let r=Date.now(),i=this.rateLimitStore.get(e);return i?r<i.reset?i.count>=t?false:(i.count++,true):(this.rateLimitStore.set(e,{count:1,reset:r+n}),true):(this.rateLimitStore.set(e,{count:1,reset:r+n}),true)}cleanupRateLimit(){let e=Date.now();for(let[t,n]of this.rateLimitStore.entries())e>n.reset&&this.rateLimitStore.delete(t);}generateCsrfToken(e,t=36e5){let n=ge.randomBytes(32).toString("hex");return this.csrfTokens.set(n,{sessionId:e,expires:Date.now()+t}),n}validateCsrfToken(e,t){let n=this.csrfTokens.get(e);return n?Date.now()>n.expires?(this.csrfTokens.delete(e),false):n.sessionId===t?(this.csrfTokens.delete(e),true):false:false}cleanupCsrfTokens(){let e=Date.now();for(let[t,n]of this.csrfTokens.entries())e>n.expires&&this.csrfTokens.delete(t);}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(/\u0000/g,""):""}logRequest(e,t,n,r,i,u){if(this.requestLog.set(e,{timestamp:new Date().toISOString(),method:t,path:n,ip:r,status:i,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 D=class extends Error{constructor(t,n=500,r){super(t);this.message=t;this.statusCode=n;this.code=r;this.name="AppError";}},I=class extends D{constructor(t,n){super(t,400,"VALIDATION_ERROR");this.issues=n;this.name="ValidationError";}},ie=class extends D{constructor(e){super(e,500,"DATABASE_ERROR"),this.name="DatabaseError";}},J=class extends D{constructor(e="Request timeout"){super(e,408,"TIMEOUT_ERROR"),this.name="TimeoutError";}},ae=class extends D{constructor(e="Too many requests"){super(e,429,"RATE_LIMIT_ERROR"),this.name="RateLimitError";}};var O=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 t=e.request.url,r=new URL(t).pathname;r.startsWith(this.config.path)&&(r=r.slice(this.config.path.length));try{r=decodeURIComponent(r);}catch{return e.json({error:"Invalid URL encoding"},400)}if(r.includes("..")||r.includes("\\"))return e.json({error:"Forbidden"},403);if(this.config.dotfiles!=="allow"&&r.split("/").some(a=>a.startsWith(".")))return this.config.dotfiles==="deny"?e.json({error:"Forbidden"},403):this.handleNotFound(e);let i=this.resolveFilePath(r);if(!i)return this.handleNotFound(e);if(!this.isPathSafe(i))return e.json({error:"Forbidden"},403);if(!existsSync(i))return this.handleNotFound(e);let u=statSync(i);return u.isDirectory()?this.serveDirectory(e,i,r):this.serveFile(e,i,u)}}getPathPattern(){return `${this.config.path}/*`}resolveFilePath(e){e.startsWith("/")&&(e=e.slice(1));let t=join(this.resolvedDir,e);if(!existsSync(t)&&this.config.extensions.length>0)for(let n of this.config.extensions){let r=`${t}.${n}`;if(existsSync(r))return r}return t}isPathSafe(e){return !relative(this.resolvedDir,resolve(e)).startsWith("..")&&!resolve(e).startsWith("..")}async serveDirectory(e,t,n){for(let r of this.config.index){let i=join(t,r);if(existsSync(i)){let u=statSync(i);if(u.isFile())return this.serveFile(e,i,u)}}return this.handleNotFound(e)}async serveFile(e,t,n){let r=e.request.method.toUpperCase();if(r!=="GET"&&r!=="HEAD")return e.json({error:"Method not allowed"},405);let i=t,u=this.fileCache.get(i);if(u&&u.mtime!==n.mtimeMs&&(u=void 0),!u){if(u={etag:this.generateEtag(n),lastModified:new Date(n.mtime),size:n.size,mtime:n.mtimeMs},this.fileCache.size>=this.CACHE_MAX_SIZE){let h=this.fileCache.keys().next().value;h&&this.fileCache.delete(h);}this.fileCache.set(i,u);}let a=e.request.headers.get("if-none-match"),f=e.request.headers.get("if-modified-since");if(this.config.etag&&a===u.etag)return new Response(null,{status:304,headers:this.buildHeaders(t,u)});if(this.config.lastModified&&f){let h=new Date(f);if(u.lastModified<=h)return new Response(null,{status:304,headers:this.buildHeaders(t,u)})}let w=Bun.file(t),C=this.buildHeaders(t,u);return this.config.setHeaders&&this.config.setHeaders(e,t),r==="HEAD"?new Response(null,{status:200,headers:C}):new Response(w,{status:200,headers:C})}buildHeaders(e,t){let n=new Headers,r=this.getMimeType(e);if(n.set("Content-Type",r),n.set("Content-Length",t.size.toString()),this.config.etag&&n.set("ETag",t.etag),this.config.lastModified&&n.set("Last-Modified",t.lastModified.toUTCString()),this.config.maxAge>0){let i=`public, max-age=${this.config.maxAge}`;this.config.immutable&&(i+=", immutable"),n.set("Cache-Control",i);}else n.set("Cache-Control","no-cache");return n.set("Accept-Ranges","bytes"),n}generateEtag(e){return `"${e.size.toString(16)}-${e.mtimeMs.toString(16)}"`}getMimeType(e){let t=extname(e).toLowerCase();return ye[t]||"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 me(o){return new O(o)}var ye={".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=class{constructor(e){this.translations={};this.currentLanguage="en";this.defaultLanguage="en";this.supportedLanguages=new Set(["en"]);this.cachePath="";e&&(this.defaultLanguage=e.defaultLanguage||"en",this.currentLanguage=e.defaultLanguage||"en",this.cachePath=e.staticPath||"static/i18n",e.supportedLanguages&&(this.supportedLanguages=new Set(e.supportedLanguages)));}loadLanguage(e,t){this.translations[e]||(this.translations[e]={});let n=this.flattenObject(t);this.translations[e]={...this.translations[e],...n},this.supportedLanguages.add(e);}flattenObject(e,t=""){let n={};for(let r in e)if(Object.prototype.hasOwnProperty.call(e,r)){let i=e[r],u=t?`${t}.${r}`:r;typeof i=="object"&&i!==null&&!Array.isArray(i)?Object.assign(n,this.flattenObject(i,u)):n[u]=String(i);}return n}loadTranslations(e){Object.entries(e).forEach(([t,n])=>{this.loadLanguage(t,n);});}setLanguage(e){this.supportedLanguages.has(e)?this.currentLanguage=e:this.supportedLanguages.has(this.defaultLanguage)&&(this.currentLanguage=this.defaultLanguage);}getLanguage(){return this.currentLanguage}getSupportedLanguages(){return Array.from(this.supportedLanguages)}t(e,t,n){let r=this.currentLanguage,i=this.getTranslation(e,n);return t&&Object.entries(t).forEach(([u,a])=>{let f=this.translations[r]?.[a]||this.translations[this.defaultLanguage]?.[a]||a;i=i.replace(new RegExp(`\\{${u}\\}`,"g"),f);}),i}getTranslation(e,t){let n=this.currentLanguage;return this.translations[n]?.[e]?this.translations[n]?.[e]||this.translations[this.defaultLanguage]?.[e]:(console.warn(`Translation key not found: ${e}`),t||e)}tLang(e,t,n){let r=this.currentLanguage;this.setLanguage(t);let i=this.t(e,n);return this.currentLanguage=r,i}getTranslations(){return this.translations[this.currentLanguage]||{}}hasKey(e){return !!(this.translations[this.currentLanguage]?.[e]||this.translations[this.defaultLanguage]?.[e])}},q=null;function te(o){return q||(q=new U(o)),q}function B(){return q||(q=new U),q}function be(o,e,t){return B().t(o,e,t)}function Re(o){B().setLanguage(o);}function xe(){return B().getLanguage()}function we(){return B().getSupportedLanguages()}var W=new X,T=new V;function ve(o={}){let e=Number(o.port)||3e3,t=o.hostname||"localhost",n=o.maxRequestSize||10*1024*1024,r=o.requestTimeout||3e4,i=o.gracefulShutdownTimeout||1e4,u=typeof o.logging=="object"?o.logging:{},a=o.logging?new Logger(u.level||"info",u.pretty):null,f=null;if(o.i18n){let s=typeof o.i18n=="object"?o.i18n:{};f=te({defaultLanguage:s.defaultLanguage||"en",supportedLanguages:s.supportedLanguages||["en","ar","fr"],staticPath:s.staticPath||"./src/frontend/static/i18n"});}let w=new Map,C=[],h=new Set,z=setInterval(()=>{W.cleanupRateLimit(),W.cleanupCsrfTokens();},120*1e3);async function K(s,c){let g=Date.now(),l=crypto.randomUUID(),x=new URL(s.url).pathname,S=s.method.toUpperCase(),b=Ae(s,c);h.add(l);try{let R=s.headers.get("content-length");if(R&&parseInt(R)>n)return a?.warn({requestId:l,size:R,ip:b},"Request too large"),new Response(JSON.stringify({error:"Payload too large"}),{status:413,headers:{"Content-Type":"application/json"}});let M=Te(s,o);if(S==="OPTIONS")return new Response(null,{status:204,headers:M});if(o.security&&typeof o.security=="object"&&o.security.rateLimit){let k=typeof o.security.rateLimit=="object"?o.security.rateLimit:{},j=k.max||100,Q=k.windowMs||6e4,oe=k.keyGenerator?k.keyGenerator({request:s,ip:b}):b;if(!W.checkRateLimit(oe,j,Q))return a?.warn({requestId:l,ip:b,key:oe},"Rate limit exceeded"),new Response(JSON.stringify({error:k.message||"Too many requests"}),{status:429,headers:{"Content-Type":"application/json"}})}let L=null;["POST","PUT","PATCH"].includes(S)&&(L=await Se(s,a,n));let E=w.get("default"),$=Object.fromEntries(new URL(s.url).searchParams).lang||s.headers.get("Accept-Language")?.split(",")[0]?.split("-")[0]||"en";f&&!f.getSupportedLanguages().includes($)&&($=f.getLanguage()),f&&f.setLanguage($);let F=T.match(S,x);if(!F){let k=ce(b,s,{},E,a,l,f,$);return a?.warn({requestId:l,method:S,path:x,ip:b},"Route not found"),k.json({error:"Not Found",path:x},404)}let G=ce(b,s,F.params||{},E,a,l,f,$);G.body=L,G.request=s;let ne=new AbortController,de=new Promise((k,j)=>{let Q=setTimeout(()=>{ne.abort(),j(new J("Request timeout"));},r);ne.signal.addEventListener("abort",()=>clearTimeout(Q));}),re=F.metadata?.middlewares||[],Z;re.length>0?Z=P(G,re,F.handler):Z=Promise.resolve(F.handler(G));let N=await Promise.race([Z,de]),H=new Headers(N.headers);M.forEach((k,j)=>{H.has(j)||H.set(j,k);}),H.set("X-Request-ID",l),H.set("X-Content-Type-Options","nosniff"),H.set("X-Frame-Options","DENY"),H.set("X-XSS-Protection","1; mode=block"),H.set("Referrer-Policy","strict-origin-when-cross-origin");let se=Date.now()-g;return W.logRequest(l,S,x,b,N.status,se),a?.info({requestId:l,method:S,path:x,status:N.status,duration:se,ip:b},"Request completed"),new Response(N.body,{status:N.status,headers:H})}catch(R){if(R instanceof D)return a?.warn({error:R.message,requestId:l,ip:b},`App error: ${R.message}`),new Response(JSON.stringify({error:R.message,code:R.code,requestId:l}),{status:R.statusCode,headers:{"Content-Type":"application/json"}});a?.error({error:String(R),requestId:l,ip:b},"Unhandled error");let M=process.env.NODE_ENV==="production"?"Internal Server Error":R.message;return new Response(JSON.stringify({error:M,requestId:l}),{status:500,headers:{"Content-Type":"application/json"}})}finally{h.delete(l);}}async function P(s,c,g){let l=0,p=null,x=s.json.bind(s),S=s.text.bind(s),b=s.html.bind(s),R=s.redirect.bind(s);s.json=function(L,E){let A=x(L,E);return p=A,A},s.text=function(L,E){let A=S(L,E);return p=A,A},s.html=function(L,E){let A=b(L,E);return p=A,A},s.redirect=function(L,E){let A=R(L,E);return p=A,A};async function M(){if(!p&&l<c.length){let L=c[l];l++,await L(s,M);}}return await M(),s.json=x,s.text=S,s.html=b,s.redirect=R,p||g(s)}let d={method:"GET",path:"/health",handler:s=>s.json({status:"healthy",timestamp:new Date().toISOString(),uptime:process.uptime(),activeRequests:h.size})},m={method:"GET",path:"/readiness",handler:s=>{let c=w.size>0,g=c||w.size===0;return s.json({ready:g,checks:{database:c?"connected":"not configured",activeRequests:h.size},timestamp:new Date().toISOString()},g?200:503)}};if(o.routes&&o.routes.forEach(s=>{C.push(s),(Array.isArray(s.method)?s.method:[s.method]).forEach(g=>{T.register(g,s.path,s.handler,s);});}),o.static){let s=Array.isArray(o.static)?o.static:[o.static];for(let c of s)try{let l=new O(c).handler(),p={method:"GET",path:c.path==="/"?"/*":`${c.path}/*`,handler:l};C.push(p),c.path==="/"?(T.register("GET","/",l,p),T.register("HEAD","/",l,p),T.register("GET","/*",l,p),T.register("HEAD","/*",l,p)):(T.register("GET",`${c.path}/*`,l,p),T.register("HEAD",`${c.path}/*`,l,p));}catch(g){throw a?.error({error:String(g),path:c.path},"Failed to initialize static file server"),g}}C.push(d,m),T.register("GET","/health",d.handler,d),T.register("GET","/readiness",m.handler,m);let y=null,v={app:null,logger:a,db:w,bunServer:null,async start(){if(f&&o.i18n){let c=typeof o.i18n=="object"?o.i18n:{},g=c.staticPath||"./src/frontend/static/i18n",l=c.supportedLanguages||["en","ar","fr"];try{for(let p of l){let x=`${g}/${p}.json`,S=Bun.file(x);if(await S.exists()){let b=await S.json();f.loadLanguage(p,b);}}a?.info({languages:f.getSupportedLanguages()},"i18n translations loaded");}catch(p){a?.warn({error:String(p)},"Failed to load i18n translations");}}if(o.database){let c=Array.isArray(o.database)?o.database:[o.database];for(let g of c){let l=g.name||"default";try{if(typeof g.connection=="string"){let p=new le.DB(g.connection);if(g.schema&&typeof g.schema=="object")for(let[,x]of Object.entries(g.schema))x&&typeof x=="object"&&p.defineSchema(x);w.set(l,p),a?.info({name:l,connection:g.connection},"\u2714 Database connected");}else throw new Error(`Database connection must be a string path (got ${typeof g.connection})`)}catch(p){throw a?.error({error:String(p),name:l},"Failed to connect to database"),p}}}y=Bun.serve({port:e,hostname:t,fetch:(c,g)=>K(c,g)}),v.bunServer=y;let s=`http://${t}:${e}`;if(a?.info({url:s},"\u2714 Server started"),o.onStartup)try{await o.onStartup(v);}catch(c){a?.error({error:String(c)},"Error in startup handler");}if(o.onReady)try{await o.onReady(v,w);}catch(c){a?.error({error:String(c)},"Error in ready handler");}},async stop(){if(a?.info("Stopping server..."),h.size>0){a?.info({count:h.size},"Waiting for active requests...");let s=Date.now()+i;for(;h.size>0&&Date.now()<s;)await new Promise(c=>setTimeout(c,100));h.size>0&&a?.warn({count:h.size},"Force closing with active requests");}if(clearInterval(z),o.onShutdown)try{await o.onShutdown();}catch(s){a?.error({error:String(s)},"Error in shutdown handler");}for(let[s,c]of w.entries())try{c&&typeof c.close=="function"&&c.close(),a?.info({name:s},"Database closed");}catch(g){a?.error({error:String(g),name:s},"Error closing database");}y&&typeof y.stop=="function"&&(y.stop(),a?.info("Bun server stopped")),a?.info("Server stopped successfully");},addRoute(s){C.push(s),(Array.isArray(s.method)?s.method:[s.method]).forEach(g=>{T.register(g,s.path,s.handler,s);}),a?.info({method:s.method,path:s.path},"Route added");},addRoutes(s){s.forEach(c=>this.addRoute(c));},getRoutes(){return C}};return v}async function Se(o,e,t){let n=o.headers.get("content-type")||"";try{if(n.includes("application/json")){let r=await o.text();if(r.length>t)throw new I("Payload too large");if(!r.trim())return {};try{return JSON.parse(r)}catch(i){throw e?.warn({error:String(i),bodyPreview:r.substring(0,100)},"Invalid JSON in request body"),new I("Invalid JSON in request body")}}if(n.includes("application/x-www-form-urlencoded")){let r=await o.text();if(r.length>t)throw new I("Payload too large");return Object.fromEntries(new URLSearchParams(r))}if(n.includes("multipart/form-data"))return await o.formData()}catch(r){throw r instanceof I?r:(e?.error({error:String(r)},"Error parsing request body"),new I("Failed to parse request body"))}return {}}function Le(o){let e=new Map;if(!o)return e;let t=o.split(";");for(let n of t){let[r,...i]=n.trim().split("=");if(r){let u=i.join("=");e.set(r,u?decodeURIComponent(u):"");}}return e}function ce(o,e,t,n,r,i,u=null,a="en"){let f=new URL(e.url),w=Object.fromEntries(f.searchParams),C=e.headers,h=200,z=new Map,K=Le(C.get("cookie")||""),P={ip:o,request:e,params:t,query:w,headers:C,db:n,logger:r,i18n:u,lang:a,requestId:i,get statusCode(){return h},set statusCode(d){h=d;},body:null,state:{},json(d,m){return new Response(JSON.stringify(d),{status:m??h,headers:{"Content-Type":"application/json",...this._setCookieHeaders()}})},text(d,m){return new Response(d,{status:m??h,headers:{"Content-Type":"text/plain",...this._setCookieHeaders()}})},html(d,m){return new Response(d,{status:m??h,headers:{"Content-Type":"text/html; charset=utf-8",...this._setCookieHeaders()}})},redirect(d,m=302){return new Response(null,{status:m,headers:{Location:d,...this._setCookieHeaders()}})},file(d,m="application/octet-stream"){let y=Bun.file(d);return new Response(y,{headers:{"Content-Type":m,...this._setCookieHeaders()}})},setCookie(d,m,y={}){let v=`${d}=${encodeURIComponent(m)}`;return y.maxAge!==void 0&&(v+=`; Max-Age=${y.maxAge}`),y.expires&&(v+=`; Expires=${y.expires.toUTCString()}`),y.path&&(v+=`; Path=${y.path}`),y.domain&&(v+=`; Domain=${y.domain}`),y.secure&&(v+="; Secure"),y.httpOnly&&(v+="; HttpOnly"),y.sameSite&&(v+=`; SameSite=${y.sameSite}`),z.set(d,v),P},getCookie(d){return K.get(d)},deleteCookie(d,m={}){return P.setCookie(d,"",{...m,maxAge:0,path:m.path||"/"})},setHeader(d,m){return C.set(d,m),P},getHeader(d){return C.get(d)||void 0},status(d){return h=d,P},_setCookieHeaders(){let d={};return z.size>0&&(d["Set-Cookie"]=Array.from(z.values())),d}};return P}function Ae(o,e){let t=o.headers.get("x-forwarded-for");if(t)return t.split(",").map(i=>i.trim())[0]||"unknown";let n=o.headers.get("x-real-ip");if(n)return n;if(e)try{let i=e.requestIP?.(o);if(i?.address)return i.address}catch{}return "unknown"}function Te(o,e){let t=new Headers;if(!e.security||typeof e.security!="object"||!e.security.cors)return t;let n=typeof e.security.cors=="object"?e.security.cors:{},r=o.headers.get("Origin");if(r){typeof n.origin=="function"?n.origin(r)&&t.set("Access-Control-Allow-Origin",r):Array.isArray(n.origin)?n.origin.includes(r)&&t.set("Access-Control-Allow-Origin",r):typeof n.origin=="string"?t.set("Access-Control-Allow-Origin",n.origin):t.set("Access-Control-Allow-Origin",r);let i=n.methods||["GET","POST","PUT","DELETE","PATCH","OPTIONS"];t.set("Access-Control-Allow-Methods",i.join(", "));let u=n.allowedHeaders||["Content-Type","Authorization","X-Requested-With"];t.set("Access-Control-Allow-Headers",u.join(", ")),n.credentials&&t.set("Access-Control-Allow-Credentials","true"),n.maxAge&&t.set("Access-Control-Max-Age",n.maxAge.toString());}return t}var _e=ve;
2
+ export{D as AppError,ie as DatabaseError,U as I18nManager,ae as RateLimitError,V as Router,X as SecurityManager,O as StaticFileServer,J as TimeoutError,I as ValidationError,me as createStatic,_e as default,xe as getCurrentLanguage,B as getI18n,we as getSupportedLanguages,te as initI18n,ve as server,Re as setLanguage,be as t};//# sourceMappingURL=main.js.map
3
3
  //# sourceMappingURL=main.js.map