sibujs 4.7.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/dist/browser.cjs +45 -4
- package/dist/browser.d.cts +23 -0
- package/dist/browser.d.ts +23 -0
- package/dist/browser.js +6 -5
- package/dist/build.cjs +513 -78
- package/dist/build.d.cts +22 -8
- package/dist/build.d.ts +22 -8
- package/dist/build.js +68 -21
- package/dist/cdn.dev.global.js +15 -13
- package/dist/cdn.full.dev.global.js +15 -13
- package/dist/cdn.full.global.js +14 -12
- package/dist/cdn.global.js +14 -12
- package/dist/chunk-3ACAAQIN.js +224 -0
- package/dist/{chunk-DKXRACVN.js → chunk-44MTL7ED.js} +6 -6
- package/dist/{chunk-3QWBSL5R.js → chunk-4WMSTZ7J.js} +8 -5
- package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
- package/dist/{chunk-BSY63EM6.js → chunk-C5PEWEGH.js} +20 -5
- package/dist/{chunk-BC2SECJD.js → chunk-C7AA2CZX.js} +1 -1
- package/dist/{chunk-SLM3IA34.js → chunk-CZPRSWSS.js} +1 -1
- package/dist/{chunk-FQRUXCEE.js → chunk-DSID757X.js} +1 -1
- package/dist/{chunk-ZVL7TY4K.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-Z3OHK6QT.js → chunk-GSGNX35Q.js} +5 -5
- package/dist/{chunk-XYV3EDB7.js → chunk-J3YQ457H.js} +37 -20
- package/dist/{chunk-VZSG24LS.js → chunk-JUTFX2RM.js} +18 -10
- package/dist/{chunk-RUSSKG6G.js → chunk-KJ3UKABJ.js} +1 -1
- package/dist/{chunk-UNWRJRKC.js → chunk-LI4W3U6C.js} +1 -1
- package/dist/{chunk-FNJXNGYZ.js → chunk-O6TRVMJI.js} +58 -6
- package/dist/{chunk-7LUJQAOJ.js → chunk-Q5J2LVKH.js} +1 -1
- package/dist/{chunk-4AWA2PVD.js → chunk-QGMSESDD.js} +10 -5
- package/dist/{chunk-XZR4PXRE.js → chunk-RGLPEBDE.js} +13 -10
- package/dist/{chunk-JWKYU5GV.js → chunk-TQQUN4WR.js} +1 -1
- package/dist/{chunk-WOLJZUFQ.js → chunk-UCNP3HVM.js} +12 -8
- package/dist/{chunk-WEQ3DMVL.js → chunk-V3LHZSU5.js} +1 -1
- package/dist/{chunk-UOL2ECCS.js → chunk-VWT3VXPL.js} +18 -12
- package/dist/{chunk-2BPG2XDA.js → chunk-ZVLHNEEL.js} +17 -35
- 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 +9 -2
- package/dist/data.js +6 -6
- package/dist/devtools.cjs +1 -1
- package/dist/devtools.js +5 -5
- package/dist/ecosystem.cjs +400 -37
- package/dist/ecosystem.js +9 -8
- package/dist/extras.cjs +621 -252
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +24 -23
- package/dist/index.cjs +464 -70
- package/dist/index.d.cts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.js +16 -12
- package/dist/motion.cjs +1 -1
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +10 -7
- package/dist/patterns.js +6 -6
- package/dist/performance.cjs +31 -2
- package/dist/performance.d.cts +12 -2
- package/dist/performance.d.ts +12 -2
- package/dist/performance.js +8 -6
- package/dist/plugins.cjs +558 -231
- package/dist/plugins.d.cts +8 -3
- package/dist/plugins.d.ts +8 -3
- package/dist/plugins.js +22 -28
- package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
- package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.cts} +6 -0
- package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.ts} +6 -0
- package/dist/ssr.cjs +457 -131
- package/dist/ssr.d.cts +55 -5
- package/dist/ssr.d.ts +55 -5
- package/dist/ssr.js +11 -9
- package/dist/testing.cjs +3 -3
- package/dist/testing.js +4 -4
- package/dist/ui.cjs +608 -240
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +9 -8
- package/dist/widgets.cjs +1 -1
- package/dist/widgets.js +7 -7
- package/package.json +5 -4
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
function stripControlChars(value) {
|
|
7
7
|
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
8
8
|
}
|
|
9
|
+
var ASCII_UPPER = /[A-Z]/;
|
|
10
|
+
var ASCII_UPPER_G = /[A-Z]/g;
|
|
9
11
|
function canonicalAttrName(name) {
|
|
10
|
-
return name.replace(
|
|
12
|
+
return ASCII_UPPER.test(name) ? name.replace(ASCII_UPPER_G, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32)) : name;
|
|
11
13
|
}
|
|
12
14
|
function isEventHandlerAttr(name) {
|
|
13
15
|
if (name.length < 3) return false;
|
|
@@ -20,6 +22,9 @@ function sanitizeUrl(url) {
|
|
|
20
22
|
const probe = stripControlChars(url).trim();
|
|
21
23
|
if (!probe) return "";
|
|
22
24
|
const output = url.replace(OUTER_TRIM, "");
|
|
25
|
+
if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
23
28
|
const lower = probe.toLowerCase();
|
|
24
29
|
let schemeEnd = -1;
|
|
25
30
|
for (let i = 0; i < lower.length; i++) {
|
|
@@ -36,19 +41,58 @@ function sanitizeUrl(url) {
|
|
|
36
41
|
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
37
42
|
return output;
|
|
38
43
|
}
|
|
39
|
-
var SRCSET_DESCRIPTOR = /^\
|
|
44
|
+
var SRCSET_DESCRIPTOR = /^\d+(\.\d+)?[wx]$/;
|
|
45
|
+
function isSrcsetWhitespace(ch) {
|
|
46
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
47
|
+
}
|
|
40
48
|
function sanitizeSrcset(value) {
|
|
41
|
-
const
|
|
49
|
+
const input = String(value);
|
|
42
50
|
const out = [];
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
let i = 0;
|
|
52
|
+
const n = input.length;
|
|
53
|
+
while (i < n) {
|
|
54
|
+
while (i < n && (isSrcsetWhitespace(input[i]) || input[i] === ",")) i++;
|
|
55
|
+
if (i >= n) break;
|
|
56
|
+
const urlStart = i;
|
|
57
|
+
while (i < n && !isSrcsetWhitespace(input[i])) i++;
|
|
58
|
+
let url = input.slice(urlStart, i);
|
|
59
|
+
const descriptors = [];
|
|
60
|
+
if (url.endsWith(",")) {
|
|
61
|
+
url = url.replace(/,+$/, "");
|
|
62
|
+
} else {
|
|
63
|
+
let current = "";
|
|
64
|
+
let inParens = false;
|
|
65
|
+
while (i < n) {
|
|
66
|
+
const ch = input[i];
|
|
67
|
+
if (inParens) {
|
|
68
|
+
current += ch;
|
|
69
|
+
if (ch === ")") inParens = false;
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (isSrcsetWhitespace(ch)) {
|
|
74
|
+
if (current) descriptors.push(current);
|
|
75
|
+
current = "";
|
|
76
|
+
i++;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (ch === ",") {
|
|
80
|
+
i++;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
if (ch === "(") inParens = true;
|
|
84
|
+
current += ch;
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
if (current) descriptors.push(current);
|
|
88
|
+
}
|
|
89
|
+
if (url === "") continue;
|
|
90
|
+
if (descriptors.length > 1) continue;
|
|
91
|
+
if (descriptors.length === 1 && !SRCSET_DESCRIPTOR.test(descriptors[0])) continue;
|
|
92
|
+
const safe = sanitizeUrl(url);
|
|
50
93
|
if (!safe) continue;
|
|
51
|
-
|
|
94
|
+
if (safe.startsWith(",") || safe.endsWith(",")) continue;
|
|
95
|
+
out.push(descriptors.length === 1 ? `${safe} ${descriptors[0]}` : safe);
|
|
52
96
|
}
|
|
53
97
|
return out.join(", ");
|
|
54
98
|
}
|
|
@@ -107,8 +151,16 @@ var BLOCKED_CSS_NEEDLES = [
|
|
|
107
151
|
"behavior:",
|
|
108
152
|
"@import",
|
|
109
153
|
"image-set(",
|
|
110
|
-
"filter:progid"
|
|
154
|
+
"filter:progid",
|
|
155
|
+
// CSS Images 4 `image("…")` and CSS Values 5 `src("…")` take a URL as a
|
|
156
|
+
// plain STRING, so they fetch without ever spelling `url(`. Support is thin
|
|
157
|
+
// today, which is exactly when a blocklist should get ahead of it.
|
|
158
|
+
"image(",
|
|
159
|
+
"src("
|
|
111
160
|
];
|
|
161
|
+
var CSS_REASON_MOZ_BINDING = 4;
|
|
162
|
+
var CSS_REASON_BEHAVIOR = 5;
|
|
163
|
+
var CSS_REASON_PROGID = 8;
|
|
112
164
|
function describeElement(el) {
|
|
113
165
|
if (!el) return "an element";
|
|
114
166
|
let out = el.tagName.toLowerCase();
|
|
@@ -132,7 +184,9 @@ function warnDroppedDeclaration(value, reason, ctx) {
|
|
|
132
184
|
"behavior: loads and runs a remote HTC script",
|
|
133
185
|
"@import pulls in a remote stylesheet",
|
|
134
186
|
"image-set() fetches remote images, the same exfiltration channel as url()",
|
|
135
|
-
"filter:progid activates legacy scriptable filters"
|
|
187
|
+
"filter:progid activates legacy scriptable filters",
|
|
188
|
+
"image() fetches its string argument as a URL, the same exfiltration channel as url()",
|
|
189
|
+
"src() fetches its string argument as a URL, the same exfiltration channel as url()"
|
|
136
190
|
];
|
|
137
191
|
const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
|
|
138
192
|
const where = describeElement(ctx?.element);
|
|
@@ -147,27 +201,145 @@ function warnDroppedDeclaration(value, reason, ctx) {
|
|
|
147
201
|
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.`;
|
|
148
202
|
});
|
|
149
203
|
}
|
|
204
|
+
function normalizeCssForScan(value) {
|
|
205
|
+
const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
|
|
206
|
+
return normalized.toLowerCase().replace(/\s+/g, "");
|
|
207
|
+
}
|
|
150
208
|
function blockedCssReason(value) {
|
|
151
209
|
if (!CSS_DANGER_GATE.test(value)) return -1;
|
|
152
|
-
const
|
|
153
|
-
const lower = normalized.toLowerCase().replace(/\s+/g, "");
|
|
210
|
+
const lower = normalizeCssForScan(value);
|
|
154
211
|
for (let i = 0; i < BLOCKED_CSS_NEEDLES.length; i++) {
|
|
155
212
|
if (lower.includes(BLOCKED_CSS_NEEDLES[i])) return i;
|
|
156
213
|
}
|
|
157
214
|
return -1;
|
|
158
215
|
}
|
|
159
|
-
|
|
160
|
-
|
|
216
|
+
var STYLESHEET_BLOCKED_FUNCTIONS = ["url", "image-set", "image", "src", "expression"];
|
|
217
|
+
function stripCssFunctions(css) {
|
|
218
|
+
const pattern = new RegExp(`(?:-webkit-)?(${STYLESHEET_BLOCKED_FUNCTIONS.join("|")})\\s*\\(`, "gi");
|
|
219
|
+
let out = "";
|
|
220
|
+
let last = 0;
|
|
221
|
+
for (let match = pattern.exec(css); match !== null; match = pattern.exec(css)) {
|
|
222
|
+
const before = match.index > 0 ? css[match.index - 1] : "";
|
|
223
|
+
if (/[\w-]/.test(before)) continue;
|
|
224
|
+
let depth = 1;
|
|
225
|
+
let i = pattern.lastIndex;
|
|
226
|
+
let quote = "";
|
|
227
|
+
for (; i < css.length && depth > 0; i++) {
|
|
228
|
+
const ch = css[i];
|
|
229
|
+
if (quote) {
|
|
230
|
+
if (ch === "\\") i++;
|
|
231
|
+
else if (ch === quote) quote = "";
|
|
232
|
+
} else if (ch === '"' || ch === "'") quote = ch;
|
|
233
|
+
else if (ch === "(") depth++;
|
|
234
|
+
else if (ch === ")") depth--;
|
|
235
|
+
}
|
|
236
|
+
out += `${css.slice(last, match.index)}/* ${match[1].toLowerCase()}() removed */`;
|
|
237
|
+
last = i;
|
|
238
|
+
pattern.lastIndex = i;
|
|
239
|
+
}
|
|
240
|
+
return out + css.slice(last);
|
|
241
|
+
}
|
|
242
|
+
function sanitizeStylesheetText(css) {
|
|
243
|
+
let text = css.includes("\\") ? decodeCssEscapes(css) : css;
|
|
244
|
+
text = text.replace(/@import\b[^;{}]*;?/gi, "/* @import removed */");
|
|
245
|
+
text = stripCssFunctions(text);
|
|
246
|
+
text = text.replace(/(^|[;{\s])behavior\s*:[^;}]*;?/gi, "$1/* behavior removed */");
|
|
247
|
+
text = text.replace(/(^|[;{\s])-moz-binding\s*:[^;}]*;?/gi, "$1/* -moz-binding removed */");
|
|
248
|
+
text = text.replace(/(^|[;{\s])(-ms-)?filter\s*:[^;}]*progid[^;}]*;?/gi, "$1/* filter:progid removed */");
|
|
249
|
+
return text;
|
|
250
|
+
}
|
|
251
|
+
function sanitizeCSSDeclaration(property, value, context) {
|
|
252
|
+
const reason = blockedDeclarationReason(property, value);
|
|
161
253
|
if (reason === -1) return value;
|
|
162
|
-
warnDroppedDeclaration(value, reason, context);
|
|
254
|
+
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
163
255
|
return "";
|
|
164
256
|
}
|
|
257
|
+
function blockedDeclarationReason(property, value) {
|
|
258
|
+
const reason = blockedCssReason(value);
|
|
259
|
+
if (reason !== -1) return reason;
|
|
260
|
+
const name = canonicalAttrName(property);
|
|
261
|
+
if (name === "behavior") return CSS_REASON_BEHAVIOR;
|
|
262
|
+
if (name === "-moz-binding") return CSS_REASON_MOZ_BINDING;
|
|
263
|
+
if ((name === "filter" || name === "-ms-filter") && normalizeCssForScan(value).includes("progid:")) {
|
|
264
|
+
return CSS_REASON_PROGID;
|
|
265
|
+
}
|
|
266
|
+
return -1;
|
|
267
|
+
}
|
|
268
|
+
function parseStyleDeclarations(css) {
|
|
269
|
+
const chunks = [];
|
|
270
|
+
let buf = "";
|
|
271
|
+
let depth = 0;
|
|
272
|
+
let quote = "";
|
|
273
|
+
for (let i = 0; i < css.length; i++) {
|
|
274
|
+
const ch = css[i];
|
|
275
|
+
if (quote) {
|
|
276
|
+
buf += ch;
|
|
277
|
+
if (ch === "\\" && i + 1 < css.length) buf += css[++i];
|
|
278
|
+
else if (ch === quote) quote = "";
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (ch === "\\") {
|
|
282
|
+
buf += ch;
|
|
283
|
+
if (i + 1 < css.length) buf += css[++i];
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (ch === "/" && css[i + 1] === "*") {
|
|
287
|
+
const end = css.indexOf("*/", i + 2);
|
|
288
|
+
if (end === -1) return null;
|
|
289
|
+
i = end + 1;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (ch === '"' || ch === "'") quote = ch;
|
|
293
|
+
else if (ch === "(" || ch === "[") depth++;
|
|
294
|
+
else if (ch === ")" || ch === "]") {
|
|
295
|
+
if (--depth < 0) return null;
|
|
296
|
+
} else if (ch === "{" || ch === "}") return null;
|
|
297
|
+
else if (ch === ";" && depth === 0) {
|
|
298
|
+
chunks.push(buf);
|
|
299
|
+
buf = "";
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
buf += ch;
|
|
303
|
+
}
|
|
304
|
+
if (quote || depth !== 0) return null;
|
|
305
|
+
chunks.push(buf);
|
|
306
|
+
const out = [];
|
|
307
|
+
for (const chunk of chunks) {
|
|
308
|
+
const colon = chunk.indexOf(":");
|
|
309
|
+
if (colon === -1) continue;
|
|
310
|
+
const rawName = chunk.slice(0, colon).trim();
|
|
311
|
+
if (!/^(--[\w-]+|-?[a-zA-Z_][\w-]*)$/.test(rawName)) continue;
|
|
312
|
+
let value = chunk.slice(colon + 1).trim();
|
|
313
|
+
const bang = /!\s*important\s*$/i.exec(value);
|
|
314
|
+
if (bang) value = value.slice(0, bang.index).trim();
|
|
315
|
+
if (value === "") continue;
|
|
316
|
+
const property = rawName.startsWith("--") ? rawName : canonicalAttrName(rawName);
|
|
317
|
+
out.push([property, value, bang !== null]);
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
function sanitizeStyleDeclarationList(cssText, context) {
|
|
322
|
+
const input = String(cssText);
|
|
323
|
+
const declarations = parseStyleDeclarations(input);
|
|
324
|
+
if (declarations === null) {
|
|
325
|
+
if (blockedCssReason(input) !== -1) warnDroppedDeclaration(input, blockedCssReason(input), context);
|
|
326
|
+
return "";
|
|
327
|
+
}
|
|
328
|
+
const kept = [];
|
|
329
|
+
for (const [property, value, important] of declarations) {
|
|
330
|
+
const reason = blockedDeclarationReason(property, value);
|
|
331
|
+
if (reason !== -1) {
|
|
332
|
+
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
kept.push(`${property}: ${value}${important ? " !important" : ""}`);
|
|
336
|
+
}
|
|
337
|
+
return kept.join("; ");
|
|
338
|
+
}
|
|
165
339
|
function sanitizeStyleAttribute(cssText, context) {
|
|
166
340
|
const input = String(cssText);
|
|
167
341
|
if (input.trim() === "") return "";
|
|
168
|
-
if (typeof document === "undefined")
|
|
169
|
-
return sanitizeCSSValue(input, context) === "" ? "" : input;
|
|
170
|
-
}
|
|
342
|
+
if (typeof document === "undefined") return sanitizeStyleDeclarationList(input, context);
|
|
171
343
|
const probe = document.createElement("div");
|
|
172
344
|
try {
|
|
173
345
|
probe.style.cssText = input;
|
|
@@ -178,8 +350,7 @@ function sanitizeStyleAttribute(cssText, context) {
|
|
|
178
350
|
for (let i = 0; i < probe.style.length; i++) {
|
|
179
351
|
const property = probe.style[i];
|
|
180
352
|
const value = probe.style.getPropertyValue(property);
|
|
181
|
-
|
|
182
|
-
if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
|
|
353
|
+
const reason = blockedDeclarationReason(property, value);
|
|
183
354
|
if (reason !== -1) {
|
|
184
355
|
warnDroppedDeclaration(value, reason, { property, element: context?.element });
|
|
185
356
|
continue;
|
|
@@ -241,17 +412,22 @@ function sanitizeAttributeString(attr, value, context) {
|
|
|
241
412
|
if (lower === "style") return sanitizeStyleAttribute(value, context);
|
|
242
413
|
return value;
|
|
243
414
|
}
|
|
415
|
+
function resolveAttributeValue(attr, value, context) {
|
|
416
|
+
if (!isPolicyAttribute(attr)) return value;
|
|
417
|
+
const safe = sanitizeAttributeString(attr, value, context);
|
|
418
|
+
return safe === "" ? null : safe;
|
|
419
|
+
}
|
|
244
420
|
|
|
245
421
|
export {
|
|
246
422
|
stripControlChars,
|
|
247
423
|
canonicalAttrName,
|
|
248
424
|
isEventHandlerAttr,
|
|
249
425
|
sanitizeUrl,
|
|
250
|
-
|
|
426
|
+
sanitizeStylesheetText,
|
|
427
|
+
sanitizeCSSDeclaration,
|
|
251
428
|
sanitizeStyleAttribute,
|
|
252
429
|
stripHtml,
|
|
253
430
|
isHtmlContentAttribute,
|
|
254
431
|
isUrlAttribute,
|
|
255
|
-
|
|
256
|
-
sanitizeAttributeString
|
|
432
|
+
resolveAttributeValue
|
|
257
433
|
};
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readOwnGlobal
|
|
3
|
+
} from "./chunk-62BDYGAG.js";
|
|
4
|
+
import {
|
|
5
|
+
isCodeTextElement,
|
|
6
|
+
isForbiddenMetaEntry,
|
|
7
|
+
isForbiddenMetaSnapshot,
|
|
8
|
+
resolveMetaRefreshPolicy
|
|
9
|
+
} from "./chunk-3ACAAQIN.js";
|
|
1
10
|
import {
|
|
2
11
|
canonicalAttrName,
|
|
3
12
|
isEventHandlerAttr,
|
|
4
13
|
isHtmlContentAttribute,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
stripControlChars
|
|
9
|
-
} from "./chunk-2INLLLMZ.js";
|
|
14
|
+
resolveAttributeValue,
|
|
15
|
+
sanitizeUrl
|
|
16
|
+
} from "./chunk-EXXSN3BW.js";
|
|
10
17
|
import {
|
|
11
18
|
getSSRStore
|
|
12
19
|
} from "./chunk-7XHATCIH.js";
|
|
@@ -16,95 +23,6 @@ import {
|
|
|
16
23
|
replaceChildrenSafely
|
|
17
24
|
} from "./chunk-NYNYSPK7.js";
|
|
18
25
|
|
|
19
|
-
// src/utils/metaRefresh.ts
|
|
20
|
-
function isAsciiWhitespace(ch) {
|
|
21
|
-
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
22
|
-
}
|
|
23
|
-
function trimAsciiWhitespace(value) {
|
|
24
|
-
let start = 0;
|
|
25
|
-
let end = value.length;
|
|
26
|
-
while (start < end && isAsciiWhitespace(value[start])) start++;
|
|
27
|
-
while (end > start && isAsciiWhitespace(value[end - 1])) end--;
|
|
28
|
-
return value.slice(start, end);
|
|
29
|
-
}
|
|
30
|
-
var forbidden = (reason) => ({ kind: "forbidden", reason });
|
|
31
|
-
function parseMetaRefreshContent(content) {
|
|
32
|
-
const raw = trimAsciiWhitespace(stripControlChars(content));
|
|
33
|
-
if (raw === "") return forbidden("empty content");
|
|
34
|
-
let i = 0;
|
|
35
|
-
while (i < raw.length && raw[i] >= "0" && raw[i] <= "9") i++;
|
|
36
|
-
if (i === 0) return forbidden("missing or non-numeric delay");
|
|
37
|
-
const delay = Number.parseInt(raw.slice(0, i), 10);
|
|
38
|
-
if (!Number.isFinite(delay)) return forbidden("unparseable delay");
|
|
39
|
-
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
40
|
-
if (i === raw.length) return { kind: "delay-only", delay };
|
|
41
|
-
if (raw[i] !== ";" && raw[i] !== ",") return forbidden("unexpected text after delay");
|
|
42
|
-
i++;
|
|
43
|
-
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
44
|
-
const keyStart = i;
|
|
45
|
-
while (i < raw.length && !isAsciiWhitespace(raw[i]) && raw[i] !== "=") i++;
|
|
46
|
-
const key = raw.slice(keyStart, i).toLowerCase();
|
|
47
|
-
if (key !== "url") return forbidden(`unsupported refresh key ${JSON.stringify(key)}`);
|
|
48
|
-
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
49
|
-
if (raw[i] !== "=") return forbidden("missing '=' after url");
|
|
50
|
-
i++;
|
|
51
|
-
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
52
|
-
let destination;
|
|
53
|
-
const quote = raw[i];
|
|
54
|
-
if (quote === '"' || quote === "'") {
|
|
55
|
-
const close = raw.indexOf(quote, i + 1);
|
|
56
|
-
if (close === -1) return forbidden("unterminated quoted destination");
|
|
57
|
-
destination = raw.slice(i + 1, close);
|
|
58
|
-
const rest = trimAsciiWhitespace(raw.slice(close + 1));
|
|
59
|
-
if (rest !== "") return forbidden("trailing text after quoted destination");
|
|
60
|
-
} else {
|
|
61
|
-
destination = trimAsciiWhitespace(raw.slice(i));
|
|
62
|
-
if (/[;,]/.test(destination)) return forbidden("multiple refresh assignments");
|
|
63
|
-
}
|
|
64
|
-
if (destination === "") return forbidden("empty destination");
|
|
65
|
-
const sanitizedDestination = sanitizeUrl(destination);
|
|
66
|
-
if (sanitizedDestination === "") return forbidden("destination uses a disallowed protocol");
|
|
67
|
-
return { kind: "allowed", delay, destination, sanitizedDestination };
|
|
68
|
-
}
|
|
69
|
-
function resolveMetaRefreshPolicy(attributes) {
|
|
70
|
-
let httpEquiv;
|
|
71
|
-
let content;
|
|
72
|
-
let duplicateName;
|
|
73
|
-
const seen = /* @__PURE__ */ new Set();
|
|
74
|
-
for (const [key, value] of attributes) {
|
|
75
|
-
const canonical = canonicalAttrName(key);
|
|
76
|
-
if (seen.has(canonical)) duplicateName = canonical;
|
|
77
|
-
seen.add(canonical);
|
|
78
|
-
if (canonical === "http-equiv") httpEquiv = value;
|
|
79
|
-
else if (canonical === "content") content = value;
|
|
80
|
-
}
|
|
81
|
-
if (duplicateName !== void 0) {
|
|
82
|
-
return forbidden(`duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}`);
|
|
83
|
-
}
|
|
84
|
-
if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
|
|
85
|
-
if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
|
|
86
|
-
return { kind: "not-refresh" };
|
|
87
|
-
}
|
|
88
|
-
if (typeof content !== "string") return { kind: "not-refresh" };
|
|
89
|
-
return parseMetaRefreshContent(content);
|
|
90
|
-
}
|
|
91
|
-
function normalizeMetaAttributes(props) {
|
|
92
|
-
const out = /* @__PURE__ */ new Map();
|
|
93
|
-
const seen = /* @__PURE__ */ new Set();
|
|
94
|
-
for (const key of Object.keys(props)) {
|
|
95
|
-
const canonical = canonicalAttrName(key);
|
|
96
|
-
if (seen.has(canonical)) return null;
|
|
97
|
-
seen.add(canonical);
|
|
98
|
-
out.set(key, props[key]);
|
|
99
|
-
}
|
|
100
|
-
return out;
|
|
101
|
-
}
|
|
102
|
-
function isForbiddenMetaEntry(props) {
|
|
103
|
-
const normalized = normalizeMetaAttributes(props);
|
|
104
|
-
if (normalized === null) return true;
|
|
105
|
-
return resolveMetaRefreshPolicy(normalized).kind === "forbidden";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
26
|
// src/utils/headEntry.ts
|
|
109
27
|
var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
110
28
|
function isEmittableHeadAttrName(name) {
|
|
@@ -115,9 +33,7 @@ function isEmittableHeadAttrName(name) {
|
|
|
115
33
|
return true;
|
|
116
34
|
}
|
|
117
35
|
function sanitizeHeadAttrValue(canonical, value) {
|
|
118
|
-
|
|
119
|
-
const safe = sanitizeAttributeString(canonical, value);
|
|
120
|
-
return safe === "" ? null : safe;
|
|
36
|
+
return resolveAttributeValue(canonical, value);
|
|
121
37
|
}
|
|
122
38
|
function findDuplicateAttributeName(names) {
|
|
123
39
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -174,15 +90,9 @@ function serializeHeadEntry(tag, attrs) {
|
|
|
174
90
|
}
|
|
175
91
|
|
|
176
92
|
// src/platform/ssr.ts
|
|
177
|
-
function isPolicyAttr(lowerName) {
|
|
178
|
-
return isPolicyAttribute(lowerName);
|
|
179
|
-
}
|
|
180
93
|
function isForbiddenSsrAttr(lowerName) {
|
|
181
94
|
return isHtmlContentAttribute(lowerName);
|
|
182
95
|
}
|
|
183
|
-
function sanitizeSsrAttributeValue(lowerName, value) {
|
|
184
|
-
return sanitizeAttributeString(lowerName, value);
|
|
185
|
-
}
|
|
186
96
|
var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
187
97
|
function isSafeAttrName(name) {
|
|
188
98
|
return SAFE_ATTR_NAME.test(name);
|
|
@@ -213,6 +123,64 @@ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
|
213
123
|
"track",
|
|
214
124
|
"wbr"
|
|
215
125
|
]);
|
|
126
|
+
function findNativeGetter(proto, name) {
|
|
127
|
+
for (let p = proto; p; p = Object.getPrototypeOf(p)) {
|
|
128
|
+
const get = Object.getOwnPropertyDescriptor(p, name)?.get;
|
|
129
|
+
if (get) return get;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
var nativeAccessorsByRealm = /* @__PURE__ */ new WeakMap();
|
|
134
|
+
function nativeAccessors() {
|
|
135
|
+
const realm = Element;
|
|
136
|
+
let accessors = nativeAccessorsByRealm.get(realm);
|
|
137
|
+
if (!accessors) {
|
|
138
|
+
accessors = {
|
|
139
|
+
attributes: findNativeGetter(Element.prototype, "attributes"),
|
|
140
|
+
tagName: findNativeGetter(Element.prototype, "tagName"),
|
|
141
|
+
childNodes: findNativeGetter(Node.prototype, "childNodes"),
|
|
142
|
+
getAttribute: Element.prototype.getAttribute
|
|
143
|
+
};
|
|
144
|
+
nativeAccessorsByRealm.set(realm, accessors);
|
|
145
|
+
}
|
|
146
|
+
return accessors;
|
|
147
|
+
}
|
|
148
|
+
function attributesOf(el) {
|
|
149
|
+
const get = nativeAccessors().attributes;
|
|
150
|
+
return get ? get.call(el) : el.attributes;
|
|
151
|
+
}
|
|
152
|
+
function tagNameOf(el) {
|
|
153
|
+
const get = nativeAccessors().tagName;
|
|
154
|
+
return get ? get.call(el) : el.tagName;
|
|
155
|
+
}
|
|
156
|
+
function childNodesOf(node) {
|
|
157
|
+
const get = nativeAccessors().childNodes;
|
|
158
|
+
return get ? get.call(node) : node.childNodes;
|
|
159
|
+
}
|
|
160
|
+
function getAttributeOf(el, name) {
|
|
161
|
+
return nativeAccessors().getAttribute.call(el, name);
|
|
162
|
+
}
|
|
163
|
+
function serializeOpenTag(element, tag) {
|
|
164
|
+
let html = `<${tag}`;
|
|
165
|
+
const meta = tag === "meta" ? /* @__PURE__ */ new Map() : null;
|
|
166
|
+
const attrs = attributesOf(element);
|
|
167
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
168
|
+
const attr = attrs[i];
|
|
169
|
+
const rawName = attr.name;
|
|
170
|
+
if (!isSafeAttrName(rawName)) continue;
|
|
171
|
+
if (isEventHandlerAttr(rawName)) continue;
|
|
172
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
173
|
+
const value = resolveAttributeValue(rawName.toLowerCase(), attr.value);
|
|
174
|
+
if (value === null) continue;
|
|
175
|
+
meta?.set(rawName, value);
|
|
176
|
+
html += ` ${rawName}="${escapeAttr(value)}"`;
|
|
177
|
+
}
|
|
178
|
+
if (meta && isForbiddenMetaSnapshot(meta)) return null;
|
|
179
|
+
if (!getAttributeOf(element, "data-sibu-hydrate")) {
|
|
180
|
+
html += ` data-sibu-ssr="true"`;
|
|
181
|
+
}
|
|
182
|
+
return html;
|
|
183
|
+
}
|
|
216
184
|
function renderToString(element) {
|
|
217
185
|
if (element instanceof DocumentFragment) {
|
|
218
186
|
return Array.from(element.childNodes).map((child) => {
|
|
@@ -232,35 +200,21 @@ function renderToString(element) {
|
|
|
232
200
|
if (!(element instanceof HTMLElement)) {
|
|
233
201
|
return escapeHtml(element.textContent || "");
|
|
234
202
|
}
|
|
235
|
-
const tag = element.
|
|
236
|
-
if (tag
|
|
203
|
+
const tag = tagNameOf(element).toLowerCase();
|
|
204
|
+
if (isCodeTextElement(tag)) {
|
|
237
205
|
return DEV ? `<!--ssr:${tag}-stripped-->` : "";
|
|
238
206
|
}
|
|
239
207
|
if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
|
|
240
208
|
return DEV ? "<!--ssr:invalid-tag-->" : "";
|
|
241
209
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (!isSafeAttrName(rawName)) continue;
|
|
246
|
-
if (isEventHandlerAttr(rawName)) continue;
|
|
247
|
-
if (isForbiddenSsrAttr(rawName)) continue;
|
|
248
|
-
const lowerName = rawName.toLowerCase();
|
|
249
|
-
let value = attr.value;
|
|
250
|
-
if (isPolicyAttr(lowerName)) {
|
|
251
|
-
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
252
|
-
if (!value) continue;
|
|
253
|
-
}
|
|
254
|
-
html += ` ${rawName}="${escapeAttr(value)}"`;
|
|
255
|
-
}
|
|
256
|
-
if (element.dataset && !element.dataset.sibuHydrate) {
|
|
257
|
-
html += ` data-sibu-ssr="true"`;
|
|
258
|
-
}
|
|
210
|
+
const openTag = serializeOpenTag(element, tag);
|
|
211
|
+
if (openTag === null) return DEV ? `<!--ssr:${tag}-stripped-->` : "";
|
|
212
|
+
let html = openTag;
|
|
259
213
|
if (VOID_ELEMENTS.has(tag)) {
|
|
260
214
|
return `${html} />`;
|
|
261
215
|
}
|
|
262
216
|
html += ">";
|
|
263
|
-
for (const child of Array.from(element
|
|
217
|
+
for (const child of Array.from(childNodesOf(element))) {
|
|
264
218
|
try {
|
|
265
219
|
html += renderToString(child);
|
|
266
220
|
} catch (err) {
|
|
@@ -410,12 +364,8 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
410
364
|
if (!isSafeAttrName(rawKey)) continue;
|
|
411
365
|
if (!allowEventHandlers && isEventHandlerAttr(rawKey)) continue;
|
|
412
366
|
if (isForbiddenSsrAttr(rawKey)) continue;
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
if (isPolicyAttr(lowerKey)) {
|
|
416
|
-
value = sanitizeSsrAttributeValue(lowerKey, value);
|
|
417
|
-
if (!value) continue;
|
|
418
|
-
}
|
|
367
|
+
const value = resolveAttributeValue(rawKey.toLowerCase(), String(attrs[rawKey]));
|
|
368
|
+
if (value === null) continue;
|
|
419
369
|
out.push(`${rawKey}="${escapeAttr(value)}"`);
|
|
420
370
|
}
|
|
421
371
|
return out.join(" ");
|
|
@@ -478,8 +428,8 @@ async function* renderToStream(element) {
|
|
|
478
428
|
yield escapeHtml(element.textContent || "");
|
|
479
429
|
return;
|
|
480
430
|
}
|
|
481
|
-
const tag = element.
|
|
482
|
-
if (tag
|
|
431
|
+
const tag = tagNameOf(element).toLowerCase();
|
|
432
|
+
if (isCodeTextElement(tag)) {
|
|
483
433
|
if (DEV) yield `<!--ssr:${tag}-stripped-->`;
|
|
484
434
|
return;
|
|
485
435
|
}
|
|
@@ -487,29 +437,17 @@ async function* renderToStream(element) {
|
|
|
487
437
|
if (DEV) yield "<!--ssr:invalid-tag-->";
|
|
488
438
|
return;
|
|
489
439
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (isEventHandlerAttr(rawName)) continue;
|
|
495
|
-
if (isForbiddenSsrAttr(rawName)) continue;
|
|
496
|
-
const lowerName = rawName.toLowerCase();
|
|
497
|
-
let value = attr.value;
|
|
498
|
-
if (isPolicyAttr(lowerName)) {
|
|
499
|
-
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
500
|
-
if (!value) continue;
|
|
501
|
-
}
|
|
502
|
-
openTag += ` ${rawName}="${escapeAttr(value)}"`;
|
|
503
|
-
}
|
|
504
|
-
if (element.dataset && !element.dataset.sibuHydrate) {
|
|
505
|
-
openTag += ` data-sibu-ssr="true"`;
|
|
440
|
+
const openTag = serializeOpenTag(element, tag);
|
|
441
|
+
if (openTag === null) {
|
|
442
|
+
if (DEV) yield `<!--ssr:${tag}-stripped-->`;
|
|
443
|
+
return;
|
|
506
444
|
}
|
|
507
445
|
if (VOID_ELEMENTS.has(tag)) {
|
|
508
446
|
yield `${openTag} />`;
|
|
509
447
|
return;
|
|
510
448
|
}
|
|
511
449
|
yield `${openTag}>`;
|
|
512
|
-
for (const child of Array.from(element
|
|
450
|
+
for (const child of Array.from(childNodesOf(element))) {
|
|
513
451
|
try {
|
|
514
452
|
yield* renderToStream(child);
|
|
515
453
|
} catch (err) {
|
|
@@ -694,8 +632,7 @@ function deserializeState(validate) {
|
|
|
694
632
|
"[SibuJS SSR] deserializeState() called without a validate guard \u2014 tampered SSR payloads will not be detected."
|
|
695
633
|
);
|
|
696
634
|
}
|
|
697
|
-
const
|
|
698
|
-
const raw = w[SSR_DATA_ATTR];
|
|
635
|
+
const raw = readOwnGlobal(SSR_DATA_ATTR);
|
|
699
636
|
if (raw === void 0) return void 0;
|
|
700
637
|
if (validate && !validate(raw)) return void 0;
|
|
701
638
|
return raw;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
watch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CZPRSWSS.js";
|
|
4
4
|
import {
|
|
5
5
|
createId,
|
|
6
6
|
domBinding,
|
|
7
7
|
idSegment
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-C7AA2CZX.js";
|
|
9
9
|
import {
|
|
10
10
|
derived
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KJ3UKABJ.js";
|
|
12
12
|
import {
|
|
13
13
|
stripHtml
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EXXSN3BW.js";
|
|
15
15
|
import {
|
|
16
16
|
batch,
|
|
17
17
|
signal
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-DSID757X.js";
|
|
19
19
|
|
|
20
20
|
// src/widgets/Combobox.ts
|
|
21
21
|
var boundComboboxes = /* @__PURE__ */ new WeakMap();
|