sibujs 3.2.1 → 3.3.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 (60) hide show
  1. package/dist/browser.cjs +7 -1
  2. package/dist/browser.js +5 -5
  3. package/dist/build.cjs +49 -22
  4. package/dist/build.js +14 -14
  5. package/dist/cdn.global.js +7 -7
  6. package/dist/{chunk-5N74TKLD.js → chunk-24DBWDTK.js} +1 -1
  7. package/dist/{chunk-2C4E3HBM.js → chunk-4WXWJ4SW.js} +5 -5
  8. package/dist/{chunk-C427DVQF.js → chunk-5K72I3UQ.js} +2 -2
  9. package/dist/{chunk-2UPRY23K.js → chunk-5VGSK6D2.js} +1 -1
  10. package/dist/{chunk-NPIEEKPT.js → chunk-6G6UNHZI.js} +6 -6
  11. package/dist/{chunk-FDY42FIU.js → chunk-7JHWAGRQ.js} +2 -2
  12. package/dist/{chunk-V2MTG5FT.js → chunk-BEIKESVL.js} +33 -16
  13. package/dist/{chunk-LMLD24FC.js → chunk-COY6PUD2.js} +7 -1
  14. package/dist/{chunk-JYXOEYI4.js → chunk-CVMMULHO.js} +2 -2
  15. package/dist/{chunk-H6PCHJZQ.js → chunk-F4UM7QBJ.js} +11 -3
  16. package/dist/{chunk-BGNLPNGV.js → chunk-IHBVTURX.js} +4 -4
  17. package/dist/{chunk-YFDGQWDA.js → chunk-JBXNCZSC.js} +1 -1
  18. package/dist/{chunk-HMJFCBRR.js → chunk-L3GAGWCC.js} +10 -1
  19. package/dist/{chunk-GOUM4JCT.js → chunk-M6WSIGYW.js} +2 -2
  20. package/dist/{chunk-VOCE4NNK.js → chunk-MWZFOIBG.js} +5 -5
  21. package/dist/{chunk-X67UYC74.js → chunk-NUWKIEHE.js} +14 -24
  22. package/dist/{chunk-RLUJL2MV.js → chunk-Q2ERM6NT.js} +6 -7
  23. package/dist/{chunk-HXMS4SNP.js → chunk-R3QEDXFS.js} +2 -2
  24. package/dist/{chunk-NFYWLRUO.js → chunk-S4FHR5ZZ.js} +3 -3
  25. package/dist/{chunk-7XDYVJLE.js → chunk-SLCUP2EK.js} +6 -6
  26. package/dist/{chunk-OYLPZO4N.js → chunk-USDR2GFV.js} +4 -4
  27. package/dist/{chunk-4JCAUOLN.js → chunk-WVJJUFPC.js} +22 -12
  28. package/dist/{chunk-Z2FWAE4B.js → chunk-X3NHE2DK.js} +3 -1
  29. package/dist/{chunk-FOI23UJL.js → chunk-XQ7XSGYP.js} +1 -1
  30. package/dist/{chunk-RDRSWYNP.js → chunk-ZUVLC7TM.js} +1 -1
  31. package/dist/data.cjs +7 -1
  32. package/dist/data.js +8 -8
  33. package/dist/devtools.cjs +7 -1
  34. package/dist/devtools.js +7 -7
  35. package/dist/ecosystem.cjs +22 -19
  36. package/dist/ecosystem.js +10 -10
  37. package/dist/extras.cjs +62 -28
  38. package/dist/extras.js +24 -24
  39. package/dist/index.cjs +49 -22
  40. package/dist/index.d.cts +13 -0
  41. package/dist/index.d.ts +13 -0
  42. package/dist/index.js +14 -14
  43. package/dist/motion.cjs +7 -1
  44. package/dist/motion.js +4 -4
  45. package/dist/patterns.cjs +7 -1
  46. package/dist/patterns.js +6 -6
  47. package/dist/performance.cjs +47 -9
  48. package/dist/performance.js +8 -8
  49. package/dist/plugins.cjs +24 -18
  50. package/dist/plugins.js +12 -12
  51. package/dist/{ssr-2QDQ27EV.js → ssr-6D67RAVB.js} +3 -3
  52. package/dist/ssr.cjs +22 -19
  53. package/dist/ssr.js +11 -11
  54. package/dist/testing.cjs +19 -7
  55. package/dist/testing.js +16 -10
  56. package/dist/ui.cjs +27 -4
  57. package/dist/ui.js +9 -9
  58. package/dist/widgets.cjs +32 -2
  59. package/dist/widgets.js +8 -8
  60. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -234,7 +234,13 @@ module.exports = __toCommonJS(index_exports);
