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/ui.cjs
CHANGED
|
@@ -834,7 +834,7 @@ function forEachSubscriber(signal2, visit) {
|
|
|
834
834
|
}
|
|
835
835
|
|
|
836
836
|
// src/reactivity/track.ts
|
|
837
|
-
var _runtimeVersion = true ? "4.
|
|
837
|
+
var _runtimeVersion = true ? "4.8.0" : "dev";
|
|
838
838
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
839
839
|
function resolveReactiveApi() {
|
|
840
840
|
const g = globalThis;
|
|
@@ -890,6 +890,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
|
|
|
890
890
|
var getSubscriberDeps2 = API.getSubscriberDeps;
|
|
891
891
|
var forEachSubscriber2 = API.forEachSubscriber;
|
|
892
892
|
|
|
893
|
+
// src/core/devtoolsHook.ts
|
|
894
|
+
function emitDevtools(hook, event, payload) {
|
|
895
|
+
try {
|
|
896
|
+
hook.emit(event, payload);
|
|
897
|
+
} catch {
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
893
901
|
// src/core/signals/derived.ts
|
|
894
902
|
function derived(getter, options) {
|
|
895
903
|
devAssert(typeof getter === "function", "derived: argument must be a getter function.");
|
|
@@ -916,17 +924,17 @@ function derived(getter, options) {
|
|
|
916
924
|
cs._d = false;
|
|
917
925
|
cs._init = true;
|
|
918
926
|
};
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
try {
|
|
927
|
+
try {
|
|
928
|
+
track2(() => {
|
|
922
929
|
cs._v = getter();
|
|
923
930
|
cs._d = false;
|
|
924
931
|
cs._init = true;
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
932
|
+
}, markDirty);
|
|
933
|
+
} catch (err) {
|
|
934
|
+
disposed = true;
|
|
935
|
+
cleanup2(markDirty);
|
|
936
|
+
throw err;
|
|
937
|
+
}
|
|
930
938
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
931
939
|
const validate = () => {
|
|
932
940
|
if (!cs._d || disposed || pendingError !== void 0) return;
|
|
@@ -945,7 +953,7 @@ function derived(getter, options) {
|
|
|
945
953
|
if (disposed) cleanup2(markDirty);
|
|
946
954
|
}
|
|
947
955
|
if (hook && !disposed && !Object.is(oldValue, cs._v)) {
|
|
948
|
-
hook
|
|
956
|
+
emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
949
957
|
}
|
|
950
958
|
};
|
|
951
959
|
cs._validate = validate;
|
|
@@ -996,7 +1004,7 @@ function derived(getter, options) {
|
|
|
996
1004
|
}
|
|
997
1005
|
}
|
|
998
1006
|
};
|
|
999
|
-
if (hook) hook
|
|
1007
|
+
if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
|
|
1000
1008
|
return computedGetter;
|
|
1001
1009
|
}
|
|
1002
1010
|
|
|
@@ -1083,7 +1091,7 @@ function signal(initial, options) {
|
|
|
1083
1091
|
state2.__v++;
|
|
1084
1092
|
if (DEV) {
|
|
1085
1093
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1086
|
-
if (hook) hook
|
|
1094
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1087
1095
|
}
|
|
1088
1096
|
if (!enqueueBatchedSignal(state2)) {
|
|
1089
1097
|
notifySubscribers2(state2);
|
|
@@ -1097,7 +1105,7 @@ function signal(initial, options) {
|
|
|
1097
1105
|
state2.value = newValue;
|
|
1098
1106
|
state2.__v++;
|
|
1099
1107
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1100
|
-
if (hook) hook
|
|
1108
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1101
1109
|
if (!enqueueBatchedSignal(state2)) {
|
|
1102
1110
|
notifySubscribers2(state2);
|
|
1103
1111
|
}
|
|
@@ -1116,7 +1124,7 @@ function signal(initial, options) {
|
|
|
1116
1124
|
}
|
|
1117
1125
|
if (DEV) {
|
|
1118
1126
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1119
|
-
if (hook) hook
|
|
1127
|
+
if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1120
1128
|
}
|
|
1121
1129
|
return [get, set];
|
|
1122
1130
|
}
|
|
@@ -2171,33 +2179,402 @@ function createDialogAria(element, options = {}) {
|
|
|
2171
2179
|
return { titleId, descriptionId };
|
|
2172
2180
|
}
|
|
2173
2181
|
|
|
2174
|
-
// src/
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2182
|
+
// src/utils/sanitize.ts
|
|
2183
|
+
function stripControlChars(value) {
|
|
2184
|
+
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
2185
|
+
}
|
|
2186
|
+
var ASCII_UPPER = /[A-Z]/;
|
|
2187
|
+
var ASCII_UPPER_G = /[A-Z]/g;
|
|
2188
|
+
function canonicalAttrName(name) {
|
|
2189
|
+
return ASCII_UPPER.test(name) ? name.replace(ASCII_UPPER_G, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32)) : name;
|
|
2190
|
+
}
|
|
2191
|
+
function isEventHandlerAttr(name) {
|
|
2192
|
+
if (name.length < 3) return false;
|
|
2193
|
+
const lower = name.toLowerCase();
|
|
2194
|
+
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
2195
|
+
}
|
|
2196
|
+
var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
|
|
2197
|
+
var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
|
|
2198
|
+
function sanitizeUrl(url) {
|
|
2199
|
+
const probe = stripControlChars(url).trim();
|
|
2200
|
+
if (!probe) return "";
|
|
2201
|
+
const output = url.replace(OUTER_TRIM, "");
|
|
2202
|
+
if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
|
|
2203
|
+
return "";
|
|
2204
|
+
}
|
|
2205
|
+
const lower = probe.toLowerCase();
|
|
2206
|
+
let schemeEnd = -1;
|
|
2207
|
+
for (let i = 0; i < lower.length; i++) {
|
|
2208
|
+
const ch = lower.charCodeAt(i);
|
|
2209
|
+
if (ch === 58) {
|
|
2210
|
+
schemeEnd = i;
|
|
2211
|
+
break;
|
|
2212
|
+
}
|
|
2213
|
+
if (ch === 47 || ch === 63 || ch === 35) break;
|
|
2214
|
+
}
|
|
2215
|
+
if (schemeEnd === -1) return output;
|
|
2216
|
+
const scheme = lower.slice(0, schemeEnd + 1);
|
|
2217
|
+
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output;
|
|
2218
|
+
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
2219
|
+
return output;
|
|
2220
|
+
}
|
|
2221
|
+
var SRCSET_DESCRIPTOR = /^\d+(\.\d+)?[wx]$/;
|
|
2222
|
+
function isSrcsetWhitespace(ch) {
|
|
2223
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
2224
|
+
}
|
|
2225
|
+
function sanitizeSrcset(value) {
|
|
2226
|
+
const input = String(value);
|
|
2227
|
+
const out = [];
|
|
2228
|
+
let i = 0;
|
|
2229
|
+
const n = input.length;
|
|
2230
|
+
while (i < n) {
|
|
2231
|
+
while (i < n && (isSrcsetWhitespace(input[i]) || input[i] === ",")) i++;
|
|
2232
|
+
if (i >= n) break;
|
|
2233
|
+
const urlStart = i;
|
|
2234
|
+
while (i < n && !isSrcsetWhitespace(input[i])) i++;
|
|
2235
|
+
let url = input.slice(urlStart, i);
|
|
2236
|
+
const descriptors = [];
|
|
2237
|
+
if (url.endsWith(",")) {
|
|
2238
|
+
url = url.replace(/,+$/, "");
|
|
2239
|
+
} else {
|
|
2240
|
+
let current = "";
|
|
2241
|
+
let inParens = false;
|
|
2242
|
+
while (i < n) {
|
|
2243
|
+
const ch = input[i];
|
|
2244
|
+
if (inParens) {
|
|
2245
|
+
current += ch;
|
|
2246
|
+
if (ch === ")") inParens = false;
|
|
2247
|
+
i++;
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
if (isSrcsetWhitespace(ch)) {
|
|
2251
|
+
if (current) descriptors.push(current);
|
|
2252
|
+
current = "";
|
|
2253
|
+
i++;
|
|
2254
|
+
continue;
|
|
2185
2255
|
}
|
|
2256
|
+
if (ch === ",") {
|
|
2257
|
+
i++;
|
|
2258
|
+
break;
|
|
2259
|
+
}
|
|
2260
|
+
if (ch === "(") inParens = true;
|
|
2261
|
+
current += ch;
|
|
2262
|
+
i++;
|
|
2263
|
+
}
|
|
2264
|
+
if (current) descriptors.push(current);
|
|
2265
|
+
}
|
|
2266
|
+
if (url === "") continue;
|
|
2267
|
+
if (descriptors.length > 1) continue;
|
|
2268
|
+
if (descriptors.length === 1 && !SRCSET_DESCRIPTOR.test(descriptors[0])) continue;
|
|
2269
|
+
const safe = sanitizeUrl(url);
|
|
2270
|
+
if (!safe) continue;
|
|
2271
|
+
if (safe.startsWith(",") || safe.endsWith(",")) continue;
|
|
2272
|
+
out.push(descriptors.length === 1 ? `${safe} ${descriptors[0]}` : safe);
|
|
2273
|
+
}
|
|
2274
|
+
return out.join(", ");
|
|
2275
|
+
}
|
|
2276
|
+
var CSS_DANGER_GATE = /[(:@\\]/;
|
|
2277
|
+
function isCssWhitespace(ch) {
|
|
2278
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
2279
|
+
}
|
|
2280
|
+
function isHexDigit(ch) {
|
|
2281
|
+
return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
|
|
2282
|
+
}
|
|
2283
|
+
function decodeCssEscapes(value) {
|
|
2284
|
+
let out = "";
|
|
2285
|
+
for (let i = 0; i < value.length; i++) {
|
|
2286
|
+
const ch = value[i];
|
|
2287
|
+
if (ch !== "\\") {
|
|
2288
|
+
out += ch;
|
|
2289
|
+
continue;
|
|
2290
|
+
}
|
|
2291
|
+
if (i + 1 >= value.length) break;
|
|
2292
|
+
const next = value[i + 1];
|
|
2293
|
+
if (next === "\n" || next === "\f") {
|
|
2294
|
+
i += 1;
|
|
2295
|
+
continue;
|
|
2296
|
+
}
|
|
2297
|
+
if (next === "\r") {
|
|
2298
|
+
i += value[i + 2] === "\n" ? 2 : 1;
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
if (isHexDigit(next)) {
|
|
2302
|
+
let hex = "";
|
|
2303
|
+
let j = i + 1;
|
|
2304
|
+
while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
|
|
2305
|
+
hex += value[j];
|
|
2306
|
+
j++;
|
|
2307
|
+
}
|
|
2308
|
+
if (j < value.length && isCssWhitespace(value[j])) {
|
|
2309
|
+
j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
|
|
2186
2310
|
}
|
|
2187
|
-
|
|
2311
|
+
const code = Number.parseInt(hex, 16);
|
|
2312
|
+
const invalid = code === 0 || code > 1114111 || code >= 55296 && code <= 57343;
|
|
2313
|
+
out += invalid ? "\uFFFD" : String.fromCodePoint(code);
|
|
2314
|
+
i = j - 1;
|
|
2315
|
+
continue;
|
|
2316
|
+
}
|
|
2317
|
+
out += next;
|
|
2318
|
+
i += 1;
|
|
2319
|
+
}
|
|
2320
|
+
return out;
|
|
2321
|
+
}
|
|
2322
|
+
var BLOCKED_CSS_NEEDLES = [
|
|
2323
|
+
"url(",
|
|
2324
|
+
"expression(",
|
|
2325
|
+
"javascript:",
|
|
2326
|
+
"vbscript:",
|
|
2327
|
+
"-moz-binding",
|
|
2328
|
+
"behavior:",
|
|
2329
|
+
"@import",
|
|
2330
|
+
"image-set(",
|
|
2331
|
+
"filter:progid",
|
|
2332
|
+
// CSS Images 4 `image("…")` and CSS Values 5 `src("…")` take a URL as a
|
|
2333
|
+
// plain STRING, so they fetch without ever spelling `url(`. Support is thin
|
|
2334
|
+
// today, which is exactly when a blocklist should get ahead of it.
|
|
2335
|
+
"image(",
|
|
2336
|
+
"src("
|
|
2337
|
+
];
|
|
2338
|
+
var CSS_REASON_MOZ_BINDING = 4;
|
|
2339
|
+
var CSS_REASON_BEHAVIOR = 5;
|
|
2340
|
+
var CSS_REASON_PROGID = 8;
|
|
2341
|
+
function describeElement(el) {
|
|
2342
|
+
if (!el) return "an element";
|
|
2343
|
+
let out = el.tagName.toLowerCase();
|
|
2344
|
+
const id = el.getAttribute("id");
|
|
2345
|
+
if (id) out += `#${id}`;
|
|
2346
|
+
const cls = el.getAttribute("class");
|
|
2347
|
+
if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
|
|
2348
|
+
return `<${out}>`;
|
|
2349
|
+
}
|
|
2350
|
+
var warnedDeclarations = /* @__PURE__ */ new Set();
|
|
2351
|
+
var MAX_WARNED_DECLARATIONS = 100;
|
|
2352
|
+
var declarationCapAnnounced = false;
|
|
2353
|
+
function warnDroppedDeclaration(value, reason, ctx) {
|
|
2354
|
+
devWarnLazy(() => {
|
|
2355
|
+
const reasons = [
|
|
2356
|
+
"url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
|
|
2357
|
+
"expression() executes arbitrary script in legacy engines",
|
|
2358
|
+
"the javascript: scheme executes script",
|
|
2359
|
+
"the vbscript: scheme executes script",
|
|
2360
|
+
"-moz-binding loads and runs remote XBL",
|
|
2361
|
+
"behavior: loads and runs a remote HTC script",
|
|
2362
|
+
"@import pulls in a remote stylesheet",
|
|
2363
|
+
"image-set() fetches remote images, the same exfiltration channel as url()",
|
|
2364
|
+
"filter:progid activates legacy scriptable filters",
|
|
2365
|
+
"image() fetches its string argument as a URL, the same exfiltration channel as url()",
|
|
2366
|
+
"src() fetches its string argument as a URL, the same exfiltration channel as url()"
|
|
2367
|
+
];
|
|
2368
|
+
const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
|
|
2369
|
+
const where = describeElement(ctx?.element);
|
|
2370
|
+
const key = `${where}|${declaration}`;
|
|
2371
|
+
if (warnedDeclarations.has(key)) return "";
|
|
2372
|
+
if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
|
|
2373
|
+
if (declarationCapAnnounced) return "";
|
|
2374
|
+
declarationCapAnnounced = true;
|
|
2375
|
+
return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
|
|
2188
2376
|
}
|
|
2189
|
-
|
|
2377
|
+
warnedDeclarations.add(key);
|
|
2378
|
+
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.`;
|
|
2190
2379
|
});
|
|
2191
2380
|
}
|
|
2192
|
-
function
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2381
|
+
function normalizeCssForScan(value) {
|
|
2382
|
+
const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
|
|
2383
|
+
return normalized.toLowerCase().replace(/\s+/g, "");
|
|
2384
|
+
}
|
|
2385
|
+
function blockedCssReason(value) {
|
|
2386
|
+
if (!CSS_DANGER_GATE.test(value)) return -1;
|
|
2387
|
+
const lower = normalizeCssForScan(value);
|
|
2388
|
+
for (let i = 0; i < BLOCKED_CSS_NEEDLES.length; i++) {
|
|
2389
|
+
if (lower.includes(BLOCKED_CSS_NEEDLES[i])) return i;
|
|
2390
|
+
}
|
|
2391
|
+
return -1;
|
|
2200
2392
|
}
|
|
2393
|
+
var STYLESHEET_BLOCKED_FUNCTIONS = ["url", "image-set", "image", "src", "expression"];
|
|
2394
|
+
function stripCssFunctions(css) {
|
|
2395
|
+
const pattern = new RegExp(`(?:-webkit-)?(${STYLESHEET_BLOCKED_FUNCTIONS.join("|")})\\s*\\(`, "gi");
|
|
2396
|
+
let out = "";
|
|
2397
|
+
let last = 0;
|
|
2398
|
+
for (let match = pattern.exec(css); match !== null; match = pattern.exec(css)) {
|
|
2399
|
+
const before = match.index > 0 ? css[match.index - 1] : "";
|
|
2400
|
+
if (/[\w-]/.test(before)) continue;
|
|
2401
|
+
let depth = 1;
|
|
2402
|
+
let i = pattern.lastIndex;
|
|
2403
|
+
let quote = "";
|
|
2404
|
+
for (; i < css.length && depth > 0; i++) {
|
|
2405
|
+
const ch = css[i];
|
|
2406
|
+
if (quote) {
|
|
2407
|
+
if (ch === "\\") i++;
|
|
2408
|
+
else if (ch === quote) quote = "";
|
|
2409
|
+
} else if (ch === '"' || ch === "'") quote = ch;
|
|
2410
|
+
else if (ch === "(") depth++;
|
|
2411
|
+
else if (ch === ")") depth--;
|
|
2412
|
+
}
|
|
2413
|
+
out += `${css.slice(last, match.index)}/* ${match[1].toLowerCase()}() removed */`;
|
|
2414
|
+
last = i;
|
|
2415
|
+
pattern.lastIndex = i;
|
|
2416
|
+
}
|
|
2417
|
+
return out + css.slice(last);
|
|
2418
|
+
}
|
|
2419
|
+
function sanitizeStylesheetText(css) {
|
|
2420
|
+
let text = css.includes("\\") ? decodeCssEscapes(css) : css;
|
|
2421
|
+
text = text.replace(/@import\b[^;{}]*;?/gi, "/* @import removed */");
|
|
2422
|
+
text = stripCssFunctions(text);
|
|
2423
|
+
text = text.replace(/(^|[;{\s])behavior\s*:[^;}]*;?/gi, "$1/* behavior removed */");
|
|
2424
|
+
text = text.replace(/(^|[;{\s])-moz-binding\s*:[^;}]*;?/gi, "$1/* -moz-binding removed */");
|
|
2425
|
+
text = text.replace(/(^|[;{\s])(-ms-)?filter\s*:[^;}]*progid[^;}]*;?/gi, "$1/* filter:progid removed */");
|
|
2426
|
+
return text;
|
|
2427
|
+
}
|
|
2428
|
+
function blockedDeclarationReason(property, value) {
|
|
2429
|
+
const reason = blockedCssReason(value);
|
|
2430
|
+
if (reason !== -1) return reason;
|
|
2431
|
+
const name = canonicalAttrName(property);
|
|
2432
|
+
if (name === "behavior") return CSS_REASON_BEHAVIOR;
|
|
2433
|
+
if (name === "-moz-binding") return CSS_REASON_MOZ_BINDING;
|
|
2434
|
+
if ((name === "filter" || name === "-ms-filter") && normalizeCssForScan(value).includes("progid:")) {
|
|
2435
|
+
return CSS_REASON_PROGID;
|
|
2436
|
+
}
|
|
2437
|
+
return -1;
|
|
2438
|
+
}
|
|
2439
|
+
function parseStyleDeclarations(css) {
|
|
2440
|
+
const chunks = [];
|
|
2441
|
+
let buf = "";
|
|
2442
|
+
let depth = 0;
|
|
2443
|
+
let quote = "";
|
|
2444
|
+
for (let i = 0; i < css.length; i++) {
|
|
2445
|
+
const ch = css[i];
|
|
2446
|
+
if (quote) {
|
|
2447
|
+
buf += ch;
|
|
2448
|
+
if (ch === "\\" && i + 1 < css.length) buf += css[++i];
|
|
2449
|
+
else if (ch === quote) quote = "";
|
|
2450
|
+
continue;
|
|
2451
|
+
}
|
|
2452
|
+
if (ch === "\\") {
|
|
2453
|
+
buf += ch;
|
|
2454
|
+
if (i + 1 < css.length) buf += css[++i];
|
|
2455
|
+
continue;
|
|
2456
|
+
}
|
|
2457
|
+
if (ch === "/" && css[i + 1] === "*") {
|
|
2458
|
+
const end = css.indexOf("*/", i + 2);
|
|
2459
|
+
if (end === -1) return null;
|
|
2460
|
+
i = end + 1;
|
|
2461
|
+
continue;
|
|
2462
|
+
}
|
|
2463
|
+
if (ch === '"' || ch === "'") quote = ch;
|
|
2464
|
+
else if (ch === "(" || ch === "[") depth++;
|
|
2465
|
+
else if (ch === ")" || ch === "]") {
|
|
2466
|
+
if (--depth < 0) return null;
|
|
2467
|
+
} else if (ch === "{" || ch === "}") return null;
|
|
2468
|
+
else if (ch === ";" && depth === 0) {
|
|
2469
|
+
chunks.push(buf);
|
|
2470
|
+
buf = "";
|
|
2471
|
+
continue;
|
|
2472
|
+
}
|
|
2473
|
+
buf += ch;
|
|
2474
|
+
}
|
|
2475
|
+
if (quote || depth !== 0) return null;
|
|
2476
|
+
chunks.push(buf);
|
|
2477
|
+
const out = [];
|
|
2478
|
+
for (const chunk of chunks) {
|
|
2479
|
+
const colon = chunk.indexOf(":");
|
|
2480
|
+
if (colon === -1) continue;
|
|
2481
|
+
const rawName = chunk.slice(0, colon).trim();
|
|
2482
|
+
if (!/^(--[\w-]+|-?[a-zA-Z_][\w-]*)$/.test(rawName)) continue;
|
|
2483
|
+
let value = chunk.slice(colon + 1).trim();
|
|
2484
|
+
const bang = /!\s*important\s*$/i.exec(value);
|
|
2485
|
+
if (bang) value = value.slice(0, bang.index).trim();
|
|
2486
|
+
if (value === "") continue;
|
|
2487
|
+
const property = rawName.startsWith("--") ? rawName : canonicalAttrName(rawName);
|
|
2488
|
+
out.push([property, value, bang !== null]);
|
|
2489
|
+
}
|
|
2490
|
+
return out;
|
|
2491
|
+
}
|
|
2492
|
+
function sanitizeStyleDeclarationList(cssText, context) {
|
|
2493
|
+
const input = String(cssText);
|
|
2494
|
+
const declarations = parseStyleDeclarations(input);
|
|
2495
|
+
if (declarations === null) {
|
|
2496
|
+
if (blockedCssReason(input) !== -1) warnDroppedDeclaration(input, blockedCssReason(input), context);
|
|
2497
|
+
return "";
|
|
2498
|
+
}
|
|
2499
|
+
const kept = [];
|
|
2500
|
+
for (const [property, value, important] of declarations) {
|
|
2501
|
+
const reason = blockedDeclarationReason(property, value);
|
|
2502
|
+
if (reason !== -1) {
|
|
2503
|
+
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
2504
|
+
continue;
|
|
2505
|
+
}
|
|
2506
|
+
kept.push(`${property}: ${value}${important ? " !important" : ""}`);
|
|
2507
|
+
}
|
|
2508
|
+
return kept.join("; ");
|
|
2509
|
+
}
|
|
2510
|
+
function sanitizeStyleAttribute(cssText, context) {
|
|
2511
|
+
const input = String(cssText);
|
|
2512
|
+
if (input.trim() === "") return "";
|
|
2513
|
+
if (typeof document === "undefined") return sanitizeStyleDeclarationList(input, context);
|
|
2514
|
+
const probe = document.createElement("div");
|
|
2515
|
+
try {
|
|
2516
|
+
probe.style.cssText = input;
|
|
2517
|
+
} catch {
|
|
2518
|
+
return "";
|
|
2519
|
+
}
|
|
2520
|
+
const declarations = [];
|
|
2521
|
+
for (let i = 0; i < probe.style.length; i++) {
|
|
2522
|
+
const property = probe.style[i];
|
|
2523
|
+
const value = probe.style.getPropertyValue(property);
|
|
2524
|
+
const reason = blockedDeclarationReason(property, value);
|
|
2525
|
+
if (reason !== -1) {
|
|
2526
|
+
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
2527
|
+
continue;
|
|
2528
|
+
}
|
|
2529
|
+
const priority = probe.style.getPropertyPriority(property);
|
|
2530
|
+
declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
|
|
2531
|
+
}
|
|
2532
|
+
return declarations.join("; ");
|
|
2533
|
+
}
|
|
2534
|
+
var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
2535
|
+
"href",
|
|
2536
|
+
"xlink:href",
|
|
2537
|
+
"src",
|
|
2538
|
+
"action",
|
|
2539
|
+
"formaction",
|
|
2540
|
+
"formtarget",
|
|
2541
|
+
"cite",
|
|
2542
|
+
"poster",
|
|
2543
|
+
"background",
|
|
2544
|
+
"srcset",
|
|
2545
|
+
"ping",
|
|
2546
|
+
"data",
|
|
2547
|
+
// `<html manifest>` is a URL sink too. It was carried only by the SSR
|
|
2548
|
+
// serializers' private copy of this set; folding that copy into this one would
|
|
2549
|
+
// otherwise have silently dropped its coverage.
|
|
2550
|
+
"manifest"
|
|
2551
|
+
]);
|
|
2552
|
+
var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
|
|
2553
|
+
function isHtmlContentAttribute(name) {
|
|
2554
|
+
return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
|
|
2555
|
+
}
|
|
2556
|
+
function isUrlAttribute(attr) {
|
|
2557
|
+
return URL_ATTRIBUTES.has(attr.toLowerCase());
|
|
2558
|
+
}
|
|
2559
|
+
function isPolicyAttribute(name) {
|
|
2560
|
+
const lower = name.toLowerCase();
|
|
2561
|
+
return lower === "style" || URL_ATTRIBUTES.has(lower);
|
|
2562
|
+
}
|
|
2563
|
+
function sanitizeAttributeString(attr, value, context) {
|
|
2564
|
+
const lower = attr.toLowerCase();
|
|
2565
|
+
if (lower === "srcset") return sanitizeSrcset(value);
|
|
2566
|
+
if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
|
|
2567
|
+
if (lower === "style") return sanitizeStyleAttribute(value, context);
|
|
2568
|
+
return value;
|
|
2569
|
+
}
|
|
2570
|
+
function resolveAttributeValue(attr, value, context) {
|
|
2571
|
+
if (!isPolicyAttribute(attr)) return value;
|
|
2572
|
+
const safe = sanitizeAttributeString(attr, value, context);
|
|
2573
|
+
return safe === "" ? null : safe;
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
// src/ui/scopedStyle.ts
|
|
2577
|
+
var _scope = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scopedStyle.v1"), () => ({ n: 0 }));
|
|
2201
2578
|
function firstCompoundEnd(selector) {
|
|
2202
2579
|
let parens = 0;
|
|
2203
2580
|
let brackets = 0;
|
|
@@ -2359,7 +2736,7 @@ function scopeStylesheet(css, attr) {
|
|
|
2359
2736
|
function scopedStyle(css) {
|
|
2360
2737
|
const id = `sibu-s${_scope.n++}`;
|
|
2361
2738
|
const attr = `data-${id}`;
|
|
2362
|
-
const safeCss =
|
|
2739
|
+
const safeCss = sanitizeStylesheetText(css);
|
|
2363
2740
|
const scopedCSS = scopeStylesheet(safeCss, attr);
|
|
2364
2741
|
if (typeof document !== "undefined") {
|
|
2365
2742
|
const styleEl = document.createElement("style");
|
|
@@ -2385,240 +2762,223 @@ function removeScopedStyle(scopeId) {
|
|
|
2385
2762
|
if (el) el.remove();
|
|
2386
2763
|
}
|
|
2387
2764
|
|
|
2388
|
-
// src/utils/
|
|
2389
|
-
function
|
|
2390
|
-
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
2391
|
-
}
|
|
2392
|
-
function isEventHandlerAttr(name) {
|
|
2393
|
-
if (name.length < 3) return false;
|
|
2394
|
-
const lower = name.toLowerCase();
|
|
2395
|
-
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
2396
|
-
}
|
|
2397
|
-
var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
|
|
2398
|
-
var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
|
|
2399
|
-
function sanitizeUrl(url) {
|
|
2400
|
-
const probe = stripControlChars(url).trim();
|
|
2401
|
-
if (!probe) return "";
|
|
2402
|
-
const output = url.replace(OUTER_TRIM, "");
|
|
2403
|
-
const lower = probe.toLowerCase();
|
|
2404
|
-
let schemeEnd = -1;
|
|
2405
|
-
for (let i = 0; i < lower.length; i++) {
|
|
2406
|
-
const ch = lower.charCodeAt(i);
|
|
2407
|
-
if (ch === 58) {
|
|
2408
|
-
schemeEnd = i;
|
|
2409
|
-
break;
|
|
2410
|
-
}
|
|
2411
|
-
if (ch === 47 || ch === 63 || ch === 35) break;
|
|
2412
|
-
}
|
|
2413
|
-
if (schemeEnd === -1) return output;
|
|
2414
|
-
const scheme = lower.slice(0, schemeEnd + 1);
|
|
2415
|
-
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output;
|
|
2416
|
-
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
2417
|
-
return output;
|
|
2418
|
-
}
|
|
2419
|
-
var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
|
|
2420
|
-
function sanitizeSrcset(value) {
|
|
2421
|
-
const parts = value.split(",");
|
|
2422
|
-
const out = [];
|
|
2423
|
-
for (let i = 0; i < parts.length; i++) {
|
|
2424
|
-
const part = parts[i].trim();
|
|
2425
|
-
if (!part) continue;
|
|
2426
|
-
const m = part.match(/^(\S+)(\s+.+)?$/);
|
|
2427
|
-
if (!m) continue;
|
|
2428
|
-
if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
|
|
2429
|
-
const safe = sanitizeUrl(m[1]);
|
|
2430
|
-
if (!safe) continue;
|
|
2431
|
-
out.push(m[2] ? `${safe}${m[2]}` : safe);
|
|
2432
|
-
}
|
|
2433
|
-
return out.join(", ");
|
|
2434
|
-
}
|
|
2435
|
-
var CSS_DANGER_GATE = /[(:@\\]/;
|
|
2436
|
-
function isCssWhitespace(ch) {
|
|
2765
|
+
// src/utils/metaRefresh.ts
|
|
2766
|
+
function isAsciiWhitespace(ch) {
|
|
2437
2767
|
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
2438
2768
|
}
|
|
2439
|
-
function
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
"
|
|
2483
|
-
"
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
const reasons = [
|
|
2507
|
-
"url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
|
|
2508
|
-
"expression() executes arbitrary script in legacy engines",
|
|
2509
|
-
"the javascript: scheme executes script",
|
|
2510
|
-
"the vbscript: scheme executes script",
|
|
2511
|
-
"-moz-binding loads and runs remote XBL",
|
|
2512
|
-
"behavior: loads and runs a remote HTC script",
|
|
2513
|
-
"@import pulls in a remote stylesheet",
|
|
2514
|
-
"image-set() fetches remote images, the same exfiltration channel as url()",
|
|
2515
|
-
"filter:progid activates legacy scriptable filters"
|
|
2516
|
-
];
|
|
2517
|
-
const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
|
|
2518
|
-
const where = describeElement(ctx?.element);
|
|
2519
|
-
const key = `${where}|${declaration}`;
|
|
2520
|
-
if (warnedDeclarations.has(key)) return "";
|
|
2521
|
-
if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
|
|
2522
|
-
if (declarationCapAnnounced) return "";
|
|
2523
|
-
declarationCapAnnounced = true;
|
|
2524
|
-
return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
|
|
2525
|
-
}
|
|
2526
|
-
warnedDeclarations.add(key);
|
|
2527
|
-
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.`;
|
|
2528
|
-
});
|
|
2529
|
-
}
|
|
2530
|
-
function blockedCssReason(value) {
|
|
2531
|
-
if (!CSS_DANGER_GATE.test(value)) return -1;
|
|
2532
|
-
const normalized = value.includes("\\") ? decodeCssEscapes2(value) : value;
|
|
2533
|
-
const lower = normalized.toLowerCase().replace(/\s+/g, "");
|
|
2534
|
-
for (let i = 0; i < BLOCKED_CSS_NEEDLES.length; i++) {
|
|
2535
|
-
if (lower.includes(BLOCKED_CSS_NEEDLES[i])) return i;
|
|
2536
|
-
}
|
|
2537
|
-
return -1;
|
|
2538
|
-
}
|
|
2539
|
-
function sanitizeCSSValue(value, context) {
|
|
2540
|
-
const reason = blockedCssReason(value);
|
|
2541
|
-
if (reason === -1) return value;
|
|
2542
|
-
warnDroppedDeclaration(value, reason, context);
|
|
2543
|
-
return "";
|
|
2544
|
-
}
|
|
2545
|
-
function sanitizeStyleAttribute(cssText, context) {
|
|
2546
|
-
const input = String(cssText);
|
|
2547
|
-
if (input.trim() === "") return "";
|
|
2548
|
-
if (typeof document === "undefined") {
|
|
2549
|
-
return sanitizeCSSValue(input, context) === "" ? "" : input;
|
|
2550
|
-
}
|
|
2551
|
-
const probe = document.createElement("div");
|
|
2552
|
-
try {
|
|
2553
|
-
probe.style.cssText = input;
|
|
2554
|
-
} catch {
|
|
2555
|
-
return "";
|
|
2556
|
-
}
|
|
2557
|
-
const declarations = [];
|
|
2558
|
-
for (let i = 0; i < probe.style.length; i++) {
|
|
2559
|
-
const property = probe.style[i];
|
|
2560
|
-
const value = probe.style.getPropertyValue(property);
|
|
2561
|
-
let reason = blockedCssReason(value);
|
|
2562
|
-
if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
|
|
2563
|
-
if (reason !== -1) {
|
|
2564
|
-
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
2565
|
-
continue;
|
|
2566
|
-
}
|
|
2567
|
-
const priority = probe.style.getPropertyPriority(property);
|
|
2568
|
-
declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
|
|
2569
|
-
}
|
|
2570
|
-
return declarations.join("; ");
|
|
2769
|
+
function trimAsciiWhitespace(value) {
|
|
2770
|
+
let start = 0;
|
|
2771
|
+
let end = value.length;
|
|
2772
|
+
while (start < end && isAsciiWhitespace(value[start])) start++;
|
|
2773
|
+
while (end > start && isAsciiWhitespace(value[end - 1])) end--;
|
|
2774
|
+
return value.slice(start, end);
|
|
2775
|
+
}
|
|
2776
|
+
var forbidden = (reason) => ({ kind: "forbidden", reason });
|
|
2777
|
+
function parseMetaRefreshContent(content) {
|
|
2778
|
+
const raw = trimAsciiWhitespace(stripControlChars(content));
|
|
2779
|
+
if (raw === "") return forbidden(DEV ? "empty content" : "");
|
|
2780
|
+
let i = 0;
|
|
2781
|
+
while (i < raw.length && raw[i] >= "0" && raw[i] <= "9") i++;
|
|
2782
|
+
if (i === 0) return forbidden(DEV ? "missing or non-numeric delay" : "");
|
|
2783
|
+
const delay = Number.parseInt(raw.slice(0, i), 10);
|
|
2784
|
+
if (!Number.isFinite(delay)) return forbidden(DEV ? "unparseable delay" : "");
|
|
2785
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
2786
|
+
if (i === raw.length) return { kind: "delay-only", delay };
|
|
2787
|
+
if (raw[i] !== ";" && raw[i] !== ",") return forbidden(DEV ? "unexpected text after delay" : "");
|
|
2788
|
+
i++;
|
|
2789
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
2790
|
+
const keyStart = i;
|
|
2791
|
+
while (i < raw.length && !isAsciiWhitespace(raw[i]) && raw[i] !== "=") i++;
|
|
2792
|
+
const key = raw.slice(keyStart, i).toLowerCase();
|
|
2793
|
+
if (key !== "url") return forbidden(DEV ? `unsupported refresh key ${JSON.stringify(key)}` : "");
|
|
2794
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
2795
|
+
if (raw[i] !== "=") return forbidden(DEV ? "missing '=' after url" : "");
|
|
2796
|
+
i++;
|
|
2797
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
2798
|
+
let destination;
|
|
2799
|
+
const quote = raw[i];
|
|
2800
|
+
if (quote === '"' || quote === "'") {
|
|
2801
|
+
const close = raw.indexOf(quote, i + 1);
|
|
2802
|
+
if (close === -1) return forbidden(DEV ? "unterminated quoted destination" : "");
|
|
2803
|
+
destination = raw.slice(i + 1, close);
|
|
2804
|
+
const rest = trimAsciiWhitespace(raw.slice(close + 1));
|
|
2805
|
+
if (rest !== "") return forbidden(DEV ? "trailing text after quoted destination" : "");
|
|
2806
|
+
} else {
|
|
2807
|
+
destination = trimAsciiWhitespace(raw.slice(i));
|
|
2808
|
+
if (/[;,]/.test(destination)) return forbidden(DEV ? "multiple refresh assignments" : "");
|
|
2809
|
+
}
|
|
2810
|
+
if (destination === "") return forbidden(DEV ? "empty destination" : "");
|
|
2811
|
+
const sanitizedDestination = sanitizeUrl(destination);
|
|
2812
|
+
if (sanitizedDestination === "") return forbidden(DEV ? "destination uses a disallowed protocol" : "");
|
|
2813
|
+
return { kind: "allowed", delay, destination, sanitizedDestination };
|
|
2814
|
+
}
|
|
2815
|
+
function resolveMetaRefreshPolicy(attributes) {
|
|
2816
|
+
let httpEquiv;
|
|
2817
|
+
let content;
|
|
2818
|
+
let duplicateName;
|
|
2819
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2820
|
+
for (const [key, value] of attributes) {
|
|
2821
|
+
const canonical = canonicalAttrName(key);
|
|
2822
|
+
if (seen.has(canonical)) duplicateName = canonical;
|
|
2823
|
+
seen.add(canonical);
|
|
2824
|
+
if (canonical === "http-equiv") httpEquiv = value;
|
|
2825
|
+
else if (canonical === "content") content = value;
|
|
2826
|
+
}
|
|
2827
|
+
if (duplicateName !== void 0) {
|
|
2828
|
+
return forbidden(DEV ? `duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}` : "");
|
|
2829
|
+
}
|
|
2830
|
+
if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
|
|
2831
|
+
if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
|
|
2832
|
+
return { kind: "not-refresh" };
|
|
2833
|
+
}
|
|
2834
|
+
if (typeof content !== "string") return { kind: "not-refresh" };
|
|
2835
|
+
return parseMetaRefreshContent(content);
|
|
2571
2836
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2837
|
+
|
|
2838
|
+
// src/utils/elementPolicy.ts
|
|
2839
|
+
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
2840
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
2841
|
+
var BLOCKED_ELEMENTS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
2842
|
+
function canonicalTagName(tag) {
|
|
2843
|
+
return canonicalAttrName(tag);
|
|
2844
|
+
}
|
|
2845
|
+
function isBlockedElement(tag) {
|
|
2846
|
+
return BLOCKED_ELEMENTS.has(canonicalTagName(tag));
|
|
2847
|
+
}
|
|
2848
|
+
var ContextualRefusal = {
|
|
2849
|
+
None: 0,
|
|
2850
|
+
/** The element would carry a refresh directive the shared policy forbids. */
|
|
2851
|
+
MetaRefreshForbidden: 1,
|
|
2852
|
+
/** A `<meta>` with a reactive binding may not carry any refresh directive. */
|
|
2853
|
+
MetaRefreshReactive: 2,
|
|
2854
|
+
/** A runtime value may not choose which program a `<script>` runs. */
|
|
2855
|
+
ScriptSource: 3,
|
|
2856
|
+
/** An SVG animation may not target a URL, handler or document attribute. */
|
|
2857
|
+
AnimationTarget: 4,
|
|
2858
|
+
/** A runtime-written `href` may not become an applied stylesheet. */
|
|
2859
|
+
StylesheetSource: 5
|
|
2860
|
+
};
|
|
2861
|
+
var SCRIPT_SOURCE_ATTRIBUTES = /* @__PURE__ */ new Set(["src", "href", "xlink:href", "type", "language"]);
|
|
2862
|
+
var SVG_ANIMATION_ELEMENTS = /* @__PURE__ */ new Set(["animate", "set", "animateMotion", "animateTransform", "animateColor"]);
|
|
2863
|
+
var reactiveMetaElements = /* @__PURE__ */ new WeakSet();
|
|
2864
|
+
function isHtmlNamespace(el) {
|
|
2865
|
+
return el.namespaceURI === HTML_NS || el.namespaceURI === null;
|
|
2866
|
+
}
|
|
2867
|
+
function metaSnapshot(el, canonical, value) {
|
|
2868
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
2869
|
+
const attrs = el.attributes;
|
|
2870
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
2871
|
+
const attr = attrs[i];
|
|
2872
|
+
if (attr.name === canonical) continue;
|
|
2873
|
+
snapshot.set(attr.name, attr.value);
|
|
2874
|
+
}
|
|
2875
|
+
if (canonical !== null && value !== null) snapshot.set(canonical, value);
|
|
2876
|
+
return snapshot;
|
|
2877
|
+
}
|
|
2878
|
+
function claimReactiveMeta(el) {
|
|
2879
|
+
if (reactiveMetaElements.has(el)) return;
|
|
2880
|
+
reactiveMetaElements.add(el);
|
|
2881
|
+
if (el.getAttribute("content") === null) return;
|
|
2882
|
+
if (resolveMetaRefreshPolicy(metaSnapshot(el, null, null)).kind !== "not-refresh") {
|
|
2883
|
+
el.removeAttribute("content");
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
function metaRefusal(el, name, value, origin) {
|
|
2887
|
+
if (!isHtmlNamespace(el)) return ContextualRefusal.None;
|
|
2888
|
+
const canonical = canonicalAttrName(name);
|
|
2889
|
+
if (canonical !== "http-equiv" && canonical !== "content") return ContextualRefusal.None;
|
|
2890
|
+
if (origin === "reactive") claimReactiveMeta(el);
|
|
2891
|
+
if (value === null) return ContextualRefusal.None;
|
|
2892
|
+
if (canonical === "content" && !el.hasAttribute("http-equiv")) return ContextualRefusal.None;
|
|
2893
|
+
const decision = resolveMetaRefreshPolicy(metaSnapshot(el, canonical, value));
|
|
2894
|
+
if (decision.kind === "forbidden") return ContextualRefusal.MetaRefreshForbidden;
|
|
2895
|
+
if (decision.kind !== "not-refresh" && reactiveMetaElements.has(el)) return ContextualRefusal.MetaRefreshReactive;
|
|
2896
|
+
return ContextualRefusal.None;
|
|
2897
|
+
}
|
|
2898
|
+
function animationRefusal(name, value) {
|
|
2899
|
+
if (value === null || canonicalAttrName(name) !== "attributename") return ContextualRefusal.None;
|
|
2900
|
+
const target = stripControlChars(value);
|
|
2901
|
+
return isUrlAttribute(target) || isEventHandlerAttr(target) || isHtmlContentAttribute(target) ? ContextualRefusal.AnimationTarget : ContextualRefusal.None;
|
|
2902
|
+
}
|
|
2903
|
+
var runtimeHrefLinks = /* @__PURE__ */ new WeakSet();
|
|
2904
|
+
function relHasStylesheet(rel) {
|
|
2905
|
+
if (rel === null) return false;
|
|
2906
|
+
const lower = canonicalAttrName(rel);
|
|
2907
|
+
if (!lower.includes("stylesheet")) return false;
|
|
2908
|
+
const tokens = lower.split(/[\t\n\f\r ]+/);
|
|
2909
|
+
for (let i = 0; i < tokens.length; i++) if (tokens[i] === "stylesheet") return true;
|
|
2910
|
+
return false;
|
|
2593
2911
|
}
|
|
2594
|
-
function
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
if (
|
|
2598
|
-
|
|
2599
|
-
|
|
2912
|
+
function stylesheetRefusal(el, name, value, origin) {
|
|
2913
|
+
if (!isHtmlNamespace(el)) return ContextualRefusal.None;
|
|
2914
|
+
const canonical = canonicalAttrName(name);
|
|
2915
|
+
if (canonical === "href") {
|
|
2916
|
+
if (origin === "static" || value === null) return ContextualRefusal.None;
|
|
2917
|
+
if (relHasStylesheet(el.getAttribute("rel"))) return ContextualRefusal.StylesheetSource;
|
|
2918
|
+
runtimeHrefLinks.add(el);
|
|
2919
|
+
return ContextualRefusal.None;
|
|
2920
|
+
}
|
|
2921
|
+
if (canonical === "rel") {
|
|
2922
|
+
if (!relHasStylesheet(value)) return ContextualRefusal.None;
|
|
2923
|
+
return runtimeHrefLinks.has(el) && el.hasAttribute("href") ? ContextualRefusal.StylesheetSource : ContextualRefusal.None;
|
|
2924
|
+
}
|
|
2925
|
+
return ContextualRefusal.None;
|
|
2926
|
+
}
|
|
2927
|
+
function contextualAttributeRefusal(el, name, value, origin) {
|
|
2928
|
+
const local = el.localName;
|
|
2929
|
+
if (local === "meta") return metaRefusal(el, name, value, origin);
|
|
2930
|
+
if (local === "link") return stylesheetRefusal(el, name, value, origin);
|
|
2931
|
+
if (local === "script") {
|
|
2932
|
+
if (origin === "static" || value === null) return ContextualRefusal.None;
|
|
2933
|
+
return SCRIPT_SOURCE_ATTRIBUTES.has(canonicalAttrName(name)) ? ContextualRefusal.ScriptSource : ContextualRefusal.None;
|
|
2934
|
+
}
|
|
2935
|
+
if (SVG_ANIMATION_ELEMENTS.has(local) && el.namespaceURI === SVG_NS) return animationRefusal(name, value);
|
|
2936
|
+
return ContextualRefusal.None;
|
|
2937
|
+
}
|
|
2938
|
+
function settleAttributeProvenance(el, name, origin) {
|
|
2939
|
+
if (el.localName !== "link" || canonicalAttrName(name) !== "href") return;
|
|
2940
|
+
if (origin !== "static" && el.hasAttribute("href")) runtimeHrefLinks.add(el);
|
|
2941
|
+
else runtimeHrefLinks.delete(el);
|
|
2600
2942
|
}
|
|
2601
2943
|
|
|
2602
2944
|
// src/utils/setSafeAttribute.ts
|
|
2603
|
-
var
|
|
2604
|
-
var
|
|
2945
|
+
var HTML_NS2 = "http://www.w3.org/1999/xhtml";
|
|
2946
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
2605
2947
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
2606
2948
|
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
2607
2949
|
var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
|
|
2608
2950
|
function isHtmlElement(el) {
|
|
2609
|
-
return el.namespaceURI === null || el.namespaceURI ===
|
|
2951
|
+
return el.namespaceURI === null || el.namespaceURI === HTML_NS2;
|
|
2952
|
+
}
|
|
2953
|
+
function warnContextualRefusal(label, name, refusal) {
|
|
2954
|
+
devWarnLazy(() => {
|
|
2955
|
+
const reasons = [
|
|
2956
|
+
"",
|
|
2957
|
+
'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)',
|
|
2958
|
+
"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",
|
|
2959
|
+
"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",
|
|
2960
|
+
"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",
|
|
2961
|
+
'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'
|
|
2962
|
+
];
|
|
2963
|
+
return `${label}: refusing "${name}" in this element's context \u2014 ${reasons[refusal]}. The write was not performed.`;
|
|
2964
|
+
});
|
|
2610
2965
|
}
|
|
2611
2966
|
function setProp(el, key, val) {
|
|
2612
2967
|
el[key] = val;
|
|
2613
2968
|
}
|
|
2614
2969
|
function namespaceFor(el, name) {
|
|
2615
|
-
if (el.namespaceURI !==
|
|
2970
|
+
if (el.namespaceURI !== SVG_NS2) return null;
|
|
2616
2971
|
const lower = name.toLowerCase();
|
|
2617
2972
|
if (lower.startsWith("xlink:")) return XLINK_NS;
|
|
2618
2973
|
if (lower.startsWith("xml:")) return XML_NS;
|
|
2619
2974
|
return null;
|
|
2620
2975
|
}
|
|
2621
2976
|
function setSafeAttribute(el, name, value, options = {}) {
|
|
2977
|
+
const written = commitSafeAttribute(el, name, value, options);
|
|
2978
|
+
if (el.localName === "link") settleAttributeProvenance(el, name, options.reactive ? "reactive" : "runtime");
|
|
2979
|
+
return written;
|
|
2980
|
+
}
|
|
2981
|
+
function commitSafeAttribute(el, name, value, options) {
|
|
2622
2982
|
const ns = namespaceFor(el, name);
|
|
2623
2983
|
const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
|
|
2624
2984
|
if (isEventHandlerAttr(name)) {
|
|
@@ -2631,6 +2991,14 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
2631
2991
|
else el.removeAttribute(name);
|
|
2632
2992
|
return false;
|
|
2633
2993
|
}
|
|
2994
|
+
const pending = value == null || value === false ? null : value === true ? "" : String(value);
|
|
2995
|
+
const refusal = contextualAttributeRefusal(el, name, pending, options.reactive ? "reactive" : "runtime");
|
|
2996
|
+
if (refusal !== ContextualRefusal.None) {
|
|
2997
|
+
warnContextualRefusal(options.label ?? "setSafeAttribute", name, refusal);
|
|
2998
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
2999
|
+
else el.removeAttribute(name);
|
|
3000
|
+
return false;
|
|
3001
|
+
}
|
|
2634
3002
|
if (value == null) {
|
|
2635
3003
|
if (ns) el.removeAttributeNS(ns, localName);
|
|
2636
3004
|
else el.removeAttribute(name);
|
|
@@ -2674,7 +3042,12 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
2674
3042
|
setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
|
|
2675
3043
|
return true;
|
|
2676
3044
|
}
|
|
2677
|
-
const safe =
|
|
3045
|
+
const safe = str === "" ? "" : resolveAttributeValue(name, str, { element: el });
|
|
3046
|
+
if (safe === null) {
|
|
3047
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
3048
|
+
else el.removeAttribute(name);
|
|
3049
|
+
return true;
|
|
3050
|
+
}
|
|
2678
3051
|
const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
|
|
2679
3052
|
if (current === safe) return true;
|
|
2680
3053
|
if (ns) el.setAttributeNS(ns, name, safe);
|
|
@@ -2697,7 +3070,7 @@ function bindAttribute(el, attr, getter) {
|
|
|
2697
3070
|
reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
|
|
2698
3071
|
return;
|
|
2699
3072
|
}
|
|
2700
|
-
setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")
|
|
3073
|
+
setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")`, reactive: true });
|
|
2701
3074
|
}
|
|
2702
3075
|
return reactiveBinding2(commit, el);
|
|
2703
3076
|
}
|
|
@@ -2734,7 +3107,7 @@ function bindBoolAttr(el, attr, getter) {
|
|
|
2734
3107
|
reportError(err, { phase: "binding", name: "bindBoolAttr", node: el });
|
|
2735
3108
|
return;
|
|
2736
3109
|
}
|
|
2737
|
-
setSafeAttribute(el, attr, Boolean(value), { label: "bindBoolAttr" });
|
|
3110
|
+
setSafeAttribute(el, attr, Boolean(value), { label: "bindBoolAttr", reactive: true });
|
|
2738
3111
|
}
|
|
2739
3112
|
return reactiveBinding2(commit, el);
|
|
2740
3113
|
}
|
|
@@ -3182,12 +3555,17 @@ function effect(effectFn, options) {
|
|
|
3182
3555
|
try {
|
|
3183
3556
|
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
3184
3557
|
if (ctx.rerunPending) drainReruns(ctx);
|
|
3558
|
+
} catch (err) {
|
|
3559
|
+
ctx.disposed = true;
|
|
3560
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3561
|
+
cleanup2(ctx.subscriber);
|
|
3562
|
+
throw err;
|
|
3185
3563
|
} finally {
|
|
3186
3564
|
ctx.running = false;
|
|
3187
3565
|
ctx.rerunPending = false;
|
|
3188
3566
|
}
|
|
3189
3567
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
3190
|
-
if (hook) hook
|
|
3568
|
+
if (hook) emitDevtools(hook, "effect:create", { effectFn });
|
|
3191
3569
|
return () => disposeEffect(ctx);
|
|
3192
3570
|
}
|
|
3193
3571
|
|
|
@@ -3375,7 +3753,7 @@ function scrollLock() {
|
|
|
3375
3753
|
}
|
|
3376
3754
|
|
|
3377
3755
|
// src/platform/customElement.ts
|
|
3378
|
-
var
|
|
3756
|
+
var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
3379
3757
|
var MAX_RENDER_PASSES = 10;
|
|
3380
3758
|
var ABANDONED_RENDER = /* @__PURE__ */ Symbol("sibujs.defineElement.abandonedRender");
|
|
3381
3759
|
function defineElement(name, component, options = {}) {
|
|
@@ -3524,7 +3902,10 @@ function defineElement(name, component, options = {}) {
|
|
|
3524
3902
|
customElements.define(name, SibuElement);
|
|
3525
3903
|
}
|
|
3526
3904
|
function svgElement(tag, props = {}, ...nodes) {
|
|
3527
|
-
|
|
3905
|
+
if (isBlockedElement(tag)) {
|
|
3906
|
+
throw new Error(`svgElement: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
|
|
3907
|
+
}
|
|
3908
|
+
const el = document.createElementNS(SVG_NS3, tag);
|
|
3528
3909
|
for (const [key, value] of Object.entries(props)) {
|
|
3529
3910
|
if (key === "nodes") continue;
|
|
3530
3911
|
if (typeof value === "function" && isEventHandlerAttr(key)) {
|