sibujs 3.1.0 → 3.2.0
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 +203 -136
- package/dist/plugins.d.cts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +96 -45
- 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) {
|
|
@@ -2943,7 +2995,8 @@ function KeepAliveRoute(options) {
|
|
|
2943
2995
|
return;
|
|
2944
2996
|
}
|
|
2945
2997
|
if (!("component" in routeDef)) return;
|
|
2946
|
-
const
|
|
2998
|
+
const queryStr = Object.keys(route2.query).length > 0 ? `?${new URLSearchParams(route2.query).toString()}` : "";
|
|
2999
|
+
const cacheKey = `${route2.path}${queryStr}${route2.hash ? `#${route2.hash}` : ""}`;
|
|
2947
3000
|
const shouldCache = !includeNames || routeDef.name != null && includeNames.includes(routeDef.name);
|
|
2948
3001
|
if (cacheKey === currentKey && currentNode) return;
|
|
2949
3002
|
isUpdating = true;
|
|
@@ -3033,7 +3086,8 @@ function RouterLink(props) {
|
|
|
3033
3086
|
const { to, replace: replace2 = false, activeClass, exactActiveClass, nodes, target, rel, class: classAttr, ...attrs } = props;
|
|
3034
3087
|
const baseClass = typeof classAttr === "string" ? classAttr : "";
|
|
3035
3088
|
const routeGetter = globalRouter.routeGetter;
|
|
3036
|
-
const
|
|
3089
|
+
const rawHref = globalRouter["resolvePath"](to);
|
|
3090
|
+
const href = isSafeNavigationTarget(rawHref) ? rawHref : "#";
|
|
3037
3091
|
const hrefPath = href.split("?")[0].split("#")[0];
|
|
3038
3092
|
const link2 = document.createElement("a");
|
|
3039
3093
|
link2.href = href;
|
|
@@ -3065,9 +3119,18 @@ function RouterLink(props) {
|
|
|
3065
3119
|
});
|
|
3066
3120
|
registerDisposer(link2, effectCleanup);
|
|
3067
3121
|
Object.entries(attrs).forEach(([key, value]) => {
|
|
3068
|
-
|
|
3122
|
+
const lkey = key.toLowerCase();
|
|
3123
|
+
if (lkey === "href" || lkey[0] === "o" && lkey[1] === "n") return;
|
|
3069
3124
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
3070
|
-
|
|
3125
|
+
const str = String(value);
|
|
3126
|
+
if (isUrlAttribute(lkey)) {
|
|
3127
|
+
const safe = sanitizeUrl(str);
|
|
3128
|
+
if (safe) link2.setAttribute(key, safe);
|
|
3129
|
+
} else if (lkey === "style") {
|
|
3130
|
+
link2.setAttribute(key, sanitizeCSSValue(str));
|
|
3131
|
+
} else {
|
|
3132
|
+
link2.setAttribute(key, str);
|
|
3133
|
+
}
|
|
3071
3134
|
}
|
|
3072
3135
|
});
|
|
3073
3136
|
if (typeof nodes === "string") {
|
|
@@ -3318,10 +3381,14 @@ function createMemoryRouter(routes, _initialPath = "/") {
|
|
|
3318
3381
|
|
|
3319
3382
|
// src/plugins/routerSSR.ts
|
|
3320
3383
|
init_ssr();
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3384
|
+
|
|
3385
|
+
// src/utils/guards.ts
|
|
3386
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3387
|
+
function isUnsafeKey(key) {
|
|
3388
|
+
return UNSAFE_KEYS.has(key);
|
|
3324
3389
|
}
|
|
3390
|
+
|
|
3391
|
+
// src/plugins/routerSSR.ts
|
|
3325
3392
|
function safeDecode(raw) {
|
|
3326
3393
|
try {
|
|
3327
3394
|
return decodeURIComponent(raw);
|
|
@@ -3362,7 +3429,7 @@ function parseURL(url) {
|
|
|
3362
3429
|
key = safeDecode(pair.slice(0, eqIndex));
|
|
3363
3430
|
value = safeDecode(pair.slice(eqIndex + 1));
|
|
3364
3431
|
}
|
|
3365
|
-
if (
|
|
3432
|
+
if (isUnsafeKey(key)) continue;
|
|
3366
3433
|
query[key] = value;
|
|
3367
3434
|
}
|
|
3368
3435
|
}
|
|
@@ -3424,7 +3491,7 @@ function matchRoute(path2, routes, parentPath = "", parentChain = []) {
|
|
|
3424
3491
|
const params = nullObject();
|
|
3425
3492
|
for (let i2 = 0; i2 < compiled.keys.length; i2++) {
|
|
3426
3493
|
const key = compiled.keys[i2];
|
|
3427
|
-
if (
|
|
3494
|
+
if (isUnsafeKey(key)) continue;
|
|
3428
3495
|
if (match[i2 + 1] !== void 0) {
|
|
3429
3496
|
params[key] = safeDecode(match[i2 + 1]);
|
|
3430
3497
|
}
|
|
@@ -3521,7 +3588,7 @@ function renderRouteToString(url, routes, _options) {
|
|
|
3521
3588
|
function renderRouteToDocument(url, routes, options) {
|
|
3522
3589
|
const { html: html2, state } = renderRouteToString(url, routes, options);
|
|
3523
3590
|
const opts = options || {};
|
|
3524
|
-
const metaTags = (opts.meta || []).map((attrs) => {
|
|
3591
|
+
const metaTags = (opts.meta || []).filter((attrs) => !isDangerousMetaRefresh(attrs)).map((attrs) => {
|
|
3525
3592
|
const pairs = buildSafeAttrString(attrs);
|
|
3526
3593
|
return pairs ? `<meta ${pairs} />` : "";
|
|
3527
3594
|
}).filter(Boolean).join("\n ");
|
|
@@ -3601,7 +3668,7 @@ var SAFE_ATTR_NAME2 = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
|
3601
3668
|
function isSafeAttrName2(name) {
|
|
3602
3669
|
return SAFE_ATTR_NAME2.test(name);
|
|
3603
3670
|
}
|
|
3604
|
-
function
|
|
3671
|
+
function isEventHandlerAttr2(name) {
|
|
3605
3672
|
if (name.length < 3) return false;
|
|
3606
3673
|
const lower = name.toLowerCase();
|
|
3607
3674
|
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
@@ -3634,7 +3701,7 @@ function buildSafeAttrString(attrs) {
|
|
|
3634
3701
|
for (const rawKey of Object.keys(attrs)) {
|
|
3635
3702
|
if (!Object.hasOwn(attrs, rawKey)) continue;
|
|
3636
3703
|
if (!isSafeAttrName2(rawKey)) continue;
|
|
3637
|
-
if (
|
|
3704
|
+
if (isEventHandlerAttr2(rawKey)) continue;
|
|
3638
3705
|
const lowerKey = rawKey.toLowerCase();
|
|
3639
3706
|
let value = String(attrs[rawKey]);
|
|
3640
3707
|
if (URL_ATTRS2.has(lowerKey)) {
|
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
|
|