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.
Files changed (81) hide show
  1. package/dist/browser.cjs +45 -4
  2. package/dist/browser.d.cts +23 -0
  3. package/dist/browser.d.ts +23 -0
  4. package/dist/browser.js +6 -5
  5. package/dist/build.cjs +513 -78
  6. package/dist/build.d.cts +22 -8
  7. package/dist/build.d.ts +22 -8
  8. package/dist/build.js +68 -21
  9. package/dist/cdn.dev.global.js +15 -13
  10. package/dist/cdn.full.dev.global.js +15 -13
  11. package/dist/cdn.full.global.js +14 -12
  12. package/dist/cdn.global.js +14 -12
  13. package/dist/chunk-3ACAAQIN.js +224 -0
  14. package/dist/{chunk-DKXRACVN.js → chunk-44MTL7ED.js} +6 -6
  15. package/dist/{chunk-3QWBSL5R.js → chunk-4WMSTZ7J.js} +8 -5
  16. package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
  17. package/dist/{chunk-BSY63EM6.js → chunk-C5PEWEGH.js} +20 -5
  18. package/dist/{chunk-BC2SECJD.js → chunk-C7AA2CZX.js} +1 -1
  19. package/dist/{chunk-SLM3IA34.js → chunk-CZPRSWSS.js} +1 -1
  20. package/dist/{chunk-FQRUXCEE.js → chunk-DSID757X.js} +1 -1
  21. package/dist/{chunk-ZVL7TY4K.js → chunk-EGUQLBHE.js} +22 -8
  22. package/dist/{chunk-2INLLLMZ.js → chunk-EXXSN3BW.js} +202 -26
  23. package/dist/{chunk-HURREPU2.js → chunk-FC6KCJSA.js} +87 -150
  24. package/dist/{chunk-Z3OHK6QT.js → chunk-GSGNX35Q.js} +5 -5
  25. package/dist/{chunk-XYV3EDB7.js → chunk-J3YQ457H.js} +37 -20
  26. package/dist/{chunk-VZSG24LS.js → chunk-JUTFX2RM.js} +18 -10
  27. package/dist/{chunk-RUSSKG6G.js → chunk-KJ3UKABJ.js} +1 -1
  28. package/dist/{chunk-UNWRJRKC.js → chunk-LI4W3U6C.js} +1 -1
  29. package/dist/{chunk-FNJXNGYZ.js → chunk-O6TRVMJI.js} +58 -6
  30. package/dist/{chunk-7LUJQAOJ.js → chunk-Q5J2LVKH.js} +1 -1
  31. package/dist/{chunk-4AWA2PVD.js → chunk-QGMSESDD.js} +10 -5
  32. package/dist/{chunk-XZR4PXRE.js → chunk-RGLPEBDE.js} +13 -10
  33. package/dist/{chunk-JWKYU5GV.js → chunk-TQQUN4WR.js} +1 -1
  34. package/dist/{chunk-WOLJZUFQ.js → chunk-UCNP3HVM.js} +12 -8
  35. package/dist/{chunk-WEQ3DMVL.js → chunk-V3LHZSU5.js} +1 -1
  36. package/dist/{chunk-UOL2ECCS.js → chunk-VWT3VXPL.js} +18 -12
  37. package/dist/{chunk-2BPG2XDA.js → chunk-ZVLHNEEL.js} +17 -35
  38. package/dist/{customElement-MmInOW1U.d.ts → customElement-pYDKUzWl.d.cts} +11 -3
  39. package/dist/{customElement-MmInOW1U.d.cts → customElement-pYDKUzWl.d.ts} +11 -3
  40. package/dist/data.cjs +9 -2
  41. package/dist/data.js +6 -6
  42. package/dist/devtools.cjs +1 -1
  43. package/dist/devtools.js +5 -5
  44. package/dist/ecosystem.cjs +400 -37
  45. package/dist/ecosystem.js +9 -8
  46. package/dist/extras.cjs +621 -252
  47. package/dist/extras.d.cts +2 -2
  48. package/dist/extras.d.ts +2 -2
  49. package/dist/extras.js +24 -23
  50. package/dist/index.cjs +464 -70
  51. package/dist/index.d.cts +28 -2
  52. package/dist/index.d.ts +28 -2
  53. package/dist/index.js +16 -12
  54. package/dist/motion.cjs +1 -1
  55. package/dist/motion.js +3 -3
  56. package/dist/patterns.cjs +10 -7
  57. package/dist/patterns.js +6 -6
  58. package/dist/performance.cjs +31 -2
  59. package/dist/performance.d.cts +12 -2
  60. package/dist/performance.d.ts +12 -2
  61. package/dist/performance.js +8 -6
  62. package/dist/plugins.cjs +558 -231
  63. package/dist/plugins.d.cts +8 -3
  64. package/dist/plugins.d.ts +8 -3
  65. package/dist/plugins.js +22 -28
  66. package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
  67. package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.cts} +6 -0
  68. package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.ts} +6 -0
  69. package/dist/ssr.cjs +457 -131
  70. package/dist/ssr.d.cts +55 -5
  71. package/dist/ssr.d.ts +55 -5
  72. package/dist/ssr.js +11 -9
  73. package/dist/testing.cjs +3 -3
  74. package/dist/testing.js +4 -4
  75. package/dist/ui.cjs +608 -240
  76. package/dist/ui.d.cts +1 -1
  77. package/dist/ui.d.ts +1 -1
  78. package/dist/ui.js +9 -8
  79. package/dist/widgets.cjs +1 -1
  80. package/dist/widgets.js +7 -7
  81. 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(/[A-Z]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32));
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 = /^\s+\d+(\.\d+)?[wx]$/;
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 parts = value.split(",");
49
+ const input = String(value);
42
50
  const out = [];
