sibujs 1.5.0 → 2.1.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 (208) hide show
  1. package/dist/browser.cjs +332 -121
  2. package/dist/browser.d.cts +5 -0
  3. package/dist/browser.d.ts +5 -0
  4. package/dist/browser.js +6 -6
  5. package/dist/build.cjs +1049 -344
  6. package/dist/build.js +15 -13
  7. package/dist/cdn.global.js +17 -16
  8. package/dist/chunk-2RA7SHDA.js +65 -0
  9. package/dist/chunk-2UPRY23K.js +80 -0
  10. package/dist/{chunk-BMPL52BF.js → chunk-3DZP6OIT.js} +118 -66
  11. package/dist/chunk-3JHCYHWN.js +125 -0
  12. package/dist/{chunk-CZUGLNJS.js → chunk-45YP72ZQ.js} +3 -3
  13. package/dist/{chunk-JCDUJN2F.js → chunk-AMK2TYNW.js} +490 -153
  14. package/dist/{chunk-NHUC2QWH.js → chunk-CWBVQML6.js} +1 -1
  15. package/dist/{chunk-XHK6BDAJ.js → chunk-DRUZZAK4.js} +25 -8
  16. package/dist/{chunk-RJ46C3CS.js → chunk-GWWURC5M.js} +71 -20
  17. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  18. package/dist/{chunk-2BYQDGN3.js → chunk-KGYT6UO6.js} +234 -63
  19. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  20. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  21. package/dist/{chunk-XUEEGU5O.js → chunk-NASX6ST2.js} +16 -4
  22. package/dist/{chunk-VQDZK23A.js → chunk-O6EFQ3KT.js} +181 -66
  23. package/dist/{chunk-BGN5ZMP4.js → chunk-OJ3P4ECI.js} +14 -2
  24. package/dist/chunk-ON5MMR2J.js +1327 -0
  25. package/dist/{chunk-SFKNRVCU.js → chunk-P2HSJDDN.js} +135 -79
  26. package/dist/chunk-QO3WC6FS.js +384 -0
  27. package/dist/{chunk-WZSPOOER.js → chunk-RDTDJCAB.js} +8 -5
  28. package/dist/{chunk-7GRNSCFT.js → chunk-TH2ILCYW.js} +312 -185
  29. package/dist/chunk-UCS6AMJ7.js +79 -0
  30. package/dist/{chunk-VAPYJN4X.js → chunk-V6C4FADE.js} +93 -23
  31. package/dist/{chunk-OUZZEE4S.js → chunk-WANSMF2L.js} +17 -11
  32. package/dist/{chunk-23VV7YD3.js → chunk-WIPZPFBQ.js} +25 -30
  33. package/dist/chunk-WZA53FXU.js +149 -0
  34. package/dist/{chunk-BGTHZHJ5.js → chunk-ZAQSMOED.js} +188 -44
  35. package/dist/{customElement-BL3Uo8dL.d.cts → customElement-CPfIrbvg.d.cts} +14 -10
  36. package/dist/{customElement-BL3Uo8dL.d.ts → customElement-CPfIrbvg.d.ts} +14 -10
  37. package/dist/data.cjs +536 -151
  38. package/dist/data.d.cts +20 -2
  39. package/dist/data.d.ts +20 -2
  40. package/dist/data.js +11 -9
  41. package/dist/devtools.cjs +613 -266
  42. package/dist/devtools.d.cts +1 -1
  43. package/dist/devtools.d.ts +1 -1
  44. package/dist/devtools.js +12 -6
  45. package/dist/ecosystem.cjs +602 -197
  46. package/dist/ecosystem.d.cts +9 -7
  47. package/dist/ecosystem.d.ts +9 -7
  48. package/dist/ecosystem.js +12 -11
  49. package/dist/extras.cjs +3500 -1608
  50. package/dist/extras.d.cts +9 -9
  51. package/dist/extras.d.ts +9 -9
  52. package/dist/extras.js +58 -45
  53. package/dist/index.cjs +1055 -344
  54. package/dist/index.d.cts +85 -8
  55. package/dist/index.d.ts +85 -8
  56. package/dist/index.js +32 -16
  57. package/dist/{introspect-BumjnBKr.d.cts → introspect-2TOlQ7oa.d.cts} +25 -3
  58. package/dist/{introspect-CZrlcaYy.d.ts → introspect-DnIpHQQz.d.ts} +25 -3
  59. package/dist/motion.cjs +122 -63
  60. package/dist/motion.js +4 -4
  61. package/dist/patterns.cjs +450 -110
  62. package/dist/patterns.d.cts +11 -12
  63. package/dist/patterns.d.ts +11 -12
  64. package/dist/patterns.js +7 -7
  65. package/dist/performance.cjs +373 -149
  66. package/dist/performance.d.cts +23 -16
  67. package/dist/performance.d.ts +23 -16
  68. package/dist/performance.js +13 -8
  69. package/dist/plugin-D30wlGW5.d.cts +71 -0
  70. package/dist/plugin-D30wlGW5.d.ts +71 -0
  71. package/dist/plugins.cjs +729 -301
  72. package/dist/plugins.d.cts +10 -3
  73. package/dist/plugins.d.ts +10 -3
  74. package/dist/plugins.js +106 -38
  75. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  76. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  77. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  78. package/dist/ssr.cjs +736 -274
  79. package/dist/ssr.d.cts +26 -6
  80. package/dist/ssr.d.ts +26 -6
  81. package/dist/ssr.js +12 -11
  82. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.cts} +9 -1
  83. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.ts} +9 -1
  84. package/dist/testing.cjs +303 -76
  85. package/dist/testing.d.cts +17 -4
  86. package/dist/testing.d.ts +17 -4
  87. package/dist/testing.js +100 -44
  88. package/dist/ui.cjs +589 -178
  89. package/dist/ui.d.cts +1 -1
  90. package/dist/ui.d.ts +1 -1
  91. package/dist/ui.js +20 -17
  92. package/dist/widgets.cjs +1103 -146
  93. package/dist/widgets.d.cts +104 -2
  94. package/dist/widgets.d.ts +104 -2
  95. package/dist/widgets.js +9 -7
  96. package/package.json +8 -2
  97. package/dist/chunk-32DY64NT.js +0 -282
  98. package/dist/chunk-3AIRKM3B.js +0 -1263
  99. package/dist/chunk-3ARAQO7B.js +0 -398
  100. package/dist/chunk-3CRQALYP.js +0 -877
  101. package/dist/chunk-4EI4AG32.js +0 -482
  102. package/dist/chunk-4MYMUBRS.js +0 -21
  103. package/dist/chunk-5ZYQ6KDD.js +0 -154
  104. package/dist/chunk-6BMPXPUW.js +0 -26
  105. package/dist/chunk-6HLLIF3K.js +0 -398
  106. package/dist/chunk-6LSNVCS2.js +0 -937
  107. package/dist/chunk-6SA3QQES.js +0 -61
  108. package/dist/chunk-77L6NL3X.js +0 -1097
  109. package/dist/chunk-7BF6TK55.js +0 -1097
  110. package/dist/chunk-7TQKR4PP.js +0 -294
  111. package/dist/chunk-7V26P53V.js +0 -712
  112. package/dist/chunk-AZ3ISID5.js +0 -298
  113. package/dist/chunk-B7SWRFUT.js +0 -332
  114. package/dist/chunk-BTU3TJDS.js +0 -365
  115. package/dist/chunk-BW3WT46K.js +0 -937
  116. package/dist/chunk-C6KFWOFV.js +0 -616
  117. package/dist/chunk-CHF5OHIA.js +0 -61
  118. package/dist/chunk-CHJ27IGK.js +0 -26
  119. package/dist/chunk-CMBFNA7L.js +0 -27
  120. package/dist/chunk-DAHRH4ON.js +0 -331
  121. package/dist/chunk-DKOHBI74.js +0 -924
  122. package/dist/chunk-DTCOOBMX.js +0 -725
  123. package/dist/chunk-EBGIRKQY.js +0 -616
  124. package/dist/chunk-EUZND3CB.js +0 -27
  125. package/dist/chunk-EVCZO745.js +0 -365
  126. package/dist/chunk-EWFVA3TJ.js +0 -282
  127. package/dist/chunk-F3FA4F32.js +0 -292
  128. package/dist/chunk-FGOEVHY3.js +0 -60
  129. package/dist/chunk-G3BOQPVO.js +0 -365
  130. package/dist/chunk-GCOK2LC3.js +0 -282
  131. package/dist/chunk-GJPXRJ45.js +0 -37
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-JAKHTMQU.js +0 -1000
  134. package/dist/chunk-JCI5M6U6.js +0 -956
  135. package/dist/chunk-K4G4ZQNR.js +0 -286
  136. package/dist/chunk-K5ZUMYVS.js +0 -89
  137. package/dist/chunk-KQPDEVVS.js +0 -398
  138. package/dist/chunk-L6JRBDNS.js +0 -60
  139. package/dist/chunk-LA6KQEDU.js +0 -712
  140. package/dist/chunk-MB6QFH3I.js +0 -2776
  141. package/dist/chunk-MDVXJWFN.js +0 -304
  142. package/dist/chunk-MEZVEBPN.js +0 -2008
  143. package/dist/chunk-MK4ERFYL.js +0 -2249
  144. package/dist/chunk-MLKGABMK.js +0 -9
  145. package/dist/chunk-MQ5GOYPH.js +0 -2249
  146. package/dist/chunk-MYRV7VDM.js +0 -742
  147. package/dist/chunk-N6IZB6KJ.js +0 -567
  148. package/dist/chunk-NEKUBFPT.js +0 -60
  149. package/dist/chunk-NMRUZALC.js +0 -1097
  150. package/dist/chunk-NYVAC6P5.js +0 -37
  151. package/dist/chunk-NZIIMDWI.js +0 -84
  152. package/dist/chunk-OF7UZIVB.js +0 -725
  153. package/dist/chunk-P3XWXJZU.js +0 -282
  154. package/dist/chunk-P6W3STU4.js +0 -2249
  155. package/dist/chunk-PBHF5WKN.js +0 -616
  156. package/dist/chunk-PDZQY43A.js +0 -616
  157. package/dist/chunk-PTQJDMRT.js +0 -146
  158. package/dist/chunk-PZEGYCF5.js +0 -61
  159. package/dist/chunk-QBMDLBU2.js +0 -975
  160. package/dist/chunk-QWZG56ET.js +0 -2744
  161. package/dist/chunk-RQGQSLQK.js +0 -725
  162. package/dist/chunk-SDLZDHKP.js +0 -107
  163. package/dist/chunk-TDGZL5CU.js +0 -365
  164. package/dist/chunk-TNQWPPE6.js +0 -37
  165. package/dist/chunk-TSOKIX5Z.js +0 -654
  166. package/dist/chunk-UHNL42EF.js +0 -2730
  167. package/dist/chunk-UNXCEF6S.js +0 -21
  168. package/dist/chunk-V2XTI523.js +0 -347
  169. package/dist/chunk-VAU366PN.js +0 -2241
  170. package/dist/chunk-VMVDTCXB.js +0 -712
  171. package/dist/chunk-VQNQZCWJ.js +0 -61
  172. package/dist/chunk-VRW3FULF.js +0 -725
  173. package/dist/chunk-WADYRCO2.js +0 -304
  174. package/dist/chunk-WILQZRO4.js +0 -282
  175. package/dist/chunk-WR5D4EGH.js +0 -26
  176. package/dist/chunk-WUHJISPP.js +0 -298
  177. package/dist/chunk-XYU6TZOW.js +0 -182
  178. package/dist/chunk-Y6GP4QGG.js +0 -276
  179. package/dist/chunk-YECR7UIA.js +0 -347
  180. package/dist/chunk-YUTWTI4B.js +0 -654
  181. package/dist/chunk-Z65KYU7I.js +0 -26
  182. package/dist/chunk-Z6POF5YC.js +0 -975
  183. package/dist/chunk-ZBJP6WFL.js +0 -482
  184. package/dist/chunk-ZD6OAMTH.js +0 -277
  185. package/dist/chunk-ZWKZCBO6.js +0 -317
  186. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  187. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  188. package/dist/contracts-DOrhwbke.d.cts +0 -245
  189. package/dist/contracts-DOrhwbke.d.ts +0 -245
  190. package/dist/contracts-xo5ckdRP.d.cts +0 -240
  191. package/dist/contracts-xo5ckdRP.d.ts +0 -240
  192. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  193. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  194. package/dist/customElement-D2DJp_xn.d.cts +0 -313
  195. package/dist/customElement-D2DJp_xn.d.ts +0 -313
  196. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  197. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  198. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  199. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  200. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  201. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  202. package/dist/ssr-3RXHP5ES.js +0 -38
  203. package/dist/ssr-6GIMY5MX.js +0 -38
  204. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  205. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  206. package/dist/ssr-WKUPVSSK.js +0 -36
  207. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  208. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
