sibujs 4.7.0 → 4.9.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 (83) hide show
  1. package/dist/browser.cjs +48 -6
  2. package/dist/browser.d.cts +26 -2
  3. package/dist/browser.d.ts +26 -2
  4. package/dist/browser.js +6 -5
  5. package/dist/build.cjs +539 -96
  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-Z3OHK6QT.js → chunk-2K3Z3XO4.js} +5 -5
  14. package/dist/chunk-3ACAAQIN.js +224 -0
  15. package/dist/{chunk-WOLJZUFQ.js → chunk-562UVHMM.js} +32 -15
  16. package/dist/{chunk-UOL2ECCS.js → chunk-5MLWTIQE.js} +18 -12
  17. package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
  18. package/dist/{chunk-3QWBSL5R.js → chunk-7OV5Q2UU.js} +8 -5
  19. package/dist/{chunk-SLM3IA34.js → chunk-CDBKFGXN.js} +1 -1
  20. package/dist/{chunk-4AWA2PVD.js → chunk-CF2KCWRE.js} +10 -5
  21. package/dist/{chunk-BSY63EM6.js → chunk-DRMLIF6M.js} +20 -5
  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-BC2SECJD.js → chunk-FHCZMHHE.js} +1 -1
  25. package/dist/{chunk-XYV3EDB7.js → chunk-FNOK662L.js} +63 -38
  26. package/dist/{chunk-JWKYU5GV.js → chunk-G2WHYEL7.js} +1 -1
  27. package/dist/{chunk-DKXRACVN.js → chunk-K6K5ACO6.js} +6 -6
  28. package/dist/{chunk-VZSG24LS.js → chunk-KG25XMGP.js} +45 -17
  29. package/dist/{chunk-FNJXNGYZ.js → chunk-MPWDSTYK.js} +58 -6
  30. package/dist/{chunk-UNWRJRKC.js → chunk-NQMKWBKU.js} +1 -1
  31. package/dist/{chunk-7LUJQAOJ.js → chunk-QBKCK6MJ.js} +1 -1
  32. package/dist/{chunk-WEQ3DMVL.js → chunk-SBTZEYNI.js} +1 -1
  33. package/dist/{chunk-FQRUXCEE.js → chunk-SYHGGDNW.js} +1 -1
  34. package/dist/{chunk-XZR4PXRE.js → chunk-TV464N2M.js} +13 -10
  35. package/dist/{chunk-RUSSKG6G.js → chunk-X7NRHFZR.js} +1 -1
  36. package/dist/{chunk-2BPG2XDA.js → chunk-XJVTQUIV.js} +18 -36
  37. package/dist/{chunk-ZVL7TY4K.js → chunk-YW6HQKGO.js} +25 -10
  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 +29 -9
  41. package/dist/data.d.cts +23 -4
  42. package/dist/data.d.ts +23 -4
  43. package/dist/data.js +6 -6
  44. package/dist/devtools.cjs +1 -1
  45. package/dist/devtools.js +5 -5
  46. package/dist/ecosystem.cjs +400 -37
  47. package/dist/ecosystem.js +9 -8
  48. package/dist/extras.cjs +672 -269
  49. package/dist/extras.d.cts +2 -2
  50. package/dist/extras.d.ts +2 -2
  51. package/dist/extras.js +24 -23
  52. package/dist/index.cjs +490 -88
  53. package/dist/index.d.cts +34 -2
  54. package/dist/index.d.ts +34 -2
  55. package/dist/index.js +16 -12
  56. package/dist/motion.cjs +1 -1
  57. package/dist/motion.js +3 -3
  58. package/dist/patterns.cjs +10 -7
  59. package/dist/patterns.js +6 -6
  60. package/dist/performance.cjs +31 -2
  61. package/dist/performance.d.cts +12 -2
  62. package/dist/performance.d.ts +12 -2
  63. package/dist/performance.js +8 -6
  64. package/dist/plugins.cjs +571 -240
  65. package/dist/plugins.d.cts +14 -3
  66. package/dist/plugins.d.ts +14 -3
  67. package/dist/plugins.js +35 -37
  68. package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
  69. package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.cts} +6 -0
  70. package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.ts} +6 -0
  71. package/dist/ssr.cjs +484 -138
  72. package/dist/ssr.d.cts +59 -5
  73. package/dist/ssr.d.ts +59 -5
  74. package/dist/ssr.js +11 -9
  75. package/dist/testing.cjs +3 -3
  76. package/dist/testing.js +4 -4
  77. package/dist/ui.cjs +609 -241
  78. package/dist/ui.d.cts +1 -1
  79. package/dist/ui.d.ts +1 -1
  80. package/dist/ui.js +9 -8
  81. package/dist/widgets.cjs +1 -1
  82. package/dist/widgets.js +7 -7
  83. package/package.json +5 -4
