viewlogic 1.2.2 → 1.2.3

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.
@@ -9,13 +9,8 @@ var I18nManager = class {
9
9
  constructor(router, options = {}) {
10
10
  this.config = {
11
11
  enabled: options.useI18n !== void 0 ? options.useI18n : true,
12
- defaultLanguage: options.defaultLanguage || "ko",
13
- fallbackLanguage: options.defaultLanguage || "ko",
14
- cacheKey: options.cacheKey || "viewlogic_lang",
15
- dataCacheKey: options.dataCacheKey || "viewlogic_i18n_data",
16
- cacheVersion: options.cacheVersion || "1.0.0",
17
- enableDataCache: options.enableDataCache !== false,
18
- debug: options.debug || false
12
+ defaultLanguage: options.defaultLanguage || "en",
13
+ fallbackLanguage: options.defaultLanguage || "en"
19
14
  };
20
15
  this.router = router;
21
16
  this.messages = /* @__PURE__ */ new Map();
@@ -33,10 +28,6 @@ var I18nManager = class {
33
28
  return;
34
29
  }
35
30
  this.loadLanguageFromCache();
36
- if (this.config.debug) {
37
- this.config.enableDataCache = false;
38
- this.log("debug", "Data cache disabled in debug mode");
39
- }
40
31
  if (!this.messages.has(this.currentLanguage)) {
41
32
  try {
42
33
  await this.loadMessages(this.currentLanguage);
@@ -54,7 +45,7 @@ var I18nManager = class {
54
45
  */
55
46
  loadLanguageFromCache() {
56
47
  try {
57
- const cachedLang = localStorage.getItem(this.config.cacheKey);
48
+ const cachedLang = this.router.cacheManager?.get("viewlogic_lang");
58
49
  if (cachedLang && this.isValidLanguage(cachedLang)) {
59
50
  this.currentLanguage = cachedLang;
60
51
  this.log("debug", "Language loaded from cache:", cachedLang);
@@ -118,7 +109,7 @@ var I18nManager = class {
118
109
  */
119
110
  saveLanguageToCache(language) {
120
111
  try {
121
- localStorage.setItem(this.config.cacheKey, language);
112
+ this.router.cacheManager?.set("viewlogic_lang", language);
122
113
  this.log("debug", "Language saved to cache:", language);
123
114
  } catch (error) {
124
115
  this.log("warn", "Failed to save language to cache:", error);
@@ -156,12 +147,11 @@ var I18nManager = class {
156
147
  * 파일에서 메시지 로드 (캐싱 지원)
157
148
  */
158
149
  async _loadMessagesFromFile(language) {
159
- if (this.config.enableDataCache) {
160
- const cachedData = this.getDataFromCache(language);
161
- if (cachedData) {
162
- this.log("debug", "Messages loaded from cache:", language);
163
- return cachedData;
164
- }
150
+ const cacheKey = `i18n_${language}`;
151
+ const cachedData = this.router.cacheManager?.get(cacheKey);
152
+ if (cachedData) {
153
+ this.log("debug", "Messages loaded from cache:", language);
154
+ return cachedData;
165
155
  }
166
156
  try {
167
157
  const i18nPath = `${this.router.config.i18nPath}/${language}.json`;
@@ -170,9 +160,7 @@ var I18nManager = class {
170
160
  throw new Error(`HTTP error! status: ${response.status}`);
171
161
  }
172
162
  const messages = await response.json();
173
- if (this.config.enableDataCache) {
174
- this.saveDataToCache(language, messages);
175
- }
163
+ this.router.cacheManager?.set(cacheKey, messages);
176
164
  return messages;
177
165
  } catch (error) {
178
166
  this.log("error", "Failed to load messages file for:", language, error);
@@ -189,51 +177,6 @@ var I18nManager = class {
189
177
  return {};
190
178
  }
191
179
  }
192
- /**
193
- * 언어 데이터를 캐시에서 가져오기
194
- */
195
- getDataFromCache(language) {
196
- try {
197
- const cacheKey = `${this.config.dataCacheKey}_${language}_${this.config.cacheVersion}`;
198
- const cachedItem = localStorage.getItem(cacheKey);
199
- if (cachedItem) {
200
- const { data, timestamp, version } = JSON.parse(cachedItem);
201
- if (version !== this.config.cacheVersion) {
202
- this.log("debug", "Cache version mismatch, clearing:", language);
203
- localStorage.removeItem(cacheKey);
204
- return null;
205
- }
206
- const now = Date.now();
207
- const maxAge = 24 * 60 * 60 * 1e3;
208
- if (now - timestamp > maxAge) {
209
- this.log("debug", "Cache expired, removing:", language);
210
- localStorage.removeItem(cacheKey);
211
- return null;
212
- }
213
- return data;
214
- }
215
- } catch (error) {
216
- this.log("warn", "Failed to read from cache:", error);
217
- }
218
- return null;
219
- }
220
- /**
221
- * 언어 데이터를 캐시에 저장
222
- */
223
- saveDataToCache(language, data) {
224
- try {
225
- const cacheKey = `${this.config.dataCacheKey}_${language}_${this.config.cacheVersion}`;
226
- const cacheItem = {
227
- data,
228
- timestamp: Date.now(),
229
- version: this.config.cacheVersion
230
- };
231
- localStorage.setItem(cacheKey, JSON.stringify(cacheItem));
232
- this.log("debug", "Data saved to cache:", language);
233
- } catch (error) {
234
- this.log("warn", "Failed to save to cache:", error);
235
- }
236
- }
237
180
  /**
238
181
  * 메시지 번역
239
182
  */
@@ -286,12 +229,6 @@ var I18nManager = class {
286
229
  const pluralKey = count === 1 ? `${key}.singular` : `${key}.plural`;
287
230
  return this.t(pluralKey, { ...params, count });
288
231
  }
289
- /**
290
- * 사용 가능한 언어 목록
291
- */
292
- getAvailableLanguages() {
293
- return ["ko", "en"];
294
- }
295
232
  /**
296
233
  * 언어 변경 이벤트 리스너 등록
297
234
  */
@@ -376,49 +313,16 @@ var I18nManager = class {
376
313
  }
377
314
  }
378
315
  /**
379
- * 캐시 초기화 (버전 변경 시 사용)
316
+ * 캐시 초기화
380
317
  */
381
318
  clearCache() {
382
319
  try {
383
- const keys = Object.keys(localStorage);
384
- const cacheKeys = keys.filter((key) => key.startsWith(this.config.dataCacheKey));
385
- cacheKeys.forEach((key) => {
386
- localStorage.removeItem(key);
387
- });
388
- this.log("debug", "Cache cleared, removed", cacheKeys.length, "items");
320
+ const clearedCount = this.router.cacheManager?.deleteByPattern("i18n_");
321
+ this.log("debug", "Cache cleared, removed", clearedCount, "items");
389
322
  } catch (error) {
390
323
  this.log("warn", "Failed to clear cache:", error);
391
324
  }
392
325
  }
393
- /**
394
- * 캐시 상태 확인
395
- */
396
- getCacheInfo() {
397
- const info = {
398
- enabled: this.config.enableDataCache,
399
- version: this.config.cacheVersion,
400
- languages: {}
401
- };
402
- try {
403
- const keys = Object.keys(localStorage);
404
- const cacheKeys = keys.filter((key) => key.startsWith(this.config.dataCacheKey));
405
- cacheKeys.forEach((key) => {
406
- const match = key.match(new RegExp(`${this.config.dataCacheKey}_(w+)_(.+)`));
407
- if (match) {
408
- const [, language, version] = match;
409
- const cachedItem = JSON.parse(localStorage.getItem(key));
410
- info.languages[language] = {
411
- version,
412
- timestamp: cachedItem.timestamp,
413
- age: Date.now() - cachedItem.timestamp
414
- };
415
- }
416
- });
417
- } catch (error) {
418
- this.log("warn", "Failed to get cache info:", error);
419
- }
420
- return info;
421
- }
422
326
  /**
423
327
  * 시스템 초기화 (현재 언어의 메시지 로드)
424
328
  */
@@ -450,13 +354,9 @@ var AuthManager = class {
450
354
  publicRoutes: options.publicRoutes || ["login", "register", "home"],
451
355
  checkAuthFunction: options.checkAuthFunction || null,
452
356
  redirectAfterLogin: options.redirectAfterLogin || "home",
453
- // 쿠키/스토리지 설정
454
357
  authCookieName: options.authCookieName || "authToken",
455
- authFallbackCookieNames: options.authFallbackCookieNames || ["accessToken", "token", "jwt"],
456
- authStorage: options.authStorage || "cookie",
457
- authCookieOptions: options.authCookieOptions || {},
458
- authSkipValidation: options.authSkipValidation || false,
459
- debug: options.debug || false
358
+ authStorage: options.authStorage || "localStorage",
359
+ authSkipValidation: options.authSkipValidation || false
460
360
  };
461
361
  this.router = router;
462
362
  this.eventListeners = /* @__PURE__ */ new Map();
@@ -498,7 +398,7 @@ var AuthManager = class {
498
398
  return { allowed: false, reason: "custom_auth_error", error };
499
399
  }
500
400
  }
501
- const isAuthenticated = this.isUserAuthenticated();
401
+ const isAuthenticated = this.isAuthenticated();
502
402
  return {
503
403
  allowed: isAuthenticated,
504
404
  reason: isAuthenticated ? "authenticated" : "not_authenticated",
@@ -506,56 +406,40 @@ var AuthManager = class {
506
406
  };
507
407
  }
508
408
  /**
509
- * 사용자 인증 상태 확인
409
+ * JWT 토큰 검증
510
410
  */
511
- isUserAuthenticated() {
512
- this.log("debug", "\u{1F50D} Checking user authentication status");
513
- const token = localStorage.getItem("authToken") || localStorage.getItem("accessToken");
514
- if (token) {
515
- try {
516
- if (token.includes(".")) {
517
- const payload = JSON.parse(atob(token.split(".")[1]));
518
- if (payload.exp && Date.now() >= payload.exp * 1e3) {
519
- this.log("debug", "localStorage token expired, removing...");
520
- localStorage.removeItem("authToken");
521
- localStorage.removeItem("accessToken");
522
- return false;
523
- }
411
+ isTokenValid(token) {
412
+ if (!token) return false;
413
+ try {
414
+ if (token.includes(".")) {
415
+ const payload = JSON.parse(atob(token.split(".")[1]));
416
+ if (payload.exp && Date.now() >= payload.exp * 1e3) {
417
+ return false;
524
418
  }
525
- this.log("debug", "\u2705 Valid token found in localStorage");
526
- return true;
527
- } catch (error) {
528
- this.log("warn", "Invalid token in localStorage:", error);
529
419
  }
530
- }
531
- const sessionToken = sessionStorage.getItem("authToken") || sessionStorage.getItem("accessToken");
532
- if (sessionToken) {
533
- this.log("debug", "\u2705 Token found in sessionStorage");
534
420
  return true;
421
+ } catch (error) {
422
+ this.log("warn", "Token validation failed:", error);
423
+ return false;
535
424
  }
536
- const authCookie = this.getAuthCookie();
537
- if (authCookie) {
538
- try {
539
- if (authCookie.includes(".")) {
540
- const payload = JSON.parse(atob(authCookie.split(".")[1]));
541
- if (payload.exp && Date.now() >= payload.exp * 1e3) {
542
- this.log("debug", "Cookie token expired, removing...");
543
- this.removeAuthCookie();
544
- return false;
545
- }
546
- }
547
- this.log("debug", "\u2705 Valid token found in cookies");
548
- return true;
549
- } catch (error) {
550
- this.log("warn", "Cookie token validation failed:", error);
551
- }
425
+ }
426
+ /**
427
+ * 사용자 인증 상태 확인
428
+ */
429
+ isAuthenticated() {
430
+ this.log("debug", "\u{1F50D} Checking user authentication status");
431
+ const token = this.getAccessToken();
432
+ if (!token) {
433
+ this.log("debug", "\u274C No token found");
434
+ return false;
552
435
  }
553
- if (window.user || window.isAuthenticated) {
554
- this.log("debug", "\u2705 Global authentication variable found");
555
- return true;
436
+ if (!this.isTokenValid(token)) {
437
+ this.log("debug", "Token expired, removing...");
438
+ this.removeAccessToken();
439
+ return false;
556
440
  }
557
- this.log("debug", "\u274C No valid authentication found");
558
- return false;
441
+ this.log("debug", "\u2705 Valid token found");
442
+ return true;
559
443
  }
560
444
  /**
561
445
  * 공개 라우트인지 확인
@@ -581,18 +465,7 @@ var AuthManager = class {
581
465
  * 인증 쿠키 가져오기
582
466
  */
583
467
  getAuthCookie() {
584
- const primaryCookie = this.getCookieValue(this.config.authCookieName);
585
- if (primaryCookie) {
586
- return primaryCookie;
587
- }
588
- for (const cookieName of this.config.authFallbackCookieNames) {
589
- const cookieValue = this.getCookieValue(cookieName);
590
- if (cookieValue) {
591
- this.log("debug", `Found auth token in fallback cookie: ${cookieName}`);
592
- return cookieValue;
593
- }
594
- }
595
- return null;
468
+ return this.getCookieValue(this.config.authCookieName);
596
469
  }
597
470
  /**
598
471
  * 쿠키 값 가져오기
@@ -609,24 +482,18 @@ var AuthManager = class {
609
482
  * 인증 쿠키 제거
610
483
  */
611
484
  removeAuthCookie() {
612
- const cookiesToRemove = [this.config.authCookieName, ...this.config.authFallbackCookieNames];
613
- cookiesToRemove.forEach((cookieName) => {
614
- document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
615
- document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${window.location.pathname};`;
616
- });
617
- this.log("debug", "Auth cookies removed");
485
+ document.cookie = `${this.config.authCookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
486
+ this.log("debug", "Auth cookie removed");
618
487
  }
619
488
  /**
620
489
  * 액세스 토큰 가져오기
621
490
  */
622
491
  getAccessToken() {
623
- let token = localStorage.getItem("authToken") || localStorage.getItem("accessToken");
492
+ let token = localStorage.getItem("authToken");
624
493
  if (token) return token;
625
- token = sessionStorage.getItem("authToken") || sessionStorage.getItem("accessToken");
494
+ token = sessionStorage.getItem("authToken");
626
495
  if (token) return token;
627
- token = this.getAuthCookie();
628
- if (token) return token;
629
- return null;
496
+ return this.getAuthCookie();
630
497
  }
631
498
  /**
632
499
  * 액세스 토큰 설정
@@ -642,17 +509,9 @@ var AuthManager = class {
642
509
  skipValidation = this.config.authSkipValidation
643
510
  } = options;
644
511
  try {
645
- if (!skipValidation && token.includes(".")) {
646
- try {
647
- const payload = JSON.parse(atob(token.split(".")[1]));
648
- if (payload.exp && Date.now() >= payload.exp * 1e3) {
649
- this.log("warn", "\u274C Token is expired");
650
- return false;
651
- }
652
- this.log("debug", "\u2705 JWT token validated");
653
- } catch (error) {
654
- this.log("warn", "\u26A0\uFE0F JWT validation failed, but proceeding:", error.message);
655
- }
512
+ if (!skipValidation && !this.isTokenValid(token)) {
513
+ this.log("warn", "\u274C Token is expired or invalid");
514
+ return false;
656
515
  }
657
516
  switch (storage) {
658
517
  case "localStorage":
@@ -664,7 +523,7 @@ var AuthManager = class {
664
523
  this.log("debug", "Token saved to sessionStorage");
665
524
  break;
666
525
  case "cookie":
667
- this.setAuthCookie(token, cookieOptions);
526
+ this.setAuthCookie(token);
668
527
  break;
669
528
  default:
670
529
  localStorage.setItem("authToken", token);
@@ -684,41 +543,25 @@ var AuthManager = class {
684
543
  /**
685
544
  * 인증 쿠키 설정
686
545
  */
687
- setAuthCookie(token, options = {}) {
688
- const {
689
- cookieName = this.config.authCookieName,
690
- secure = window.location.protocol === "https:",
691
- sameSite = "Strict",
692
- path = "/",
693
- domain = null
694
- } = options;
695
- let cookieString = `${cookieName}=${encodeURIComponent(token)}; path=${path}`;
546
+ setAuthCookie(token) {
547
+ const secure = window.location.protocol === "https:";
548
+ let cookieString = `${this.config.authCookieName}=${encodeURIComponent(token)}; path=/; SameSite=Strict`;
696
549
  if (secure) {
697
550
  cookieString += "; Secure";
698
551
  }
699
- if (sameSite) {
700
- cookieString += `; SameSite=${sameSite}`;
701
- }
702
- if (domain) {
703
- cookieString += `; Domain=${domain}`;
704
- }
705
- try {
706
- if (token.includes(".")) {
707
- try {
708
- const payload = JSON.parse(atob(token.split(".")[1]));
709
- if (payload.exp) {
710
- const expireDate = new Date(payload.exp * 1e3);
711
- cookieString += `; Expires=${expireDate.toUTCString()}`;
712
- }
713
- } catch (error) {
714
- this.log("Could not extract expiration from JWT token");
552
+ if (token.includes(".")) {
553
+ try {
554
+ const payload = JSON.parse(atob(token.split(".")[1]));
555
+ if (payload.exp) {
556
+ const expireDate = new Date(payload.exp * 1e3);
557
+ cookieString += `; Expires=${expireDate.toUTCString()}`;
715
558
  }
559
+ } catch (error) {
560
+ this.log("warn", "Could not extract expiration from JWT token");
716
561
  }
717
- } catch (error) {
718
- this.log("Token processing error:", error);
719
562
  }
720
563
  document.cookie = cookieString;
721
- this.log(`Auth cookie set: ${cookieName}`);
564
+ this.log("debug", "Auth cookie set");
722
565
  }
723
566
  /**
724
567
  * 토큰 제거
@@ -739,9 +582,7 @@ var AuthManager = class {
739
582
  case "all":
740
583
  default:
741
584
  localStorage.removeItem("authToken");
742
- localStorage.removeItem("accessToken");
743
585
  sessionStorage.removeItem("authToken");
744
- sessionStorage.removeItem("accessToken");
745
586
  this.removeAuthCookie();
746
587
  break;
747
588
  }
@@ -751,7 +592,7 @@ var AuthManager = class {
751
592
  /**
752
593
  * 로그인 성공 처리
753
594
  */
754
- handleLoginSuccess(targetRoute = null) {
595
+ loginSuccess(targetRoute = null) {
755
596
  const redirectRoute = targetRoute || this.config.redirectAfterLogin;
756
597
  this.log(`\u{1F389} Login success, redirecting to: ${redirectRoute}`);
757
598
  this.emitAuthEvent("login_success", { targetRoute: redirectRoute });
@@ -763,11 +604,9 @@ var AuthManager = class {
763
604
  /**
764
605
  * 로그아웃 처리
765
606
  */
766
- handleLogout() {
607
+ logout() {
767
608
  this.log("\u{1F44B} Logging out user");
768
609
  this.removeAccessToken();
769
- if (window.user) window.user = null;
770
- if (window.isAuthenticated) window.isAuthenticated = false;
771
610
  this.emitAuthEvent("logout", {});
772
611
  if (this.router && typeof this.router.navigateTo === "function") {
773
612
  this.router.navigateTo(this.config.loginRoute);
@@ -824,7 +663,7 @@ var AuthManager = class {
824
663
  getAuthStats() {
825
664
  return {
826
665
  enabled: this.config.enabled,
827
- isAuthenticated: this.isUserAuthenticated(),
666
+ isAuthenticated: this.isAuthenticated(),
828
667
  hasToken: !!this.getAccessToken(),
829
668
  protectedRoutesCount: this.config.protectedRoutes.length,
830
669
  protectedPrefixesCount: this.config.protectedPrefixes.length,
@@ -850,9 +689,8 @@ var CacheManager = class {
850
689
  // 'memory' 또는 'lru'
851
690
  cacheTTL: options.cacheTTL || 3e5,
852
691
  // 5분 (밀리초)
853
- maxCacheSize: options.maxCacheSize || 50,
692
+ maxCacheSize: options.maxCacheSize || 50
854
693
  // LRU 캐시 최대 크기
855
- debug: options.debug || false
856
694
  };
857
695
  this.router = router;
858
696
  this.cache = /* @__PURE__ */ new Map();
@@ -871,7 +709,7 @@ var CacheManager = class {
871
709
  /**
872
710
  * 캐시에 값 저장
873
711
  */
874
- setCache(key, value) {
712
+ set(key, value) {
875
713
  const now = Date.now();
876
714
  if (this.config.cacheMode === "lru") {
877
715
  if (this.cache.size >= this.config.maxCacheSize && !this.cache.has(key)) {
@@ -895,7 +733,7 @@ var CacheManager = class {
895
733
  /**
896
734
  * 캐시에서 값 가져오기
897
735
  */
898
- getFromCache(key) {
736
+ get(key) {
899
737
  const now = Date.now();
900
738
  const timestamp = this.cacheTimestamps.get(key);
901
739
  if (timestamp && now - timestamp > this.config.cacheTTL) {
@@ -928,13 +766,13 @@ var CacheManager = class {
928
766
  /**
929
767
  * 캐시에 키가 있는지 확인
930
768
  */
931
- hasCache(key) {
932
- return this.cache.has(key) && this.getFromCache(key) !== null;
769
+ has(key) {
770
+ return this.cache.has(key) && this.get(key) !== null;
933
771
  }
934
772
  /**
935
773
  * 특정 키 패턴의 캐시 삭제
936
774
  */
937
- invalidateByPattern(pattern) {
775
+ deleteByPattern(pattern) {
938
776
  const keysToDelete = [];
939
777
  for (const key of this.cache.keys()) {
940
778
  if (key.includes(pattern) || key.startsWith(pattern)) {
@@ -951,13 +789,13 @@ var CacheManager = class {
951
789
  }
952
790
  }
953
791
  });
954
- this.log("debug", `\u{1F9F9} Invalidated ${keysToDelete.length} cache entries matching: ${pattern}`);
792
+ this.log("debug", `\u{1F9F9} Deleted ${keysToDelete.length} cache entries matching: ${pattern}`);
955
793
  return keysToDelete.length;
956
794
  }
957
795
  /**
958
- * 특정 컴포넌트 캐시 무효화
796
+ * 특정 컴포넌트 캐시 삭제
959
797
  */
960
- invalidateComponentCache(routeName) {
798
+ deleteComponent(routeName) {
961
799
  const patterns = [
962
800
  `component_${routeName}`,
963
801
  `script_${routeName}`,
@@ -967,27 +805,27 @@ var CacheManager = class {
967
805
  ];
968
806
  let totalInvalidated = 0;
969
807
  patterns.forEach((pattern) => {
970
- totalInvalidated += this.invalidateByPattern(pattern);
808
+ totalInvalidated += this.deleteByPattern(pattern);
971
809
  });
972
- this.log(`\u{1F504} Invalidated component cache for route: ${routeName} (${totalInvalidated} entries)`);
810
+ this.log(`\u{1F504} Deleted component cache for route: ${routeName} (${totalInvalidated} entries)`);
973
811
  return totalInvalidated;
974
812
  }
975
813
  /**
976
814
  * 모든 컴포넌트 캐시 삭제
977
815
  */
978
- clearComponentCache() {
816
+ deleteAllComponents() {
979
817
  const componentPatterns = ["component_", "script_", "template_", "style_", "layout_"];
980
818
  let totalCleared = 0;
981
819
  componentPatterns.forEach((pattern) => {
982
- totalCleared += this.invalidateByPattern(pattern);
820
+ totalCleared += this.deleteByPattern(pattern);
983
821
  });
984
- this.log(`\u{1F9FD} Cleared all component caches (${totalCleared} entries)`);
822
+ this.log(`\u{1F9FD} Deleted all component caches (${totalCleared} entries)`);
985
823
  return totalCleared;
986
824
  }
987
825
  /**
988
826
  * 전체 캐시 삭제
989
827
  */
990
- clearCache() {
828
+ clearAll() {
991
829
  const size = this.cache.size;
992
830
  this.cache.clear();
993
831
  this.cacheTimestamps.clear();
@@ -998,7 +836,7 @@ var CacheManager = class {
998
836
  /**
999
837
  * 만료된 캐시 항목들 정리
1000
838
  */
1001
- cleanExpiredCache() {
839
+ cleanExpired() {
1002
840
  const now = Date.now();
1003
841
  const expiredKeys = [];
1004
842
  for (const [key, timestamp] of this.cacheTimestamps.entries()) {
@@ -1024,15 +862,15 @@ var CacheManager = class {
1024
862
  /**
1025
863
  * 캐시 통계 정보
1026
864
  */
1027
- getCacheStats() {
865
+ getStats() {
1028
866
  return {
1029
867
  size: this.cache.size,
1030
868
  maxSize: this.config.maxCacheSize,
1031
869
  mode: this.config.cacheMode,
1032
870
  ttl: this.config.cacheTTL,
1033
871
  memoryUsage: this.getMemoryUsage(),
1034
- hitRatio: this.getHitRatio(),
1035
- categories: this.getCategorizedStats()
872
+ hitRatio: this.getHitRate(),
873
+ categories: this.getStatsByCategory()
1036
874
  };
1037
875
  }
1038
876
  /**
@@ -1059,14 +897,14 @@ var CacheManager = class {
1059
897
  /**
1060
898
  * 히트 비율 계산 (간단한 추정)
1061
899
  */
1062
- getHitRatio() {
900
+ getHitRate() {
1063
901
  const ratio = this.cache.size > 0 ? Math.min(this.cache.size / this.config.maxCacheSize, 1) : 0;
1064
902
  return Math.round(ratio * 100);
1065
903
  }
1066
904
  /**
1067
905
  * 카테고리별 캐시 통계
1068
906
  */
1069
- getCategorizedStats() {
907
+ getStatsByCategory() {
1070
908
  const categories = {
1071
909
  components: 0,
1072
910
  scripts: 0,
@@ -1088,14 +926,14 @@ var CacheManager = class {
1088
926
  /**
1089
927
  * 캐시 키 목록 반환
1090
928
  */
1091
- getCacheKeys() {
929
+ getKeys() {
1092
930
  return Array.from(this.cache.keys());
1093
931
  }
1094
932
  /**
1095
933
  * 특정 패턴의 캐시 키들 반환
1096
934
  */
1097
- getCacheKeysByPattern(pattern) {
1098
- return this.getCacheKeys().filter(
935
+ getKeysByPattern(pattern) {
936
+ return this.getKeys().filter(
1099
937
  (key) => key.includes(pattern) || key.startsWith(pattern)
1100
938
  );
1101
939
  }
@@ -1107,7 +945,7 @@ var CacheManager = class {
1107
945
  clearInterval(this.cleanupInterval);
1108
946
  }
1109
947
  this.cleanupInterval = setInterval(() => {
1110
- this.cleanExpiredCache();
948
+ this.cleanExpired();
1111
949
  }, interval);
1112
950
  this.log(`\u{1F916} Auto cleanup started (interval: ${interval}ms)`);
1113
951
  }
@@ -1126,27 +964,18 @@ var CacheManager = class {
1126
964
  */
1127
965
  destroy() {
1128
966
  this.stopAutoCleanup();
1129
- this.clearCache();
967
+ this.clearAll();
1130
968
  this.log("debug", "CacheManager destroyed");
1131
969
  }
1132
970
  };
1133
971
 
1134
972
  // src/plugins/QueryManager.js
1135
973
  var QueryManager = class {
1136
- constructor(router, options = {}) {
1137
- this.config = {
1138
- enableParameterValidation: options.enableParameterValidation !== false,
1139
- logSecurityWarnings: options.logSecurityWarnings !== false,
1140
- maxParameterLength: options.maxParameterLength || 1e3,
1141
- maxArraySize: options.maxArraySize || 100,
1142
- maxParameterCount: options.maxParameterCount || 50,
1143
- allowedKeyPattern: options.allowedKeyPattern || /^[a-zA-Z0-9_\-]+$/,
1144
- debug: options.debug || false
1145
- };
974
+ constructor(router) {
1146
975
  this.router = router;
1147
976
  this.currentQueryParams = {};
1148
977
  this.currentRouteParams = {};
1149
- this.log("info", "QueryManager initialized with config:", this.config);
978
+ this.log("debug", "QueryManager initialized");
1150
979
  }
1151
980
  /**
1152
981
  * 로깅 래퍼 메서드
@@ -1156,97 +985,6 @@ var QueryManager = class {
1156
985
  this.router.errorHandler.log(level, "QueryManager", ...args);
1157
986
  }
1158
987
  }
1159
- /**
1160
- * 파라미터 값 sanitize (XSS, SQL Injection 방어)
1161
- */
1162
- sanitizeParameter(value) {
1163
- if (typeof value !== "string") return value;
1164
- let sanitized = value.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, "");
1165
- const sqlPatterns = [
1166
- /(\b(union|select|insert|update|delete|drop|create|alter|exec|execute|sp_|xp_)\b)/gi,
1167
- /(;|\||&|\*|%|<|>)/g,
1168
- // 위험한 특수문자
1169
- /(--|\/\*|\*\/)/g,
1170
- // SQL 주석
1171
- /(\bor\b.*\b=\b|\band\b.*\b=\b)/gi,
1172
- // OR/AND 조건문
1173
- /('.*'|".*")/g,
1174
- // 따옴표로 둘러싸인 문자열
1175
- /(\\\w+)/g
1176
- // 백슬래시 이스케이프
1177
- ];
1178
- for (const pattern of sqlPatterns) {
1179
- sanitized = sanitized.replace(pattern, "");
1180
- }
1181
- sanitized = sanitized.replace(/[<>'"&]{2,}/g, "");
1182
- if (sanitized.length > this.config.maxParameterLength) {
1183
- sanitized = sanitized.substring(0, this.config.maxParameterLength);
1184
- }
1185
- return sanitized.trim();
1186
- }
1187
- /**
1188
- * 파라미터 유효성 검증
1189
- */
1190
- validateParameter(key, value) {
1191
- if (!this.config.enableParameterValidation) {
1192
- return true;
1193
- }
1194
- if (typeof key !== "string" || key.length === 0) {
1195
- return false;
1196
- }
1197
- if (!this.config.allowedKeyPattern.test(key)) {
1198
- if (this.config.logSecurityWarnings) {
1199
- console.warn(`Invalid parameter key format: ${key}`);
1200
- }
1201
- return false;
1202
- }
1203
- if (key.length > 50) {
1204
- if (this.config.logSecurityWarnings) {
1205
- console.warn(`Parameter key too long: ${key}`);
1206
- }
1207
- return false;
1208
- }
1209
- if (value !== null && value !== void 0) {
1210
- if (typeof value === "string") {
1211
- if (value.length > this.config.maxParameterLength) {
1212
- if (this.config.logSecurityWarnings) {
1213
- console.warn(`Parameter value too long for key: ${key}`);
1214
- }
1215
- return false;
1216
- }
1217
- const dangerousPatterns = [
1218
- /<script|<iframe|<object|<embed/gi,
1219
- /javascript:|vbscript:|data:/gi,
1220
- /union.*select|insert.*into|delete.*from/gi,
1221
- /\.\.\//g,
1222
- // 경로 탐색 공격
1223
- /[<>'"&]{3,}/g
1224
- // 연속된 특수문자
1225
- ];
1226
- for (const pattern of dangerousPatterns) {
1227
- if (pattern.test(value)) {
1228
- if (this.config.logSecurityWarnings) {
1229
- console.warn(`Dangerous pattern detected in parameter ${key}:`, value);
1230
- }
1231
- return false;
1232
- }
1233
- }
1234
- } else if (Array.isArray(value)) {
1235
- if (value.length > this.config.maxArraySize) {
1236
- if (this.config.logSecurityWarnings) {
1237
- console.warn(`Parameter array too large for key: ${key}`);
1238
- }
1239
- return false;
1240
- }
1241
- for (const item of value) {
1242
- if (!this.validateParameter(`${key}[]`, item)) {
1243
- return false;
1244
- }
1245
- }
1246
- }
1247
- }
1248
- return true;
1249
- }
1250
988
  /**
1251
989
  * 쿼리스트링 파싱
1252
990
  */
@@ -1255,56 +993,22 @@ var QueryManager = class {
1255
993
  if (!queryString) return params;
1256
994
  const pairs = queryString.split("&");
1257
995
  for (const pair of pairs) {
996
+ const [rawKey, rawValue] = pair.split("=");
997
+ if (!rawKey) continue;
1258
998
  try {
1259
- const [rawKey, rawValue] = pair.split("=");
1260
- if (!rawKey) continue;
1261
- let key, value;
1262
- try {
1263
- key = decodeURIComponent(rawKey);
1264
- value = rawValue ? decodeURIComponent(rawValue) : "";
1265
- } catch (e) {
1266
- this.log("warn", "Failed to decode URI component:", pair);
1267
- continue;
1268
- }
1269
- if (!this.validateParameter(key, value)) {
1270
- this.log("warn", `Parameter rejected by security filter: ${key}`);
1271
- continue;
1272
- }
1273
- const sanitizedValue = this.sanitizeParameter(value);
999
+ const key = decodeURIComponent(rawKey);
1000
+ const value = rawValue ? decodeURIComponent(rawValue) : "";
1274
1001
  if (key.endsWith("[]")) {
1275
1002
  const arrayKey = key.slice(0, -2);
1276
- if (!this.validateParameter(arrayKey, [])) {
1277
- continue;
1278
- }
1279
1003
  if (!params[arrayKey]) params[arrayKey] = [];
1280
- if (params[arrayKey].length < this.config.maxArraySize) {
1281
- params[arrayKey].push(sanitizedValue);
1282
- } else {
1283
- if (this.config.logSecurityWarnings) {
1284
- console.warn(`Array parameter ${arrayKey} size limit exceeded`);
1285
- }
1286
- }
1004
+ params[arrayKey].push(value);
1287
1005
  } else {
1288
- params[key] = sanitizedValue;
1006
+ params[key] = value;
1289
1007
  }
1290
1008
  } catch (error) {
1291
- this.log("error", "Error parsing query parameter:", pair, error);
1009
+ this.log("warn", "Failed to decode query parameter:", pair);
1292
1010
  }
1293
1011
  }
1294
- const paramCount = Object.keys(params).length;
1295
- if (paramCount > this.config.maxParameterCount) {
1296
- if (this.config.logSecurityWarnings) {
1297
- console.warn(`Too many parameters (${paramCount}). Limiting to first ${this.config.maxParameterCount}.`);
1298
- }
1299
- const limitedParams = {};
1300
- let count = 0;
1301
- for (const [key, value] of Object.entries(params)) {
1302
- if (count >= this.config.maxParameterCount) break;
1303
- limitedParams[key] = value;
1304
- count++;
1305
- }
1306
- return limitedParams;
1307
- }
1308
1012
  return params;
1309
1013
  }
1310
1014
  /**
@@ -1358,36 +1062,17 @@ var QueryManager = class {
1358
1062
  */
1359
1063
  setQueryParams(params, replace = false) {
1360
1064
  if (!params || typeof params !== "object") {
1361
- console.warn("Invalid parameters object provided to setQueryParams");
1065
+ this.log("warn", "Invalid parameters object provided to setQueryParams");
1362
1066
  return;
1363
1067
  }
1364
1068
  const currentParams = replace ? {} : { ...this.currentQueryParams };
1365
- const sanitizedParams = {};
1366
1069
  for (const [key, value] of Object.entries(params)) {
1367
- if (!this.validateParameter(key, value)) {
1368
- console.warn(`Parameter ${key} rejected by security filter`);
1369
- continue;
1370
- }
1371
- if (value !== void 0 && value !== null) {
1372
- if (Array.isArray(value)) {
1373
- sanitizedParams[key] = value.map((item) => this.sanitizeParameter(item));
1374
- } else {
1375
- sanitizedParams[key] = this.sanitizeParameter(value);
1376
- }
1377
- }
1378
- }
1379
- Object.assign(currentParams, sanitizedParams);
1380
- for (const [key, value] of Object.entries(currentParams)) {
1381
- if (value === void 0 || value === null || value === "") {
1070
+ if (value !== void 0 && value !== null && value !== "") {
1071
+ currentParams[key] = value;
1072
+ } else {
1382
1073
  delete currentParams[key];
1383
1074
  }
1384
1075
  }
1385
- const paramCount = Object.keys(currentParams).length;
1386
- if (paramCount > this.config.maxParameterCount) {
1387
- if (this.config.logSecurityWarnings) {
1388
- console.warn(`Too many parameters after update (${paramCount}). Some parameters may be dropped.`);
1389
- }
1390
- }
1391
1076
  this.currentQueryParams = currentParams;
1392
1077
  this.updateURL();
1393
1078
  }
@@ -1466,8 +1151,6 @@ var QueryManager = class {
1466
1151
  getStats() {
1467
1152
  return {
1468
1153
  currentParams: Object.keys(this.currentQueryParams).length,
1469
- maxAllowed: this.config.maxParameterCount,
1470
- validationEnabled: this.config.enableParameterValidation,
1471
1154
  currentQueryString: this.buildQueryString(this.currentQueryParams)
1472
1155
  };
1473
1156
  }
@@ -1938,7 +1621,7 @@ var ComponentLoader = class {
1938
1621
  throw new Error("Component name must be a non-empty string");
1939
1622
  }
1940
1623
  const cacheKey = `component_${componentName}`;
1941
- const cachedComponent = this.router?.cacheManager?.getFromCache(cacheKey);
1624
+ const cachedComponent = this.router?.cacheManager?.get(cacheKey);
1942
1625
  if (cachedComponent) {
1943
1626
  this.log("debug", `Component '${componentName}' loaded from cache`);
1944
1627
  return cachedComponent;
@@ -1951,7 +1634,7 @@ var ComponentLoader = class {
1951
1634
  try {
1952
1635
  const component = await loadPromise;
1953
1636
  if (component && this.router?.cacheManager) {
1954
- this.router.cacheManager.setCache(cacheKey, component);
1637
+ this.router.cacheManager.set(cacheKey, component);
1955
1638
  this.log("debug", `Component '${componentName}' cached successfully`);
1956
1639
  }
1957
1640
  return component;
@@ -2025,11 +1708,11 @@ var ComponentLoader = class {
2025
1708
  async _loadProductionComponents() {
2026
1709
  try {
2027
1710
  const componentsPath = `${this.router?.config?.routesPath || "/routes"}/_components.js`;
2028
- this.log("info", "[PRODUCTION] Loading unified components from:", componentsPath);
1711
+ this.log("debug", "[PRODUCTION] Loading unified components from:", componentsPath);
2029
1712
  const componentsModule = await import(componentsPath);
2030
1713
  if (typeof componentsModule.registerComponents === "function") {
2031
1714
  this.unifiedComponents = componentsModule.components || {};
2032
- this.log("info", `[PRODUCTION] Unified components loaded: ${Object.keys(this.unifiedComponents).length} components`);
1715
+ this.log("debug", `[PRODUCTION] Unified components loaded: ${Object.keys(this.unifiedComponents).length} components`);
2033
1716
  return this.unifiedComponents;
2034
1717
  } else {
2035
1718
  throw new Error("registerComponents function not found in components module");
@@ -2047,10 +1730,10 @@ var ComponentLoader = class {
2047
1730
  const namesToLoad = componentNames || [];
2048
1731
  const components = {};
2049
1732
  if (namesToLoad.length === 0) {
2050
- this.log("info", "[DEVELOPMENT] No components to load");
1733
+ this.log("debug", "[DEVELOPMENT] No components to load");
2051
1734
  return components;
2052
1735
  }
2053
- this.log("info", `[DEVELOPMENT] Loading individual components: ${namesToLoad.join(", ")}`);
1736
+ this.log("debug", `[DEVELOPMENT] Loading individual components: ${namesToLoad.join(", ")}`);
2054
1737
  for (const name of namesToLoad) {
2055
1738
  try {
2056
1739
  const component = await this.loadComponent(name);
@@ -2061,7 +1744,7 @@ var ComponentLoader = class {
2061
1744
  this.log("warn", `[DEVELOPMENT] Failed to load component ${name}:`, loadError.message);
2062
1745
  }
2063
1746
  }
2064
- this.log("info", `[DEVELOPMENT] Individual components loaded: ${Object.keys(components).length} components`);
1747
+ this.log("debug", `[DEVELOPMENT] Individual components loaded: ${Object.keys(components).length} components`);
2065
1748
  return components;
2066
1749
  }
2067
1750
  /**
@@ -2083,7 +1766,7 @@ var ComponentLoader = class {
2083
1766
  if (!layout || typeof layout !== "string") return /* @__PURE__ */ new Set();
2084
1767
  if (!layoutName || typeof layoutName !== "string") return /* @__PURE__ */ new Set();
2085
1768
  const cacheKey = `layout_components_${layoutName}`;
2086
- const cachedComponents = this.router?.cacheManager?.getFromCache(cacheKey);
1769
+ const cachedComponents = this.router?.cacheManager?.get(cacheKey);
2087
1770
  if (cachedComponents) {
2088
1771
  this.log("debug", `Using cached layout components for '${layoutName}'`);
2089
1772
  return cachedComponents;
@@ -2091,7 +1774,7 @@ var ComponentLoader = class {
2091
1774
  const componentSet = /* @__PURE__ */ new Set();
2092
1775
  this._extractComponentsFromContent(layout, componentSet);
2093
1776
  if (this.router?.cacheManager) {
2094
- this.router.cacheManager.setCache(cacheKey, componentSet);
1777
+ this.router.cacheManager.set(cacheKey, componentSet);
2095
1778
  this.log("debug", `Cached layout components for '${layoutName}': ${Array.from(componentSet).join(", ")}`);
2096
1779
  }
2097
1780
  return componentSet;
@@ -2298,7 +1981,7 @@ ${template}`;
2298
1981
  */
2299
1982
  async createVueComponent(routeName) {
2300
1983
  const cacheKey = `component_${routeName}`;
2301
- const cached = this.router.cacheManager?.getFromCache(cacheKey);
1984
+ const cached = this.router.cacheManager?.get(cacheKey);
2302
1985
  if (cached) {
2303
1986
  return cached;
2304
1987
  }
@@ -2333,7 +2016,7 @@ ${template}`;
2333
2016
  if (!isProduction) {
2334
2017
  const layoutName = script.layout || this.config.defaultLayout;
2335
2018
  componentNames = this.componentLoader.getComponentNames(template, layout, layoutName);
2336
- this.log("info", `[DEVELOPMENT] Discovered components for route '${routeName}':`, componentNames);
2019
+ this.log("debug", `[DEVELOPMENT] Discovered components for route '${routeName}':`, componentNames);
2337
2020
  }
2338
2021
  loadedComponents = await this.componentLoader.loadAllComponents(componentNames);
2339
2022
  this.log("debug", `Components loaded successfully for route: ${routeName}`);
@@ -2401,15 +2084,26 @@ ${template}`;
2401
2084
  }
2402
2085
  },
2403
2086
  // 인증 관련
2404
- $isAuthenticated: () => router.authManager?.isUserAuthenticated() || false,
2405
- $logout: () => router.authManager ? router.navigateTo(router.authManager.handleLogout()) : null,
2406
- $loginSuccess: (target) => router.authManager ? router.navigateTo(router.authManager.handleLoginSuccess(target)) : null,
2087
+ $isAuthenticated: () => router.authManager?.isAuthenticated() || false,
2088
+ $logout: () => router.authManager ? router.navigateTo(router.authManager.logout()) : null,
2089
+ $loginSuccess: (target) => router.authManager ? router.navigateTo(router.authManager.loginSuccess(target)) : null,
2407
2090
  $checkAuth: (route) => router.authManager ? router.authManager.checkAuthentication(route) : Promise.resolve({ allowed: true, reason: "auth_disabled" }),
2408
2091
  $getToken: () => router.authManager?.getAccessToken() || null,
2409
2092
  $setToken: (token, options) => router.authManager?.setAccessToken(token, options) || false,
2410
2093
  $removeToken: (storage) => router.authManager?.removeAccessToken(storage) || null,
2411
2094
  $getAuthCookie: () => router.authManager?.getAuthCookie() || null,
2412
2095
  $getCookie: (name) => router.authManager?.getCookieValue(name) || null,
2096
+ // 상태 관리
2097
+ $state: {
2098
+ get: (key, defaultValue) => router.stateHandler?.get(key, defaultValue),
2099
+ set: (key, value) => router.stateHandler?.set(key, value),
2100
+ has: (key) => router.stateHandler?.has(key) || false,
2101
+ delete: (key) => router.stateHandler?.delete(key) || false,
2102
+ update: (updates) => router.stateHandler?.update(updates),
2103
+ watch: (key, callback) => router.stateHandler?.watch(key, callback),
2104
+ unwatch: (key, callback) => router.stateHandler?.unwatch(key, callback),
2105
+ getAll: () => router.stateHandler?.getAll() || {}
2106
+ },
2413
2107
  // 데이터 fetch (ApiHandler 래퍼)
2414
2108
  async $fetchData(dataConfig = null) {
2415
2109
  const configToUse = dataConfig || script.dataURL;
@@ -2447,7 +2141,7 @@ ${template}`;
2447
2141
  if (!isProduction && style) {
2448
2142
  component._style = style;
2449
2143
  }
2450
- this.router.cacheManager?.setCache(cacheKey, component);
2144
+ this.router.cacheManager?.set(cacheKey, component);
2451
2145
  return component;
2452
2146
  }
2453
2147
  /**
@@ -2507,7 +2201,7 @@ var ErrorHandler = class {
2507
2201
  info: 2,
2508
2202
  debug: 3
2509
2203
  };
2510
- this.log("info", "ErrorHandler", "ErrorHandler initialized with config:", this.config);
2204
+ this.log("debug", "ErrorHandler", "ErrorHandler initialized with config:", this.config);
2511
2205
  }
2512
2206
  /**
2513
2207
  * 라우트 에러 처리
@@ -2666,8 +2360,8 @@ var ErrorHandler = class {
2666
2360
  userAgent: navigator.userAgent,
2667
2361
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2668
2362
  routerConfig: {
2669
- environment: this.router.config.environment,
2670
- mode: this.router.config.mode
2363
+ environment: this.router?.config?.environment || "unknown",
2364
+ mode: this.router?.config?.mode || "unknown"
2671
2365
  }
2672
2366
  };
2673
2367
  this.error("ErrorHandler", "\uB77C\uC6B0\uD130 \uC5D0\uB7EC \uB9AC\uD3EC\uD2B8:", errorReport);
@@ -2762,6 +2456,146 @@ var ErrorHandler = class {
2762
2456
  }
2763
2457
  };
2764
2458
 
2459
+ // src/core/StateHandler.js
2460
+ var StateHandler = class {
2461
+ constructor(router) {
2462
+ this.router = router;
2463
+ this.state = {};
2464
+ this.listeners = /* @__PURE__ */ new Map();
2465
+ this.log("debug", "StateHandler initialized");
2466
+ }
2467
+ /**
2468
+ * 로깅 래퍼 메서드
2469
+ */
2470
+ log(level, ...args) {
2471
+ if (this.router?.errorHandler) {
2472
+ this.router.errorHandler.log(level, "StateHandler", ...args);
2473
+ }
2474
+ }
2475
+ /**
2476
+ * 상태 값 설정
2477
+ */
2478
+ set(key, value) {
2479
+ const oldValue = this.state[key];
2480
+ this.state[key] = value;
2481
+ this.emitChange(key, value, oldValue);
2482
+ this.log("debug", `State set: ${key}`, value);
2483
+ return value;
2484
+ }
2485
+ /**
2486
+ * 상태 값 가져오기
2487
+ */
2488
+ get(key, defaultValue = void 0) {
2489
+ const value = this.state.hasOwnProperty(key) ? this.state[key] : defaultValue;
2490
+ this.log("debug", `State get: ${key}`, value);
2491
+ return value;
2492
+ }
2493
+ /**
2494
+ * 상태 존재 확인
2495
+ */
2496
+ has(key) {
2497
+ return this.state.hasOwnProperty(key);
2498
+ }
2499
+ /**
2500
+ * 상태 삭제
2501
+ */
2502
+ delete(key) {
2503
+ if (this.has(key)) {
2504
+ const oldValue = this.state[key];
2505
+ delete this.state[key];
2506
+ this.emitChange(key, void 0, oldValue);
2507
+ this.log("debug", `State deleted: ${key}`);
2508
+ return true;
2509
+ }
2510
+ return false;
2511
+ }
2512
+ /**
2513
+ * 모든 상태 초기화
2514
+ */
2515
+ clear() {
2516
+ const keys = Object.keys(this.state);
2517
+ this.state = {};
2518
+ keys.forEach((key) => {
2519
+ this.emitChange(key, void 0, this.state[key]);
2520
+ });
2521
+ this.log("debug", "All state cleared");
2522
+ return keys.length;
2523
+ }
2524
+ /**
2525
+ * 여러 상태 한 번에 설정
2526
+ */
2527
+ update(updates) {
2528
+ if (!updates || typeof updates !== "object") {
2529
+ this.log("warn", "Invalid updates object provided");
2530
+ return;
2531
+ }
2532
+ Object.entries(updates).forEach(([key, value]) => {
2533
+ this.set(key, value);
2534
+ });
2535
+ }
2536
+ /**
2537
+ * 모든 상태 반환
2538
+ */
2539
+ getAll() {
2540
+ return { ...this.state };
2541
+ }
2542
+ /**
2543
+ * 상태 변경 리스너 등록
2544
+ */
2545
+ watch(key, callback) {
2546
+ if (!this.listeners.has(key)) {
2547
+ this.listeners.set(key, []);
2548
+ }
2549
+ this.listeners.get(key).push(callback);
2550
+ this.log("debug", `Watcher added for: ${key}`);
2551
+ }
2552
+ /**
2553
+ * 상태 변경 리스너 제거
2554
+ */
2555
+ unwatch(key, callback) {
2556
+ if (this.listeners.has(key)) {
2557
+ const callbacks = this.listeners.get(key);
2558
+ const index = callbacks.indexOf(callback);
2559
+ if (index > -1) {
2560
+ callbacks.splice(index, 1);
2561
+ this.log("debug", `Watcher removed for: ${key}`);
2562
+ }
2563
+ }
2564
+ }
2565
+ /**
2566
+ * 상태 변경 이벤트 발생
2567
+ */
2568
+ emitChange(key, newValue, oldValue) {
2569
+ if (this.listeners.has(key)) {
2570
+ this.listeners.get(key).forEach((callback) => {
2571
+ try {
2572
+ callback(newValue, oldValue, key);
2573
+ } catch (error) {
2574
+ this.log("error", "State watcher error:", error);
2575
+ }
2576
+ });
2577
+ }
2578
+ }
2579
+ /**
2580
+ * 상태 통계
2581
+ */
2582
+ getStats() {
2583
+ return {
2584
+ stateCount: Object.keys(this.state).length,
2585
+ watcherCount: Array.from(this.listeners.values()).reduce((sum, arr) => sum + arr.length, 0),
2586
+ keys: Object.keys(this.state)
2587
+ };
2588
+ }
2589
+ /**
2590
+ * 정리 (메모리 누수 방지)
2591
+ */
2592
+ destroy() {
2593
+ this.state = {};
2594
+ this.listeners.clear();
2595
+ this.log("debug", "StateHandler destroyed");
2596
+ }
2597
+ };
2598
+
2765
2599
  // src/viewlogic-router.js
2766
2600
  var ViewLogicRouter = class {
2767
2601
  constructor(options = {}) {
@@ -2809,16 +2643,8 @@ var ViewLogicRouter = class {
2809
2643
  checkAuthFunction: null,
2810
2644
  redirectAfterLogin: "home",
2811
2645
  authCookieName: "authToken",
2812
- authFallbackCookieNames: ["accessToken", "token", "jwt"],
2813
- authStorage: "cookie",
2814
- authCookieOptions: {},
2815
- authSkipValidation: false,
2816
- enableParameterValidation: true,
2817
- maxParameterLength: 1e3,
2818
- maxParameterCount: 50,
2819
- maxArraySize: 100,
2820
- allowedKeyPattern: /^[a-zA-Z0-9_-]+$/,
2821
- logSecurityWarnings: true
2646
+ authStorage: "localStorage",
2647
+ authSkipValidation: false
2822
2648
  };
2823
2649
  const config = { ...defaults, ...options };
2824
2650
  config.srcPath = this.resolvePath(config.srcPath, config.basePath);
@@ -2885,8 +2711,9 @@ var ViewLogicRouter = class {
2885
2711
  async initialize() {
2886
2712
  try {
2887
2713
  this.cacheManager = new CacheManager(this, this.config);
2714
+ this.stateHandler = new StateHandler(this);
2888
2715
  this.routeLoader = new RouteLoader(this, this.config);
2889
- this.queryManager = new QueryManager(this, this.config);
2716
+ this.queryManager = new QueryManager(this);
2890
2717
  this.errorHandler = new ErrorHandler(this, this.config);
2891
2718
  if (this.config.useI18n) {
2892
2719
  try {