234
234
 
235
235
  // src/core/dev.ts
236
236
  function isDev() {
237
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
237
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
238
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
239
+ // production builds; under the test runner it is always undefined, so
240
+ // this branch is unreachable here.
241
+ /* v8 ignore next 2 */
242
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
243
+ );
238
244
  }
239
245
  var _isDev = isDev();
240
246
  function devAssert(condition, message) {
@@ -291,7 +297,9 @@ function sanitizeSrcset(value) {
291
297
  }
292
298
  return out.join(", ");
293
299
  }
300
+ var CSS_DANGER_GATE = /[(:@\\]/;
294
301
  function sanitizeCSSValue(value) {
302
+ if (!CSS_DANGER_GATE.test(value)) return value;
295
303
  const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
296
304
  const code2 = Number.parseInt(hex, 16);
297
305
  if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
@@ -324,6 +332,12 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
324
332
  function isUrlAttribute(attr) {
325
333
  return URL_ATTRIBUTES.has(attr.toLowerCase());
326
334
  }
335
+ function sanitizeAttributeString(attr, value) {
336
+ const lower = attr.toLowerCase();
337
+ if (lower === "srcset") return sanitizeSrcset(value);
338
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
339
+ return value;
340
+ }
327
341
 
328
342
  // src/reactivity/track.ts
329
343
  var _isDev2 = isDev();
@@ -747,7 +761,7 @@ function bindAttribute(el, attr, getter) {
747
761
  if ((attr === "value" || attr === "checked") && attr in el) {
748
762
  setProp(el, attr, attr === "checked" ? Boolean(value) : str);
749
763
  } else {
750
- el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
764
+ el.setAttribute(attr, sanitizeAttributeString(attr, str));
751
765
  }
752
766
  }
753
767
  return reactiveBinding(commit);
@@ -777,7 +791,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
777
791
  if ((name === "value" || name === "checked") && name in el) {
778
792
  setProp(el, name, name === "checked" ? Boolean(value) : str);
779
793
  } else {
780
- el.setAttribute(name, isUrlAttribute(name) ? sanitizeUrl(str) : str);
794
+ el.setAttribute(name, sanitizeAttributeString(name, str));
781
795
  }
782
796
  prevName = name;
783
797
  }
@@ -937,11 +951,8 @@ function bindChildNode(placeholder, getter) {
937
951
  var SVG_NS = "http://www.w3.org/2000/svg";
938
952
  var _isDev6 = isDev();
939
953
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
940
- function validateTagName(tag) {
941
- const lower = tag.toLowerCase();
942
- if (BLOCKED_TAGS.has(lower)) {
943
- throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
944
- }
954
+ function isBlockedTag(tag) {
955
+ return BLOCKED_TAGS.has(tag.toLowerCase());
945
956
  }
946
957
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
947
958
  "config",
@@ -1096,8 +1107,11 @@ function appendChildren(el, nodes) {
1096
1107
  }
1097
1108
  }
1098
1109
  var tagFactory = (tag, ns) => {
1110
+ const blocked = isBlockedTag(tag);
1099
1111
  return (first, second) => {
1100
- validateTagName(tag);
1112
+ if (blocked) {
1113
+ throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1114
+ }
1101
1115
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
1102
1116
  if (first === void 0) return el;
1103
1117
  if (typeof first === "string") {
@@ -1167,7 +1181,6 @@ var tagFactory = (tag, ns) => {
1167
1181
  default: {
1168
1182
  const value = props[key];
1169
1183
  if (value == null) continue;
1170
- const lkey = key.toLowerCase();
1171
1184
  if (isEventHandlerAttr(key)) continue;
1172
1185
  if (typeof value === "function") {
1173
1186
  registerDisposer(el, bindAttribute(el, key, value));
@@ -1180,14 +1193,7 @@ var tagFactory = (tag, ns) => {
1180
1193
  el.removeAttribute(key);
1181
1194
  }
1182
1195
  } else {
1183
- const str = String(value);
1184
- if (lkey === "srcset") {
1185
- el.setAttribute(key, sanitizeSrcset(str));
1186
- } else if (isUrlAttribute(lkey)) {
1187
- el.setAttribute(key, sanitizeUrl(str));
1188
- } else {
1189
- el.setAttribute(key, str);
1190
- }
1196
+ el.setAttribute(key, sanitizeAttributeString(key, String(value)));
1191
1197
  }
1192
1198
  }
1193
1199
  }
@@ -2777,8 +2783,14 @@ function watch(getter, callback) {
2777
2783
  return;
2778
2784
  }
2779
2785
  if (!Object.is(newValue, oldValue)) {
2780
- callback(newValue, oldValue);
2786
+ const prev = oldValue;
2781
2787
  oldValue = newValue;
2788
+ suspendTracking();
2789
+ try {
2790
+ callback(newValue, prev);
2791
+ } finally {
2792
+ resumeTracking();
2793
+ }
2782
2794
  }
2783
2795
  };
2784
2796
  const teardown = track(subscriber);
@@ -2844,7 +2856,12 @@ function store(initialState) {
2844
2856
  first = false;
2845
2857
  return;
2846
2858
  }
2847
- callback(snapshot);
2859
+ suspendTracking();
2860
+ try {
2861
+ callback(snapshot);
2862
+ } finally {
2863
+ resumeTracking();
2864
+ }
2848
2865
  });
2849
2866
  };
2850
2867
  const subscribeKey = (key, callback) => {
@@ -2860,7 +2877,12 @@ function store(initialState) {
2860
2877
  if (!Object.is(current, prev)) {
2861
2878
  const oldPrev = prev;
2862
2879
  prev = current;
2863
- callback(current, oldPrev);
2880
+ suspendTracking();
2881
+ try {
2882
+ callback(current, oldPrev);
2883
+ } finally {
2884
+ resumeTracking();
2885
+ }
2864
2886
  }
2865
2887
  });
2866
2888
  };
