viewlogic 1.2.4 โ 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
</a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
> **
|
|
15
|
+
> **AI-First Vue 3 Framework**: The future of web development in the age of artificial intelligence
|
|
16
16
|
|
|
17
17
|
## ๐ฏ Core Philosophy
|
|
18
18
|
|
|
19
|
-
ViewLogic Router revolutionizes Vue development with
|
|
19
|
+
ViewLogic Router revolutionizes Vue development with three fundamental principles:
|
|
20
20
|
|
|
21
21
|
### ๐ญ View-Logic Separation
|
|
22
22
|
**Complete separation between View (presentation) and Logic (business logic)**. Views are pure HTML templates, logic is pure JavaScript components. This separation makes your code more maintainable, testable, and scalable.
|
|
@@ -24,6 +24,9 @@ ViewLogic Router revolutionizes Vue development with two fundamental principles:
|
|
|
24
24
|
### ๐ Zero Build Development
|
|
25
25
|
**Zero build step required in development mode**. Work directly with source files, see changes instantly without any compilation, bundling, or build processes. True real-time development experience.
|
|
26
26
|
|
|
27
|
+
### ๐ค AI-First Architecture
|
|
28
|
+
**Built from the ground up for the AI coding era**. Clear conventions, predictable patterns, and separated concerns make ViewLogic the perfect framework for AI-assisted development. Generate pages, components, and logic with AI tools seamlessly.
|
|
29
|
+
|
|
27
30
|
## โจ What Makes ViewLogic Special
|
|
28
31
|
|
|
29
32
|
**All-in-One Solution** - Replace multiple libraries with one unified framework:
|
|
@@ -187,33 +190,23 @@ export default {
|
|
|
187
190
|
|
|
188
191
|
**src/layouts/default.html** (optional)
|
|
189
192
|
```html
|
|
190
|
-
|
|
191
|
-
<
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
</
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
<div class="container">
|
|
208
|
-
{{ content }}
|
|
209
|
-
</div>
|
|
210
|
-
</main>
|
|
211
|
-
|
|
212
|
-
<footer>
|
|
213
|
-
<p>© 2024 My ViewLogic App</p>
|
|
214
|
-
</footer>
|
|
215
|
-
</body>
|
|
216
|
-
</html>
|
|
193
|
+
<header class="navbar">
|
|
194
|
+
<h1>My App</h1>
|
|
195
|
+
<nav>
|
|
196
|
+
<a href="#/home">Home</a>
|
|
197
|
+
<a href="#/about">About</a>
|
|
198
|
+
</nav>
|
|
199
|
+
</header>
|
|
200
|
+
|
|
201
|
+
<main class="main-content">
|
|
202
|
+
<div class="container">
|
|
203
|
+
{{ content }}
|
|
204
|
+
</div>
|
|
205
|
+
</main>
|
|
206
|
+
|
|
207
|
+
<footer>
|
|
208
|
+
<p>© 2024 My ViewLogic App</p>
|
|
209
|
+
</footer>
|
|
217
210
|
```
|
|
218
211
|
|
|
219
212
|
### Query Parameter Example
|
|
@@ -357,7 +350,8 @@ if (this.$state.has('user')) {
|
|
|
357
350
|
// Watch for changes (reactive)
|
|
358
351
|
this.$state.watch('user', (newValue, oldValue) => {
|
|
359
352
|
console.log('User changed:', newValue);
|
|
360
|
-
|
|
353
|
+
// Update component data to trigger reactivity
|
|
354
|
+
this.currentUser = newValue;
|
|
361
355
|
});
|
|
362
356
|
|
|
363
357
|
// Bulk updates
|
|
@@ -389,8 +383,7 @@ this.setToken('jwt-token-here');
|
|
|
389
383
|
|
|
390
384
|
// Login with options
|
|
391
385
|
this.setToken('jwt-token', {
|
|
392
|
-
storage: 'localStorage'
|
|
393
|
-
skipValidation: false // Skip JWT validation
|
|
386
|
+
storage: 'localStorage' // 'localStorage', 'sessionStorage', 'cookie'
|
|
394
387
|
});
|
|
395
388
|
|
|
396
389
|
// Get current token
|
|
@@ -685,8 +678,9 @@ const router = new ViewLogicRouter({
|
|
|
685
678
|
maxCacheSize: 100, // Maximum number of cached items
|
|
686
679
|
|
|
687
680
|
// API settings
|
|
688
|
-
apiBaseURL: '/
|
|
689
|
-
|
|
681
|
+
apiBaseURL: 'https://api.example.com/v1', // Base URL for API requests
|
|
682
|
+
requestTimeout: 30000, // Form submission timeout in milliseconds (30 seconds)
|
|
683
|
+
uploadTimeout: 300000, // File upload timeout in milliseconds (5 minutes)
|
|
690
684
|
|
|
691
685
|
// Development settings
|
|
692
686
|
environment: 'development', // 'development' or 'production'
|
package/dist/viewlogic-router.js
CHANGED
|
@@ -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();
|
|
@@ -510,11 +509,10 @@ var AuthManager = class {
|
|
|
510
509
|
}
|
|
511
510
|
const {
|
|
512
511
|
storage = this.config.authStorage,
|
|
513
|
-
cookieOptions = this.config.authCookieOptions
|
|
514
|
-
skipValidation = this.config.authSkipValidation
|
|
512
|
+
cookieOptions = this.config.authCookieOptions
|
|
515
513
|
} = options;
|
|
516
514
|
try {
|
|
517
|
-
if (!
|
|
515
|
+
if (!this.isTokenValid(token)) {
|
|
518
516
|
this.log("warn", "\u274C Token is expired or invalid");
|
|
519
517
|
return false;
|
|
520
518
|
}
|
|
@@ -541,7 +539,7 @@ var AuthManager = class {
|
|
|
541
539
|
});
|
|
542
540
|
return true;
|
|
543
541
|
} catch (error) {
|
|
544
|
-
this.log("Failed to set token:", error);
|
|
542
|
+
this.log("error", "Failed to set token:", error);
|
|
545
543
|
return false;
|
|
546
544
|
}
|
|
547
545
|
}
|
|
@@ -592,14 +590,14 @@ var AuthManager = class {
|
|
|
592
590
|
break;
|
|
593
591
|
}
|
|
594
592
|
this.emitAuthEvent("token_removed", { storage });
|
|
595
|
-
this.log(`Token removed from: ${storage}`);
|
|
593
|
+
this.log("debug", `Token removed from: ${storage}`);
|
|
596
594
|
}
|
|
597
595
|
/**
|
|
598
596
|
* ๋ก๊ทธ์ธ ์ฑ๊ณต ์ฒ๋ฆฌ
|
|
599
597
|
*/
|
|
600
598
|
loginSuccess(targetRoute = null) {
|
|
601
599
|
const redirectRoute = targetRoute || this.config.redirectAfterLogin;
|
|
602
|
-
this.log(`\u{1F389} Login success, redirecting to: ${redirectRoute}`);
|
|
600
|
+
this.log("info", `\u{1F389} Login success, redirecting to: ${redirectRoute}`);
|
|
603
601
|
this.emitAuthEvent("login_success", { targetRoute: redirectRoute });
|
|
604
602
|
if (this.router && typeof this.router.navigateTo === "function") {
|
|
605
603
|
this.router.navigateTo(redirectRoute);
|
|
@@ -610,7 +608,7 @@ var AuthManager = class {
|
|
|
610
608
|
* ๋ก๊ทธ์์ ์ฒ๋ฆฌ
|
|
611
609
|
*/
|
|
612
610
|
logout() {
|
|
613
|
-
this.log("\u{1F44B} Logging out user");
|
|
611
|
+
this.log("info", "\u{1F44B} Logging out user");
|
|
614
612
|
this.removeAccessToken();
|
|
615
613
|
this.emitAuthEvent("logout", {});
|
|
616
614
|
if (this.router && typeof this.router.navigateTo === "function") {
|
|
@@ -635,11 +633,11 @@ var AuthManager = class {
|
|
|
635
633
|
try {
|
|
636
634
|
listener(data);
|
|
637
635
|
} catch (error) {
|
|
638
|
-
this.log("Event listener error:", error);
|
|
636
|
+
this.log("error", "Event listener error:", error);
|
|
639
637
|
}
|
|
640
638
|
});
|
|
641
639
|
}
|
|
642
|
-
this.log(`\u{1F514} Auth event emitted: ${eventType}`, data);
|
|
640
|
+
this.log("debug", `\u{1F514} Auth event emitted: ${eventType}`, data);
|
|
643
641
|
}
|
|
644
642
|
/**
|
|
645
643
|
* ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ฑ๋ก
|
|
@@ -812,7 +810,7 @@ var CacheManager = class {
|
|
|
812
810
|
patterns.forEach((pattern) => {
|
|
813
811
|
totalInvalidated += this.deleteByPattern(pattern);
|
|
814
812
|
});
|
|
815
|
-
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)`);
|
|
816
814
|
return totalInvalidated;
|
|
817
815
|
}
|
|
818
816
|
/**
|
|
@@ -824,7 +822,7 @@ var CacheManager = class {
|
|
|
824
822
|
componentPatterns.forEach((pattern) => {
|
|
825
823
|
totalCleared += this.deleteByPattern(pattern);
|
|
826
824
|
});
|
|
827
|
-
this.log(`\u{1F9FD} Deleted all component caches (${totalCleared} entries)`);
|
|
825
|
+
this.log("debug", `\u{1F9FD} Deleted all component caches (${totalCleared} entries)`);
|
|
828
826
|
return totalCleared;
|
|
829
827
|
}
|
|
830
828
|
/**
|
|
@@ -835,7 +833,7 @@ var CacheManager = class {
|
|
|
835
833
|
this.cache.clear();
|
|
836
834
|
this.cacheTimestamps.clear();
|
|
837
835
|
this.lruOrder = [];
|
|
838
|
-
this.log(`\u{1F525} Cleared all cache (${size} entries)`);
|
|
836
|
+
this.log("debug", `\u{1F525} Cleared all cache (${size} entries)`);
|
|
839
837
|
return size;
|
|
840
838
|
}
|
|
841
839
|
/**
|
|
@@ -860,7 +858,7 @@ var CacheManager = class {
|
|
|
860
858
|
}
|
|
861
859
|
});
|
|
862
860
|
if (expiredKeys.length > 0) {
|
|
863
|
-
this.log(`\u23F1\uFE0F Cleaned ${expiredKeys.length} expired cache entries`);
|
|
861
|
+
this.log("debug", `\u23F1\uFE0F Cleaned ${expiredKeys.length} expired cache entries`);
|
|
864
862
|
}
|
|
865
863
|
return expiredKeys.length;
|
|
866
864
|
}
|
|
@@ -952,7 +950,7 @@ var CacheManager = class {
|
|
|
952
950
|
this.cleanupInterval = setInterval(() => {
|
|
953
951
|
this.cleanExpired();
|
|
954
952
|
}, interval);
|
|
955
|
-
this.log(`\u{1F916} Auto cleanup started (interval: ${interval}ms)`);
|
|
953
|
+
this.log("debug", `\u{1F916} Auto cleanup started (interval: ${interval}ms)`);
|
|
956
954
|
}
|
|
957
955
|
/**
|
|
958
956
|
* ์๋ ์ ๋ฆฌ ์ค์ง
|
|
@@ -1174,11 +1172,8 @@ var QueryManager = class {
|
|
|
1174
1172
|
var FormHandler = class {
|
|
1175
1173
|
constructor(router, options = {}) {
|
|
1176
1174
|
this.router = router;
|
|
1177
|
-
this.
|
|
1178
|
-
|
|
1179
|
-
requestTimeout: options.requestTimeout || 3e4,
|
|
1180
|
-
...options
|
|
1181
|
-
};
|
|
1175
|
+
this.requestTimeout = options.requestTimeout || 3e4;
|
|
1176
|
+
this.uploadTimeout = options.uploadTimeout || 3e5;
|
|
1182
1177
|
this.log("debug", "FormHandler initialized");
|
|
1183
1178
|
}
|
|
1184
1179
|
/**
|
|
@@ -1205,11 +1200,13 @@ var FormHandler = class {
|
|
|
1205
1200
|
startFormSubmission(form) {
|
|
1206
1201
|
form._isSubmitting = true;
|
|
1207
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;
|
|
1208
1205
|
form._timeoutId = setTimeout(() => {
|
|
1209
1206
|
if (form._isSubmitting) {
|
|
1210
1207
|
this.abortFormSubmission(form);
|
|
1211
1208
|
}
|
|
1212
|
-
},
|
|
1209
|
+
}, timeout);
|
|
1213
1210
|
}
|
|
1214
1211
|
/**
|
|
1215
1212
|
* ํผ ์ ์ถ ์๋ฃ
|
|
@@ -1293,7 +1290,7 @@ var FormHandler = class {
|
|
|
1293
1290
|
if (errorHandler && component[errorHandler]) {
|
|
1294
1291
|
component[errorHandler](error, form);
|
|
1295
1292
|
} else {
|
|
1296
|
-
|
|
1293
|
+
this.log("error", "Form submission error (no error handler defined):", error);
|
|
1297
1294
|
}
|
|
1298
1295
|
} finally {
|
|
1299
1296
|
if (loadingHandler && component[loadingHandler]) {
|
|
@@ -1413,12 +1410,7 @@ var FormHandler = class {
|
|
|
1413
1410
|
var ApiHandler = class {
|
|
1414
1411
|
constructor(router, options = {}) {
|
|
1415
1412
|
this.router = router;
|
|
1416
|
-
this.
|
|
1417
|
-
debug: options.debug || false,
|
|
1418
|
-
timeout: options.timeout || 1e4,
|
|
1419
|
-
retries: options.retries || 1,
|
|
1420
|
-
...options
|
|
1421
|
-
};
|
|
1413
|
+
this.apiBaseURL = options.apiBaseURL || "";
|
|
1422
1414
|
this.log("debug", "ApiHandler initialized");
|
|
1423
1415
|
}
|
|
1424
1416
|
/**
|
|
@@ -1435,6 +1427,9 @@ var ApiHandler = class {
|
|
|
1435
1427
|
async fetchData(dataURL, component = null, options = {}) {
|
|
1436
1428
|
try {
|
|
1437
1429
|
let processedURL = this.processURLParameters(dataURL, component);
|
|
1430
|
+
if (this.apiBaseURL && !this.isAbsoluteURL(processedURL)) {
|
|
1431
|
+
processedURL = this.combineURLs(this.apiBaseURL, processedURL);
|
|
1432
|
+
}
|
|
1438
1433
|
const queryString = this.router.queryManager?.buildQueryString(this.router.queryManager?.getQueryParams()) || "";
|
|
1439
1434
|
const fullURL = queryString ? `${processedURL}?${queryString}` : processedURL;
|
|
1440
1435
|
this.log("debug", `Fetching data from: ${fullURL}`);
|
|
@@ -1525,15 +1520,15 @@ var ApiHandler = class {
|
|
|
1525
1520
|
const paramName = match.slice(1, -1);
|
|
1526
1521
|
try {
|
|
1527
1522
|
let paramValue = null;
|
|
1528
|
-
if (component
|
|
1529
|
-
paramValue = component
|
|
1523
|
+
if (component.$options?.computed?.[paramName]) {
|
|
1524
|
+
paramValue = component[paramName];
|
|
1530
1525
|
}
|
|
1531
1526
|
if (paramValue === null || paramValue === void 0) {
|
|
1532
1527
|
paramValue = component[paramName];
|
|
1533
1528
|
}
|
|
1534
1529
|
if (paramValue === null || paramValue === void 0) {
|
|
1535
|
-
if (component
|
|
1536
|
-
paramValue = component
|
|
1530
|
+
if (component.getParam) {
|
|
1531
|
+
paramValue = component.getParam(paramName);
|
|
1537
1532
|
}
|
|
1538
1533
|
}
|
|
1539
1534
|
if (paramValue === null || paramValue === void 0) {
|
|
@@ -1587,6 +1582,20 @@ var ApiHandler = class {
|
|
|
1587
1582
|
fetchMultipleData: (dataConfig) => this.fetchMultipleData(dataConfig, component)
|
|
1588
1583
|
};
|
|
1589
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
|
+
}
|
|
1590
1599
|
/**
|
|
1591
1600
|
* ์ ๋ฆฌ (๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง)
|
|
1592
1601
|
*/
|
|
@@ -1602,7 +1611,6 @@ var ComponentLoader = class {
|
|
|
1602
1611
|
this.config = {
|
|
1603
1612
|
componentsPath: options.componentsPath || "/components",
|
|
1604
1613
|
// srcPath ๊ธฐ์ค ์๋ ๊ฒฝ๋ก
|
|
1605
|
-
debug: options.debug || false,
|
|
1606
1614
|
environment: options.environment || "development",
|
|
1607
1615
|
...options
|
|
1608
1616
|
};
|
|
@@ -1873,8 +1881,7 @@ var RouteLoader = class {
|
|
|
1873
1881
|
// ํ๋ก๋์
๋ผ์ฐํธ ๊ฒฝ๋ก
|
|
1874
1882
|
environment: options.environment || "development",
|
|
1875
1883
|
useLayout: options.useLayout !== false,
|
|
1876
|
-
defaultLayout: options.defaultLayout || "default"
|
|
1877
|
-
debug: options.debug || false
|
|
1884
|
+
defaultLayout: options.defaultLayout || "default"
|
|
1878
1885
|
};
|
|
1879
1886
|
this.router = router;
|
|
1880
1887
|
this.formHandler = new FormHandler(router, this.config);
|
|
@@ -2189,9 +2196,7 @@ ${template}`;
|
|
|
2189
2196
|
var ErrorHandler = class {
|
|
2190
2197
|
constructor(router, options = {}) {
|
|
2191
2198
|
this.config = {
|
|
2192
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
*
|
|
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
|
-
|
|
2665
|
-
|
|
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.
|
|
2676
|
-
|
|
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
|
* ๋ก๊น
๋ํผ ๋ฉ์๋
|
|
@@ -2842,7 +2840,7 @@ var ViewLogicRouter = class {
|
|
|
2842
2840
|
if (this.errorHandler) {
|
|
2843
2841
|
await this.errorHandler.handleRouteError(routeName, error);
|
|
2844
2842
|
} else {
|
|
2845
|
-
console.error("[Router] No error handler available");
|
|
2843
|
+
console.error("[Router] Critical: No error handler available for route error:", error);
|
|
2846
2844
|
}
|
|
2847
2845
|
} finally {
|
|
2848
2846
|
this.transitionInProgress = false;
|
|
@@ -2883,6 +2881,7 @@ var ViewLogicRouter = class {
|
|
|
2883
2881
|
currentQuery: this.queryManager?.getQueryParams() || {}
|
|
2884
2882
|
};
|
|
2885
2883
|
newVueApp.mount(`#${newPageContainer.id}`);
|
|
2884
|
+
window.scrollTo(0, 0);
|
|
2886
2885
|
requestAnimationFrame(() => {
|
|
2887
2886
|
this.cleanupPreviousPages();
|
|
2888
2887
|
this.transitionInProgress = false;
|
|
@@ -2936,31 +2935,17 @@ var ViewLogicRouter = class {
|
|
|
2936
2935
|
updateURL(route, params = null) {
|
|
2937
2936
|
const queryParams = params || this.queryManager?.getQueryParams() || {};
|
|
2938
2937
|
const queryString = this.queryManager?.buildQueryString(queryParams) || "";
|
|
2939
|
-
|
|
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
|
-
};
|
|
2938
|
+
let base = route === "home" ? "/" : `/${route}`;
|
|
2947
2939
|
if (this.config.mode === "hash") {
|
|
2948
|
-
const
|
|
2940
|
+
const url = queryString ? `${base}?${queryString}` : base;
|
|
2941
|
+
const newHash = `#${url}`;
|
|
2949
2942
|
if (window.location.hash !== newHash) {
|
|
2950
2943
|
window.location.hash = newHash;
|
|
2951
2944
|
}
|
|
2952
2945
|
} else {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
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
|
-
}
|
|
2946
|
+
base = this.combinePaths(this.config.basePath, base);
|
|
2947
|
+
const url = queryString ? `${base}?${queryString}` : base;
|
|
2948
|
+
window.history.pushState({}, "", url);
|
|
2964
2949
|
this.handleRouteChange();
|
|
2965
2950
|
}
|
|
2966
2951
|
}
|