viewlogic 1.2.6 → 1.2.8
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/README.md +35 -7
- package/dist/viewlogic-router.esm.js +4 -0
- package/dist/{viewlogic-router.js.map → viewlogic-router.esm.js.map} +3 -3
- package/dist/viewlogic-router.min.js +9 -52
- package/dist/viewlogic-router.min.js.map +4 -4
- package/package.json +7 -9
- package/dist/viewlogic-router.js +0 -2984
- package/dist/viewlogic-router.umd.js +0 -139
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ ViewLogic Router revolutionizes Vue development with three fundamental principle
|
|
|
38
38
|
- 🌐 **API Client** - HTTP client with automatic token injection
|
|
39
39
|
- 📝 **Form Handling** - Revolutionary form processing with parameter substitution
|
|
40
40
|
|
|
41
|
-
**Tiny Bundle Size** - Complete framework in just **
|
|
41
|
+
**Tiny Bundle Size** - Complete framework in just **52KB minified / 18KB gzipped**!
|
|
42
42
|
|
|
43
43
|
**Easy Integration** - Drop-in UMD build available for instant usage without build tools.
|
|
44
44
|
|
|
@@ -69,9 +69,8 @@ project/
|
|
|
69
69
|
├── css/ # Global CSS files
|
|
70
70
|
│ └── base.css # Base styles
|
|
71
71
|
├── js/ # JavaScript library files
|
|
72
|
-
│ ├── viewlogic-router.js
|
|
73
|
-
│
|
|
74
|
-
│ └── viewlogic-router.umd.js # UMD bundle
|
|
72
|
+
│ ├── viewlogic-router.esm.js # ESM module (minified)
|
|
73
|
+
│ └── viewlogic-router.min.js # UMD bundle (minified)
|
|
75
74
|
├── i18n/ # Internationalization files
|
|
76
75
|
│ ├── en.json # English translations
|
|
77
76
|
│ ├── ko.json # Korean translations
|
|
@@ -104,12 +103,12 @@ This creates a complete project with examples and starts the development server.
|
|
|
104
103
|
<head>
|
|
105
104
|
<title>My ViewLogic App</title>
|
|
106
105
|
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
|
107
|
-
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.
|
|
106
|
+
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.min.js"></script>
|
|
108
107
|
</head>
|
|
109
108
|
<body>
|
|
110
109
|
<div id="app"></div>
|
|
111
110
|
<script>
|
|
112
|
-
ViewLogicRouter({
|
|
111
|
+
const router = new ViewLogicRouter({
|
|
113
112
|
environment: 'development'
|
|
114
113
|
});
|
|
115
114
|
</script>
|
|
@@ -123,6 +122,18 @@ This creates a complete project with examples and starts the development server.
|
|
|
123
122
|
npm install viewlogic
|
|
124
123
|
```
|
|
125
124
|
|
|
125
|
+
**Option A: Using bundler (Vite, Webpack, etc.)**
|
|
126
|
+
```javascript
|
|
127
|
+
import { ViewLogicRouter } from 'viewlogic';
|
|
128
|
+
|
|
129
|
+
const router = new ViewLogicRouter({
|
|
130
|
+
environment: 'production',
|
|
131
|
+
authEnabled: true,
|
|
132
|
+
useI18n: true
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Option B: Direct ESM import**
|
|
126
137
|
```html
|
|
127
138
|
<!DOCTYPE html>
|
|
128
139
|
<html>
|
|
@@ -132,7 +143,7 @@ npm install viewlogic
|
|
|
132
143
|
<body>
|
|
133
144
|
<div id="app"></div>
|
|
134
145
|
<script type="module">
|
|
135
|
-
import { ViewLogicRouter } from 'viewlogic';
|
|
146
|
+
import { ViewLogicRouter } from 'https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.esm.js';
|
|
136
147
|
const router = new ViewLogicRouter({
|
|
137
148
|
environment: 'production',
|
|
138
149
|
authEnabled: true,
|
|
@@ -731,6 +742,23 @@ ViewLogic Router automatically optimizes for production:
|
|
|
731
742
|
- **Caching**: Aggressive caching for static assets
|
|
732
743
|
- **Lazy loading**: Routes and components load on demand
|
|
733
744
|
|
|
745
|
+
## 📦 Build Output
|
|
746
|
+
|
|
747
|
+
ViewLogic Router provides two optimized build outputs:
|
|
748
|
+
|
|
749
|
+
### ESM Module (`viewlogic-router.esm.js`)
|
|
750
|
+
- **Format**: ES6 modules
|
|
751
|
+
- **Size**: ~52KB minified
|
|
752
|
+
- **Usage**: Modern bundlers (Vite, Webpack, Rollup)
|
|
753
|
+
- **Import**: `import { ViewLogicRouter } from 'viewlogic'`
|
|
754
|
+
|
|
755
|
+
### UMD Bundle (`viewlogic-router.min.js`)
|
|
756
|
+
- **Format**: UMD (Universal Module Definition)
|
|
757
|
+
- **Size**: ~52KB minified
|
|
758
|
+
- **Usage**: Direct browser usage, CDN
|
|
759
|
+
- **Global**: `window.ViewLogicRouter`
|
|
760
|
+
|
|
761
|
+
Both builds are fully minified and production-ready with source maps included.
|
|
734
762
|
|
|
735
763
|
## 🤝 Contributing
|
|
736
764
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/* ViewLogic v1.2.7 - ESM Module | (c) 2024 hopegiver | MIT License */
|
|
2
|
+
var p=class{constructor(e,t={}){this.config={enabled:t.useI18n!==void 0?t.useI18n:!0,defaultLanguage:t.defaultLanguage||"en",fallbackLanguage:t.defaultLanguage||"en"},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.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=this.router.cacheManager?.get("viewlogic_lang");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{this.router.cacheManager?.set("viewlogic_lang",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 s={};return this.messages.set(e,s),s}}async _loadMessagesFromFile(e){let t=`i18n_${e}`,r=this.router.cacheManager?.get(t);if(r)return this.log("debug","Messages loaded from cache:",e),r;try{let s=`${this.router.config.i18nPath}/${e}.json`,o=await fetch(s);if(!o.ok)throw new Error(`HTTP error! status: ${o.status}`);let a=await o.json();return this.router.cacheManager?.set(t,a),a}catch(s){if(this.log("error","Failed to load messages file for:",e,s),e!==this.config.fallbackLanguage){this.log("info","Trying fallback language:",this.config.fallbackLanguage);try{return await this._loadMessagesFromFile(this.config.fallbackLanguage)}catch(o){return this.log("error","Fallback language also failed:",o),{}}}return this.log("warn",`No messages available for language: ${e}, using empty fallback`),{}}}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 s=this.getNestedValue(r,e);if(s===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 a=this.getNestedValue(o,e);if(a!==void 0)return this.interpolate(a,t)}return e}return this.interpolate(s,t)}getNestedValue(e,t){return t.split(".").reduce((r,s)=>r&&r[s]!==void 0?r[s]:void 0,e)}interpolate(e,t){return typeof e!="string"?e:e.replace(/\{(\w+)\}/g,(r,s)=>t.hasOwnProperty(s)?t[s]:r)}plural(e,t,r={}){let s=t===1?`${e}.singular`:`${e}.plural`;return this.t(s,{...r,count:t})}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(s){this.log("error","Error in event listener:",s)}})}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 e=this.router.cacheManager?.deleteByPattern("i18n_");this.log("debug","Cache cleared, removed",e,"items")}catch(e){this.log("warn","Failed to clear cache:",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 y=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",authStorage:t.authStorage||"localStorage"},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 s={name:e,$api:this.router.routeLoader.apiHandler.bindToComponent({}),$state:this.router.stateHandler},o=await this.config.checkAuthFunction(s);return{allowed:o,reason:o?"custom_auth_success":"custom_auth_failed",routeName:e}}catch(s){return this.log("error","Custom auth function failed:",s),{allowed:!1,reason:"custom_auth_error",error:s}}let r=this.isAuthenticated();return{allowed:r,reason:r?"authenticated":"not_authenticated",routeName:e}}isTokenValid(e){if(!e)return!1;try{if(e.includes(".")){let t=JSON.parse(atob(e.split(".")[1]));if(t.exp&&Date.now()>=t.exp*1e3)return!1}return!0}catch(t){return this.log("warn","Token validation failed:",t),!1}}isAuthenticated(){this.log("debug","\u{1F50D} Checking user authentication status");let e=this.getAccessToken();return e?this.isTokenValid(e)?(this.log("debug","\u2705 Valid token found"),!0):(this.log("debug","Token expired, removing..."),this.removeAccessToken(),!1):(this.log("debug","\u274C No token 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(){return this.getCookieValue(this.config.authCookieName)}getCookieValue(e){let r=`; ${document.cookie}`.split(`; ${e}=`);return r.length===2?decodeURIComponent(r.pop().split(";").shift()):null}removeAuthCookie(){document.cookie=`${this.config.authCookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`,this.log("debug","Auth cookie removed")}getAccessToken(){let e=localStorage.getItem("authToken");return e||(e=sessionStorage.getItem("authToken"),e)?e:this.getAuthCookie()}setAccessToken(e,t={}){if(!e)return this.log("warn","Empty token provided"),!1;let{storage:r=this.config.authStorage,cookieOptions:s=this.config.authCookieOptions}=t;try{if(!this.isTokenValid(e))return this.log("warn","\u274C Token is expired or invalid"),!1;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);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(o){return this.log("error","Failed to set token:",o),!1}}setAuthCookie(e){let t=window.location.protocol==="https:",r=`${this.config.authCookieName}=${encodeURIComponent(e)}; path=/; SameSite=Strict`;if(t&&(r+="; Secure"),e.includes("."))try{let s=JSON.parse(atob(e.split(".")[1]));if(s.exp){let o=new Date(s.exp*1e3);r+=`; Expires=${o.toUTCString()}`}}catch{this.log("warn","Could not extract expiration from JWT token")}document.cookie=r,this.log("debug","Auth cookie set")}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"),sessionStorage.removeItem("authToken"),this.removeAuthCookie();break}this.emitAuthEvent("token_removed",{storage:e}),this.log("debug",`Token removed from: ${e}`)}loginSuccess(e=null){let t=e||this.config.redirectAfterLogin;return this.log("info",`\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}logout(){return this.log("info","\u{1F44B} Logging out user"),this.removeAccessToken(),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(s=>{try{s(t)}catch(o){this.log("error","Event listener error:",o)}}),this.log("debug",`\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),s=r.indexOf(t);s>-1&&r.splice(s,1)}}getAuthStats(){return{enabled:this.config.enabled,isAuthenticated:this.isAuthenticated(),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 b=class{constructor(e,t={}){this.config={cacheMode:t.cacheMode||"memory",cacheTTL:t.cacheTTL||3e5,maxCacheSize:t.maxCacheSize||50},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)}set(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 s=this.lruOrder.indexOf(e);s>-1&&this.lruOrder.splice(s,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})`)}get(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 s=this.cache.get(e);if(s&&this.config.cacheMode==="lru"){let o=this.lruOrder.indexOf(e);o>-1&&(this.lruOrder.splice(o,1),this.lruOrder.push(e))}return s?this.log("debug",`\u{1F3AF} Cache hit: ${e}`):this.log("debug",`\u274C Cache miss: ${e}`),s}has(e){return this.cache.has(e)&&this.get(e)!==null}deleteByPattern(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 s=this.lruOrder.indexOf(r);s>-1&&this.lruOrder.splice(s,1)}}),this.log("debug",`\u{1F9F9} Deleted ${t.length} cache entries matching: ${e}`),t.length}deleteComponent(e){let t=[`component_${e}`,`script_${e}`,`template_${e}`,`style_${e}`,`layout_${e}`],r=0;return t.forEach(s=>{r+=this.deleteByPattern(s)}),this.log("debug",`\u{1F504} Deleted component cache for route: ${e} (${r} entries)`),r}deleteAllComponents(){let e=["component_","script_","template_","style_","layout_"],t=0;return e.forEach(r=>{t+=this.deleteByPattern(r)}),this.log("debug",`\u{1F9FD} Deleted all component caches (${t} entries)`),t}clearAll(){let e=this.cache.size;return this.cache.clear(),this.cacheTimestamps.clear(),this.lruOrder=[],this.log("debug",`\u{1F525} Cleared all cache (${e} entries)`),e}cleanExpired(){let e=Date.now(),t=[];for(let[r,s]of this.cacheTimestamps.entries())e-s>this.config.cacheTTL&&t.push(r);return t.forEach(r=>{if(this.cache.delete(r),this.cacheTimestamps.delete(r),this.config.cacheMode==="lru"){let s=this.lruOrder.indexOf(r);s>-1&&this.lruOrder.splice(s,1)}}),t.length>0&&this.log("debug",`\u23F1\uFE0F Cleaned ${t.length} expired cache entries`),t.length}getStats(){return{size:this.cache.size,maxSize:this.config.maxCacheSize,mode:this.config.cacheMode,ttl:this.config.cacheTTL,memoryUsage:this.getMemoryUsage(),hitRatio:this.getHitRate(),categories:this.getStatsByCategory()}}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}}getHitRate(){let e=this.cache.size>0?Math.min(this.cache.size/this.config.maxCacheSize,1):0;return Math.round(e*100)}getStatsByCategory(){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}getKeys(){return Array.from(this.cache.keys())}getKeysByPattern(e){return this.getKeys().filter(t=>t.includes(e)||t.startsWith(e))}startAutoCleanup(e=6e4){this.cleanupInterval&&clearInterval(this.cleanupInterval),this.cleanupInterval=setInterval(()=>{this.cleanExpired()},e),this.log("debug",`\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.clearAll(),this.log("debug","CacheManager destroyed")}};var w=class{constructor(e){this.router=e,this.currentQueryParams={},this.currentRouteParams={},this.log("debug","QueryManager initialized")}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"QueryManager",...t)}parseQueryString(e){let t={};if(!e)return t;let r=e.split("&");for(let s of r){let[o,a]=s.split("=");if(o)try{let i=decodeURIComponent(o),n=a?decodeURIComponent(a):"";if(i.endsWith("[]")){let l=i.slice(0,-2);t[l]||(t[l]=[]),t[l].push(n)}else t[i]=n}catch{this.log("warn","Failed to decode query parameter:",s)}}return t}buildQueryString(e){if(!e||Object.keys(e).length===0)return"";let t=[];for(let[r,s]of Object.entries(e))if(Array.isArray(s))for(let o of s)t.push(`${encodeURIComponent(r)}[]=${encodeURIComponent(o)}`);else s!=null&&t.push(`${encodeURIComponent(r)}=${encodeURIComponent(s)}`);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 s of t)if(JSON.stringify(this.currentQueryParams[s])!==JSON.stringify(e[s]))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"){this.log("warn","Invalid parameters object provided to setQueryParams");return}let r=t?{}:{...this.currentQueryParams};for(let[s,o]of Object.entries(e))o!=null&&o!==""?r[s]=o:delete r[s];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,currentQueryString:this.buildQueryString(this.currentQueryParams)}}destroy(){this.currentQueryParams={},this.currentRouteParams={},this.router=null,this.log("debug","QueryManager destroyed")}};var P=class{constructor(e,t={}){this.router=e,this.requestTimeout=t.requestTimeout||3e4,this.uploadTimeout=t.uploadTimeout||3e5,this.log("debug","FormHandler initialized")}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"FormHandler",...t)}isDuplicateRequest(e){return e._isSubmitting?(this.log("debug","Duplicate request blocked"),!0):!1}startFormSubmission(e){e._isSubmitting=!0,e._abortController=new AbortController;let r=Array.from(e.elements).some(s=>s.type==="file"&&s.files.length>0)?this.uploadTimeout:this.requestTimeout;e._timeoutId=setTimeout(()=>{e._isSubmitting&&this.abortFormSubmission(e)},r)}finishFormSubmission(e){e._isSubmitting=!1,e._timeoutId&&(clearTimeout(e._timeoutId),delete e._timeoutId),delete e._abortController}abortFormSubmission(e){e._abortController&&e._abortController.abort(),this.finishFormSubmission(e)}bindAutoForms(e){document.querySelectorAll("form.auto-form, form[action]").forEach(r=>{r.removeEventListener("submit",r._boundSubmitHandler);let s=o=>this.handleFormSubmit(o,e);r._boundSubmitHandler=s,r.addEventListener("submit",s),this.log("debug",`Form auto-bound: ${r.getAttribute("action")}`)})}async handleFormSubmit(e,t){e.preventDefault();let r=e.target,s=r.getAttribute("action"),o=r.getAttribute("method")||"POST",a=r.getAttribute("data-success"),i=r.getAttribute("data-error"),n=r.getAttribute("data-loading"),l=r.getAttribute("data-redirect");if(s=this.processActionParams(s,t),!this.validateForm(r,t)||this.isDuplicateRequest(r))return;this.startFormSubmission(r);let g=new FormData(r),f=Object.fromEntries(g.entries());try{n&&t[n]&&t[n](!0,r),this.log("debug",`Form submitting to: ${s}`,f);let h=await this.submitFormData(s,o,f,r,t,r._abortController.signal);a&&t[a]&&t[a](h,r),this.finishFormSubmission(r),l&&requestAnimationFrame(()=>{setTimeout(()=>{t.navigateTo(l)},1e3)})}catch(h){if(h.name==="AbortError"){this.log("debug","Form submission aborted");return}this.log("warn","Form submission error:",h),this.finishFormSubmission(r),i&&t[i]?t[i](h,r):this.log("error","Form submission error (no error handler defined):",h)}finally{n&&t[n]&&t[n](!1,r)}}processActionParams(e,t){return this.router.routeLoader.apiHandler.processURLParameters(e,t)}async submitFormData(e,t,r,s,o,a=null){let i=Array.from(s.elements).some(l=>l.type==="file"&&l.files.length>0),n={method:t.toUpperCase(),headers:{},signal:a};return i?n.data=new FormData(s):(n.data=r,n.headers["Content-Type"]="application/json"),await this.router.routeLoader.apiHandler.fetchData(e,o,n)}validateForm(e,t){let r=!0;return e.querySelectorAll("input, textarea, select").forEach(o=>{if(!o.checkValidity()){r=!1,o.classList.add("error");return}let a=o.getAttribute("data-validation");a?this.validateInput(o,a,t)?o.classList.remove("error"):(r=!1,o.classList.add("error")):o.classList.remove("error")}),r}validateInput(e,t,r){let s=e.value;if(typeof r[t]=="function")try{return r[t](s,e)}catch(o){return this.log("warn",`Validation function '${t}' error:`,o),!1}return this.log("warn",`Validation function '${t}' not found`),!0}cancelAllRequests(){document.querySelectorAll("form").forEach(t=>{t._isSubmitting&&this.abortFormSubmission(t)})}destroy(){this.cancelAllRequests(),document.querySelectorAll("form.auto-form, form[action]").forEach(t=>{t._boundSubmitHandler&&(t.removeEventListener("submit",t._boundSubmitHandler),delete t._boundSubmitHandler),this.cleanupFormState(t)}),this.log("debug","FormHandler destroyed"),this.router=null}cleanupFormState(e){delete e._isSubmitting,delete e._abortController,e._timeoutId&&(clearTimeout(e._timeoutId),delete e._timeoutId)}};var L=class{constructor(e,t={}){this.router=e,this.apiBaseURL=t.apiBaseURL||"",this.log("debug","ApiHandler initialized")}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"ApiHandler",...t)}async fetchData(e,t=null,r={}){try{let s=this.processURLParameters(e,t);this.apiBaseURL&&!this.isAbsoluteURL(s)&&(s=this.combineURLs(this.apiBaseURL,s));let o=this.router.queryManager?.buildQueryString(this.router.queryManager?.getQueryParams())||"",a=o?`${s}?${o}`:s;this.log("debug",`Fetching data from: ${a}`);let i={method:r.method||"GET",headers:{"Content-Type":"application/json",Accept:"application/json",...r.headers},...r};t?.$getToken&&t.$getToken()&&(i.headers.Authorization=`Bearer ${t.$getToken()}`),r.data&&["POST","PUT","PATCH"].includes(i.method.toUpperCase())&&(r.data instanceof FormData?(i.body=r.data,delete i.headers["Content-Type"]):i.body=JSON.stringify(r.data));let n=await fetch(a,i);if(!n.ok){let l;try{l=await n.json()}catch{l={message:`HTTP ${n.status}: ${n.statusText}`}}throw new Error(l.message||`HTTP ${n.status}`)}try{let l=await n.json();if(typeof l!="object"||l===null)throw new Error("Invalid data format: expected object");return l}catch{return{success:!0}}}catch(s){throw this.log("error","Failed to fetch data:",s),s}}async fetchMultipleData(e,t=null){if(!e||typeof e!="object")return{};let r={},s={},o=Object.entries(e).map(async([a,i])=>{try{let n,l={};if(typeof i=="string"?n=i:typeof i=="object"&&(n=i.url,l={...i},delete l.url),n){let g=await this.fetchData(n,t,l);r[a]=g}}catch(n){s[a]=n,this.log("warn",`Failed to fetch data for '${a}':`,n)}});return await Promise.all(o),{results:r,errors:s}}processURLParameters(e,t=null){if(!e||typeof e!="string")return e;let r=e,s=e.match(/\{([^}]+)\}/g);return s&&t&&s.forEach(o=>{let a=o.slice(1,-1);try{let i=null;t.$options?.computed?.[a]&&(i=t[a]),i==null&&(i=t[a]),i==null&&t.getParam&&(i=t.getParam(a)),i==null&&(i=this.router.queryManager?.getParam(a)),i!=null?(r=r.replace(o,encodeURIComponent(i)),this.log("debug",`URL parameter resolved: ${a} = ${i}`)):this.log("warn",`URL parameter '${a}' not found, keeping original: ${o}`)}catch(i){this.log("warn",`Error processing URL parameter '${a}':`,i)}}),r}async get(e,t=null,r={}){return this.fetchData(e,t,{...r,method:"GET"})}async post(e,t,r=null,s={}){return this.fetchData(e,r,{...s,method:"POST",data:t})}async put(e,t,r=null,s={}){return this.fetchData(e,r,{...s,method:"PUT",data:t})}async patch(e,t,r=null,s={}){return this.fetchData(e,r,{...s,method:"PATCH",data:t})}async delete(e,t=null,r={}){return this.fetchData(e,t,{...r,method:"DELETE"})}bindToComponent(e){return{get:(t,r={})=>this.get(t,e,r),post:(t,r,s={})=>this.post(t,r,e,s),put:(t,r,s={})=>this.put(t,r,e,s),patch:(t,r,s={})=>this.patch(t,r,e,s),delete:(t,r={})=>this.delete(t,e,r),fetchData:(t,r={})=>this.fetchData(t,e,r),fetchMultipleData:t=>this.fetchMultipleData(t,e)}}isAbsoluteURL(e){return/^https?:\/\//.test(e)||e.startsWith("//")}combineURLs(e,t){let r=e.replace(/\/$/,""),s=t.startsWith("/")?t:`/${t}`;return`${r}${s}`}destroy(){this.log("debug","ApiHandler destroyed"),this.router=null}};var C=class{constructor(e=null,t={}){this.config={componentsPath:t.componentsPath||"/components",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");let t=`component_${e}`,r=this.router?.cacheManager?.get(t);if(r)return this.log("debug",`Component '${e}' loaded from cache`),r;if(this.loadingPromises.has(e))return this.loadingPromises.get(e);let s=this._loadComponentFromFile(e);this.loadingPromises.set(e,s);try{let o=await s;return o&&this.router?.cacheManager&&(this.router.cacheManager.set(t,o),this.log("debug",`Component '${e}' cached successfully`)),o}catch(o){throw o}finally{this.loadingPromises.delete(e)}}async _loadComponentFromFile(e){let t=`${this.config.componentsPath}/${e}.js`,r;if(this.router&&this.router.config.srcPath){let s=this.router.config.srcPath;if(s.startsWith("http")){let o=s.endsWith("/")?s.slice(0,-1):s,a=t.startsWith("/")?t:`/${t}`;r=`${o}${a}`}else r=this.router.resolvePath(`${s}${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(s){throw this.log("error",`Failed to load component '${e}':`,s),new Error(`Component '${e}' not found: ${s.message}`)}}clearComponents(){this.loadingPromises.clear(),this.unifiedComponents=null,this.log("debug","All components cleared")}async loadAllComponents(e=null){let t;if(this.config.environment==="production"){if(this.unifiedComponents)return this.log("debug","Using existing unified components"),this.unifiedComponents;t=await this._loadProductionComponents()}else t=await this._loadDevelopmentComponents(e);return t}async _loadProductionComponents(){try{let e=`${this.router?.config?.routesPath||"/routes"}/_components.js`;this.log("debug","[PRODUCTION] Loading unified components from:",e);let t=await import(e);if(typeof t.registerComponents=="function")return this.unifiedComponents=t.components||{},this.log("debug",`[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(e=null){let t=e||[],r={};if(t.length===0)return this.log("debug","[DEVELOPMENT] No components to load"),r;this.log("debug",`[DEVELOPMENT] Loading individual components: ${t.join(", ")}`);for(let s of t)try{let o=await this.loadComponent(s);o&&(r[s]=o)}catch(o){this.log("warn",`[DEVELOPMENT] Failed to load component ${s}:`,o.message)}return this.log("debug",`[DEVELOPMENT] Individual components loaded: ${Object.keys(r).length} components`),r}getComponentNames(e,t=null,r=null){let s=t?this._getLayoutComponents(t,r):new Set;e&&this._extractComponentsFromContent(e,s);let o=Array.from(s);return this.log("debug",`Discovered ${o.length} components:`,o),o}_getLayoutComponents(e,t){if(!e||typeof e!="string")return new Set;if(!t||typeof t!="string")return new Set;let r=`layout_components_${t}`,s=this.router?.cacheManager?.get(r);if(s)return this.log("debug",`Using cached layout components for '${t}'`),s;let o=new Set;return this._extractComponentsFromContent(e,o),this.router?.cacheManager&&(this.router.cacheManager.set(r,o),this.log("debug",`Cached layout components for '${t}': ${Array.from(o).join(", ")}`)),o}_extractComponentsFromContent(e,t){if(!e||typeof e!="string")return;let r=/<([A-Z][a-zA-Z0-9]*)(?:\s[^>]*)?\/?>|<\/([A-Z][a-zA-Z0-9]*)\s*>/gs,s;for(;(s=r.exec(e))!==null;){let o=s[1]||s[2];o&&!this._isHtmlTag(o)&&(t.add(o),this.log("debug",`Found component: ${o}`))}}_isHtmlTag(e){return["div","span","p","a","img","ul","ol","li","h1","h2","h3","h4","h5","h6","table","tr","td","th","form","select","option","textarea","nav","header","footer","main","section","article","aside","figure","figcaption","video","audio","canvas","svg","iframe","script","style","link","meta","title","body","html","head","template","slot"].includes(e)}dispose(){this.clearComponents(),this.log("debug","ComponentLoader disposed"),this.router=null}};var $=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"},this.router=e,this.formHandler=new P(e,this.config),this.apiHandler=new L(e,this.config),this.componentLoader=new C(e,this.config),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 s=await r.text();return this.log("debug",`Template '${e}' loaded successfully`),s}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 s=await r.text();return this.log("debug",`Style '${e}' loaded successfully`),s}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 s=await r.text();return this.log("debug",`Layout '${e}' loaded successfully`),s}catch(t){return this.log("debug",`Layout '${e}' not found, no layout applied:`,t.message),null}}mergeLayoutWithTemplate(e,t,r){let s;return t.includes("{{ content }}")?s=t.replace(/{{ content }}/s,r):t.includes('class="main-content"')?(this.log("debug","Using main-content replacement"),s=t.replace(/(<div class="container">).*?(<\/div>\s*<\/main>)/s,`$1${r}$2`)):(this.log("debug","Wrapping template with layout"),s=`${t}
|
|
3
|
+
${r}`),s}async createVueComponent(e){let t=`component_${e}`,r=this.router.cacheManager?.get(t);if(r)return r;let s=await this.loadScript(e),o=this.router,a=this.config.environment==="production",i,n="",l=null;if(a)i=s.template||this.generateDefaultTemplate(e);else{let h=[this.loadTemplate(e),this.loadStyle(e)];this.config.useLayout&&s.layout!==null?h.push(this.loadLayout(s.layout||this.config.defaultLayout)):h.push(Promise.resolve(null));let[u,c,m]=await Promise.all(h);i=u,n=c,l=m,l&&(i=this.mergeLayoutWithTemplate(e,l,i))}let g={};if(this.componentLoader)try{let h=null;if(!a){let u=s.layout||this.config.defaultLayout;h=this.componentLoader.getComponentNames(i,l,u),this.log("debug",`[DEVELOPMENT] Discovered components for route '${e}':`,h)}g=await this.componentLoader.loadAllComponents(h),this.log("debug",`Components loaded successfully for route: ${e}`)}catch(h){this.log("warn",`Component loading failed for route '${e}', continuing without components:`,h.message),g={}}let f={...s,name:s.name||this.toPascalCase(e),template:i,components:g,data(){return{...s.data?s.data():{},currentRoute:e,$query:o.queryManager?.getQueryParams()||{},$params:o.queryManager?.getRouteParams()||{},$lang:(()=>{try{return o.i18nManager?.getCurrentLanguage()||o.config.i18nDefaultLanguage||o.config.defaultLanguage||"ko"}catch(c){return o.errorHandler&&o.errorHandler.warn("RouteLoader","Failed to get current language:",c),o.config.defaultLanguage||"ko"}})(),$dataLoading:!1}},computed:{...s.computed||{},params(){return o.queryManager?.getAllParams()||{}}},async mounted(){this.$api=o.routeLoader.apiHandler.bindToComponent(this),this.$state=o.stateHandler,s.mounted&&await s.mounted.call(this),s.dataURL&&await this.fetchData(),await this.$nextTick(),o.routeLoader.formHandler.bindAutoForms(this)},methods:{...s.methods,navigateTo:(h,u)=>o.navigateTo(h,u),getCurrentRoute:()=>o.getCurrentRoute(),getParams:()=>o.queryManager?.getAllParams()||{},getParam:(h,u)=>o.queryManager?.getParam(h,u),$t:(h,u)=>{try{return o.i18nManager?.t(h,u)||h}catch(c){return o.errorHandler&&o.errorHandler.warn("RouteLoader","i18n translation failed, returning key:",c),h}},isAuth:()=>o.authManager?.isAuthenticated()||!1,logout:()=>o.authManager?o.navigateTo(o.authManager.logout()):null,getToken:()=>o.authManager?.getAccessToken()||null,setToken:(h,u)=>o.authManager?.setAccessToken(h,u)||!1,getLanguage:()=>o.i18nManager?.getCurrentLanguage()||o.config.defaultLanguage||"ko",setLanguage:h=>o.i18nManager?.setLanguage(h),log:(h,...u)=>{o.errorHandler&&o.errorHandler.log(h,`[${e}]`,...u)},async fetchData(h=null){let u=h||s.dataURL;if(!u)return null;this.$dataLoading=!0;try{if(typeof u=="string"){let c=await o.routeLoader.apiHandler.fetchData(u,this);return Object.assign(this,c),this.$emit("data-loaded",c),c}else if(typeof u=="object"){let{results:c,errors:m}=await o.routeLoader.apiHandler.fetchMultipleData(u,this);return Object.assign(this,c),Object.keys(c).length>0&&this.$emit("data-loaded",c),Object.keys(m).length>0&&this.$emit("data-error",m),c}return null}catch(c){throw o.errorHandler&&o.errorHandler.warn("RouteLoader",`Failed to fetch data for ${e}:`,c),this.$emit("data-error",c),c}finally{this.$dataLoading=!1}},async $createComponent(h){try{return await o.routeLoader.createVueComponent(h)}catch(u){throw o.errorHandler&&o.errorHandler.warn("RouteLoader",`Failed to create component '${h}':`,u),u}}},_routeName:e};return!a&&n&&(f._style=n),this.router.cacheManager?.set(t,f),f}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>`}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"RouteLoader",...t)}destroy(){this.formHandler&&(this.formHandler.destroy(),this.formHandler=null),this.apiHandler&&(this.apiHandler.destroy(),this.apiHandler=null),this.componentLoader&&(this.componentLoader.dispose(),this.componentLoader=null),this.log("debug","RouteLoader destroyed"),this.router=null}};var v=class{constructor(e,t={}){this.config={logLevel:t.logLevel||"info",environment:t.environment||"development"},this.router=e,this.logLevels={error:0,warn:1,info:2,debug:3},this.log("debug","ErrorHandler","ErrorHandler initialized with config:",this.config)}async handleRouteError(e,t){let r=500,s="\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,s=`'${e}' \uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`):t.message.includes("network")&&!t.message.includes("not found")?(r=503,s="\uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD574 \uC8FC\uC138\uC694."):(t.message.includes("permission")||t.message.includes("403"))&&(r=403,s="\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.reportError(e,t,r);try{r===404?await this.load404Page():await this.loadErrorPage(r,s)}catch(o){this.error("ErrorHandler","\uC5D0\uB7EC \uD398\uC774\uC9C0 \uB85C\uB529 \uC2E4\uD328:",o),this.showFallbackErrorPage(r,s)}}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 s=`<div style="text-align:center;padding:2rem;font-family:sans-serif"><h1>${e}</h1><p>${t}</p><button onclick="location.reload()">Retry</button></div>`;r.innerHTML=s,this.info("ErrorHandler",`Fallback error page displayed for ${e}`)}reportError(e,t,r){let s={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||"unknown",mode:this.router?.config?.mode||"unknown"}};this.error("ErrorHandler","\uB77C\uC6B0\uD130 \uC5D0\uB7EC \uB9AC\uD3EC\uD2B8:",s)}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="info");let s=this.logLevels[this.config.logLevel]||this.logLevels.info,o=this.logLevels[e]||this.logLevels.info;if(o>s||this.config.environment==="production"&&o>this.logLevels.warn)return;let a=t?`[${t}]`:"[ViewLogic]",i=new Date().toISOString().substring(11,23);switch(e){case"error":console.error(`${i} ${a}`,...r);break;case"warn":console.warn(`${i} ${a}`,...r);break;case"info":console.info(`${i} ${a}`,...r);break;case"debug":console.log(`${i} ${a}`,...r);break;default:console.log(`${i} ${a}`,...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){this.router=e,this.state={},this.listeners=new Map,this.log("debug","StateHandler initialized")}log(e,...t){this.router?.errorHandler&&this.router.errorHandler.log(e,"StateHandler",...t)}set(e,t){let r=this.state[e];return this.state[e]=t,this.emitChange(e,t,r),this.log("debug",`State set: ${e}`,t),t}get(e,t=void 0){let r=this.state.hasOwnProperty(e)?this.state[e]:t;return this.log("debug",`State get: ${e}`,r),r}has(e){return this.state.hasOwnProperty(e)}delete(e){if(this.has(e)){let t=this.state[e];return delete this.state[e],this.emitChange(e,void 0,t),this.log("debug",`State deleted: ${e}`),!0}return!1}clear(){let e=Object.keys(this.state);return this.state={},e.forEach(t=>{this.emitChange(t,void 0,this.state[t])}),this.log("debug","All state cleared"),e.length}update(e){if(!e||typeof e!="object"){this.log("warn","Invalid updates object provided");return}Object.entries(e).forEach(([t,r])=>{this.set(t,r)})}getAll(){return{...this.state}}watch(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t),this.log("debug",`Watcher added for: ${e}`)}unwatch(e,t){if(this.listeners.has(e)){let r=this.listeners.get(e),s=r.indexOf(t);s>-1&&(r.splice(s,1),this.log("debug",`Watcher removed for: ${e}`))}}emitChange(e,t,r){this.listeners.has(e)&&this.listeners.get(e).forEach(s=>{try{s(t,r,e)}catch(o){this.log("error","State watcher error:",o)}})}getStats(){return{stateCount:Object.keys(this.state).length,watcherCount:Array.from(this.listeners.values()).reduce((e,t)=>e+t.length,0),keys:Object.keys(this.state)}}destroy(){this.state={},this.listeners.clear(),this.log("debug","StateHandler destroyed")}};var R=class{constructor(e={}){this.version=e.version||"1.0.0",this.config=this._buildConfig(e),this.currentHash="",this.currentVueApp=null,this.previousVueApp=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,s={...{basePath:"/",srcPath:"/src",mode:"hash",cacheMode:"memory",cacheTTL:3e5,maxCacheSize:50,useLayout:!0,defaultLayout:"default",environment:"development",routesPath:"/routes",enableErrorReporting:!0,useI18n:!1,defaultLanguage:"ko",i18nPath:"/i18n",logLevel:"info",apiBaseURL:"",requestTimeout:3e4,uploadTimeout:3e5,authEnabled:!1,loginRoute:"login",protectedRoutes:[],protectedPrefixes:[],publicRoutes:["login","register","home"],checkAuthFunction:null,redirectAfterLogin:"home",authCookieName:"authToken",authStorage:"localStorage"},...e};return s.srcPath=this.resolvePath(s.srcPath,s.basePath),s.routesPath=this.resolvePath(s.routesPath,s.basePath),s.i18nPath=this.resolvePath(s.i18nPath,s.basePath),s}combinePaths(e,t){if(!e||e==="/")return t.replace(/\/+/g,"/");let r=e.endsWith("/")?e.slice(0,-1):e,s=t.startsWith("/")?t:`/${t}`;return`${r}${s}`.replace(/\/+/g,"/")}resolvePath(e,t=null){t===null&&(t=this.config?.basePath||"/");let r=window.location.origin;if(e.startsWith("http"))return e;if(e.startsWith("/")){let i=this.combinePaths(t,e);return`${r}${i}`}let s=window.location.pathname,o=s.endsWith("/")?s:s.substring(0,s.lastIndexOf("/")+1),a=this.combinePaths(o,e);return`${r}${a}`}log(e,...t){this.errorHandler&&this.errorHandler.log(e,"Router",...t)}async initialize(){try{if(this.cacheManager=new b(this,this.config),this.stateHandler=new T(this),this.routeLoader=new $(this,this.config),this.queryManager=new w(this),this.errorHandler=new v(this,this.config),this.config.useI18n)try{this.i18nManager=new p(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}this.config.authEnabled&&(this.authManager=new y(this,this.config)),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.config.mode==="hash"){let e=window.location.hash.slice(1)||"/",[t,r]=e.split("?"),s="home";return t&&t!=="/"&&(s=t.startsWith("/")?t.slice(1):t),{route:s||"home",queryParams:this.queryManager?.parseQueryString(r||window.location.search.slice(1))||{}}}else{let e=window.location.pathname,t=this.config.basePath||"/",r=e;return t!=="/"&&e.startsWith(t)&&(r=e.slice(t.length)),r.startsWith("/")&&(r=r.slice(1)),{route:r||"home",queryParams:this.queryManager?.parseQueryString(window.location.search.slice(1))||{}}}}async loadRoute(e){if(!this.transitionInProgress)try{if(this.transitionInProgress=!0,!(this.authManager?await this.authManager.checkAuthentication(e):{allowed:!0,reason:"auth_disabled"}).allowed){this.authManager&&(this.authManager.emitAuthEvent("auth_required",{originalRoute:e,loginRoute:this.config.loginRoute}),e!==this.config.loginRoute?this.navigateTo(this.config.loginRoute,{redirect:e}):this.navigateTo(this.config.loginRoute));return}if(!document.getElementById("app"))throw new Error("App element not found");let o=await this.routeLoader.createVueComponent(e);await this.renderComponentWithTransition(o,e)}catch(r){this.log("error",`Route loading failed [${e}]:`,r.message),this.errorHandler?await this.errorHandler.handleRouteError(e,r):console.error("[Router] Critical: No error handler available for route error:",r)}finally{this.transitionInProgress=!1}}async renderComponentWithTransition(e,t){let r=document.getElementById("app");if(!r)return;let s=document.createElement("div");s.className="page-container page-entered",s.id=`page-${t}-${Date.now()}`,r.querySelectorAll(".page-container").forEach(n=>{n.classList.remove("page-entered"),n.classList.add("page-exiting")}),r.appendChild(s),this.config.environment==="development"&&e._style&&this.applyStyle(e._style,t);let{createApp:a}=Vue,i=a(e);i.config.globalProperties.$router={navigateTo:(n,l)=>this.navigateTo(n,l),getCurrentRoute:()=>this.getCurrentRoute(),getParams:()=>this.queryManager?.getAllParams()||{},getParam:(n,l)=>this.queryManager?.getParam(n,l),getQueryParams:()=>this.queryManager?.getQueryParams()||{},getQueryParam:(n,l)=>this.queryManager?.getQueryParam(n,l),setQueryParams:(n,l)=>this.queryManager?.setQueryParams(n,l),removeQueryParams:n=>this.queryManager?.removeQueryParams(n),getRouteParams:()=>this.queryManager?.getRouteParams()||{},getRouteParam:(n,l)=>this.queryManager?.getRouteParam(n,l),currentRoute:this.currentHash,currentQuery:this.queryManager?.getQueryParams()||{}},i.mount(`#${s.id}`),window.scrollTo(0,0),requestAnimationFrame(()=>{this.cleanupPreviousPages(),this.transitionInProgress=!1}),this.currentVueApp&&(this.previousVueApp=this.currentVueApp),this.currentVueApp=i}cleanupPreviousPages(){let e=document.getElementById("app");if(!e)return;let t=document.createDocumentFragment();if(e.querySelectorAll(".page-container.page-exiting").forEach(s=>s.remove()),this.previousVueApp){try{this.previousVueApp.unmount()}catch{}this.previousVueApp=null}e.querySelector(".loading")?.remove()}applyStyle(e,t){let r=document.querySelector(`style[data-route="${t}"]`);if(r&&r.remove(),e){let s=document.createElement("style");s.textContent=e,s.setAttribute("data-route",t),document.head.appendChild(s)}}navigateTo(e,t=null){typeof e=="object"&&(t=e.params||null,e=e.route),e!==this.currentHash&&this.queryManager&&this.queryManager.clearQueryParams(),this.queryManager&&this.queryManager.setCurrentRouteParams(t),this.updateURL(e,t)}getCurrentRoute(){return this.currentHash}updateURL(e,t=null){let r=t||this.queryManager?.getQueryParams()||{},s=this.queryManager?.buildQueryString(r)||"",o=e==="home"?"/":`/${e}`;if(this.config.mode==="hash"){let i=`#${s?`${o}?${s}`:o}`;window.location.hash!==i&&(window.location.hash=i)}else{o=this.combinePaths(this.config.basePath,o);let a=s?`${o}?${s}`:o;window.history.pushState({},"",a),this.handleRouteChange()}}destroy(){window.removeEventListener(this.config.mode==="hash"?"hashchange":"popstate",this._boundHandleRouteChange),this.currentVueApp&&(this.currentVueApp.unmount(),this.currentVueApp=null),this.previousVueApp&&(this.previousVueApp.unmount(),this.previousVueApp=null),Object.values(this).forEach(t=>{t&&typeof t.destroy=="function"&&t.destroy()}),this.cacheManager?.clearAll();let e=document.getElementById("app");e&&(e.innerHTML=""),this.log("info","Router destroyed")}};export{R as ViewLogicRouter};
|
|
4
|
+
//# sourceMappingURL=viewlogic-router.esm.js.map
|