@@ -4410,7 +4432,12 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
4410
4432
  } else if (collected.length > 1) {
4411
4433
  const Agg = globalThis.AggregateError;
4412
4434
  handleError(
4413
- Agg ? new Agg(collected, `${collected.length} pre-mount errors caught by ErrorBoundary`) : new Error(collected.map((e) => e.message).join("; "))
4435
+ Agg ? new Agg(collected, `${collected.length} pre-mount errors caught by ErrorBoundary`) : (
4436
+ // AggregateError is ES2021 and present in every supported runtime
4437
+ // (matches the browserslist targets), so this fallback is defensive.
4438
+ /* v8 ignore next */
4439
+ new Error(collected.map((e) => e.message).join("; "))
4440
+ )
4414
4441
  );
4415
4442
  }
4416
4443
  return void 0;
package/dist/index.d.cts CHANGED
@@ -442,6 +442,15 @@ declare function show<T extends Element>(condition: () => boolean, element: T):
442
442
  * () => div("Please log in")
443
443
  * );
444
444
  * ```
445
+ *
446
+ * GOTCHA — branch factories rebuild only when `condition` changes. A signal
447
+ * read *eagerly* inside a branch is captured once and never updates:
448
+ * ```ts
449
+ * when(() => show(), () => div(`Count: ${count()}`)); // ✗ frozen at first count
450
+ * when(() => show(), () => div(() => `Count: ${count()}`)); // ✓ reactive text child
451
+ * ```
452
+ * Drive per-branch reactivity with a nested getter (or a reactive child), not a
453
+ * bare read in the factory body.
445
454
  */
446
455
  declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBranch?: () => NodeChild): Comment;
447
456
  /**
@@ -465,6 +474,10 @@ declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBr
465
474
  * () => div("Unknown status")
466
475
  * );
467
476
  * ```
