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.cjs
CHANGED
|
@@ -43,8 +43,16 @@ var init_dev = __esm({
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
// src/utils/sanitize.ts
|
|
46
|
+
function stripControlChars(value) {
|
|
47
|
+
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
48
|
+
}
|
|
49
|
+
function isEventHandlerAttr(name) {
|
|
50
|
+
if (name.length < 3) return false;
|
|
51
|
+
const lower = name.toLowerCase();
|
|
52
|
+
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
53
|
+
}
|
|
46
54
|
function sanitizeUrl(url) {
|
|
47
|
-
const trimmed = url
|
|
55
|
+
const trimmed = stripControlChars(url).trim();
|
|
48
56
|
if (!trimmed) return "";
|
|
49
57
|
const lower = trimmed.toLowerCase();
|
|
50
58
|
let schemeEnd = -1;
|
|
@@ -93,7 +101,7 @@ function sanitizeCSSValue(value) {
|
|
|
93
101
|
return value;
|
|
94
102
|
}
|
|
95
103
|
function isUrlAttribute(attr) {
|
|
96
|
-
return URL_ATTRIBUTES.has(attr);
|
|
104
|
+
return URL_ATTRIBUTES.has(attr.toLowerCase());
|
|
97
105
|
}
|
|
98
106
|
var SAFE_URL_PROTOCOLS, URL_ATTRIBUTES;
|
|
99
107
|
var init_sanitize = __esm({
|
|
@@ -135,11 +143,15 @@ var init_ssr_context = __esm({
|
|
|
135
143
|
als = null;
|
|
136
144
|
try {
|
|
137
145
|
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
let mod = null;
|
|
147
|
+
const getBuiltin = process.getBuiltinModule;
|
|
148
|
+
if (typeof getBuiltin === "function") {
|
|
149
|
+
mod = getBuiltin("node:async_hooks");
|
|
150
|
+
} else {
|
|
151
|
+
const req = Function("return typeof require==='function'?require:null")();
|
|
152
|
+
if (req) mod = req("node:async_hooks");
|
|
142
153
|
}
|
|
154
|
+
if (mod) als = new mod.AsyncLocalStorage();
|
|
143
155
|
}
|
|
144
156
|
} catch {
|
|
145
157
|
als = null;
|
|
@@ -157,6 +169,7 @@ __export(ssr_exports, {
|
|
|
157
169
|
hydrate: () => hydrate,
|
|
158
170
|
hydrateIslands: () => hydrateIslands,
|
|
159
171
|
hydrateProgressively: () => hydrateProgressively,
|
|
172
|
+
isDangerousMetaRefresh: () => isDangerousMetaRefresh,
|
|
160
173
|
island: () => island,
|
|
161
174
|
renderToDocument: () => renderToDocument,
|
|
162
175
|
renderToReadableStream: () => renderToReadableStream,
|
|
@@ -169,14 +182,12 @@ __export(ssr_exports, {
|
|
|
169
182
|
suspenseSwapScript: () => suspenseSwapScript,
|
|
170
183
|
trustHTML: () => trustHTML
|
|
171
184
|
});
|
|
185
|
+
function sanitizeUrlAttr(name, value) {
|
|
186
|
+
return name === "srcset" ? sanitizeSrcset(value) : sanitizeUrl(value);
|
|
187
|
+
}
|
|
172
188
|
function isSafeAttrName(name) {
|
|
173
189
|
return SAFE_ATTR_NAME.test(name);
|
|
174
190
|
}
|
|
175
|
-
function isEventHandlerAttr2(name) {
|
|
176
|
-
if (name.length < 3) return false;
|
|
177
|
-
const lower = name.toLowerCase();
|
|
178
|
-
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
179
|
-
}
|
|
180
191
|
function ssrErrorComment(err) {
|
|
181
192
|
if (_isDev8) {
|
|
182
193
|
const msg = escapeHtml(err instanceof Error ? err.message : String(err));
|
|
@@ -217,11 +228,11 @@ function renderToString(element) {
|
|
|
217
228
|
for (const attr of Array.from(element.attributes)) {
|
|
218
229
|
const rawName = attr.name;
|
|
219
230
|
if (!isSafeAttrName(rawName)) continue;
|
|
220
|
-
if (
|
|
231
|
+
if (isEventHandlerAttr(rawName)) continue;
|
|
221
232
|
const lowerName = rawName.toLowerCase();
|
|
222
233
|
let value = attr.value;
|
|
223
234
|
if (URL_ATTRS.has(lowerName)) {
|
|
224
|
-
value =
|
|
235
|
+
value = sanitizeUrlAttr(lowerName, value);
|
|
225
236
|
if (!value) continue;
|
|
226
237
|
}
|
|
227
238
|
html2 += ` ${rawName}="${escapeAttr(value)}"`;
|
|
@@ -358,11 +369,11 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
358
369
|
for (const rawKey of Object.keys(attrs)) {
|
|
359
370
|
if (!Object.hasOwn(attrs, rawKey)) continue;
|
|
360
371
|
if (!isSafeAttrName(rawKey)) continue;
|
|
361
|
-
if (!allowEventHandlers &&
|
|
372
|
+
if (!allowEventHandlers && isEventHandlerAttr(rawKey)) continue;
|
|
362
373
|
const lowerKey = rawKey.toLowerCase();
|
|
363
374
|
let value = String(attrs[rawKey]);
|
|
364
375
|
if (URL_ATTRS.has(lowerKey)) {
|
|
365
|
-
value =
|
|
376
|
+
value = sanitizeUrlAttr(lowerKey, value);
|
|
366
377
|
if (!value) continue;
|
|
367
378
|
}
|
|
368
379
|
out.push(`${rawKey}="${escapeAttr(value)}"`);
|
|
@@ -370,12 +381,17 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
370
381
|
return out.join(" ");
|
|
371
382
|
}
|
|
372
383
|
function isDangerousMetaRefresh(metaProps) {
|
|
373
|
-
|
|
384
|
+
let httpEquiv;
|
|
385
|
+
let content;
|
|
386
|
+
for (const k in metaProps) {
|
|
387
|
+
const lk = k.toLowerCase();
|
|
388
|
+
if (lk === "http-equiv") httpEquiv = metaProps[k];
|
|
389
|
+
else if (lk === "content") content = metaProps[k];
|
|
390
|
+
}
|
|
374
391
|
if (typeof httpEquiv !== "string") return false;
|
|
375
392
|
if (httpEquiv.toLowerCase() !== "refresh") return false;
|
|
376
|
-
const content = metaProps.content;
|
|
377
393
|
if (typeof content !== "string") return false;
|
|
378
|
-
const normalized = content
|
|
394
|
+
const normalized = stripControlChars(content).toLowerCase();
|
|
379
395
|
return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
|
|
380
396
|
}
|
|
381
397
|
function renderToDocument(component, options = {}) {
|
|
@@ -453,11 +469,11 @@ async function* renderToStream(element) {
|
|
|
453
469
|
for (const attr of Array.from(element.attributes)) {
|
|
454
470
|
const rawName = attr.name;
|
|
455
471
|
if (!isSafeAttrName(rawName)) continue;
|
|
456
|
-
if (
|
|
472
|
+
if (isEventHandlerAttr(rawName)) continue;
|
|
457
473
|
const lowerName = rawName.toLowerCase();
|
|
458
474
|
let value = attr.value;
|
|
459
475
|
if (URL_ATTRS.has(lowerName)) {
|
|
460
|
-
value =
|
|
476
|
+
value = sanitizeUrlAttr(lowerName, value);
|
|
461
477
|
if (!value) continue;
|
|
462
478
|
}
|
|
463
479
|
openTag += ` ${rawName}="${escapeAttr(value)}"`;
|
|
@@ -533,11 +549,11 @@ function hydrateProgressively(container, islands, options) {
|
|
|
533
549
|
(entries) => {
|
|
534
550
|
for (const entry of entries) {
|
|
535
551
|
if (entry.isIntersecting) {
|
|
552
|
+
observer.disconnect();
|
|
536
553
|
const clientTree = factory();
|
|
537
554
|
clientTree.setAttribute("data-sibu-island", id);
|
|
538
555
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
539
556
|
marker2.replaceWith(clientTree);
|
|
540
|
-
observer.disconnect();
|
|
541
557
|
break;
|
|
542
558
|
}
|
|
543
559
|
}
|
|
@@ -922,7 +938,7 @@ function track(effectFn, subscriber) {
|
|
|
922
938
|
function reactiveBinding(commit) {
|
|
923
939
|
const run = () => {
|
|
924
940
|
const s2 = subscriber;
|
|
925
|
-
if (s2._reentrant) return;
|
|
941
|
+
if (s2._disposed || s2._reentrant) return;
|
|
926
942
|
s2._reentrant = true;
|
|
927
943
|
try {
|
|
928
944
|
retrack(commit, subscriber);
|
|
@@ -938,8 +954,12 @@ function reactiveBinding(commit) {
|
|
|
938
954
|
subscriber._runEpoch = 0;
|
|
939
955
|
subscriber._runs = 0;
|
|
940
956
|
subscriber._reentrant = false;
|
|
957
|
+
subscriber._disposed = false;
|
|
941
958
|
run();
|
|
942
|
-
return subscriber._dispose ?? (subscriber._dispose = () =>
|
|
959
|
+
return subscriber._dispose ?? (subscriber._dispose = () => {
|
|
960
|
+
subscriber._disposed = true;
|
|
961
|
+
cleanup(subscriber);
|
|
962
|
+
});
|
|
943
963
|
}
|
|
944
964
|
function recordDependency(signal2) {
|
|
945
965
|
if (!currentSubscriber) return;
|
|
@@ -1097,11 +1117,6 @@ var _isDev3 = isDev();
|
|
|
1097
1117
|
function setProp(el, key, val) {
|
|
1098
1118
|
el[key] = val;
|
|
1099
1119
|
}
|
|
1100
|
-
function isEventHandlerAttr(name) {
|
|
1101
|
-
if (name.length < 3) return false;
|
|
1102
|
-
const lower = name.toLowerCase();
|
|
1103
|
-
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
1104
|
-
}
|
|
1105
1120
|
function bindAttribute(el, attr, getter) {
|
|
1106
1121
|
if (isEventHandlerAttr(attr)) {
|
|
1107
1122
|
if (_isDev3)
|
|
@@ -1142,7 +1157,71 @@ function bindAttribute(el, attr, getter) {
|
|
|
1142
1157
|
|
|
1143
1158
|
// src/reactivity/bindChildNode.ts
|
|
1144
1159
|
init_dev();
|
|
1160
|
+
|
|
1161
|
+
// src/core/rendering/dispose.ts
|
|
1162
|
+
init_dev();
|
|
1163
|
+
var elementDisposers = /* @__PURE__ */ new WeakMap();
|
|
1145
1164
|
var _isDev4 = isDev();
|
|
1165
|
+
var activeBindingCount = 0;
|
|
1166
|
+
function registerDisposer(node, teardown) {
|
|
1167
|
+
let disposers = elementDisposers.get(node);
|
|
1168
|
+
if (!disposers) {
|
|
1169
|
+
disposers = [];
|
|
1170
|
+
elementDisposers.set(node, disposers);
|
|
1171
|
+
}
|
|
1172
|
+
disposers.push(teardown);
|
|
1173
|
+
if (_isDev4) activeBindingCount++;
|
|
1174
|
+
}
|
|
1175
|
+
function dispose(node) {
|
|
1176
|
+
const stack = [node];
|
|
1177
|
+
const order = [];
|
|
1178
|
+
while (stack.length > 0) {
|
|
1179
|
+
const current = stack.pop();
|
|
1180
|
+
order.push(current);
|
|
1181
|
+
const children = Array.from(current.childNodes);
|
|
1182
|
+
for (let i2 = 0; i2 < children.length; i2++) {
|
|
1183
|
+
stack.push(children[i2]);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
for (let i2 = order.length - 1; i2 >= 0; i2--) {
|
|
1187
|
+
const current = order[i2];
|
|
1188
|
+
const disposers = elementDisposers.get(current);
|
|
1189
|
+
if (disposers) {
|
|
1190
|
+
const snapshot = disposers.slice();
|
|
1191
|
+
elementDisposers.delete(current);
|
|
1192
|
+
if (_isDev4) activeBindingCount -= snapshot.length;
|
|
1193
|
+
for (const d of snapshot) {
|
|
1194
|
+
try {
|
|
1195
|
+
d();
|
|
1196
|
+
} catch (err) {
|
|
1197
|
+
if (_isDev4 && typeof console !== "undefined") {
|
|
1198
|
+
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
let extraPasses = 0;
|
|
1203
|
+
while (extraPasses++ < 8) {
|
|
1204
|
+
const added = elementDisposers.get(current);
|
|
1205
|
+
if (!added || added.length === 0) break;
|
|
1206
|
+
const moreSnapshot = added.slice();
|
|
1207
|
+
elementDisposers.delete(current);
|
|
1208
|
+
if (_isDev4) activeBindingCount -= moreSnapshot.length;
|
|
1209
|
+
for (const d of moreSnapshot) {
|
|
1210
|
+
try {
|
|
1211
|
+
d();
|
|
1212
|
+
} catch (err) {
|
|
1213
|
+
if (_isDev4 && typeof console !== "undefined") {
|
|
1214
|
+
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// src/reactivity/bindChildNode.ts
|
|
1224
|
+
var _isDev5 = isDev();
|
|
1146
1225
|
function bindChildNode(placeholder, getter) {
|
|
1147
1226
|
let lastNodes = [];
|
|
1148
1227
|
function commit() {
|
|
@@ -1150,12 +1229,13 @@ function bindChildNode(placeholder, getter) {
|
|
|
1150
1229
|
try {
|
|
1151
1230
|
result = getter();
|
|
1152
1231
|
} catch (err) {
|
|
1153
|
-
if (
|
|
1232
|
+
if (_isDev5) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
1154
1233
|
return;
|
|
1155
1234
|
}
|
|
1156
1235
|
if (result == null || typeof result === "boolean") {
|
|
1157
1236
|
for (let i2 = 0; i2 < lastNodes.length; i2++) {
|
|
1158
1237
|
const node = lastNodes[i2];
|
|
1238
|
+
dispose(node);
|
|
1159
1239
|
if (node.parentNode) node.parentNode.removeChild(node);
|
|
1160
1240
|
}
|
|
1161
1241
|
lastNodes.length = 0;
|
|
@@ -1175,7 +1255,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
1175
1255
|
if (item == null || typeof item === "boolean") continue;
|
|
1176
1256
|
const node = item instanceof Node ? item : document.createTextNode(String(item));
|
|
1177
1257
|
if (seen.has(node)) {
|
|
1178
|
-
if (
|
|
1258
|
+
if (_isDev5)
|
|
1179
1259
|
devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
|
|
1180
1260
|
continue;
|
|
1181
1261
|
}
|
|
@@ -1197,18 +1277,16 @@ function bindChildNode(placeholder, getter) {
|
|
|
1197
1277
|
for (let i2 = 0; i2 < lastNodes.length; i2++) {
|
|
1198
1278
|
const node = lastNodes[i2];
|
|
1199
1279
|
if (reused?.has(node)) continue;
|
|
1280
|
+
dispose(node);
|
|
1200
1281
|
if (node.parentNode) node.parentNode.removeChild(node);
|
|
1201
1282
|
}
|
|
1202
|
-
|
|
1283
|
+
let prev = placeholder;
|
|
1203
1284
|
for (let i2 = 0; i2 < newNodes.length; i2++) {
|
|
1204
1285
|
const node = newNodes[i2];
|
|
1205
|
-
if (
|
|
1206
|
-
|
|
1207
|
-
parent.insertBefore(node, anchor);
|
|
1208
|
-
}
|
|
1209
|
-
} else {
|
|
1210
|
-
parent.insertBefore(node, anchor);
|
|
1286
|
+
if (prev.nextSibling !== node) {
|
|
1287
|
+
parent.insertBefore(node, prev.nextSibling);
|
|
1211
1288
|
}
|
|
1289
|
+
prev = node;
|
|
1212
1290
|
}
|
|
1213
1291
|
lastNodes = newNodes;
|
|
1214
1292
|
}
|
|
@@ -1217,70 +1295,6 @@ function bindChildNode(placeholder, getter) {
|
|
|
1217
1295
|
|
|
1218
1296
|
// src/core/rendering/tagFactory.ts
|
|
1219
1297
|
init_sanitize();
|
|
1220
|
-
|
|
1221
|
-
// src/core/rendering/dispose.ts
|
|
1222
|
-
init_dev();
|
|
1223
|
-
var elementDisposers = /* @__PURE__ */ new WeakMap();
|
|
1224
|
-
var _isDev5 = isDev();
|
|
1225
|
-
var activeBindingCount = 0;
|
|
1226
|
-
function registerDisposer(node, teardown) {
|
|
1227
|
-
let disposers = elementDisposers.get(node);
|
|
1228
|
-
if (!disposers) {
|
|
1229
|
-
disposers = [];
|
|
1230
|
-
elementDisposers.set(node, disposers);
|
|
1231
|
-
}
|
|
1232
|
-
disposers.push(teardown);
|
|
1233
|
-
if (_isDev5) activeBindingCount++;
|
|
1234
|
-
}
|
|
1235
|
-
function dispose(node) {
|
|
1236
|
-
const stack = [node];
|
|
1237
|
-
const order = [];
|
|
1238
|
-
while (stack.length > 0) {
|
|
1239
|
-
const current = stack.pop();
|
|
1240
|
-
order.push(current);
|
|
1241
|
-
const children = Array.from(current.childNodes);
|
|
1242
|
-
for (let i2 = 0; i2 < children.length; i2++) {
|
|
1243
|
-
stack.push(children[i2]);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
for (let i2 = order.length - 1; i2 >= 0; i2--) {
|
|
1247
|
-
const current = order[i2];
|
|
1248
|
-
const disposers = elementDisposers.get(current);
|
|
1249
|
-
if (disposers) {
|
|
1250
|
-
const snapshot = disposers.slice();
|
|
1251
|
-
elementDisposers.delete(current);
|
|
1252
|
-
if (_isDev5) activeBindingCount -= snapshot.length;
|
|
1253
|
-
for (const d of snapshot) {
|
|
1254
|
-
try {
|
|
1255
|
-
d();
|
|
1256
|
-
} catch (err) {
|
|
1257
|
-
if (_isDev5 && typeof console !== "undefined") {
|
|
1258
|
-
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
let extraPasses = 0;
|
|
1263
|
-
while (extraPasses++ < 8) {
|
|
1264
|
-
const added = elementDisposers.get(current);
|
|
1265
|
-
if (!added || added.length === 0) break;
|
|
1266
|
-
const moreSnapshot = added.slice();
|
|
1267
|
-
elementDisposers.delete(current);
|
|
1268
|
-
if (_isDev5) activeBindingCount -= moreSnapshot.length;
|
|
1269
|
-
for (const d of moreSnapshot) {
|
|
1270
|
-
try {
|
|
1271
|
-
d();
|
|
1272
|
-
} catch (err) {
|
|
1273
|
-
if (_isDev5 && typeof console !== "undefined") {
|
|
1274
|
-
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
// src/core/rendering/tagFactory.ts
|
|
1284
1298
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1285
1299
|
var _isDev6 = isDev();
|
|
1286
1300
|
var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
@@ -1515,7 +1529,7 @@ var tagFactory = (tag, ns) => {
|
|
|
1515
1529
|
const value = props[key];
|
|
1516
1530
|
if (value == null) continue;
|
|
1517
1531
|
const lkey = key.toLowerCase();
|
|
1518
|
-
if (
|
|
1532
|
+
if (isEventHandlerAttr(key)) continue;
|
|
1519
1533
|
if (typeof value === "function") {
|
|
1520
1534
|
registerDisposer(el, bindAttribute(el, key, value));
|
|
1521
1535
|
} else if (typeof value === "boolean") {
|
|
@@ -1879,6 +1893,7 @@ function effect(effectFn, options) {
|
|
|
1879
1893
|
ctx.fn(ctx.onCleanup);
|
|
1880
1894
|
};
|
|
1881
1895
|
const sub2 = (() => {
|
|
1896
|
+
if (ctx.disposed) return;
|
|
1882
1897
|
if (ctx.running) {
|
|
1883
1898
|
ctx.rerunPending = true;
|
|
1884
1899
|
return;
|
|
@@ -1918,7 +1933,9 @@ function effect(effectFn, options) {
|
|
|
1918
1933
|
init_sanitize();
|
|
1919
1934
|
function isSafeNavigationTarget(path2) {
|
|
1920
1935
|
if (path2 === "") return true;
|
|
1921
|
-
|
|
1936
|
+
const normalized = stripControlChars(path2).replace(/\\/g, "/");
|
|
1937
|
+
if (normalized.startsWith("//")) return false;
|
|
1938
|
+
return sanitizeUrl(normalized) !== "";
|
|
1922
1939
|
}
|
|
1923
1940
|
var LRUCache = class {
|
|
1924
1941
|
constructor(maxSize = 100) {
|
|
@@ -2055,8 +2072,13 @@ var RouteMatcher = class {
|
|
|
2055
2072
|
if (match) {
|
|
2056
2073
|
const params = {};
|
|
2057
2074
|
compiled.keys.forEach((key, i2) => {
|
|
2058
|
-
|
|
2059
|
-
|
|
2075
|
+
const raw = match[i2 + 1];
|
|
2076
|
+
if (raw !== void 0) {
|
|
2077
|
+
try {
|
|
2078
|
+
params[key] = decodeURIComponent(raw);
|
|
2079
|
+
} catch {
|
|
2080
|
+
params[key] = raw;
|
|
2081
|
+
}
|
|
2060
2082
|
}
|
|
2061
2083
|
});
|
|
2062
2084
|
return { params };
|
|
@@ -2107,14 +2129,23 @@ var RouteMatcher = class {
|
|
|
2107
2129
|
}
|
|
2108
2130
|
}
|
|
2109
2131
|
removeRoute(path2) {
|
|
2110
|
-
this.routeTrie.delete(path2);
|
|
2111
|
-
this.parentChain.delete(path2);
|
|
2112
2132
|
this.compiledPatterns.clear();
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2133
|
+
const root = this.routeTrie.get(path2);
|
|
2134
|
+
if (!root) return;
|
|
2135
|
+
const removed = /* @__PURE__ */ new Set();
|
|
2136
|
+
const collect = (r) => {
|
|
2137
|
+
removed.add(r);
|
|
2138
|
+
if (r.children) for (const child of r.children) collect(child);
|
|
2139
|
+
};
|
|
2140
|
+
collect(root);
|
|
2141
|
+
for (const [key, route2] of [...this.routeTrie]) {
|
|
2142
|
+
if (removed.has(route2)) this.routeTrie.delete(key);
|
|
2143
|
+
}
|
|
2144
|
+
for (const [key, chain] of [...this.parentChain]) {
|
|
2145
|
+
if (chain.length > 0 && removed.has(chain[chain.length - 1])) this.parentChain.delete(key);
|
|
2146
|
+
}
|
|
2147
|
+
for (const [name, route2] of [...this.namedRoutes]) {
|
|
2148
|
+
if (removed.has(route2)) this.namedRoutes.delete(name);
|
|
2118
2149
|
}
|
|
2119
2150
|
}
|
|
2120
2151
|
};
|
|
@@ -2226,42 +2257,61 @@ var GuardManager = class {
|
|
|
2226
2257
|
this.afterEachHooks = [];
|
|
2227
2258
|
}
|
|
2228
2259
|
};
|
|
2229
|
-
var
|
|
2260
|
+
var _ComponentLoader = class _ComponentLoader {
|
|
2230
2261
|
constructor(cacheSize = 50, retryDelay = 1e3) {
|
|
2231
2262
|
this.errorCache = /* @__PURE__ */ new Map();
|
|
2232
2263
|
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
2264
|
+
// Stable per-route-definition id. Caching by the RESOLVED path (e.g.
|
|
2265
|
+
// /users/123) gave the cache one entry per visited URL, so parameterized
|
|
2266
|
+
// routes thrashed/evicted and the component was reloaded every navigation.
|
|
2267
|
+
// Keying by route-definition identity makes the cache effective again.
|
|
2268
|
+
this.routeKeys = /* @__PURE__ */ new WeakMap();
|
|
2269
|
+
this.keyCounter = 0;
|
|
2233
2270
|
this.componentCache = new LRUCache(cacheSize);
|
|
2234
2271
|
this.retryDelay = retryDelay;
|
|
2235
2272
|
}
|
|
2273
|
+
keyFor(route2) {
|
|
2274
|
+
let key = this.routeKeys.get(route2);
|
|
2275
|
+
if (key === void 0) {
|
|
2276
|
+
key = `route#${this.keyCounter++}`;
|
|
2277
|
+
this.routeKeys.set(route2, key);
|
|
2278
|
+
}
|
|
2279
|
+
return key;
|
|
2280
|
+
}
|
|
2236
2281
|
async loadComponent(route2, routePath) {
|
|
2237
2282
|
if (!("component" in route2)) {
|
|
2238
2283
|
throw new Error(`Route ${routePath} does not have a component`);
|
|
2239
2284
|
}
|
|
2240
2285
|
const comp = route2.component;
|
|
2241
|
-
const
|
|
2286
|
+
const cacheKey = this.keyFor(route2);
|
|
2287
|
+
const cached = this.componentCache.get(cacheKey);
|
|
2242
2288
|
if (cached) return cached;
|
|
2243
|
-
const existingPromise = this.loadingPromises.get(
|
|
2289
|
+
const existingPromise = this.loadingPromises.get(cacheKey);
|
|
2244
2290
|
if (existingPromise) return existingPromise;
|
|
2245
|
-
const errorInfo = this.errorCache.get(
|
|
2291
|
+
const errorInfo = this.errorCache.get(cacheKey);
|
|
2246
2292
|
if (errorInfo && Date.now() - errorInfo.timestamp < this.retryDelay) {
|
|
2247
2293
|
throw new Error(`Component loading failed recently, retry in ${this.retryDelay}ms`);
|
|
2248
2294
|
}
|
|
2249
2295
|
const loadingPromise = this.doLoadComponent(comp, routePath);
|
|
2250
|
-
this.loadingPromises.set(
|
|
2296
|
+
this.loadingPromises.set(cacheKey, loadingPromise);
|
|
2251
2297
|
try {
|
|
2252
2298
|
const component = await loadingPromise;
|
|
2253
|
-
this.componentCache.set(
|
|
2254
|
-
this.errorCache.delete(
|
|
2299
|
+
this.componentCache.set(cacheKey, component);
|
|
2300
|
+
this.errorCache.delete(cacheKey);
|
|
2255
2301
|
return component;
|
|
2256
2302
|
} catch (error) {
|
|
2257
|
-
const currentError = this.errorCache.get(
|
|
2258
|
-
this.errorCache.
|
|
2303
|
+
const currentError = this.errorCache.get(cacheKey) || { timestamp: 0, count: 0 };
|
|
2304
|
+
if (!this.errorCache.has(cacheKey) && this.errorCache.size >= _ComponentLoader.MAX_ERROR_ENTRIES) {
|
|
2305
|
+
const oldest = this.errorCache.keys().next().value;
|
|
2306
|
+
if (oldest !== void 0) this.errorCache.delete(oldest);
|
|
2307
|
+
}
|
|
2308
|
+
this.errorCache.set(cacheKey, {
|
|
2259
2309
|
timestamp: Date.now(),
|
|
2260
2310
|
count: currentError.count + 1
|
|
2261
2311
|
});
|
|
2262
2312
|
throw error;
|
|
2263
2313
|
} finally {
|
|
2264
|
-
this.loadingPromises.delete(
|
|
2314
|
+
this.loadingPromises.delete(cacheKey);
|
|
2265
2315
|
}
|
|
2266
2316
|
}
|
|
2267
2317
|
async doLoadComponent(comp, routePath) {
|
|
@@ -2315,6 +2365,8 @@ var ComponentLoader = class {
|
|
|
2315
2365
|
this.loadingPromises.clear();
|
|
2316
2366
|
}
|
|
2317
2367
|
};
|
|
2368
|
+
_ComponentLoader.MAX_ERROR_ENTRIES = 256;
|
|
2369
|
+
var ComponentLoader = _ComponentLoader;
|
|
2318
2370
|
var _SibuRouter = class _SibuRouter {
|
|
2319
2371
|
constructor(routes, options = {}) {
|
|
2320
2372
|
// Event listeners cleanup
|
|
@@ -2380,7 +2432,7 @@ var _SibuRouter = class _SibuRouter {
|
|
|
2380
2432
|
return window.location.hash.slice(1) || "/";
|
|
2381
2433
|
}
|
|
2382
2434
|
let path2 = window.location.pathname;
|
|
2383
|
-
if (base2 && path2.startsWith(base2)) {
|
|
2435
|
+
if (base2 && (path2 === base2 || path2.startsWith(`${base2}/`))) {
|
|
2384
2436
|
path2 = path2.slice(base2.length);
|
|
2385
2437
|
}
|
|
2386
2438
|
return (path2 || "/") + window.location.search + window.location.hash;
|
|
@@ -2513,7 +2565,7 @@ var _SibuRouter = class _SibuRouter {
|
|
|
2513
2565
|
}
|
|
2514
2566
|
if (to.params) {
|
|
2515
2567
|
for (const [key, value] of Object.entries(to.params)) {
|
|
2516
|
-
path2 = path2.replace(`:${key}`, encodeURIComponent(value));
|
|
2568
|
+
path2 = path2.replace(new RegExp(`:${key}(?=[/?#]|$)`, "g"), encodeURIComponent(value));
|
|
2517
2569
|
}
|
|
2518
2570
|
}
|
|
2519
2571
|
if (to.query && Object.keys(to.query).length > 0) {
|
|
@@ -2749,8 +2801,7 @@ function Route() {
|
|
|
2749
2801
|
let currentNode = null;
|
|
2750
2802
|
let loadingNode = null;
|
|
2751
2803
|
let errorNode = null;
|
|
2752
|
-
let
|
|
2753
|
-
let currentPath = "";
|
|
2804
|
+
let navSeq = 0;
|
|
2754
2805
|
let currentTopRoute = null;
|
|
2755
2806
|
const cleanupNodes = () => {
|
|
2756
2807
|
[currentNode, loadingNode, errorNode].forEach((node) => {
|
|
@@ -2828,30 +2879,21 @@ function Route() {
|
|
|
2828
2879
|
errorNode.appendChild(retryButton);
|
|
2829
2880
|
anchor.parentNode.insertBefore(errorNode, anchor.nextSibling);
|
|
2830
2881
|
};
|
|
2831
|
-
let pendingUpdate = false;
|
|
2832
2882
|
const update = async () => {
|
|
2833
2883
|
if (!globalRouter) return;
|
|
2834
|
-
|
|
2835
|
-
pendingUpdate = true;
|
|
2836
|
-
return;
|
|
2837
|
-
}
|
|
2884
|
+
const seq = ++navSeq;
|
|
2838
2885
|
const route2 = globalRouter.currentRoute;
|
|
2839
2886
|
try {
|
|
2840
2887
|
const match = globalRouter["matcher"].match(route2.path);
|
|
2841
2888
|
if (!match) {
|
|
2842
|
-
currentPath = route2.path;
|
|
2843
2889
|
currentTopRoute = null;
|
|
2844
2890
|
cleanupNodes();
|
|
2845
2891
|
return;
|
|
2846
2892
|
}
|
|
2847
2893
|
const routeDef = match.matched[0] || match.route;
|
|
2848
2894
|
if (routeDef === currentTopRoute && currentNode) {
|
|
2849
|
-
currentPath = route2.path;
|
|
2850
2895
|
return;
|
|
2851
2896
|
}
|
|
2852
|
-
isUpdating = true;
|
|
2853
|
-
currentPath = route2.path;
|
|
2854
|
-
currentTopRoute = routeDef;
|
|
2855
2897
|
if ("redirect" in routeDef) {
|
|
2856
2898
|
const redirectPath = typeof routeDef.redirect === "function" ? routeDef.redirect(route2) : routeDef.redirect;
|
|
2857
2899
|
queueMicrotask(() => {
|
|
@@ -2868,27 +2910,25 @@ function Route() {
|
|
|
2868
2910
|
showLoading();
|
|
2869
2911
|
}
|
|
2870
2912
|
const component = await globalRouter.loadComponent(routeDef, route2.path);
|
|
2913
|
+
if (seq !== navSeq) return;
|
|
2871
2914
|
const node = component();
|
|
2872
|
-
if (node && anchor.parentNode
|
|
2915
|
+
if (node && anchor.parentNode) {
|
|
2916
|
+
currentTopRoute = routeDef;
|
|
2873
2917
|
cleanupNodes();
|
|
2874
2918
|
anchor.parentNode.insertBefore(node, anchor.nextSibling);
|
|
2875
2919
|
currentNode = node;
|
|
2876
2920
|
}
|
|
2877
2921
|
} catch (error) {
|
|
2922
|
+
if (seq !== navSeq) return;
|
|
2878
2923
|
hideLoading();
|
|
2879
2924
|
console.error("[Route] Component error:", error);
|
|
2880
2925
|
showError(error instanceof Error ? error : new Error(String(error)), routeDef);
|
|
2881
2926
|
}
|
|
2882
2927
|
}
|
|
2883
2928
|
} catch (error) {
|
|
2929
|
+
if (seq !== navSeq) return;
|
|
2884
2930
|
console.error("[Route] Update failed:", error);
|
|
2885
2931
|
showError(error instanceof Error ? error : new Error(String(error)));
|
|
2886
|
-
} finally {
|
|
2887
|
-
isUpdating = false;
|
|
2888
|
-
if (pendingUpdate) {
|
|
2889
|
-
pendingUpdate = false;
|
|
2890
|
-
update();
|
|
2891
|
-
}
|
|
2892
2932
|
}
|
|
2893
2933
|
};
|
|
2894
2934
|
let routeInitialized = false;
|
|
@@ -2943,7 +2983,8 @@ function KeepAliveRoute(options) {
|
|
|
2943
2983
|
return;
|
|
2944
2984
|
}
|
|
2945
2985
|
if (!("component" in routeDef)) return;
|
|
2946
|
-
const
|
|
2986
|
+
const queryStr = Object.keys(route2.query).length > 0 ? `?${new URLSearchParams(route2.query).toString()}` : "";
|
|
2987
|
+
const cacheKey = `${route2.path}${queryStr}${route2.hash ? `#${route2.hash}` : ""}`;
|
|
2947
2988
|
const shouldCache = !includeNames || routeDef.name != null && includeNames.includes(routeDef.name);
|
|
2948
2989
|
if (cacheKey === currentKey && currentNode) return;
|
|
2949
2990
|
isUpdating = true;
|
|
@@ -3033,7 +3074,8 @@ function RouterLink(props) {
|
|
|
3033
3074
|
const { to, replace: replace2 = false, activeClass, exactActiveClass, nodes, target, rel, class: classAttr, ...attrs } = props;
|
|
3034
3075
|
const baseClass = typeof classAttr === "string" ? classAttr : "";
|
|
3035
3076
|
const routeGetter = globalRouter.routeGetter;
|
|
3036
|
-
const
|
|
3077
|
+
const rawHref = globalRouter["resolvePath"](to);
|
|
3078
|
+
const href = isSafeNavigationTarget(rawHref) ? rawHref : "#";
|
|
3037
3079
|
const hrefPath = href.split("?")[0].split("#")[0];
|
|
3038
3080
|
const link2 = document.createElement("a");
|
|
3039
3081
|
link2.href = href;
|
|
@@ -3065,9 +3107,18 @@ function RouterLink(props) {
|
|
|
3065
3107
|
});
|
|
3066
3108
|
registerDisposer(link2, effectCleanup);
|
|
3067
3109
|
Object.entries(attrs).forEach(([key, value]) => {
|
|
3068
|
-
|
|
3110
|
+
const lkey = key.toLowerCase();
|
|
3111
|
+
if (lkey === "href" || lkey[0] === "o" && lkey[1] === "n") return;
|
|
3069
3112
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
3070
|
-
|
|
3113
|
+
const str = String(value);
|
|
3114
|
+
if (isUrlAttribute(lkey)) {
|
|
3115
|
+
const safe = sanitizeUrl(str);
|
|
3116
|
+
if (safe) link2.setAttribute(key, safe);
|
|
3117
|
+
} else if (lkey === "style") {
|
|
3118
|
+
link2.setAttribute(key, sanitizeCSSValue(str));
|
|
3119
|
+
} else {
|
|
3120
|
+
link2.setAttribute(key, str);
|
|
3121
|
+
}
|
|
3071
3122
|
}
|
|
3072
3123
|
});
|
|
3073
3124
|
if (typeof nodes === "string") {
|
|
@@ -3224,24 +3275,43 @@ function __removeRouterPagehideHandler() {
|
|
|
3224
3275
|
function Outlet() {
|
|
3225
3276
|
const anchor = document.createComment("route-outlet-nested");
|
|
3226
3277
|
let currentNode = null;
|
|
3278
|
+
let currentChild = null;
|
|
3279
|
+
let navSeq = 0;
|
|
3280
|
+
const clearCurrent = () => {
|
|
3281
|
+
if (currentNode) {
|
|
3282
|
+
dispose(currentNode);
|
|
3283
|
+
if (currentNode.parentNode) currentNode.parentNode.removeChild(currentNode);
|
|
3284
|
+
currentNode = null;
|
|
3285
|
+
}
|
|
3286
|
+
currentChild = null;
|
|
3287
|
+
};
|
|
3227
3288
|
const update = async () => {
|
|
3228
3289
|
if (!globalRouter) return;
|
|
3290
|
+
const seq = ++navSeq;
|
|
3229
3291
|
const route2 = globalRouter.currentRoute;
|
|
3230
|
-
if (route2.matched.length < 2)
|
|
3292
|
+
if (route2.matched.length < 2) {
|
|
3293
|
+
clearCurrent();
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3231
3296
|
const childRoute = route2.matched[route2.matched.length - 1];
|
|
3232
|
-
if (!childRoute || !("component" in childRoute))
|
|
3297
|
+
if (!childRoute || !("component" in childRoute)) {
|
|
3298
|
+
clearCurrent();
|
|
3299
|
+
return;
|
|
3300
|
+
}
|
|
3301
|
+
if (childRoute === currentChild && currentNode) return;
|
|
3233
3302
|
try {
|
|
3234
3303
|
const cacheKey = `${route2.path}\0${childRoute.path}`;
|
|
3235
3304
|
const component = await globalRouter.loadComponent(childRoute, cacheKey);
|
|
3305
|
+
if (seq !== navSeq) return;
|
|
3236
3306
|
const node = component();
|
|
3237
3307
|
if (node && anchor.parentNode) {
|
|
3238
|
-
|
|
3239
|
-
currentNode.parentNode.removeChild(currentNode);
|
|
3240
|
-
}
|
|
3308
|
+
clearCurrent();
|
|
3241
3309
|
anchor.parentNode.insertBefore(node, anchor.nextSibling);
|
|
3242
3310
|
currentNode = node;
|
|
3311
|
+
currentChild = childRoute;
|
|
3243
3312
|
}
|
|
3244
3313
|
} catch (error) {
|
|
3314
|
+
if (seq !== navSeq) return;
|
|
3245
3315
|
console.error("[Outlet] Failed to render child route:", error);
|
|
3246
3316
|
}
|
|
3247
3317
|
};
|
|
@@ -3318,10 +3388,14 @@ function createMemoryRouter(routes, _initialPath = "/") {
|
|
|
3318
3388
|
|
|
3319
3389
|
// src/plugins/routerSSR.ts
|
|
3320
3390
|
init_ssr();
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3391
|
+
|
|
3392
|
+
// src/utils/guards.ts
|
|
3393
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3394
|
+
function isUnsafeKey(key) {
|
|
3395
|
+
return UNSAFE_KEYS.has(key);
|
|
3324
3396
|
}
|
|
3397
|
+
|
|
3398
|
+
// src/plugins/routerSSR.ts
|
|
3325
3399
|
function safeDecode(raw) {
|
|
3326
3400
|
try {
|
|
3327
3401
|
return decodeURIComponent(raw);
|
|
@@ -3362,7 +3436,7 @@ function parseURL(url) {
|
|
|
3362
3436
|
key = safeDecode(pair.slice(0, eqIndex));
|
|
3363
3437
|
value = safeDecode(pair.slice(eqIndex + 1));
|
|
3364
3438
|
}
|
|
3365
|
-
if (
|
|
3439
|
+
if (isUnsafeKey(key)) continue;
|
|
3366
3440
|
query[key] = value;
|
|
3367
3441
|
}
|
|
3368
3442
|
}
|
|
@@ -3424,7 +3498,7 @@ function matchRoute(path2, routes, parentPath = "", parentChain = []) {
|
|
|
3424
3498
|
const params = nullObject();
|
|
3425
3499
|
for (let i2 = 0; i2 < compiled.keys.length; i2++) {
|
|
3426
3500
|
const key = compiled.keys[i2];
|
|
3427
|
-
if (
|
|
3501
|
+
if (isUnsafeKey(key)) continue;
|
|
3428
3502
|
if (match[i2 + 1] !== void 0) {
|
|
3429
3503
|
params[key] = safeDecode(match[i2 + 1]);
|
|
3430
3504
|
}
|
|
@@ -3521,7 +3595,7 @@ function renderRouteToString(url, routes, _options) {
|
|
|
3521
3595
|
function renderRouteToDocument(url, routes, options) {
|
|
3522
3596
|
const { html: html2, state } = renderRouteToString(url, routes, options);
|
|
3523
3597
|
const opts = options || {};
|
|
3524
|
-
const metaTags = (opts.meta || []).map((attrs) => {
|
|
3598
|
+
const metaTags = (opts.meta || []).filter((attrs) => !isDangerousMetaRefresh(attrs)).map((attrs) => {
|
|
3525
3599
|
const pairs = buildSafeAttrString(attrs);
|
|
3526
3600
|
return pairs ? `<meta ${pairs} />` : "";
|
|
3527
3601
|
}).filter(Boolean).join("\n ");
|
|
@@ -3601,7 +3675,7 @@ var SAFE_ATTR_NAME2 = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
|
3601
3675
|
function isSafeAttrName2(name) {
|
|
3602
3676
|
return SAFE_ATTR_NAME2.test(name);
|
|
3603
3677
|
}
|
|
3604
|
-
function
|
|
3678
|
+
function isEventHandlerAttr2(name) {
|
|
3605
3679
|
if (name.length < 3) return false;
|
|
3606
3680
|
const lower = name.toLowerCase();
|
|
3607
3681
|
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
@@ -3634,7 +3708,7 @@ function buildSafeAttrString(attrs) {
|
|
|
3634
3708
|
for (const rawKey of Object.keys(attrs)) {
|
|
3635
3709
|
if (!Object.hasOwn(attrs, rawKey)) continue;
|
|
3636
3710
|
if (!isSafeAttrName2(rawKey)) continue;
|
|
3637
|
-
if (
|
|
3711
|
+
if (isEventHandlerAttr2(rawKey)) continue;
|
|
3638
3712
|
const lowerKey = rawKey.toLowerCase();
|
|
3639
3713
|
let value = String(attrs[rawKey]);
|
|
3640
3714
|
if (URL_ATTRS2.has(lowerKey)) {
|