sibujs 4.6.0 → 4.8.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 +1 -1
- package/dist/browser.cjs +62 -8
- package/dist/browser.d.cts +23 -0
- package/dist/browser.d.ts +23 -0
- package/dist/browser.js +6 -5
- package/dist/build.cjs +2388 -1004
- package/dist/build.d.cts +147 -44
- package/dist/build.d.ts +147 -44
- package/dist/build.js +1410 -564
- package/dist/cdn.dev.global.js +14 -12
- package/dist/cdn.full.dev.global.js +15 -13
- package/dist/cdn.full.global.js +14 -12
- package/dist/cdn.global.js +12 -10
- package/dist/chunk-3ACAAQIN.js +224 -0
- package/dist/{chunk-7NCARGJW.js → chunk-44MTL7ED.js} +6 -6
- package/dist/{chunk-BAAG6ZTI.js → chunk-4WMSTZ7J.js} +8 -5
- package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
- package/dist/{chunk-DKGBBKOF.js → chunk-C5PEWEGH.js} +20 -5
- package/dist/{chunk-3ISI6ACU.js → chunk-C7AA2CZX.js} +1 -1
- package/dist/{chunk-UZQ6ALFS.js → chunk-CZPRSWSS.js} +1 -1
- package/dist/{chunk-D33YTSX5.js → chunk-DSID757X.js} +5 -4
- package/dist/{chunk-OXUY2A6L.js → chunk-EGUQLBHE.js} +22 -8
- package/dist/{chunk-2INLLLMZ.js → chunk-EXXSN3BW.js} +202 -26
- package/dist/{chunk-HURREPU2.js → chunk-FC6KCJSA.js} +87 -150
- package/dist/{chunk-HYXDKS4N.js → chunk-GSGNX35Q.js} +5 -5
- package/dist/{chunk-7GQCWFOE.js → chunk-J3YQ457H.js} +171 -85
- package/dist/{chunk-ORMZXBKQ.js → chunk-JUTFX2RM.js} +18 -10
- package/dist/{chunk-QE4TTDU3.js → chunk-KJ3UKABJ.js} +12 -11
- package/dist/{chunk-XRRZKZYX.js → chunk-LI4W3U6C.js} +1 -1
- package/dist/{chunk-47M47FOM.js → chunk-O6TRVMJI.js} +58 -6
- package/dist/{chunk-JSXPZCET.js → chunk-Q5J2LVKH.js} +1 -1
- package/dist/{chunk-5MT6SJ3P.js → chunk-QGMSESDD.js} +10 -5
- package/dist/{chunk-AIF3Z2T7.js → chunk-RGLPEBDE.js} +13 -10
- package/dist/{chunk-5Q4R7HCL.js → chunk-TQQUN4WR.js} +11 -2
- package/dist/{chunk-PK6FK2G2.js → chunk-UCNP3HVM.js} +183 -30
- package/dist/{chunk-XC4MEKGA.js → chunk-V3LHZSU5.js} +8 -2
- package/dist/{chunk-VVWPJ543.js → chunk-VWT3VXPL.js} +55 -20
- package/dist/{chunk-SXXVZMKZ.js → chunk-ZVLHNEEL.js} +17 -35
- package/dist/{contracts-DRIuclVT.d.cts → contracts-CLqzJnOV.d.ts} +28 -17
- package/dist/{contracts-DRIuclVT.d.ts → contracts-CTOJXu-x.d.cts} +28 -17
- package/dist/{customElement-MmInOW1U.d.ts → customElement-pYDKUzWl.d.cts} +11 -3
- package/dist/{customElement-MmInOW1U.d.cts → customElement-pYDKUzWl.d.ts} +11 -3
- package/dist/data.cjs +205 -39
- package/dist/data.d.cts +119 -3
- package/dist/data.d.ts +119 -3
- package/dist/data.js +6 -6
- package/dist/devtools.cjs +18 -5
- package/dist/devtools.js +5 -5
- package/dist/ecosystem.cjs +462 -58
- package/dist/ecosystem.d.cts +2 -1
- package/dist/ecosystem.d.ts +2 -1
- package/dist/ecosystem.js +9 -8
- package/dist/extras.cjs +852 -296
- package/dist/extras.d.cts +5 -4
- package/dist/extras.d.ts +5 -4
- package/dist/extras.js +24 -23
- package/dist/index.cjs +721 -176
- package/dist/index.d.cts +220 -169
- package/dist/index.d.ts +220 -169
- package/dist/index.js +16 -12
- package/dist/motion.cjs +12 -4
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +37 -21
- package/dist/patterns.d.cts +2 -1
- package/dist/patterns.d.ts +2 -1
- package/dist/patterns.js +6 -6
- package/dist/performance.cjs +48 -6
- package/dist/performance.d.cts +12 -2
- package/dist/performance.d.ts +12 -2
- package/dist/performance.js +8 -6
- package/dist/plugins.cjs +672 -261
- package/dist/plugins.d.cts +131 -13
- package/dist/plugins.d.ts +131 -13
- package/dist/plugins.js +83 -47
- package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
- package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.cts} +6 -0
- package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.ts} +6 -0
- package/dist/ssr.cjs +509 -142
- package/dist/ssr.d.cts +55 -5
- package/dist/ssr.d.ts +55 -5
- package/dist/ssr.js +11 -9
- package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
- package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
- package/dist/testing.cjs +17 -4
- package/dist/testing.js +4 -4
- package/dist/types-CJFViL6Q.d.cts +26 -0
- package/dist/types-CJFViL6Q.d.ts +26 -0
- package/dist/ui.cjs +635 -254
- package/dist/ui.d.cts +3 -2
- package/dist/ui.d.ts +3 -2
- package/dist/ui.js +9 -8
- package/dist/widgets.cjs +22 -14
- package/dist/widgets.js +7 -7
- package/package.json +8 -5
package/dist/ecosystem.cjs
CHANGED
|
@@ -716,7 +716,7 @@ function forEachSubscriber(signal2, visit) {
|
|
|
716
716
|
}
|
|
717
717
|
|
|
718
718
|
// src/reactivity/track.ts
|
|
719
|
-
var _runtimeVersion = true ? "4.
|
|
719
|
+
var _runtimeVersion = true ? "4.8.0" : "dev";
|
|
720
720
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
721
721
|
function resolveReactiveApi() {
|
|
722
722
|
const g = globalThis;
|
|
@@ -772,6 +772,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
|
|
|
772
772
|
var getSubscriberDeps2 = API.getSubscriberDeps;
|
|
773
773
|
var forEachSubscriber2 = API.forEachSubscriber;
|
|
774
774
|
|
|
775
|
+
// src/core/devtoolsHook.ts
|
|
776
|
+
function emitDevtools(hook, event, payload) {
|
|
777
|
+
try {
|
|
778
|
+
hook.emit(event, payload);
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
775
783
|
// src/core/ssr-context.ts
|
|
776
784
|
var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
|
|
777
785
|
function detectSSRShared() {
|
|
@@ -926,12 +934,17 @@ function effect(effectFn, options) {
|
|
|
926
934
|
try {
|
|
927
935
|
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
928
936
|
if (ctx.rerunPending) drainReruns(ctx);
|
|
937
|
+
} catch (err) {
|
|
938
|
+
ctx.disposed = true;
|
|
939
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
940
|
+
cleanup2(ctx.subscriber);
|
|
941
|
+
throw err;
|
|
929
942
|
} finally {
|
|
930
943
|
ctx.running = false;
|
|
931
944
|
ctx.rerunPending = false;
|
|
932
945
|
}
|
|
933
946
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
934
|
-
if (hook) hook
|
|
947
|
+
if (hook) emitDevtools(hook, "effect:create", { effectFn });
|
|
935
948
|
return () => disposeEffect(ctx);
|
|
936
949
|
}
|
|
937
950
|
|
|
@@ -1018,7 +1031,7 @@ function signal(initial, options) {
|
|
|
1018
1031
|
state2.__v++;
|
|
1019
1032
|
if (DEV) {
|
|
1020
1033
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1021
|
-
if (hook) hook
|
|
1034
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1022
1035
|
}
|
|
1023
1036
|
if (!enqueueBatchedSignal(state2)) {
|
|
1024
1037
|
notifySubscribers2(state2);
|
|
@@ -1032,7 +1045,7 @@ function signal(initial, options) {
|
|
|
1032
1045
|
state2.value = newValue;
|
|
1033
1046
|
state2.__v++;
|
|
1034
1047
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1035
|
-
if (hook) hook
|
|
1048
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1036
1049
|
if (!enqueueBatchedSignal(state2)) {
|
|
1037
1050
|
notifySubscribers2(state2);
|
|
1038
1051
|
}
|
|
@@ -1051,7 +1064,7 @@ function signal(initial, options) {
|
|
|
1051
1064
|
}
|
|
1052
1065
|
if (DEV) {
|
|
1053
1066
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1054
|
-
if (hook) hook
|
|
1067
|
+
if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1055
1068
|
}
|
|
1056
1069
|
return [get, set];
|
|
1057
1070
|
}
|
|
@@ -1334,17 +1347,17 @@ function derived(getter, options) {
|
|
|
1334
1347
|
cs._d = false;
|
|
1335
1348
|
cs._init = true;
|
|
1336
1349
|
};
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
try {
|
|
1350
|
+
try {
|
|
1351
|
+
track2(() => {
|
|
1340
1352
|
cs._v = getter();
|
|
1341
1353
|
cs._d = false;
|
|
1342
1354
|
cs._init = true;
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1355
|
+
}, markDirty);
|
|
1356
|
+
} catch (err) {
|
|
1357
|
+
disposed = true;
|
|
1358
|
+
cleanup2(markDirty);
|
|
1359
|
+
throw err;
|
|
1360
|
+
}
|
|
1348
1361
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1349
1362
|
const validate = () => {
|
|
1350
1363
|
if (!cs._d || disposed || pendingError !== void 0) return;
|
|
@@ -1363,7 +1376,7 @@ function derived(getter, options) {
|
|
|
1363
1376
|
if (disposed) cleanup2(markDirty);
|
|
1364
1377
|
}
|
|
1365
1378
|
if (hook && !disposed && !Object.is(oldValue, cs._v)) {
|
|
1366
|
-
hook
|
|
1379
|
+
emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
1367
1380
|
}
|
|
1368
1381
|
};
|
|
1369
1382
|
cs._validate = validate;
|
|
@@ -1414,7 +1427,7 @@ function derived(getter, options) {
|
|
|
1414
1427
|
}
|
|
1415
1428
|
}
|
|
1416
1429
|
};
|
|
1417
|
-
if (hook) hook
|
|
1430
|
+
if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
|
|
1418
1431
|
return computedGetter;
|
|
1419
1432
|
}
|
|
1420
1433
|
|
|
@@ -1471,6 +1484,11 @@ function zustandAdapter(options) {
|
|
|
1471
1484
|
function stripControlChars(value) {
|
|
1472
1485
|
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
1473
1486
|
}
|
|
1487
|
+
var ASCII_UPPER = /[A-Z]/;
|
|
1488
|
+
var ASCII_UPPER_G = /[A-Z]/g;
|
|
1489
|
+
function canonicalAttrName(name) {
|
|
1490
|
+
return ASCII_UPPER.test(name) ? name.replace(ASCII_UPPER_G, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32)) : name;
|
|
1491
|
+
}
|
|
1474
1492
|
function isEventHandlerAttr(name) {
|
|
1475
1493
|
if (name.length < 3) return false;
|
|
1476
1494
|
const lower = name.toLowerCase();
|
|
@@ -1482,6 +1500,9 @@ function sanitizeUrl(url) {
|
|
|
1482
1500
|
const probe = stripControlChars(url).trim();
|
|
1483
1501
|
if (!probe) return "";
|
|
1484
1502
|
const output = url.replace(OUTER_TRIM, "");
|
|
1503
|
+
if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
|
|
1504
|
+
return "";
|
|
1505
|
+
}
|
|
1485
1506
|
const lower = probe.toLowerCase();
|
|
1486
1507
|
let schemeEnd = -1;
|
|
1487
1508
|
for (let i = 0; i < lower.length; i++) {
|
|
@@ -1498,19 +1519,58 @@ function sanitizeUrl(url) {
|
|
|
1498
1519
|
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
1499
1520
|
return output;
|
|
1500
1521
|
}
|
|
1501
|
-
var SRCSET_DESCRIPTOR = /^\
|
|
1522
|
+
var SRCSET_DESCRIPTOR = /^\d+(\.\d+)?[wx]$/;
|
|
1523
|
+
function isSrcsetWhitespace(ch) {
|
|
1524
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
1525
|
+
}
|
|
1502
1526
|
function sanitizeSrcset(value) {
|
|
1503
|
-
const
|
|
1527
|
+
const input = String(value);
|
|
1504
1528
|
const out = [];
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
if (
|
|
1510
|
-
|
|
1511
|
-
|
|
1529
|
+
let i = 0;
|
|
1530
|
+
const n = input.length;
|
|
1531
|
+
while (i < n) {
|
|
1532
|
+
while (i < n && (isSrcsetWhitespace(input[i]) || input[i] === ",")) i++;
|
|
1533
|
+
if (i >= n) break;
|
|
1534
|
+
const urlStart = i;
|
|
1535
|
+
while (i < n && !isSrcsetWhitespace(input[i])) i++;
|
|
1536
|
+
let url = input.slice(urlStart, i);
|
|
1537
|
+
const descriptors = [];
|
|
1538
|
+
if (url.endsWith(",")) {
|
|
1539
|
+
url = url.replace(/,+$/, "");
|
|
1540
|
+
} else {
|
|
1541
|
+
let current = "";
|
|
1542
|
+
let inParens = false;
|
|
1543
|
+
while (i < n) {
|
|
1544
|
+
const ch = input[i];
|
|
1545
|
+
if (inParens) {
|
|
1546
|
+
current += ch;
|
|
1547
|
+
if (ch === ")") inParens = false;
|
|
1548
|
+
i++;
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
if (isSrcsetWhitespace(ch)) {
|
|
1552
|
+
if (current) descriptors.push(current);
|
|
1553
|
+
current = "";
|
|
1554
|
+
i++;
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
if (ch === ",") {
|
|
1558
|
+
i++;
|
|
1559
|
+
break;
|
|
1560
|
+
}
|
|
1561
|
+
if (ch === "(") inParens = true;
|
|
1562
|
+
current += ch;
|
|
1563
|
+
i++;
|
|
1564
|
+
}
|
|
1565
|
+
if (current) descriptors.push(current);
|
|
1566
|
+
}
|
|
1567
|
+
if (url === "") continue;
|
|
1568
|
+
if (descriptors.length > 1) continue;
|
|
1569
|
+
if (descriptors.length === 1 && !SRCSET_DESCRIPTOR.test(descriptors[0])) continue;
|
|
1570
|
+
const safe = sanitizeUrl(url);
|
|
1512
1571
|
if (!safe) continue;
|
|
1513
|
-
|
|
1572
|
+
if (safe.startsWith(",") || safe.endsWith(",")) continue;
|
|
1573
|
+
out.push(descriptors.length === 1 ? `${safe} ${descriptors[0]}` : safe);
|
|
1514
1574
|
}
|
|
1515
1575
|
return out.join(", ");
|
|
1516
1576
|
}
|
|
@@ -1569,8 +1629,16 @@ var BLOCKED_CSS_NEEDLES = [
|
|
|
1569
1629
|
"behavior:",
|
|
1570
1630
|
"@import",
|
|
1571
1631
|
"image-set(",
|
|
1572
|
-
"filter:progid"
|
|
1632
|
+
"filter:progid",
|
|
1633
|
+
// CSS Images 4 `image("…")` and CSS Values 5 `src("…")` take a URL as a
|
|
1634
|
+
// plain STRING, so they fetch without ever spelling `url(`. Support is thin
|
|
1635
|
+
// today, which is exactly when a blocklist should get ahead of it.
|
|
1636
|
+
"image(",
|
|
1637
|
+
"src("
|
|
1573
1638
|
];
|
|
1639
|
+
var CSS_REASON_MOZ_BINDING = 4;
|
|
1640
|
+
var CSS_REASON_BEHAVIOR = 5;
|
|
1641
|
+
var CSS_REASON_PROGID = 8;
|
|
1574
1642
|
function describeElement(el) {
|
|
1575
1643
|
if (!el) return "an element";
|
|
1576
1644
|
let out = el.tagName.toLowerCase();
|
|
@@ -1594,7 +1662,9 @@ function warnDroppedDeclaration(value, reason, ctx) {
|
|
|
1594
1662
|
"behavior: loads and runs a remote HTC script",
|
|
1595
1663
|
"@import pulls in a remote stylesheet",
|
|
1596
1664
|
"image-set() fetches remote images, the same exfiltration channel as url()",
|
|
1597
|
-
"filter:progid activates legacy scriptable filters"
|
|
1665
|
+
"filter:progid activates legacy scriptable filters",
|
|
1666
|
+
"image() fetches its string argument as a URL, the same exfiltration channel as url()",
|
|
1667
|
+
"src() fetches its string argument as a URL, the same exfiltration channel as url()"
|
|
1598
1668
|
];
|
|
1599
1669
|
const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
|
|
1600
1670
|
const where = describeElement(ctx?.element);
|
|
@@ -1609,27 +1679,110 @@ function warnDroppedDeclaration(value, reason, ctx) {
|
|
|
1609
1679
|
return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
|
|
1610
1680
|
});
|
|
1611
1681
|
}
|
|
1682
|
+
function normalizeCssForScan(value) {
|
|
1683
|
+
const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
|
|
1684
|
+
return normalized.toLowerCase().replace(/\s+/g, "");
|
|
1685
|
+
}
|
|
1612
1686
|
function blockedCssReason(value) {
|
|
1613
1687
|
if (!CSS_DANGER_GATE.test(value)) return -1;
|
|
1614
|
-
const
|
|
1615
|
-
const lower = normalized.toLowerCase().replace(/\s+/g, "");
|
|
1688
|
+
const lower = normalizeCssForScan(value);
|
|
1616
1689
|
for (let i = 0; i < BLOCKED_CSS_NEEDLES.length; i++) {
|
|
1617
1690
|
if (lower.includes(BLOCKED_CSS_NEEDLES[i])) return i;
|
|
1618
1691
|
}
|
|
1619
1692
|
return -1;
|
|
1620
1693
|
}
|
|
1621
|
-
function
|
|
1622
|
-
const reason =
|
|
1694
|
+
function sanitizeCSSDeclaration(property, value, context) {
|
|
1695
|
+
const reason = blockedDeclarationReason(property, value);
|
|
1623
1696
|
if (reason === -1) return value;
|
|
1624
|
-
warnDroppedDeclaration(value, reason, context);
|
|
1697
|
+
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
1625
1698
|
return "";
|
|
1626
1699
|
}
|
|
1700
|
+
function blockedDeclarationReason(property, value) {
|
|
1701
|
+
const reason = blockedCssReason(value);
|
|
1702
|
+
if (reason !== -1) return reason;
|
|
1703
|
+
const name = canonicalAttrName(property);
|
|
1704
|
+
if (name === "behavior") return CSS_REASON_BEHAVIOR;
|
|
1705
|
+
if (name === "-moz-binding") return CSS_REASON_MOZ_BINDING;
|
|
1706
|
+
if ((name === "filter" || name === "-ms-filter") && normalizeCssForScan(value).includes("progid:")) {
|
|
1707
|
+
return CSS_REASON_PROGID;
|
|
1708
|
+
}
|
|
1709
|
+
return -1;
|
|
1710
|
+
}
|
|
1711
|
+
function parseStyleDeclarations(css) {
|
|
1712
|
+
const chunks = [];
|
|
1713
|
+
let buf = "";
|
|
1714
|
+
let depth = 0;
|
|
1715
|
+
let quote2 = "";
|
|
1716
|
+
for (let i = 0; i < css.length; i++) {
|
|
1717
|
+
const ch = css[i];
|
|
1718
|
+
if (quote2) {
|
|
1719
|
+
buf += ch;
|
|
1720
|
+
if (ch === "\\" && i + 1 < css.length) buf += css[++i];
|
|
1721
|
+
else if (ch === quote2) quote2 = "";
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
if (ch === "\\") {
|
|
1725
|
+
buf += ch;
|
|
1726
|
+
if (i + 1 < css.length) buf += css[++i];
|
|
1727
|
+
continue;
|
|
1728
|
+
}
|
|
1729
|
+
if (ch === "/" && css[i + 1] === "*") {
|
|
1730
|
+
const end = css.indexOf("*/", i + 2);
|
|
1731
|
+
if (end === -1) return null;
|
|
1732
|
+
i = end + 1;
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
if (ch === '"' || ch === "'") quote2 = ch;
|
|
1736
|
+
else if (ch === "(" || ch === "[") depth++;
|
|
1737
|
+
else if (ch === ")" || ch === "]") {
|
|
1738
|
+
if (--depth < 0) return null;
|
|
1739
|
+
} else if (ch === "{" || ch === "}") return null;
|
|
1740
|
+
else if (ch === ";" && depth === 0) {
|
|
1741
|
+
chunks.push(buf);
|
|
1742
|
+
buf = "";
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
buf += ch;
|
|
1746
|
+
}
|
|
1747
|
+
if (quote2 || depth !== 0) return null;
|
|
1748
|
+
chunks.push(buf);
|
|
1749
|
+
const out = [];
|
|
1750
|
+
for (const chunk of chunks) {
|
|
1751
|
+
const colon = chunk.indexOf(":");
|
|
1752
|
+
if (colon === -1) continue;
|
|
1753
|
+
const rawName = chunk.slice(0, colon).trim();
|
|
1754
|
+
if (!/^(--[\w-]+|-?[a-zA-Z_][\w-]*)$/.test(rawName)) continue;
|
|
1755
|
+
let value = chunk.slice(colon + 1).trim();
|
|
1756
|
+
const bang = /!\s*important\s*$/i.exec(value);
|
|
1757
|
+
if (bang) value = value.slice(0, bang.index).trim();
|
|
1758
|
+
if (value === "") continue;
|
|
1759
|
+
const property = rawName.startsWith("--") ? rawName : canonicalAttrName(rawName);
|
|
1760
|
+
out.push([property, value, bang !== null]);
|
|
1761
|
+
}
|
|
1762
|
+
return out;
|
|
1763
|
+
}
|
|
1764
|
+
function sanitizeStyleDeclarationList(cssText, context) {
|
|
1765
|
+
const input = String(cssText);
|
|
1766
|
+
const declarations = parseStyleDeclarations(input);
|
|
1767
|
+
if (declarations === null) {
|
|
1768
|
+
if (blockedCssReason(input) !== -1) warnDroppedDeclaration(input, blockedCssReason(input), context);
|
|
1769
|
+
return "";
|
|
1770
|
+
}
|
|
1771
|
+
const kept = [];
|
|
1772
|
+
for (const [property, value, important] of declarations) {
|
|
1773
|
+
const reason = blockedDeclarationReason(property, value);
|
|
1774
|
+
if (reason !== -1) {
|
|
1775
|
+
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
kept.push(`${property}: ${value}${important ? " !important" : ""}`);
|
|
1779
|
+
}
|
|
1780
|
+
return kept.join("; ");
|
|
1781
|
+
}
|
|
1627
1782
|
function sanitizeStyleAttribute(cssText, context) {
|
|
1628
1783
|
const input = String(cssText);
|
|
1629
1784
|
if (input.trim() === "") return "";
|
|
1630
|
-
if (typeof document === "undefined")
|
|
1631
|
-
return sanitizeCSSValue(input, context) === "" ? "" : input;
|
|
1632
|
-
}
|
|
1785
|
+
if (typeof document === "undefined") return sanitizeStyleDeclarationList(input, context);
|
|
1633
1786
|
const probe = document.createElement("div");
|
|
1634
1787
|
try {
|
|
1635
1788
|
probe.style.cssText = input;
|
|
@@ -1640,8 +1793,7 @@ function sanitizeStyleAttribute(cssText, context) {
|
|
|
1640
1793
|
for (let i = 0; i < probe.style.length; i++) {
|
|
1641
1794
|
const property = probe.style[i];
|
|
1642
1795
|
const value = probe.style.getPropertyValue(property);
|
|
1643
|
-
|
|
1644
|
-
if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
|
|
1796
|
+
const reason = blockedDeclarationReason(property, value);
|
|
1645
1797
|
if (reason !== -1) {
|
|
1646
1798
|
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
1647
1799
|
continue;
|
|
@@ -1673,6 +1825,13 @@ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
|
|
|
1673
1825
|
function isHtmlContentAttribute(name) {
|
|
1674
1826
|
return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
|
|
1675
1827
|
}
|
|
1828
|
+
function isUrlAttribute(attr) {
|
|
1829
|
+
return URL_ATTRIBUTES.has(attr.toLowerCase());
|
|
1830
|
+
}
|
|
1831
|
+
function isPolicyAttribute(name) {
|
|
1832
|
+
const lower = name.toLowerCase();
|
|
1833
|
+
return lower === "style" || URL_ATTRIBUTES.has(lower);
|
|
1834
|
+
}
|
|
1676
1835
|
function sanitizeAttributeString(attr, value, context) {
|
|
1677
1836
|
const lower = attr.toLowerCase();
|
|
1678
1837
|
if (lower === "srcset") return sanitizeSrcset(value);
|
|
@@ -1680,27 +1839,229 @@ function sanitizeAttributeString(attr, value, context) {
|
|
|
1680
1839
|
if (lower === "style") return sanitizeStyleAttribute(value, context);
|
|
1681
1840
|
return value;
|
|
1682
1841
|
}
|
|
1842
|
+
function resolveAttributeValue(attr, value, context) {
|
|
1843
|
+
if (!isPolicyAttribute(attr)) return value;
|
|
1844
|
+
const safe = sanitizeAttributeString(attr, value, context);
|
|
1845
|
+
return safe === "" ? null : safe;
|
|
1846
|
+
}
|
|
1683
1847
|
|
|
1684
|
-
// src/utils/
|
|
1848
|
+
// src/utils/metaRefresh.ts
|
|
1849
|
+
function isAsciiWhitespace(ch) {
|
|
1850
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
1851
|
+
}
|
|
1852
|
+
function trimAsciiWhitespace(value) {
|
|
1853
|
+
let start = 0;
|
|
1854
|
+
let end = value.length;
|
|
1855
|
+
while (start < end && isAsciiWhitespace(value[start])) start++;
|
|
1856
|
+
while (end > start && isAsciiWhitespace(value[end - 1])) end--;
|
|
1857
|
+
return value.slice(start, end);
|
|
1858
|
+
}
|
|
1859
|
+
var forbidden = (reason) => ({ kind: "forbidden", reason });
|
|
1860
|
+
function parseMetaRefreshContent(content) {
|
|
1861
|
+
const raw = trimAsciiWhitespace(stripControlChars(content));
|
|
1862
|
+
if (raw === "") return forbidden(DEV ? "empty content" : "");
|
|
1863
|
+
let i = 0;
|
|
1864
|
+
while (i < raw.length && raw[i] >= "0" && raw[i] <= "9") i++;
|
|
1865
|
+
if (i === 0) return forbidden(DEV ? "missing or non-numeric delay" : "");
|
|
1866
|
+
const delay = Number.parseInt(raw.slice(0, i), 10);
|
|
1867
|
+
if (!Number.isFinite(delay)) return forbidden(DEV ? "unparseable delay" : "");
|
|
1868
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
1869
|
+
if (i === raw.length) return { kind: "delay-only", delay };
|
|
1870
|
+
if (raw[i] !== ";" && raw[i] !== ",") return forbidden(DEV ? "unexpected text after delay" : "");
|
|
1871
|
+
i++;
|
|
1872
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
1873
|
+
const keyStart = i;
|
|
1874
|
+
while (i < raw.length && !isAsciiWhitespace(raw[i]) && raw[i] !== "=") i++;
|
|
1875
|
+
const key = raw.slice(keyStart, i).toLowerCase();
|
|
1876
|
+
if (key !== "url") return forbidden(DEV ? `unsupported refresh key ${JSON.stringify(key)}` : "");
|
|
1877
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
1878
|
+
if (raw[i] !== "=") return forbidden(DEV ? "missing '=' after url" : "");
|
|
1879
|
+
i++;
|
|
1880
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
1881
|
+
let destination;
|
|
1882
|
+
const quote2 = raw[i];
|
|
1883
|
+
if (quote2 === '"' || quote2 === "'") {
|
|
1884
|
+
const close = raw.indexOf(quote2, i + 1);
|
|
1885
|
+
if (close === -1) return forbidden(DEV ? "unterminated quoted destination" : "");
|
|
1886
|
+
destination = raw.slice(i + 1, close);
|
|
1887
|
+
const rest = trimAsciiWhitespace(raw.slice(close + 1));
|
|
1888
|
+
if (rest !== "") return forbidden(DEV ? "trailing text after quoted destination" : "");
|
|
1889
|
+
} else {
|
|
1890
|
+
destination = trimAsciiWhitespace(raw.slice(i));
|
|
1891
|
+
if (/[;,]/.test(destination)) return forbidden(DEV ? "multiple refresh assignments" : "");
|
|
1892
|
+
}
|
|
1893
|
+
if (destination === "") return forbidden(DEV ? "empty destination" : "");
|
|
1894
|
+
const sanitizedDestination = sanitizeUrl(destination);
|
|
1895
|
+
if (sanitizedDestination === "") return forbidden(DEV ? "destination uses a disallowed protocol" : "");
|
|
1896
|
+
return { kind: "allowed", delay, destination, sanitizedDestination };
|
|
1897
|
+
}
|
|
1898
|
+
function resolveMetaRefreshPolicy(attributes) {
|
|
1899
|
+
let httpEquiv;
|
|
1900
|
+
let content;
|
|
1901
|
+
let duplicateName;
|
|
1902
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1903
|
+
for (const [key, value] of attributes) {
|
|
1904
|
+
const canonical = canonicalAttrName(key);
|
|
1905
|
+
if (seen.has(canonical)) duplicateName = canonical;
|
|
1906
|
+
seen.add(canonical);
|
|
1907
|
+
if (canonical === "http-equiv") httpEquiv = value;
|
|
1908
|
+
else if (canonical === "content") content = value;
|
|
1909
|
+
}
|
|
1910
|
+
if (duplicateName !== void 0) {
|
|
1911
|
+
return forbidden(DEV ? `duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}` : "");
|
|
1912
|
+
}
|
|
1913
|
+
if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
|
|
1914
|
+
if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
|
|
1915
|
+
return { kind: "not-refresh" };
|
|
1916
|
+
}
|
|
1917
|
+
if (typeof content !== "string") return { kind: "not-refresh" };
|
|
1918
|
+
return parseMetaRefreshContent(content);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// src/utils/elementPolicy.ts
|
|
1685
1922
|
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
1686
1923
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1924
|
+
var BLOCKED_ELEMENTS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
1925
|
+
function canonicalTagName(tag) {
|
|
1926
|
+
return canonicalAttrName(tag);
|
|
1927
|
+
}
|
|
1928
|
+
function isBlockedElement(tag) {
|
|
1929
|
+
return BLOCKED_ELEMENTS.has(canonicalTagName(tag));
|
|
1930
|
+
}
|
|
1931
|
+
var ContextualRefusal = {
|
|
1932
|
+
None: 0,
|
|
1933
|
+
/** The element would carry a refresh directive the shared policy forbids. */
|
|
1934
|
+
MetaRefreshForbidden: 1,
|
|
1935
|
+
/** A `<meta>` with a reactive binding may not carry any refresh directive. */
|
|
1936
|
+
MetaRefreshReactive: 2,
|
|
1937
|
+
/** A runtime value may not choose which program a `<script>` runs. */
|
|
1938
|
+
ScriptSource: 3,
|
|
1939
|
+
/** An SVG animation may not target a URL, handler or document attribute. */
|
|
1940
|
+
AnimationTarget: 4,
|
|
1941
|
+
/** A runtime-written `href` may not become an applied stylesheet. */
|
|
1942
|
+
StylesheetSource: 5
|
|
1943
|
+
};
|
|
1944
|
+
var SCRIPT_SOURCE_ATTRIBUTES = /* @__PURE__ */ new Set(["src", "href", "xlink:href", "type", "language"]);
|
|
1945
|
+
var SVG_ANIMATION_ELEMENTS = /* @__PURE__ */ new Set(["animate", "set", "animateMotion", "animateTransform", "animateColor"]);
|
|
1946
|
+
var reactiveMetaElements = /* @__PURE__ */ new WeakSet();
|
|
1947
|
+
function isHtmlNamespace(el) {
|
|
1948
|
+
return el.namespaceURI === HTML_NS || el.namespaceURI === null;
|
|
1949
|
+
}
|
|
1950
|
+
function metaSnapshot(el, canonical, value) {
|
|
1951
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
1952
|
+
const attrs = el.attributes;
|
|
1953
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1954
|
+
const attr = attrs[i];
|
|
1955
|
+
if (attr.name === canonical) continue;
|
|
1956
|
+
snapshot.set(attr.name, attr.value);
|
|
1957
|
+
}
|
|
1958
|
+
if (canonical !== null && value !== null) snapshot.set(canonical, value);
|
|
1959
|
+
return snapshot;
|
|
1960
|
+
}
|
|
1961
|
+
function claimReactiveMeta(el) {
|
|
1962
|
+
if (reactiveMetaElements.has(el)) return;
|
|
1963
|
+
reactiveMetaElements.add(el);
|
|
1964
|
+
if (el.getAttribute("content") === null) return;
|
|
1965
|
+
if (resolveMetaRefreshPolicy(metaSnapshot(el, null, null)).kind !== "not-refresh") {
|
|
1966
|
+
el.removeAttribute("content");
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
function metaRefusal(el, name, value, origin) {
|
|
1970
|
+
if (!isHtmlNamespace(el)) return ContextualRefusal.None;
|
|
1971
|
+
const canonical = canonicalAttrName(name);
|
|
1972
|
+
if (canonical !== "http-equiv" && canonical !== "content") return ContextualRefusal.None;
|
|
1973
|
+
if (origin === "reactive") claimReactiveMeta(el);
|
|
1974
|
+
if (value === null) return ContextualRefusal.None;
|
|
1975
|
+
if (canonical === "content" && !el.hasAttribute("http-equiv")) return ContextualRefusal.None;
|
|
1976
|
+
const decision = resolveMetaRefreshPolicy(metaSnapshot(el, canonical, value));
|
|
1977
|
+
if (decision.kind === "forbidden") return ContextualRefusal.MetaRefreshForbidden;
|
|
1978
|
+
if (decision.kind !== "not-refresh" && reactiveMetaElements.has(el)) return ContextualRefusal.MetaRefreshReactive;
|
|
1979
|
+
return ContextualRefusal.None;
|
|
1980
|
+
}
|
|
1981
|
+
function animationRefusal(name, value) {
|
|
1982
|
+
if (value === null || canonicalAttrName(name) !== "attributename") return ContextualRefusal.None;
|
|
1983
|
+
const target = stripControlChars(value);
|
|
1984
|
+
return isUrlAttribute(target) || isEventHandlerAttr(target) || isHtmlContentAttribute(target) ? ContextualRefusal.AnimationTarget : ContextualRefusal.None;
|
|
1985
|
+
}
|
|
1986
|
+
var runtimeHrefLinks = /* @__PURE__ */ new WeakSet();
|
|
1987
|
+
function relHasStylesheet(rel) {
|
|
1988
|
+
if (rel === null) return false;
|
|
1989
|
+
const lower = canonicalAttrName(rel);
|
|
1990
|
+
if (!lower.includes("stylesheet")) return false;
|
|
1991
|
+
const tokens = lower.split(/[\t\n\f\r ]+/);
|
|
1992
|
+
for (let i = 0; i < tokens.length; i++) if (tokens[i] === "stylesheet") return true;
|
|
1993
|
+
return false;
|
|
1994
|
+
}
|
|
1995
|
+
function stylesheetRefusal(el, name, value, origin) {
|
|
1996
|
+
if (!isHtmlNamespace(el)) return ContextualRefusal.None;
|
|
1997
|
+
const canonical = canonicalAttrName(name);
|
|
1998
|
+
if (canonical === "href") {
|
|
1999
|
+
if (origin === "static" || value === null) return ContextualRefusal.None;
|
|
2000
|
+
if (relHasStylesheet(el.getAttribute("rel"))) return ContextualRefusal.StylesheetSource;
|
|
2001
|
+
runtimeHrefLinks.add(el);
|
|
2002
|
+
return ContextualRefusal.None;
|
|
2003
|
+
}
|
|
2004
|
+
if (canonical === "rel") {
|
|
2005
|
+
if (!relHasStylesheet(value)) return ContextualRefusal.None;
|
|
2006
|
+
return runtimeHrefLinks.has(el) && el.hasAttribute("href") ? ContextualRefusal.StylesheetSource : ContextualRefusal.None;
|
|
2007
|
+
}
|
|
2008
|
+
return ContextualRefusal.None;
|
|
2009
|
+
}
|
|
2010
|
+
function contextualAttributeRefusal(el, name, value, origin) {
|
|
2011
|
+
const local = el.localName;
|
|
2012
|
+
if (local === "meta") return metaRefusal(el, name, value, origin);
|
|
2013
|
+
if (local === "link") return stylesheetRefusal(el, name, value, origin);
|
|
2014
|
+
if (local === "script") {
|
|
2015
|
+
if (origin === "static" || value === null) return ContextualRefusal.None;
|
|
2016
|
+
return SCRIPT_SOURCE_ATTRIBUTES.has(canonicalAttrName(name)) ? ContextualRefusal.ScriptSource : ContextualRefusal.None;
|
|
2017
|
+
}
|
|
2018
|
+
if (SVG_ANIMATION_ELEMENTS.has(local) && el.namespaceURI === SVG_NS) return animationRefusal(name, value);
|
|
2019
|
+
return ContextualRefusal.None;
|
|
2020
|
+
}
|
|
2021
|
+
function settleAttributeProvenance(el, name, origin) {
|
|
2022
|
+
if (el.localName !== "link" || canonicalAttrName(name) !== "href") return;
|
|
2023
|
+
if (origin !== "static" && el.hasAttribute("href")) runtimeHrefLinks.add(el);
|
|
2024
|
+
else runtimeHrefLinks.delete(el);
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// src/utils/setSafeAttribute.ts
|
|
2028
|
+
var HTML_NS2 = "http://www.w3.org/1999/xhtml";
|
|
2029
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
1687
2030
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
1688
2031
|
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
1689
2032
|
var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
|
|
1690
2033
|
function isHtmlElement(el) {
|
|
1691
|
-
return el.namespaceURI === null || el.namespaceURI ===
|
|
2034
|
+
return el.namespaceURI === null || el.namespaceURI === HTML_NS2;
|
|
2035
|
+
}
|
|
2036
|
+
function warnContextualRefusal(label, name, refusal) {
|
|
2037
|
+
devWarnLazy(() => {
|
|
2038
|
+
const reasons = [
|
|
2039
|
+
"",
|
|
2040
|
+
'the element would become a <meta http-equiv="refresh"> directive the shared refresh policy forbids (a destination outside the URL allowlist, or a directive too malformed to read unambiguously)',
|
|
2041
|
+
"a <meta> whose http-equiv or content is reactive may never carry a refresh directive: a browser schedules the navigation as soon as the directive is valid, and nothing can withdraw it when the state changes back",
|
|
2042
|
+
"a runtime value may not choose the program a <script> runs; name the script in static template source, or load it through Head({ script }) as an explicit trust decision",
|
|
2043
|
+
"an SVG animation may not target a URL, event-handler or nested-document attribute; its to / values would be written into that attribute without passing the URL policy",
|
|
2044
|
+
'a runtime-chosen href may not become an applied <link rel="stylesheet">: a well-formed URL is not a trusted stylesheet. Name the stylesheet in static template source, or load a runtime-chosen one through Head({ link }) as an explicit trust decision'
|
|
2045
|
+
];
|
|
2046
|
+
return `${label}: refusing "${name}" in this element's context \u2014 ${reasons[refusal]}. The write was not performed.`;
|
|
2047
|
+
});
|
|
1692
2048
|
}
|
|
1693
2049
|
function setProp(el, key, val) {
|
|
1694
2050
|
el[key] = val;
|
|
1695
2051
|
}
|
|
1696
2052
|
function namespaceFor(el, name) {
|
|
1697
|
-
if (el.namespaceURI !==
|
|
2053
|
+
if (el.namespaceURI !== SVG_NS2) return null;
|
|
1698
2054
|
const lower = name.toLowerCase();
|
|
1699
2055
|
if (lower.startsWith("xlink:")) return XLINK_NS;
|
|
1700
2056
|
if (lower.startsWith("xml:")) return XML_NS;
|
|
1701
2057
|
return null;
|
|
1702
2058
|
}
|
|
1703
2059
|
function setSafeAttribute(el, name, value, options = {}) {
|
|
2060
|
+
const written = commitSafeAttribute(el, name, value, options);
|
|
2061
|
+
if (el.localName === "link") settleAttributeProvenance(el, name, options.reactive ? "reactive" : "runtime");
|
|
2062
|
+
return written;
|
|
2063
|
+
}
|
|
2064
|
+
function commitSafeAttribute(el, name, value, options) {
|
|
1704
2065
|
const ns = namespaceFor(el, name);
|
|
1705
2066
|
const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
|
|
1706
2067
|
if (isEventHandlerAttr(name)) {
|
|
@@ -1713,6 +2074,14 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
1713
2074
|
else el.removeAttribute(name);
|
|
1714
2075
|
return false;
|
|
1715
2076
|
}
|
|
2077
|
+
const pending = value == null || value === false ? null : value === true ? "" : String(value);
|
|
2078
|
+
const refusal = contextualAttributeRefusal(el, name, pending, options.reactive ? "reactive" : "runtime");
|
|
2079
|
+
if (refusal !== ContextualRefusal.None) {
|
|
2080
|
+
warnContextualRefusal(options.label ?? "setSafeAttribute", name, refusal);
|
|
2081
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
2082
|
+
else el.removeAttribute(name);
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
1716
2085
|
if (value == null) {
|
|
1717
2086
|
if (ns) el.removeAttributeNS(ns, localName);
|
|
1718
2087
|
else el.removeAttribute(name);
|
|
@@ -1756,7 +2125,12 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
1756
2125
|
setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
|
|
1757
2126
|
return true;
|
|
1758
2127
|
}
|
|
1759
|
-
const safe =
|
|
2128
|
+
const safe = str === "" ? "" : resolveAttributeValue(name, str, { element: el });
|
|
2129
|
+
if (safe === null) {
|
|
2130
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
2131
|
+
else el.removeAttribute(name);
|
|
2132
|
+
return true;
|
|
2133
|
+
}
|
|
1760
2134
|
const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
|
|
1761
2135
|
if (current === safe) return true;
|
|
1762
2136
|
if (ns) el.setAttributeNS(ns, name, safe);
|
|
@@ -1779,7 +2153,7 @@ function bindAttribute(el, attr, getter) {
|
|
|
1779
2153
|
reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
|
|
1780
2154
|
return;
|
|
1781
2155
|
}
|
|
1782
|
-
setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")
|
|
2156
|
+
setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")`, reactive: true });
|
|
1783
2157
|
}
|
|
1784
2158
|
return reactiveBinding2(commit, el);
|
|
1785
2159
|
}
|
|
@@ -1953,10 +2327,6 @@ function bindChildNode(placeholder, getter) {
|
|
|
1953
2327
|
}
|
|
1954
2328
|
|
|
1955
2329
|
// src/core/rendering/tagFactory.ts
|
|
1956
|
-
var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
1957
|
-
function isBlockedTag(tag) {
|
|
1958
|
-
return BLOCKED_TAGS.has(tag.toLowerCase());
|
|
1959
|
-
}
|
|
1960
2330
|
var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
|
|
1961
2331
|
"config",
|
|
1962
2332
|
"location",
|
|
@@ -1983,8 +2353,35 @@ function toKebab(prop) {
|
|
|
1983
2353
|
}
|
|
1984
2354
|
function applyStyle(el, style) {
|
|
1985
2355
|
if (typeof style === "function") {
|
|
2356
|
+
const getter = style;
|
|
2357
|
+
let written = [];
|
|
2358
|
+
let wroteString = false;
|
|
1986
2359
|
const teardown = reactiveBinding2(() => {
|
|
1987
|
-
|
|
2360
|
+
const value = getter();
|
|
2361
|
+
if (value === null || typeof value !== "object") {
|
|
2362
|
+
written = [];
|
|
2363
|
+
wroteString = true;
|
|
2364
|
+
el.setAttribute("style", sanitizeStyleAttribute(String(value), { element: el }));
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
const decl = el.style;
|
|
2368
|
+
if (wroteString) {
|
|
2369
|
+
el.removeAttribute("style");
|
|
2370
|
+
wroteString = false;
|
|
2371
|
+
}
|
|
2372
|
+
const next = [];
|
|
2373
|
+
for (const prop in value) {
|
|
2374
|
+
if (!Object.hasOwn(value, prop)) continue;
|
|
2375
|
+
const val = value[prop];
|
|
2376
|
+
if (val == null) continue;
|
|
2377
|
+
const name = toKebab(prop);
|
|
2378
|
+
next.push(name);
|
|
2379
|
+
decl.setProperty(name, sanitizeCSSDeclaration(name, String(val), { element: el }));
|
|
2380
|
+
}
|
|
2381
|
+
for (let i = 0; i < written.length; i++) {
|
|
2382
|
+
if (next.indexOf(written[i]) === -1) decl.removeProperty(written[i]);
|
|
2383
|
+
}
|
|
2384
|
+
written = next;
|
|
1988
2385
|
}, el);
|
|
1989
2386
|
registerDisposer(el, teardown);
|
|
1990
2387
|
return;
|
|
@@ -1995,16 +2392,17 @@ function applyStyle(el, style) {
|
|
|
1995
2392
|
}
|
|
1996
2393
|
const htmlEl = el;
|
|
1997
2394
|
for (const prop in style) {
|
|
2395
|
+
if (!Object.hasOwn(style, prop)) continue;
|
|
1998
2396
|
const val = style[prop];
|
|
1999
2397
|
const name = toKebab(prop);
|
|
2000
2398
|
if (typeof val === "function") {
|
|
2001
2399
|
const getter = val;
|
|
2002
2400
|
const teardown = reactiveBinding2(() => {
|
|
2003
|
-
htmlEl.style.setProperty(name,
|
|
2401
|
+
htmlEl.style.setProperty(name, sanitizeCSSDeclaration(name, String(getter()), { element: el }));
|
|
2004
2402
|
}, el);
|
|
2005
2403
|
registerDisposer(el, teardown);
|
|
2006
2404
|
} else {
|
|
2007
|
-
htmlEl.style.setProperty(name,
|
|
2405
|
+
htmlEl.style.setProperty(name, sanitizeCSSDeclaration(name, String(val), { element: el }));
|
|
2008
2406
|
}
|
|
2009
2407
|
}
|
|
2010
2408
|
}
|
|
@@ -2015,7 +2413,9 @@ function applyClass(el, cls) {
|
|
|
2015
2413
|
}
|
|
2016
2414
|
if (typeof cls === "function") {
|
|
2017
2415
|
const teardown = reactiveBinding2(() => {
|
|
2018
|
-
|
|
2416
|
+
const value = cls();
|
|
2417
|
+
if (value == null || value === false) el.removeAttribute("class");
|
|
2418
|
+
else el.setAttribute("class", value);
|
|
2019
2419
|
}, el);
|
|
2020
2420
|
registerDisposer(el, teardown);
|
|
2021
2421
|
return;
|
|
@@ -2024,6 +2424,7 @@ function applyClass(el, cls) {
|
|
|
2024
2424
|
let hasReactive = false;
|
|
2025
2425
|
let result = "";
|
|
2026
2426
|
for (const name in obj) {
|
|
2427
|
+
if (!Object.hasOwn(obj, name)) continue;
|
|
2027
2428
|
const val = obj[name];
|
|
2028
2429
|
if (typeof val === "function") {
|
|
2029
2430
|
hasReactive = true;
|
|
@@ -2035,6 +2436,7 @@ function applyClass(el, cls) {
|
|
|
2035
2436
|
const update = () => {
|
|
2036
2437
|
let r = "";
|
|
2037
2438
|
for (const name in obj) {
|
|
2439
|
+
if (!Object.hasOwn(obj, name)) continue;
|
|
2038
2440
|
const val = obj[name];
|
|
2039
2441
|
const active = typeof val === "function" ? val() : val;
|
|
2040
2442
|
if (active) r = r ? `${r} ${name}` : name;
|
|
@@ -2097,8 +2499,8 @@ function appendChildren(el, nodes) {
|
|
|
2097
2499
|
}
|
|
2098
2500
|
}
|
|
2099
2501
|
}
|
|
2100
|
-
|
|
2101
|
-
const blocked =
|
|
2502
|
+
function tagFactory(tag, ns) {
|
|
2503
|
+
const blocked = isBlockedElement(tag);
|
|
2102
2504
|
return (first, second) => {
|
|
2103
2505
|
if (blocked) {
|
|
2104
2506
|
throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
|
|
@@ -2146,7 +2548,9 @@ var tagFactory = (tag, ns) => {
|
|
|
2146
2548
|
const pClass = props.class;
|
|
2147
2549
|
if (pClass != null) applyClass(el, pClass);
|
|
2148
2550
|
const pId = props.id;
|
|
2149
|
-
if (pId
|
|
2551
|
+
if (typeof pId === "function") {
|
|
2552
|
+
registerDisposer(el, bindAttribute(el, "id", pId));
|
|
2553
|
+
} else if (pId != null) {
|
|
2150
2554
|
if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
|
|
2151
2555
|
devWarn(
|
|
2152
2556
|
`tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
|
|
@@ -2159,6 +2563,7 @@ var tagFactory = (tag, ns) => {
|
|
|
2159
2563
|
const pOn = props.on;
|
|
2160
2564
|
if (pOn) {
|
|
2161
2565
|
for (const ev in pOn) {
|
|
2566
|
+
if (!Object.hasOwn(pOn, ev)) continue;
|
|
2162
2567
|
const handler = pOn[ev];
|
|
2163
2568
|
if (typeof handler === "function") {
|
|
2164
2569
|
el.addEventListener(ev, handler);
|
|
@@ -2185,6 +2590,7 @@ var tagFactory = (tag, ns) => {
|
|
|
2185
2590
|
continue;
|
|
2186
2591
|
// already handled above / below
|
|
2187
2592
|
default: {
|
|
2593
|
+
if (!Object.hasOwn(props, key)) continue;
|
|
2188
2594
|
const value = props[key];
|
|
2189
2595
|
if (value == null) continue;
|
|
2190
2596
|
if (isEventHandlerAttr(key)) continue;
|
|
@@ -2196,12 +2602,10 @@ var tagFactory = (tag, ns) => {
|
|
|
2196
2602
|
}
|
|
2197
2603
|
}
|
|
2198
2604
|
}
|
|
2199
|
-
if (
|
|
2200
|
-
props.onElement(el);
|
|
2201
|
-
}
|
|
2605
|
+
if (typeof props.onElement === "function") props.onElement(el);
|
|
2202
2606
|
return el;
|
|
2203
2607
|
};
|
|
2204
|
-
}
|
|
2608
|
+
}
|
|
2205
2609
|
|
|
2206
2610
|
// src/ecosystem/ui/componentAdapter.ts
|
|
2207
2611
|
var _themeLayers = globalSingleton(
|
|
@@ -2211,7 +2615,7 @@ var _themeLayers = globalSingleton(
|
|
|
2211
2615
|
function commitThemeProperty(root, name, record) {
|
|
2212
2616
|
const top = record.layers[record.layers.length - 1];
|
|
2213
2617
|
if (top) {
|
|
2214
|
-
root.style.setProperty(name, top.value);
|
|
2618
|
+
root.style.setProperty(name, sanitizeCSSDeclaration(name, String(top.value), { element: root }));
|
|
2215
2619
|
return;
|
|
2216
2620
|
}
|
|
2217
2621
|
_themeLayers.get(root)?.delete(name);
|