477
+ *
478
+ * GOTCHA — like `when()`, a case factory rebuilds only when the matched key
479
+ * changes. A signal read eagerly inside a case is frozen at build time; use a
480
+ * nested getter (`() => div(() => label())`) for reactive per-case content.
468
481
  */
469
482
  declare function match<T extends string | number>(value: () => T, cases: Record<string, () => NodeChild>, fallback?: () => NodeChild): Comment;
470
483
 
package/dist/index.d.ts CHANGED
@@ -442,6 +442,15 @@ declare function show<T extends Element>(condition: () => boolean, element: T):
442
442
  * () => div("Please log in")
443
443
  * );
444
444
  * ```
445
+ *
446
+ * GOTCHA — branch factories rebuild only when `condition` changes. A signal
447
+ * read *eagerly* inside a branch is captured once and never updates:
448
+ * ```ts
449
+ * when(() => show(), () => div(`Count: ${count()}`)); // ✗ frozen at first count
450
+ * when(() => show(), () => div(() => `Count: ${count()}`)); // ✓ reactive text child
451
+ * ```
452
+ * Drive per-branch reactivity with a nested getter (or a reactive child), not a
453
+ * bare read in the factory body.
445
454
  */
446
455
  declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBranch?: () => NodeChild): Comment;
447
456
  /**
@@ -465,6 +474,10 @@ declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBr
465
474
  * () => div("Unknown status")
466
475
  * );
467
476
  * ```
477
+ *
478
+ * GOTCHA — like `when()`, a case factory rebuilds only when the matched key
479
+ * changes. A signal read eagerly inside a case is frozen at build time; use a
480
+ * nested getter (`() => div(() => label())`) for reactive per-case content.
468
481
  */
469
482
  declare function match<T extends string | number>(value: () => T, cases: Record<string, () => NodeChild>, fallback?: () => NodeChild): Comment;
