@je-es/server 0.1.4 → 0.1.6

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
@@ -2,6 +2,114 @@ import { DB } from '@je-es/sdb';
2
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';
3
3
  export { Logger } from '@je-es/slog';
4
4
 
5
+ declare class I18nManager {
6
+ private translations;
7
+ private currentLanguage;
8
+ private defaultLanguage;
9
+ private supportedLanguages;
10
+ private cachePath;
11
+ constructor(config?: I18nConfig);
12
+ /**
13
+ * Load translations for a specific language
14
+ * @param lang Language code (e.g., 'en', 'ar', 'fr')
15
+ * @param translations Translation object
16
+ */
17
+ loadLanguage(lang: string, translations: Record<string, string>): void;
18
+ /**
19
+ * Load all translations from static files
20
+ * @param translations Object with language codes as keys and translation objects as values
21
+ */
22
+ loadTranslations(translations: Record<string, Record<string, string>>): void;
23
+ /**
24
+ * Set the current language
25
+ * @param lang Language code
26
+ */
27
+ setLanguage(lang: string): void;
28
+ /**
29
+ * Get the current language
30
+ */
31
+ getLanguage(): string;
32
+ /**
33
+ * Get all supported languages
34
+ */
35
+ getSupportedLanguages(): string[];
36
+ /**
37
+ * Translate a key with smart parameter replacement
38
+ * Supports nested translation keys as parameter values
39
+ *
40
+ * @example
41
+ * // Simple translation
42
+ * t('app.name') // => "JE-ES Server"
43
+ *
44
+ * @example
45
+ * // With parameters
46
+ * t('validation.invalid', { field: 'email' })
47
+ * // => "Invalid value for email"
48
+ *
49
+ * @example
50
+ * // With nested translation keys as parameters
51
+ * t('message.validation', { error: 'validation.required' })
52
+ * // => "Message: This field is required"
53
+ *
54
+ * @param key Translation key (dot-notation)
55
+ * @param params Optional parameters for replacement
56
+ * @param defaultValue Optional default value
57
+ * @returns Translated string with replaced parameters
58
+ */
59
+ t(key: string, params?: Record<string, string>, defaultValue?: string): string;
60
+ private getTranslation;
61
+ /**
62
+ * Translate with a specific language (overrides current language temporarily)
63
+ *
64
+ * @param key Translation key
65
+ * @param lang Language code
66
+ * @param params Optional parameters
67
+ * @returns Translated string
68
+ */
69
+ tLang(key: string, lang: string, params?: Record<string, string>): string;
70
+ /**
71
+ * Get all translations for current language
72
+ */
73
+ getTranslations(): Record<string, string>;
74
+ /**
75
+ * Check if a translation key exists
76
+ * @param key Translation key
77
+ * @returns true if key exists in current or default language
78
+ */
79
+ hasKey(key: string): boolean;
80
+ }
81
+ /**
82
+ * Initialize the i18n manager
83
+ * @param config I18n configuration
84
+ * @returns I18nManager instance
85
+ */
86
+ declare function initI18n(config?: I18nConfig): I18nManager;
87
+ /**
88
+ * Get the global i18n instance
89
+ */
90
+ declare function getI18n(): I18nManager;
91
+ /**
92
+ * Global translation function
93
+ * @param key Translation key
94
+ * @param params Optional parameters
95
+ * @param defaultValue Optional default value
96
+ * @returns Translated string
97
+ */
98
+ declare function t(key: string, params?: Record<string, string>, defaultValue?: string): string;
99
+ /**
100
+ * Set the current language globally
101
+ * @param lang Language code
102
+ */
103
+ declare function setLanguage(lang: string): void;
104
+ /**
105
+ * Get the current language
106
+ */
107
+ declare function getCurrentLanguage(): string;
108
+ /**
109
+ * Get all supported languages
110
+ */
111
+ declare function getSupportedLanguages(): string[];
112
+
5
113
  // src/types.d.ts
6
114
  //
7
115
  // Developed with ❤️ by Maysara.
@@ -29,6 +137,8 @@ export { Logger } from '@je-es/slog';
29
137
  headers : Headers;
30
138
  db : DB | undefined;
31
139
  logger : Logger | null;
140
+ i18n : I18nManager | null;
141
+ lang? : string;
32
142
  user? : unknown;
33
143
  requestId : string;
34
144
 
@@ -159,6 +269,14 @@ export { Logger } from '@je-es/slog';
159
269
  pretty?: boolean;
160
270
  }
161
271
 
