viewlogic 1.2.3 → 1.2.5

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.
@@ -355,8 +355,7 @@ var AuthManager = class {
355
355
  checkAuthFunction: options.checkAuthFunction || null,
356
356
  redirectAfterLogin: options.redirectAfterLogin || "home",
357
357
  authCookieName: options.authCookieName || "authToken",
358
- authStorage: options.authStorage || "localStorage",
359
- authSkipValidation: options.authSkipValidation || false
358
+ authStorage: options.authStorage || "localStorage"
360
359
  };
361
360
  this.router = router;
362
361
  this.eventListeners = /* @__PURE__ */ new Map();
@@ -387,7 +386,12 @@ var AuthManager = class {
387
386
  }
388
387
  if (typeof this.config.checkAuthFunction === "function") {
389
388
  try {
390
- const isAuthenticated2 = await this.config.checkAuthFunction(routeName);
389
+ const route = {
390
+ name: routeName,
391
+ $api: this.router.routeLoader.apiHandler.bindToComponent({}),
392
+ $state: this.router.stateHandler
393
+ };
394
+ const isAuthenticated2 = await this.config.checkAuthFunction(route);
391
395
  return {
392
396
  allowed: isAuthenticated2,
393
397
  reason: isAuthenticated2 ? "custom_auth_success" : "custom_auth_failed",
@@ -505,11 +509,10 @@ var AuthManager = class {
505
509
  }
506
510
  const {
507
511
  storage = this.config.authStorage,
508
- cookieOptions = this.config.authCookieOptions,
509
- skipValidation = this.config.authSkipValidation
512
+ cookieOptions = this.config.authCookieOptions
510
513
  } = options;
511
514
  try {
512
- if (!skipValidation && !this.isTokenValid(token)) {
515
+ if (!this.isTokenValid(token)) {
513
516
  this.log("warn", "\u274C Token is expired or invalid");
514
517
  return false;
515
518
  }
@@ -536,7 +539,7 @@ var AuthManager = class {
536
539
  });
537
540
  return true;
538
541
  } catch (error) {
539
- this.log("Failed to set token:", error);
542
+ this.log("error", "Failed to set token:", error);
540
543
  return false;
541
544
  }
542
545
  }
@@ -587,14 +590,14 @@ var AuthManager = class {
587
590
  break;
588
591
  }
589
592
  this.emitAuthEvent("token_removed", { storage });
590
- this.log(`Token removed from: ${storage}`);
593
+ this.log("debug", `Token removed from: ${storage}`);
591
594
  }
592
595
  /**
593
596
  * 로그인 성공 처리
594
597
  */
595
598
  loginSuccess(targetRoute = null) {
596
599
  const redirectRoute = targetRoute || this.config.redirectAfterLogin;
597
- this.log(`\u{1F389} Login success, redirecting to: ${redirectRoute}`);
600
+ this.log("info", `\u{1F389} Login success, redirecting to: ${redirectRoute}`);
598
601
  this.emitAuthEvent("login_success", { targetRoute: redirectRoute });
599
602
  if (this.router && typeof this.router.navigateTo === "function") {
600
603
  this.router.navigateTo(redirectRoute);
@@ -605,7 +608,7 @@ var AuthManager = class {
605
608
  * 로그아웃 처리
606
609
  */
607
610
  logout() {
608
- this.log("\u{1F44B} Logging out user");
611
+ this.log("info", "\u{1F44B} Logging out user");
609
612
  this.removeAccessToken();
610
613
  this.emitAuthEvent("logout", {});
611
614
  if (this.router && typeof this.router.navigateTo === "function") {
@@ -630,11 +633,11 @@ var AuthManager = class {
630
633
  try {
631
634
  listener(data);
632
635
  } catch (error) {
633
- this.log("Event listener error:", error);
636
+ this.log("error", "Event listener error:", error);
634
637
  }
635
638
  });
636
639
  }
637
- this.log(`\u{1F514} Auth event emitted: ${eventType}`, data);
640
+ this.log("debug", `\u{1F514} Auth event emitted: ${eventType}`, data);
638
641
  }
639
642
  /**
640
643
  * 이벤트 리스너 등록
@@ -807,7 +810,7 @@ var CacheManager = class {
807
810
  patterns.forEach((pattern) => {
808
811
  totalInvalidated += this.deleteByPattern(pattern);
809
812
  });
810
- this.log(`\u{1F504} Deleted component cache for route: ${routeName} (${totalInvalidated} entries)`);
813
+ this.log("debug", `\u{1F504} Deleted component cache for route: ${routeName} (${totalInvalidated} entries)`);
811
814
  return totalInvalidated;
812
815
  }
813
816
  /**
@@ -819,7 +822,7 @@ var CacheManager = class {
819
822
  componentPatterns.forEach((pattern) => {
820
823
  totalCleared += this.deleteByPattern(pattern);
821
824
  });
822
- this.log(`\u{1F9FD} Deleted all component caches (${totalCleared} entries)`);
825
+ this.log("debug", `\u{1F9FD} Deleted all component caches (${totalCleared} entries)`);
823
826
  return totalCleared;
824
827
  }
825
828
  /**
@@ -830,7 +833,7 @@ var CacheManager = class {
830
833
  this.cache.clear();
831
834
  this.cacheTimestamps.clear();
832
835
  this.lruOrder = [];
833
- this.log(`\u{1F525} Cleared all cache (${size} entries)`);
836
+ this.log("debug", `\u{1F525} Cleared all cache (${size} entries)`);
834
837
  return size;
835
838
  }
836
839
  /**
@@ -855,7 +858,7 @@ var CacheManager = class {
855
858
  }
856
859
  });
857
860
  if (expiredKeys.length > 0) {
858
- this.log(`\u23F1\uFE0F Cleaned ${expiredKeys.length} expired cache entries`);
861
+ this.log("debug", `\u23F1\uFE0F Cleaned ${expiredKeys.length} expired cache entries`);
859
862
  }
860
863
  return expiredKeys.length;
861
864
  }
@@ -947,7 +950,7 @@ var CacheManager = class {
947
950
  this.cleanupInterval = setInterval(() => {
948
951
  this.cleanExpired();
949
952
  }, interval);
950
- this.log(`\u{1F916} Auto cleanup started (interval: ${interval}ms)`);
953
+ this.log("debug", `\u{1F916} Auto cleanup started (interval: ${interval}ms)`);
951
954
  }
952
955
  /**
953
956
  * 자동 정리 중지
@@ -1169,11 +1172,8 @@ var QueryManager = class {
1169
1172
  var FormHandler = class {
1170
1173
  constructor(router, options = {}) {
1171
1174
  this.router = router;
1172
- this.config = {
1173
- debug: options.debug || false,
1174
- requestTimeout: options.requestTimeout || 3e4,
1175
- ...options
1176
- };
1175
+ this.requestTimeout = options.requestTimeout || 3e4;
1176
+ this.uploadTimeout = options.uploadTimeout || 3e5;
1177
1177
  this.log("debug", "FormHandler initialized");
1178
1178
  }
1179
1179
  /**
@@ -1200,11 +1200,13 @@ var FormHandler = class {
1200
1200
  startFormSubmission(form) {
1201
1201
  form._isSubmitting = true;
1202
1202
  form._abortController = new AbortController();
1203
+ const hasFile = Array.from(form.elements).some((el) => el.type === "file" && el.files.length > 0);
1204
+ const timeout = hasFile ? this.uploadTimeout : this.requestTimeout;
1203
1205
  form._timeoutId = setTimeout(() => {
1204
1206
  if (form._isSubmitting) {
1205
1207
  this.abortFormSubmission(form);
1206
1208
  }
1207
- }, this.config.requestTimeout);
1209
+ }, timeout);
1208
1210
  }
1209
1211
  /**
1210
1212
  * 폼 제출 완료
@@ -1247,9 +1249,9 @@ var FormHandler = class {
1247
1249
  const form = event.target;
1248
1250
  let action = form.getAttribute("action");
1249
1251
  const method = form.getAttribute("method") || "POST";
1250
- const successHandler = form.getAttribute("data-success-handler");
1251
- const errorHandler = form.getAttribute("data-error-handler");
1252
- const loadingHandler = form.getAttribute("data-loading-handler");
1252
+ const successHandler = form.getAttribute("data-success");
1253
+ const errorHandler = form.getAttribute("data-error");
1254
+ const loadingHandler = form.getAttribute("data-loading");
1253
1255
  const redirectTo = form.getAttribute("data-redirect");
1254
1256
  action = this.processActionParams(action, component);
1255
1257
  if (!this.validateForm(form, component)) {
@@ -1408,12 +1410,7 @@ var FormHandler = class {
1408
1410
  var ApiHandler = class {
1409
1411
  constructor(router, options = {}) {
1410
1412
  this.router = router;
1411
- this.config = {
1412
- debug: options.debug || false,
1413
- timeout: options.timeout || 1e4,
1414
- retries: options.retries || 1,
1415
- ...options
1416
- };
1413
+ this.apiBaseURL = options.apiBaseURL || "";
1417
1414
  this.log("debug", "ApiHandler initialized");
1418
1415
  }
1419
1416
  /**
@@ -1430,6 +1427,9 @@ var ApiHandler = class {
1430
1427
  async fetchData(dataURL, component = null, options = {}) {
1431
1428
  try {
1432
1429
  let processedURL = this.processURLParameters(dataURL, component);
1430
+ if (this.apiBaseURL && !this.isAbsoluteURL(processedURL)) {
1431
+ processedURL = this.combineURLs(this.apiBaseURL, processedURL);
1432
+ }
1433
1433
  const queryString = this.router.queryManager?.buildQueryString(this.router.queryManager?.getQueryParams()) || "";
1434
1434
  const fullURL = queryString ? `${processedURL}?${queryString}` : processedURL;
1435
1435
  this.log("debug", `Fetching data from: ${fullURL}`);
@@ -1520,15 +1520,15 @@ var ApiHandler = class {
1520
1520
  const paramName = match.slice(1, -1);
1521
1521
  try {
1522
1522
  let paramValue = null;
1523
- if (component.getParam) {
1524
- paramValue = component.getParam(paramName);
1523
+ if (component.$options?.computed?.[paramName]) {
1524
+ paramValue = component[paramName];
1525
1525
  }
1526
1526
  if (paramValue === null || paramValue === void 0) {
1527
1527
  paramValue = component[paramName];
1528
1528
  }
1529
1529
  if (paramValue === null || paramValue === void 0) {
1530
- if (component.$options?.computed?.[paramName]) {
1531
- paramValue = component[paramName];
1530
+ if (component.getParam) {
1531
+ paramValue = component.getParam(paramName);
1532
1532
  }
1533
1533
  }
1534
1534
  if (paramValue === null || paramValue === void 0) {
@@ -1582,6 +1582,20 @@ var ApiHandler = class {
1582
1582
  fetchMultipleData: (dataConfig) => this.fetchMultipleData(dataConfig, component)
1583
1583
  };
1584
1584
  }
1585
+ /**
1586
+ * 절대 URL인지 확인
1587
+ */
1588
+ isAbsoluteURL(url) {
1589
+ return /^https?:\/\//.test(url) || url.startsWith("//");
1590
+ }
1591
+ /**
1592
+ * 두 URL을 조합
1593
+ */
1594
+ combineURLs(baseURL, relativeURL) {
1595
+ const cleanBase = baseURL.replace(/\/$/, "");
1596
+ const cleanRelative = relativeURL.startsWith("/") ? relativeURL : `/${relativeURL}`;
1597
+ return `${cleanBase}${cleanRelative}`;
1598
+ }
1585
1599
  /**
1586
1600
  * 정리 (메모리 누수 방지)
1587
1601
  */
@@ -1597,7 +1611,6 @@ var ComponentLoader = class {
1597
1611
  this.config = {
1598
1612
  componentsPath: options.componentsPath || "/components",
1599
1613
  // srcPath 기준 상대 경로
1600
- debug: options.debug || false,
1601
1614
  environment: options.environment || "development",
1602
1615
  ...options
1603
1616
  };
@@ -1868,8 +1881,7 @@ var RouteLoader = class {
1868
1881
  // 프로덕션 라우트 경로
1869
1882
  environment: options.environment || "development",
1870
1883
  useLayout: options.useLayout !== false,
1871
- defaultLayout: options.defaultLayout || "default",
1872
- debug: options.debug || false
1884
+ defaultLayout: options.defaultLayout || "default"
1873
1885
  };
1874
1886
  this.router = router;
1875
1887
  this.formHandler = new FormHandler(router, this.config);
@@ -2036,6 +2048,7 @@ ${template}`;
2036
2048
  ...originalData,
2037
2049
  currentRoute: routeName,
2038
2050
  $query: router.queryManager?.getQueryParams() || {},
2051
+ $params: router.queryManager?.getRouteParams() || {},
2039
2052
  $lang: (() => {
2040
2053
  try {
2041
2054
  return router.i18nManager?.getCurrentLanguage() || router.config.i18nDefaultLanguage || router.config.defaultLanguage || "ko";
@@ -2057,11 +2070,12 @@ ${template}`;
2057
2070
  },
2058
2071
  async mounted() {
2059
2072
  this.$api = router.routeLoader.apiHandler.bindToComponent(this);
2073
+ this.$state = router.stateHandler;
2060
2074
  if (script.mounted) {
2061
2075
  await script.mounted.call(this);
2062
2076
  }
2063
2077
  if (script.dataURL) {
2064
- await this.$fetchData();
2078
+ await this.fetchData();
2065
2079
  }
2066
2080
  await this.$nextTick();
2067
2081
  router.routeLoader.formHandler.bindAutoForms(this);
@@ -2083,29 +2097,22 @@ ${template}`;
2083
2097
  return key;
2084
2098
  }
2085
2099
  },
2086
- // 인증 관련
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,
2090
- $checkAuth: (route) => router.authManager ? router.authManager.checkAuthentication(route) : Promise.resolve({ allowed: true, reason: "auth_disabled" }),
2091
- $getToken: () => router.authManager?.getAccessToken() || null,
2092
- $setToken: (token, options) => router.authManager?.setAccessToken(token, options) || false,
2093
- $removeToken: (storage) => router.authManager?.removeAccessToken(storage) || null,
2094
- $getAuthCookie: () => router.authManager?.getAuthCookie() || null,
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() || {}
2100
+ // 인증 관련 (핵심 4개 메소드만)
2101
+ isAuth: () => router.authManager?.isAuthenticated() || false,
2102
+ logout: () => router.authManager ? router.navigateTo(router.authManager.logout()) : null,
2103
+ getToken: () => router.authManager?.getAccessToken() || null,
2104
+ setToken: (token, options) => router.authManager?.setAccessToken(token, options) || false,
2105
+ // i18n 언어 관리
2106
+ getLanguage: () => router.i18nManager?.getCurrentLanguage() || router.config.defaultLanguage || "ko",
2107
+ setLanguage: (lang) => router.i18nManager?.setLanguage(lang),
2108
+ // 로깅 에러 처리
2109
+ log: (level, ...args) => {
2110
+ if (router.errorHandler) {
2111
+ router.errorHandler.log(level, `[${routeName}]`, ...args);
2112
+ }
2106
2113
  },
2107
2114
  // 데이터 fetch (ApiHandler 래퍼)
2108
- async $fetchData(dataConfig = null) {
2115
+ async fetchData(dataConfig = null) {
2109
2116
  const configToUse = dataConfig || script.dataURL;
2110
2117
  if (!configToUse) return null;
2111
2118
  this.$dataLoading = true;
@@ -2189,9 +2196,7 @@ ${template}`;
2189
2196
  var ErrorHandler = class {
2190
2197
  constructor(router, options = {}) {
2191
2198
  this.config = {
2192
- enableErrorReporting: options.enableErrorReporting !== false,
2193
- debug: options.debug || false,
2194
- logLevel: options.logLevel || (options.debug ? "debug" : "info"),
2199
+ logLevel: options.logLevel || "info",
2195
2200
  environment: options.environment || "development"
2196
2201
  };
2197
2202
  this.router = router;
@@ -2221,9 +2226,7 @@ var ErrorHandler = class {
2221
2226
  errorMessage = "\uD398\uC774\uC9C0\uC5D0 \uC811\uADFC\uD560 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
2222
2227
  }
2223
2228
  this.debug("ErrorHandler", `\uC5D0\uB7EC \uCF54\uB4DC \uACB0\uC815: ${errorCode} (\uB77C\uC6B0\uD2B8: ${routeName})`);
2224
- if (this.config.enableErrorReporting) {
2225
- this.reportError(routeName, error, errorCode);
2226
- }
2229
+ this.reportError(routeName, error, errorCode);
2227
2230
  try {
2228
2231
  if (errorCode === 404) {
2229
2232
  await this.load404Page();
@@ -2394,7 +2397,7 @@ var ErrorHandler = class {
2394
2397
  if (typeof level !== "string" || !this.logLevels.hasOwnProperty(level)) {
2395
2398
  args = [component, ...args];
2396
2399
  component = level;
2397
- level = this.config.debug ? "debug" : "info";
2400
+ level = "info";
2398
2401
  }
2399
2402
  const currentLevelValue = this.logLevels[this.config.logLevel] || this.logLevels.info;
2400
2403
  const messageLevelValue = this.logLevels[level] || this.logLevels.info;
@@ -2635,6 +2638,9 @@ var ViewLogicRouter = class {
2635
2638
  i18nPath: "/i18n",
2636
2639
  // 다국어 파일 경로
2637
2640
  logLevel: "info",
2641
+ apiBaseURL: "",
2642
+ requestTimeout: 3e4,
2643
+ uploadTimeout: 3e5,
2638
2644
  authEnabled: false,
2639
2645
  loginRoute: "login",
2640
2646
  protectedRoutes: [],
@@ -2643,8 +2649,7 @@ var ViewLogicRouter = class {
2643
2649
  checkAuthFunction: null,
2644
2650
  redirectAfterLogin: "home",
2645
2651
  authCookieName: "authToken",
2646
- authStorage: "localStorage",
2647
- authSkipValidation: false
2652
+ authStorage: "localStorage"
2648
2653
  };
2649
2654
  const config = { ...defaults, ...options };
2650
2655
  config.srcPath = this.resolvePath(config.srcPath, config.basePath);
@@ -2653,49 +2658,42 @@ var ViewLogicRouter = class {
2653
2658
  return config;
2654
2659
  }
2655
2660
  /**
2656
- * 통합 경로 해결 - 서브폴더 배포 basePath 지원
2661
+ * 경로를 안전하게 조합 정규화 (기본 유틸리티)
2662
+ * @param {string} basePath - 기본 경로
2663
+ * @param {string} relativePath - 상대 경로
2664
+ * @returns {string} 조합된 경로 (예: '/examples' + '/about' → '/examples/about')
2665
+ */
2666
+ combinePaths(basePath, relativePath) {
2667
+ if (!basePath || basePath === "/") {
2668
+ return relativePath.replace(/\/+/g, "/");
2669
+ }
2670
+ const cleanBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
2671
+ const cleanRelative = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
2672
+ const combined = `${cleanBase}${cleanRelative}`;
2673
+ return combined.replace(/\/+/g, "/");
2674
+ }
2675
+ /**
2676
+ * 상대 경로를 완전한 절대 URL로 변환 (파일 시스템 전용)
2677
+ * @param {string} path - 변환할 경로
2678
+ * @param {string} basePath - 기본 경로 (옵션)
2679
+ * @returns {string} 완전한 절대 URL (예: 'http://localhost:3000/examples/about')
2657
2680
  */
2658
2681
  resolvePath(path, basePath = null) {
2682
+ if (basePath === null) {
2683
+ basePath = this.config?.basePath || "/";
2684
+ }
2659
2685
  const currentOrigin = window.location.origin;
2660
2686
  if (path.startsWith("http")) {
2661
2687
  return path;
2662
2688
  }
2663
2689
  if (path.startsWith("/")) {
2664
- if (basePath && basePath !== "/") {
2665
- const cleanBasePath = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
2666
- const cleanPath = path.startsWith("/") ? path : `/${path}`;
2667
- const fullPath = `${cleanBasePath}${cleanPath}`;
2668
- const fullUrl2 = `${currentOrigin}${fullPath}`;
2669
- return fullUrl2.replace(/([^:])\/{2,}/g, "$1/");
2670
- }
2671
- return `${currentOrigin}${path}`;
2690
+ const combinedPath = this.combinePaths(basePath, path);
2691
+ return `${currentOrigin}${combinedPath}`;
2672
2692
  }
2673
2693
  const currentPathname = window.location.pathname;
2674
2694
  const currentBase = currentPathname.endsWith("/") ? currentPathname : currentPathname.substring(0, currentPathname.lastIndexOf("/") + 1);
2675
- const resolvedPath = this.normalizePath(currentBase + path);
2676
- const fullUrl = `${currentOrigin}${resolvedPath}`;
2677
- return fullUrl.replace(/([^:])\/{2,}/g, "$1/");
2678
- }
2679
- /**
2680
- * URL 경로 정규화 (이중 슬래시 제거 및 ../, ./ 처리)
2681
- */
2682
- normalizePath(path) {
2683
- path = path.replace(/\/+/g, "/");
2684
- const parts = path.split("/").filter((part) => part !== "" && part !== ".");
2685
- const stack = [];
2686
- for (const part of parts) {
2687
- if (part === "..") {
2688
- if (stack.length > 0 && stack[stack.length - 1] !== "..") {
2689
- stack.pop();
2690
- } else if (!path.startsWith("/")) {
2691
- stack.push(part);
2692
- }
2693
- } else {
2694
- stack.push(part);
2695
- }
2696
- }
2697
- const normalized = "/" + stack.join("/");
2698
- return normalized === "/" ? "/" : normalized;
2695
+ const resolvedPath = this.combinePaths(currentBase, path);
2696
+ return `${currentOrigin}${resolvedPath}`;
2699
2697
  }
2700
2698
  /**
2701
2699
  * 로깅 래퍼 메서드
@@ -2936,31 +2934,17 @@ var ViewLogicRouter = class {
2936
2934
  updateURL(route, params = null) {
2937
2935
  const queryParams = params || this.queryManager?.getQueryParams() || {};
2938
2936
  const queryString = this.queryManager?.buildQueryString(queryParams) || "";
2939
- const buildURL = (route2, queryString2, isHash = true) => {
2940
- let base = route2 === "home" ? "/" : `/${route2}`;
2941
- if (!isHash && this.config.basePath && this.config.basePath !== "/") {
2942
- base = `${this.config.basePath}${base}`;
2943
- }
2944
- const url = queryString2 ? `${base}?${queryString2}` : base;
2945
- return isHash ? `#${url}` : url;
2946
- };
2937
+ let base = route === "home" ? "/" : `/${route}`;
2947
2938
  if (this.config.mode === "hash") {
2948
- const newHash = buildURL(route, queryString);
2939
+ const url = queryString ? `${base}?${queryString}` : base;
2940
+ const newHash = `#${url}`;
2949
2941
  if (window.location.hash !== newHash) {
2950
2942
  window.location.hash = newHash;
2951
2943
  }
2952
2944
  } else {
2953
- const newPath = buildURL(route, queryString, false);
2954
- let expectedPath = route === "home" ? "/" : `/${route}`;
2955
- if (this.config.basePath && this.config.basePath !== "/") {
2956
- expectedPath = `${this.config.basePath}${expectedPath}`;
2957
- }
2958
- const isSameRoute = window.location.pathname === expectedPath;
2959
- if (isSameRoute) {
2960
- window.history.replaceState({}, "", newPath);
2961
- } else {
2962
- window.history.pushState({}, "", newPath);
2963
- }
2945
+ base = this.combinePaths(this.config.basePath, base);
2946
+ const url = queryString ? `${base}?${queryString}` : base;
2947
+ window.history.pushState({}, "", url);
2964
2948
  this.handleRouteChange();
2965
2949
  }
2966
2950
  }