470
483
 
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ import {
44
44
  unregisterComponent,
45
45
  when,
46
46
  writable
47
- } from "./chunk-V2MTG5FT.js";
47
+ } from "./chunk-BEIKESVL.js";
48
48
  import {
49
49
  __resetIdCounter,
50
50
  createId
@@ -186,36 +186,36 @@ import {
186
186
  use,
187
187
  var_,
188
188
  video
189
- } from "./chunk-5N74TKLD.js";
189
+ } from "./chunk-24DBWDTK.js";
190
190
  import {
191
191
  watch
192
- } from "./chunk-H6PCHJZQ.js";
192
+ } from "./chunk-F4UM7QBJ.js";
193
193
  import {
194
194
  trustHTML
195
- } from "./chunk-HXMS4SNP.js";
195
+ } from "./chunk-R3QEDXFS.js";
196
196
  import {
197
197
  context
198
- } from "./chunk-YFDGQWDA.js";
198
+ } from "./chunk-JBXNCZSC.js";
199
199
  import {
200
200
  SVG_NS,
201
201
  tagFactory
202
- } from "./chunk-X67UYC74.js";
202
+ } from "./chunk-NUWKIEHE.js";
203
203
  import {
204
204
  bindDynamic
205
- } from "./chunk-RLUJL2MV.js";
205
+ } from "./chunk-Q2ERM6NT.js";
206
206
  import {
207
207
  derived
208
- } from "./chunk-JYXOEYI4.js";
208
+ } from "./chunk-CVMMULHO.js";
209
209
  import {
210
210
  checkLeaks,
211
211
  dispose,
212
212
  registerDisposer
213
- } from "./chunk-2UPRY23K.js";
214
- import "./chunk-HMJFCBRR.js";
213
+ } from "./chunk-5VGSK6D2.js";
214
+ import "./chunk-L3GAGWCC.js";
215
215
  import {
216
216
  effect,
217
217
  on
218
- } from "./chunk-FDY42FIU.js";
218
+ } from "./chunk-7JHWAGRQ.js";
219
219
  import {
220
220
  disableSSR,
221
221
  enableSSR,
@@ -230,13 +230,13 @@ import {
230
230
  enqueueBatchedSignal,
231
231
  isBatching,
232
232
  signal
233
- } from "./chunk-C427DVQF.js";
233
+ } from "./chunk-5K72I3UQ.js";
234
234
  import {
235
235
  retrack,
236
236
  setMaxDrainIterations,
237
237
  untracked
238
- } from "./chunk-Z2FWAE4B.js";
239
- import "./chunk-LMLD24FC.js";
238
+ } from "./chunk-X3NHE2DK.js";
239
+ import "./chunk-COY6PUD2.js";
240
240
  export {
241
241
  DynamicComponent,
242
242
  ErrorBoundary,
package/dist/motion.cjs CHANGED
@@ -288,7 +288,13 @@ async function sequence(steps) {
288
288
 
289
289
  // src/core/dev.ts
290
290
  function isDev() {
291
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
291
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
292
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
293
+ // production builds; under the test runner it is always undefined, so
294
+ // this branch is unreachable here.
295
+ /* v8 ignore next 2 */
296
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
297
+ );
292
298
  }
293
299
  var _isDev = isDev();
294
300
  function devWarn(message) {
package/dist/motion.js CHANGED
@@ -19,10 +19,10 @@ import {
19
19
  stagger,
20
20
  transition,
21
21
  viewTransition
22
- } from "./chunk-FOI23UJL.js";
23
- import "./chunk-C427DVQF.js";
24
- import "./chunk-Z2FWAE4B.js";
25
- import "./chunk-LMLD24FC.js";
22
+ } from "./chunk-XQ7XSGYP.js";
23
+ import "./chunk-5K72I3UQ.js";
24
+ import "./chunk-X3NHE2DK.js";
25
+ import "./chunk-COY6PUD2.js";
26
26
  export {
27
27
  TransitionGroup,
28
28
  animate,
package/dist/patterns.cjs CHANGED
@@ -45,7 +45,13 @@ module.exports = __toCommonJS(patterns_exports);
45
45
 
46
46
  // src/core/dev.ts
47
47
  function isDev() {
48
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
48
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
49
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
50
+ // production builds; under the test runner it is always undefined, so
51
+ // this branch is unreachable here.
52
+ /* v8 ignore next 2 */
53
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
54
+ );
49
55
  }
50
56
  var _isDev = isDev();
51
57
  function devAssert(condition, message) {
package/dist/patterns.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  optimisticList,
6
6
  persisted,
7
7
  timeline
8
- } from "./chunk-NFYWLRUO.js";
8
+ } from "./chunk-S4FHR5ZZ.js";
9
9
  import {
10
10
  RenderProp,
11
11
  assertType,
@@ -23,12 +23,12 @@ import {
23
23
  withWrapper
24
24
  } from "./chunk-VJE6DDYM.js";
25
25
  import "./chunk-H3SRKIYX.js";
26
- import "./chunk-JYXOEYI4.js";
27
- import "./chunk-FDY42FIU.js";
26
+ import "./chunk-CVMMULHO.js";
27
+ import "./chunk-7JHWAGRQ.js";
28
28
  import "./chunk-GOJMFRBL.js";
29
- import "./chunk-C427DVQF.js";
30
- import "./chunk-Z2FWAE4B.js";
31
- import "./chunk-LMLD24FC.js";
29
+ import "./chunk-5K72I3UQ.js";
30
+ import "./chunk-X3NHE2DK.js";
31
+ import "./chunk-COY6PUD2.js";
32
32
  export {
33
33
  RenderProp,
34
34
  assertType,
@@ -218,7 +218,13 @@ async function processInChunks(items, processor, chunkSize = 50) {
218
218
 
219
219
  // src/core/dev.ts
220
220
  function isDev() {
221
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
221
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
222
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
223
+ // production builds; under the test runner it is always undefined, so
224
+ // this branch is unreachable here.
225
+ /* v8 ignore next 2 */
226
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
227
+ );
222
228
  }
223
229
  var _isDev = isDev();
224
230
  function devAssert(condition, message) {
@@ -778,6 +784,31 @@ function uniqueId(suffix) {
778
784
  return suffix ? `${id}-${suffix}` : id;
779
785
  }
780
786
 
787
+ // src/utils/sanitize.ts
788
+ function stripControlChars(value) {
789
+ return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
790
+ }
791
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
792
+ function sanitizeUrl(url) {
793
+ const trimmed = stripControlChars(url).trim();
794
+ if (!trimmed) return "";
795
+ const lower = trimmed.toLowerCase();
796
+ let schemeEnd = -1;
797
+ for (let i = 0; i < lower.length; i++) {
798
+ const ch = lower.charCodeAt(i);
799
+ if (ch === 58) {
800
+ schemeEnd = i;
801
+ break;
802
+ }
803
+ if (ch === 47 || ch === 63 || ch === 35) break;
804
+ }
805
+ if (schemeEnd === -1) return trimmed;
806
+ const scheme = lower.slice(0, schemeEnd + 1);
807
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
808
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
809
+ return trimmed;
810
+ }
811
+
781
812
  // src/performance/domRecycler.ts
782
813
  var _isDev4 = isDev();
783
814
  var DOMPool = class {
@@ -853,11 +884,13 @@ function getDOMPool() {
853
884
  }
854
885
  var preloadedResources = /* @__PURE__ */ new Set();
855
886
  function preloadResource(url, type = "fetch") {
856
- if (preloadedResources.has(url)) return;
857
- preloadedResources.add(url);
887
+ const safe = sanitizeUrl(url);
888
+ if (!safe) return;
889
+ if (preloadedResources.has(safe)) return;
890
+ preloadedResources.add(safe);
858
891
  const link = document.createElement("link");
859
892
  link.rel = "preload";
860
- link.href = url;
893
+ link.href = safe;
861
894
  switch (type) {
862
895
  case "script":
863
896
  link.setAttribute("as", "script");
@@ -875,11 +908,13 @@ function preloadResource(url, type = "fetch") {
875
908
  document.head.appendChild(link);
876
909
  }
877
910
  function prefetch(url) {
878
- if (preloadedResources.has(url)) return;
879
- preloadedResources.add(url);
911
+ const safe = sanitizeUrl(url);
912
+ if (!safe) return;
913
+ if (preloadedResources.has(safe)) return;
914
+ preloadedResources.add(safe);
880
915
  const link = document.createElement("link");
881
916
  link.rel = "prefetch";
882
- link.href = url;
917
+ link.href = safe;
883
918
  document.head.appendChild(link);
884
919
  }
885
920
  function preloadImage(src) {
@@ -1307,11 +1342,14 @@ function lazyChunk(id, loader, registry, fallback) {
1307
1342
  }
1308
1343
  function preloadModule(url) {
1309
1344
  if (typeof document === "undefined") return;
1310
- const existing = document.querySelector(`link[href="${url}"][rel="modulepreload"]`);
1345
+ const safe = sanitizeUrl(url);
1346
+ if (!safe) return;
1347
+ const safeHref = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(safe) : safe.replace(/["\\]/g, "\\$&");
1348
+ const existing = document.querySelector(`link[rel="modulepreload"][href="${safeHref}"]`);
1311
1349
  if (existing) return;
1312
1350
  const link = document.createElement("link");
1313
1351
  link.rel = "modulepreload";
1314
- link.href = url;
1352
+ link.href = safe;
1315
1353
  document.head.appendChild(link);
1316
1354
  }
1317
1355
  function preloadModules(urls) {
@@ -33,17 +33,17 @@ import {
33
33
  transitionState,
34
34
  uniqueId,
35
35
  yieldToMain
36
- } from "./chunk-4JCAUOLN.js";
36
+ } from "./chunk-WVJJUFPC.js";
37
37
  import {
38
38
  trustHTML
39
- } from "./chunk-HXMS4SNP.js";
40
- import "./chunk-2UPRY23K.js";
41
- import "./chunk-HMJFCBRR.js";
42
- import "./chunk-FDY42FIU.js";
39
+ } from "./chunk-R3QEDXFS.js";
40
+ import "./chunk-5VGSK6D2.js";
41
+ import "./chunk-L3GAGWCC.js";
42
+ import "./chunk-7JHWAGRQ.js";
43
43
  import "./chunk-GOJMFRBL.js";
44
- import "./chunk-C427DVQF.js";
45
- import "./chunk-Z2FWAE4B.js";
46
- import "./chunk-LMLD24FC.js";
44
+ import "./chunk-5K72I3UQ.js";
45
+ import "./chunk-X3NHE2DK.js";
46
+ import "./chunk-COY6PUD2.js";
47
47
  export {
48
48
  DOMPool,
49
49
  Features,
package/dist/plugins.cjs CHANGED
@@ -22,7 +22,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
 
23
23
  // src/core/dev.ts
24
24
  function isDev() {
25
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
25
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
26
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
27
+ // production builds; under the test runner it is always undefined, so
28
+ // this branch is unreachable here.
29
+ /* v8 ignore next 2 */
30
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
31
+ );
26
32
  }
27
33
  function devAssert(condition, message) {
28
34
  if (_isDev && !condition) {
@@ -85,6 +91,7 @@ function sanitizeSrcset(value) {
85
91
  return out.join(", ");
86
92
  }
87
93
  function sanitizeCSSValue(value) {
94
+ if (!CSS_DANGER_GATE.test(value)) return value;
88
95
  const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
89
96
  const code2 = Number.parseInt(hex, 16);
90
97
  if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
@@ -103,11 +110,18 @@ function sanitizeCSSValue(value) {
103
110
  function isUrlAttribute(attr) {
104
111
  return URL_ATTRIBUTES.has(attr.toLowerCase());
105
112
  }
106
- var SAFE_URL_PROTOCOLS, URL_ATTRIBUTES;
113
+ function sanitizeAttributeString(attr, value) {
114
+ const lower = attr.toLowerCase();
115
+ if (lower === "srcset") return sanitizeSrcset(value);
116
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
117
+ return value;
118
+ }
119
+ var SAFE_URL_PROTOCOLS, CSS_DANGER_GATE, URL_ATTRIBUTES;
107
120
  var init_sanitize = __esm({
108
121
  "src/utils/sanitize.ts"() {
109
122
  "use strict";
110
123
  SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
124
+ CSS_DANGER_GATE = /[(:@\\]/;
111
125
  URL_ATTRIBUTES = /* @__PURE__ */ new Set([
112
126
  "href",
113
127
  "xlink:href",
@@ -1149,7 +1163,7 @@ function bindAttribute(el, attr, getter) {
1149
1163
  if ((attr === "value" || attr === "checked") && attr in el) {
1150
1164
  setProp(el, attr, attr === "checked" ? Boolean(value) : str);
1151
1165
  } else {
1152
- el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
1166
+ el.setAttribute(attr, sanitizeAttributeString(attr, str));
1153
1167
  }
1154
1168
  }
1155
1169
  return reactiveBinding(commit);
@@ -1298,11 +1312,8 @@ init_sanitize();
1298
1312
  var SVG_NS = "http://www.w3.org/2000/svg";
1299
1313
  var _isDev6 = isDev();
1300
1314
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
1301
- function validateTagName(tag) {
1302
- const lower = tag.toLowerCase();
1303
- if (BLOCKED_TAGS.has(lower)) {
1304
- throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1305
- }
1315
+ function isBlockedTag(tag) {
1316
+ return BLOCKED_TAGS.has(tag.toLowerCase());
1306
1317
  }
1307
1318
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
1308
1319
  "config",
@@ -1457,8 +1468,11 @@ function appendChildren(el, nodes) {
1457
1468
  }
1458
1469
  }
1459
1470
  var tagFactory = (tag, ns) => {
1471
+ const blocked = isBlockedTag(tag);
1460
1472
  return (first, second) => {
1461
- validateTagName(tag);
1473
+ if (blocked) {
1474
+ throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1475
+ }
1462
1476
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
1463
1477
  if (first === void 0) return el;
1464
1478
  if (typeof first === "string") {
@@ -1528,7 +1542,6 @@ var tagFactory = (tag, ns) => {
1528
1542
  default: {
1529
1543
  const value = props[key];
1530
1544
  if (value == null) continue;
1531
- const lkey = key.toLowerCase();
1532
1545
  if (isEventHandlerAttr(key)) continue;
1533
1546
  if (typeof value === "function") {
1534
1547
  registerDisposer(el, bindAttribute(el, key, value));
@@ -1541,14 +1554,7 @@ var tagFactory = (tag, ns) => {
1541
1554
  el.removeAttribute(key);
1542
1555
  }
1543
1556
  } else {
1544
- const str = String(value);
1545
- if (lkey === "srcset") {
1546
- el.setAttribute(key, sanitizeSrcset(str));
1547
- } else if (isUrlAttribute(lkey)) {
1548
- el.setAttribute(key, sanitizeUrl(str));
1549
- } else {
1550
- el.setAttribute(key, str);
1551
- }
1557
+ el.setAttribute(key, sanitizeAttributeString(key, String(value)));
1552
1558
  }
1553
1559
  }
1554
1560
  }
package/dist/plugins.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  preloadCritical,
19
19
  prerenderRoutes,
20
20
  satisfies
21
- } from "./chunk-RDRSWYNP.js";
21
+ } from "./chunk-ZUVLC7TM.js";
22
22
  import {
23
23
  isUnsafeKey
24
24
  } from "./chunk-H3SRKIYX.js";
@@ -35,35 +35,35 @@ import {
35
35
  } from "./chunk-3JHCYHWN.js";
36
36
  import {
37
37
  span
38
- } from "./chunk-5N74TKLD.js";
38
+ } from "./chunk-24DBWDTK.js";
39
39
  import {
40
40
  escapeScriptJson,
41
41
  isDangerousMetaRefresh,
42
42
  renderToString
43
- } from "./chunk-HXMS4SNP.js";
44
- import "./chunk-X67UYC74.js";
45
- import "./chunk-RLUJL2MV.js";
43
+ } from "./chunk-R3QEDXFS.js";
44
+ import "./chunk-NUWKIEHE.js";
45
+ import "./chunk-Q2ERM6NT.js";
46
46
  import {
47
47
  dispose,
48
48
  registerDisposer
49
- } from "./chunk-2UPRY23K.js";
49
+ } from "./chunk-5VGSK6D2.js";
50
50
  import {
51
51
  isUrlAttribute,
52
52
  sanitizeCSSValue,
53
53
  sanitizeUrl,
54
54
  stripControlChars
55
- } from "./chunk-HMJFCBRR.js";
55
+ } from "./chunk-L3GAGWCC.js";
56
56
  import {
57
57
  effect
58
- } from "./chunk-FDY42FIU.js";
58
+ } from "./chunk-7JHWAGRQ.js";
59
59
  import "./chunk-GOJMFRBL.js";
60
60
  import {
61
61
  signal
62
- } from "./chunk-C427DVQF.js";
62
+ } from "./chunk-5K72I3UQ.js";
63
63
  import {
64
64
  track
65
- } from "./chunk-Z2FWAE4B.js";
66
- import "./chunk-LMLD24FC.js";
65
+ } from "./chunk-X3NHE2DK.js";
66
+ import "./chunk-COY6PUD2.js";
67
67
 
68
68
  // src/plugins/i18n.ts
69
69
  var [currentLocale, setLocaleInternal] = signal("en");
@@ -1800,7 +1800,7 @@ function hydrateRouter(routes, options) {
1800
1800
  if (container && serverState.path) {
1801
1801
  const resolved = resolveServerRoute(serverState.path, routes);
1802
1802
  if (resolved.component) {
1803
- import("./ssr-2QDQ27EV.js").then(({ hydrate }) => {
1803
+ import("./ssr-6D67RAVB.js").then(({ hydrate }) => {
1804
1804
  if (resolved.component) {
1805
1805
  hydrate(resolved.component, container);
1806
1806
  }
@@ -17,10 +17,10 @@ import {
17
17
  ssrSuspense,
18
18
  suspenseSwapScript,
19
19
  trustHTML
20
- } from "./chunk-HXMS4SNP.js";
21
- import "./chunk-HMJFCBRR.js";
20
+ } from "./chunk-R3QEDXFS.js";
21
+ import "./chunk-L3GAGWCC.js";
22
22
  import "./chunk-GOJMFRBL.js";
23
- import "./chunk-LMLD24FC.js";
23
+ import "./chunk-COY6PUD2.js";
24
24
  export {
25
25
  collectStream,
26
26
  deserializeState,