272
+ interface I18nConfig {
273
+ defaultLanguage? : string;
274
+ supportedLanguages? : string[];
275
+ staticPath? : string; // Path to static i18n files
276
+ }
277
+
278
+ type TranslationSet = Record<string, Record<string, string>>;
279
+
162
280
  interface ServerConfig {
163
281
  port? : number | string;
164
282
  hostname? : string;
@@ -174,6 +292,9 @@ export { Logger } from '@je-es/slog';
174
292
 
175
293
  logging? : boolean | LoggingConfig;
176
294
 
295
+ // Internationalization (i18n)
296
+ i18n? : boolean | I18nConfig;
297
+
177
298
  // Static file serving
178
299
  static? : StaticConfig$1 | StaticConfig$1[];
179
300
 
@@ -248,6 +369,7 @@ type RouteHandler = (ctx: AppContext) => Response | Promise<Response>;
248
369
  interface RouteMatch {
249
370
  handler: RouteHandler;
250
371
  params: Record<string, string>;
372
+ metadata?: unknown;
251
373
  }
252
374
  interface RouteInfo {
253
375
  method: string;
@@ -261,7 +383,7 @@ declare class Router {
261
383
  getAll(): RouteInfo[];
262
384
  clear(): void;
263
385
  remove(method: string, path: string): boolean;
264
- register(method: string, path: string, handler: RouteHandler, _?: unknown): void;
386
+ register(method: string, path: string, handler: RouteHandler, metadata?: unknown): void;
265
387
  private pathToRegex;
266
388
  }
267
389
 
@@ -351,4 +473,4 @@ declare function createStatic(config: StaticConfig): StaticFileServer;
351
473
 
352
474
  declare function server(config?: ServerConfig): ServerInstance;
353
475
 
354
- export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type CookieOptions, type CorsConfig, type CsrfConfig, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, type LogLevel, type LoggingConfig, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler$1 as RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type StaticConfig, StaticFileServer, TimeoutError, ValidationError, type ValidationSchema, createStatic, server as default, server };
476
+ export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type CookieOptions, type CorsConfig, type CsrfConfig, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, type I18nConfig, I18nManager, type LogLevel, type LoggingConfig, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler$1 as RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type StaticConfig, StaticFileServer, TimeoutError, type TranslationSet, ValidationError, type ValidationSchema, createStatic, server as default, getCurrentLanguage, getI18n, getSupportedLanguages, initI18n, server, setLanguage, t };
package/dist/main.d.ts CHANGED
@@ -2,6 +2,114 @@ import { DB } from '@je-es/sdb';
2
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';
3
3
  export { Logger } from '@je-es/slog';
4
4
 
5
+ declare class I18nManager {
6
+ private translations;
7
+ private currentLanguage;
8
+ private defaultLanguage;
9
+ private supportedLanguages;
10
+ private cachePath;
11
+ constructor(config?: I18nConfig);
12
+ /**
13
+ * Load translations for a specific language
14
+ * @param lang Language code (e.g., 'en', 'ar', 'fr')
15
+ * @param translations Translation object
16
+ */
17
+ loadLanguage(lang: string, translations: Record<string, string>): void;
18
+ /**
19
+ * Load all translations from static files
20
+ * @param translations Object with language codes as keys and translation objects as values
21
+ */
22
+ loadTranslations(translations: Record<string, Record<string, string>>): void;
23
+ /**
24
+ * Set the current language
25
+ * @param lang Language code
26
+ */
27
+ setLanguage(lang: string): void;
28
+ /**
29
+ * Get the current language
30
+ */
31
+ getLanguage(): string;
32
+ /**
33
+ * Get all supported languages
34
+ */
35
+ getSupportedLanguages(): string[];
36
+ /**
37
+ * Translate a key with smart parameter replacement
38
+ * Supports nested translation keys as parameter values
39
+ *
40
+ * @example
41
+ * // Simple translation
42
+ * t('app.name') // => "JE-ES Server"
43
+ *
44
+ * @example
45
+ * // With parameters
46
+ * t('validation.invalid', { field: 'email' })
47
+ * // => "Invalid value for email"
48
+ *
49
+ * @example
50
+ * // With nested translation keys as parameters
51
+ * t('message.validation', { error: 'validation.required' })
52
+ * // => "Message: This field is required"
53
+ *
54
+ * @param key Translation key (dot-notation)
55
+ * @param params Optional parameters for replacement
56
+ * @param defaultValue Optional default value
57
+ * @returns Translated string with replaced parameters
58
+ */
59
+ t(key: string, params?: Record<string, string>, defaultValue?: string): string;
60
+ private getTranslation;
61
+ /**
62
+ * Translate with a specific language (overrides current language temporarily)
63
+ *
64
+ * @param key Translation key
65
+ * @param lang Language code
66
+ * @param params Optional parameters
67
+ * @returns Translated string
68
+ */
69
+ tLang(key: string, lang: string, params?: Record<string, string>): string;
70
+ /**
71
+ * Get all translations for current language
72
+ */
73
+ getTranslations(): Record<string, string>;
74
+ /**
75
+ * Check if a translation key exists
76
+ * @param key Translation key
77
+ * @returns true if key exists in current or default language
78
+ */
79
+ hasKey(key: string): boolean;
80
+ }
81
+ /**
82
+ * Initialize the i18n manager
83
+ * @param config I18n configuration
84
+ * @returns I18nManager instance
85
+ */
86
+ declare function initI18n(config?: I18nConfig): I18nManager;
87
+ /**
88
+ * Get the global i18n instance
89
+ */
90
+ declare function getI18n(): I18nManager;
91
+ /**
92
+ * Global translation function
93
+ * @param key Translation key
94
+ * @param params Optional parameters
95
+ * @param defaultValue Optional default value
96
+ * @returns Translated string
97
+ */
98
+ declare function t(key: string, params?: Record<string, string>, defaultValue?: string): string;
99
+ /**
100
+ * Set the current language globally
101
+ * @param lang Language code
102
+ */
103
+ declare function setLanguage(lang: string): void;
104
+ /**
105
+ * Get the current language
106
+ */
107
+ declare function getCurrentLanguage(): string;
108
+ /**
109
+ * Get all supported languages
110
+ */
111
+ declare function getSupportedLanguages(): string[];
112
+
5
113
  // src/types.d.ts
