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
@@ -38,26 +38,39 @@ function isDev() {
38
38
  var _isDev = isDev();
39
39
  function devAssert(condition, message) {
40
40
  if (_isDev && !condition) {
41
- throw new Error(`[Sibu] ${message}`);
41
+ throw new Error(`[SibuJS] ${message}`);
42
42
  }
43
43
  }
44
44
  function devWarn(message) {
45
45
  if (_isDev) {
46
- console.warn(`[Sibu] ${message}`);
46
+ console.warn(`[SibuJS] ${message}`);
47
47
  }
48
48
  }
49
49
 
50
50
  // src/reactivity/track.ts
51
51
  var _isDev2 = isDev();
52
- var subscriberStack = new Array(32);
53
- var stackCapacity = 32;
52
+ var STACK_INITIAL = 32;
53
+ var STACK_SHRINK_THRESHOLD = 128;
54
+ var subscriberStack = new Array(STACK_INITIAL);
55
+ var stackCapacity = STACK_INITIAL;
54
56
  var stackTop = -1;
55
57
  var currentSubscriber = null;
56
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
57
58
  var SUBS = "__s";
59
+ function syncFastPath(signal2, subs) {
60
+ const size = subs.size;
61
+ if (size === 0) {
62
+ signal2.__f = void 0;
63
+ delete signal2[SUBS];
64
+ } else if (size === 1) {
65
+ signal2.__f = subs.values().next().value;
66
+ } else {
67
+ signal2.__f = void 0;
68
+ }
69
+ }
58
70
  var notifyDepth = 0;
59
71
  var pendingQueue = [];
60
72
  var pendingSet = /* @__PURE__ */ new Set();
73
+ var propagateStack = [];
61
74
  function safeInvoke(sub) {
62
75
  try {
63
76
  sub();
@@ -66,6 +79,47 @@ function safeInvoke(sub) {
66
79
  }
67
80
  }
68
81
  var trackingSuspended = false;
82
+ var subscriberEpochCounter = 0;
83
+ function retrack(effectFn, subscriber) {
84
+ const prev = currentSubscriber;
85
+ currentSubscriber = subscriber;
86
+ const sub = subscriber;
87
+ const epoch = ++subscriberEpochCounter;
88
+ sub._epoch = epoch;
89
+ try {
90
+ effectFn();
91
+ } finally {
92
+ currentSubscriber = prev;
93
+ pruneStaleDeps(sub, epoch);
94
+ }
95
+ }
96
+ function pruneStaleDeps(sub, currentEpoch) {
97
+ if (sub._dep !== void 0) {
98
+ if (sub._depEpoch !== currentEpoch) {
99
+ const sig = sub._dep;
100
+ const subs = sig[SUBS];
101
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
102
+ sub._dep = void 0;
103
+ sub._depEpoch = void 0;
104
+ }
105
+ return;
106
+ }
107
+ const deps = sub._deps;
108
+ if (!deps || deps.size === 0) return;
109
+ let stales;
110
+ for (const [signal2, epoch] of deps) {
111
+ if (epoch !== currentEpoch) {
112
+ (stales ?? (stales = [])).push(signal2);
113
+ }
114
+ }
115
+ if (!stales) return;
116
+ for (const signal2 of stales) {
117
+ deps.delete(signal2);
118
+ const sig = signal2;
119
+ const subs = sig[SUBS];
120
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
121
+ }
122
+ }
69
123
  function track(effectFn, subscriber) {
70
124
  if (!subscriber) subscriber = effectFn;
71
125
  cleanup(subscriber);
@@ -81,37 +135,49 @@ function track(effectFn, subscriber) {
81
135
  } finally {
82
136
  stackTop--;
83
137
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
138
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
139
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
140
+ subscriberStack.length = stackCapacity;
141
+ }
84
142
  }
85
143
  return () => cleanup(subscriber);
86
144
  }
87
145
  function recordDependency(signal2) {
88
146
  if (!currentSubscriber) return;
89
147
  const sub = currentSubscriber;
90
- if (sub._dep === signal2) return;
148
+ const epoch = sub._epoch;
149
+ if (sub._dep === signal2) {
150
+ sub._depEpoch = epoch;
151
+ return;
152
+ }
91
153
  const deps = sub._deps;
92
154
  if (deps) {
93
- if (deps.has(signal2)) return;
94
- deps.add(signal2);
155
+ deps.set(signal2, epoch);
95
156
  } else if (sub._dep !== void 0) {
96
- const set = /* @__PURE__ */ new Set();
97
- set.add(sub._dep);
98
- set.add(signal2);
99
- sub._deps = set;
157
+ const map = /* @__PURE__ */ new Map();
158
+ map.set(sub._dep, sub._depEpoch);
159
+ map.set(signal2, epoch);
160
+ sub._deps = map;
100
161
  sub._dep = void 0;
162
+ sub._depEpoch = void 0;
101
163
  } else {
102
164
  sub._dep = signal2;
165
+ sub._depEpoch = epoch;
103
166
  }
104
- let subs = signal2[SUBS];
167
+ const sig = signal2;
168
+ let subs = sig[SUBS];
105
169
  if (!subs) {
106
170
  subs = /* @__PURE__ */ new Set();
107
- signalSubscribers.set(signal2, subs);
108
- signal2[SUBS] = subs;
171
+ sig[SUBS] = subs;
109
172
  }
173
+ const prevSize = subs.size;
110
174
  subs.add(currentSubscriber);
111
- if (subs.size === 1) {
112
- signal2.__f = currentSubscriber;
113
- } else if (signal2.__f !== void 0) {
114
- signal2.__f = void 0;
175
+ if (subs.size !== prevSize) {
176
+ if (subs.size === 1) {
177
+ sig.__f = currentSubscriber;
178
+ } else if (sig.__f !== void 0) {
179
+ sig.__f = void 0;
180
+ }
115
181
  }
116
182
  }
117
183
  function queueSignalNotification(signal2) {
@@ -126,66 +192,102 @@ function queueSignalNotification(signal2) {
126
192
  }
127
193
  }
128
194
  }
129
- var MAX_DRAIN_ITERATIONS = 1e3;
195
+ var maxSubscriberRepeats = 50;
196
+ var maxDrainIterations = 1e6;
197
+ var drainEpoch = 0;
198
+ function tickRepeat(sub) {
199
+ const s = sub;
200
+ if (s._runEpoch !== drainEpoch) {
201
+ s._runEpoch = drainEpoch;
202
+ s._runs = 1;
203
+ return false;
204
+ }
205
+ return ++s._runs > maxSubscriberRepeats;
206
+ }
207
+ function cycleError(sub) {
208
+ if (typeof console !== "undefined") {
209
+ const name = sub.__name ?? "<unnamed>";
210
+ console.error(
211
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
212
+ );
213
+ }
214
+ }
215
+ function absoluteDrainError() {
216
+ if (typeof console !== "undefined") {
217
+ console.error(
218
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
219
+ );
220
+ }
221
+ }
222
+ function drainQueue() {
223
+ let i = 0;
224
+ while (i < pendingQueue.length) {
225
+ if (i >= maxDrainIterations) {
226
+ absoluteDrainError();
227
+ break;
228
+ }
229
+ const sub = pendingQueue[i++];
230
+ if (tickRepeat(sub)) {
231
+ cycleError(sub);
232
+ break;
233
+ }
234
+ pendingSet.delete(sub);
235
+ safeInvoke(sub);
236
+ }
237
+ }
130
238
  function drainNotificationQueue() {
131
239
  if (notifyDepth > 0) return;
132
240
  notifyDepth++;
241
+ drainEpoch++;
133
242
  try {
134
- let i = 0;
135
- while (i < pendingQueue.length) {
136
- if (i >= MAX_DRAIN_ITERATIONS) {
137
- if (typeof console !== "undefined") {
138
- console.error(
139
- `[SibuJS] Notification queue exceeded ${MAX_DRAIN_ITERATIONS} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
140
- );
141
- }
142
- break;
143
- }
144
- safeInvoke(pendingQueue[i]);
145
- i++;
146
- }
243
+ drainQueue();
147
244
  } finally {
148
- pendingQueue.length = 0;
149
- pendingSet.clear();
150
245
  notifyDepth--;
246
+ if (notifyDepth === 0) {
247
+ pendingQueue.length = 0;
248
+ pendingSet.clear();
249
+ }
151
250
  }
152
251
  }
153
252
  function propagateDirty(sub) {
154
253
  sub();
155
- let sig = sub._sig;
156
- while (sig) {
254
+ const rootSig = sub._sig;
255
+ if (!rootSig) return;
256
+ const stack = propagateStack;
257
+ const baseLen = stack.length;
258
+ stack.push(rootSig);
259
+ while (stack.length > baseLen) {
260
+ const sig = stack.pop();
157
261
  const first = sig.__f;
158
262
  if (first) {
159
263
  if (first._c) {
160
264
  const nSig = first._sig;
161
- nSig._d = true;
162
- sig = nSig;
163
- continue;
164
- }
165
- if (!pendingSet.has(first)) {
265
+ if (!nSig._d) {
266
+ nSig._d = true;
267
+ stack.push(nSig);
268
+ }
269
+ } else if (!pendingSet.has(first)) {
166
270
  pendingSet.add(first);
167
271
  pendingQueue.push(first);
168
272
  }
169
- break;
273
+ continue;
170
274
  }
171
275
  const subs = sig[SUBS];
172
- if (!subs) break;
173
- let nextSig;
276
+ if (!subs) continue;
174
277
  for (const s of subs) {
175
278
  if (s._c) {
176
- s();
177
279
  const nSig = s._sig;
178
- if (nSig && !nextSig) {
179
- nextSig = nSig;
180
- } else if (nSig) {
181
- propagateDirty(s);
280
+ if (nSig && !nSig._d) {
281
+ nSig._d = true;
282
+ stack.push(nSig);
283
+ } else if (!nSig) {
284
+ s();
182
285
  }
183
286
  } else if (!pendingSet.has(s)) {
184
287
  pendingSet.add(s);
185
288
  pendingQueue.push(s);
186
289
  }
187
290
  }
188
- sig = nextSig;
189
291
  }
190
292
  }
191
293
  function notifySubscribers(signal2) {
@@ -201,21 +303,22 @@ function notifySubscribers(signal2) {
201
303
  return;
202
304
  }
203
305
  notifyDepth++;
306
+ drainEpoch++;
204
307
  try {
205
308
  if (first._c) {
206
309
  propagateDirty(first);
310
+ } else if (tickRepeat(first)) {
311
+ cycleError(first);
207
312
  } else {
208
313
  safeInvoke(first);
209
314
  }
210
- let i = 0;
211
- while (i < pendingQueue.length) {
212
- safeInvoke(pendingQueue[i]);
213
- i++;
214
- }
315
+ drainQueue();
215
316
  } finally {
216
- pendingQueue.length = 0;
217
- pendingSet.clear();
218
317
  notifyDepth--;
318
+ if (notifyDepth === 0) {
319
+ pendingQueue.length = 0;
320
+ pendingSet.clear();
321
+ }
219
322
  }
220
323
  return;
221
324
  }
@@ -233,66 +336,73 @@ function notifySubscribers(signal2) {
233
336
  return;
234
337
  }
235
338
  notifyDepth++;
339
+ drainEpoch++;
236
340
  try {
237
- let directCount = 0;
238
341
  for (const sub of subs) {
239
- pendingQueue[directCount++] = sub;
240
- }
241
- for (let i2 = 0; i2 < directCount; i2++) {
242
- if (pendingQueue[i2]._c) {
243
- propagateDirty(pendingQueue[i2]);
244
- }
245
- }
246
- for (let i2 = 0; i2 < directCount; i2++) {
247
- if (!pendingQueue[i2]._c) {
248
- if (!pendingSet.has(pendingQueue[i2])) {
249
- safeInvoke(pendingQueue[i2]);
250
- }
342
+ if (sub._c) {
343
+ propagateDirty(sub);
344
+ } else if (!pendingSet.has(sub)) {
345
+ pendingSet.add(sub);
346
+ pendingQueue.push(sub);
251
347
  }
252
348
  }
253
- let i = directCount;
254
- while (i < pendingQueue.length) {
255
- safeInvoke(pendingQueue[i]);
256
- i++;
257
- }
349
+ drainQueue();
258
350
  } finally {
259
- pendingQueue.length = 0;
260
- pendingSet.clear();
261
351
  notifyDepth--;
352
+ if (notifyDepth === 0) {
353
+ pendingQueue.length = 0;
354
+ pendingSet.clear();
355
+ }
262
356
  }
263
357
  }
264
358
  function cleanup(subscriber) {
265
359
  const sub = subscriber;
266
360
  const singleDep = sub._dep;
267
361
  if (singleDep !== void 0) {
268
- const subs = singleDep[SUBS];
269
- if (subs) {
270
- subs.delete(subscriber);
271
- if (singleDep.__f === subscriber) {
272
- singleDep.__f = void 0;
273
- }
362
+ const sig = singleDep;
363
+ const subs = sig[SUBS];
364
+ if (subs?.delete(subscriber)) {
365
+ syncFastPath(sig, subs);
274
366
  }
275
367
  sub._dep = void 0;
368
+ sub._depEpoch = void 0;
276
369
  return;
277
370
  }
278
371
  const deps = sub._deps;
279
372
  if (!deps || deps.size === 0) return;
280
- for (const signal2 of deps) {
281
- const subs = signal2[SUBS];
282
- if (subs) {
283
- subs.delete(subscriber);
284
- if (signal2.__f === subscriber) {
285
- signal2.__f = void 0;
286
- }
373
+ for (const signal2 of deps.keys()) {
374
+ const sig = signal2;
375
+ const subs = sig[SUBS];
376
+ if (subs?.delete(subscriber)) {
377
+ syncFastPath(sig, subs);
287
378
  }
288
379
  }
289
380
  deps.clear();
290
381
  }
291
382
 
292
383
  // src/core/ssr-context.ts
293
- var ssrMode = false;
384
+ var als = null;
385
+ try {
386
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
387
+ const req = Function("return typeof require==='function'?require:null")();
388
+ if (req) {
389
+ const mod = req("node:async_hooks");
390
+ als = new mod.AsyncLocalStorage();
391
+ }
392
+ }
393
+ } catch {
394
+ als = null;
395
+ }
396
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
397
+ function getSSRStore() {
398
+ if (als) {
399
+ const s = als.getStore();
400
+ if (s) return s;
401
+ }
402
+ return fallbackStore;
403
+ }
294
404
  function isSSR() {
295
- return ssrMode;
405
+ return getSSRStore().ssr;
296
406
  }
297
407
 
298
408
  // src/core/signals/effect.ts
@@ -302,26 +412,114 @@ function effect(effectFn, options) {
302
412
  if (isSSR()) return () => {
303
413
  };
304
414
  const onError = options?.onError;
415
+ let userCleanups = [];
416
+ const onCleanup = (fn) => {
417
+ userCleanups.push(fn);
418
+ };
419
+ const runUserCleanups = () => {
420
+ if (userCleanups.length === 0) return;
421
+ const list = userCleanups;
422
+ userCleanups = [];
423
+ for (let i = list.length - 1; i >= 0; i--) {
424
+ try {
425
+ list[i]();
426
+ } catch (err) {
427
+ if (typeof console !== "undefined") {
428
+ console.warn("[SibuJS effect] onCleanup threw:", err);
429
+ }
430
+ }
431
+ }
432
+ };
433
+ const invokeBody = () => effectFn(onCleanup);
305
434
  const wrappedFn = onError ? () => {
306
435
  try {
307
- effectFn();
436
+ invokeBody();
308
437
  } catch (err) {
309
438
  onError(err);
310
439
  }
311
- } : effectFn;
440
+ } : invokeBody;
312
441
  let cleanupHandle = () => {
313
442
  };
443
+ let running = false;
444
+ let rerunPending = false;
445
+ const MAX_RERUNS = 100;
314
446
  const subscriber = () => {
315
- cleanupHandle();
316
- cleanupHandle = track(wrappedFn, subscriber);
447
+ if (running) {
448
+ rerunPending = true;
449
+ return;
450
+ }
451
+ running = true;
452
+ try {
453
+ let reruns = 0;
454
+ do {
455
+ rerunPending = false;
456
+ runUserCleanups();
457
+ cleanupHandle();
458
+ cleanupHandle = track(wrappedFn, subscriber);
459
+ if (++reruns > MAX_RERUNS) {
460
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
461
+ console.error(
462
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
463
+ );
464
+ }
465
+ rerunPending = false;
466
+ break;
467
+ }
468
+ } while (rerunPending);
469
+ } finally {
470
+ running = false;
471
+ rerunPending = false;
472
+ }
317
473
  };
318
- cleanupHandle = track(wrappedFn, subscriber);
474
+ running = true;
475
+ try {
476
+ let reruns = 0;
477
+ do {
478
+ rerunPending = false;
479
+ runUserCleanups();
480
+ cleanupHandle();
481
+ cleanupHandle = track(wrappedFn, subscriber);
482
+ if (++reruns > MAX_RERUNS) {
483
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
484
+ console.error(
485
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
486
+ );
487
+ }
488
+ rerunPending = false;
489
+ break;
490
+ }
491
+ } while (rerunPending);
492
+ } finally {
493
+ running = false;
494
+ rerunPending = false;
495
+ }
319
496
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
320
497
  if (hook) hook.emit("effect:create", { effectFn });
498
+ let disposed = false;
321
499
  return () => {
500
+ if (disposed) return;
501
+ disposed = true;
322
502
  const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
323
- if (h) h.emit("effect:destroy", { effectFn });
324
- cleanupHandle();
503
+ if (h) {
504
+ try {
505
+ h.emit("effect:destroy", { effectFn });
506
+ } catch {
507
+ }
508
+ }
509
+ try {
510
+ runUserCleanups();
511
+ } catch (err) {
512
+ if (typeof console !== "undefined") {
513
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
514
+ }
515
+ }
516
+ try {
517
+ cleanupHandle();
518
+ } catch (err) {
519
+ if (typeof console !== "undefined") {
520
+ console.warn("[SibuJS effect] dispose threw:", err);
521
+ }
522
+ }
325
523
  };
326
524
  }
327
525
 
@@ -345,10 +543,13 @@ function enqueueBatchedSignal(signal2) {
345
543
  return true;
346
544
  }
347
545
  function flushBatch() {
348
- for (const signal2 of pendingSignals) {
349
- queueSignalNotification(signal2);
546
+ try {
547
+ for (const signal2 of pendingSignals) {
548
+ queueSignalNotification(signal2);
549
+ }
550
+ } finally {
551
+ pendingSignals.clear();
350
552
  }
351
- pendingSignals.clear();
352
553
  drainNotificationQueue();
353
554
  }
354
555
 
@@ -391,6 +592,85 @@ function signal(initial, options) {
391
592
  }
392
593
 
393
594
  // src/plugins/plugin.ts
595
+ function createPluginRegistry() {
596
+ const installedPlugins = /* @__PURE__ */ new Set();
597
+ const hooks = { init: [], mount: [], unmount: [], error: [] };
598
+ const provided = /* @__PURE__ */ new Map();
599
+ const registry = {
600
+ installedPlugins,
601
+ hooks,
602
+ provided,
603
+ plugin(p, options) {
604
+ if (installedPlugins.has(p.name)) {
605
+ console.warn(`[Plugin] "${p.name}" is already installed.`);
606
+ return;
607
+ }
608
+ const ctx = {
609
+ onInit: (cb) => hooks.init.push(cb),
610
+ onMount: (cb) => hooks.mount.push(cb),
611
+ onUnmount: (cb) => hooks.unmount.push(cb),
612
+ onError: (cb) => hooks.error.push(cb),
613
+ provide: (key, value) => provided.set(key, value)
614
+ };
615
+ const initHooksBefore = hooks.init.length;
616
+ p.install(ctx, options);
617
+ installedPlugins.add(p.name);
618
+ const justAdded = hooks.init.slice(initHooksBefore);
619
+ for (const cb of justAdded) {
620
+ try {
621
+ cb();
622
+ } catch (e) {
623
+ console.error(`[Plugin] "${p.name}" init error:`, e);
624
+ }
625
+ }
626
+ },
627
+ inject(key, defaultValue) {
628
+ if (provided.has(key)) return provided.get(key);
629
+ if (defaultValue !== void 0) return defaultValue;
630
+ throw new Error(`[Plugin] No provider found for key "${key}"`);
631
+ },
632
+ triggerMount(element) {
633
+ const snapshot = hooks.mount.slice();
634
+ for (const hook of snapshot) {
635
+ try {
636
+ hook(element);
637
+ } catch (e) {
638
+ console.error("[Plugin] Mount hook error:", e);
639
+ }
640
+ }
641
+ },
642
+ triggerUnmount(element) {
643
+ const snapshot = hooks.unmount.slice();
644
+ for (const hook of snapshot) {
645
+ try {
646
+ hook(element);
647
+ } catch (e) {
648
+ console.error("[Plugin] Unmount hook error:", e);
649
+ }
650
+ }
651
+ },
652
+ triggerError(error) {
653
+ const snapshot = hooks.error.slice();
654
+ for (const hook of snapshot) {
655
+ try {
656
+ hook(error);
657
+ } catch (e) {
658
+ console.error("[Plugin] Error hook error:", e);
659
+ }
660
+ }
661
+ },
662
+ reset() {
663
+ installedPlugins.clear();
664
+ hooks.init.length = 0;
665
+ hooks.mount.length = 0;
666
+ hooks.unmount.length = 0;
667
+ hooks.error.length = 0;
668
+ provided.clear();
669
+ }
670
+ };
671
+ return registry;
672
+ }
673
+ var defaultRegistry = createPluginRegistry();
394
674
  function createPlugin(name, install) {
395
675
  return { name, install };
396
676
  }
@@ -401,7 +681,7 @@ function mobXAdapter(options) {
401
681
  const { autorun } = options;
402
682
  const disposers = [];
403
683
  function fromMobX(expression) {
404
- const [getValue, setValue] = signal(expression());
684
+ const [getValue, setValue] = signal(void 0);
405
685
  const disposer = autorun(() => {
406
686
  const newValue = expression();
407
687
  batch(() => {
@@ -409,7 +689,13 @@ function mobXAdapter(options) {
409
689
  });
410
690
  });
411
691
  disposers.push(disposer);
412
- return getValue;
692
+ const getter = (() => getValue());
693
+ getter.dispose = () => {
694
+ const i = disposers.indexOf(disposer);
695
+ if (i >= 0) disposers.splice(i, 1);
696
+ disposer();
697
+ };
698
+ return getter;
413
699
  }
414
700
  function toMobX(sibuGetter, callback) {
415
701
  return effect(() => {
@@ -431,6 +717,7 @@ function mobXAdapter(options) {
431
717
  function derived(getter, options) {
432
718
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
433
719
  const debugName = options?.name;
720
+ const equals = options?.equals;
434
721
  const cs = {};
435
722
  cs._d = false;
436
723
  cs._g = getter;
@@ -441,8 +728,14 @@ function derived(getter, options) {
441
728
  markDirty._c = 1;
442
729
  markDirty._sig = cs;
443
730
  track(() => {
444
- cs._d = false;
445
- cs._v = getter();
731
+ let threw = true;
732
+ try {
733
+ cs._v = getter();
734
+ cs._d = false;
735
+ threw = false;
736
+ } finally {
737
+ if (threw) cs._d = true;
738
+ }
446
739
  }, markDirty);
447
740
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
448
741
  let evaluating = false;
@@ -455,11 +748,16 @@ function derived(getter, options) {
455
748
  if (trackingSuspended) {
456
749
  if (cs._d) {
457
750
  evaluating = true;
751
+ let threw = true;
458
752
  try {
459
- cs._d = false;
460
- cs._v = getter();
753
+ retrack(() => {
754
+ cs._v = getter();
755
+ cs._d = false;
756
+ threw = false;
757
+ }, markDirty);
461
758
  } finally {
462
759
  evaluating = false;
760
+ if (threw) cs._d = true;
463
761
  }
464
762
  }
465
763
  return cs._v;
@@ -468,13 +766,17 @@ function derived(getter, options) {
468
766
  if (cs._d) {
469
767
  const oldValue = cs._v;
470
768
  evaluating = true;
769
+ let threw = true;
471
770
  try {
472
- track(() => {
771
+ retrack(() => {
772
+ const next = getter();
773
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
473
774
  cs._d = false;
474
- cs._v = getter();
775
+ threw = false;
475
776
  }, markDirty);
476
777
  } finally {
477
778
  evaluating = false;
779
+ if (threw) cs._d = true;
478
780
  }
479
781
  if (hook && oldValue !== cs._v) {
480
782
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
@@ -501,12 +803,12 @@ function reduxAdapter(options) {
501
803
  setState(store.getState());
502
804
  });
503
805
  });
504
- function useSelector(selector) {
806
+ function select(selector) {
505
807
  return derived(() => selector(getState()));
506
808
  }
507
809
  const api = {
508
810
  getState,
509
- useSelector,
811
+ select,
510
812
  dispatch: store.dispatch.bind(store),
511
813
  destroy: unsubscribe
512
814
  };
@@ -524,12 +826,12 @@ function zustandAdapter(options) {
524
826
  setSibuState(state);
525
827
  });
526
828
  });
527
- function useSelector(selector) {
829
+ function select(selector) {
528
830
  return derived(() => selector(getState()));
529
831
  }
530
832
  const api = {
531
833
  getState,
532
- useSelector,
834
+ select,
533
835
  setState: store.setState.bind(store),
534
836
  destroy() {
535
837
  unsubscribe();
@@ -541,29 +843,79 @@ function zustandAdapter(options) {
541
843
  }
542
844
 
543
845
  // src/utils/sanitize.ts
846
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
544
847
  function sanitizeUrl(url) {
545
848
  const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
546
849
  if (!trimmed) return "";
547
850
  const lower = trimmed.toLowerCase();
548
- if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
549
- return "";
851
+ let schemeEnd = -1;
852
+ for (let i = 0; i < lower.length; i++) {
853
+ const ch = lower.charCodeAt(i);
854
+ if (ch === 58) {
855
+ schemeEnd = i;
856
+ break;
857
+ }
858
+ if (ch === 47 || ch === 63 || ch === 35) break;
550
859
  }
860
+ if (schemeEnd === -1) return trimmed;
861
+ const scheme = lower.slice(0, schemeEnd + 1);
862
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
863
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
551
864
  return trimmed;
552
865
  }
866
+ function sanitizeSrcset(value) {
867
+ const parts = value.split(",");
868
+ const out = [];
869
+ for (let i = 0; i < parts.length; i++) {
870
+ const part = parts[i].trim();
871
+ if (!part) continue;
872
+ const m = part.match(/^(\S+)(\s+.+)?$/);
873
+ if (!m) continue;
874
+ const safe = sanitizeUrl(m[1]);
875
+ if (!safe) continue;
876
+ out.push(m[2] ? `${safe}${m[2]}` : safe);
877
+ }
878
+ return out.join(", ");
879
+ }
553
880
  function sanitizeCSSValue(value) {
554
- const lower = value.toLowerCase().replace(/\s+/g, "");
555
- if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("-moz-binding")) {
881
+ const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
882
+ const code = Number.parseInt(hex, 16);
883
+ if (!Number.isFinite(code) || code < 0 || code > 1114111) return "";
884
+ try {
885
+ return String.fromCodePoint(code);
886
+ } catch {
887
+ return "";
888
+ }
889
+ });
890
+ const lower = decoded.toLowerCase().replace(/\s+/g, "");
891
+ if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("vbscript:") || lower.includes("-moz-binding") || lower.includes("behavior:") || lower.includes("@import") || lower.includes("image-set(") || lower.includes("filter:progid")) {
556
892
  return "";
557
893
  }
558
894
  return value;
559
895
  }
560
- var URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "cite", "poster", "background", "srcset"]);
896
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
897
+ "href",
898
+ "xlink:href",
899
+ "src",
900
+ "action",
901
+ "formaction",
902
+ "formtarget",
903
+ "cite",
904
+ "poster",
905
+ "background",
906
+ "srcset",
907
+ "ping",
908
+ "data"
909
+ ]);
561
910
  function isUrlAttribute(attr) {
562
911
  return URL_ATTRIBUTES.has(attr);
563
912
  }
564
913
 
565
914
  // src/reactivity/bindAttribute.ts
566
915
  var _isDev4 = isDev();
916
+ function setProp(el, key, val) {
917
+ el[key] = val;
918
+ }
567
919
  function isEventHandlerAttr(name) {
568
920
  if (name.length < 3) return false;
569
921
  const lower = name.toLowerCase();
@@ -589,7 +941,7 @@ function bindAttribute(el, attr, getter) {
589
941
  }
590
942
  if (typeof value === "boolean") {
591
943
  if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
592
- el[attr] = value;
944
+ setProp(el, attr, value);
593
945
  } else if (value) {
594
946
  el.setAttribute(attr, "");
595
947
  } else {
@@ -599,7 +951,7 @@ function bindAttribute(el, attr, getter) {
599
951
  }
600
952
  const str = String(value);
601
953
  if ((attr === "value" || attr === "checked") && attr in el) {
602
- el[attr] = attr === "checked" ? Boolean(value) : str;
954
+ setProp(el, attr, attr === "checked" ? Boolean(value) : str);
603
955
  } else {
604
956
  el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
605
957
  }
@@ -636,24 +988,29 @@ function bindChildNode(placeholder, getter) {
636
988
  let newNodes;
637
989
  if (Array.isArray(result)) {
638
990
  newNodes = [];
991
+ const seen = /* @__PURE__ */ new Set();
639
992
  for (let i = 0; i < result.length; i++) {
640
993
  const item = result[i];
641
994
  if (item == null || typeof item === "boolean") continue;
642
- newNodes.push(item instanceof Node ? item : document.createTextNode(String(item)));
995
+ const node = item instanceof Node ? item : document.createTextNode(String(item));
996
+ if (seen.has(node)) {
997
+ if (_isDev5)
998
+ devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
999
+ continue;
1000
+ }
1001
+ seen.add(node);
1002
+ newNodes.push(node);
643
1003
  }
644
1004
  } else {
645
1005
  const node = result instanceof Node ? result : document.createTextNode(String(result));
646
1006
  newNodes = [node];
647
1007
  }
648
- const reused = lastNodes.length > 0 && newNodes.length > 0 ? /* @__PURE__ */ new Set() : void 0;
649
- if (reused) {
1008
+ let reused;
1009
+ if (lastNodes.length > 0 && newNodes.length > 0) {
1010
+ const lastSet = new Set(lastNodes);
1011
+ reused = /* @__PURE__ */ new Set();
650
1012
  for (let i = 0; i < newNodes.length; i++) {
651
- for (let j = 0; j < lastNodes.length; j++) {
652
- if (newNodes[i] === lastNodes[j]) {
653
- reused.add(newNodes[i]);
654
- break;
655
- }
656
- }
1013
+ if (lastSet.has(newNodes[i])) reused.add(newNodes[i]);
657
1014
  }
658
1015
  }
659
1016
  for (let i = 0; i < lastNodes.length; i++) {
@@ -692,6 +1049,30 @@ function registerDisposer(node, teardown) {
692
1049
  }
693
1050
 
694
1051
  // src/core/rendering/tagFactory.ts
1052
+ var _isDev7 = isDev();
1053
+ var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
1054
+ function validateTagName(tag) {
1055
+ const lower = tag.toLowerCase();
1056
+ if (BLOCKED_TAGS.has(lower)) {
1057
+ throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
1058
+ }
1059
+ }
1060
+ var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
1061
+ "config",
1062
+ "location",
1063
+ "history",
1064
+ "document",
1065
+ "window",
1066
+ "navigator",
1067
+ "name",
1068
+ "top",
1069
+ "parent",
1070
+ "self",
1071
+ "frames"
1072
+ ]);
1073
+ function setProp2(el, key, val) {
1074
+ el[key] = val;
1075
+ }
695
1076
  var kebabCache = /* @__PURE__ */ new Map();
696
1077
  function toKebab(prop) {
697
1078
  let cached = kebabCache.get(prop);
@@ -816,79 +1197,103 @@ function appendChildren(el, nodes) {
816
1197
  }
817
1198
  }
818
1199
  }
819
- var tagFactory = (tag, ns) => (first, second) => {
820
- const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
821
- if (first === void 0) return el;
822
- if (typeof first === "string") {
823
- if (second !== void 0) {
824
- el.setAttribute("class", first);
825
- appendChildren(el, second);
1200
+ var tagFactory = (tag, ns) => {
1201
+ return (first, second) => {
1202
+ validateTagName(tag);
1203
+ const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
1204
+ if (first === void 0) return el;
1205
+ if (typeof first === "string") {
1206
+ if (second !== void 0) {
1207
+ el.setAttribute("class", first);
1208
+ appendChildren(el, second);
1209
+ return el;
1210
+ }
1211
+ el.textContent = first;
826
1212
  return el;
827
1213
  }
828
- el.textContent = first;
829
- return el;
830
- }
831
- if (typeof first === "number") {
832
- el.textContent = String(first);
833
- return el;
834
- }
835
- if (Array.isArray(first) || first instanceof Node || typeof first === "function") {
836
- appendChildren(el, first);
837
- return el;
838
- }
839
- const props = first;
840
- const pClass = props.class;
841
- if (pClass != null) applyClass(el, pClass);
842
- const pId = props.id;
843
- if (pId != null) el.id = pId;
844
- const pNodes = second !== void 0 ? second : props.nodes;
845
- if (pNodes != null) appendChildren(el, pNodes);
846
- const pOn = props.on;
847
- if (pOn) {
848
- for (const ev in pOn) {
849
- el.addEventListener(ev, pOn[ev]);
850
- }
851
- }
852
- const pStyle = props.style;
853
- if (pStyle != null) applyStyle(el, pStyle);
854
- const pRef = props.ref;
855
- if (pRef) pRef.current = el;
856
- for (const key in props) {
857
- switch (key) {
858
- case "class":
859
- case "id":
860
- case "nodes":
861
- case "on":
862
- case "style":
863
- case "ref":
864
- case "onElement":
865
- continue;
866
- // already handled above / below
867
- default: {
868
- const value = props[key];
869
- if (value == null) continue;
870
- if (key[0] === "o" && key[1] === "n") continue;
871
- if (typeof value === "function") {
872
- registerDisposer(el, bindAttribute(el, key, value));
873
- } else if (typeof value === "boolean") {
874
- if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
875
- el[key] = value;
876
- } else if (value) {
877
- el.setAttribute(key, "");
1214
+ if (typeof first === "number") {
1215
+ el.textContent = String(first);
1216
+ return el;
1217
+ }
1218
+ if (Array.isArray(first) || first instanceof Node || typeof first === "function") {
1219
+ appendChildren(el, first);
1220
+ return el;
1221
+ }
1222
+ const props = first;
1223
+ const pClass = props.class;
1224
+ if (pClass != null) applyClass(el, pClass);
1225
+ const pId = props.id;
1226
+ if (pId != null) {
1227
+ if (_isDev7 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1228
+ devWarn(
1229
+ `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
1230
+ );
1231
+ }
1232
+ el.id = pId;
1233
+ }
1234
+ const pNodes = second !== void 0 ? second : props.nodes;
1235
+ if (pNodes != null) appendChildren(el, pNodes);
1236
+ const pOn = props.on;
1237
+ if (pOn) {
1238
+ for (const ev in pOn) {
1239
+ const handler = pOn[ev];
1240
+ if (typeof handler === "function") {
1241
+ el.addEventListener(ev, handler);
1242
+ } else if (_isDev7) {
1243
+ devWarn(
1244
+ `tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
1245
+ );
1246
+ }
1247
+ }
1248
+ }
1249
+ const pStyle = props.style;
1250
+ if (pStyle != null) applyStyle(el, pStyle);
1251
+ const pRef = props.ref;
1252
+ if (pRef) pRef.current = el;
1253
+ for (const key in props) {
1254
+ switch (key) {
1255
+ case "class":
1256
+ case "id":
1257
+ case "nodes":
1258
+ case "on":
1259
+ case "style":
1260
+ case "ref":
1261
+ case "onElement":
1262
+ continue;
1263
+ // already handled above / below
1264
+ default: {
1265
+ const value = props[key];
1266
+ if (value == null) continue;
1267
+ const lkey = key.toLowerCase();
1268
+ if (lkey[0] === "o" && lkey[1] === "n") continue;
1269
+ if (typeof value === "function") {
1270
+ registerDisposer(el, bindAttribute(el, key, value));
1271
+ } else if (typeof value === "boolean") {
1272
+ if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
1273
+ setProp2(el, key, value);
1274
+ } else if (value) {
1275
+ el.setAttribute(key, "");
1276
+ } else {
1277
+ el.removeAttribute(key);
1278
+ }
878
1279
  } else {
879
- el.removeAttribute(key);
1280
+ const str = String(value);
1281
+ if (lkey === "srcset") {
1282
+ el.setAttribute(key, sanitizeSrcset(str));
1283
+ } else if (isUrlAttribute(lkey)) {
1284
+ el.setAttribute(key, sanitizeUrl(str));
1285
+ } else {
1286
+ el.setAttribute(key, str);
1287
+ }
880
1288
  }
881
- } else {
882
- const str = String(value);
883
- el.setAttribute(key, isUrlAttribute(key) ? sanitizeUrl(str) : str);
884
1289
  }
885
1290
  }
886
1291
  }
887
- }
888
- if (props.onElement && typeof props.onElement === "function") {
889
- props.onElement(el);
890
- }
891
- return el;
1292
+ if (props.onElement && typeof props.onElement === "function") {
1293
+ props.onElement(el);
1294
+ }
1295
+ return el;
1296
+ };
892
1297
  };
893
1298
 
894
1299
  // src/ecosystem/ui/componentAdapter.ts