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/testing.cjs CHANGED
@@ -60,11 +60,75 @@ __export(testing_exports, {
60
60
  snapshotComponent: () => snapshotComponent,
61
61
  testComponent: () => testComponent,
62
62
  type: () => type,
63
+ unmountAll: () => unmountAll,
63
64
  waitFor: () => waitFor,
64
65
  waitForSignal: () => waitForSignal
65
66
  });
66
67
  module.exports = __toCommonJS(testing_exports);
67
68
 
69
+ // src/core/dev.ts
70
+ function isDev() {
71
+ return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
72
+ }
73
+ var _isDev = isDev();
74
+ function devAssert(condition, message) {
75
+ if (_isDev && !condition) {
76
+ throw new Error(`[SibuJS] ${message}`);
77
+ }
78
+ }
79
+
80
+ // src/core/rendering/dispose.ts
81
+ var elementDisposers = /* @__PURE__ */ new WeakMap();
82
+ var _isDev2 = isDev();
83
+ var activeBindingCount = 0;
84
+ function dispose(node) {
85
+ const stack = [node];
86
+ const order = [];
87
+ while (stack.length > 0) {
88
+ const current = stack.pop();
89
+ order.push(current);
90
+ const children = Array.from(current.childNodes);
91
+ for (let i = 0; i < children.length; i++) {
92
+ stack.push(children[i]);
93
+ }
94
+ }
95
+ for (let i = order.length - 1; i >= 0; i--) {
96
+ const current = order[i];
97
+ const disposers = elementDisposers.get(current);
98
+ if (disposers) {
99
+ const snapshot = disposers.slice();
100
+ elementDisposers.delete(current);
101
+ if (_isDev2) activeBindingCount -= snapshot.length;
102
+ for (const d of snapshot) {
103
+ try {
104
+ d();
105
+ } catch (err) {
106
+ if (_isDev2 && typeof console !== "undefined") {
107
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
108
+ }
109
+ }
110
+ }
111
+ let extraPasses = 0;
112
+ while (extraPasses++ < 8) {
113
+ const added = elementDisposers.get(current);
114
+ if (!added || added.length === 0) break;
115
+ const moreSnapshot = added.slice();
116
+ elementDisposers.delete(current);
117
+ if (_isDev2) activeBindingCount -= moreSnapshot.length;
118
+ for (const d of moreSnapshot) {
119
+ try {
120
+ d();
121
+ } catch (err) {
122
+ if (_isDev2 && typeof console !== "undefined") {
123
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+
68
132
  // src/testing/a11y.ts
69
133
  var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
70
134
  "alert",
@@ -1200,8 +1264,9 @@ function createUniversalAdapter() {
1200
1264
  }
1201
1265
 
1202
1266
  // src/testing/e2e.ts
1203
- function createHttpMock(routes = []) {
1267
+ function createHttpMock(routes = [], options = {}) {
1204
1268
  const originalFetch = globalThis.fetch;
1269
+ const hadOriginalFetch = Object.hasOwn(globalThis, "fetch");
1205
1270
  const requestLog = [];
1206
1271
  const mockRoutes = [...routes];
1207
1272
  function matchRoute(url, method) {
@@ -1222,29 +1287,41 @@ function createHttpMock(routes = []) {
1222
1287
  return new Response(JSON.stringify({ error: "Not mocked" }), { status: 404 });
1223
1288
  }
1224
1289
  let mockResponse;
1225
- if (typeof route.response === "function") {
1226
- mockResponse = await route.response({ url, method, body, headers: new Headers(init?.headers) });
1227
- } else {
1228
- mockResponse = route.response;
1290
+ try {
1291
+ if (typeof route.response === "function") {
1292
+ mockResponse = await route.response({ url, method, body, headers: new Headers(init?.headers) });
1293
+ } else {
1294
+ mockResponse = route.response;
1295
+ }
1296
+ if (mockResponse.delay) {
1297
+ await new Promise((r) => setTimeout(r, mockResponse.delay));
1298
+ }
1299
+ return new Response(
1300
+ typeof mockResponse.body === "string" ? mockResponse.body : JSON.stringify(mockResponse.body),
1301
+ {
1302
+ status: mockResponse.status || 200,
1303
+ statusText: mockResponse.statusText || "OK",
1304
+ headers: mockResponse.headers
1305
+ }
1306
+ );
1307
+ } catch (err) {
1308
+ return new Response(JSON.stringify({ error: String(err) }), { status: 500 });
1229
1309
  }
1230
- if (mockResponse.delay) {
1231
- await new Promise((r) => setTimeout(r, mockResponse.delay));
1310
+ };
1311
+ const restore = () => {
1312
+ if (hadOriginalFetch) {
1313
+ globalThis.fetch = originalFetch;
1314
+ } else {
1315
+ delete globalThis.fetch;
1232
1316
  }
1233
- return new Response(typeof mockResponse.body === "string" ? mockResponse.body : JSON.stringify(mockResponse.body), {
1234
- status: mockResponse.status || 200,
1235
- statusText: mockResponse.statusText || "OK",
1236
- headers: mockResponse.headers
1237
- });
1238
1317
  };
1239
- return {
1318
+ const api = {
1240
1319
  /** Install the mock (replace global fetch) */
1241
1320
  install() {
1242
1321
  globalThis.fetch = mockFetch;
1243
1322
  },
1244
1323
  /** Restore original fetch */
1245
- restore() {
1246
- globalThis.fetch = originalFetch;
1247
- },
1324
+ restore,
1248
1325
  /** Add a mock route */
1249
1326
  addRoute(route) {
1250
1327
  mockRoutes.push(route);
@@ -1276,18 +1353,29 @@ function createHttpMock(routes = []) {
1276
1353
  return requestLog.filter((r) => r.url.includes(url) && r.method.toUpperCase() === method.toUpperCase()).length;
1277
1354
  }
1278
1355
  };
1356
+ if (typeof options.afterEach === "function") {
1357
+ options.afterEach(() => api.restore());
1358
+ }
1359
+ return api;
1279
1360
  }
1280
- function createTimerMock() {
1281
- const originalSetTimeout = globalThis.setTimeout;
1282
- const originalSetInterval = globalThis.setInterval;
1283
- const originalClearTimeout = globalThis.clearTimeout;
1284
- const originalClearInterval = globalThis.clearInterval;
1285
- const originalRAF = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : void 0;
1286
- const originalCAF = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : void 0;
1361
+ function createTimerMock(options = {}) {
1362
+ const g = globalThis;
1363
+ const snapshot = (key) => ({
1364
+ had: Object.hasOwn(globalThis, key),
1365
+ value: g[key]
1366
+ });
1367
+ const saved = {
1368
+ setTimeout: snapshot("setTimeout"),
1369
+ setInterval: snapshot("setInterval"),
1370
+ clearTimeout: snapshot("clearTimeout"),
1371
+ clearInterval: snapshot("clearInterval"),
1372
+ requestAnimationFrame: snapshot("requestAnimationFrame"),
1373
+ cancelAnimationFrame: snapshot("cancelAnimationFrame")
1374
+ };
1287
1375
  let currentTime = 0;
1288
1376
  let nextId = 1;
1289
1377
  const timers = [];
1290
- return {
1378
+ const api = {
1291
1379
  install() {
1292
1380
  currentTime = 0;
1293
1381
  globalThis.setTimeout = (cb, delay = 0) => {
@@ -1319,12 +1407,13 @@ function createTimerMock() {
1319
1407
  };
1320
1408
  },
1321
1409
  restore() {
1322
- globalThis.setTimeout = originalSetTimeout;
1323
- globalThis.setInterval = originalSetInterval;
1324
- globalThis.clearTimeout = originalClearTimeout;
1325
- globalThis.clearInterval = originalClearInterval;
1326
- if (originalRAF) globalThis.requestAnimationFrame = originalRAF;
1327
- if (originalCAF) globalThis.cancelAnimationFrame = originalCAF;
1410
+ for (const [key, snap] of Object.entries(saved)) {
1411
+ if (snap.had) {
1412
+ g[key] = snap.value;
1413
+ } else {
1414
+ delete g[key];
1415
+ }
1416
+ }
1328
1417
  timers.length = 0;
1329
1418
  },
1330
1419
  /** Advance time by a given number of ms, running any timers that fire */
@@ -1370,6 +1459,10 @@ function createTimerMock() {
1370
1459
  return timers.length;
1371
1460
  }
1372
1461
  };
1462
+ if (typeof options.afterEach === "function") {
1463
+ options.afterEach(() => api.restore());
1464
+ }
1465
+ return api;
1373
1466
  }
1374
1467
  function createDOMSnapshot(element) {
1375
1468
  return serializeElement(element, 0);
@@ -1454,24 +1547,26 @@ function testComponent(component, options = {}) {
1454
1547
  };
1455
1548
  }
1456
1549
 
1457
- // src/core/dev.ts
1458
- function isDev() {
1459
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1460
- }
1461
- var _isDev = isDev();
1462
- function devAssert(condition, message) {
1463
- if (_isDev && !condition) {
1464
- throw new Error(`[Sibu] ${message}`);
1465
- }
1466
- }
1467
-
1468
1550
  // src/reactivity/track.ts
1469
- var _isDev2 = isDev();
1470
- var subscriberStack = new Array(32);
1471
- var stackCapacity = 32;
1551
+ var _isDev3 = isDev();
1552
+ var STACK_INITIAL = 32;
1553
+ var STACK_SHRINK_THRESHOLD = 128;
1554
+ var subscriberStack = new Array(STACK_INITIAL);
1555
+ var stackCapacity = STACK_INITIAL;
1472
1556
  var stackTop = -1;
1473
1557
  var currentSubscriber = null;
1474
1558
  var SUBS = "__s";
1559
+ function syncFastPath(signal, subs) {
1560
+ const size = subs.size;
1561
+ if (size === 0) {
1562
+ signal.__f = void 0;
1563
+ delete signal[SUBS];
1564
+ } else if (size === 1) {
1565
+ signal.__f = subs.values().next().value;
1566
+ } else {
1567
+ signal.__f = void 0;
1568
+ }
1569
+ }
1475
1570
  function track(effectFn, subscriber) {
1476
1571
  if (!subscriber) subscriber = effectFn;
1477
1572
  cleanup(subscriber);
@@ -1487,6 +1582,10 @@ function track(effectFn, subscriber) {
1487
1582
  } finally {
1488
1583
  stackTop--;
1489
1584
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
1585
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
1586
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
1587
+ subscriberStack.length = stackCapacity;
1588
+ }
1490
1589
  }
1491
1590
  return () => cleanup(subscriber);
1492
1591
  }
@@ -1494,34 +1593,50 @@ function cleanup(subscriber) {
1494
1593
  const sub = subscriber;
1495
1594
  const singleDep = sub._dep;
1496
1595
  if (singleDep !== void 0) {
1497
- const subs = singleDep[SUBS];
1498
- if (subs) {
1499
- subs.delete(subscriber);
1500
- if (singleDep.__f === subscriber) {
1501
- singleDep.__f = void 0;
1502
- }
1596
+ const sig = singleDep;
1597
+ const subs = sig[SUBS];
1598
+ if (subs?.delete(subscriber)) {
1599
+ syncFastPath(sig, subs);
1503
1600
  }
1504
1601
  sub._dep = void 0;
1602
+ sub._depEpoch = void 0;
1505
1603
  return;
1506
1604
  }
1507
1605
  const deps = sub._deps;
1508
1606
  if (!deps || deps.size === 0) return;
1509
- for (const signal of deps) {
1510
- const subs = signal[SUBS];
1511
- if (subs) {
1512
- subs.delete(subscriber);
1513
- if (signal.__f === subscriber) {
1514
- signal.__f = void 0;
1515
- }
1607
+ for (const signal of deps.keys()) {
1608
+ const sig = signal;
1609
+ const subs = sig[SUBS];
1610
+ if (subs?.delete(subscriber)) {
1611
+ syncFastPath(sig, subs);
1516
1612
  }
1517
1613
  }
1518
1614
  deps.clear();
1519
1615
  }
1520
1616
 
1521
1617
  // src/core/ssr-context.ts
1522
- var ssrMode = false;
1618
+ var als = null;
1619
+ try {
1620
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
1621
+ const req = Function("return typeof require==='function'?require:null")();
1622
+ if (req) {
1623
+ const mod = req("node:async_hooks");
1624
+ als = new mod.AsyncLocalStorage();
1625
+ }
1626
+ }
1627
+ } catch {
1628
+ als = null;
1629
+ }
1630
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
1631
+ function getSSRStore() {
1632
+ if (als) {
1633
+ const s = als.getStore();
1634
+ if (s) return s;
1635
+ }
1636
+ return fallbackStore;
1637
+ }
1523
1638
  function isSSR() {
1524
- return ssrMode;
1639
+ return getSSRStore().ssr;
1525
1640
  }
1526
1641
 
1527
1642
  // src/core/signals/effect.ts
@@ -1531,26 +1646,114 @@ function effect(effectFn, options) {
1531
1646
  if (isSSR()) return () => {
1532
1647
  };
1533
1648
  const onError = options?.onError;
1649
+ let userCleanups = [];
1650
+ const onCleanup = (fn) => {
1651
+ userCleanups.push(fn);
1652
+ };
1653
+ const runUserCleanups = () => {
1654
+ if (userCleanups.length === 0) return;
1655
+ const list = userCleanups;
1656
+ userCleanups = [];
1657
+ for (let i = list.length - 1; i >= 0; i--) {
1658
+ try {
1659
+ list[i]();
1660
+ } catch (err) {
1661
+ if (typeof console !== "undefined") {
1662
+ console.warn("[SibuJS effect] onCleanup threw:", err);
1663
+ }
1664
+ }
1665
+ }
1666
+ };
1667
+ const invokeBody = () => effectFn(onCleanup);
1534
1668
  const wrappedFn = onError ? () => {
1535
1669
  try {
1536
- effectFn();
1670
+ invokeBody();
1537
1671
  } catch (err) {
1538
1672
  onError(err);
1539
1673
  }
1540
- } : effectFn;
1674
+ } : invokeBody;
1541
1675
  let cleanupHandle = () => {
1542
1676
  };
1677
+ let running = false;
1678
+ let rerunPending = false;
1679
+ const MAX_RERUNS = 100;
1543
1680
  const subscriber = () => {
1544
- cleanupHandle();
1545
- cleanupHandle = track(wrappedFn, subscriber);
1681
+ if (running) {
1682
+ rerunPending = true;
1683
+ return;
1684
+ }
1685
+ running = true;
1686
+ try {
1687
+ let reruns = 0;
1688
+ do {
1689
+ rerunPending = false;
1690
+ runUserCleanups();
1691
+ cleanupHandle();
1692
+ cleanupHandle = track(wrappedFn, subscriber);
1693
+ if (++reruns > MAX_RERUNS) {
1694
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1695
+ console.error(
1696
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1697
+ );
1698
+ }
1699
+ rerunPending = false;
1700
+ break;
1701
+ }
1702
+ } while (rerunPending);
1703
+ } finally {
1704
+ running = false;
1705
+ rerunPending = false;
1706
+ }
1546
1707
  };
1547
- cleanupHandle = track(wrappedFn, subscriber);
1708
+ running = true;
1709
+ try {
1710
+ let reruns = 0;
1711
+ do {
1712
+ rerunPending = false;
1713
+ runUserCleanups();
1714
+ cleanupHandle();
1715
+ cleanupHandle = track(wrappedFn, subscriber);
1716
+ if (++reruns > MAX_RERUNS) {
1717
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1718
+ console.error(
1719
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1720
+ );
1721
+ }
1722
+ rerunPending = false;
1723
+ break;
1724
+ }
1725
+ } while (rerunPending);
1726
+ } finally {
1727
+ running = false;
1728
+ rerunPending = false;
1729
+ }
1548
1730
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1549
1731
  if (hook) hook.emit("effect:create", { effectFn });
1732
+ let disposed = false;
1550
1733
  return () => {
1734
+ if (disposed) return;
1735
+ disposed = true;
1551
1736
  const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1552
- if (h) h.emit("effect:destroy", { effectFn });
1553
- cleanupHandle();
1737
+ if (h) {
1738
+ try {
1739
+ h.emit("effect:destroy", { effectFn });
1740
+ } catch {
1741
+ }
1742
+ }
1743
+ try {
1744
+ runUserCleanups();
1745
+ } catch (err) {
1746
+ if (typeof console !== "undefined") {
1747
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
1748
+ }
1749
+ }
1750
+ try {
1751
+ cleanupHandle();
1752
+ } catch (err) {
1753
+ if (typeof console !== "undefined") {
1754
+ console.warn("[SibuJS effect] dispose threw:", err);
1755
+ }
1756
+ }
1554
1757
  };
1555
1758
  }
1556
1759
 
@@ -1640,21 +1843,27 @@ function waitForSignal(getter, predicate, options = {}) {
1640
1843
  const timeoutMs = options.timeout ?? 1e3;
1641
1844
  return new Promise((resolve, reject) => {
1642
1845
  let resolved = false;
1643
- const timer = setTimeout(() => {
1644
- if (!resolved) {
1645
- resolved = true;
1846
+ let timer;
1847
+ const finish = (fn) => {
1848
+ if (resolved) return;
1849
+ resolved = true;
1850
+ if (timer !== void 0) clearTimeout(timer);
1851
+ fn();
1852
+ };
1853
+ timer = setTimeout(() => {
1854
+ finish(() => {
1646
1855
  teardown();
1647
1856
  reject(new Error(`waitForSignal: predicate did not match within ${timeoutMs}ms`));
1648
- }
1857
+ });
1649
1858
  }, timeoutMs);
1650
1859
  const teardown = effect(() => {
1651
1860
  if (resolved) return;
1652
1861
  const value = getter();
1653
1862
  if (predicate(value)) {
1654
- resolved = true;
1655
- clearTimeout(timer);
1656
- queueMicrotask(() => teardown());
1657
- resolve(value);
1863
+ finish(() => {
1864
+ queueMicrotask(() => teardown());
1865
+ resolve(value);
1866
+ });
1658
1867
  }
1659
1868
  });
1660
1869
  });
@@ -2005,9 +2214,24 @@ function truncate(str, maxLen) {
2005
2214
  }
2006
2215
 
2007
2216
  // src/testing/index.ts
2217
+ function escapeSelector(value) {
2218
+ const g = globalThis;
2219
+ if (g.CSS && typeof g.CSS.escape === "function") return g.CSS.escape(value);
2220
+ return value.replace(/[^\w-]/g, (m) => `\\${m.charCodeAt(0).toString(16)} `);
2221
+ }
2222
+ var _renderedContainers = /* @__PURE__ */ new Set();
2223
+ function unmountAll() {
2224
+ for (const container of _renderedContainers) {
2225
+ for (const child of Array.from(container.childNodes)) dispose(child);
2226
+ container.replaceChildren();
2227
+ if (container.parentNode) container.parentNode.removeChild(container);
2228
+ }
2229
+ _renderedContainers.clear();
2230
+ }
2008
2231
  function render(component) {
2009
2232
  const container = document.createElement("div");
2010
2233
  document.body.appendChild(container);
2234
+ _renderedContainers.add(container);
2011
2235
  const element = component();
2012
2236
  container.appendChild(element);
2013
2237
  function getByText(text) {
@@ -2024,7 +2248,7 @@ function render(component) {
2024
2248
  return walk(container);
2025
2249
  }
2026
2250
  function getByTestId(testId) {
2027
- return container.querySelector(`[data-testid="${testId}"]`);
2251
+ return container.querySelector(`[data-testid="${escapeSelector(testId)}"]`);
2028
2252
  }
2029
2253
  function getByRole(role) {
2030
2254
  return container.querySelector(`[role="${role}"]`);
@@ -2033,8 +2257,10 @@ function render(component) {
2033
2257
  return Array.from(container.querySelectorAll(selector));
2034
2258
  }
2035
2259
  function unmount() {
2036
- container.innerHTML = "";
2260
+ for (const child of Array.from(container.childNodes)) dispose(child);
2261
+ container.replaceChildren();
2037
2262
  if (container.parentNode) container.parentNode.removeChild(container);
2263
+ _renderedContainers.delete(container);
2038
2264
  }
2039
2265
  return { container, element, getByText, getByTestId, getByRole, queryAll, unmount };
2040
2266
  }
@@ -2145,6 +2371,7 @@ function mockStore(initialState) {
2145
2371
  snapshotComponent,
2146
2372
  testComponent,
2147
2373
  type,
2374
+ unmountAll,
2148
2375
  waitFor,
2149
2376
  waitForSignal
2150
2377
  });
@@ -242,11 +242,13 @@ interface MockRoute {
242
242
  * Create an HTTP mock server that intercepts fetch calls.
243
243
  * Useful for testing components that make API calls.
244
244
  */
245
- declare function createHttpMock(routes?: MockRoute[]): {
245
+ declare function createHttpMock(routes?: MockRoute[], options?: {
246
+ afterEach?: (cleanup: () => void) => void;
247
+ }): {
246
248
  /** Install the mock (replace global fetch) */
247
249
  install(): void;
248
250
  /** Restore original fetch */
249
- restore(): void;
251
+ restore: () => void;
250
252
  /** Add a mock route */
251
253
  addRoute(route: MockRoute): void;
252
254
  /** Remove all mock routes */
@@ -271,7 +273,9 @@ declare function createHttpMock(routes?: MockRoute[]): {
271
273
  * Create a fake timer system for testing time-dependent code.
272
274
  * Mocks setTimeout, setInterval, requestAnimationFrame.
273
275
  */
274
- declare function createTimerMock(): {
276
+ declare function createTimerMock(options?: {
277
+ afterEach?: (cleanup: () => void) => void;
278
+ }): {
275
279
  install(): void;
276
280
  restore(): void;
277
281
  /** Advance time by a given number of ms, running any timers that fire */
@@ -479,8 +483,17 @@ declare function createVisualSuite(): {
479
483
  clear: () => void;
480
484
  };
481
485
 
486
+ /**
487
+ * Unmount every container still alive from prior `render()` calls.
488
+ * Safe to call from an `afterEach` hook to guarantee teardown.
489
+ */
490
+ declare function unmountAll(): void;
482
491
  /**
483
492
  * render mounts a component into a test container and returns helpers.
493
+ *
494
+ * The caller is responsible for calling `unmount()` (typically from an
495
+ * `afterEach` hook). For bulk teardown across many renders, call
496
+ * `unmountAll()` instead — every live container is tracked internally.
484
497
  */
485
498
  declare function render(component: () => HTMLElement): {
486
499
  container: HTMLElement;
@@ -529,4 +542,4 @@ declare function mockStore<T extends Record<string, unknown>>(initialState: T):
529
542
  reset: () => void;
530
543
  };
531
544
 
532
- export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FindOptions, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, findByRole, findByTestId, findByText, fireEvent, matchSnapshot, mockRouter, mockStore, queryByLabel, queryByRole, queryByTestId, queryByText, render, snapshotComponent, testComponent, type, waitFor, waitForSignal };
545
+ export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FindOptions, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, findByRole, findByTestId, findByText, fireEvent, matchSnapshot, mockRouter, mockStore, queryByLabel, queryByRole, queryByTestId, queryByText, render, snapshotComponent, testComponent, type, unmountAll, waitFor, waitForSignal };
package/dist/testing.d.ts CHANGED
@@ -242,11 +242,13 @@ interface MockRoute {
242
242
  * Create an HTTP mock server that intercepts fetch calls.
243
243
  * Useful for testing components that make API calls.
244
244
  */
245
- declare function createHttpMock(routes?: MockRoute[]): {
245
+ declare function createHttpMock(routes?: MockRoute[], options?: {
246
+ afterEach?: (cleanup: () => void) => void;
247
+ }): {
246
248
  /** Install the mock (replace global fetch) */
247
249
  install(): void;
248
250
  /** Restore original fetch */
249
- restore(): void;
251
+ restore: () => void;
250
252
  /** Add a mock route */
251
253
  addRoute(route: MockRoute): void;
252
254
  /** Remove all mock routes */
@@ -271,7 +273,9 @@ declare function createHttpMock(routes?: MockRoute[]): {
271
273
  * Create a fake timer system for testing time-dependent code.
272
274
  * Mocks setTimeout, setInterval, requestAnimationFrame.
273
275
  */
274
- declare function createTimerMock(): {
276
+ declare function createTimerMock(options?: {
277
+ afterEach?: (cleanup: () => void) => void;
278
+ }): {
275
279
  install(): void;
276
280
  restore(): void;
277
281
  /** Advance time by a given number of ms, running any timers that fire */
@@ -479,8 +483,17 @@ declare function createVisualSuite(): {
479
483
  clear: () => void;
480
484
  };
481
485
 
486
+ /**
487
+ * Unmount every container still alive from prior `render()` calls.
488
+ * Safe to call from an `afterEach` hook to guarantee teardown.
489
+ */
490
+ declare function unmountAll(): void;
482
491
  /**
483
492
  * render mounts a component into a test container and returns helpers.
493
+ *
494
+ * The caller is responsible for calling `unmount()` (typically from an
495
+ * `afterEach` hook). For bulk teardown across many renders, call
496
+ * `unmountAll()` instead — every live container is tracked internally.
484
497
  */
485
498
  declare function render(component: () => HTMLElement): {
486
499
  container: HTMLElement;
@@ -529,4 +542,4 @@ declare function mockStore<T extends Record<string, unknown>>(initialState: T):
529
542
  reset: () => void;
530
543
  };
531
544
 
532
- export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FindOptions, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, findByRole, findByTestId, findByText, fireEvent, matchSnapshot, mockRouter, mockStore, queryByLabel, queryByRole, queryByTestId, queryByText, render, snapshotComponent, testComponent, type, waitFor, waitForSignal };
545
+ export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FindOptions, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, findByRole, findByTestId, findByText, fireEvent, matchSnapshot, mockRouter, mockStore, queryByLabel, queryByRole, queryByTestId, queryByText, render, snapshotComponent, testComponent, type, unmountAll, waitFor, waitForSignal };