package/dist/browser.cjs CHANGED
@@ -674,7 +674,7 @@ function forEachSubscriber(signal2, visit) {
674
674
  }
675
675
 
676
676
  // src/reactivity/track.ts
677
- var _runtimeVersion = true ? "4.7.0" : "dev";
677
+ var _runtimeVersion = true ? "4.9.0" : "dev";
678
678
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
679
679
  function resolveReactiveApi() {
680
680
  const g = globalThis;
@@ -1658,13 +1658,19 @@ function colorScheme() {
1658
1658
  }
1659
1659
 
1660
1660
  // src/browser/format.ts
1661
+ var I18N_STATE = /* @__PURE__ */ Symbol.for("sibujs.i18n.v1");
1662
+ function activeI18nLocale() {
1663
+ const state2 = globalThis[I18N_STATE];
1664
+ if (!state2) return void 0;
1665
+ return state2.getLocale ? state2.getLocale() : state2.locale?.[0]();
1666
+ }
1661
1667
  function formatNumber(value, options) {
1662
1668
  const { locale, ...formatOptions } = options ?? {};
1663
- return new Intl.NumberFormat(locale, formatOptions).format(value);
1669
+ return new Intl.NumberFormat(locale ?? activeI18nLocale(), formatOptions).format(value);
1664
1670
  }
1665
1671
  function formatCurrency(value, currency, options) {
1666
1672
  const { locale, ...formatOptions } = options ?? {};
1667
- return new Intl.NumberFormat(locale, {
1673
+ return new Intl.NumberFormat(locale ?? activeI18nLocale(), {
1668
1674
  ...formatOptions,
1669
1675
  style: "currency",
1670
1676
  currency
@@ -2258,6 +2264,7 @@ function keyboard(options = {}) {
2258
2264
  };
2259
2265
  }
2260
2266
  const target = options.target ?? window;
2267
+ const view = options.target?.ownerDocument?.defaultView ?? window;
2261
2268
  const filter = options.keys ? new Set(options.keys) : null;
2262
2269
  const onDown = (e) => {
2263
2270
  if (filter && !filter.has(e.key)) return;
@@ -2280,14 +2287,14 @@ function keyboard(options = {}) {
2280
2287
  const onBlur = () => setPressed(/* @__PURE__ */ new Set());
2281
2288
  target.addEventListener("keydown", onDown);
2282
2289
  target.addEventListener("keyup", onUp);
2283
- window.addEventListener("blur", onBlur);
2290
+ view.addEventListener("blur", onBlur);
2284
2291
  function isPressed(key) {
2285
2292
  return pressed().has(key);
2286
2293
  }
2287
2294
  function dispose() {
2288
2295
  target.removeEventListener("keydown", onDown);
2289
2296
  target.removeEventListener("keyup", onUp);
2290
- window.removeEventListener("blur", onBlur);
2297
+ view.removeEventListener("blur", onBlur);
2291
2298
  }
2292
2299
  return { pressed, isPressed, dispose };
2293
2300
  }
@@ -2592,6 +2599,36 @@ function textSelection() {
2592
2599
  };
2593
2600
  }
2594
2601
 
2602
+ // src/utils/sanitize.ts
2603
+ function stripControlChars(value) {
2604
+ return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
2605
+ }
2606
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
2607
+ var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
2608
+ function sanitizeUrl(url) {
2609
+ const probe = stripControlChars(url).trim();
2610
+ if (!probe) return "";
2611
+ const output = url.replace(OUTER_TRIM, "");
2612
+ if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
2613
+ return "";
2614
+ }
2615
+ const lower = probe.toLowerCase();
2616
+ let schemeEnd = -1;
2617
+ for (let i = 0; i < lower.length; i++) {
2618
+ const ch = lower.charCodeAt(i);
2619
+ if (ch === 58) {
2620
+ schemeEnd = i;
2621
+ break;
2622
+ }
2623
+ if (ch === 47 || ch === 63 || ch === 35) break;
2624
+ }
2625
+ if (schemeEnd === -1) return output;
2626
+ const scheme = lower.slice(0, schemeEnd + 1);
2627
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output;
2628
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
2629
+ return output;
2630
+ }
2631
+
2595
2632
  // src/browser/imageLoader.ts
2596
2633
  function imageLoader(src) {
2597
2634
  const [status, setStatus] = signal("pending");
@@ -2633,6 +2670,11 @@ function imageLoader(src) {
2633
2670
  abandonCurrent();
2634
2671
  resetState();
2635
2672
  if (disposed || token !== startToken) return;
2673
+ const safe = sanitizeUrl(String(url));
2674
+ if (!safe) {
2675
+ setStatus("error");
2676
+ return;
2677
+ }
2636
2678
  const img = new Image();
2637
2679
  current = img;
2638
2680
  currentSettled = false;
@@ -2651,7 +2693,7 @@ function imageLoader(src) {
2651
2693
  currentSettled = true;
2652
2694
  setStatus("error");
2653
2695
  };
2654
- img.src = url;
2696
+ img.src = safe;
2655
2697
  }
2656
2698
  let srcEffectTeardown = null;
2657
2699
  if (typeof src === "function") {
@@ -198,6 +198,18 @@ declare function colorScheme(): {
198
198
  /**
199
199
  * Locale-aware number and currency formatting using Intl.NumberFormat.
200
200
  *
201
+ * LOCALE. An explicit `locale` option always wins. Without one, the active i18n
202
+ * locale is used when the i18n plugin is loaded (`setLocale()` / `getLocale()`),
203
+ * and the runtime's default locale otherwise. The i18n read is reactive, so a
204
+ * formatter called inside a binding re-formats in place when the locale
205
+ * switches:
206
+ *
207
+ * ```ts
208
+ * span(() => formatCurrency(price(), "EUR")); // follows setLocale("es")
209
+ * ```
210
+ *
211
+ * On the server the read is request-scoped, exactly as `getLocale()` is.
212
+ *
201
213
  * @example
202
214
  * ```ts
203
215
  * formatNumber(1234567.89); // "1,234,567.89" (locale-dependent)
@@ -630,8 +642,9 @@ interface KeyboardOptions {
630
642
  * `KeyboardEvent.key`). On every key event the signal is replaced with a
631
643
  * new Set instance so reactive subscribers see the change.
632
644
  *
633
- * Also listens to `window.blur` to clear stuck keys if the window loses
634
- * focus mid-press — otherwise held modifiers can "ghost" forever.
645
+ * Also listens to `blur` on the target's window (the global `window`, or the
646
+ * window of the document a `target` element belongs to) to clear stuck keys
647
+ * if it loses focus mid-press — otherwise held modifiers can "ghost" forever.
635
648
  *
636
649
  * @example
637
650
  * ```ts
@@ -792,6 +805,13 @@ declare function vibrate(pattern: number | number[]): boolean;
792
805
  * Ensures a `<link rel="icon">` exists — creates one if missing, updates
793
806
  * the `href` otherwise.
794
807
  *
808
+ * NOT routed through the canonical URL allowlist, deliberately: `data:` icons
809
+ * are this helper's purpose (see `svgFavicon`), and the allowlist refuses
810
+ * `data:`. That is safe here because an icon link only ever fetches an image —
811
+ * the browser never navigates to, executes or renders it as a document, so no
812
+ * scheme turns this sink into script. Do not copy this exception to any sink
813
+ * that navigates, executes or embeds.
814
+ *
795
815
  * @param url Favicon URL or `data:` URI
796
816
  *
797
817
  * @example
@@ -874,6 +894,10 @@ interface ImageLoaderState {
874
894
  * and its value changes, the previous load is abandoned and a new one
875
895
  * starts.
876
896
  *
897
+ * The URL passes the framework's canonical URL policy first (`http:`,
898
+ * `https:`, relative, …). A refused URL — `javascript:`, `data:`, `blob:`, … —
899
+ * requests nothing and reports `status() === "error"`.
900
+ *
877
901
  * @example
878
902
  * ```ts
879
903
  * const img = imageLoader("/hero.jpg");
package/dist/browser.d.ts CHANGED
@@ -198,6 +198,18 @@ declare function colorScheme(): {
198
198
  /**
199
199
  * Locale-aware number and currency formatting using Intl.NumberFormat.
200
200
  *
201
+ * LOCALE. An explicit `locale` option always wins. Without one, the active i18n
202
+ * locale is used when the i18n plugin is loaded (`setLocale()` / `getLocale()`),
203
+ * and the runtime's default locale otherwise. The i18n read is reactive, so a
204
+ * formatter called inside a binding re-formats in place when the locale
205
+ * switches:
206
+ *
207
+ * ```ts
208
+ * span(() => formatCurrency(price(), "EUR")); // follows setLocale("es")
209
+ * ```
210
+ *
211
+ * On the server the read is request-scoped, exactly as `getLocale()` is.
212
+ *
201
213
  * @example
202
214
  * ```ts
203
215
  * formatNumber(1234567.89); // "1,234,567.89" (locale-dependent)
@@ -630,8 +642,9 @@ interface KeyboardOptions {
630
642
  * `KeyboardEvent.key`). On every key event the signal is replaced with a
631
643
  * new Set instance so reactive subscribers see the change.
632
644
  *
633
- * Also listens to `window.blur` to clear stuck keys if the window loses
634
- * focus mid-press — otherwise held modifiers can "ghost" forever.
645
+ * Also listens to `blur` on the target's window (the global `window`, or the
646
+ * window of the document a `target` element belongs to) to clear stuck keys
647
+ * if it loses focus mid-press — otherwise held modifiers can "ghost" forever.
635
648
  *
636
649
  * @example
637
650
  * ```ts
@@ -792,6 +805,13 @@ declare function vibrate(pattern: number | number[]): boolean;
792
805
  * Ensures a `<link rel="icon">` exists — creates one if missing, updates
793
806
  * the `href` otherwise.
794
807
  *
808
+ * NOT routed through the canonical URL allowlist, deliberately: `data:` icons
809
+ * are this helper's purpose (see `svgFavicon`), and the allowlist refuses
810
+ * `data:`. That is safe here because an icon link only ever fetches an image —
811
+ * the browser never navigates to, executes or renders it as a document, so no
812
+ * scheme turns this sink into script. Do not copy this exception to any sink
813
+ * that navigates, executes or embeds.
814
+ *
795
815
  * @param url Favicon URL or `data:` URI
796
816
  *
797
817
  * @example
@@ -874,6 +894,10 @@ interface ImageLoaderState {
874
894
  * and its value changes, the previous load is abandoned and a new one
875
895
  * starts.
876
896
  *
897
+ * The URL passes the framework's canonical URL policy first (`http:`,
898
+ * `https:`, relative, …). A refused URL — `javascript:`, `data:`, `blob:`, … —
899
+ * requests nothing and reports `status() === "error"`.
900
+ *
877
901
  * @example
878
902
  * ```ts
879
903
  * const img = imageLoader("/hero.jpg");
package/dist/browser.js CHANGED
@@ -36,14 +36,15 @@ import {
36
36
  visibility,
37
37
  wakeLock,
38
38
  windowSize
39
- } from "./chunk-ZVL7TY4K.js";
40
- import "./chunk-H3SRKIYX.js";
39
+ } from "./chunk-YW6HQKGO.js";
41
40
  import "./chunk-52XFPGSN.js";
41
+ import "./chunk-62BDYGAG.js";
42
42
  import "./chunk-7ZHH77QA.js";
43
- import "./chunk-WEQ3DMVL.js";
43
+ import "./chunk-SBTZEYNI.js";
44
+ import "./chunk-EXXSN3BW.js";
44
45
  import "./chunk-7XHATCIH.js";
45
- import "./chunk-FQRUXCEE.js";
46
- import "./chunk-JWKYU5GV.js";
46
+ import "./chunk-SYHGGDNW.js";
47
+ import "./chunk-G2WHYEL7.js";
47
48
  import "./chunk-NYNYSPK7.js";
48
49
  export {
49
50
  __resetSpeechCoordinator,