43
- for (let i = 0; i < parts.length; i++) {
44
- const part = parts[i].trim();
45
- if (!part) continue;
46
- const m = part.match(/^(\S+)(\s+.+)?$/);
47
- if (!m) continue;
48
- if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
49
- const safe = sanitizeUrl(m[1]);
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
- out.push(m[2] ? `${safe}${m[2]}` : safe);
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 normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
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
- function sanitizeCSSValue(value, context) {
160
- const reason = blockedCssReason(value);
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
- let reason = blockedCssReason(value);
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
- sanitizeCSSValue,
426
+ sanitizeStylesheetText,
427
+ sanitizeCSSDeclaration,
251
428
  sanitizeStyleAttribute,
252
429
  stripHtml,
253
430
  isHtmlContentAttribute,
254
431
  isUrlAttribute,
255
- isPolicyAttribute,
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
- isPolicyAttribute,
6
- sanitizeAttributeString,
7
- sanitizeUrl,
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
- if (!isPolicyAttribute(canonical)) return value;
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.tagName.toLowerCase();
236
- if (tag === "script" || tag === "style") {
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
- let html = `<${tag}`;
243
- for (const attr of Array.from(element.attributes)) {
244
- const rawName = attr.name;
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.childNodes)) {
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 lowerKey = rawKey.toLowerCase();
414
- let value = String(attrs[rawKey]);
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.tagName.toLowerCase();
482
- if (tag === "script" || tag === "style") {
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
- let openTag = `<${tag}`;
491
- for (const attr of Array.from(element.attributes)) {
492
- const rawName = attr.name;
493
- if (!isSafeAttrName(rawName)) continue;
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.childNodes)) {
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 w = window;
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-SLM3IA34.js";
3
+ } from "./chunk-CZPRSWSS.js";
4
4
  import {
5
5
  createId,
6
6
  domBinding,
7
7
  idSegment
8
- } from "./chunk-BC2SECJD.js";
8
+ } from "./chunk-C7AA2CZX.js";
9
9
  import {
10
10
  derived
11
- } from "./chunk-RUSSKG6G.js";
11
+ } from "./chunk-KJ3UKABJ.js";
12
12
  import {
13
13
  stripHtml
14
- } from "./chunk-2INLLLMZ.js";
14
+ } from "./chunk-EXXSN3BW.js";
15
15
  import {
16
16
  batch,
17
17
  signal
18
- } from "./chunk-FQRUXCEE.js";
18
+ } from "./chunk-DSID757X.js";
19
19
 
20
20
  // src/widgets/Combobox.ts
21
21
  var boundComboboxes = /* @__PURE__ */ new WeakMap();