package/dist/ui.cjs CHANGED
@@ -23,6 +23,7 @@ __export(ui_exports, {
23
23
  FocusTrap: () => FocusTrap,
24
24
  RenderProp: () => RenderProp,
25
25
  VirtualList: () => VirtualList,
26
+ __resetDialogStack: () => __resetDialogStack,
26
27
  announce: () => announce,
27
28
  aria: () => aria,
28
29
  assertType: () => assertType,
@@ -91,26 +92,39 @@ function isDev() {
91
92
  var _isDev = isDev();
92
93
  function devAssert(condition, message) {
93
94
  if (_isDev && !condition) {
94
- throw new Error(`[Sibu] ${message}`);
95
+ throw new Error(`[SibuJS] ${message}`);
95
96
  }
96
97
  }
97
98
  function devWarn(message) {
98
99
  if (_isDev) {
99
- console.warn(`[Sibu] ${message}`);
100
+ console.warn(`[SibuJS] ${message}`);
100
101
  }
101
102
  }
102
103
 
103
104
  // src/reactivity/track.ts
104
105
  var _isDev2 = isDev();
105
- var subscriberStack = new Array(32);
106
- var stackCapacity = 32;
106
+ var STACK_INITIAL = 32;
107
+ var STACK_SHRINK_THRESHOLD = 128;
108
+ var subscriberStack = new Array(STACK_INITIAL);
109
+ var stackCapacity = STACK_INITIAL;
107
110
  var stackTop = -1;
108
111
  var currentSubscriber = null;
109
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
110
112
  var SUBS = "__s";
113
+ function syncFastPath(signal2, subs) {
114
+ const size = subs.size;
115
+ if (size === 0) {
116
+ signal2.__f = void 0;
117
+ delete signal2[SUBS];
118
+ } else if (size === 1) {
119
+ signal2.__f = subs.values().next().value;
120
+ } else {
121
+ signal2.__f = void 0;
122
+ }
123
+ }
111
124
  var notifyDepth = 0;
112
125
  var pendingQueue = [];
113
126
  var pendingSet = /* @__PURE__ */ new Set();
127
+ var propagateStack = [];
114
128
  function safeInvoke(sub) {
115
129
  try {
116
130
  sub();
@@ -119,6 +133,47 @@ function safeInvoke(sub) {
119
133
  }
120
134
  }
121
135
  var trackingSuspended = false;
136
+ var subscriberEpochCounter = 0;
137
+ function retrack(effectFn, subscriber) {
138
+ const prev = currentSubscriber;
139
+ currentSubscriber = subscriber;
140
+ const sub = subscriber;
141
+ const epoch = ++subscriberEpochCounter;
142
+ sub._epoch = epoch;
143
+ try {
144
+ effectFn();
145
+ } finally {
146
+ currentSubscriber = prev;
147
+ pruneStaleDeps(sub, epoch);
148
+ }
149
+ }
150
+ function pruneStaleDeps(sub, currentEpoch) {
151
+ if (sub._dep !== void 0) {
152
+ if (sub._depEpoch !== currentEpoch) {
153
+ const sig = sub._dep;
154
+ const subs = sig[SUBS];
155
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
156
+ sub._dep = void 0;
157
+ sub._depEpoch = void 0;
158
+ }
159
+ return;
160
+ }
161
+ const deps = sub._deps;
162
+ if (!deps || deps.size === 0) return;
163
+ let stales;
164
+ for (const [signal2, epoch] of deps) {
165
+ if (epoch !== currentEpoch) {
166
+ (stales ?? (stales = [])).push(signal2);
167
+ }
168
+ }
169
+ if (!stales) return;
170
+ for (const signal2 of stales) {
171
+ deps.delete(signal2);
172
+ const sig = signal2;
173
+ const subs = sig[SUBS];
174
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
175
+ }
176
+ }
122
177
  function track(effectFn, subscriber) {
123
178
  if (!subscriber) subscriber = effectFn;
124
179
  cleanup(subscriber);
@@ -134,75 +189,133 @@ function track(effectFn, subscriber) {
134
189
  } finally {
135
190
  stackTop--;
136
191
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
192
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
193
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
194
+ subscriberStack.length = stackCapacity;
195
+ }
137
196
  }
138
197
  return () => cleanup(subscriber);
139
198
  }
140
199
  function recordDependency(signal2) {
141
200
  if (!currentSubscriber) return;
142
201
  const sub = currentSubscriber;
143
- if (sub._dep === signal2) return;
202
+ const epoch = sub._epoch;
203
+ if (sub._dep === signal2) {
204
+ sub._depEpoch = epoch;
205
+ return;
206
+ }
144
207
  const deps = sub._deps;
145
208
  if (deps) {
146
- if (deps.has(signal2)) return;
147
- deps.add(signal2);
209
+ deps.set(signal2, epoch);
148
210
  } else if (sub._dep !== void 0) {
149
- const set = /* @__PURE__ */ new Set();
150
- set.add(sub._dep);
151
- set.add(signal2);
152
- sub._deps = set;
211
+ const map = /* @__PURE__ */ new Map();
212
+ map.set(sub._dep, sub._depEpoch);
213
+ map.set(signal2, epoch);
214
+ sub._deps = map;
153
215
  sub._dep = void 0;
216
+ sub._depEpoch = void 0;
154
217
  } else {
155
218
  sub._dep = signal2;
219
+ sub._depEpoch = epoch;
156
220
  }
157
- let subs = signal2[SUBS];
221
+ const sig = signal2;
222
+ let subs = sig[SUBS];
158
223
  if (!subs) {
159
224
  subs = /* @__PURE__ */ new Set();
160
- signalSubscribers.set(signal2, subs);
161
- signal2[SUBS] = subs;
225
+ sig[SUBS] = subs;
162
226
  }
227
+ const prevSize = subs.size;
163
228
  subs.add(currentSubscriber);
164
- if (subs.size === 1) {
165
- signal2.__f = currentSubscriber;
166
- } else if (signal2.__f !== void 0) {
167
- signal2.__f = void 0;
229
+ if (subs.size !== prevSize) {
230
+ if (subs.size === 1) {
231
+ sig.__f = currentSubscriber;
232
+ } else if (sig.__f !== void 0) {
233
+ sig.__f = void 0;
234
+ }
235
+ }
236
+ }
237
+ var maxSubscriberRepeats = 50;
238
+ var maxDrainIterations = 1e6;
239
+ var drainEpoch = 0;
240
+ function tickRepeat(sub) {
241
+ const s = sub;
242
+ if (s._runEpoch !== drainEpoch) {
243
+ s._runEpoch = drainEpoch;
244
+ s._runs = 1;
245
+ return false;
246
+ }
247
+ return ++s._runs > maxSubscriberRepeats;
248
+ }
249
+ function cycleError(sub) {
250
+ if (typeof console !== "undefined") {
251
+ const name = sub.__name ?? "<unnamed>";
252
+ console.error(
253
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
254
+ );
255
+ }
256
+ }
257
+ function absoluteDrainError() {
258
+ if (typeof console !== "undefined") {
259
+ console.error(
260
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
261
+ );
262
+ }
263
+ }
264
+ function drainQueue() {
265
+ let i = 0;
266
+ while (i < pendingQueue.length) {
267
+ if (i >= maxDrainIterations) {
268
+ absoluteDrainError();
269
+ break;
270
+ }
271
+ const sub = pendingQueue[i++];
272
+ if (tickRepeat(sub)) {
273
+ cycleError(sub);
274
+ break;
275
+ }
276
+ pendingSet.delete(sub);
277
+ safeInvoke(sub);
168
278
  }
169
279
  }
170
280
  function propagateDirty(sub) {
171
281
  sub();
172
- let sig = sub._sig;
173
- while (sig) {
282
+ const rootSig = sub._sig;
283
+ if (!rootSig) return;
284
+ const stack = propagateStack;
285
+ const baseLen = stack.length;
286
+ stack.push(rootSig);
287
+ while (stack.length > baseLen) {
288
+ const sig = stack.pop();
174
289
  const first = sig.__f;
175
290
  if (first) {
176
291
  if (first._c) {
177
292
  const nSig = first._sig;
178
- nSig._d = true;
179
- sig = nSig;
180
- continue;
181
- }
182
- if (!pendingSet.has(first)) {
293
+ if (!nSig._d) {
294
+ nSig._d = true;
295
+ stack.push(nSig);
296
+ }
297
+ } else if (!pendingSet.has(first)) {
183
298
  pendingSet.add(first);
184
299
  pendingQueue.push(first);
185
300
  }
186
- break;
301
+ continue;
187
302
  }
188
303
  const subs = sig[SUBS];
189
- if (!subs) break;
190
- let nextSig;
304
+ if (!subs) continue;
191
305
  for (const s of subs) {
192
306
  if (s._c) {
193
- s();
194
307
  const nSig = s._sig;
195
- if (nSig && !nextSig) {
196
- nextSig = nSig;
197
- } else if (nSig) {
198
- propagateDirty(s);
308
+ if (nSig && !nSig._d) {
309
+ nSig._d = true;
310
+ stack.push(nSig);
311
+ } else if (!nSig) {
312
+ s();
199
313
  }
200
314
  } else if (!pendingSet.has(s)) {
201
315
  pendingSet.add(s);
202
316
  pendingQueue.push(s);
203
317
  }
204
318
  }
205
- sig = nextSig;
206
319
  }
207
320
  }
208
321
  function notifySubscribers(signal2) {
@@ -218,21 +331,22 @@ function notifySubscribers(signal2) {
218
331
  return;
219
332
  }
220
333
  notifyDepth++;
334
+ drainEpoch++;
221
335
  try {
222
336
  if (first._c) {
223
337
  propagateDirty(first);
338
+ } else if (tickRepeat(first)) {
339
+ cycleError(first);
224
340
  } else {
225
341
  safeInvoke(first);
226
342
  }
227
- let i = 0;
228
- while (i < pendingQueue.length) {
229
- safeInvoke(pendingQueue[i]);
230
- i++;
231
- }
343
+ drainQueue();
232
344
  } finally {
233
- pendingQueue.length = 0;
234
- pendingSet.clear();
235
345
  notifyDepth--;
346
+ if (notifyDepth === 0) {
347
+ pendingQueue.length = 0;
348
+ pendingSet.clear();
349
+ }
236
350
  }
237
351
  return;
238
352
  }
@@ -250,57 +364,45 @@ function notifySubscribers(signal2) {
250
364
  return;
251
365
  }
252
366
  notifyDepth++;
367
+ drainEpoch++;
253
368
  try {
254
- let directCount = 0;
255
369
  for (const sub of subs) {
256
- pendingQueue[directCount++] = sub;
257
- }
258
- for (let i2 = 0; i2 < directCount; i2++) {
259
- if (pendingQueue[i2]._c) {
260
- propagateDirty(pendingQueue[i2]);
261
- }
262
- }
263
- for (let i2 = 0; i2 < directCount; i2++) {
264
- if (!pendingQueue[i2]._c) {
265
- if (!pendingSet.has(pendingQueue[i2])) {
266
- safeInvoke(pendingQueue[i2]);
267
- }
370
+ if (sub._c) {
371
+ propagateDirty(sub);
372
+ } else if (!pendingSet.has(sub)) {
373
+ pendingSet.add(sub);
374
+ pendingQueue.push(sub);
268
375
  }
269
376
  }
270
- let i = directCount;
271
- while (i < pendingQueue.length) {
272
- safeInvoke(pendingQueue[i]);
273
- i++;
274
- }
377
+ drainQueue();
275
378
  } finally {
276
- pendingQueue.length = 0;
277
- pendingSet.clear();
278
379
  notifyDepth--;
380
+ if (notifyDepth === 0) {
381
+ pendingQueue.length = 0;
382
+ pendingSet.clear();
383
+ }
279
384
  }
280
385
  }
281
386
  function cleanup(subscriber) {
282
387
  const sub = subscriber;
283
388
  const singleDep = sub._dep;
284
389
  if (singleDep !== void 0) {
285
- const subs = singleDep[SUBS];
286
- if (subs) {
287
- subs.delete(subscriber);
288
- if (singleDep.__f === subscriber) {
289
- singleDep.__f = void 0;
290
- }
390
+ const sig = singleDep;
391
+ const subs = sig[SUBS];
392
+ if (subs?.delete(subscriber)) {
393
+ syncFastPath(sig, subs);
291
394
  }
292
395
  sub._dep = void 0;
396
+ sub._depEpoch = void 0;
293
397
  return;
294
398
  }
295
399
  const deps = sub._deps;
296
400
  if (!deps || deps.size === 0) return;
297
- for (const signal2 of deps) {
298
- const subs = signal2[SUBS];
299
- if (subs) {
300
- subs.delete(subscriber);
301
- if (signal2.__f === subscriber) {
302
- signal2.__f = void 0;
303
- }
401
+ for (const signal2 of deps.keys()) {
402
+ const sig = signal2;
403
+ const subs = sig[SUBS];
404
+ if (subs?.delete(subscriber)) {
405
+ syncFastPath(sig, subs);
304
406
  }
305
407
  }
306
408
  deps.clear();
@@ -310,6 +412,7 @@ function cleanup(subscriber) {
310
412
  function derived(getter, options) {
311
413
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
312
414
  const debugName = options?.name;
415
+ const equals = options?.equals;
313
416
  const cs = {};
314
417
  cs._d = false;
315
418
  cs._g = getter;
@@ -320,8 +423,14 @@ function derived(getter, options) {
320
423
  markDirty._c = 1;
321
424
  markDirty._sig = cs;
322
425
  track(() => {
323
- cs._d = false;
324
- cs._v = getter();
426
+ let threw = true;
427
+ try {
428
+ cs._v = getter();
429
+ cs._d = false;
430
+ threw = false;
431
+ } finally {
432
+ if (threw) cs._d = true;
433
+ }
325
434
  }, markDirty);
326
435
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
327
436
  let evaluating = false;
@@ -334,11 +443,16 @@ function derived(getter, options) {
334
443
  if (trackingSuspended) {
335
444
  if (cs._d) {
336
445
  evaluating = true;
446
+ let threw = true;
337
447
  try {
338
- cs._d = false;
339
- cs._v = getter();
448
+ retrack(() => {
449
+ cs._v = getter();
450
+ cs._d = false;
451
+ threw = false;
452
+ }, markDirty);
340
453
  } finally {
341
454
  evaluating = false;
455
+ if (threw) cs._d = true;
342
456
  }
343
457
  }
344
458
  return cs._v;
@@ -347,13 +461,17 @@ function derived(getter, options) {
347
461
  if (cs._d) {
348
462
  const oldValue = cs._v;
349
463
  evaluating = true;
464
+ let threw = true;
350
465
  try {
351
- track(() => {
466
+ retrack(() => {
467
+ const next = getter();
468
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
352
469
  cs._d = false;
353
- cs._v = getter();
470
+ threw = false;
354
471
  }, markDirty);
355
472
  } finally {
356
473
  evaluating = false;
474
+ if (threw) cs._d = true;
357
475
  }
358
476
  if (hook && oldValue !== cs._v) {
359
477
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
@@ -518,9 +636,18 @@ function form(config) {
518
636
  }
519
637
  return null;
520
638
  });
639
+ const wrappedSet = (next) => {
640
+ setValue(next);
641
+ setManualErrors((prev) => {
642
+ if (!(name in prev) || prev[name] == null) return prev;
643
+ const copy = { ...prev };
644
+ copy[name] = null;
645
+ return copy;
646
+ });
647
+ };
521
648
  fieldMap[name] = {
522
649
  value,
523
- set: setValue,
650
+ set: wrappedSet,
524
651
  error,
525
652
  touched: isTouched,
526
653
  touch: () => setTouched(true),
@@ -654,11 +781,77 @@ function registerDisposer(node, teardown) {
654
781
  disposers.push(teardown);
655
782
  if (_isDev4) activeBindingCount++;
656
783
  }
784
+ function dispose(node) {
785
+ const stack = [node];
786
+ const order = [];
787
+ while (stack.length > 0) {
788
+ const current = stack.pop();
789
+ order.push(current);
790
+ const children = Array.from(current.childNodes);
791
+ for (let i = 0; i < children.length; i++) {
792
+ stack.push(children[i]);
793
+ }
794
+ }
795
+ for (let i = order.length - 1; i >= 0; i--) {
796
+ const current = order[i];
797
+ const disposers = elementDisposers.get(current);
798
+ if (disposers) {
799
+ const snapshot = disposers.slice();
800
+ elementDisposers.delete(current);
801
+ if (_isDev4) activeBindingCount -= snapshot.length;
802
+ for (const d of snapshot) {
803
+ try {
804
+ d();
805
+ } catch (err) {
806
+ if (_isDev4 && typeof console !== "undefined") {
807
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
808
+ }
809
+ }
810
+ }
811
+ let extraPasses = 0;
812
+ while (extraPasses++ < 8) {
813
+ const added = elementDisposers.get(current);
814
+ if (!added || added.length === 0) break;
815
+ const moreSnapshot = added.slice();
816
+ elementDisposers.delete(current);
817
+ if (_isDev4) activeBindingCount -= moreSnapshot.length;
818
+ for (const d of moreSnapshot) {
819
+ try {
820
+ d();
821
+ } catch (err) {
822
+ if (_isDev4 && typeof console !== "undefined") {
823
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
824
+ }
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }
830
+ }
657
831
 
658
832
  // src/core/ssr-context.ts
659
- var ssrMode = false;
833
+ var als = null;
834
+ try {
835
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
836
+ const req = Function("return typeof require==='function'?require:null")();
837
+ if (req) {
838
+ const mod = req("node:async_hooks");
839
+ als = new mod.AsyncLocalStorage();
840
+ }
841
+ }
842
+ } catch {
843
+ als = null;
844
+ }
845
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
846
+ function getSSRStore() {
847
+ if (als) {
848
+ const s = als.getStore();
849
+ if (s) return s;
850
+ }
851
+ return fallbackStore;
852
+ }
660
853
  function isSSR() {
661
- return ssrMode;
854
+ return getSSRStore().ssr;
662
855
  }
663
856
 
664
857
  // src/core/signals/effect.ts
@@ -668,26 +861,114 @@ function effect(effectFn, options) {
668
861
  if (isSSR()) return () => {
669
862
  };
670
863
  const onError = options?.onError;
864
+ let userCleanups = [];
865
+ const onCleanup = (fn) => {
866
+ userCleanups.push(fn);
867
+ };
868
+ const runUserCleanups = () => {
869
+ if (userCleanups.length === 0) return;
870
+ const list = userCleanups;
871
+ userCleanups = [];
872
+ for (let i = list.length - 1; i >= 0; i--) {
873
+ try {
874
+ list[i]();
875
+ } catch (err) {
876
+ if (typeof console !== "undefined") {
877
+ console.warn("[SibuJS effect] onCleanup threw:", err);
878
+ }
879
+ }
880
+ }
881
+ };
882
+ const invokeBody = () => effectFn(onCleanup);
671
883
  const wrappedFn = onError ? () => {
672
884
  try {
673
- effectFn();
885
+ invokeBody();
674
886
  } catch (err) {
675
887
  onError(err);
676
888
  }
677
- } : effectFn;
889
+ } : invokeBody;
678
890
  let cleanupHandle = () => {
679
891
  };
892
+ let running = false;
893
+ let rerunPending = false;
894
+ const MAX_RERUNS = 100;
680
895
  const subscriber = () => {
681
- cleanupHandle();
682
- cleanupHandle = track(wrappedFn, subscriber);
896
+ if (running) {
897
+ rerunPending = true;
898
+ return;
899
+ }
900
+ running = true;
901
+ try {
902
+ let reruns = 0;
903
+ do {
904
+ rerunPending = false;
905
+ runUserCleanups();
906
+ cleanupHandle();
907
+ cleanupHandle = track(wrappedFn, subscriber);
908
+ if (++reruns > MAX_RERUNS) {
909
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
910
+ console.error(
911
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
912
+ );
913
+ }
914
+ rerunPending = false;
915
+ break;
916
+ }
917
+ } while (rerunPending);
918
+ } finally {
919
+ running = false;
920
+ rerunPending = false;
921
+ }
683
922
  };
684
- cleanupHandle = track(wrappedFn, subscriber);
923
+ running = true;
924
+ try {
925
+ let reruns = 0;
926
+ do {
927
+ rerunPending = false;
928
+ runUserCleanups();
929
+ cleanupHandle();
930
+ cleanupHandle = track(wrappedFn, subscriber);
931
+ if (++reruns > MAX_RERUNS) {
932
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
933
+ console.error(
934
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
935
+ );
936
+ }
937
+ rerunPending = false;
938
+ break;
939
+ }
940
+ } while (rerunPending);
941
+ } finally {
942
+ running = false;
943
+ rerunPending = false;
944
+ }
685
945
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
686
946
  if (hook) hook.emit("effect:create", { effectFn });
947
+ let disposed = false;
687
948
  return () => {
949
+ if (disposed) return;
950
+ disposed = true;
688
951
  const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
689
- if (h) h.emit("effect:destroy", { effectFn });
690
- cleanupHandle();
952
+ if (h) {
953
+ try {
954
+ h.emit("effect:destroy", { effectFn });
955
+ } catch {
956
+ }
957
+ }
958
+ try {
959
+ runUserCleanups();
960
+ } catch (err) {
961
+ if (typeof console !== "undefined") {
962
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
963
+ }
964
+ }
965
+ try {
966
+ cleanupHandle();
967
+ } catch (err) {
968
+ if (typeof console !== "undefined") {
969
+ console.warn("[SibuJS effect] dispose threw:", err);
970
+ }
971
+ }
691
972
  };
692
973
  }
693
974
 
@@ -739,6 +1020,7 @@ function intersection(options) {
739
1020
  let observer = null;
740
1021
  let currentElement = null;
741
1022
  function observe(element) {
1023
+ if (typeof IntersectionObserver === "undefined") return;
742
1024
  unobserve();
743
1025
  currentElement = element;
744
1026
  observer = new IntersectionObserver((entries) => {
@@ -766,6 +1048,11 @@ function intersection(options) {
766
1048
  };
767
1049
  }
768
1050
  function lazyLoad(element, loader, options) {
1051
+ if (typeof IntersectionObserver === "undefined") {
1052
+ loader();
1053
+ return () => {
1054
+ };
1055
+ }
769
1056
  const observer = new IntersectionObserver((entries) => {
770
1057
  for (const entry of entries) {
771
1058
  if (entry.isIntersecting) {
@@ -840,7 +1127,7 @@ function inputMask(options) {
840
1127
  const stripRegex = buildStripRegex();
841
1128
  const rawCharTest = options.pattern.includes("*") ? () => true : (c) => /[a-zA-Z0-9]/.test(c);
842
1129
  function bind(input) {
843
- input.addEventListener("input", () => {
1130
+ const onInput = () => {
844
1131
  const cursorBefore = input.selectionStart ?? input.value.length;
845
1132
  const oldValue = input.value;
846
1133
  const raw = oldValue.replace(stripRegex, "");
@@ -864,13 +1151,19 @@ function inputMask(options) {
864
1151
  }
865
1152
  }
866
1153
  input.setSelectionRange(newCursor, newCursor);
867
- });
868
- input.addEventListener("focus", () => {
1154
+ };
1155
+ const onFocus = () => {
869
1156
  if (!input.value) {
870
1157
  const display = options.pattern.replace(/9/g, placeholder).replace(/A/g, placeholder).replace(/\*/g, placeholder);
871
1158
  input.placeholder = display;
872
1159
  }
873
- });
1160
+ };
1161
+ input.addEventListener("input", onInput);
1162
+ input.addEventListener("focus", onFocus);
1163
+ return () => {
1164
+ input.removeEventListener("input", onInput);
1165
+ input.removeEventListener("focus", onFocus);
1166
+ };
874
1167
  }
875
1168
  return { value, rawValue, bind };
876
1169
  }
@@ -912,8 +1205,20 @@ function focus() {
912
1205
  let currentElement = null;
913
1206
  function bind(element) {
914
1207
  currentElement = element;
915
- element.addEventListener("focus", () => setIsFocused(true));
916
- element.addEventListener("blur", () => setIsFocused(false));
1208
+ const onFocus = () => setIsFocused(true);
1209
+ const onBlur = () => setIsFocused(false);
1210
+ element.addEventListener("focus", onFocus);
1211
+ element.addEventListener("blur", onBlur);
1212
+ let disposed = false;
1213
+ const dispose2 = () => {
1214
+ if (disposed) return;
1215
+ disposed = true;
1216
+ element.removeEventListener("focus", onFocus);
1217
+ element.removeEventListener("blur", onBlur);
1218
+ if (currentElement === element) currentElement = null;
1219
+ };
1220
+ registerDisposer(element, dispose2);
1221
+ return dispose2;
917
1222
  }
918
1223
  function focus2() {
919
1224
  currentElement?.focus();
@@ -928,11 +1233,35 @@ function FocusTrap(nodes, options = {}) {
928
1233
  container.setAttribute("data-sibu-focus-trap", "true");
929
1234
  container.appendChild(nodes);
930
1235
  const previouslyFocused = document.activeElement;
931
- container.addEventListener("keydown", (e) => {
1236
+ const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable]';
1237
+ function isEffectivelyVisible(el) {
1238
+ let node = el;
1239
+ while (node) {
1240
+ if (node.hasAttribute("inert")) return false;
1241
+ if (node.getAttribute("aria-hidden") === "true") return false;
1242
+ if (node.hidden) return false;
1243
+ node = node.parentElement;
1244
+ }
1245
+ if (el.offsetParent === null && el.getClientRects().length === 0) return false;
1246
+ return true;
1247
+ }
1248
+ function getFocusable() {
1249
+ const raw = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
1250
+ const out = [];
1251
+ for (const el of raw) {
1252
+ if (el.hasAttribute("disabled")) continue;
1253
+ if (el.getAttribute("aria-hidden") === "true") continue;
1254
+ if (el.hasAttribute("inert")) continue;
1255
+ const ce = el.getAttribute("contenteditable");
1256
+ if (ce !== null && ce === "false") continue;
1257
+ if (!isEffectivelyVisible(el)) continue;
1258
+ out.push(el);
1259
+ }
1260
+ return out;
1261
+ }
1262
+ const onTrapKeydown = (e) => {
932
1263
  if (e.key !== "Tab") return;
933
- const focusable = container.querySelectorAll(
934
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
935
- );
1264
+ const focusable = getFocusable();
936
1265
  if (focusable.length === 0) {
937
1266
  e.preventDefault();
938
1267
  return;
@@ -950,18 +1279,18 @@ function FocusTrap(nodes, options = {}) {
950
1279
  first.focus();
951
1280
  }
952
1281
  }
953
- });
1282
+ };
1283
+ container.addEventListener("keydown", onTrapKeydown);
954
1284
  if (options.autoFocus !== false) {
955
1285
  queueMicrotask(() => {
956
- const first = container.querySelector(
957
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
958
- );
1286
+ const first = getFocusable()[0];
959
1287
  first?.focus();
960
1288
  });
961
1289
  }
962
1290
  let trapObserver = null;
963
1291
  function restoreFocusAndCleanup() {
964
1292
  if (options.restoreFocus !== false) previouslyFocused?.focus();
1293
+ container.removeEventListener("keydown", onTrapKeydown);
965
1294
  if (trapObserver) {
966
1295
  trapObserver.disconnect();
967
1296
  trapObserver = null;
@@ -975,7 +1304,7 @@ function FocusTrap(nodes, options = {}) {
975
1304
  });
976
1305
  queueMicrotask(() => {
977
1306
  if (container.isConnected) {
978
- trapObserver.observe(document.body, { childList: true, subtree: true });
1307
+ trapObserver.observe(container, { childList: true, subtree: true });
979
1308
  }
980
1309
  });
981
1310
  }
@@ -1012,7 +1341,16 @@ function hotkey(combo, handler, options = {}) {
1012
1341
  document.addEventListener("keydown", listener);
1013
1342
  return () => document.removeEventListener("keydown", listener);
1014
1343
  }
1015
- function announce(message, priority = "polite") {
1344
+ var announceQueues = {
1345
+ polite: [],
1346
+ assertive: []
1347
+ };
1348
+ var announceDraining = {
1349
+ polite: false,
1350
+ assertive: false
1351
+ };
1352
+ var ANNOUNCE_INTERVAL_MS = 150;
1353
+ function ensureLiveRegion(priority) {
1016
1354
  let region = document.getElementById(`sibu-announce-${priority}`);
1017
1355
  if (!region) {
1018
1356
  region = document.createElement("div");
@@ -1023,11 +1361,33 @@ function announce(message, priority = "polite") {
1023
1361
  region.style.cssText = "position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;";
1024
1362
  document.body.appendChild(region);
1025
1363
  }
1364
+ return region;
1365
+ }
1366
+ function drainAnnounceQueue(priority) {
1367
+ if (announceDraining[priority]) return;
1368
+ const queue = announceQueues[priority];
1369
+ if (queue.length === 0) return;
1370
+ announceDraining[priority] = true;
1371
+ const region = ensureLiveRegion(priority);
1372
+ const next = queue.shift();
1026
1373
  region.textContent = "";
1027
1374
  requestAnimationFrame(() => {
1028
- if (region) region.textContent = message;
1375
+ if (!region.isConnected) {
1376
+ announceDraining[priority] = false;
1377
+ return;
1378
+ }
1379
+ region.textContent = next;
1380
+ setTimeout(() => {
1381
+ announceDraining[priority] = false;
1382
+ drainAnnounceQueue(priority);
1383
+ }, ANNOUNCE_INTERVAL_MS);
1029
1384
  });
1030
1385
  }
1386
+ function announce(message, priority = "polite") {
1387
+ if (typeof document === "undefined") return;
1388
+ announceQueues[priority].push(message);
1389
+ drainAnnounceQueue(priority);
1390
+ }
1031
1391
 
1032
1392
  // src/core/rendering/createId.ts
1033
1393
  var idCounter = 0;
@@ -1121,22 +1481,22 @@ function createListbox(container, options = {}) {
1121
1481
  }
1122
1482
  }
1123
1483
  function select(value) {
1484
+ const previous = selectedValue();
1485
+ let nextSelectedSet;
1124
1486
  if (multiple) {
1125
- const current2 = selectedValue();
1126
- const set = new Set((current2 ?? "").split(",").filter(Boolean));
1127
- if (set.has(value)) set.delete(value);
1128
- else set.add(value);
1129
- setSelectedValue(Array.from(set).join(","));
1487
+ nextSelectedSet = new Set((previous ?? "").split(",").filter(Boolean));
1488
+ if (nextSelectedSet.has(value)) nextSelectedSet.delete(value);
1489
+ else nextSelectedSet.add(value);
1490
+ setSelectedValue(Array.from(nextSelectedSet).join(","));
1130
1491
  } else {
1492
+ nextSelectedSet = /* @__PURE__ */ new Set([value]);
1131
1493
  setSelectedValue(value);
1132
1494
  }
1133
1495
  options.onSelect?.(value);
1134
1496
  const opts = getOptions();
1135
- const current = selectedValue();
1136
- const selected = new Set((current ?? "").split(",").filter(Boolean));
1137
1497
  for (const opt of opts) {
1138
1498
  const ov = opt.dataset.value ?? "";
1139
- opt.setAttribute("aria-selected", selected.has(ov) ? "true" : "false");
1499
+ opt.setAttribute("aria-selected", nextSelectedSet.has(ov) ? "true" : "false");
1140
1500
  }
1141
1501
  }
1142
1502
  function moveActive(delta) {
@@ -1194,12 +1554,12 @@ function createListbox(container, options = {}) {
1194
1554
  }
1195
1555
  container.addEventListener("keydown", onKeyDown);
1196
1556
  container.addEventListener("click", onClick);
1197
- function dispose() {
1557
+ function dispose2() {
1198
1558
  container.removeEventListener("keydown", onKeyDown);
1199
1559
  container.removeEventListener("click", onClick);
1200
1560
  }
1201
- registerDisposer(container, dispose);
1202
- return { activeValue, selectedValue, activeDescendantId, dispose };
1561
+ registerDisposer(container, dispose2);
1562
+ return { activeValue, selectedValue, activeDescendantId, dispose: dispose2 };
1203
1563
  }
1204
1564
  function createDialogAria(element, options = {}) {
1205
1565
  const titleId = options.labelledBy ?? createId("dialog-title");
@@ -1287,22 +1647,49 @@ function removeScopedStyle(scopeId) {
1287
1647
  }
1288
1648
 
1289
1649
  // src/utils/sanitize.ts
1650
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1290
1651
  function sanitizeUrl(url) {
1291
1652
  const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1292
1653
  if (!trimmed) return "";
1293
1654
  const lower = trimmed.toLowerCase();
1294
- if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
1295
- return "";
1655
+ let schemeEnd = -1;
1656
+ for (let i = 0; i < lower.length; i++) {
1657
+ const ch = lower.charCodeAt(i);
1658
+ if (ch === 58) {
1659
+ schemeEnd = i;
1660
+ break;
1661
+ }
1662
+ if (ch === 47 || ch === 63 || ch === 35) break;
1296
1663
  }
1664
+ if (schemeEnd === -1) return trimmed;
1665
+ const scheme = lower.slice(0, schemeEnd + 1);
1666
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
1667
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1297
1668
  return trimmed;
1298
1669
  }
1299
- var URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "cite", "poster", "background", "srcset"]);
1670
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1671
+ "href",
1672
+ "xlink:href",
1673
+ "src",
1674
+ "action",
1675
+ "formaction",
1676
+ "formtarget",
1677
+ "cite",
1678
+ "poster",
1679
+ "background",
1680
+ "srcset",
1681
+ "ping",
1682
+ "data"
1683
+ ]);
1300
1684
  function isUrlAttribute(attr) {
1301
1685
  return URL_ATTRIBUTES.has(attr);
1302
1686
  }
1303
1687
 
1304
1688
  // src/reactivity/bindAttribute.ts
1305
1689
  var _isDev5 = isDev();
1690
+ function setProp(el, key, val) {
1691
+ el[key] = val;
1692
+ }
1306
1693
  function isEventHandlerAttr(name) {
1307
1694
  if (name.length < 3) return false;
1308
1695
  const lower = name.toLowerCase();
@@ -1328,7 +1715,7 @@ function bindAttribute(el, attr, getter) {
1328
1715
  }
1329
1716
  if (typeof value === "boolean") {
1330
1717
  if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
1331
- el[attr] = value;
1718
+ setProp(el, attr, value);
1332
1719
  } else if (value) {
1333
1720
  el.setAttribute(attr, "");
1334
1721
  } else {
@@ -1338,7 +1725,7 @@ function bindAttribute(el, attr, getter) {
1338
1725
  }
1339
1726
  const str = String(value);
1340
1727
  if ((attr === "value" || attr === "checked") && attr in el) {
1341
- el[attr] = attr === "checked" ? Boolean(value) : str;
1728
+ setProp(el, attr, attr === "checked" ? Boolean(value) : str);
1342
1729
  } else {
1343
1730
  el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
1344
1731
  }
@@ -1408,66 +1795,91 @@ function bindData(el, key, getter) {
1408
1795
  }
1409
1796
 
1410
1797
  // src/ui/dialog.ts
1798
+ var dialogStack = [];
1799
+ var globalListenerAttached = false;
1800
+ function __resetDialogStack() {
1801
+ while (dialogStack.length > 0) dialogStack.pop();
1802
+ if (typeof window !== "undefined" && globalListenerAttached) {
1803
+ window.removeEventListener("keydown", handleGlobalKeydown);
1804
+ globalListenerAttached = false;
1805
+ }
1806
+ }
1807
+ function handleGlobalKeydown(event) {
1808
+ if (event.key !== "Escape") return;
1809
+ const top = dialogStack[dialogStack.length - 1];
1810
+ if (top) top.close();
1811
+ }
1812
+ function ensureGlobalListener() {
1813
+ if (typeof window === "undefined" || globalListenerAttached) return;
1814
+ window.addEventListener("keydown", handleGlobalKeydown);
1815
+ globalListenerAttached = true;
1816
+ }
1817
+ function removeGlobalListenerIfIdle() {
1818
+ if (typeof window === "undefined") return;
1819
+ if (!globalListenerAttached) return;
1820
+ if (dialogStack.length > 0) return;
1821
+ window.removeEventListener("keydown", handleGlobalKeydown);
1822
+ globalListenerAttached = false;
1823
+ }
1411
1824
  function dialog() {
1412
1825
  const [isOpen, setIsOpen] = signal(false);
1413
- let listenerAttached = false;
1414
- function handleKeydown(event) {
1415
- if (event.key === "Escape") {
1416
- close();
1417
- }
1826
+ const entry = { close: () => close() };
1827
+ function pushOnStack() {
1828
+ if (dialogStack.indexOf(entry) !== -1) return;
1829
+ dialogStack.push(entry);
1830
+ ensureGlobalListener();
1418
1831
  }
1419
- function attachListener() {
1420
- if (typeof window !== "undefined" && !listenerAttached) {
1421
- window.addEventListener("keydown", handleKeydown);
1422
- listenerAttached = true;
1423
- }
1424
- }
1425
- function detachListener() {
1426
- if (typeof window !== "undefined" && listenerAttached) {
1427
- window.removeEventListener("keydown", handleKeydown);
1428
- listenerAttached = false;
1429
- }
1832
+ function removeFromStack() {
1833
+ const idx = dialogStack.indexOf(entry);
1834
+ if (idx !== -1) dialogStack.splice(idx, 1);
1835
+ removeGlobalListenerIfIdle();
1430
1836
  }
1431
1837
  function open() {
1838
+ if (isOpen()) return;
1432
1839
  setIsOpen(true);
1433
- attachListener();
1840
+ pushOnStack();
1434
1841
  }
1435
1842
  function close() {
1843
+ if (!isOpen()) {
1844
+ removeFromStack();
1845
+ return;
1846
+ }
1436
1847
  setIsOpen(false);
1437
- detachListener();
1848
+ removeFromStack();
1438
1849
  }
1439
1850
  function toggle() {
1440
1851
  if (isOpen()) close();
1441
1852
  else open();
1442
1853
  }
1443
- function dispose() {
1444
- detachListener();
1854
+ function dispose2() {
1855
+ removeFromStack();
1445
1856
  setIsOpen(false);
1446
1857
  }
1447
- return { open, close, isOpen, toggle, dispose };
1858
+ return { open, close, isOpen, toggle, dispose: dispose2 };
1448
1859
  }
1449
1860
 
1450
1861
  // src/ui/toast.ts
1451
- var toastCounter = 0;
1452
1862
  function toast(options) {
1453
1863
  const duration = options?.duration ?? 3e3;
1454
1864
  const maxToasts = options?.maxToasts ?? Infinity;
1455
1865
  const [toasts, setToasts] = signal([]);
1456
1866
  const timers = /* @__PURE__ */ new Map();
1867
+ let toastCounter = 0;
1457
1868
  function show(message, type) {
1458
1869
  const id = `toast-${++toastCounter}`;
1459
1870
  const toast2 = { id, message, type };
1871
+ const trimmedIds = [];
1460
1872
  setToasts((prev) => {
1461
1873
  const next = [...prev, toast2];
1462
1874
  if (next.length > maxToasts) {
1463
1875
  const removed = next.splice(0, next.length - maxToasts);
1464
- for (const r of removed) {
1465
- clearTimerForToast(r.id);
1466
- }
1876
+ for (const r of removed) trimmedIds.push(r.id);
1467
1877
  }
1468
1878
  return next;
1469
1879
  });
1470
- if (duration > 0) {
1880
+ for (const tid of trimmedIds) clearTimerForToast(tid);
1881
+ const wasTrimmed = trimmedIds.indexOf(id) !== -1;
1882
+ if (duration > 0 && !wasTrimmed) {
1471
1883
  const timer = setTimeout(() => {
1472
1884
  dismiss(id);
1473
1885
  }, duration);
@@ -1553,14 +1965,14 @@ function infiniteScroll(options) {
1553
1965
  },
1554
1966
  configurable: true
1555
1967
  });
1556
- function dispose() {
1968
+ function dispose2() {
1557
1969
  disposed = true;
1558
1970
  if (observer) {
1559
1971
  observer.disconnect();
1560
1972
  observer = null;
1561
1973
  }
1562
1974
  }
1563
- return { sentinelRef: originalRef, loading, dispose };
1975
+ return { sentinelRef: originalRef, loading, dispose: dispose2 };
1564
1976
  }
1565
1977
 
1566
1978
  // src/ui/pagination.ts
@@ -1635,21 +2047,21 @@ function eventBus() {
1635
2047
  // src/ui/lazyEffect.ts
1636
2048
  function lazyEffect(element, effectFn, options) {
1637
2049
  if (typeof IntersectionObserver === "undefined") {
1638
- const dispose2 = effect(effectFn);
1639
- return dispose2;
2050
+ const dispose3 = effect(effectFn);
2051
+ return dispose3;
1640
2052
  }
1641
- let dispose = null;
2053
+ let dispose2 = null;
1642
2054
  let disposed = false;
1643
2055
  const observer = new IntersectionObserver(
1644
2056
  (entries) => {
1645
2057
  if (disposed) return;
1646
2058
  const entry = entries[0];
1647
2059
  if (!entry) return;
1648
- if (entry.isIntersecting && !dispose) {
1649
- dispose = effect(effectFn);
1650
- } else if (!entry.isIntersecting && dispose) {
1651
- dispose();
1652
- dispose = null;
2060
+ if (entry.isIntersecting && !dispose2) {
2061
+ dispose2 = effect(effectFn);
2062
+ } else if (!entry.isIntersecting && dispose2) {
2063
+ dispose2();
2064
+ dispose2 = null;
1653
2065
  }
1654
2066
  },
1655
2067
  { threshold: 0, ...options }
@@ -1658,9 +2070,9 @@ function lazyEffect(element, effectFn, options) {
1658
2070
  return () => {
1659
2071
  disposed = true;
1660
2072
  observer.disconnect();
1661
- if (dispose) {
1662
- dispose();
1663
- dispose = null;
2073
+ if (dispose2) {
2074
+ dispose2();
2075
+ dispose2 = null;
1664
2076
  }
1665
2077
  };
1666
2078
  }
@@ -1717,11 +2129,11 @@ function hover(target) {
1717
2129
  const onLeave = () => setHovered(false);
1718
2130
  target.addEventListener("pointerenter", onEnter);
1719
2131
  target.addEventListener("pointerleave", onLeave);
1720
- function dispose() {
2132
+ function dispose2() {
1721
2133
  target.removeEventListener("pointerenter", onEnter);
1722
2134
  target.removeEventListener("pointerleave", onLeave);
1723
2135
  }
1724
- return { hovered, dispose };
2136
+ return { hovered, dispose: dispose2 };
1725
2137
  }
1726
2138
 
1727
2139
  // src/ui/scrollLock.ts
@@ -1765,7 +2177,7 @@ function defineElement(name, component, options = {}) {
1765
2177
  class SibuElement extends HTMLElement {
1766
2178
  constructor() {
1767
2179
  super();
1768
- this._rendered = false;
2180
+ this._rendered = null;
1769
2181
  if (options.shadow !== false) {
1770
2182
  this._root = this.attachShadow({ mode: options.mode || "open" });
1771
2183
  } else {
@@ -1779,25 +2191,23 @@ function defineElement(name, component, options = {}) {
1779
2191
  this._render();
1780
2192
  }
1781
2193
  disconnectedCallback() {
1782
- if (this._root instanceof ShadowRoot) {
1783
- this._root.innerHTML = "";
1784
- }
1785
- this._rendered = false;
2194
+ this._teardown();
1786
2195
  }
1787
2196
  attributeChangedCallback() {
1788
2197
  if (this._rendered) {
1789
2198
  this._render();
1790
2199
  }
1791
2200
  }
2201
+ _teardown() {
2202
+ if (this._rendered) {
2203
+ dispose(this._rendered);
2204
+ this._rendered = null;
2205
+ }
2206
+ this._root.replaceChildren();
2207
+ }
1792
2208
  _render() {
2209
+ this._teardown();
1793
2210
  const props = this._getProps();
1794
- if (this._root instanceof ShadowRoot) {
1795
- this._root.innerHTML = "";
1796
- } else {
1797
- while (this._root.firstChild) {
1798
- this._root.removeChild(this._root.firstChild);
1799
- }
1800
- }
1801
2211
  if (options.styles && this._root instanceof ShadowRoot) {
1802
2212
  const styleEl = document.createElement("style");
1803
2213
  styleEl.textContent = options.styles;
@@ -1805,7 +2215,7 @@ function defineElement(name, component, options = {}) {
1805
2215
  }
1806
2216
  const el = component(props, this);
1807
2217
  this._root.appendChild(el);
1808
- this._rendered = true;
2218
+ this._rendered = el;
1809
2219
  }
1810
2220
  _getProps() {
1811
2221
  const props = {};
@@ -2004,6 +2414,7 @@ function createGuard(validator) {
2004
2414
  FocusTrap,
2005
2415
  RenderProp,
2006
2416
  VirtualList,
2417
+ __resetDialogStack,
2007
2418
  announce,
2008
2419
  aria,
2009
2420
  assertType,