sibujs 3.2.2 → 3.3.1

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 +217 -27
  2. package/dist/browser.js +5 -5
  3. package/dist/build.cjs +236 -105
  4. package/dist/build.js +14 -14
  5. package/dist/cdn.global.js +7 -7
  6. package/dist/{chunk-5N74TKLD.js → chunk-37BUKSLH.js} +1 -1
  7. package/dist/{chunk-BGNLPNGV.js → chunk-4UUMSLSL.js} +4 -4
  8. package/dist/{chunk-2UPRY23K.js → chunk-5VGSK6D2.js} +1 -1
  9. package/dist/{chunk-OYLPZO4N.js → chunk-6LTFHJQG.js} +4 -4
  10. package/dist/{chunk-VOCE4NNK.js → chunk-AMIKDMLP.js} +5 -5
  11. package/dist/{chunk-LMLD24FC.js → chunk-COY6PUD2.js} +7 -1
  12. package/dist/{chunk-YFDGQWDA.js → chunk-F7FXQ3QS.js} +1 -1
  13. package/dist/{chunk-V2MTG5FT.js → chunk-IKLYI3RF.js} +33 -16
  14. package/dist/{chunk-HMJFCBRR.js → chunk-L3GAGWCC.js} +10 -1
  15. package/dist/{chunk-X67UYC74.js → chunk-LU2MQXQQ.js} +14 -24
  16. package/dist/{chunk-4JCAUOLN.js → chunk-M5KBNOSJ.js} +22 -12
  17. package/dist/{chunk-FDY42FIU.js → chunk-MHBCEJQO.js} +2 -2
  18. package/dist/{chunk-RLUJL2MV.js → chunk-NHKQKKZU.js} +6 -7
  19. package/dist/{chunk-HXMS4SNP.js → chunk-R3QEDXFS.js} +2 -2
  20. package/dist/{chunk-NPIEEKPT.js → chunk-RYMOSG5B.js} +6 -6
  21. package/dist/{chunk-H6PCHJZQ.js → chunk-SLMFA3ZZ.js} +11 -3
  22. package/dist/{chunk-Z2FWAE4B.js → chunk-TEFZT5PJ.js} +84 -16
  23. package/dist/{chunk-7XDYVJLE.js → chunk-ULLTNDRA.js} +6 -6
  24. package/dist/{chunk-GOUM4JCT.js → chunk-VOVVTOEA.js} +2 -2
  25. package/dist/{chunk-FOI23UJL.js → chunk-WL7BIR6O.js} +1 -1
  26. package/dist/{chunk-JYXOEYI4.js → chunk-WW6DAGGR.js} +5 -5
  27. package/dist/{chunk-C427DVQF.js → chunk-WZG2SZOT.js} +22 -5
  28. package/dist/{chunk-NFYWLRUO.js → chunk-XH2RTYEQ.js} +3 -3
  29. package/dist/{chunk-2C4E3HBM.js → chunk-Z37APKBV.js} +5 -5
  30. package/dist/{chunk-RDRSWYNP.js → chunk-ZUVLC7TM.js} +1 -1
  31. package/dist/data.cjs +168 -24
  32. package/dist/data.js +8 -8
  33. package/dist/devtools.cjs +252 -32
  34. package/dist/devtools.js +7 -7
  35. package/dist/ecosystem.cjs +205 -64
  36. package/dist/ecosystem.js +10 -10
  37. package/dist/extras.cjs +245 -112
  38. package/dist/extras.js +24 -24
  39. package/dist/index.cjs +236 -105
  40. package/dist/index.d.cts +72 -8
  41. package/dist/index.d.ts +72 -8
  42. package/dist/index.js +14 -14
  43. package/dist/motion.cjs +336 -14
  44. package/dist/motion.js +4 -4
  45. package/dist/patterns.cjs +168 -24
  46. package/dist/patterns.js +6 -6
  47. package/dist/performance.cjs +305 -32
  48. package/dist/performance.js +8 -8
  49. package/dist/plugins.cjs +268 -69
  50. package/dist/plugins.js +12 -12
  51. package/dist/{ssr-2QDQ27EV.js → ssr-6D67RAVB.js} +3 -3
  52. package/dist/ssr.cjs +205 -63
  53. package/dist/ssr.js +11 -11
  54. package/dist/testing.cjs +415 -12
  55. package/dist/testing.js +16 -10
  56. package/dist/ui.cjs +248 -36
  57. package/dist/ui.js +9 -9
  58. package/dist/widgets.cjs +176 -26
  59. package/dist/widgets.js +8 -8
  60. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -170,7 +170,7 @@ __export(index_exports, {
170
170
  registerComponent: () => registerComponent,
171
171
  registerDisposer: () => registerDisposer,
172
172
  resolveComponent: () => resolveComponent,
173
- retrack: () => retrack,
173
+ retrack: () => retrack2,
174
174
  rp: () => rp,
175
175
  rt: () => rt,
176
176
  ruby: () => ruby,
@@ -181,7 +181,7 @@ __export(index_exports, {
181
181
  section: () => section,
182
182
  select: () => select,
183
183
  setGlobalErrorHandler: () => setGlobalErrorHandler,
184
- setMaxDrainIterations: () => setMaxDrainIterations,
184
+ setMaxDrainIterations: () => setMaxDrainIterations2,
185
185
  show: () => show,
186
186
  signal: () => signal,
187
187
  slot: () => slot,
@@ -213,7 +213,7 @@ __export(index_exports, {
213
213
  time: () => time,
214
214
  title: () => title,
215
215
  tr: () => tr,
216
- track: () => track2,
216
+ track: () => track3,
217
217
  transition: () => transition,
218
218
  trapFocus: () => trapFocus,
219
219
  trustHTML: () => trustHTML,
@@ -221,7 +221,7 @@ __export(index_exports, {
221
221
  u: () => u,
222
222
  ul: () => ul,
223
223
  unregisterComponent: () => unregisterComponent,
224
- untracked: () => untracked,
224
+ untracked: () => untracked2,
225
225
  use: () => use,
226
226
  var_: () => var_,
227
227
  video: () => video,
@@ -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,8 +332,14 @@ 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
- // src/reactivity/track.ts
342
+ // src/reactivity/track-core.ts
329
343
  var _isDev2 = isDev();
330
344
  var POOL_MAX = 4096;
331
345
  var nodePool = [];
@@ -439,6 +453,9 @@ function resumeTracking() {
439
453
  trackingSuspended = false;
440
454
  }
441
455
  }
456
+ function isTrackingSuspended() {
457
+ return trackingSuspended;
458
+ }
442
459
  function untracked(fn) {
443
460
  suspendTracking();
444
461
  try {
@@ -556,6 +573,11 @@ function cleanup(subscriber) {
556
573
  var maxSubscriberRepeats = 50;
557
574
  var maxDrainIterations = 1e6;
558
575
  var drainEpoch = 0;
576
+ function setMaxSubscriberRepeats(n) {
577
+ const prev = maxSubscriberRepeats;
578
+ if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
579
+ return prev;
580
+ }
559
581
  function setMaxDrainIterations(n) {
560
582
  const prev = maxDrainIterations;
561
583
  if (Number.isFinite(n) && n > 0) maxDrainIterations = Math.floor(n);
@@ -709,15 +731,94 @@ function notifySubscribers(signal2) {
709
731
  }
710
732
  }
711
733
  }
734
+ function getSubscriberCount(signal2) {
735
+ return signal2.__sc ?? 0;
736
+ }
737
+ function getSubscriberDeps(subscriber) {
738
+ const sub2 = subscriber;
739
+ const out = [];
740
+ let node = sub2.depsHead ?? null;
741
+ while (node) {
742
+ if (node.sig) out.push(node.sig);
743
+ node = node.subNext;
744
+ }
745
+ return out;
746
+ }
747
+ function forEachSubscriber(signal2, visit) {
748
+ let node = signal2.subsHead ?? null;
749
+ while (node) {
750
+ const s2 = node.sub;
751
+ if (s2) visit(s2);
752
+ node = node.sigNext;
753
+ }
754
+ }
712
755
 
713
- // src/reactivity/bindAttribute.ts
756
+ // src/reactivity/track.ts
714
757
  var _isDev3 = isDev();
758
+ var _runtimeVersion = typeof __SIBU_VERSION__ !== "undefined" ? __SIBU_VERSION__ : "dev";
759
+ var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
760
+ function resolveReactiveApi() {
761
+ const g2 = globalThis;
762
+ const existing = g2[REGISTRY_KEY];
763
+ if (existing) {
764
+ if (_isDev3 && !existing.__dupWarned) {
765
+ existing.__dupWarned = true;
766
+ devWarn(
767
+ `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity still works \u2014 all copies share the first one \u2014 but de-duplicate sibujs in your bundler (e.g. Vite optimizeDeps.exclude: ['sibujs'] or resolve.dedupe: ['sibujs']).`
768
+ );
769
+ }
770
+ return existing;
771
+ }
772
+ const local = {
773
+ suspendTracking,
774
+ resumeTracking,
775
+ isTrackingSuspended,
776
+ untracked,
777
+ retrack,
778
+ track,
779
+ reactiveBinding,
780
+ recordDependency,
781
+ cleanup,
782
+ setMaxSubscriberRepeats,
783
+ setMaxDrainIterations,
784
+ drainNotificationQueue,
785
+ queueSignalNotification,
786
+ notifySubscribers,
787
+ getSubscriberCount,
788
+ getSubscriberDeps,
789
+ forEachSubscriber,
790
+ version: _runtimeVersion
791
+ };
792
+ g2[REGISTRY_KEY] = local;
793
+ return local;
794
+ }
795
+ var API = resolveReactiveApi();
796
+ var suspendTracking2 = API.suspendTracking;
797
+ var resumeTracking2 = API.resumeTracking;
798
+ var isTrackingSuspended2 = API.isTrackingSuspended;
799
+ var untracked2 = API.untracked;
800
+ var retrack2 = API.retrack;
801
+ var track2 = API.track;
802
+ var reactiveBinding2 = API.reactiveBinding;
803
+ var recordDependency2 = API.recordDependency;
804
+ var cleanup2 = API.cleanup;
805
+ var setMaxSubscriberRepeats2 = API.setMaxSubscriberRepeats;
806
+ var setMaxDrainIterations2 = API.setMaxDrainIterations;
807
+ var drainNotificationQueue2 = API.drainNotificationQueue;
808
+ var queueSignalNotification2 = API.queueSignalNotification;
809
+ var notifySubscribers2 = API.notifySubscribers;
810
+ var getSubscriberCount2 = API.getSubscriberCount;
811
+ var getSubscriberDeps2 = API.getSubscriberDeps;
812
+ var forEachSubscriber2 = API.forEachSubscriber;
813
+
814
+ // src/reactivity/bindAttribute.ts
815
+ var _isDev4 = isDev();
715
816
  function setProp(el, key, val) {
716
817
  el[key] = val;
717
818
  }
718
819
  function bindAttribute(el, attr, getter) {
719
820
  if (isEventHandlerAttr(attr)) {
720
- if (_isDev3)
821
+ if (_isDev4)
721
822
  devWarn(
722
823
  `bindAttribute: refusing to bind event-handler attribute "${attr}". Use on:{ ${attr.slice(2)}: fn } instead.`
723
824
  );
@@ -729,7 +830,7 @@ function bindAttribute(el, attr, getter) {
729
830
  try {
730
831
  value = getter();
731
832
  } catch (err) {
732
- if (_isDev3)
833
+ if (_isDev4)
733
834
  devWarn(`bindAttribute: getter for "${attr}" threw: ${err instanceof Error ? err.message : String(err)}`);
734
835
  return;
735
836
  }
@@ -747,10 +848,10 @@ function bindAttribute(el, attr, getter) {
747
848
  if ((attr === "value" || attr === "checked") && attr in el) {
748
849
  setProp(el, attr, attr === "checked" ? Boolean(value) : str);
749
850
  } else {
750
- el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
851
+ el.setAttribute(attr, sanitizeAttributeString(attr, str));
751
852
  }
752
853
  }
753
- return reactiveBinding(commit);
854
+ return reactiveBinding2(commit);
754
855
  }
755
856
  function bindDynamic(el, nameGetter, valueGetter) {
756
857
  let prevName = null;
@@ -759,14 +860,14 @@ function bindDynamic(el, nameGetter, valueGetter) {
759
860
  try {
760
861
  name = typeof nameGetter === "function" ? nameGetter() : nameGetter;
761
862
  } catch (err) {
762
- if (_isDev3) devWarn(`bindDynamic: name getter threw: ${err instanceof Error ? err.message : String(err)}`);
863
+ if (_isDev4) devWarn(`bindDynamic: name getter threw: ${err instanceof Error ? err.message : String(err)}`);
763
864
  return;
764
865
  }
765
866
  let value;
766
867
  try {
767
868
  value = typeof valueGetter === "function" ? valueGetter() : valueGetter;
768
869
  } catch (err) {
769
- if (_isDev3) devWarn(`bindDynamic: value getter threw: ${err instanceof Error ? err.message : String(err)}`);
870
+ if (_isDev4) devWarn(`bindDynamic: value getter threw: ${err instanceof Error ? err.message : String(err)}`);
770
871
  return;
771
872
  }
772
873
  if (isEventHandlerAttr(name)) return;
@@ -777,11 +878,11 @@ function bindDynamic(el, nameGetter, valueGetter) {
777
878
  if ((name === "value" || name === "checked") && name in el) {
778
879
  setProp(el, name, name === "checked" ? Boolean(value) : str);
779
880
  } else {
780
- el.setAttribute(name, isUrlAttribute(name) ? sanitizeUrl(str) : str);
881
+ el.setAttribute(name, sanitizeAttributeString(name, str));
781
882
  }
782
883
  prevName = name;
783
884
  }
784
- const teardown = reactiveBinding(commit);
885
+ const teardown = reactiveBinding2(commit);
785
886
  return () => {
786
887
  teardown();
787
888
  if (prevName !== null) {
@@ -792,7 +893,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
792
893
 
793
894
  // src/core/rendering/dispose.ts
794
895
  var elementDisposers = /* @__PURE__ */ new WeakMap();
795
- var _isDev4 = isDev();
896
+ var _isDev5 = isDev();
796
897
  var activeBindingCount = 0;
797
898
  function registerDisposer(node, teardown) {
798
899
  let disposers = elementDisposers.get(node);
@@ -801,7 +902,7 @@ function registerDisposer(node, teardown) {
801
902
  elementDisposers.set(node, disposers);
802
903
  }
803
904
  disposers.push(teardown);
804
- if (_isDev4) activeBindingCount++;
905
+ if (_isDev5) activeBindingCount++;
805
906
  }
806
907
  function dispose(node) {
807
908
  const stack = [node];
@@ -820,12 +921,12 @@ function dispose(node) {
820
921
  if (disposers) {
821
922
  const snapshot = disposers.slice();
822
923
  elementDisposers.delete(current);
823
- if (_isDev4) activeBindingCount -= snapshot.length;
924
+ if (_isDev5) activeBindingCount -= snapshot.length;
824
925
  for (const d of snapshot) {
825
926
  try {
826
927
  d();
827
928
  } catch (err) {
828
- if (_isDev4 && typeof console !== "undefined") {
929
+ if (_isDev5 && typeof console !== "undefined") {
829
930
  console.warn("[SibuJS] Disposer threw during cleanup:", err);
830
931
  }
831
932
  }
@@ -836,12 +937,12 @@ function dispose(node) {
836
937
  if (!added || added.length === 0) break;
837
938
  const moreSnapshot = added.slice();
838
939
  elementDisposers.delete(current);
839
- if (_isDev4) activeBindingCount -= moreSnapshot.length;
940
+ if (_isDev5) activeBindingCount -= moreSnapshot.length;
840
941
  for (const d of moreSnapshot) {
841
942
  try {
842
943
  d();
843
944
  } catch (err) {
844
- if (_isDev4 && typeof console !== "undefined") {
945
+ if (_isDev5 && typeof console !== "undefined") {
845
946
  console.warn("[SibuJS] Disposer threw during cleanup:", err);
846
947
  }
847
948
  }
@@ -851,7 +952,7 @@ function dispose(node) {
851
952
  }
852
953
  }
853
954
  function checkLeaks(warnThreshold = 0) {
854
- if (!_isDev4) return 0;
955
+ if (!_isDev5) return 0;
855
956
  if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
856
957
  devWarn(
857
958
  `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
@@ -861,7 +962,7 @@ function checkLeaks(warnThreshold = 0) {
861
962
  }
862
963
 
863
964
  // src/reactivity/bindChildNode.ts
864
- var _isDev5 = isDev();
965
+ var _isDev6 = isDev();
865
966
  function bindChildNode(placeholder, getter) {
866
967
  let lastNodes = [];
867
968
  function commit() {
@@ -869,7 +970,7 @@ function bindChildNode(placeholder, getter) {
869
970
  try {
870
971
  result = getter();
871
972
  } catch (err) {
872
- if (_isDev5) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
973
+ if (_isDev6) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
873
974
  return;
874
975
  }
875
976
  if (result == null || typeof result === "boolean") {
@@ -895,7 +996,7 @@ function bindChildNode(placeholder, getter) {
895
996
  if (item == null || typeof item === "boolean") continue;
896
997
  const node = item instanceof Node ? item : document.createTextNode(String(item));
897
998
  if (seen.has(node)) {
898
- if (_isDev5)
999
+ if (_isDev6)
899
1000
  devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
900
1001
  continue;
901
1002
  }
@@ -930,18 +1031,15 @@ function bindChildNode(placeholder, getter) {
930
1031
  }
931
1032
  lastNodes = newNodes;
932
1033
  }
933
- return reactiveBinding(commit);
1034
+ return reactiveBinding2(commit);
934
1035
  }
935
1036
 
936
1037
  // src/core/rendering/tagFactory.ts
937
1038
  var SVG_NS = "http://www.w3.org/2000/svg";
938
- var _isDev6 = isDev();
1039
+ var _isDev7 = isDev();
939
1040
  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
- }
1041
+ function isBlockedTag(tag) {
1042
+ return BLOCKED_TAGS.has(tag.toLowerCase());
945
1043
  }
946
1044
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
947
1045
  "config",
@@ -981,7 +1079,7 @@ function toKebab(prop) {
981
1079
  }
982
1080
  function applyStyle(el, style2) {
983
1081
  if (typeof style2 === "function") {
984
- const teardown = track(() => {
1082
+ const teardown = track2(() => {
985
1083
  el.setAttribute("style", style2());
986
1084
  });
987
1085
  registerDisposer(el, teardown);
@@ -997,7 +1095,7 @@ function applyStyle(el, style2) {
997
1095
  const name = toKebab(prop);
998
1096
  if (typeof val === "function") {
999
1097
  const getter = val;
1000
- const teardown = track(() => {
1098
+ const teardown = track2(() => {
1001
1099
  htmlEl.style.setProperty(name, sanitizeCSSValue(String(getter())));
1002
1100
  });
1003
1101
  registerDisposer(el, teardown);
@@ -1012,7 +1110,7 @@ function applyClass(el, cls) {
1012
1110
  return;
1013
1111
  }
1014
1112
  if (typeof cls === "function") {
1015
- const teardown = track(() => {
1113
+ const teardown = track2(() => {
1016
1114
  el.setAttribute("class", cls());
1017
1115
  });
1018
1116
  registerDisposer(el, teardown);
@@ -1039,7 +1137,7 @@ function applyClass(el, cls) {
1039
1137
  }
1040
1138
  el.setAttribute("class", r);
1041
1139
  };
1042
- const teardown = track(update);
1140
+ const teardown = track2(update);
1043
1141
  registerDisposer(el, teardown);
1044
1142
  } else {
1045
1143
  el.setAttribute("class", result);
@@ -1096,8 +1194,11 @@ function appendChildren(el, nodes) {
1096
1194
  }
1097
1195
  }
1098
1196
  var tagFactory = (tag, ns) => {
1197
+ const blocked = isBlockedTag(tag);
1099
1198
  return (first, second) => {
1100
- validateTagName(tag);
1199
+ if (blocked) {
1200
+ throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1201
+ }
1101
1202
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
1102
1203
  if (first === void 0) return el;
1103
1204
  if (typeof first === "string") {
@@ -1106,7 +1207,7 @@ var tagFactory = (tag, ns) => {
1106
1207
  appendChildren(el, second);
1107
1208
  return el;
1108
1209
  }
1109
- if (_isDev6 && looksLikeClassList(first)) {
1210
+ if (_isDev7 && looksLikeClassList(first)) {
1110
1211
  devWarn(
1111
1212
  `tagFactory: lone string "${first}" looks like a class list but is being rendered as TEXT. For a class, use ${tag}({ class: "${first}" }) \u2014 or ${tag}("${first}", children) to set the class AND add children.`
1112
1213
  );
@@ -1127,7 +1228,7 @@ var tagFactory = (tag, ns) => {
1127
1228
  if (pClass != null) applyClass(el, pClass);
1128
1229
  const pId = props.id;
1129
1230
  if (pId != null) {
1130
- if (_isDev6 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1231
+ if (_isDev7 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1131
1232
  devWarn(
1132
1233
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
1133
1234
  );
@@ -1142,7 +1243,7 @@ var tagFactory = (tag, ns) => {
1142
1243
  const handler = pOn[ev];
1143
1244
  if (typeof handler === "function") {
1144
1245
  el.addEventListener(ev, handler);
1145
- } else if (_isDev6) {
1246
+ } else if (_isDev7) {
1146
1247
  devWarn(
1147
1248
  `tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
1148
1249
  );
@@ -1167,7 +1268,6 @@ var tagFactory = (tag, ns) => {
1167
1268
  default: {
1168
1269
  const value = props[key];
1169
1270
  if (value == null) continue;
1170
- const lkey = key.toLowerCase();
1171
1271
  if (isEventHandlerAttr(key)) continue;
1172
1272
  if (typeof value === "function") {
1173
1273
  registerDisposer(el, bindAttribute(el, key, value));
@@ -1180,14 +1280,7 @@ var tagFactory = (tag, ns) => {
1180
1280
  el.removeAttribute(key);
1181
1281
  }
1182
1282
  } 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
- }
1283
+ el.setAttribute(key, sanitizeAttributeString(key, String(value)));
1191
1284
  }
1192
1285
  }
1193
1286
  }
@@ -1263,7 +1356,7 @@ var area = tagFactory("area");
1263
1356
  var audio = tagFactory("audio");
1264
1357
  var img = tagFactory("img");
1265
1358
  var map = tagFactory("map");
1266
- var track2 = tagFactory("track");
1359
+ var track3 = tagFactory("track");
1267
1360
  var video = tagFactory("video");
1268
1361
  var embed = tagFactory("embed");
1269
1362
  var iframe = tagFactory("iframe");
@@ -1339,7 +1432,7 @@ var marquee = tagFactory("marquee");
1339
1432
  var customElement = (tagName) => tagFactory(tagName);
1340
1433
 
1341
1434
  // src/core/rendering/htm.ts
1342
- var _isDev7 = isDev();
1435
+ var _isDev8 = isDev();
1343
1436
  var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
1344
1437
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1345
1438
  "area",
@@ -1635,7 +1728,7 @@ function executeElement(tmpl, values) {
1635
1728
  const fn = values[attr.idx];
1636
1729
  if (typeof fn === "function") {
1637
1730
  el.addEventListener(attr.name, fn);
1638
- } else if (_isDev7) {
1731
+ } else if (_isDev8) {
1639
1732
  devWarn(
1640
1733
  `html: on:${attr.name} handler is not a function (got ${typeof fn}). Event listener was not attached.`
1641
1734
  );
@@ -1763,7 +1856,7 @@ function mount(component, container) {
1763
1856
  }
1764
1857
 
1765
1858
  // src/core/rendering/each.ts
1766
- var _isDev8 = isDev();
1859
+ var _isDev9 = isDev();
1767
1860
  function resolveNodeChild(child) {
1768
1861
  if (typeof child === "function") {
1769
1862
  return resolveNodeChild(child());
@@ -1848,7 +1941,7 @@ function each(getArray, render, options) {
1848
1941
  workMap.clear();
1849
1942
  keyIndexMap.clear();
1850
1943
  for (let i2 = 0; i2 < newLen; i2++) {
1851
- if (_isDev8 && keyIndexMap.has(newKeys[i2])) {
1944
+ if (_isDev9 && keyIndexMap.has(newKeys[i2])) {
1852
1945
  devWarn(
1853
1946
  `each: duplicate key "${String(newKeys[i2])}" at index ${i2} (first seen at ${keyIndexMap.get(newKeys[i2])}). Keys must be unique \u2014 duplicates cause rows to be dropped or mis-ordered.`
1854
1947
  );
@@ -1863,12 +1956,12 @@ function each(getArray, render, options) {
1863
1956
  node = existing;
1864
1957
  } else {
1865
1958
  const itemKey = key;
1866
- const itemGetter = () => untracked(() => getArray()[keyIndexMap.get(itemKey)]);
1959
+ const itemGetter = () => untracked2(() => getArray()[keyIndexMap.get(itemKey)]);
1867
1960
  const indexGetter = () => keyIndexMap.get(itemKey);
1868
1961
  try {
1869
1962
  node = resolveNodeChild(render(itemGetter, indexGetter));
1870
1963
  } catch (err) {
1871
- if (_isDev8) {
1964
+ if (_isDev9) {
1872
1965
  devWarn(
1873
1966
  `each: render threw for item at index ${i2} (key="${newKeys[i2]}"): ${err instanceof Error ? err.message : String(err)}`
1874
1967
  );
@@ -1882,7 +1975,7 @@ function each(getArray, render, options) {
1882
1975
  target.dispatchEvent(
1883
1976
  new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
1884
1977
  );
1885
- } else if (_isDev8) {
1978
+ } else if (_isDev9) {
1886
1979
  devWarn(`each: error not surfaced \u2014 anchor detached: ${errorObj.message}`);
1887
1980
  }
1888
1981
  } catch {
@@ -1956,7 +2049,7 @@ function each(getArray, render, options) {
1956
2049
  workMap = tmp;
1957
2050
  initialized = true;
1958
2051
  };
1959
- const untrack = track(update);
2052
+ const untrack = track2(update);
1960
2053
  registerDisposer(anchor, untrack);
1961
2054
  if (!initialized) {
1962
2055
  queueMicrotask(() => {
@@ -2069,7 +2162,7 @@ function DynamicComponent(is) {
2069
2162
  }
2070
2163
  container.replaceChildren(el);
2071
2164
  }
2072
- const untrack = track(render);
2165
+ const untrack = track2(render);
2073
2166
  registerDisposer(container, untrack);
2074
2167
  return container;
2075
2168
  }
@@ -2084,7 +2177,7 @@ function show(condition, element) {
2084
2177
  const update = () => {
2085
2178
  element.style.display = condition() ? "" : "none";
2086
2179
  };
2087
- registerDisposer(element, track(update));
2180
+ registerDisposer(element, track2(update));
2088
2181
  return element;
2089
2182
  }
2090
2183
  function when(condition, thenBranch, elseBranch) {
@@ -2111,7 +2204,7 @@ function when(condition, thenBranch, elseBranch) {
2111
2204
  }
2112
2205
  initialized = true;
2113
2206
  };
2114
- registerDisposer(anchor, track(update));
2207
+ registerDisposer(anchor, track2(update));
2115
2208
  if (!initialized) {
2116
2209
  queueMicrotask(() => {
2117
2210
  if (!initialized && anchor.parentNode) update();
@@ -2146,7 +2239,7 @@ function match(value, cases, fallback) {
2146
2239
  }
2147
2240
  initialized = true;
2148
2241
  };
2149
- registerDisposer(anchor, track(update));
2242
+ registerDisposer(anchor, track2(update));
2150
2243
  if (!initialized) {
2151
2244
  queueMicrotask(() => {
2152
2245
  if (!initialized && anchor.parentNode) update();
@@ -2215,7 +2308,7 @@ function KeepAlive(activeKey, cases, options) {
2215
2308
  currentNode = node;
2216
2309
  initialized = true;
2217
2310
  };
2218
- const untrack = track(update);
2311
+ const untrack = track2(update);
2219
2312
  if (!initialized) {
2220
2313
  queueMicrotask(() => {
2221
2314
  if (!initialized && anchor.parentNode) update();
@@ -2237,9 +2330,9 @@ function KeepAlive(activeKey, cases, options) {
2237
2330
 
2238
2331
  // src/core/rendering/action.ts
2239
2332
  function action(element, actionFn, param2) {
2240
- const cleanup2 = actionFn(element, param2);
2241
- if (typeof cleanup2 === "function") {
2242
- registerDisposer(element, cleanup2);
2333
+ const cleanup3 = actionFn(element, param2);
2334
+ if (typeof cleanup3 === "function") {
2335
+ registerDisposer(element, cleanup3);
2243
2336
  }
2244
2337
  }
2245
2338
  var clickOutside = (element, callback) => {
@@ -2373,7 +2466,7 @@ function __resetIdCounter() {
2373
2466
  // src/reactivity/batch.ts
2374
2467
  var batchDepth = 0;
2375
2468
  var pendingSignals = /* @__PURE__ */ new Set();
2376
- function batch(fn) {
2469
+ function batchImpl(fn) {
2377
2470
  batchDepth++;
2378
2471
  try {
2379
2472
  return fn();
@@ -2384,28 +2477,45 @@ function batch(fn) {
2384
2477
  }
2385
2478
  }
2386
2479
  }
2387
- function enqueueBatchedSignal(signal2) {
2480
+ function enqueueBatchedSignalImpl(signal2) {
2388
2481
  if (batchDepth === 0) return false;
2389
2482
  pendingSignals.add(signal2);
2390
2483
  return true;
2391
2484
  }
2392
- function isBatching() {
2485
+ function isBatchingImpl() {
2393
2486
  return batchDepth > 0;
2394
2487
  }
2395
2488
  function flushBatch() {
2396
2489
  try {
2397
2490
  for (const signal2 of pendingSignals) {
2398
- queueSignalNotification(signal2);
2491
+ queueSignalNotification2(signal2);
2399
2492
  }
2400
2493
  } finally {
2401
2494
  pendingSignals.clear();
2402
2495
  }
2403
- drainNotificationQueue();
2496
+ drainNotificationQueue2();
2404
2497
  }
2498
+ var BATCH_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.batch.v1");
2499
+ function resolveBatchApi() {
2500
+ const g2 = globalThis;
2501
+ const existing = g2[BATCH_REGISTRY_KEY];
2502
+ if (existing) return existing;
2503
+ const local = {
2504
+ batch: batchImpl,
2505
+ enqueueBatchedSignal: enqueueBatchedSignalImpl,
2506
+ isBatching: isBatchingImpl
2507
+ };
2508
+ g2[BATCH_REGISTRY_KEY] = local;
2509
+ return local;
2510
+ }
2511
+ var API2 = resolveBatchApi();
2512
+ var batch = API2.batch;
2513
+ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
2514
+ var isBatching = API2.isBatching;
2405
2515
 
2406
2516
  // src/core/signals/signal.ts
2407
2517
  var _g = globalThis;
2408
- var _isDev9 = isDev();
2518
+ var _isDev10 = isDev();
2409
2519
  function signal(initial, options) {
2410
2520
  const state = {
2411
2521
  value: initial,
@@ -2416,11 +2526,11 @@ function signal(initial, options) {
2416
2526
  __activeNode: null,
2417
2527
  __name: void 0
2418
2528
  };
2419
- const debugName = _isDev9 ? options?.name : void 0;
2529
+ const debugName = _isDev10 ? options?.name : void 0;
2420
2530
  const equalsFn = options?.equals;
2421
2531
  if (debugName) state.__name = debugName;
2422
2532
  function get() {
2423
- recordDependency(state);
2533
+ recordDependency2(state);
2424
2534
  return state.value;
2425
2535
  }
2426
2536
  get.__signal = state;
@@ -2433,15 +2543,15 @@ function signal(initial, options) {
2433
2543
  if (equalsFn(prev, newValue)) return;
2434
2544
  state.value = newValue;
2435
2545
  state.__v++;
2436
- if (_isDev9) {
2546
+ if (_isDev10) {
2437
2547
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2438
2548
  if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
2439
2549
  }
2440
2550
  if (!enqueueBatchedSignal(state)) {
2441
- notifySubscribers(state);
2551
+ notifySubscribers2(state);
2442
2552
  }
2443
2553
  };
2444
- } else if (_isDev9) {
2554
+ } else if (_isDev10) {
2445
2555
  set = (next) => {
2446
2556
  const prev = state.value;
2447
2557
  const newValue = typeof next === "function" ? next(prev) : next;
@@ -2451,7 +2561,7 @@ function signal(initial, options) {
2451
2561
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2452
2562
  if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
2453
2563
  if (!enqueueBatchedSignal(state)) {
2454
- notifySubscribers(state);
2564
+ notifySubscribers2(state);
2455
2565
  }
2456
2566
  };
2457
2567
  } else {
@@ -2462,11 +2572,11 @@ function signal(initial, options) {
2462
2572
  state.value = newValue;
2463
2573
  state.__v++;
2464
2574
  if (!enqueueBatchedSignal(state)) {
2465
- notifySubscribers(state);
2575
+ notifySubscribers2(state);
2466
2576
  }
2467
2577
  };
2468
2578
  }
2469
- if (_isDev9) {
2579
+ if (_isDev10) {
2470
2580
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2471
2581
  if (hook) hook.emit("signal:create", { signal: state, name: debugName, getter: get, initial });
2472
2582
  }
@@ -2555,11 +2665,11 @@ function on(deps, handler) {
2555
2665
  if (first) {
2556
2666
  first = false;
2557
2667
  prev = value;
2558
- untracked(() => handler(value, void 0));
2668
+ untracked2(() => handler(value, void 0));
2559
2669
  } else {
2560
2670
  const p2 = prev;
2561
2671
  prev = value;
2562
- untracked(() => handler(value, p2));
2672
+ untracked2(() => handler(value, p2));
2563
2673
  }
2564
2674
  };
2565
2675
  }
@@ -2581,7 +2691,7 @@ function drainReruns(ctx) {
2581
2691
  do {
2582
2692
  ctx.rerunPending = false;
2583
2693
  if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2584
- retrack(ctx.bodyFn, ctx.subscriber);
2694
+ retrack2(ctx.bodyFn, ctx.subscriber);
2585
2695
  } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
2586
2696
  if (ctx.rerunPending) {
2587
2697
  ctx.rerunPending = false;
@@ -2610,7 +2720,7 @@ function disposeEffect(ctx) {
2610
2720
  }
2611
2721
  }
2612
2722
  try {
2613
- cleanup(ctx.subscriber);
2723
+ cleanup2(ctx.subscriber);
2614
2724
  } catch (err) {
2615
2725
  if (typeof console !== "undefined") {
2616
2726
  console.warn("[SibuJS effect] dispose threw:", err);
@@ -2655,7 +2765,7 @@ function effect(effectFn, options) {
2655
2765
  try {
2656
2766
  ctx.rerunPending = false;
2657
2767
  if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2658
- retrack(ctx.bodyFn, sub2);
2768
+ retrack2(ctx.bodyFn, sub2);
2659
2769
  if (ctx.rerunPending) drainReruns(ctx);
2660
2770
  } finally {
2661
2771
  ctx.running = false;
@@ -2671,7 +2781,7 @@ function effect(effectFn, options) {
2671
2781
  ctx.subscriber = sub2;
2672
2782
  ctx.running = true;
2673
2783
  try {
2674
- retrack(ctx.bodyFn, ctx.subscriber);
2784
+ retrack2(ctx.bodyFn, ctx.subscriber);
2675
2785
  if (ctx.rerunPending) drainReruns(ctx);
2676
2786
  } finally {
2677
2787
  ctx.running = false;
@@ -2704,7 +2814,7 @@ function derived(getter, options) {
2704
2814
  cs._d = false;
2705
2815
  cs._init = true;
2706
2816
  };
2707
- track(() => {
2817
+ track2(() => {
2708
2818
  let threw = true;
2709
2819
  try {
2710
2820
  cs._v = getter();
@@ -2723,12 +2833,12 @@ function derived(getter, options) {
2723
2833
  `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
2724
2834
  );
2725
2835
  }
2726
- if (trackingSuspended) {
2836
+ if (isTrackingSuspended2()) {
2727
2837
  if (cs._d) {
2728
2838
  const prev = cs._v;
2729
2839
  evaluating = true;
2730
2840
  try {
2731
- retrack(recompute, markDirty);
2841
+ retrack2(recompute, markDirty);
2732
2842
  if (!Object.is(prev, cs._v)) cs.__v++;
2733
2843
  } finally {
2734
2844
  evaluating = false;
@@ -2736,12 +2846,12 @@ function derived(getter, options) {
2736
2846
  }
2737
2847
  return cs._v;
2738
2848
  }
2739
- recordDependency(cs);
2849
+ recordDependency2(cs);
2740
2850
  if (cs._d) {
2741
2851
  const oldValue = cs._v;
2742
2852
  evaluating = true;
2743
2853
  try {
2744
- retrack(recompute, markDirty);
2854
+ retrack2(recompute, markDirty);
2745
2855
  if (!Object.is(oldValue, cs._v)) cs.__v++;
2746
2856
  } finally {
2747
2857
  evaluating = false;
@@ -2777,11 +2887,17 @@ function watch(getter, callback) {
2777
2887
  return;
2778
2888
  }
2779
2889
  if (!Object.is(newValue, oldValue)) {
2780
- callback(newValue, oldValue);
2890
+ const prev = oldValue;
2781
2891
  oldValue = newValue;
2892
+ suspendTracking2();
2893
+ try {
2894
+ callback(newValue, prev);
2895
+ } finally {
2896
+ resumeTracking2();
2897
+ }
2782
2898
  }
2783
2899
  };
2784
- const teardown = track(subscriber);
2900
+ const teardown = track2(subscriber);
2785
2901
  return teardown;
2786
2902
  }
2787
2903
 
@@ -2844,7 +2960,12 @@ function store(initialState) {
2844
2960
  first = false;
2845
2961
  return;
2846
2962
  }
2847
- callback(snapshot);
2963
+ suspendTracking2();
2964
+ try {
2965
+ callback(snapshot);
2966
+ } finally {
2967
+ resumeTracking2();
2968
+ }
2848
2969
  });
2849
2970
  };
2850
2971
  const subscribeKey = (key, callback) => {
@@ -2860,7 +2981,12 @@ function store(initialState) {
2860
2981
  if (!Object.is(current, prev)) {
2861
2982
  const oldPrev = prev;
2862
2983
  prev = current;
2863
- callback(current, oldPrev);
2984
+ suspendTracking2();
2985
+ try {
2986
+ callback(current, oldPrev);
2987
+ } finally {
2988
+ resumeTracking2();
2989
+ }
2864
2990
  }
2865
2991
  });
2866
2992
  };
@@ -2961,11 +3087,11 @@ function reactiveArray(initial = []) {
2961
3087
  function notify() {
2962
3088
  snapshot = null;
2963
3089
  if (!enqueueBatchedSignal(signal2)) {
2964
- notifySubscribers(signal2);
3090
+ notifySubscribers2(signal2);
2965
3091
  }
2966
3092
  }
2967
3093
  function get() {
2968
- recordDependency(signal2);
3094
+ recordDependency2(signal2);
2969
3095
  if (snapshot === null) {
2970
3096
  const copy = data2.slice();
2971
3097
  snapshot = Object.freeze(copy);
@@ -3221,9 +3347,9 @@ function safeCall(cb, hookName) {
3221
3347
  }
3222
3348
  }
3223
3349
  function runMountCallback(callback, hookName, element) {
3224
- const cleanup2 = safeCall(callback, hookName);
3225
- if (typeof cleanup2 === "function" && element) {
3226
- registerDisposer(element, cleanup2);
3350
+ const cleanup3 = safeCall(callback, hookName);
3351
+ if (typeof cleanup3 === "function" && element) {
3352
+ registerDisposer(element, cleanup3);
3227
3353
  }
3228
3354
  }
3229
3355
  var mountWatchers = /* @__PURE__ */ new WeakMap();
@@ -3539,7 +3665,7 @@ function defer(getter) {
3539
3665
  }
3540
3666
  });
3541
3667
  };
3542
- const teardown = track(() => {
3668
+ const teardown = track2(() => {
3543
3669
  latest = getter();
3544
3670
  schedule();
3545
3671
  });
@@ -3691,14 +3817,14 @@ function Suspense({ nodes, fallback }) {
3691
3817
  }
3692
3818
 
3693
3819
  // src/platform/ssr.ts
3694
- var _isDev10 = isDev();
3820
+ var _isDev11 = isDev();
3695
3821
  function trustHTML(html3) {
3696
3822
  return html3;
3697
3823
  }
3698
3824
  var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
3699
3825
 
3700
3826
  // src/components/ErrorDisplay.ts
3701
- var _isDev11 = isDev();
3827
+ var _isDev12 = isDev();
3702
3828
  var STYLES = `
3703
3829
  .sibu-error-display {
3704
3830
  border: 1px solid var(--sibu-err-border, #e5484d);
@@ -4009,7 +4135,7 @@ function ErrorDisplay(props) {
4009
4135
  injectStyles();
4010
4136
  const severity = props.severity ?? "error";
4011
4137
  const normalized = normalizeError(props.error);
4012
- const showDetails = props.alwaysShowDetails ?? _isDev11;
4138
+ const showDetails = props.alwaysShowDetails ?? _isDev12;
4013
4139
  const headline = props.title ?? normalized.message;
4014
4140
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
4015
4141
  const [copyLabel, setCopyLabel] = signal("Copy");
@@ -4410,7 +4536,12 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
4410
4536
  } else if (collected.length > 1) {
4411
4537
  const Agg = globalThis.AggregateError;
4412
4538
  handleError(
4413
- Agg ? new Agg(collected, `${collected.length} pre-mount errors caught by ErrorBoundary`) : new Error(collected.map((e) => e.message).join("; "))
4539
+ Agg ? new Agg(collected, `${collected.length} pre-mount errors caught by ErrorBoundary`) : (
4540
+ // AggregateError is ES2021 and present in every supported runtime
4541
+ // (matches the browserslist targets), so this fallback is defensive.
4542
+ /* v8 ignore next */
4543
+ new Error(collected.map((e) => e.message).join("; "))
4544
+ )
4414
4545
  );
4415
4546
  }
4416
4547
  return void 0;