sibujs 3.1.0 → 3.2.1
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 +6 -0
- package/dist/browser.cjs +16 -8
- package/dist/browser.js +6 -5
- package/dist/build.cjs +235 -147
- package/dist/build.js +35 -24
- package/dist/cdn.global.js +7 -7
- package/dist/{chunk-WYU7CYJ3.js → chunk-2C4E3HBM.js} +5 -5
- package/dist/{chunk-3DYB5B3S.js → chunk-4JCAUOLN.js} +45 -23
- package/dist/{chunk-2HAGQWDV.js → chunk-5N74TKLD.js} +1 -1
- package/dist/{chunk-SVVAUX7J.js → chunk-7XDYVJLE.js} +19 -9
- package/dist/{chunk-2N2UL7O4.js → chunk-BGNLPNGV.js} +20 -12
- package/dist/{chunk-RK4BQG25.js → chunk-C427DVQF.js} +1 -1
- package/dist/{chunk-ZIBE2SAT.js → chunk-FDY42FIU.js} +3 -2
- package/dist/{chunk-GQ7RRFPU.js → chunk-FOI23UJL.js} +11 -1
- package/dist/{chunk-2RA7SHDA.js → chunk-GOJMFRBL.js} +20 -4
- package/dist/{chunk-IVOUCSZL.js → chunk-GOUM4JCT.js} +6 -6
- package/dist/chunk-H3SRKIYX.js +17 -0
- package/dist/{chunk-3DJH25UO.js → chunk-H6PCHJZQ.js} +2 -2
- package/dist/{chunk-UCS6AMJ7.js → chunk-HMJFCBRR.js} +26 -3
- package/dist/{chunk-JYD2PWXH.js → chunk-HXMS4SNP.js} +22 -15
- package/dist/{chunk-SC437AMI.js → chunk-JYXOEYI4.js} +12 -18
- package/dist/{chunk-KB3BA2XK.js → chunk-NFYWLRUO.js} +11 -18
- package/dist/{chunk-QNQY5DUS.js → chunk-NPIEEKPT.js} +20 -11
- package/dist/{chunk-UYX2NDOH.js → chunk-OYLPZO4N.js} +33 -15
- package/dist/{chunk-LYTCUZ7H.js → chunk-RDRSWYNP.js} +1 -1
- package/dist/{chunk-2ZJ7TSW4.js → chunk-RLUJL2MV.js} +4 -8
- package/dist/{chunk-CR4MXPHB.js → chunk-V2MTG5FT.js} +99 -36
- package/dist/{chunk-CNZ35WI2.js → chunk-VJE6DDYM.js} +2 -2
- package/dist/{chunk-PMSDFTK3.js → chunk-VOCE4NNK.js} +157 -75
- package/dist/{chunk-WKUXSE7V.js → chunk-X67UYC74.js} +12 -11
- package/dist/{chunk-EFOAE5NC.js → chunk-YFDGQWDA.js} +1 -1
- package/dist/{chunk-3U4ZVXVD.js → chunk-Z2FWAE4B.js} +6 -2
- package/dist/data.cjs +190 -94
- package/dist/data.d.cts +7 -1
- package/dist/data.d.ts +7 -1
- package/dist/data.js +8 -8
- package/dist/devtools.cjs +38 -10
- package/dist/devtools.d.cts +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.js +6 -6
- package/dist/ecosystem.cjs +123 -63
- package/dist/ecosystem.js +9 -9
- package/dist/extras.cjs +380 -196
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +27 -24
- package/dist/index.cjs +214 -136
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +15 -13
- package/dist/{introspect-BZWKvQUZ.d.ts → introspect-DOZfmC-4.d.ts} +1 -1
- package/dist/{introspect-DsJlDD2T.d.cts → introspect-RjLfIFpL.d.cts} +1 -1
- package/dist/motion.cjs +10 -0
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +45 -40
- package/dist/patterns.js +8 -7
- package/dist/performance.cjs +101 -25
- package/dist/performance.d.cts +2 -2
- package/dist/performance.d.ts +2 -2
- package/dist/performance.js +8 -7
- package/dist/plugins.cjs +234 -160
- package/dist/plugins.d.cts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +127 -69
- package/dist/{ssr-FXD2PPMC.js → ssr-2QDQ27EV.js} +5 -3
- package/dist/{ssr-CrVNy6Pa.d.cts → ssr-D62yFwuw.d.cts} +8 -1
- package/dist/{ssr-CrVNy6Pa.d.ts → ssr-D62yFwuw.d.ts} +8 -1
- package/dist/ssr.cjs +145 -66
- package/dist/ssr.d.cts +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/ssr.js +12 -10
- package/dist/testing.cjs +9 -4
- package/dist/testing.js +3 -3
- package/dist/ui.cjs +54 -38
- package/dist/ui.js +10 -9
- package/dist/widgets.cjs +40 -24
- package/dist/widgets.js +8 -8
- package/package.json +3 -1
package/dist/plugins.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as TrustedHTML } from './ssr-
|
|
1
|
+
import { T as TrustedHTML } from './ssr-D62yFwuw.cjs';
|
|
2
2
|
export { P as PluginContext, a as PluginRegistry, S as SibuPlugin, c as createPlugin, b as createPluginRegistry, i as inject, p as plugin, r as resetPlugins, s as setDefaultPluginRegistry, t as triggerPluginError, d as triggerPluginMount, e as triggerPluginUnmount } from './plugin-D30wlGW5.cjs';
|
|
3
3
|
export { M as Migration, S as SemVer, V as VERSION, b as bundlerMetadata, c as checkCompatibility, a as compareSemVer, d as createBootSequence, e as createBundle, f as createMigrationRunner, g as createModuleRegistry, h as createSSRCache, i as createTestHarness, j as deferNonCritical, k as env, l as healthCheck, m as lazyModule, p as packageInfo, n as parseSemVer, o as preloadCritical, q as prerenderRoutes, s as satisfies } from './startup-0Qv6aosO.cjs';
|
|
4
4
|
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as TrustedHTML } from './ssr-
|
|
1
|
+
import { T as TrustedHTML } from './ssr-D62yFwuw.js';
|
|
2
2
|
export { P as PluginContext, a as PluginRegistry, S as SibuPlugin, c as createPlugin, b as createPluginRegistry, i as inject, p as plugin, r as resetPlugins, s as setDefaultPluginRegistry, t as triggerPluginError, d as triggerPluginMount, e as triggerPluginUnmount } from './plugin-D30wlGW5.js';
|
|
3
3
|
export { M as Migration, S as SemVer, V as VERSION, b as bundlerMetadata, c as checkCompatibility, a as compareSemVer, d as createBootSequence, e as createBundle, f as createMigrationRunner, g as createModuleRegistry, h as createSSRCache, i as createTestHarness, j as deferNonCritical, k as env, l as healthCheck, m as lazyModule, p as packageInfo, n as parseSemVer, o as preloadCritical, q as prerenderRoutes, s as satisfies } from './startup-0Qv6aosO.js';
|
|
4
4
|
|
package/dist/plugins.js
CHANGED
|
@@ -18,7 +18,10 @@ import {
|
|
|
18
18
|
preloadCritical,
|
|
19
19
|
prerenderRoutes,
|
|
20
20
|
satisfies
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-RDRSWYNP.js";
|
|
22
|
+
import {
|
|
23
|
+
isUnsafeKey
|
|
24
|
+
} from "./chunk-H3SRKIYX.js";
|
|
22
25
|
import {
|
|
23
26
|
createPlugin,
|
|
24
27
|
createPluginRegistry,
|
|
@@ -32,30 +35,34 @@ import {
|
|
|
32
35
|
} from "./chunk-3JHCYHWN.js";
|
|
33
36
|
import {
|
|
34
37
|
span
|
|
35
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-5N74TKLD.js";
|
|
36
39
|
import {
|
|
37
40
|
escapeScriptJson,
|
|
41
|
+
isDangerousMetaRefresh,
|
|
38
42
|
renderToString
|
|
39
|
-
} from "./chunk-
|
|
40
|
-
import "./chunk-
|
|
41
|
-
import "./chunk-
|
|
43
|
+
} from "./chunk-HXMS4SNP.js";
|
|
44
|
+
import "./chunk-X67UYC74.js";
|
|
45
|
+
import "./chunk-RLUJL2MV.js";
|
|
42
46
|
import {
|
|
43
47
|
dispose,
|
|
44
48
|
registerDisposer
|
|
45
49
|
} from "./chunk-2UPRY23K.js";
|
|
46
50
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
isUrlAttribute,
|
|
52
|
+
sanitizeCSSValue,
|
|
53
|
+
sanitizeUrl,
|
|
54
|
+
stripControlChars
|
|
55
|
+
} from "./chunk-HMJFCBRR.js";
|
|
49
56
|
import {
|
|
50
57
|
effect
|
|
51
|
-
} from "./chunk-
|
|
52
|
-
import "./chunk-
|
|
58
|
+
} from "./chunk-FDY42FIU.js";
|
|
59
|
+
import "./chunk-GOJMFRBL.js";
|
|
53
60
|
import {
|
|
54
61
|
signal
|
|
55
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-C427DVQF.js";
|
|
56
63
|
import {
|
|
57
64
|
track
|
|
58
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-Z2FWAE4B.js";
|
|
59
66
|
import "./chunk-LMLD24FC.js";
|
|
60
67
|
|
|
61
68
|
// src/plugins/i18n.ts
|
|
@@ -89,7 +96,9 @@ function getAvailableLocales() {
|
|
|
89
96
|
// src/plugins/router.ts
|
|
90
97
|
function isSafeNavigationTarget(path) {
|
|
91
98
|
if (path === "") return true;
|
|
92
|
-
|
|
99
|
+
const normalized = stripControlChars(path).replace(/\\/g, "/");
|
|
100
|
+
if (normalized.startsWith("//")) return false;
|
|
101
|
+
return sanitizeUrl(normalized) !== "";
|
|
93
102
|
}
|
|
94
103
|
var LRUCache = class {
|
|
95
104
|
constructor(maxSize = 100) {
|
|
@@ -226,8 +235,13 @@ var RouteMatcher = class {
|
|
|
226
235
|
if (match) {
|
|
227
236
|
const params = {};
|
|
228
237
|
compiled.keys.forEach((key, i) => {
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
const raw = match[i + 1];
|
|
239
|
+
if (raw !== void 0) {
|
|
240
|
+
try {
|
|
241
|
+
params[key] = decodeURIComponent(raw);
|
|
242
|
+
} catch {
|
|
243
|
+
params[key] = raw;
|
|
244
|
+
}
|
|
231
245
|
}
|
|
232
246
|
});
|
|
233
247
|
return { params };
|
|
@@ -278,14 +292,23 @@ var RouteMatcher = class {
|
|
|
278
292
|
}
|
|
279
293
|
}
|
|
280
294
|
removeRoute(path) {
|
|
281
|
-
this.routeTrie.delete(path);
|
|
282
|
-
this.parentChain.delete(path);
|
|
283
295
|
this.compiledPatterns.clear();
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
296
|
+
const root = this.routeTrie.get(path);
|
|
297
|
+
if (!root) return;
|
|
298
|
+
const removed = /* @__PURE__ */ new Set();
|
|
299
|
+
const collect = (r) => {
|
|
300
|
+
removed.add(r);
|
|
301
|
+
if (r.children) for (const child of r.children) collect(child);
|
|
302
|
+
};
|
|
303
|
+
collect(root);
|
|
304
|
+
for (const [key, route2] of [...this.routeTrie]) {
|
|
305
|
+
if (removed.has(route2)) this.routeTrie.delete(key);
|
|
306
|
+
}
|
|
307
|
+
for (const [key, chain] of [...this.parentChain]) {
|
|
308
|
+
if (chain.length > 0 && removed.has(chain[chain.length - 1])) this.parentChain.delete(key);
|
|
309
|
+
}
|
|
310
|
+
for (const [name, route2] of [...this.namedRoutes]) {
|
|
311
|
+
if (removed.has(route2)) this.namedRoutes.delete(name);
|
|
289
312
|
}
|
|
290
313
|
}
|
|
291
314
|
};
|
|
@@ -397,42 +420,61 @@ var GuardManager = class {
|
|
|
397
420
|
this.afterEachHooks = [];
|
|
398
421
|
}
|
|
399
422
|
};
|
|
400
|
-
var
|
|
423
|
+
var _ComponentLoader = class _ComponentLoader {
|
|
401
424
|
constructor(cacheSize = 50, retryDelay = 1e3) {
|
|
402
425
|
this.errorCache = /* @__PURE__ */ new Map();
|
|
403
426
|
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
427
|
+
// Stable per-route-definition id. Caching by the RESOLVED path (e.g.
|
|
428
|
+
// /users/123) gave the cache one entry per visited URL, so parameterized
|
|
429
|
+
// routes thrashed/evicted and the component was reloaded every navigation.
|
|
430
|
+
// Keying by route-definition identity makes the cache effective again.
|
|
431
|
+
this.routeKeys = /* @__PURE__ */ new WeakMap();
|
|
432
|
+
this.keyCounter = 0;
|
|
404
433
|
this.componentCache = new LRUCache(cacheSize);
|
|
405
434
|
this.retryDelay = retryDelay;
|
|
406
435
|
}
|
|
436
|
+
keyFor(route2) {
|
|
437
|
+
let key = this.routeKeys.get(route2);
|
|
438
|
+
if (key === void 0) {
|
|
439
|
+
key = `route#${this.keyCounter++}`;
|
|
440
|
+
this.routeKeys.set(route2, key);
|
|
441
|
+
}
|
|
442
|
+
return key;
|
|
443
|
+
}
|
|
407
444
|
async loadComponent(route2, routePath) {
|
|
408
445
|
if (!("component" in route2)) {
|
|
409
446
|
throw new Error(`Route ${routePath} does not have a component`);
|
|
410
447
|
}
|
|
411
448
|
const comp = route2.component;
|
|
412
|
-
const
|
|
449
|
+
const cacheKey = this.keyFor(route2);
|
|
450
|
+
const cached = this.componentCache.get(cacheKey);
|
|
413
451
|
if (cached) return cached;
|
|
414
|
-
const existingPromise = this.loadingPromises.get(
|
|
452
|
+
const existingPromise = this.loadingPromises.get(cacheKey);
|
|
415
453
|
if (existingPromise) return existingPromise;
|
|
416
|
-
const errorInfo = this.errorCache.get(
|
|
454
|
+
const errorInfo = this.errorCache.get(cacheKey);
|
|
417
455
|
if (errorInfo && Date.now() - errorInfo.timestamp < this.retryDelay) {
|
|
418
456
|
throw new Error(`Component loading failed recently, retry in ${this.retryDelay}ms`);
|
|
419
457
|
}
|
|
420
458
|
const loadingPromise = this.doLoadComponent(comp, routePath);
|
|
421
|
-
this.loadingPromises.set(
|
|
459
|
+
this.loadingPromises.set(cacheKey, loadingPromise);
|
|
422
460
|
try {
|
|
423
461
|
const component = await loadingPromise;
|
|
424
|
-
this.componentCache.set(
|
|
425
|
-
this.errorCache.delete(
|
|
462
|
+
this.componentCache.set(cacheKey, component);
|
|
463
|
+
this.errorCache.delete(cacheKey);
|
|
426
464
|
return component;
|
|
427
465
|
} catch (error) {
|
|
428
|
-
const currentError = this.errorCache.get(
|
|
429
|
-
this.errorCache.
|
|
466
|
+
const currentError = this.errorCache.get(cacheKey) || { timestamp: 0, count: 0 };
|
|
467
|
+
if (!this.errorCache.has(cacheKey) && this.errorCache.size >= _ComponentLoader.MAX_ERROR_ENTRIES) {
|
|
468
|
+
const oldest = this.errorCache.keys().next().value;
|
|
469
|
+
if (oldest !== void 0) this.errorCache.delete(oldest);
|
|
470
|
+
}
|
|
471
|
+
this.errorCache.set(cacheKey, {
|
|
430
472
|
timestamp: Date.now(),
|
|
431
473
|
count: currentError.count + 1
|
|
432
474
|
});
|
|
433
475
|
throw error;
|
|
434
476
|
} finally {
|
|
435
|
-
this.loadingPromises.delete(
|
|
477
|
+
this.loadingPromises.delete(cacheKey);
|
|
436
478
|
}
|
|
437
479
|
}
|
|
438
480
|
async doLoadComponent(comp, routePath) {
|
|
@@ -486,6 +528,8 @@ var ComponentLoader = class {
|
|
|
486
528
|
this.loadingPromises.clear();
|
|
487
529
|
}
|
|
488
530
|
};
|
|
531
|
+
_ComponentLoader.MAX_ERROR_ENTRIES = 256;
|
|
532
|
+
var ComponentLoader = _ComponentLoader;
|
|
489
533
|
var _SibuRouter = class _SibuRouter {
|
|
490
534
|
constructor(routes, options = {}) {
|
|
491
535
|
// Event listeners cleanup
|
|
@@ -551,7 +595,7 @@ var _SibuRouter = class _SibuRouter {
|
|
|
551
595
|
return window.location.hash.slice(1) || "/";
|
|
552
596
|
}
|
|
553
597
|
let path = window.location.pathname;
|
|
554
|
-
if (base && path.startsWith(base)) {
|
|
598
|
+
if (base && (path === base || path.startsWith(`${base}/`))) {
|
|
555
599
|
path = path.slice(base.length);
|
|
556
600
|
}
|
|
557
601
|
return (path || "/") + window.location.search + window.location.hash;
|
|
@@ -684,7 +728,7 @@ var _SibuRouter = class _SibuRouter {
|
|
|
684
728
|
}
|
|
685
729
|
if (to.params) {
|
|
686
730
|
for (const [key, value] of Object.entries(to.params)) {
|
|
687
|
-
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
731
|
+
path = path.replace(new RegExp(`:${key}(?=[/?#]|$)`, "g"), encodeURIComponent(value));
|
|
688
732
|
}
|
|
689
733
|
}
|
|
690
734
|
if (to.query && Object.keys(to.query).length > 0) {
|
|
@@ -920,8 +964,7 @@ function Route() {
|
|
|
920
964
|
let currentNode = null;
|
|
921
965
|
let loadingNode = null;
|
|
922
966
|
let errorNode = null;
|
|
923
|
-
let
|
|
924
|
-
let currentPath = "";
|
|
967
|
+
let navSeq = 0;
|
|
925
968
|
let currentTopRoute = null;
|
|
926
969
|
const cleanupNodes = () => {
|
|
927
970
|
[currentNode, loadingNode, errorNode].forEach((node) => {
|
|
@@ -999,30 +1042,21 @@ function Route() {
|
|
|
999
1042
|
errorNode.appendChild(retryButton);
|
|
1000
1043
|
anchor.parentNode.insertBefore(errorNode, anchor.nextSibling);
|
|
1001
1044
|
};
|
|
1002
|
-
let pendingUpdate = false;
|
|
1003
1045
|
const update = async () => {
|
|
1004
1046
|
if (!globalRouter) return;
|
|
1005
|
-
|
|
1006
|
-
pendingUpdate = true;
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1047
|
+
const seq = ++navSeq;
|
|
1009
1048
|
const route2 = globalRouter.currentRoute;
|
|
1010
1049
|
try {
|
|
1011
1050
|
const match = globalRouter["matcher"].match(route2.path);
|
|
1012
1051
|
if (!match) {
|
|
1013
|
-
currentPath = route2.path;
|
|
1014
1052
|
currentTopRoute = null;
|
|
1015
1053
|
cleanupNodes();
|
|
1016
1054
|
return;
|
|
1017
1055
|
}
|
|
1018
1056
|
const routeDef = match.matched[0] || match.route;
|
|
1019
1057
|
if (routeDef === currentTopRoute && currentNode) {
|
|
1020
|
-
currentPath = route2.path;
|
|
1021
1058
|
return;
|
|
1022
1059
|
}
|
|
1023
|
-
isUpdating = true;
|
|
1024
|
-
currentPath = route2.path;
|
|
1025
|
-
currentTopRoute = routeDef;
|
|
1026
1060
|
if ("redirect" in routeDef) {
|
|
1027
1061
|
const redirectPath = typeof routeDef.redirect === "function" ? routeDef.redirect(route2) : routeDef.redirect;
|
|
1028
1062
|
queueMicrotask(() => {
|
|
@@ -1039,27 +1073,25 @@ function Route() {
|
|
|
1039
1073
|
showLoading();
|
|
1040
1074
|
}
|
|
1041
1075
|
const component = await globalRouter.loadComponent(routeDef, route2.path);
|
|
1076
|
+
if (seq !== navSeq) return;
|
|
1042
1077
|
const node = component();
|
|
1043
|
-
if (node && anchor.parentNode
|
|
1078
|
+
if (node && anchor.parentNode) {
|
|
1079
|
+
currentTopRoute = routeDef;
|
|
1044
1080
|
cleanupNodes();
|
|
1045
1081
|
anchor.parentNode.insertBefore(node, anchor.nextSibling);
|
|
1046
1082
|
currentNode = node;
|
|
1047
1083
|
}
|
|
1048
1084
|
} catch (error) {
|
|
1085
|
+
if (seq !== navSeq) return;
|
|
1049
1086
|
hideLoading();
|
|
1050
1087
|
console.error("[Route] Component error:", error);
|
|
1051
1088
|
showError(error instanceof Error ? error : new Error(String(error)), routeDef);
|
|
1052
1089
|
}
|
|
1053
1090
|
}
|
|
1054
1091
|
} catch (error) {
|
|
1092
|
+
if (seq !== navSeq) return;
|
|
1055
1093
|
console.error("[Route] Update failed:", error);
|
|
1056
1094
|
showError(error instanceof Error ? error : new Error(String(error)));
|
|
1057
|
-
} finally {
|
|
1058
|
-
isUpdating = false;
|
|
1059
|
-
if (pendingUpdate) {
|
|
1060
|
-
pendingUpdate = false;
|
|
1061
|
-
update();
|
|
1062
|
-
}
|
|
1063
1095
|
}
|
|
1064
1096
|
};
|
|
1065
1097
|
let routeInitialized = false;
|
|
@@ -1114,7 +1146,8 @@ function KeepAliveRoute(options) {
|
|
|
1114
1146
|
return;
|
|
1115
1147
|
}
|
|
1116
1148
|
if (!("component" in routeDef)) return;
|
|
1117
|
-
const
|
|
1149
|
+
const queryStr = Object.keys(route2.query).length > 0 ? `?${new URLSearchParams(route2.query).toString()}` : "";
|
|
1150
|
+
const cacheKey = `${route2.path}${queryStr}${route2.hash ? `#${route2.hash}` : ""}`;
|
|
1118
1151
|
const shouldCache = !includeNames || routeDef.name != null && includeNames.includes(routeDef.name);
|
|
1119
1152
|
if (cacheKey === currentKey && currentNode) return;
|
|
1120
1153
|
isUpdating = true;
|
|
@@ -1204,7 +1237,8 @@ function RouterLink(props) {
|
|
|
1204
1237
|
const { to, replace: replace2 = false, activeClass, exactActiveClass, nodes, target, rel, class: classAttr, ...attrs } = props;
|
|
1205
1238
|
const baseClass = typeof classAttr === "string" ? classAttr : "";
|
|
1206
1239
|
const routeGetter = globalRouter.routeGetter;
|
|
1207
|
-
const
|
|
1240
|
+
const rawHref = globalRouter["resolvePath"](to);
|
|
1241
|
+
const href = isSafeNavigationTarget(rawHref) ? rawHref : "#";
|
|
1208
1242
|
const hrefPath = href.split("?")[0].split("#")[0];
|
|
1209
1243
|
const link = document.createElement("a");
|
|
1210
1244
|
link.href = href;
|
|
@@ -1236,9 +1270,18 @@ function RouterLink(props) {
|
|
|
1236
1270
|
});
|
|
1237
1271
|
registerDisposer(link, effectCleanup);
|
|
1238
1272
|
Object.entries(attrs).forEach(([key, value]) => {
|
|
1239
|
-
|
|
1273
|
+
const lkey = key.toLowerCase();
|
|
1274
|
+
if (lkey === "href" || lkey[0] === "o" && lkey[1] === "n") return;
|
|
1240
1275
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1241
|
-
|
|
1276
|
+
const str = String(value);
|
|
1277
|
+
if (isUrlAttribute(lkey)) {
|
|
1278
|
+
const safe = sanitizeUrl(str);
|
|
1279
|
+
if (safe) link.setAttribute(key, safe);
|
|
1280
|
+
} else if (lkey === "style") {
|
|
1281
|
+
link.setAttribute(key, sanitizeCSSValue(str));
|
|
1282
|
+
} else {
|
|
1283
|
+
link.setAttribute(key, str);
|
|
1284
|
+
}
|
|
1242
1285
|
}
|
|
1243
1286
|
});
|
|
1244
1287
|
if (typeof nodes === "string") {
|
|
@@ -1395,24 +1438,43 @@ function __removeRouterPagehideHandler() {
|
|
|
1395
1438
|
function Outlet() {
|
|
1396
1439
|
const anchor = document.createComment("route-outlet-nested");
|
|
1397
1440
|
let currentNode = null;
|
|
1441
|
+
let currentChild = null;
|
|
1442
|
+
let navSeq = 0;
|
|
1443
|
+
const clearCurrent = () => {
|
|
1444
|
+
if (currentNode) {
|
|
1445
|
+
dispose(currentNode);
|
|
1446
|
+
if (currentNode.parentNode) currentNode.parentNode.removeChild(currentNode);
|
|
1447
|
+
currentNode = null;
|
|
1448
|
+
}
|
|
1449
|
+
currentChild = null;
|
|
1450
|
+
};
|
|
1398
1451
|
const update = async () => {
|
|
1399
1452
|
if (!globalRouter) return;
|
|
1453
|
+
const seq = ++navSeq;
|
|
1400
1454
|
const route2 = globalRouter.currentRoute;
|
|
1401
|
-
if (route2.matched.length < 2)
|
|
1455
|
+
if (route2.matched.length < 2) {
|
|
1456
|
+
clearCurrent();
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1402
1459
|
const childRoute = route2.matched[route2.matched.length - 1];
|
|
1403
|
-
if (!childRoute || !("component" in childRoute))
|
|
1460
|
+
if (!childRoute || !("component" in childRoute)) {
|
|
1461
|
+
clearCurrent();
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
if (childRoute === currentChild && currentNode) return;
|
|
1404
1465
|
try {
|
|
1405
1466
|
const cacheKey = `${route2.path}\0${childRoute.path}`;
|
|
1406
1467
|
const component = await globalRouter.loadComponent(childRoute, cacheKey);
|
|
1468
|
+
if (seq !== navSeq) return;
|
|
1407
1469
|
const node = component();
|
|
1408
1470
|
if (node && anchor.parentNode) {
|
|
1409
|
-
|
|
1410
|
-
currentNode.parentNode.removeChild(currentNode);
|
|
1411
|
-
}
|
|
1471
|
+
clearCurrent();
|
|
1412
1472
|
anchor.parentNode.insertBefore(node, anchor.nextSibling);
|
|
1413
1473
|
currentNode = node;
|
|
1474
|
+
currentChild = childRoute;
|
|
1414
1475
|
}
|
|
1415
1476
|
} catch (error) {
|
|
1477
|
+
if (seq !== navSeq) return;
|
|
1416
1478
|
console.error("[Outlet] Failed to render child route:", error);
|
|
1417
1479
|
}
|
|
1418
1480
|
};
|
|
@@ -1488,10 +1550,6 @@ function createMemoryRouter(routes, _initialPath = "/") {
|
|
|
1488
1550
|
}
|
|
1489
1551
|
|
|
1490
1552
|
// src/plugins/routerSSR.ts
|
|
1491
|
-
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1492
|
-
function isForbiddenKey(key) {
|
|
1493
|
-
return FORBIDDEN_KEYS.has(key);
|
|
1494
|
-
}
|
|
1495
1553
|
function safeDecode(raw) {
|
|
1496
1554
|
try {
|
|
1497
1555
|
return decodeURIComponent(raw);
|
|
@@ -1532,7 +1590,7 @@ function parseURL(url) {
|
|
|
1532
1590
|
key = safeDecode(pair.slice(0, eqIndex));
|
|
1533
1591
|
value = safeDecode(pair.slice(eqIndex + 1));
|
|
1534
1592
|
}
|
|
1535
|
-
if (
|
|
1593
|
+
if (isUnsafeKey(key)) continue;
|
|
1536
1594
|
query[key] = value;
|
|
1537
1595
|
}
|
|
1538
1596
|
}
|
|
@@ -1594,7 +1652,7 @@ function matchRoute(path, routes, parentPath = "", parentChain = []) {
|
|
|
1594
1652
|
const params = nullObject();
|
|
1595
1653
|
for (let i = 0; i < compiled.keys.length; i++) {
|
|
1596
1654
|
const key = compiled.keys[i];
|
|
1597
|
-
if (
|
|
1655
|
+
if (isUnsafeKey(key)) continue;
|
|
1598
1656
|
if (match[i + 1] !== void 0) {
|
|
1599
1657
|
params[key] = safeDecode(match[i + 1]);
|
|
1600
1658
|
}
|
|
@@ -1691,7 +1749,7 @@ function renderRouteToString(url, routes, _options) {
|
|
|
1691
1749
|
function renderRouteToDocument(url, routes, options) {
|
|
1692
1750
|
const { html, state } = renderRouteToString(url, routes, options);
|
|
1693
1751
|
const opts = options || {};
|
|
1694
|
-
const metaTags = (opts.meta || []).map((attrs) => {
|
|
1752
|
+
const metaTags = (opts.meta || []).filter((attrs) => !isDangerousMetaRefresh(attrs)).map((attrs) => {
|
|
1695
1753
|
const pairs = buildSafeAttrString(attrs);
|
|
1696
1754
|
return pairs ? `<meta ${pairs} />` : "";
|
|
1697
1755
|
}).filter(Boolean).join("\n ");
|
|
@@ -1742,7 +1800,7 @@ function hydrateRouter(routes, options) {
|
|
|
1742
1800
|
if (container && serverState.path) {
|
|
1743
1801
|
const resolved = resolveServerRoute(serverState.path, routes);
|
|
1744
1802
|
if (resolved.component) {
|
|
1745
|
-
import("./ssr-
|
|
1803
|
+
import("./ssr-2QDQ27EV.js").then(({ hydrate }) => {
|
|
1746
1804
|
if (resolved.component) {
|
|
1747
1805
|
hydrate(resolved.component, container);
|
|
1748
1806
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
hydrate,
|
|
6
6
|
hydrateIslands,
|
|
7
7
|
hydrateProgressively,
|
|
8
|
+
isDangerousMetaRefresh,
|
|
8
9
|
island,
|
|
9
10
|
renderToDocument,
|
|
10
11
|
renderToReadableStream,
|
|
@@ -16,9 +17,9 @@ import {
|
|
|
16
17
|
ssrSuspense,
|
|
17
18
|
suspenseSwapScript,
|
|
18
19
|
trustHTML
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-HXMS4SNP.js";
|
|
21
|
+
import "./chunk-HMJFCBRR.js";
|
|
22
|
+
import "./chunk-GOJMFRBL.js";
|
|
22
23
|
import "./chunk-LMLD24FC.js";
|
|
23
24
|
export {
|
|
24
25
|
collectStream,
|
|
@@ -27,6 +28,7 @@ export {
|
|
|
27
28
|
hydrate,
|
|
28
29
|
hydrateIslands,
|
|
29
30
|
hydrateProgressively,
|
|
31
|
+
isDangerousMetaRefresh,
|
|
30
32
|
island,
|
|
31
33
|
renderToDocument,
|
|
32
34
|
renderToReadableStream,
|
|
@@ -57,6 +57,13 @@ type TrustedHTML = string & {
|
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
59
|
declare function trustHTML(html: string): TrustedHTML;
|
|
60
|
+
/**
|
|
61
|
+
* Detect `<meta http-equiv="refresh" content="0;url=javascript:...">`.
|
|
62
|
+
* Returns true if the props describe a refresh directive whose URL uses
|
|
63
|
+
* a dangerous protocol — in which case the entire meta entry must be
|
|
64
|
+
* dropped to avoid an XSS vector via the browser refresh mechanism.
|
|
65
|
+
*/
|
|
66
|
+
declare function isDangerousMetaRefresh(metaProps: Record<string, string>): boolean;
|
|
60
67
|
/**
|
|
61
68
|
* Renders a component to a full HTML document string.
|
|
62
69
|
*
|
|
@@ -192,4 +199,4 @@ declare function serializeState(state: Record<string, unknown>, nonce?: string,
|
|
|
192
199
|
*/
|
|
193
200
|
declare function deserializeState<T = Record<string, unknown>>(validate?: (data: unknown) => data is T): T | undefined;
|
|
194
201
|
|
|
195
|
-
export { type HydrateOptions as H, type TrustedHTML as T, type HydrationMismatch as a, hydrateIslands as b, collectStream as c, deserializeState as d, escapeScriptJson as e, hydrateProgressively as f,
|
|
202
|
+
export { type HydrateOptions as H, type TrustedHTML as T, type HydrationMismatch as a, hydrateIslands as b, collectStream as c, deserializeState as d, escapeScriptJson as e, hydrateProgressively as f, island as g, hydrate as h, isDangerousMetaRefresh as i, renderToReadableStream as j, renderToStream as k, renderToString as l, renderToSuspenseStream as m, resetSSRState as n, ssrSuspense as o, suspenseSwapScript as p, renderToDocument as r, serializeState as s, trustHTML as t };
|
|
@@ -57,6 +57,13 @@ type TrustedHTML = string & {
|
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
59
|
declare function trustHTML(html: string): TrustedHTML;
|
|
60
|
+
/**
|
|
61
|
+
* Detect `<meta http-equiv="refresh" content="0;url=javascript:...">`.
|
|
62
|
+
* Returns true if the props describe a refresh directive whose URL uses
|
|
63
|
+
* a dangerous protocol — in which case the entire meta entry must be
|
|
64
|
+
* dropped to avoid an XSS vector via the browser refresh mechanism.
|
|
65
|
+
*/
|
|
66
|
+
declare function isDangerousMetaRefresh(metaProps: Record<string, string>): boolean;
|
|
60
67
|
/**
|
|
61
68
|
* Renders a component to a full HTML document string.
|
|
62
69
|
*
|
|
@@ -192,4 +199,4 @@ declare function serializeState(state: Record<string, unknown>, nonce?: string,
|
|
|
192
199
|
*/
|
|
193
200
|
declare function deserializeState<T = Record<string, unknown>>(validate?: (data: unknown) => data is T): T | undefined;
|
|
194
201
|
|
|
195
|
-
export { type HydrateOptions as H, type TrustedHTML as T, type HydrationMismatch as a, hydrateIslands as b, collectStream as c, deserializeState as d, escapeScriptJson as e, hydrateProgressively as f,
|
|
202
|
+
export { type HydrateOptions as H, type TrustedHTML as T, type HydrationMismatch as a, hydrateIslands as b, collectStream as c, deserializeState as d, escapeScriptJson as e, hydrateProgressively as f, island as g, hydrate as h, isDangerousMetaRefresh as i, renderToReadableStream as j, renderToStream as k, renderToString as l, renderToSuspenseStream as m, resetSSRState as n, ssrSuspense as o, suspenseSwapScript as p, renderToDocument as r, serializeState as s, trustHTML as t };
|