viewlogic
Version:
A lightweight, file-based routing system for Vue 3 applications with zero build configuration
52 lines • 54.6 kB
JavaScript
/* ViewLogic v1.0.0 | (c) 2024 hopegiver | MIT License */
var C=class{constructor(e,t={}){this.config={enabled:t.useI18n!==void 0?t.useI18n:!0,defaultLanguage:t.defaultLanguage||"ko",fallbackLanguage:t.defaultLanguage||"ko",cacheKey:t.cacheKey||"viewlogic_lang",dataCacheKey:t.dataCacheKey||"viewlogic_i18n_data",cacheVersion:t.cacheVersion||"1.0.0",enableDataCache:t.enableDataCache!==!1,debug:t.debug||!1},this.router=e,this.messages=new Map,this.currentLanguage=this.config.defaultLanguage,this.isLoading=!1,this.loadPromises=new Map,this.listeners={languageChanged:[]},this.initPromise=this.init()}async init(){if(!this.config.enabled){this.log("info","I18n system disabled");return}if(this.loadLanguageFromCache(),this.config.debug&&(this.config.enableDataCache=!1,this.log("debug","Data cache disabled in debug mode")),this.messages.has(this.currentLanguage))this.log("debug","Language messages already loaded:",this.currentLanguage);else try{await this.loadMessages(this.currentLanguage)}catch(e){this.log("error","Failed to load initial language file:",e),this.messages.set(this.currentLanguage,{}),this.log("info","Using empty message object as fallback")}}loadLanguageFromCache(){try{let e=localStorage.getItem(this.config.cacheKey);e&&this.isValidLanguage(e)&&(this.currentLanguage=e,this.log("debug","Language loaded from cache:",e))}catch(e){this.log("warn","Failed to load language from cache:",e)}}isValidLanguage(e){return typeof e=="string"&&/^[a-z]{2}$/.test(e)}getCurrentLanguage(){return this.currentLanguage}async setLanguage(e){if(!this.isValidLanguage(e))return this.log("warn","Invalid language code:",e),!1;if(this.currentLanguage===e)return this.log("debug","Language already set to:",e),!0;let t=this.currentLanguage;this.currentLanguage=e;try{return await this.loadMessages(e),this.saveLanguageToCache(e),this.emit("languageChanged",{from:t,to:e,messages:this.messages.get(e)}),this.log("info","Language changed successfully",{from:t,to:e}),!0}catch(r){return this.log("error","Failed to load messages for language change, using empty messages:",r),this.messages.set(e,{}),this.saveLanguageToCache(e),this.emit("languageChanged",{from:t,to:e,messages:{},error:!0}),this.log("warn","Language changed with empty messages",{from:t,to:e}),!0}}saveLanguageToCache(e){try{localStorage.setItem(this.config.cacheKey,e),this.log("debug","Language saved to cache:",e)}catch(t){this.log("warn","Failed to save language to cache:",t)}}async loadMessages(e){if(this.messages.has(e))return this.log("debug","Messages already loaded for:",e),this.messages.get(e);if(this.loadPromises.has(e))return this.log("debug","Messages loading in progress for:",e),await this.loadPromises.get(e);let t=this._loadMessagesFromFile(e);this.loadPromises.set(e,t);try{let r=await t;return this.messages.set(e,r),this.loadPromises.delete(e),this.log("debug","Messages loaded successfully for:",e),r}catch(r){this.loadPromises.delete(e),this.log("error","Failed to load messages, using empty fallback for:",e,r);let a={};return this.messages.set(e,a),a}}async _loadMessagesFromFile(e){if(this.config.enableDataCache){let t=this.getDataFromCache(e);if(t)return this.log("debug","Messages loaded from cache:",e),t}try{let t=`${this.router.config.i18nPath}/${e}.json`,r=await fetch(t);if(!r.ok)throw new Error(`HTTP error! status: ${r.status}`);let a=await r.json();return this.config.enableDataCache&&this.saveDataToCache(e,a),a}catch(t){if(this.log("error","Failed to load messages file for:",e,t),e!==this.config.fallbackLanguage){this.log("info","Trying fallback language:",this.config.fallbackLanguage);try{return await this._loadMessagesFromFile(this.config.fallbackLanguage)}catch(r){return this.log("error","Fallback language also failed:",r),{}}}return this.log("warn",`No messages available for language: ${e}, using empty fallback`),{}}}getDataFromCache(e){try{let t=`${this.config.dataCacheKey}_${e}_${this.config.cacheVersion}`,r=localStorage.getItem(t);if(r){let{data:a,timestamp:o,version:s}=JSON.parse(r);if(s!==this.config.cacheVersion)return this.log("debug","Cache version mismatch, clearing:",e),localStorage.removeItem(t),null;let n=Date.now(),c=1440*60*1e3;return n-o>c?(this.log("debug","Cache expired, removing:",e),localStorage.removeItem(t),null):a}}catch(t){this.log("warn","Failed to read from cache:",t)}return null}saveDataToCache(e,t){try{let r=`${this.config.dataCacheKey}_${e}_${this.config.cacheVersion}`,a={data:t,timestamp:Date.now(),version:this.config.cacheVersion};localStorage.setItem(r,JSON.stringify(a)),this.log("debug","Data saved to cache:",e)}catch(r){this.log("warn","Failed to save to cache:",r)}}t(e,t={}){if(!this.config.enabled)return e;let r=this.messages.get(this.currentLanguage);if(!r)return this.log("warn","No messages loaded for current language:",this.currentLanguage),e;let a=this.getNestedValue(r,e);if(a===void 0){this.log("warn","Translation not found for key:",e);let o=this.messages.get(this.config.fallbackLanguage);if(o&&this.currentLanguage!==this.config.fallbackLanguage){let s=this.getNestedValue(o,e);if(s!==void 0)return this.interpolate(s,t)}return e}return this.interpolate(a,t)}getNestedValue(e,t){return t.split(".").reduce((r,a)=>r&&r[a]!==void 0?r[a]:void 0,e)}interpolate(e,t){return typeof e!="string"?e:e.replace(/\{(\w+)\}/g,(r,a)=>t.hasOwnProperty(a)?t[a]:r)}plural(e,t,r={}){let a=t===1?`${e}.singular`:`${e}.plural`;return this.t(a,{...r,count:t})}getAvailableLanguages(){return["ko","en"]}on(e,t){this.listeners[e]&&this.listeners[e].push(t)}off(e,t){if(this.listeners[e]){let r=this.listeners[e].indexOf(t);r>-1&&this.listeners[e].splice(r,1)}}emit(e,t){this.listeners[e]&&this.listeners[e].forEach(r=>{try{r(t)}catch(a){this.log("error","Error in event listener:",a)}})}getMessages(){return this.messages.get(this.currentLanguage)||{}}formatDate(e,t={}){let r=this.currentLanguage==="ko"?"ko-KR":"en-US";return new Intl.DateTimeFormat(r,t).format(new Date(e))}formatNumber(e,t={}){let r=this.currentLanguage==="ko"?"ko-KR":"en-US";return new Intl.NumberFormat(r,t).format(e)}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"I18nManager",...t)}isEnabled(){return this.config.enabled}async isReady(){if(!this.config.enabled)return!0;try{return await this.initPromise,!0}catch(e){return this.log("error","I18n initialization failed:",e),this.log("info","I18n system ready with fallback behavior"),!0}}clearCache(){try{let t=Object.keys(localStorage).filter(r=>r.startsWith(this.config.dataCacheKey));t.forEach(r=>{localStorage.removeItem(r)}),this.log("debug","Cache cleared, removed",t.length,"items")}catch(e){this.log("warn","Failed to clear cache:",e)}}getCacheInfo(){let e={enabled:this.config.enableDataCache,version:this.config.cacheVersion,languages:{}};try{Object.keys(localStorage).filter(a=>a.startsWith(this.config.dataCacheKey)).forEach(a=>{let o=a.match(new RegExp(`${this.config.dataCacheKey}_(w+)_(.+)`));if(o){let[,s,n]=o,c=JSON.parse(localStorage.getItem(a));e.languages[s]={version:n,timestamp:c.timestamp,age:Date.now()-c.timestamp}}})}catch(t){this.log("warn","Failed to get cache info:",t)}return e}async initialize(){if(!this.config.enabled)return this.log("info","I18n system is disabled, skipping initialization"),!0;try{return await this.initPromise,this.log("info","I18n system fully initialized"),!0}catch(e){return this.log("error","Failed to initialize I18n system:",e),this.log("info","I18n system will continue with fallback behavior"),!0}}};var $=class{constructor(e,t={}){this.config={enabled:t.authEnabled||!1,loginRoute:t.loginRoute||"login",protectedRoutes:t.protectedRoutes||[],protectedPrefixes:t.protectedPrefixes||[],publicRoutes:t.publicRoutes||["login","register","home"],checkAuthFunction:t.checkAuthFunction||null,redirectAfterLogin:t.redirectAfterLogin||"home",authCookieName:t.authCookieName||"authToken",authFallbackCookieNames:t.authFallbackCookieNames||["accessToken","token","jwt"],authStorage:t.authStorage||"cookie",authCookieOptions:t.authCookieOptions||{},authSkipValidation:t.authSkipValidation||!1,debug:t.debug||!1},this.router=e,this.eventListeners=new Map,this.log("info","AuthManager initialized",{enabled:this.config.enabled})}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"AuthManager",...t)}async checkAuthentication(e){if(!this.config.enabled)return{allowed:!0,reason:"auth_disabled"};if(this.log("debug",`\u{1F510} Checking authentication for route: ${e}`),this.isPublicRoute(e))return{allowed:!0,reason:"public_route"};if(!this.isProtectedRoute(e))return{allowed:!0,reason:"not_protected"};if(typeof this.config.checkAuthFunction=="function")try{let a=await this.config.checkAuthFunction(e);return{allowed:a,reason:a?"custom_auth_success":"custom_auth_failed",routeName:e}}catch(a){return this.log("error","Custom auth function failed:",a),{allowed:!1,reason:"custom_auth_error",error:a}}let r=this.isUserAuthenticated();return{allowed:r,reason:r?"authenticated":"not_authenticated",routeName:e}}isUserAuthenticated(){this.log("debug","\u{1F50D} Checking user authentication status");let e=localStorage.getItem("authToken")||localStorage.getItem("accessToken");if(e)try{if(e.includes(".")){let a=JSON.parse(atob(e.split(".")[1]));if(a.exp&&Date.now()>=a.exp*1e3)return this.log("debug","localStorage token expired, removing..."),localStorage.removeItem("authToken"),localStorage.removeItem("accessToken"),!1}return this.log("debug","\u2705 Valid token found in localStorage"),!0}catch(a){this.log("warn","Invalid token in localStorage:",a)}if(sessionStorage.getItem("authToken")||sessionStorage.getItem("accessToken"))return this.log("debug","\u2705 Token found in sessionStorage"),!0;let r=this.getAuthCookie();if(r)try{if(r.includes(".")){let a=JSON.parse(atob(r.split(".")[1]));if(a.exp&&Date.now()>=a.exp*1e3)return this.log("debug","Cookie token expired, removing..."),this.removeAuthCookie(),!1}return this.log("debug","\u2705 Valid token found in cookies"),!0}catch(a){this.log("warn","Cookie token validation failed:",a)}return window.user||window.isAuthenticated?(this.log("debug","\u2705 Global authentication variable found"),!0):(this.log("debug","\u274C No valid authentication found"),!1)}isPublicRoute(e){return this.config.publicRoutes.includes(e)}isProtectedRoute(e){if(this.config.protectedRoutes.includes(e))return!0;for(let t of this.config.protectedPrefixes)if(e.startsWith(t))return!0;return!1}getAuthCookie(){let e=this.getCookieValue(this.config.authCookieName);if(e)return e;for(let t of this.config.authFallbackCookieNames){let r=this.getCookieValue(t);if(r)return this.log("debug",`Found auth token in fallback cookie: ${t}`),r}return null}getCookieValue(e){let r=`; ${document.cookie}`.split(`; ${e}=`);return r.length===2?decodeURIComponent(r.pop().split(";").shift()):null}removeAuthCookie(){[this.config.authCookieName,...this.config.authFallbackCookieNames].forEach(t=>{document.cookie=`${t}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`,document.cookie=`${t}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${window.location.pathname};`}),this.log("debug","Auth cookies removed")}getAccessToken(){let e=localStorage.getItem("authToken")||localStorage.getItem("accessToken");return e||(e=sessionStorage.getItem("authToken")||sessionStorage.getItem("accessToken"),e)||(e=this.getAuthCookie(),e)?e:null}setAccessToken(e,t={}){if(!e)return this.log("warn","Empty token provided"),!1;let{storage:r=this.config.authStorage,cookieOptions:a=this.config.authCookieOptions,skipValidation:o=this.config.authSkipValidation}=t;try{if(!o&&e.includes("."))try{let s=JSON.parse(atob(e.split(".")[1]));if(s.exp&&Date.now()>=s.exp*1e3)return this.log("warn","\u274C Token is expired"),!1;this.log("debug","\u2705 JWT token validated")}catch(s){this.log("warn","\u26A0\uFE0F JWT validation failed, but proceeding:",s.message)}switch(r){case"localStorage":localStorage.setItem("authToken",e),this.log("debug","Token saved to localStorage");break;case"sessionStorage":sessionStorage.setItem("authToken",e),this.log("debug","Token saved to sessionStorage");break;case"cookie":this.setAuthCookie(e,a);break;default:localStorage.setItem("authToken",e),this.log("debug","Token saved to localStorage (default)")}return this.emitAuthEvent("token_set",{storage:r,tokenLength:e.length,hasExpiration:e.includes(".")}),!0}catch(s){return this.log("Failed to set token:",s),!1}}setAuthCookie(e,t={}){let{cookieName:r=this.config.authCookieName,secure:a=window.location.protocol==="https:",sameSite:o="Strict",path:s="/",domain:n=null}=t,c=`${r}=${encodeURIComponent(e)}; path=${s}`;a&&(c+="; Secure"),o&&(c+=`; SameSite=${o}`),n&&(c+=`; Domain=${n}`);try{if(e.includes("."))try{let h=JSON.parse(atob(e.split(".")[1]));if(h.exp){let b=new Date(h.exp*1e3);c+=`; Expires=${b.toUTCString()}`}}catch{this.log("Could not extract expiration from JWT token")}}catch(h){this.log("Token processing error:",h)}document.cookie=c,this.log(`Auth cookie set: ${r}`)}removeAccessToken(e="all"){switch(e){case"localStorage":localStorage.removeItem("authToken"),localStorage.removeItem("accessToken");break;case"sessionStorage":sessionStorage.removeItem("authToken"),sessionStorage.removeItem("accessToken");break;case"cookie":this.removeAuthCookie();break;case"all":default:localStorage.removeItem("authToken"),localStorage.removeItem("accessToken"),sessionStorage.removeItem("authToken"),sessionStorage.removeItem("accessToken"),this.removeAuthCookie();break}this.emitAuthEvent("token_removed",{storage:e}),this.log(`Token removed from: ${e}`)}handleLoginSuccess(e=null){let t=e||this.config.redirectAfterLogin;return this.log(`\u{1F389} Login success, redirecting to: ${t}`),this.emitAuthEvent("login_success",{targetRoute:t}),this.router&&typeof this.router.navigateTo=="function"&&this.router.navigateTo(t),t}handleLogout(){return this.log("\u{1F44B} Logging out user"),this.removeAccessToken(),window.user&&(window.user=null),window.isAuthenticated&&(window.isAuthenticated=!1),this.emitAuthEvent("logout",{}),this.router&&typeof this.router.navigateTo=="function"&&this.router.navigateTo(this.config.loginRoute),this.config.loginRoute}emitAuthEvent(e,t){let r=new CustomEvent("router:auth",{detail:{type:e,timestamp:Date.now(),...t}});document.dispatchEvent(r),this.eventListeners.has(e)&&this.eventListeners.get(e).forEach(a=>{try{a(t)}catch(o){this.log("Event listener error:",o)}}),this.log(`\u{1F514} Auth event emitted: ${e}`,t)}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,[]),this.eventListeners.get(e).push(t)}off(e,t){if(this.eventListeners.has(e)){let r=this.eventListeners.get(e),a=r.indexOf(t);a>-1&&r.splice(a,1)}}getAuthStats(){return{enabled:this.config.enabled,isAuthenticated:this.isUserAuthenticated(),hasToken:!!this.getAccessToken(),protectedRoutesCount:this.config.protectedRoutes.length,protectedPrefixesCount:this.config.protectedPrefixes.length,publicRoutesCount:this.config.publicRoutes.length,storage:this.config.authStorage,loginRoute:this.config.loginRoute}}destroy(){this.eventListeners.clear(),this.log("debug","AuthManager destroyed")}};var v=class{constructor(e,t={}){this.config={cacheMode:t.cacheMode||"memory",cacheTTL:t.cacheTTL||3e5,maxCacheSize:t.maxCacheSize||50,debug:t.debug||!1},this.router=e,this.cache=new Map,this.cacheTimestamps=new Map,this.lruOrder=[],this.log("info","CacheManager initialized with config:",this.config)}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"CacheManager",...t)}setCache(e,t){let r=Date.now();if(this.config.cacheMode==="lru"){if(this.cache.size>=this.config.maxCacheSize&&!this.cache.has(e)){let o=this.lruOrder.shift();o&&(this.cache.delete(o),this.cacheTimestamps.delete(o),this.log("debug",`\u{1F5D1}\uFE0F LRU evicted cache key: ${o}`))}let a=this.lruOrder.indexOf(e);a>-1&&this.lruOrder.splice(a,1),this.lruOrder.push(e)}this.cache.set(e,t),this.cacheTimestamps.set(e,r),this.log("debug",`\u{1F4BE} Cached: ${e} (size: ${this.cache.size})`)}getFromCache(e){let t=Date.now(),r=this.cacheTimestamps.get(e);if(r&&t-r>this.config.cacheTTL){if(this.cache.delete(e),this.cacheTimestamps.delete(e),this.config.cacheMode==="lru"){let o=this.lruOrder.indexOf(e);o>-1&&this.lruOrder.splice(o,1)}return this.log("debug",`\u23F0 Cache expired and removed: ${e}`),null}let a=this.cache.get(e);if(a&&this.config.cacheMode==="lru"){let o=this.lruOrder.indexOf(e);o>-1&&(this.lruOrder.splice(o,1),this.lruOrder.push(e))}return a?this.log("debug",`\u{1F3AF} Cache hit: ${e}`):this.log("debug",`\u274C Cache miss: ${e}`),a}hasCache(e){return this.cache.has(e)&&this.getFromCache(e)!==null}invalidateByPattern(e){let t=[];for(let r of this.cache.keys())(r.includes(e)||r.startsWith(e))&&t.push(r);return t.forEach(r=>{if(this.cache.delete(r),this.cacheTimestamps.delete(r),this.config.cacheMode==="lru"){let a=this.lruOrder.indexOf(r);a>-1&&this.lruOrder.splice(a,1)}}),this.log("debug",`\u{1F9F9} Invalidated ${t.length} cache entries matching: ${e}`),t.length}invalidateComponentCache(e){let t=[`component_${e}`,`script_${e}`,`template_${e}`,`style_${e}`,`layout_${e}`],r=0;return t.forEach(a=>{r+=this.invalidateByPattern(a)}),this.log(`\u{1F504} Invalidated component cache for route: ${e} (${r} entries)`),r}clearComponentCache(){let e=["component_","script_","template_","style_","layout_"],t=0;return e.forEach(r=>{t+=this.invalidateByPattern(r)}),this.log(`\u{1F9FD} Cleared all component caches (${t} entries)`),t}clearCache(){let e=this.cache.size;return this.cache.clear(),this.cacheTimestamps.clear(),this.lruOrder=[],this.log(`\u{1F525} Cleared all cache (${e} entries)`),e}cleanExpiredCache(){let e=Date.now(),t=[];for(let[r,a]of this.cacheTimestamps.entries())e-a>this.config.cacheTTL&&t.push(r);return t.forEach(r=>{if(this.cache.delete(r),this.cacheTimestamps.delete(r),this.config.cacheMode==="lru"){let a=this.lruOrder.indexOf(r);a>-1&&this.lruOrder.splice(a,1)}}),t.length>0&&this.log(`\u23F1\uFE0F Cleaned ${t.length} expired cache entries`),t.length}getCacheStats(){return{size:this.cache.size,maxSize:this.config.maxCacheSize,mode:this.config.cacheMode,ttl:this.config.cacheTTL,memoryUsage:this.getMemoryUsage(),hitRatio:this.getHitRatio(),categories:this.getCategorizedStats()}}getMemoryUsage(){let e=0;for(let[t,r]of this.cache.entries())e+=t.length*2,typeof r=="string"?e+=r.length*2:typeof r=="object"&&r!==null?e+=JSON.stringify(r).length*2:e+=8;return{bytes:e,kb:Math.round(e/1024*100)/100,mb:Math.round(e/(1024*1024)*100)/100}}getHitRatio(){let e=this.cache.size>0?Math.min(this.cache.size/this.config.maxCacheSize,1):0;return Math.round(e*100)}getCategorizedStats(){let e={components:0,scripts:0,templates:0,styles:0,layouts:0,others:0};for(let t of this.cache.keys())t.startsWith("component_")?e.components++:t.startsWith("script_")?e.scripts++:t.startsWith("template_")?e.templates++:t.startsWith("style_")?e.styles++:t.startsWith("layout_")?e.layouts++:e.others++;return e}getCacheKeys(){return Array.from(this.cache.keys())}getCacheKeysByPattern(e){return this.getCacheKeys().filter(t=>t.includes(e)||t.startsWith(e))}startAutoCleanup(e=6e4){this.cleanupInterval&&clearInterval(this.cleanupInterval),this.cleanupInterval=setInterval(()=>{this.cleanExpiredCache()},e),this.log(`\u{1F916} Auto cleanup started (interval: ${e}ms)`)}stopAutoCleanup(){this.cleanupInterval&&(clearInterval(this.cleanupInterval),this.cleanupInterval=null,this.log("debug","\u{1F6D1} Auto cleanup stopped"))}destroy(){this.stopAutoCleanup(),this.clearCache(),this.log("debug","CacheManager destroyed")}};var L=class{constructor(e,t={}){this.config={enableParameterValidation:t.enableParameterValidation!==!1,logSecurityWarnings:t.logSecurityWarnings!==!1,maxParameterLength:t.maxParameterLength||1e3,maxArraySize:t.maxArraySize||100,maxParameterCount:t.maxParameterCount||50,allowedKeyPattern:t.allowedKeyPattern||/^[a-zA-Z0-9_\-]+$/,debug:t.debug||!1},this.router=e,this.currentQueryParams={},this.currentRouteParams={},this.log("info","QueryManager initialized with config:",this.config)}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"QueryManager",...t)}sanitizeParameter(e){if(typeof e!="string")return e;let t=e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"").replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,"").replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi,"").replace(/<embed\b[^<]*(?:(?!<\/embed>)<[^<]*)*<\/embed>/gi,"").replace(/<link\b[^<]*>/gi,"").replace(/<meta\b[^<]*>/gi,"").replace(/javascript:/gi,"").replace(/vbscript:/gi,"").replace(/data:/gi,"").replace(/on\w+\s*=/gi,"").replace(/expression\s*\(/gi,"").replace(/url\s*\(/gi,""),r=[/(\b(union|select|insert|update|delete|drop|create|alter|exec|execute|sp_|xp_)\b)/gi,/(;|\||&|\*|%|<|>)/g,/(--|\/\*|\*\/)/g,/(\bor\b.*\b=\b|\band\b.*\b=\b)/gi,/('.*'|".*")/g,/(\\\w+)/g];for(let a of r)t=t.replace(a,"");return t=t.replace(/[<>'"&]{2,}/g,""),t.length>this.config.maxParameterLength&&(t=t.substring(0,this.config.maxParameterLength)),t.trim()}validateParameter(e,t){if(!this.config.enableParameterValidation)return!0;if(typeof e!="string"||e.length===0)return!1;if(!this.config.allowedKeyPattern.test(e))return this.config.logSecurityWarnings&&console.warn(`Invalid parameter key format: ${e}`),!1;if(e.length>50)return this.config.logSecurityWarnings&&console.warn(`Parameter key too long: ${e}`),!1;if(t!=null){if(typeof t=="string"){if(t.length>this.config.maxParameterLength)return this.config.logSecurityWarnings&&console.warn(`Parameter value too long for key: ${e}`),!1;let r=[/<script|<iframe|<object|<embed/gi,/javascript:|vbscript:|data:/gi,/union.*select|insert.*into|delete.*from/gi,/\.\.\//g,/[<>'"&]{3,}/g];for(let a of r)if(a.test(t))return this.config.logSecurityWarnings&&console.warn(`Dangerous pattern detected in parameter ${e}:`,t),!1}else if(Array.isArray(t)){if(t.length>this.config.maxArraySize)return this.config.logSecurityWarnings&&console.warn(`Parameter array too large for key: ${e}`),!1;for(let r of t)if(!this.validateParameter(`${e}[]`,r))return!1}}return!0}parseQueryString(e){let t={};if(!e)return t;let r=e.split("&");for(let o of r)try{let[s,n]=o.split("=");if(!s)continue;let c,h;try{c=decodeURIComponent(s),h=n?decodeURIComponent(n):""}catch{this.log("warn","Failed to decode URI component:",o);continue}if(!this.validateParameter(c,h)){this.log("warn",`Parameter rejected by security filter: ${c}`);continue}let b=this.sanitizeParameter(h);if(c.endsWith("[]")){let w=c.slice(0,-2);if(!this.validateParameter(w,[]))continue;t[w]||(t[w]=[]),t[w].length<this.config.maxArraySize?t[w].push(b):this.config.logSecurityWarnings&&console.warn(`Array parameter ${w} size limit exceeded`)}else t[c]=b}catch(s){this.log("error","Error parsing query parameter:",o,s)}let a=Object.keys(t).length;if(a>this.config.maxParameterCount){this.config.logSecurityWarnings&&console.warn(`Too many parameters (${a}). Limiting to first ${this.config.maxParameterCount}.`);let o={},s=0;for(let[n,c]of Object.entries(t)){if(s>=this.config.maxParameterCount)break;o[n]=c,s++}return o}return t}buildQueryString(e){if(!e||Object.keys(e).length===0)return"";let t=[];for(let[r,a]of Object.entries(e))if(Array.isArray(a))for(let o of a)t.push(`${encodeURIComponent(r)}[]=${encodeURIComponent(o)}`);else a!=null&&t.push(`${encodeURIComponent(r)}=${encodeURIComponent(a)}`);return t.join("&")}hasQueryParamsChanged(e){if(!this.currentQueryParams&&!e)return!1;if(!this.currentQueryParams||!e)return!0;let t=Object.keys(this.currentQueryParams),r=Object.keys(e);if(t.length!==r.length)return!0;for(let a of t)if(JSON.stringify(this.currentQueryParams[a])!==JSON.stringify(e[a]))return!0;return!1}getQueryParams(){return{...this.currentQueryParams}}getQueryParam(e,t=void 0){let r=this.currentQueryParams?this.currentQueryParams[e]:void 0;return r!==void 0?r:t}setQueryParams(e,t=!1){if(!e||typeof e!="object"){console.warn("Invalid parameters object provided to setQueryParams");return}let r=t?{}:{...this.currentQueryParams},a={};for(let[s,n]of Object.entries(e)){if(!this.validateParameter(s,n)){console.warn(`Parameter ${s} rejected by security filter`);continue}n!=null&&(Array.isArray(n)?a[s]=n.map(c=>this.sanitizeParameter(c)):a[s]=this.sanitizeParameter(n))}Object.assign(r,a);for(let[s,n]of Object.entries(r))(n==null||n==="")&&delete r[s];let o=Object.keys(r).length;o>this.config.maxParameterCount&&this.config.logSecurityWarnings&&console.warn(`Too many parameters after update (${o}). Some parameters may be dropped.`),this.currentQueryParams=r,this.updateURL()}removeQueryParams(e){if(!e)return;let t=Array.isArray(e)?e:[e];for(let r of t)delete this.currentQueryParams[r];this.updateURL()}clearQueryParams(){this.currentQueryParams={},this.updateURL()}setCurrentQueryParams(e){this.currentQueryParams=e||{}}setCurrentRouteParams(e){this.currentRouteParams=e||{},this.log("debug","Route params set:",this.currentRouteParams)}getAllParams(){return{...this.currentRouteParams,...this.currentQueryParams}}getParam(e,t=void 0){let r=this.currentQueryParams[e]!==void 0?this.currentQueryParams[e]:this.currentRouteParams[e];return r!==void 0?r:t}getRouteParams(){return{...this.currentRouteParams}}getRouteParam(e,t=void 0){let r=this.currentRouteParams[e];return r!==void 0?r:t}updateURL(){if(this.router&&typeof this.router.updateURL=="function"){let e=this.router.currentHash||"home";this.router.updateURL(e,this.currentQueryParams)}}getStats(){return{currentParams:Object.keys(this.currentQueryParams).length,maxAllowed:this.config.maxParameterCount,validationEnabled:this.config.enableParameterValidation,currentQueryString:this.buildQueryString(this.currentQueryParams)}}destroy(){this.currentQueryParams={},this.currentRouteParams={},this.router=null,this.log("debug","QueryManager destroyed")}};var k=class{constructor(e,t={}){this.config={srcPath:t.srcPath||e.config.srcPath||"/src",routesPath:t.routesPath||e.config.routesPath||"/routes",environment:t.environment||"development",useLayout:t.useLayout!==!1,defaultLayout:t.defaultLayout||"default",useComponents:t.useComponents!==!1,debug:t.debug||!1},this.router=e,this.log("debug","RouteLoader initialized with config:",this.config)}async loadScript(e){let t;try{if(this.config.environment==="production"){let r=`${this.config.routesPath}/${e}.js`;this.log("debug",`Loading production route: ${r}`),t=(await import(r)).default}else{let r=`${this.config.srcPath}/logic/${e}.js`;this.log("debug",`Loading development route: ${r}`),t=(await import(r)).default}if(!t)throw new Error(`Route '${e}' not found - no default export`)}catch(r){throw r.message.includes("Failed to resolve")||r.message.includes("Failed to fetch")||r.message.includes("not found")||r.name==="TypeError"?new Error(`Route '${e}' not found - 404`):r}return t}async loadTemplate(e){try{let t=`${this.config.srcPath}/views/${e}.html`,r=await fetch(t);if(!r.ok)throw new Error(`Template not found: ${r.status}`);let a=await r.text();return this.log("debug",`Template '${e}' loaded successfully`),a}catch(t){return this.log("warn",`Template '${e}' not found, using default:`,t.message),this.generateDefaultTemplate(e)}}async loadStyle(e){try{let t=`${this.config.srcPath}/styles/${e}.css`,r=await fetch(t);if(!r.ok)throw new Error(`Style not found: ${r.status}`);let a=await r.text();return this.log("debug",`Style '${e}' loaded successfully`),a}catch(t){return this.log("debug",`Style '${e}' not found, no styles applied:`,t.message),""}}async loadLayout(e){try{let t=`${this.config.srcPath}/layouts/${e}.html`,r=await fetch(t);if(!r.ok)throw new Error(`Layout not found: ${r.status}`);let a=await r.text();return this.log("debug",`Layout '${e}' loaded successfully`),a}catch(t){return this.log("debug",`Layout '${e}' not found, no layout applied:`,t.message),null}}mergeLayoutWithTemplate(e,t,r){let a;return t.includes("{{ content }}")?a=t.replace(/{{ content }}/s,r):t.includes('class="main-content"')?(this.log("debug","Using main-content replacement"),a=t.replace(/(<div class="container">).*?(<\/div>\s*<\/main>)/s,`$1${r}$2`)):(this.log("debug","Wrapping template with layout"),a=`${t}
${r}`),a}async createVueComponent(e){let t=`component_${e}`,r=this.router.cacheManager?.getFromCache(t);if(r)return r;let a=await this.loadScript(e),o=this.router,s=this.config.environment==="production",n,c="",h=null;s?n=a.template||this.generateDefaultTemplate(e):(n=await this.loadTemplate(e),c=await this.loadStyle(e),h=this.config.useLayout&&a.layout!==null?await this.loadLayout(a.layout||this.config.defaultLayout):null,h&&(n=this.mergeLayoutWithTemplate(e,h,n)));let b={};if(this.config.useComponents&&o.componentLoader)try{b=await o.componentLoader.loadAllComponents(),this.log("debug",`Components loaded successfully for route: ${e}`)}catch(l){this.log("warn",`Component loading failed for route '${e}', continuing without components:`,l.message),b={}}let w={...a,name:a.name||this.toPascalCase(e),template:n,components:b,data(){return{...a.data?a.data():{},currentRoute:e,$query:o.queryManager?.getQueryParams()||{},$lang:(()=>{try{return o.i18nManager?.getCurrentLanguage()||o.config.i18nDefaultLanguage||o.config.defaultLanguage||"ko"}catch(g){return o.errorHandler&&o.errorHandler.warn("RouteLoader","Failed to get current language:",g),o.config.defaultLanguage||"ko"}})(),$dataLoading:!1}},computed:{...a.computed||{},params(){return o.queryManager?.getAllParams()||{}}},async mounted(){a.mounted&&await a.mounted.call(this),a.dataURL&&(typeof a.dataURL=="string"?await this.$fetchData():typeof a.dataURL=="object"&&await this.$fetchMultipleData()),await this.$nextTick(),this.$bindAutoForms()},methods:{...a.methods,navigateTo:(l,i)=>o.navigateTo(l,i),getCurrentRoute:()=>o.getCurrentRoute(),getParams:()=>o.queryManager?.getAllParams()||{},getParam:(l,i)=>o.queryManager?.getParam(l,i),$t:(l,i)=>{try{return o.i18nManager?.t(l,i)||l}catch(g){return o.errorHandler&&o.errorHandler.warn("RouteLoader","i18n translation failed, returning key:",g),l}},$isAuthenticated:()=>o.authManager?.isUserAuthenticated()||!1,$logout:()=>o.authManager?o.navigateTo(o.authManager.handleLogout()):null,$loginSuccess:l=>o.authManager?o.navigateTo(o.authManager.handleLoginSuccess(l)):null,$checkAuth:l=>o.authManager?o.authManager.checkAuthentication(l):Promise.resolve({allowed:!0,reason:"auth_disabled"}),$getToken:()=>o.authManager?.getAccessToken()||null,$setToken:(l,i)=>o.authManager?.setAccessToken(l,i)||!1,$removeToken:l=>o.authManager?.removeAccessToken(l)||null,$getAuthCookie:()=>o.authManager?.getAuthCookie()||null,$getCookie:l=>o.authManager?.getCookieValue(l)||null,async $fetchData(l){if(a.dataURL){this.$dataLoading=!0;try{if(typeof a.dataURL=="string"){let i=await o.routeLoader.fetchComponentData(a.dataURL);o.errorHandler&&o.errorHandler.debug("RouteLoader",`Data fetched for ${e}:`,i),Object.assign(this,i),this.$emit("data-loaded",i)}else if(typeof a.dataURL=="object"&&l){let i=a.dataURL[l];if(i){let g=await o.routeLoader.fetchComponentData(i);o.errorHandler&&o.errorHandler.debug("RouteLoader",`Data fetched for ${e}.${l}:`,g),this[l]=g,this.$emit("data-loaded",{[l]:g})}}else await this.$fetchMultipleData()}catch(i){o.errorHandler&&o.errorHandler.warn("RouteLoader",`Failed to fetch data for ${e}:`,i),this.$emit("data-error",i)}finally{this.$dataLoading=!1}}},async $fetchMultipleData(){if(!a.dataURL||typeof a.dataURL!="object")return;let l=a.dataURL;this.$dataLoading=!0;try{let i=Object.entries(l).map(async([u,p])=>{try{let m=await o.routeLoader.fetchComponentData(p);return{key:u,data:m,success:!0}}catch(m){return o.errorHandler&&o.errorHandler.warn("RouteLoader",`Failed to fetch ${u} for ${e}:`,m),{key:u,error:m,success:!1}}}),g=await Promise.all(i),d={},f={};g.forEach(({key:u,data:p,error:m,success:y})=>{y?(this[u]=p,d[u]=p):f[u]=m}),o.errorHandler&&o.errorHandler.debug("RouteLoader",`Multiple data fetched for ${e}:`,d),Object.keys(d).length>0&&this.$emit("data-loaded",d),Object.keys(f).length>0&&this.$emit("data-error",f)}catch(i){o.errorHandler&&o.errorHandler.warn("RouteLoader",`Failed to fetch multiple data for ${e}:`,i),this.$emit("data-error",i)}finally{this.$dataLoading=!1}},async $fetchAllData(){typeof a.dataURL=="string"?await this.$fetchData():typeof a.dataURL=="object"&&await this.$fetchMultipleData()},$bindAutoForms(){document.querySelectorAll("form.auto-form, form[action]").forEach(i=>{i.removeEventListener("submit",i._boundSubmitHandler);let g=d=>this.$handleFormSubmit(d);i._boundSubmitHandler=g,i.addEventListener("submit",g),o.errorHandler&&o.errorHandler.debug("RouteLoader",`Form auto-bound: ${i.getAttribute("action")}`)})},async $handleFormSubmit(l){l.preventDefault();let i=l.target,g=i.getAttribute("action"),d=i.getAttribute("method")||"POST",f=i.getAttribute("data-success-handler"),u=i.getAttribute("data-error-handler"),p=i.getAttribute("data-loading-handler"),m=i.getAttribute("data-redirect");try{if(p&&this[p]&&this[p](!0,i),g=this.$processActionParams(g),!this.$validateForm(i))return;let y=new FormData(i),S=Object.fromEntries(y.entries());o.errorHandler&&o.errorHandler.debug("RouteLoader",`Form submitting to: ${g}`,S);let E=await this.$submitFormData(g,d,S,i);f&&this[f]&&this[f](E,i),m&&setTimeout(()=>{this.navigateTo(m)},1e3)}catch(y){o.errorHandler&&o.errorHandler.warn("RouteLoader","Form submission error:",y),u&&this[u]?this[u](y,i):console.error("Form submission error:",y)}finally{p&&this[p]&&this[p](!1,i)}},$processActionParams(l){let i=l,g=l.match(/\{([^}]+)\}/g);return g&&g.forEach(d=>{let f=d.slice(1,-1);try{let u=null;u=this.getParam(f),u==null&&(u=this[f]),u==null&&this.$options.computed&&this.$options.computed[f]&&(u=this[f]),u!=null?(i=i.replace(d,encodeURIComponent(u)),o.errorHandler&&o.errorHandler.debug("RouteLoader",`Parameter resolved: ${f} = ${u}`)):o.errorHandler&&o.errorHandler.warn("RouteLoader",`Parameter '${f}' not found in component data, computed, or route params`)}catch(u){o.errorHandler&&o.errorHandler.warn("RouteLoader",`Error processing parameter '${f}':`,u)}}),i},async $submitFormData(l,i,g,d){let f=Array.from(d.elements).some(y=>y.type==="file"&&y.files.length>0),u={Accept:"application/json",...this.$getToken()&&{Authorization:`Bearer ${this.$getToken()}`}},p;f?p=new FormData(d):(u["Content-Type"]="application/json",p=JSON.stringify(g));let m=await fetch(l,{method:i.toUpperCase(),headers:u,body:p});if(!m.ok){let y;try{y=await m.json()}catch{y={message:`HTTP ${m.status}: ${m.statusText}`}}throw new Error(y.message||`HTTP ${m.status}`)}try{return await m.json()}catch{return{success:!0}}},$validateForm(l){let i=!0;return l.querySelectorAll("input, textarea, select").forEach(d=>{if(!d.checkValidity()){i=!1,d.classList.add("error");return}let f=d.getAttribute("data-validation");f?this.$validateInput(d,f)?d.classList.remove("error"):(i=!1,d.classList.add("error")):d.classList.remove("error")}),i},$validateInput(l,i){let g=l.value;if(typeof this[i]=="function")try{return this[i](g,l)}catch(d){return o.errorHandler&&o.errorHandler.warn("RouteLoader",`Validation function '${i}' error:`,d),!1}return o.errorHandler&&o.errorHandler.warn("RouteLoader",`Validation function '${i}' not found`),!0}},_routeName:e};return!s&&c&&(w._style=c),this.router.cacheManager?.setCache(t,w),w}toPascalCase(e){return e.split(/[-_\s]+/).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}generateDefaultTemplate(e){return`<div class="route-${e}"><h1>Route: ${e}</h1></div>`}async fetchComponentData(e){try{let t=this.router.queryManager?.buildQueryString(this.router.queryManager?.getQueryParams())||"",r=t?`${e}?${t}`:e;this.log("debug",`Fetching data from: ${r}`);let a=await fetch(r,{method:"GET",headers:{"Content-Type":"application/json",Accept:"application/json"}});if(!a.ok)throw new Error(`HTTP error! status: ${a.status}`);let o=await a.json();if(typeof o!="object"||o===null)throw new Error("Invalid data format: expected object");return o}catch(t){throw this.log("error","Failed to fetch component data:",t),t}}invalidateCache(e){this.router.cacheManager&&this.router.cacheManager.invalidateComponentCache(e),this.log("debug",`Cache invalidated for route: ${e}`)}getStats(){return{environment:this.config.environment,srcPath:this.config.srcPath,routesPath:this.config.routesPath,useLayout:this.config.useLayout,useComponents:this.config.useComponents}}generatePageTitle(e){return this.toPascalCase(e).replace(/([A-Z])/g," $1").trim()}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"RouteLoader",...t)}destroy(){this.log("debug","RouteLoader destroyed"),this.router=null}};var R=class{constructor(e,t={}){this.config={enableErrorReporting:t.enableErrorReporting!==!1,debug:t.debug||!1,logLevel:t.logLevel||(t.debug?"debug":"info"),environment:t.environment||"development"},this.router=e,this.logLevels={error:0,warn:1,info:2,debug:3},this.log("info","ErrorHandler","ErrorHandler initialized with config:",this.config)}async handleRouteError(e,t){let r=500,a="\uD398\uC774\uC9C0\uB97C \uB85C\uB4DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.";this.debug("ErrorHandler","\uC5D0\uB7EC \uC0C1\uC138:",t.message,t.name),t.message.includes("not found")||t.message.includes("404")||t.message.includes("Failed to resolve")||t.message.includes("Failed to fetch")||t.name==="TypeError"&&t.message.includes("resolve")?(r=404,a=`'${e}' \uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`):t.message.includes("network")&&!t.message.includes("not found")?(r=503,a="\uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD574 \uC8FC\uC138\uC694."):(t.message.includes("permission")||t.message.includes("403"))&&(r=403,a="\uD398\uC774\uC9C0\uC5D0 \uC811\uADFC\uD560 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."),this.debug("ErrorHandler",`\uC5D0\uB7EC \uCF54\uB4DC \uACB0\uC815: ${r} (\uB77C\uC6B0\uD2B8: ${e})`),this.config.enableErrorReporting&&this.reportError(e,t,r);try{r===404?await this.load404Page():await this.loadErrorPage(r,a)}catch(o){this.error("ErrorHandler","\uC5D0\uB7EC \uD398\uC774\uC9C0 \uB85C\uB529 \uC2E4\uD328:",o),this.showFallbackErrorPage(r,a)}}async load404Page(){try{this.info("ErrorHandler","Loading 404 page...");let e=await this.createVueComponent("404");await this.renderComponentWithTransition(e,"404"),this.info("ErrorHandler","404 page loaded successfully")}catch(e){this.error("ErrorHandler","404 page loading failed:",e),this.showFallbackErrorPage("404","\uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.")}}async loadErrorPage(e,t){try{this.info("ErrorHandler",`Loading error page for ${e}...`);let r=await this.createErrorComponent(e,t);await this.renderComponentWithTransition(r,"error"),this.info("ErrorHandler",`Error page ${e} loaded successfully`)}catch(r){this.error("ErrorHandler",`Error page ${e} loading failed:`,r),this.showFallbackErrorPage(e,t)}}async createErrorComponent(e,t){try{let r=await this.createVueComponent("error");return{...r,data(){return{...r.data?r.data():{},errorCode:e,errorMessage:t,showRetry:!0,showGoHome:!0}}}}catch(r){throw this.error("ErrorHandler","Error component load failed:",r),new Error(`Cannot load error page: ${r.message}`)}}showFallbackErrorPage(e,t){let r=document.getElementById("app");if(!r)return;let a=`
<div class="fallback-error-page" style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 2rem;
text-align: center;
background: #f8f9fa;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
">
<div style="
background: white;
padding: 3rem;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
max-width: 500px;
">
<h1 style="
font-size: 4rem;
margin: 0;
color: #dc3545;
font-weight: 300;
">${e}</h1>
<h2 style="
margin: 1rem 0;
color: #495057;
font-weight: 400;
">${t}</h2>
<p style="
color: #6c757d;
margin-bottom: 2rem;
line-height: 1.5;
">\uC694\uCCAD\uD558\uC2E0 \uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.</p>
<button onclick="window.location.hash = '#/'" style="
background: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
transition: background 0.2s;
" onmouseover="this.style.background='#0056b3'" onmouseout="this.style.background='#007bff'">
\uD648\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30
</button>
</div>
</div>
`;r.innerHTML=a,this.info("ErrorHandler",`Fallback error page displayed for ${e}`)}reportError(e,t,r){let a={route:e,errorCode:r,errorMessage:t.message,stack:t.stack,url:window.location.href,userAgent:navigator.userAgent,timestamp:new Date().toISOString(),routerConfig:{environment:this.router.config.environment,mode:this.router.config.mode}};this.error("ErrorHandler","\uB77C\uC6B0\uD130 \uC5D0\uB7EC \uB9AC\uD3EC\uD2B8:",a)}async createVueComponent(e){if(this.router.routeLoader)return await this.router.routeLoader.createVueComponent(e);throw new Error("RouteLoader not available")}async renderComponentWithTransition(e,t){if(this.router.renderComponentWithTransition)return await this.router.renderComponentWithTransition(e,t);throw new Error("Render function not available")}log(e,t,...r){(typeof e!="string"||!this.logLevels.hasOwnProperty(e))&&(r=[t,...r],t=e,e=this.config.debug?"debug":"info");let a=this.logLevels[this.config.logLevel]||this.logLevels.info,o=this.logLevels[e]||this.logLevels.info;if(o>a||this.config.environment==="production"&&o>this.logLevels.warn)return;let s=t?`[${t}]`:"[ViewLogic]",n=new Date().toISOString().substring(11,23);switch(e){case"error":console.error(`${n} ${s}`,...r);break;case"warn":console.warn(`${n} ${s}`,...r);break;case"info":console.info(`${n} ${s}`,...r);break;case"debug":console.log(`${n} ${s}`,...r);break;default:console.log(`${n} ${s}`,...r)}}error(e,...t){this.log("error",e,...t)}warn(e,...t){this.log("warn",e,...t)}info(e,...t){this.log("info",e,...t)}debug(e,...t){this.log("debug",e,...t)}destroy(){this.router=null,this.info("ErrorHandler","ErrorHandler destroyed")}};var T=class{constructor(e=null,t={}){this.config={componentsPath:t.componentsPath||"/components",debug:t.debug||!1,environment:t.environment||"development",...t},this.router=e,this.loadingPromises=new Map,this.unifiedComponents=null}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"ComponentLoader",...t)}async loadComponent(e){if(!e||typeof e!="string")throw new Error("Component name must be a non-empty string");if(this.loadingPromises.has(e))return this.loadingPromises.get(e);let t=this._loadComponentFromFile(e);this.loadingPromises.set(e,t);try{return await t}catch(r){throw r}finally{this.loadingPromises.delete(e)}}async _loadComponentFromFile(e){let t=`${this.config.componentsPath}/${e}.js`,r;if(this.router&&this.router.config.srcPath){let a=this.router.config.srcPath;if(a.startsWith("http")){let o=a.endsWith("/")?a.slice(0,-1):a,s=t.startsWith("/")?t:`/${t}`;r=`${o}${s}`}else r=this.router.resolvePath(`${a}${t}`)}else r=this.router?this.router.resolvePath(`/src${t}`):`/src${t}`;try{let o=(await import(r)).default;if(!o)throw new Error(`Component '${e}' has no default export`);return o.name||(o.name=e),this.log("debug",`Component '${e}' loaded successfully`),o}catch(a){throw this.log("error",`Failed to load component '${e}':`,a),new Error(`Component '${e}' not found: ${a.message}`)}}clearComponents(){this.loadingPromises.clear(),this.unifiedComponents=null,this.log("debug","All components cleared")}async loadAllComponents(){return this.unifiedComponents?(this.log("debug","Using existing unified components"),this.unifiedComponents):this.config.environment==="production"?await this._loadProductionComponents():await this._loadDevelopmentComponents()}async _loadProductionComponents(){try{let e=`${this.router?.config?.routesPath||"/routes"}/_components.js`;this.log("info","[PRODUCTION] Loading unified components from:",e);let t=await import(e);if(typeof t.registerComponents=="function")return this.unifiedComponents=t.components||{},this.log("info",`[PRODUCTION] Unified components loaded: ${Object.keys(this.unifiedComponents).length} components`),this.unifiedComponents;throw new Error("registerComponents function not found in components module")}catch(e){return this.log("warn","[PRODUCTION] Failed to load unified components:",e.message),this.unifiedComponents={},{}}}async _loadDevelopmentComponents(){let e=this._getComponentNames(),t={};this.log("info",`[DEVELOPMENT] Loading individual components: ${e.join(", ")}`);for(let r of e)try{let a=await this.loadComponent(r);a&&(t[r]=a)}catch(a){this.log("warn",`[DEVELOPMENT] Failed to load component ${r}:`,a.message)}return this.unifiedComponents=t,this.log("info",`[DEVELOPMENT] Individual components loaded: ${Object.keys(t).length} components`),t}_getComponentNames(){return Array.isArray(this.config.componentNames)&&this.config.componentNames.length>0?[...this.config.componentNames]:["Button","Modal","Card","Toast","Input","Tabs","Checkbox","Alert","DynamicInclude","HtmlInclude"]}dispose(){this.clearComponents(),this.log("debug","ComponentLoader disposed"),this.router=null}};var A=class{constructor(e={}){this.version=e.version||"1.0.0",this.config=this._buildConfig(e),this.currentHash="",this.currentVueApp=null,this.previousVueApp=null,this.componentLoader=null,this.transitionInProgress=!1,this.isReady=!1,this.readyPromise=null,this._boundHandleRouteChange=this.handleRouteChange.bind(this),this.readyPromise=this.initialize()}_buildConfig(e){let t=window.location.origin,a={...{basePath:"/",srcPath:"/src",mode:"hash",cacheMode:"memory",cacheTTL:3e5,maxCacheSize:50,useLayout:!0,defaultLayout:"default",environment:"development",routesPath:"/routes",enableErrorReporting:!0,useComponents:!0,componentNames:["Button","Modal","Card","Toast","Input","Tabs","Checkbox","Alert","DynamicInclude","HtmlInclude"],useI18n:!1,defaultLanguage:"ko",i18nPath:"/i18n",logLevel:"info",authEnabled:!1,loginRoute:"login",protectedRoutes:[],protectedPrefixes:[],publicRoutes:["login","register","home"],checkAuthFunction:null,redirectAfterLogin:"home",authCookieName:"authToken",authFallbackCookieNames:["accessToken","token","jwt"],authStorage:"cookie",authCookieOptions:{},authSkipValidation:!1,enableParameterValidation:!0,maxParameterLength:1e3,maxParameterCount:50,maxArraySize:100,allowedKeyPattern:/^[a-zA-Z0-9_-]+$/,logSecurityWarnings:!0},...e};return a.srcPath=this.resolvePath(a.srcPath,a.basePath),a.routesPath=this.resolvePath(a.routesPath,a.basePath),a.i18nPath=this.resolvePath(a.i18nPath,a.basePath),a}resolvePath(e,t=null){let r=window.location.origin;if(e.startsWith("http"))return e;if(e.startsWith("/")){if(t&&t!=="/"){let c=t.endsWith("/")?t.slice(0,-1):t,h=e.startsWith("/")?e:`/${e}`,b=`${c}${h}`;return`${r}${b}`.replace(/([^:])\/{2,}/g,"$1/")}return`${r}${e}`}let a=window.location.pathname,o=a.endsWith("/")?a:a.substring(0,a.lastIndexOf("/")+1),s=this.normalizePath(o+e);return`${r}${s}`.replace(/([^:])\/{2,}/g,"$1/")}normalizePath(e){e=e.replace(/\/+/g,"/");let t=e.split("/").filter(o=>o!==""&&o!=="."),r=[];for(let o of t)o===".."?r.length>0&&r[r.length-1]!==".."?r.pop():e.startsWith("/")||r.push(o):r.push(o);let a="/"+r.join("/");return a==="/"?"/":a}log(e,...t){this.errorHandler&&this.errorHandler.log(e,"Router",...t)}async initialize(){try{if(this.cacheManager=new v(this,this.config),this.routeLoader=new k(this,this.config),this.queryManager=new L(this,this.config),this.errorHandler=new R(this,this.config),this.config.useI18n)try{this.i18nManager=new C(this,this.config),this.i18nManager.initPromise&&await this.i18nManager.initPromise,this.log("info","I18nManager initialized successfully")}catch(e){this.log("warn","I18nManager initialization failed, continuing without i18n:",e.message),this.i18nManager=null,this.config.useI18n=!1}if(this.config.authEnabled&&(this.authManager=new $(this,this.config)),this.config.useComponents)try{this.componentLoader=new T(this,{...this.config,basePath:`${this.config.basePath}/components`,cache:!0,componentNames:this.config.componentNames}),await this.componentLoader.loadAllComponents(),this.log("info","ComponentLoader initialized successfully")}catch(e){this.log("warn","ComponentLoader initialization failed, continuing without components:",e.message),this.componentLoader=null}this.isReady=!0,this.init()}catch(e){this.log("error","Router initialization failed:",e),this.isReady=!0,this.init()}}async waitForReady(){return this.isReady?!0:(this.readyPromise&&await this.readyPromise,this.isReady)}init(){let e=this.config.mode==="hash";window.addEventListener(e?"hashchange":"popstate",this._boundHandleRouteChange);let t=()=>{e&&!window.location.hash?window.location.hash="#/":!e&&window.location.pathname==="/"?this.navigateTo("home"):this.handleRouteChange()};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t):requestAnimationFrame(t)}handleRouteChange(){let{route:e,queryParams:t}=this._parseCurrentLocation();this.queryManager?.setCurrentQueryParams(t),(e!==this.currentHash||this.queryManager?.hasQueryParamsChanged(t))&&(this.currentHash=e,this.loadRoute(e))}_parseCurrentLocation(){if(this.c