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/ssr.cjs CHANGED
@@ -66,7 +66,13 @@ module.exports = __toCommonJS(ssr_exports);
66
66
 
67
67
  // src/core/dev.ts
68
68
  function isDev() {
69
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
69
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
70
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
71
+ // production builds; under the test runner it is always undefined, so
72
+ // this branch is unreachable here.
73
+ /* v8 ignore next 2 */
74
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
75
+ );
70
76
  }
71
77
  var _isDev = isDev();
72
78
  function devAssert(condition, message) {
@@ -152,7 +158,9 @@ function sanitizeSrcset(value) {
152
158
  }
153
159
  return out.join(", ");
154
160
  }
161
+ var CSS_DANGER_GATE = /[(:@\\]/;
155
162
  function sanitizeCSSValue(value) {
163
+ if (!CSS_DANGER_GATE.test(value)) return value;
156
164
  const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
157
165
  const code2 = Number.parseInt(hex, 16);
158
166
  if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
@@ -182,8 +190,11 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
182
190
  "ping",
183
191
  "data"
184
192
  ]);
185
- function isUrlAttribute(attr) {
186
- return URL_ATTRIBUTES.has(attr.toLowerCase());
193
+ function sanitizeAttributeString(attr, value) {
194
+ const lower = attr.toLowerCase();
195
+ if (lower === "srcset") return sanitizeSrcset(value);
196
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
197
+ return value;
187
198
  }
188
199
 
189
200
  // src/platform/ssr.ts
@@ -1617,7 +1628,7 @@ function bindAttribute(el, attr, getter) {
1617
1628
  if ((attr === "value" || attr === "checked") && attr in el) {
1618
1629
  setProp(el, attr, attr === "checked" ? Boolean(value) : str);
1619
1630
  } else {
1620
- el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
1631
+ el.setAttribute(attr, sanitizeAttributeString(attr, str));
1621
1632
  }
1622
1633
  }
1623
1634
  return reactiveBinding(commit);
@@ -1761,11 +1772,8 @@ function bindChildNode(placeholder, getter) {
1761
1772
  var SVG_NS = "http://www.w3.org/2000/svg";
1762
1773
  var _isDev8 = isDev();
1763
1774
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
1764
- function validateTagName(tag) {
1765
- const lower = tag.toLowerCase();
1766
- if (BLOCKED_TAGS.has(lower)) {
1767
- throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1768
- }
1775
+ function isBlockedTag(tag) {
1776
+ return BLOCKED_TAGS.has(tag.toLowerCase());
1769
1777
  }
1770
1778
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
1771
1779
  "config",
@@ -1920,8 +1928,11 @@ function appendChildren(el, nodes) {
1920
1928
  }
1921
1929
  }
1922
1930
  var tagFactory = (tag, ns) => {
1931
+ const blocked = isBlockedTag(tag);
1923
1932
  return (first, second) => {
1924
- validateTagName(tag);
1933
+ if (blocked) {
1934
+ throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1935
+ }
1925
1936
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
1926
1937
  if (first === void 0) return el;
1927
1938
  if (typeof first === "string") {
@@ -1991,7 +2002,6 @@ var tagFactory = (tag, ns) => {
1991
2002
  default: {
1992
2003
  const value = props[key];
1993
2004
  if (value == null) continue;
1994
- const lkey = key.toLowerCase();
1995
2005
  if (isEventHandlerAttr(key)) continue;
1996
2006
  if (typeof value === "function") {
1997
2007
  registerDisposer(el, bindAttribute(el, key, value));
@@ -2004,14 +2014,7 @@ var tagFactory = (tag, ns) => {
2004
2014
  el.removeAttribute(key);
2005
2015
  }
2006
2016
  } else {
2007
- const str = String(value);
2008
- if (lkey === "srcset") {
2009
- el.setAttribute(key, sanitizeSrcset(str));
2010
- } else if (isUrlAttribute(lkey)) {
2011
- el.setAttribute(key, sanitizeUrl(str));
2012
- } else {
2013
- el.setAttribute(key, str);
2014
- }
2017
+ el.setAttribute(key, sanitizeAttributeString(key, String(value)));
2015
2018
  }
2016
2019
  }
2017
2020
  }
package/dist/ssr.js CHANGED
@@ -22,8 +22,8 @@ import {
22
22
  wasm,
23
23
  worker,
24
24
  workerFn
25
- } from "./chunk-OYLPZO4N.js";
26
- import "./chunk-5N74TKLD.js";
25
+ } from "./chunk-USDR2GFV.js";
26
+ import "./chunk-24DBWDTK.js";
27
27
  import {
28
28
  collectStream,
29
29
  deserializeState,
@@ -43,16 +43,16 @@ import {
43
43
  ssrSuspense,
44
44
  suspenseSwapScript,
45
45
  trustHTML
46
- } from "./chunk-HXMS4SNP.js";
47
- import "./chunk-X67UYC74.js";
48
- import "./chunk-RLUJL2MV.js";
49
- import "./chunk-2UPRY23K.js";
50
- import "./chunk-HMJFCBRR.js";
51
- import "./chunk-FDY42FIU.js";
46
+ } from "./chunk-R3QEDXFS.js";
47
+ import "./chunk-NUWKIEHE.js";
48
+ import "./chunk-Q2ERM6NT.js";
49
+ import "./chunk-5VGSK6D2.js";
50
+ import "./chunk-L3GAGWCC.js";
51
+ import "./chunk-7JHWAGRQ.js";
52
52
  import "./chunk-GOJMFRBL.js";
53
- import "./chunk-C427DVQF.js";
54
- import "./chunk-Z2FWAE4B.js";
55
- import "./chunk-LMLD24FC.js";
53
+ import "./chunk-5K72I3UQ.js";
54
+ import "./chunk-X3NHE2DK.js";
55
+ import "./chunk-COY6PUD2.js";
56
56
  export {
57
57
  Head,
58
58
  clearWasmCache,
package/dist/testing.cjs CHANGED
@@ -68,7 +68,13 @@ module.exports = __toCommonJS(testing_exports);
68
68
 
69
69
  // src/core/dev.ts
70
70
  function isDev() {
71
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
71
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
72
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
73
+ // production builds; under the test runner it is always undefined, so
74
+ // this branch is unreachable here.
75
+ /* v8 ignore next 2 */
76
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
77
+ );
72
78
  }
73
79
  var _isDev = isDev();
74
80
  function devAssert(condition, message) {
@@ -130,6 +136,9 @@ function dispose(node) {
130
136
  }
131
137
 
132
138
  // src/testing/a11y.ts
139
+ function escSel(value) {
140
+ return value.replace(/["\\]/g, "\\$&");
141
+ }
133
142
  var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
134
143
  "alert",
135
144
  "alertdialog",
@@ -386,7 +395,7 @@ function checkInputHasLabel(input, root) {
386
395
  if (input.getAttribute("title")?.trim()) return true;
387
396
  const id = input.getAttribute("id");
388
397
  if (id) {
389
- const label = root.querySelector(`label[for="${id}"]`);
398
+ const label = root.querySelector(`label[for="${escSel(id)}"]`);
390
399
  if (label?.textContent?.trim()) return true;
391
400
  }
392
401
  let parent = input.parentElement;
@@ -972,6 +981,9 @@ function assertA11y(root, options) {
972
981
  }
973
982
 
974
983
  // src/testing/adapters.ts
984
+ function escSel2(value) {
985
+ return value.replace(/["\\]/g, "\\$&");
986
+ }
975
987
  function createJestAdapter() {
976
988
  let container = null;
977
989
  return {
@@ -1187,10 +1199,10 @@ function createUniversalAdapter() {
1187
1199
  /** Query helpers */
1188
1200
  queries: {
1189
1201
  byTestId(container2, id) {
1190
- return container2.querySelector(`[data-testid="${id}"]`);
1202
+ return container2.querySelector(`[data-testid="${escSel2(id)}"]`);
1191
1203
  },
1192
1204
  byRole(container2, role) {
1193
- return container2.querySelector(`[role="${role}"]`);
1205
+ return container2.querySelector(`[role="${escSel2(role)}"]`);
1194
1206
  },
1195
1207
  byText(container2, text) {
1196
1208
  const walker = document.createTreeWalker(container2, NodeFilter.SHOW_TEXT);
@@ -1207,16 +1219,16 @@ function createUniversalAdapter() {
1207
1219
  if (labelEl.textContent?.includes(label)) {
1208
1220
  const forAttr = labelEl.getAttribute("for");
1209
1221
  if (forAttr) {
1210
- return container2.querySelector(`#${forAttr}`);
1222
+ return container2.querySelector(`[id="${escSel2(forAttr)}"]`);
1211
1223
  }
1212
1224
  const nested = labelEl.querySelector("input, select, textarea");
1213
1225
  if (nested) return nested;
1214
1226
  }
1215
1227
  }
1216
- return container2.querySelector(`[aria-label="${label}"]`);
1228
+ return container2.querySelector(`[aria-label="${escSel2(label)}"]`);
1217
1229
  },
1218
1230
  allByRole(container2, role) {
1219
- return Array.from(container2.querySelectorAll(`[role="${role}"]`));
1231
+ return Array.from(container2.querySelectorAll(`[role="${escSel2(role)}"]`));
1220
1232
  }
1221
1233
  },
1222
1234
  /** Assertion helpers */
package/dist/testing.js CHANGED
@@ -1,14 +1,17 @@
1
1
  import {
2
2
  dispose
3
- } from "./chunk-2UPRY23K.js";
3
+ } from "./chunk-5VGSK6D2.js";
4
4
  import {
5
5
  effect
6
- } from "./chunk-FDY42FIU.js";
6
+ } from "./chunk-7JHWAGRQ.js";
7
7
  import "./chunk-GOJMFRBL.js";
8
- import "./chunk-Z2FWAE4B.js";
9
- import "./chunk-LMLD24FC.js";
8
+ import "./chunk-X3NHE2DK.js";
9
+ import "./chunk-COY6PUD2.js";
10
10
 
11
11
  // src/testing/a11y.ts
12
+ function escSel(value) {
13
+ return value.replace(/["\\]/g, "\\$&");
14
+ }
12
15
  var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
13
16
  "alert",
14
17
  "alertdialog",
@@ -265,7 +268,7 @@ function checkInputHasLabel(input, root) {
265
268
  if (input.getAttribute("title")?.trim()) return true;
266
269
  const id = input.getAttribute("id");
267
270
  if (id) {
268
- const label = root.querySelector(`label[for="${id}"]`);
271
+ const label = root.querySelector(`label[for="${escSel(id)}"]`);
269
272
  if (label?.textContent?.trim()) return true;
270
273
  }
271
274
  let parent = input.parentElement;
@@ -851,6 +854,9 @@ function assertA11y(root, options) {
851
854
  }
852
855
 
853
856
  // src/testing/adapters.ts
857
+ function escSel2(value) {
858
+ return value.replace(/["\\]/g, "\\$&");
859
+ }
854
860
  function createJestAdapter() {
855
861
  let container = null;
856
862
  return {
@@ -1066,10 +1072,10 @@ function createUniversalAdapter() {
1066
1072
  /** Query helpers */
1067
1073
  queries: {
1068
1074
  byTestId(container2, id) {
1069
- return container2.querySelector(`[data-testid="${id}"]`);
1075
+ return container2.querySelector(`[data-testid="${escSel2(id)}"]`);
1070
1076
  },
1071
1077
  byRole(container2, role) {
1072
- return container2.querySelector(`[role="${role}"]`);
1078
+ return container2.querySelector(`[role="${escSel2(role)}"]`);
1073
1079
  },
1074
1080
  byText(container2, text) {
1075
1081
  const walker = document.createTreeWalker(container2, NodeFilter.SHOW_TEXT);
@@ -1086,16 +1092,16 @@ function createUniversalAdapter() {
1086
1092
  if (labelEl.textContent?.includes(label)) {
1087
1093
  const forAttr = labelEl.getAttribute("for");
1088
1094
  if (forAttr) {
1089
- return container2.querySelector(`#${forAttr}`);
1095
+ return container2.querySelector(`[id="${escSel2(forAttr)}"]`);
1090
1096
  }
1091
1097
  const nested = labelEl.querySelector("input, select, textarea");
1092
1098
  if (nested) return nested;
1093
1099
  }
1094
1100
  }
1095
- return container2.querySelector(`[aria-label="${label}"]`);
1101
+ return container2.querySelector(`[aria-label="${escSel2(label)}"]`);
1096
1102
  },
1097
1103
  allByRole(container2, role) {
1098
- return Array.from(container2.querySelectorAll(`[role="${role}"]`));
1104
+ return Array.from(container2.querySelectorAll(`[role="${escSel2(role)}"]`));
1099
1105
  }
1100
1106
  },
1101
1107
  /** Assertion helpers */
package/dist/ui.cjs CHANGED
@@ -87,7 +87,13 @@ module.exports = __toCommonJS(ui_exports);
87
87
 
88
88
  // src/core/dev.ts
89
89
  function isDev() {
90
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
90
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
91
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
92
+ // production builds; under the test runner it is always undefined, so
93
+ // this branch is unreachable here.
94
+ /* v8 ignore next 2 */
95
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
96
+ );
91
97
  }
92
98
  var _isDev = isDev();
93
99
  function devAssert(condition, message) {
@@ -1740,6 +1746,20 @@ function sanitizeUrl(url) {
1740
1746
  if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1741
1747
  return trimmed;
1742
1748
  }
1749
+ function sanitizeSrcset(value) {
1750
+ const parts = value.split(",");
1751
+ const out = [];
1752
+ for (let i = 0; i < parts.length; i++) {
1753
+ const part = parts[i].trim();
1754
+ if (!part) continue;
1755
+ const m = part.match(/^(\S+)(\s+.+)?$/);
1756
+ if (!m) continue;
1757
+ const safe = sanitizeUrl(m[1]);
1758
+ if (!safe) continue;
1759
+ out.push(m[2] ? `${safe}${m[2]}` : safe);
1760
+ }
1761
+ return out.join(", ");
1762
+ }
1743
1763
  var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1744
1764
  "href",
1745
1765
  "xlink:href",
@@ -1754,8 +1774,11 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1754
1774
  "ping",
1755
1775
  "data"
1756
1776
  ]);
1757
- function isUrlAttribute(attr) {
1758
- return URL_ATTRIBUTES.has(attr.toLowerCase());
1777
+ function sanitizeAttributeString(attr, value) {
1778
+ const lower = attr.toLowerCase();
1779
+ if (lower === "srcset") return sanitizeSrcset(value);
1780
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1781
+ return value;
1759
1782
  }
1760
1783
 
1761
1784
  // src/reactivity/bindAttribute.ts
@@ -1795,7 +1818,7 @@ function bindAttribute(el, attr, getter) {
1795
1818
  if ((attr === "value" || attr === "checked") && attr in el) {
1796
1819
  setProp(el, attr, attr === "checked" ? Boolean(value) : str);
1797
1820
  } else {
1798
- el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
1821
+ el.setAttribute(attr, sanitizeAttributeString(attr, str));
1799
1822
  }
1800
1823
  }
1801
1824
  return reactiveBinding(commit);
package/dist/ui.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  toast,
39
39
  withScopedStyle,
40
40
  zipMask
41
- } from "./chunk-NPIEEKPT.js";
41
+ } from "./chunk-6G6UNHZI.js";
42
42
  import {
43
43
  RenderProp,
44
44
  assertType,
@@ -58,21 +58,21 @@ import {
58
58
  import {
59
59
  createId
60
60
  } from "./chunk-YT6HQ6AM.js";
61
- import "./chunk-RLUJL2MV.js";
62
- import "./chunk-JYXOEYI4.js";
61
+ import "./chunk-Q2ERM6NT.js";
62
+ import "./chunk-CVMMULHO.js";
63
63
  import {
64
64
  registerDisposer
65
- } from "./chunk-2UPRY23K.js";
66
- import "./chunk-HMJFCBRR.js";
65
+ } from "./chunk-5VGSK6D2.js";
66
+ import "./chunk-L3GAGWCC.js";
67
67
  import {
68
68
  effect
69
- } from "./chunk-FDY42FIU.js";
69
+ } from "./chunk-7JHWAGRQ.js";
70
70
  import "./chunk-GOJMFRBL.js";
71
71
  import {
72
72
  signal
73
- } from "./chunk-C427DVQF.js";
74
- import "./chunk-Z2FWAE4B.js";
75
- import "./chunk-LMLD24FC.js";
73
+ } from "./chunk-5K72I3UQ.js";
74
+ import "./chunk-X3NHE2DK.js";
75
+ import "./chunk-COY6PUD2.js";
76
76
 
77
77
  // src/ui/formAction.ts
78
78
  function formAction(fn) {
package/dist/widgets.cjs CHANGED
@@ -34,7 +34,13 @@ module.exports = __toCommonJS(widgets_exports);
34
34
 
35
35
  // src/core/dev.ts
36
36
  function isDev() {
37
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
37
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
38
+ // The bare `__SIBU_DEV__` is a bundler define that only exists in
39
+ // production builds; under the test runner it is always undefined, so
40
+ // this branch is unreachable here.
41
+ /* v8 ignore next 2 */
42
+ typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
43
+ );
38
44
  }
39
45
  var _isDev = isDev();
40
46
  function devAssert(condition, message) {
@@ -132,6 +138,7 @@ function unlinkSub(node) {
132
138
  else sub.depsTail = prev;
133
139
  }
134
140
  var currentSubscriber = null;
141
+ var suspendSavedSub = null;
135
142
  var notifyDepth = 0;
136
143
  var pendingQueue = [];
137
144
  var pendingSet = /* @__PURE__ */ new Set();
@@ -143,7 +150,24 @@ function safeInvoke(sub) {
143
150
  if (_isDev2) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
144
151
  }
145
152
  }
153
+ var suspendDepth = 0;
146
154
  var trackingSuspended = false;
155
+ function suspendTracking() {
156
+ if (suspendDepth === 0) {
157
+ suspendSavedSub = currentSubscriber;
158
+ currentSubscriber = null;
159
+ trackingSuspended = true;
160
+ }
161
+ suspendDepth++;
162
+ }
163
+ function resumeTracking() {
164
+ suspendDepth--;
165
+ if (suspendDepth === 0) {
166
+ currentSubscriber = suspendSavedSub;
167
+ suspendSavedSub = null;
168
+ trackingSuspended = false;
169
+ }
170
+ }
147
171
  var subscriberEpochCounter = 0;
148
172
  function retrack(effectFn, subscriber) {
149
173
  const prev = currentSubscriber;
@@ -747,8 +771,14 @@ function watch(getter, callback) {
747
771
  return;
748
772
  }
749
773
  if (!Object.is(newValue, oldValue)) {
750
- callback(newValue, oldValue);
774
+ const prev = oldValue;
751
775
  oldValue = newValue;
776
+ suspendTracking();
777
+ try {
778
+ callback(newValue, prev);
779
+ } finally {
780
+ resumeTracking();
781
+ }
752
782
  }
753
783
  };
754
784
  const teardown = track(subscriber);
package/dist/widgets.js CHANGED
@@ -8,15 +8,15 @@ import {
8
8
  select,
9
9
  tabs,
10
10
  tooltip
11
- } from "./chunk-2C4E3HBM.js";
12
- import "./chunk-H6PCHJZQ.js";
13
- import "./chunk-JYXOEYI4.js";
14
- import "./chunk-HMJFCBRR.js";
15
- import "./chunk-FDY42FIU.js";
11
+ } from "./chunk-4WXWJ4SW.js";
12
+ import "./chunk-F4UM7QBJ.js";
13
+ import "./chunk-CVMMULHO.js";
14
+ import "./chunk-L3GAGWCC.js";
15
+ import "./chunk-7JHWAGRQ.js";
16
16
  import "./chunk-GOJMFRBL.js";
17
- import "./chunk-C427DVQF.js";
18
- import "./chunk-Z2FWAE4B.js";
19
- import "./chunk-LMLD24FC.js";
17
+ import "./chunk-5K72I3UQ.js";
18
+ import "./chunk-X3NHE2DK.js";
19
+ import "./chunk-COY6PUD2.js";
20
20
  export {
21
21
  accordion,
22
22
  combobox,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sibujs",
3
- "version": "3.2.1",
3
+ "version": "3.3.0",
4
4
  "description": "A lightweight, function-based frontend framework that combines the best of React, Svelte, and Vue — with zero VDOM and maximum simplicity. Designed for developers who want fine-grained reactivity and full control without compilation or magic.",
5
5
  "keywords": [
6
6
  "frontend",