6
114
  //
7
115
  // Developed with ❤️ by Maysara.
@@ -29,6 +137,8 @@ export { Logger } from '@je-es/slog';
29
137
  headers : Headers;
30
138
  db : DB | undefined;
31
139
  logger : Logger | null;
140
+ i18n : I18nManager | null;
141
+ lang? : string;
32
142
  user? : unknown;
33
143
  requestId : string;
34
144
 
@@ -159,6 +269,14 @@ export { Logger } from '@je-es/slog';
159
269
  pretty?: boolean;
160
270
  }
161
271
 
272
+ interface I18nConfig {
273
+ defaultLanguage? : string;
274
+ supportedLanguages? : string[];
275
+ staticPath? : string; // Path to static i18n files
276
+ }
277
+
278
+ type TranslationSet = Record<string, Record<string, string>>;
279
+
162
280
  interface ServerConfig {
163
281
  port? : number | string;
164
282
  hostname? : string;
@@ -174,6 +292,9 @@ export { Logger } from '@je-es/slog';
174
292
 
175
293
  logging? : boolean | LoggingConfig;
176
294
 
295
+ // Internationalization (i18n)
296
+ i18n? : boolean | I18nConfig;
297
+
177
298
  // Static file serving
178
299
  static? : StaticConfig$1 | StaticConfig$1[];
179
300
 
@@ -248,6 +369,7 @@ type RouteHandler = (ctx: AppContext) => Response | Promise<Response>;
248
369
  interface RouteMatch {
249
370
  handler: RouteHandler;
250
371
  params: Record<string, string>;
372
+ metadata?: unknown;
251
373
  }
252
374
  interface RouteInfo {
253
375
  method: string;
@@ -261,7 +383,7 @@ declare class Router {
261
383
  getAll(): RouteInfo[];
262
384
  clear(): void;
263
385
  remove(method: string, path: string): boolean;
264
- register(method: string, path: string, handler: RouteHandler, _?: unknown): void;
386
+ register(method: string, path: string, handler: RouteHandler, metadata?: unknown): void;
265
387
  private pathToRegex;
266
388
  }
267
389
 
@@ -351,4 +473,4 @@ declare function createStatic(config: StaticConfig): StaticFileServer;
351
473
 
352
474
  declare function server(config?: ServerConfig): ServerInstance;
353
475
 
354
- export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type CookieOptions, type CorsConfig, type CsrfConfig, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, type LogLevel, type LoggingConfig, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler$1 as RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type StaticConfig, StaticFileServer, TimeoutError, ValidationError, type ValidationSchema, createStatic, server as default, server };
476
+ export { type AppContext, AppError, type AppMiddleware, type AuthConfig, type CookieOptions, type CorsConfig, type CsrfConfig, type DatabaseConfig, DatabaseError, type HelmetConfig, type HttpMethod, type I18nConfig, I18nManager, type LogLevel, type LoggingConfig, type RateLimitConfig, RateLimitError, type RouteDefinition, type RouteHandler$1 as RouteHandler, Router, type SecurityConfig, SecurityManager, type ServerConfig, type ServerInstance, type StaticConfig, StaticFileServer, TimeoutError, type TranslationSet, ValidationError, type ValidationSchema, createStatic, server as default, getCurrentLanguage, getI18n, getSupportedLanguages, initI18n, server, setLanguage, t };
package/dist/main.js CHANGED
@@ -1,3 +1,3 @@
1
- import*as ee from'@je-es/sdb';export{DB,blob,column,defaultValue,integer,notNull,numeric,primaryKey,real,references,table,text,unique}from'@je-es/sdb';import re 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 O=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 s of this.regexRoutes)if(s.method===e){let o=r.match(s.pattern);if(o){let c=o.groups||{};return {handler:s.handler,params:c}}}return null}getAll(){let e=Array.from(this.routes.entries()).map(([t,s])=>{let o=t.indexOf(":"),c=t.substring(0,o),a=t.substring(o+1);return {method:c,path:a,handler:s}}),r=this.regexRoutes.map(t=>{let s=t.key.indexOf(":");return {method:t.method,path:t.key.substring(s+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 s=this.regexRoutes.findIndex(o=>o.key===t);return s>=0?(this.regexRoutes.splice(s,1),true):false}register(e,r,t,s={}){let o=`${e}:${r}`;if(r.includes(":")||r.includes("*")){let c=this.pathToRegex(r),a=this.regexRoutes.findIndex(h=>h.key===o),m={pattern:c,method:e,handler:t,key:o};a>=0?this.regexRoutes[a]=m:this.regexRoutes.push(m);}else this.routes.set(o,t);}pathToRegex(e){let r=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return r=r.replace(/:(\w+)/g,"(?<$1>[^/]+)"),r=r.replace(/\*/g,".*"),new RegExp(`^${r}$`)}};var j=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 s=Date.now(),o=this.rateLimitStore.get(e);return o?s<o.reset?o.count>=r?false:(o.count++,true):(this.rateLimitStore.set(e,{count:1,reset:s+t}),true):(this.rateLimitStore.set(e,{count:1,reset:s+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=re.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(/\u0000/g,""):""}logRequest(e,r,t,s,o,c){if(this.requestLog.set(e,{timestamp:new Date().toISOString(),method:r,path:t,ip:s,status:o,duration:c}),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 T=class extends Error{constructor(r,t=500,s){super(r);this.message=r;this.statusCode=t;this.code=s;this.name="AppError";}},k=class extends T{constructor(r,t){super(r,400,"VALIDATION_ERROR");this.issues=t;this.name="ValidationError";}},K=class extends T{constructor(e){super(e,500,"DATABASE_ERROR"),this.name="DatabaseError";}},z=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 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 r=e.request.url,s=new URL(r).pathname;s.startsWith(this.config.path)&&(s=s.slice(this.config.path.length));try{s=decodeURIComponent(s);}catch{return e.json({error:"Invalid URL encoding"},400)}if(s.includes("..")||s.includes("\\"))return e.json({error:"Forbidden"},403);if(this.config.dotfiles!=="allow"&&s.split("/").some(a=>a.startsWith(".")))return this.config.dotfiles==="deny"?e.json({error:"Forbidden"},403):this.handleNotFound(e);let o=this.resolveFilePath(s);if(!o)return this.handleNotFound(e);if(!this.isPathSafe(o))return e.json({error:"Forbidden"},403);if(!existsSync(o))return this.handleNotFound(e);let c=statSync(o);return c.isDirectory()?this.serveDirectory(e,o,s):this.serveFile(e,o,c)}}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 s=`${r}.${t}`;if(existsSync(s))return s}return r}isPathSafe(e){return !relative(this.resolvedDir,resolve(e)).startsWith("..")&&!resolve(e).startsWith("..")}async serveDirectory(e,r,t){for(let s of this.config.index){let o=join(r,s);if(existsSync(o)){let c=statSync(o);if(c.isFile())return this.serveFile(e,o,c)}}return this.handleNotFound(e)}async serveFile(e,r,t){let s=e.request.method.toUpperCase();if(s!=="GET"&&s!=="HEAD")return e.json({error:"Method not allowed"},405);let o=r,c=this.fileCache.get(o);if(c&&c.mtime!==t.mtimeMs&&(c=void 0),!c){if(c={etag:this.generateEtag(t),lastModified:new Date(t.mtime),size:t.size,mtime:t.mtimeMs},this.fileCache.size>=this.CACHE_MAX_SIZE){let A=this.fileCache.keys().next().value;A&&this.fileCache.delete(A);}this.fileCache.set(o,c);}let a=e.request.headers.get("if-none-match"),m=e.request.headers.get("if-modified-since");if(this.config.etag&&a===c.etag)return new Response(null,{status:304,headers:this.buildHeaders(r,c)});if(this.config.lastModified&&m){let A=new Date(m);if(c.lastModified<=A)return new Response(null,{status:304,headers:this.buildHeaders(r,c)})}let h=Bun.file(r),y=this.buildHeaders(r,c);return this.config.setHeaders&&this.config.setHeaders(e,r),s==="HEAD"?new Response(null,{status:200,headers:y}):new Response(h,{status:200,headers:y})}buildHeaders(e,r){let t=new Headers,s=this.getMimeType(e);if(t.set("Content-Type",s),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 o=`public, max-age=${this.config.maxAge}`;this.config.immutable&&(o+=", immutable"),t.set("Cache-Control",o);}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 ae[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 ie(n){return new q(n)}var ae={".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 F=new j,C=new O;function ue(n={}){let e=Number(n.port)||3e3,r=n.hostname||"localhost",t=n.maxRequestSize||10*1024*1024,s=n.requestTimeout||3e4,o=n.gracefulShutdownTimeout||1e4,c=typeof n.logging=="object"?n.logging:{},a=n.logging?new Logger(c.level||"info",c.pretty):null,m=new Map,h=[],y=new Set,A=setInterval(()=>{F.cleanupRateLimit(),F.cleanupCsrfTokens();},120*1e3);async function H(i,d){let p=Date.now(),l=crypto.randomUUID(),S=new URL(i.url).pathname,L=i.method.toUpperCase(),w=pe(i,d);y.add(l);try{let b=i.headers.get("content-length");if(b&&parseInt(b)>t)return a?.warn({requestId:l,size:b,ip:w},"Request too large"),new Response(JSON.stringify({error:"Payload too large"}),{status:413,headers:{"Content-Type":"application/json"}});let I=ge(i,n);if(L==="OPTIONS")return new Response(null,{status:204,headers:I});if(n.security&&typeof n.security=="object"&&n.security.rateLimit){let v=typeof n.security.rateLimit=="object"?n.security.rateLimit:{},D=v.max||100,_=v.windowMs||6e4,J=v.keyGenerator?v.keyGenerator({request:i,ip:w}):w;if(!F.checkRateLimit(J,D,_))return a?.warn({requestId:l,ip:w,key:J},"Rate limit exceeded"),new Response(JSON.stringify({error:v.message||"Too many requests"}),{status:429,headers:{"Content-Type":"application/json"}})}let B=null;["POST","PUT","PATCH"].includes(L)&&(B=await de(i,a,t));let V=m.get("default"),$=C.match(L,S);if(!$){let v=Y(w,i,{},V,a,l);return a?.warn({requestId:l,method:L,path:S,ip:w},"Route not found"),v.json({error:"Not Found",path:S},404)}let N=Y(w,i,$.params||{},V,a,l);N.body=B,N.request=i;let X=new AbortController,te=new Promise((v,D)=>{let _=setTimeout(()=>{X.abort(),D(new z("Request timeout"));},s);X.signal.addEventListener("abort",()=>clearTimeout(_));}),M=await Promise.race([$.handler(N),te]),E=new Headers(M.headers);I.forEach((v,D)=>{E.has(D)||E.set(D,v);}),E.set("X-Request-ID",l),E.set("X-Content-Type-Options","nosniff"),E.set("X-Frame-Options","DENY"),E.set("X-XSS-Protection","1; mode=block"),E.set("Referrer-Policy","strict-origin-when-cross-origin");let W=Date.now()-p;return F.logRequest(l,L,S,w,M.status,W),a?.info({requestId:l,method:L,path:S,status:M.status,duration:W,ip:w},"Request completed"),new Response(M.body,{status:M.status,headers:E})}catch(b){if(b instanceof T)return a?.warn({error:b.message,requestId:l,ip:w},`App error: ${b.message}`),new Response(JSON.stringify({error:b.message,code:b.code,requestId:l}),{status:b.statusCode,headers:{"Content-Type":"application/json"}});a?.error({error:String(b),requestId:l,ip:w},"Unhandled error");let I=process.env.NODE_ENV==="production"?"Internal Server Error":b.message;return new Response(JSON.stringify({error:I,requestId:l}),{status:500,headers:{"Content-Type":"application/json"}})}finally{y.delete(l);}}let u={method:"GET",path:"/health",handler:i=>i.json({status:"healthy",timestamp:new Date().toISOString(),uptime:process.uptime(),activeRequests:y.size})},g={method:"GET",path:"/readiness",handler:i=>{let d=m.size>0,p=d||m.size===0;return i.json({ready:p,checks:{database:d?"connected":"not configured",activeRequests:y.size},timestamp:new Date().toISOString()},p?200:503)}};if(n.routes&&n.routes.forEach(i=>{h.push(i),(Array.isArray(i.method)?i.method:[i.method]).forEach(p=>{C.register(p,i.path,i.handler,i);});}),n.static){let i=Array.isArray(n.static)?n.static:[n.static];for(let d of i)try{let l=new q(d).handler(),x={method:"GET",path:d.path==="/"?"/*":`${d.path}/*`,handler:l};h.push(x),d.path==="/"?(C.register("GET","/",l,x),C.register("HEAD","/",l,x),C.register("GET","/*",l,x),C.register("HEAD","/*",l,x)):(C.register("GET",`${d.path}/*`,l,x),C.register("HEAD",`${d.path}/*`,l,x));}catch(p){throw a?.error({error:String(p),path:d.path},"Failed to initialize static file server"),p}}h.push(u,g),C.register("GET","/health",u.handler,u),C.register("GET","/readiness",g.handler,g);let f=null,R={app:null,logger:a,db:m,bunServer:null,async start(){if(n.database){let d=Array.isArray(n.database)?n.database:[n.database];for(let p of d){let l=p.name||"default";try{if(typeof p.connection=="string"){let x=new ee.DB(p.connection);if(p.schema&&typeof p.schema=="object")for(let[,S]of Object.entries(p.schema))S&&typeof S=="object"&&x.defineSchema(S);m.set(l,x),a?.info({name:l,connection:p.connection},"\u2714 Database connected");}else throw new Error(`Database connection must be a string path (got ${typeof p.connection})`)}catch(x){throw a?.error({error:String(x),name:l},"Failed to connect to database"),x}}}f=Bun.serve({port:e,hostname:r,fetch:(d,p)=>H(d,p)}),R.bunServer=f;let i=`http://${r}:${e}`;a?.info({url:i},"Server started");},async stop(){if(a?.info("Stopping server..."),y.size>0){a?.info({count:y.size},"Waiting for active requests...");let i=Date.now()+o;for(;y.size>0&&Date.now()<i;)await new Promise(d=>setTimeout(d,100));y.size>0&&a?.warn({count:y.size},"Force closing with active requests");}if(clearInterval(A),n.onShutdown)try{await n.onShutdown();}catch(i){a?.error({error:String(i)},"Error in shutdown handler");}for(let[i,d]of m.entries())try{d&&typeof d.close=="function"&&d.close(),a?.info({name:i},"Database closed");}catch(p){a?.error({error:String(p),name:i},"Error closing database");}f&&typeof f.stop=="function"&&(f.stop(),a?.info("Bun server stopped")),a?.info("Server stopped successfully");},addRoute(i){h.push(i),(Array.isArray(i.method)?i.method:[i.method]).forEach(p=>{C.register(p,i.path,i.handler,i);}),a?.info({method:i.method,path:i.path},"Route added");},addRoutes(i){i.forEach(d=>this.addRoute(d));},getRoutes(){return h}};return R}async function de(n,e,r){let t=n.headers.get("content-type")||"";try{if(t.includes("application/json")){let s=await n.text();if(s.length>r)throw new k("Payload too large");if(!s.trim())return {};try{return JSON.parse(s)}catch(o){throw e?.warn({error:String(o),bodyPreview:s.substring(0,100)},"Invalid JSON in request body"),new k("Invalid JSON in request body")}}if(t.includes("application/x-www-form-urlencoded")){let s=await n.text();if(s.length>r)throw new k("Payload too large");return Object.fromEntries(new URLSearchParams(s))}if(t.includes("multipart/form-data"))return await n.formData()}catch(s){throw s instanceof k?s:(e?.error({error:String(s)},"Error parsing request body"),new k("Failed to parse request body"))}return {}}function le(n){let e=new Map;if(!n)return e;let r=n.split(";");for(let t of r){let[s,...o]=t.trim().split("=");if(s){let c=o.join("=");e.set(s,c?decodeURIComponent(c):"");}}return e}function Y(n,e,r,t,s,o){let c=new URL(e.url),a=Object.fromEntries(c.searchParams),m=e.headers,h=200,y=new Map,A=le(m.get("cookie")||""),H={ip:n,request:e,params:r,query:a,headers:m,db:t,logger:s,requestId:o,get statusCode(){return h},set statusCode(u){h=u;},body:null,json(u,g){return new Response(JSON.stringify(u),{status:g??h,headers:{"Content-Type":"application/json",...this._setCookieHeaders()}})},text(u,g){return new Response(u,{status:g??h,headers:{"Content-Type":"text/plain",...this._setCookieHeaders()}})},html(u,g){return new Response(u,{status:g??h,headers:{"Content-Type":"text/html; charset=utf-8",...this._setCookieHeaders()}})},redirect(u,g=302){return new Response(null,{status:g,headers:{Location:u,...this._setCookieHeaders()}})},file(u,g="application/octet-stream"){let f=Bun.file(u);return new Response(f,{headers:{"Content-Type":g,...this._setCookieHeaders()}})},setCookie(u,g,f={}){let R=`${u}=${encodeURIComponent(g)}`;return f.maxAge!==void 0&&(R+=`; Max-Age=${f.maxAge}`),f.expires&&(R+=`; Expires=${f.expires.toUTCString()}`),f.path&&(R+=`; Path=${f.path}`),f.domain&&(R+=`; Domain=${f.domain}`),f.secure&&(R+="; Secure"),f.httpOnly&&(R+="; HttpOnly"),f.sameSite&&(R+=`; SameSite=${f.sameSite}`),y.set(u,R),H},getCookie(u){return A.get(u)},deleteCookie(u,g={}){return H.setCookie(u,"",{...g,maxAge:0,path:g.path||"/"})},setHeader(u,g){return m.set(u,g),H},getHeader(u){return m.get(u)||void 0},status(u){return h=u,H},_setCookieHeaders(){let u={};return y.size>0&&(u["Set-Cookie"]=Array.from(y.values())),u}};return H}function pe(n,e){let r=n.headers.get("x-forwarded-for");if(r)return r.split(",").map(o=>o.trim())[0]||"unknown";let t=n.headers.get("x-real-ip");if(t)return t;if(e)try{let o=e.requestIP?.(n);if(o?.address)return o.address}catch{}return "unknown"}function ge(n,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:{},s=n.headers.get("Origin");if(s){typeof t.origin=="function"?t.origin(s)&&r.set("Access-Control-Allow-Origin",s):Array.isArray(t.origin)?t.origin.includes(s)&&r.set("Access-Control-Allow-Origin",s):typeof t.origin=="string"?r.set("Access-Control-Allow-Origin",t.origin):r.set("Access-Control-Allow-Origin",s);let o=t.methods||["GET","POST","PUT","DELETE","PATCH","OPTIONS"];r.set("Access-Control-Allow-Methods",o.join(", "));let c=t.allowedHeaders||["Content-Type","Authorization","X-Requested-With"];r.set("Access-Control-Allow-Headers",c.join(", ")),t.credentials&&r.set("Access-Control-Allow-Credentials","true"),t.maxAge&&r.set("Access-Control-Max-Age",t.maxAge.toString());}return r}var Se=ue;
2
- export{T as AppError,K as DatabaseError,Z as RateLimitError,O as Router,j as SecurityManager,q as StaticFileServer,z as TimeoutError,k as ValidationError,ie as createStatic,Se as default,ue as server};//# sourceMappingURL=main.js.map
1
+ import*as ge from'@je-es/sdb';export{DB,blob,column,defaultValue,integer,notNull,numeric,primaryKey,real,references,table,text,unique}from'@je-es/sdb';import de 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(C=>C.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=de.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 C=Bun.file(t),w=this.buildHeaders(t,u);return this.config.setHeaders&&this.config.setHeaders(e,t),r==="HEAD"?new Response(null,{status:200,headers:w}):new Response(C,{status:200,headers:w})}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 G(){return O||(O=new U),O}function be(o,e,t){return G().t(o,e,t)}function xe(o){G().setLanguage(o);}function Re(){return G().getLanguage()}function we(){return G().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||"static/i18n"});}let C=new Map,w=[],h=new Set,z=setInterval(()=>{W.cleanupRateLimit(),W.cleanupCsrfTokens();},120*1e3);async function K(s,c){let d=Date.now(),g=crypto.randomUUID(),R=new URL(s.url).pathname,v=s.method.toUpperCase(),b=Ae(s,c);h.add(g);try{let x=s.headers.get("content-length");if(x&&parseInt(x)>n)return a?.warn({requestId:g,size:x,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:g,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 S=null;["POST","PUT","PATCH"].includes(v)&&(S=await Se(s,a,n));let E=C.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,R);if(!F){let k=ce(b,s,{},E,a,g,f,$);return a?.warn({requestId:g,method:v,path:R,ip:b},"Route not found"),k.json({error:"Not Found",path:R},404)}let B=ce(b,s,F.params||{},E,a,g,f,$);B.body=S,B.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(B,re,F.handler):Z=Promise.resolve(F.handler(B));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",g),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()-d;return W.logRequest(g,v,R,b,N.status,se),a?.info({requestId:g,method:v,path:R,status:N.status,duration:se,ip:b},"Request completed"),new Response(N.body,{status:N.status,headers:H})}catch(x){if(x instanceof D)return a?.warn({error:x.message,requestId:g,ip:b},`App error: ${x.message}`),new Response(JSON.stringify({error:x.message,code:x.code,requestId:g}),{status:x.statusCode,headers:{"Content-Type":"application/json"}});a?.error({error:String(x),requestId:g,ip:b},"Unhandled error");let M=process.env.NODE_ENV==="production"?"Internal Server Error":x.message;return new Response(JSON.stringify({error:M,requestId:g}),{status:500,headers:{"Content-Type":"application/json"}})}finally{h.delete(g);}}async function P(s,c,d){let g=0,p=null,R=s.json.bind(s),v=s.text.bind(s),b=s.html.bind(s),x=s.redirect.bind(s);s.json=function(S,E){let L=R(S,E);return p=L,L},s.text=function(S,E){let L=v(S,E);return p=L,L},s.html=function(S,E){let L=b(S,E);return p=L,L},s.redirect=function(S,E){let L=x(S,E);return p=L,L};async function M(){if(!p&&g<c.length){let S=c[g];g++,await S(s,M);}}return await M(),s.json=R,s.text=v,s.html=b,s.redirect=x,p||d(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=C.size>0,d=c||C.size===0;return s.json({ready:d,checks:{database:c?"connected":"not configured",activeRequests:h.size},timestamp:new Date().toISOString()},d?200:503)}};if(o.routes&&o.routes.forEach(s=>{w.push(s),(Array.isArray(s.method)?s.method:[s.method]).forEach(d=>{T.register(d,s.path,s.handler,s);});}),o.static){let s=Array.isArray(o.static)?o.static:[o.static];for(let c of s)try{let g=new q(c).handler(),p={method:"GET",path:c.path==="/"?"/*":`${c.path}/*`,handler:g};w.push(p),c.path==="/"?(T.register("GET","/",g,p),T.register("HEAD","/",g,p),T.register("GET","/*",g,p),T.register("HEAD","/*",g,p)):(T.register("GET",`${c.path}/*`,g,p),T.register("HEAD",`${c.path}/*`,g,p));}catch(d){throw a?.error({error:String(d),path:c.path},"Failed to initialize static file server"),d}}w.push(l,m),T.register("GET","/health",l.handler,l),T.register("GET","/readiness",m.handler,m);let y=null,A={app:null,logger:a,db:C,bunServer:null,async start(){if(f&&o.i18n){let c=typeof o.i18n=="object"?o.i18n:{},d=c.staticPath||"static/i18n",g=c.supportedLanguages||["en","ar","fr"];try{for(let p of g){let R=`${d}/${p}.json`,v=Bun.file(R);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 d of c){let g=d.name||"default";try{if(typeof d.connection=="string"){let p=new ge.DB(d.connection);if(d.schema&&typeof d.schema=="object")for(let[,R]of Object.entries(d.schema))R&&typeof R=="object"&&p.defineSchema(R);C.set(g,p),a?.info({name:g,connection:d.connection},"\u2714 Database connected");}else throw new Error(`Database connection must be a string path (got ${typeof d.connection})`)}catch(p){throw a?.error({error:String(p),name:g},"Failed to connect to database"),p}}}y=Bun.serve({port:e,hostname:t,fetch:(c,d)=>K(c,d)}),A.bunServer=y;let s=`http://${t}:${e}`;a?.info({url:s},"\u2714 Server started");},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 C.entries())try{c&&typeof c.close=="function"&&c.close(),a?.info({name:s},"Database closed");}catch(d){a?.error({error:String(d),name:s},"Error closing database");}y&&typeof y.stop=="function"&&(y.stop(),a?.info("Bun server stopped")),a?.info("Server stopped successfully");},addRoute(s){w.push(s),(Array.isArray(s.method)?s.method:[s.method]).forEach(d=>{T.register(d,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 w}};return A}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),C=Object.fromEntries(f.searchParams),w=e.headers,h=200,z=new Map,K=Le(w.get("cookie")||""),P={ip:o,request:e,params:t,query:C,headers:w,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 A=`${l}=${encodeURIComponent(m)}`;return y.maxAge!==void 0&&(A+=`; Max-Age=${y.maxAge}`),y.expires&&(A+=`; Expires=${y.expires.toUTCString()}`),y.path&&(A+=`; Path=${y.path}`),y.domain&&(A+=`; Domain=${y.domain}`),y.secure&&(A+="; Secure"),y.httpOnly&&(A+="; HttpOnly"),y.sameSite&&(A+=`; SameSite=${y.sameSite}`),z.set(l,A),P},getCookie(l){return K.get(l)},deleteCookie(l,m={}){return P.setCookie(l,"",{...m,maxAge:0,path:m.path||"/"})},setHeader(l,m){return w.set(l,m),P},getHeader(l){return w.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=ve;
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,Re as getCurrentLanguage,G as getI18n,we as getSupportedLanguages,te as initI18n,ve as server,xe as setLanguage,be as t};//# sourceMappingURL=main.js.map
3
3
  //# sourceMappingURL=main.js.map