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/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",
@@ -189,7 +203,7 @@ function isSafeAttrName(name) {
189
203
  return SAFE_ATTR_NAME.test(name);
190
204
  }
191
205
  function ssrErrorComment(err) {
192
- if (_isDev8) {
206
+ if (_isDev9) {
193
207
  const msg = escapeHtml(err instanceof Error ? err.message : String(err));
194
208
  return `<!--SSR error: ${safeCommentText(msg)}-->`;
195
209
  }
@@ -219,10 +233,10 @@ function renderToString(element) {
219
233
  }
220
234
  const tag = element.tagName.toLowerCase();
221
235
  if (tag === "script" || tag === "style") {
222
- return _isDev8 ? `<!--ssr:${tag}-stripped-->` : "";
236
+ return _isDev9 ? `<!--ssr:${tag}-stripped-->` : "";
223
237
  }
224
238
  if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
225
- return _isDev8 ? "<!--ssr:invalid-tag-->" : "";
239
+ return _isDev9 ? "<!--ssr:invalid-tag-->" : "";
226
240
  }
227
241
  let html2 = `<${tag}`;
228
242
  for (const attr of Array.from(element.attributes)) {
@@ -263,7 +277,7 @@ function hydrate(component, container, options = {}) {
263
277
  const first = mismatches[0];
264
278
  if (options.onMismatch) {
265
279
  options.onMismatch(first);
266
- } else if (_isDev8) {
280
+ } else if (_isDev9) {
267
281
  console.warn(
268
282
  `[SibuJS hydration] ${first.message}
269
283
  at ${first.path}
@@ -458,11 +472,11 @@ async function* renderToStream(element) {
458
472
  }
459
473
  const tag = element.tagName.toLowerCase();
460
474
  if (tag === "script" || tag === "style") {
461
- if (_isDev8) yield `<!--ssr:${tag}-stripped-->`;
475
+ if (_isDev9) yield `<!--ssr:${tag}-stripped-->`;
462
476
  return;
463
477
  }
464
478
  if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
465
- if (_isDev8) yield "<!--ssr:invalid-tag-->";
479
+ if (_isDev9) yield "<!--ssr:invalid-tag-->";
466
480
  return;
467
481
  }
468
482
  let openTag = `<${tag}`;
@@ -565,7 +579,7 @@ function hydrateProgressively(container, islands, options) {
565
579
  }
566
580
  container.setAttribute("data-sibu-hydrated", "progressive");
567
581
  return () => {
568
- for (const cleanup2 of cleanups) cleanup2();
582
+ for (const cleanup3 of cleanups) cleanup3();
569
583
  };
570
584
  }
571
585
  function resetSSRState() {
@@ -594,7 +608,7 @@ function ssrSuspense(props) {
594
608
  },
595
609
  (err) => {
596
610
  if (timer) clearTimeout(timer);
597
- if (_isDev8) console.warn("[SibuJS SSR] ssrSuspense rejected:", err);
611
+ if (_isDev9) console.warn("[SibuJS SSR] ssrSuspense rejected:", err);
598
612
  return { id, html: fallbackHtml };
599
613
  }
600
614
  );
@@ -637,7 +651,7 @@ function serializeState(state, nonce, options) {
637
651
  }
638
652
  function deserializeState(validate) {
639
653
  if (typeof window === "undefined") return void 0;
640
- if (_isDev8 && !validate) {
654
+ if (_isDev9 && !validate) {
641
655
  console.warn(
642
656
  "[SibuJS SSR] deserializeState() called without a validate guard \u2014 tampered SSR payloads will not be detected."
643
657
  );
@@ -654,14 +668,14 @@ function escapeHtml(str) {
654
668
  function escapeAttr(str) {
655
669
  return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
656
670
  }
657
- var _isDev8, SAFE_ATTR_NAME, URL_ATTRS, VOID_ELEMENTS, SAFE_ID, SAFE_SUSPENSE_ID, SSR_DATA_ATTR, DEFAULT_MAX_SSR_BYTES;
671
+ var _isDev9, SAFE_ATTR_NAME, URL_ATTRS, VOID_ELEMENTS, SAFE_ID, SAFE_SUSPENSE_ID, SSR_DATA_ATTR, DEFAULT_MAX_SSR_BYTES;
658
672
  var init_ssr = __esm({
659
673
  "src/platform/ssr.ts"() {
660
674
  "use strict";
661
675
  init_dev();
662
676
  init_ssr_context();
663
677
  init_sanitize();
664
- _isDev8 = isDev();
678
+ _isDev9 = isDev();
665
679
  SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
666
680
  URL_ATTRS = /* @__PURE__ */ new Set([
667
681
  "href",
@@ -790,6 +804,9 @@ init_sanitize();
790
804
 
791
805
  // src/reactivity/track.ts
792
806
  init_dev();
807
+
808
+ // src/reactivity/track-core.ts
809
+ init_dev();
793
810
  var _isDev2 = isDev();
794
811
  var POOL_MAX = 4096;
795
812
  var nodePool = [];
@@ -873,6 +890,7 @@ function unlinkSub(node) {
873
890
  else sub2.depsTail = prev;
874
891
  }
875
892
  var currentSubscriber = null;
893
+ var suspendSavedSub = null;
876
894
  var notifyDepth = 0;
877
895
  var pendingQueue = [];
878
896
  var pendingSet = /* @__PURE__ */ new Set();
@@ -884,6 +902,35 @@ function safeInvoke(sub2) {
884
902
  if (_isDev2) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
885
903
  }
886
904
  }
905
+ var suspendDepth = 0;
906
+ var trackingSuspended = false;
907
+ function suspendTracking() {
908
+ if (suspendDepth === 0) {
909
+ suspendSavedSub = currentSubscriber;
910
+ currentSubscriber = null;
911
+ trackingSuspended = true;
912
+ }
913
+ suspendDepth++;
914
+ }
915
+ function resumeTracking() {
916
+ suspendDepth--;
917
+ if (suspendDepth === 0) {
918
+ currentSubscriber = suspendSavedSub;
919
+ suspendSavedSub = null;
920
+ trackingSuspended = false;
921
+ }
922
+ }
923
+ function isTrackingSuspended() {
924
+ return trackingSuspended;
925
+ }
926
+ function untracked(fn) {
927
+ suspendTracking();
928
+ try {
929
+ return fn();
930
+ } finally {
931
+ resumeTracking();
932
+ }
933
+ }
887
934
  var subscriberEpochCounter = 0;
888
935
  function retrack(effectFn, subscriber) {
889
936
  const prev = currentSubscriber;
@@ -993,6 +1040,16 @@ function cleanup(subscriber) {
993
1040
  var maxSubscriberRepeats = 50;
994
1041
  var maxDrainIterations = 1e6;
995
1042
  var drainEpoch = 0;
1043
+ function setMaxSubscriberRepeats(n) {
1044
+ const prev = maxSubscriberRepeats;
1045
+ if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
1046
+ return prev;
1047
+ }
1048
+ function setMaxDrainIterations(n) {
1049
+ const prev = maxDrainIterations;
1050
+ if (Number.isFinite(n) && n > 0) maxDrainIterations = Math.floor(n);
1051
+ return prev;
1052
+ }
996
1053
  function tickRepeat(sub2) {
997
1054
  const s2 = sub2;
998
1055
  if (s2._runEpoch !== drainEpoch) {
@@ -1034,6 +1091,20 @@ function drainQueue() {
1034
1091
  safeInvoke(sub2);
1035
1092
  }
1036
1093
  }
1094
+ function drainNotificationQueue() {
1095
+ if (notifyDepth > 0) return;
1096
+ notifyDepth++;
1097
+ drainEpoch++;
1098
+ try {
1099
+ drainQueue();
1100
+ } finally {
1101
+ notifyDepth--;
1102
+ if (notifyDepth === 0) {
1103
+ pendingQueue.length = 0;
1104
+ pendingSet.clear();
1105
+ }
1106
+ }
1107
+ }
1037
1108
  function propagateDirty(sub2) {
1038
1109
  sub2();
1039
1110
  const rootSig = sub2._sig;
@@ -1066,6 +1137,22 @@ function propagateDirty(sub2) {
1066
1137
  }
1067
1138
  }
1068
1139
  }
1140
+ function queueSignalNotification(signal2) {
1141
+ const sig = signal2;
1142
+ let node = sig.subsHead ?? null;
1143
+ while (node) {
1144
+ const s2 = node.sub;
1145
+ if (s2) {
1146
+ if (s2._c) {
1147
+ propagateDirty(s2);
1148
+ } else if (!pendingSet.has(s2)) {
1149
+ pendingSet.add(s2);
1150
+ pendingQueue.push(s2);
1151
+ }
1152
+ }
1153
+ node = node.sigNext;
1154
+ }
1155
+ }
1069
1156
  function notifySubscribers(signal2) {
1070
1157
  const sig = signal2;
1071
1158
  const head2 = sig.subsHead;
@@ -1111,15 +1198,94 @@ function notifySubscribers(signal2) {
1111
1198
  }
1112
1199
  }
1113
1200
  }
1201
+ function getSubscriberCount(signal2) {
1202
+ return signal2.__sc ?? 0;
1203
+ }
1204
+ function getSubscriberDeps(subscriber) {
1205
+ const sub2 = subscriber;
1206
+ const out = [];
1207
+ let node = sub2.depsHead ?? null;
1208
+ while (node) {
1209
+ if (node.sig) out.push(node.sig);
1210
+ node = node.subNext;
1211
+ }
1212
+ return out;
1213
+ }
1214
+ function forEachSubscriber(signal2, visit) {
1215
+ let node = signal2.subsHead ?? null;
1216
+ while (node) {
1217
+ const s2 = node.sub;
1218
+ if (s2) visit(s2);
1219
+ node = node.sigNext;
1220
+ }
1221
+ }
1114
1222
 
1115
- // src/reactivity/bindAttribute.ts
1223
+ // src/reactivity/track.ts
1116
1224
  var _isDev3 = isDev();
1225
+ var _runtimeVersion = typeof __SIBU_VERSION__ !== "undefined" ? __SIBU_VERSION__ : "dev";
1226
+ var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
1227
+ function resolveReactiveApi() {
1228
+ const g2 = globalThis;
1229
+ const existing = g2[REGISTRY_KEY];
1230
+ if (existing) {
1231
+ if (_isDev3 && !existing.__dupWarned) {
1232
+ existing.__dupWarned = true;
1233
+ devWarn(
1234
+ `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']).`
1235
+ );
1236
+ }
1237
+ return existing;
1238
+ }
1239
+ const local = {
1240
+ suspendTracking,
1241
+ resumeTracking,
1242
+ isTrackingSuspended,
1243
+ untracked,
1244
+ retrack,
1245
+ track,
1246
+ reactiveBinding,
1247
+ recordDependency,
1248
+ cleanup,
1249
+ setMaxSubscriberRepeats,
1250
+ setMaxDrainIterations,
1251
+ drainNotificationQueue,
1252
+ queueSignalNotification,
1253
+ notifySubscribers,
1254
+ getSubscriberCount,
1255
+ getSubscriberDeps,
1256
+ forEachSubscriber,
1257
+ version: _runtimeVersion
1258
+ };
1259
+ g2[REGISTRY_KEY] = local;
1260
+ return local;
1261
+ }
1262
+ var API = resolveReactiveApi();
1263
+ var suspendTracking2 = API.suspendTracking;
1264
+ var resumeTracking2 = API.resumeTracking;
1265
+ var isTrackingSuspended2 = API.isTrackingSuspended;
1266
+ var untracked2 = API.untracked;
1267
+ var retrack2 = API.retrack;
1268
+ var track2 = API.track;
1269
+ var reactiveBinding2 = API.reactiveBinding;
1270
+ var recordDependency2 = API.recordDependency;
1271
+ var cleanup2 = API.cleanup;
1272
+ var setMaxSubscriberRepeats2 = API.setMaxSubscriberRepeats;
1273
+ var setMaxDrainIterations2 = API.setMaxDrainIterations;
1274
+ var drainNotificationQueue2 = API.drainNotificationQueue;
1275
+ var queueSignalNotification2 = API.queueSignalNotification;
1276
+ var notifySubscribers2 = API.notifySubscribers;
1277
+ var getSubscriberCount2 = API.getSubscriberCount;
1278
+ var getSubscriberDeps2 = API.getSubscriberDeps;
1279
+ var forEachSubscriber2 = API.forEachSubscriber;
1280
+
1281
+ // src/reactivity/bindAttribute.ts
1282
+ var _isDev4 = isDev();
1117
1283
  function setProp(el, key, val) {
1118
1284
  el[key] = val;
1119
1285
  }
1120
1286
  function bindAttribute(el, attr, getter) {
1121
1287
  if (isEventHandlerAttr(attr)) {
1122
- if (_isDev3)
1288
+ if (_isDev4)
1123
1289
  devWarn(
1124
1290
  `bindAttribute: refusing to bind event-handler attribute "${attr}". Use on:{ ${attr.slice(2)}: fn } instead.`
1125
1291
  );
@@ -1131,7 +1297,7 @@ function bindAttribute(el, attr, getter) {
1131
1297
  try {
1132
1298
  value = getter();
1133
1299
  } catch (err) {
1134
- if (_isDev3)
1300
+ if (_isDev4)
1135
1301
  devWarn(`bindAttribute: getter for "${attr}" threw: ${err instanceof Error ? err.message : String(err)}`);
1136
1302
  return;
1137
1303
  }
@@ -1149,10 +1315,10 @@ function bindAttribute(el, attr, getter) {
1149
1315
  if ((attr === "value" || attr === "checked") && attr in el) {
1150
1316
  setProp(el, attr, attr === "checked" ? Boolean(value) : str);
1151
1317
  } else {
1152
- el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
1318
+ el.setAttribute(attr, sanitizeAttributeString(attr, str));
1153
1319
  }
1154
1320
  }
1155
- return reactiveBinding(commit);
1321
+ return reactiveBinding2(commit);
1156
1322
  }
1157
1323
 
1158
1324
  // src/reactivity/bindChildNode.ts
@@ -1161,7 +1327,7 @@ init_dev();
1161
1327
  // src/core/rendering/dispose.ts
1162
1328
  init_dev();
1163
1329
  var elementDisposers = /* @__PURE__ */ new WeakMap();
1164
- var _isDev4 = isDev();
1330
+ var _isDev5 = isDev();
1165
1331
  var activeBindingCount = 0;
1166
1332
  function registerDisposer(node, teardown) {
1167
1333
  let disposers = elementDisposers.get(node);
@@ -1170,7 +1336,7 @@ function registerDisposer(node, teardown) {
1170
1336
  elementDisposers.set(node, disposers);
1171
1337
  }
1172
1338
  disposers.push(teardown);
1173
- if (_isDev4) activeBindingCount++;
1339
+ if (_isDev5) activeBindingCount++;
1174
1340
  }
1175
1341
  function dispose(node) {
1176
1342
  const stack = [node];
@@ -1189,12 +1355,12 @@ function dispose(node) {
1189
1355
  if (disposers) {
1190
1356
  const snapshot = disposers.slice();
1191
1357
  elementDisposers.delete(current);
1192
- if (_isDev4) activeBindingCount -= snapshot.length;
1358
+ if (_isDev5) activeBindingCount -= snapshot.length;
1193
1359
  for (const d of snapshot) {
1194
1360
  try {
1195
1361
  d();
1196
1362
  } catch (err) {
1197
- if (_isDev4 && typeof console !== "undefined") {
1363
+ if (_isDev5 && typeof console !== "undefined") {
1198
1364
  console.warn("[SibuJS] Disposer threw during cleanup:", err);
1199
1365
  }
1200
1366
  }
@@ -1205,12 +1371,12 @@ function dispose(node) {
1205
1371
  if (!added || added.length === 0) break;
1206
1372
  const moreSnapshot = added.slice();
1207
1373
  elementDisposers.delete(current);
1208
- if (_isDev4) activeBindingCount -= moreSnapshot.length;
1374
+ if (_isDev5) activeBindingCount -= moreSnapshot.length;
1209
1375
  for (const d of moreSnapshot) {
1210
1376
  try {
1211
1377
  d();
1212
1378
  } catch (err) {
1213
- if (_isDev4 && typeof console !== "undefined") {
1379
+ if (_isDev5 && typeof console !== "undefined") {
1214
1380
  console.warn("[SibuJS] Disposer threw during cleanup:", err);
1215
1381
  }
1216
1382
  }
@@ -1221,7 +1387,7 @@ function dispose(node) {
1221
1387
  }
1222
1388
 
1223
1389
  // src/reactivity/bindChildNode.ts
1224
- var _isDev5 = isDev();
1390
+ var _isDev6 = isDev();
1225
1391
  function bindChildNode(placeholder, getter) {
1226
1392
  let lastNodes = [];
1227
1393
  function commit() {
@@ -1229,7 +1395,7 @@ function bindChildNode(placeholder, getter) {
1229
1395
  try {
1230
1396
  result = getter();
1231
1397
  } catch (err) {
1232
- if (_isDev5) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
1398
+ if (_isDev6) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
1233
1399
  return;
1234
1400
  }
1235
1401
  if (result == null || typeof result === "boolean") {
@@ -1255,7 +1421,7 @@ function bindChildNode(placeholder, getter) {
1255
1421
  if (item == null || typeof item === "boolean") continue;
1256
1422
  const node = item instanceof Node ? item : document.createTextNode(String(item));
1257
1423
  if (seen.has(node)) {
1258
- if (_isDev5)
1424
+ if (_isDev6)
1259
1425
  devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
1260
1426
  continue;
1261
1427
  }
@@ -1290,19 +1456,16 @@ function bindChildNode(placeholder, getter) {
1290
1456
  }
1291
1457
  lastNodes = newNodes;
1292
1458
  }
1293
- return reactiveBinding(commit);
1459
+ return reactiveBinding2(commit);
1294
1460
  }
1295
1461
 
1296
1462
  // src/core/rendering/tagFactory.ts
1297
1463
  init_sanitize();
1298
1464
  var SVG_NS = "http://www.w3.org/2000/svg";
1299
- var _isDev6 = isDev();
1465
+ var _isDev7 = isDev();
1300
1466
  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
- }
1467
+ function isBlockedTag(tag) {
1468
+ return BLOCKED_TAGS.has(tag.toLowerCase());
1306
1469
  }
1307
1470
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
1308
1471
  "config",
@@ -1342,7 +1505,7 @@ function toKebab(prop) {
1342
1505
  }
1343
1506
  function applyStyle(el, style2) {
1344
1507
  if (typeof style2 === "function") {
1345
- const teardown = track(() => {
1508
+ const teardown = track2(() => {
1346
1509
  el.setAttribute("style", style2());
1347
1510
  });
1348
1511
  registerDisposer(el, teardown);
@@ -1358,7 +1521,7 @@ function applyStyle(el, style2) {
1358
1521
  const name = toKebab(prop);
1359
1522
  if (typeof val === "function") {
1360
1523
  const getter = val;
1361
- const teardown = track(() => {
1524
+ const teardown = track2(() => {
1362
1525
  htmlEl.style.setProperty(name, sanitizeCSSValue(String(getter())));
1363
1526
  });
1364
1527
  registerDisposer(el, teardown);
@@ -1373,7 +1536,7 @@ function applyClass(el, cls) {
1373
1536
  return;
1374
1537
  }
1375
1538
  if (typeof cls === "function") {
1376
- const teardown = track(() => {
1539
+ const teardown = track2(() => {
1377
1540
  el.setAttribute("class", cls());
1378
1541
  });
1379
1542
  registerDisposer(el, teardown);
@@ -1400,7 +1563,7 @@ function applyClass(el, cls) {
1400
1563
  }
1401
1564
  el.setAttribute("class", r);
1402
1565
  };
1403
- const teardown = track(update);
1566
+ const teardown = track2(update);
1404
1567
  registerDisposer(el, teardown);
1405
1568
  } else {
1406
1569
  el.setAttribute("class", result);
@@ -1457,8 +1620,11 @@ function appendChildren(el, nodes) {
1457
1620
  }
1458
1621
  }
1459
1622
  var tagFactory = (tag, ns) => {
1623
+ const blocked = isBlockedTag(tag);
1460
1624
  return (first, second) => {
1461
- validateTagName(tag);
1625
+ if (blocked) {
1626
+ throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1627
+ }
1462
1628
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
1463
1629
  if (first === void 0) return el;
1464
1630
  if (typeof first === "string") {
@@ -1467,7 +1633,7 @@ var tagFactory = (tag, ns) => {
1467
1633
  appendChildren(el, second);
1468
1634
  return el;
1469
1635
  }
1470
- if (_isDev6 && looksLikeClassList(first)) {
1636
+ if (_isDev7 && looksLikeClassList(first)) {
1471
1637
  devWarn(
1472
1638
  `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.`
1473
1639
  );
@@ -1488,7 +1654,7 @@ var tagFactory = (tag, ns) => {
1488
1654
  if (pClass != null) applyClass(el, pClass);
1489
1655
  const pId = props.id;
1490
1656
  if (pId != null) {
1491
- if (_isDev6 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1657
+ if (_isDev7 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1492
1658
  devWarn(
1493
1659
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
1494
1660
  );
@@ -1503,7 +1669,7 @@ var tagFactory = (tag, ns) => {
1503
1669
  const handler = pOn[ev];
1504
1670
  if (typeof handler === "function") {
1505
1671
  el.addEventListener(ev, handler);
1506
- } else if (_isDev6) {
1672
+ } else if (_isDev7) {
1507
1673
  devWarn(
1508
1674
  `tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
1509
1675
  );
@@ -1528,7 +1694,6 @@ var tagFactory = (tag, ns) => {
1528
1694
  default: {
1529
1695
  const value = props[key];
1530
1696
  if (value == null) continue;
1531
- const lkey = key.toLowerCase();
1532
1697
  if (isEventHandlerAttr(key)) continue;
1533
1698
  if (typeof value === "function") {
1534
1699
  registerDisposer(el, bindAttribute(el, key, value));
@@ -1541,14 +1706,7 @@ var tagFactory = (tag, ns) => {
1541
1706
  el.removeAttribute(key);
1542
1707
  }
1543
1708
  } 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
- }
1709
+ el.setAttribute(key, sanitizeAttributeString(key, String(value)));
1552
1710
  }
1553
1711
  }
1554
1712
  }
@@ -1624,7 +1782,7 @@ var area = tagFactory("area");
1624
1782
  var audio = tagFactory("audio");
1625
1783
  var img = tagFactory("img");
1626
1784
  var map = tagFactory("map");
1627
- var track2 = tagFactory("track");
1785
+ var track3 = tagFactory("track");
1628
1786
  var video = tagFactory("video");
1629
1787
  var embed = tagFactory("embed");
1630
1788
  var iframe = tagFactory("iframe");
@@ -1701,16 +1859,57 @@ var marquee = tagFactory("marquee");
1701
1859
  // src/reactivity/batch.ts
1702
1860
  var batchDepth = 0;
1703
1861
  var pendingSignals = /* @__PURE__ */ new Set();
1704
- function enqueueBatchedSignal(signal2) {
1862
+ function batchImpl(fn) {
1863
+ batchDepth++;
1864
+ try {
1865
+ return fn();
1866
+ } finally {
1867
+ batchDepth--;
1868
+ if (batchDepth === 0) {
1869
+ flushBatch();
1870
+ }
1871
+ }
1872
+ }
1873
+ function enqueueBatchedSignalImpl(signal2) {
1705
1874
  if (batchDepth === 0) return false;
1706
1875
  pendingSignals.add(signal2);
1707
1876
  return true;
1708
1877
  }
1878
+ function isBatchingImpl() {
1879
+ return batchDepth > 0;
1880
+ }
1881
+ function flushBatch() {
1882
+ try {
1883
+ for (const signal2 of pendingSignals) {
1884
+ queueSignalNotification2(signal2);
1885
+ }
1886
+ } finally {
1887
+ pendingSignals.clear();
1888
+ }
1889
+ drainNotificationQueue2();
1890
+ }
1891
+ var BATCH_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.batch.v1");
1892
+ function resolveBatchApi() {
1893
+ const g2 = globalThis;
1894
+ const existing = g2[BATCH_REGISTRY_KEY];
1895
+ if (existing) return existing;
1896
+ const local = {
1897
+ batch: batchImpl,
1898
+ enqueueBatchedSignal: enqueueBatchedSignalImpl,
1899
+ isBatching: isBatchingImpl
1900
+ };
1901
+ g2[BATCH_REGISTRY_KEY] = local;
1902
+ return local;
1903
+ }
1904
+ var API2 = resolveBatchApi();
1905
+ var batch = API2.batch;
1906
+ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
1907
+ var isBatching = API2.isBatching;
1709
1908
 
1710
1909
  // src/core/signals/signal.ts
1711
1910
  init_dev();
1712
1911
  var _g = globalThis;
1713
- var _isDev7 = isDev();
1912
+ var _isDev8 = isDev();
1714
1913
  function signal(initial, options) {
1715
1914
  const state = {
1716
1915
  value: initial,
@@ -1721,11 +1920,11 @@ function signal(initial, options) {
1721
1920
  __activeNode: null,
1722
1921
  __name: void 0
1723
1922
  };
1724
- const debugName = _isDev7 ? options?.name : void 0;
1923
+ const debugName = _isDev8 ? options?.name : void 0;
1725
1924
  const equalsFn = options?.equals;
1726
1925
  if (debugName) state.__name = debugName;
1727
1926
  function get() {
1728
- recordDependency(state);
1927
+ recordDependency2(state);
1729
1928
  return state.value;
1730
1929
  }
1731
1930
  get.__signal = state;
@@ -1738,15 +1937,15 @@ function signal(initial, options) {
1738
1937
  if (equalsFn(prev, newValue)) return;
1739
1938
  state.value = newValue;
1740
1939
  state.__v++;
1741
- if (_isDev7) {
1940
+ if (_isDev8) {
1742
1941
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1743
1942
  if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
1744
1943
  }
1745
1944
  if (!enqueueBatchedSignal(state)) {
1746
- notifySubscribers(state);
1945
+ notifySubscribers2(state);
1747
1946
  }
1748
1947
  };
1749
- } else if (_isDev7) {
1948
+ } else if (_isDev8) {
1750
1949
  set = (next) => {
1751
1950
  const prev = state.value;
1752
1951
  const newValue = typeof next === "function" ? next(prev) : next;
@@ -1756,7 +1955,7 @@ function signal(initial, options) {
1756
1955
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1757
1956
  if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
1758
1957
  if (!enqueueBatchedSignal(state)) {
1759
- notifySubscribers(state);
1958
+ notifySubscribers2(state);
1760
1959
  }
1761
1960
  };
1762
1961
  } else {
@@ -1767,11 +1966,11 @@ function signal(initial, options) {
1767
1966
  state.value = newValue;
1768
1967
  state.__v++;
1769
1968
  if (!enqueueBatchedSignal(state)) {
1770
- notifySubscribers(state);
1969
+ notifySubscribers2(state);
1771
1970
  }
1772
1971
  };
1773
1972
  }
1774
- if (_isDev7) {
1973
+ if (_isDev8) {
1775
1974
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1776
1975
  if (hook) hook.emit("signal:create", { signal: state, name: debugName, getter: get, initial });
1777
1976
  }
@@ -1828,7 +2027,7 @@ function drainReruns(ctx) {
1828
2027
  do {
1829
2028
  ctx.rerunPending = false;
1830
2029
  if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1831
- retrack(ctx.bodyFn, ctx.subscriber);
2030
+ retrack2(ctx.bodyFn, ctx.subscriber);
1832
2031
  } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
1833
2032
  if (ctx.rerunPending) {
1834
2033
  ctx.rerunPending = false;
@@ -1857,7 +2056,7 @@ function disposeEffect(ctx) {
1857
2056
  }
1858
2057
  }
1859
2058
  try {
1860
- cleanup(ctx.subscriber);
2059
+ cleanup2(ctx.subscriber);
1861
2060
  } catch (err) {
1862
2061
  if (typeof console !== "undefined") {
1863
2062
  console.warn("[SibuJS effect] dispose threw:", err);
@@ -1902,7 +2101,7 @@ function effect(effectFn, options) {
1902
2101
  try {
1903
2102
  ctx.rerunPending = false;
1904
2103
  if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1905
- retrack(ctx.bodyFn, sub2);
2104
+ retrack2(ctx.bodyFn, sub2);
1906
2105
  if (ctx.rerunPending) drainReruns(ctx);
1907
2106
  } finally {
1908
2107
  ctx.running = false;
@@ -1918,7 +2117,7 @@ function effect(effectFn, options) {
1918
2117
  ctx.subscriber = sub2;
1919
2118
  ctx.running = true;
1920
2119
  try {
1921
- retrack(ctx.bodyFn, ctx.subscriber);
2120
+ retrack2(ctx.bodyFn, ctx.subscriber);
1922
2121
  if (ctx.rerunPending) drainReruns(ctx);
1923
2122
  } finally {
1924
2123
  ctx.running = false;
@@ -2937,7 +3136,7 @@ function Route() {
2937
3136
  await originalUpdate();
2938
3137
  routeInitialized = true;
2939
3138
  };
2940
- const routeTeardown = track(wrappedUpdate);
3139
+ const routeTeardown = track2(wrappedUpdate);
2941
3140
  if (!routeInitialized) {
2942
3141
  queueMicrotask(() => {
2943
3142
  if (!routeInitialized && anchor.parentNode) wrappedUpdate();
@@ -3050,7 +3249,7 @@ function KeepAliveRoute(options) {
3050
3249
  await update();
3051
3250
  initialized = true;
3052
3251
  };
3053
- const kaTeardown = track(wrappedUpdate);
3252
+ const kaTeardown = track2(wrappedUpdate);
3054
3253
  if (!initialized) {
3055
3254
  queueMicrotask(() => {
3056
3255
  if (!initialized && anchor.parentNode) wrappedUpdate();
@@ -3315,7 +3514,7 @@ function Outlet() {
3315
3514
  console.error("[Outlet] Failed to render child route:", error);
3316
3515
  }
3317
3516
  };
3318
- const outletTeardown = track(update);
3517
+ const outletTeardown = track2(update);
3319
3518
  if (!anchor.parentNode) {
3320
3519
  queueMicrotask(() => {
3321
3520
  if (anchor.parentNode) update();