sibujs 4.4.0 → 4.6.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 (112) hide show
  1. package/README.md +11 -0
  2. package/dist/browser.cjs +339 -130
  3. package/dist/browser.d.cts +61 -16
  4. package/dist/browser.d.ts +61 -16
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +273 -82
  7. package/dist/build.js +15 -16
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  13. package/dist/chunk-3ISI6ACU.js +44 -0
  14. package/dist/{chunk-VCTAEPSB.js → chunk-47M47FOM.js} +11 -3
  15. package/dist/chunk-5HZXGZ6T.js +24 -0
  16. package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
  17. package/dist/{chunk-3BTTCZ5J.js → chunk-5Q4R7HCL.js} +42 -4
  18. package/dist/{chunk-ADM46X22.js → chunk-7GQCWFOE.js} +157 -61
  19. package/dist/{chunk-4Z3SPQ67.js → chunk-7NCARGJW.js} +16 -8
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/{chunk-LQFGQNMV.js → chunk-AIF3Z2T7.js} +302 -39
  22. package/dist/{chunk-UCALUKB5.js → chunk-BAAG6ZTI.js} +98 -29
  23. package/dist/{chunk-NVNJH22U.js → chunk-D33YTSX5.js} +2 -2
  24. package/dist/{chunk-M2F7TZHH.js → chunk-DKGBBKOF.js} +136 -80
  25. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  26. package/dist/{chunk-HFCOH2GN.js → chunk-HYXDKS4N.js} +153 -110
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-36S2YPP4.js → chunk-JSXPZCET.js} +1 -1
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-HQSEH5F6.js → chunk-ORMZXBKQ.js} +108 -45
  31. package/dist/{chunk-LYVUX7NT.js → chunk-OXUY2A6L.js} +287 -130
  32. package/dist/{chunk-6GSIXTWX.js → chunk-PK6FK2G2.js} +187 -66
  33. package/dist/{chunk-W2EQ7X2L.js → chunk-QE4TTDU3.js} +47 -9
  34. package/dist/{chunk-XLO7SLIX.js → chunk-SXXVZMKZ.js} +262 -70
  35. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  36. package/dist/{chunk-RR7M3FHM.js → chunk-UZQ6ALFS.js} +3 -3
  37. package/dist/{chunk-PNIRUQ4C.js → chunk-VVWPJ543.js} +6 -8
  38. package/dist/{chunk-7GIHSAWB.js → chunk-XC4MEKGA.js} +3 -3
  39. package/dist/{chunk-SKPTERHP.js → chunk-XRRZKZYX.js} +55 -31
  40. package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
  41. package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
  42. package/dist/{customElement-CNZxEB9G.d.ts → customElement-MmInOW1U.d.cts} +59 -10
  43. package/dist/{customElement-CNZxEB9G.d.cts → customElement-MmInOW1U.d.ts} +59 -10
  44. package/dist/data.cjs +293 -125
  45. package/dist/data.d.cts +47 -9
  46. package/dist/data.d.ts +47 -9
  47. package/dist/data.js +12 -9
  48. package/dist/devtools.cjs +121 -58
  49. package/dist/devtools.js +7 -8
  50. package/dist/dispose-GEIG2KOF.js +28 -0
  51. package/dist/ecosystem.cjs +391 -112
  52. package/dist/ecosystem.d.cts +31 -7
  53. package/dist/ecosystem.d.ts +31 -7
  54. package/dist/ecosystem.js +12 -12
  55. package/dist/extras.cjs +2250 -879
  56. package/dist/extras.d.cts +10 -9
  57. package/dist/extras.d.ts +10 -9
  58. package/dist/extras.js +39 -28
  59. package/dist/index.cjs +273 -82
  60. package/dist/index.d.cts +44 -154
  61. package/dist/index.d.ts +44 -154
  62. package/dist/index.js +24 -27
  63. package/dist/motion.cjs +137 -42
  64. package/dist/motion.js +5 -5
  65. package/dist/patterns.cjs +397 -48
  66. package/dist/patterns.d.cts +32 -9
  67. package/dist/patterns.d.ts +32 -9
  68. package/dist/patterns.js +8 -8
  69. package/dist/performance.cjs +338 -217
  70. package/dist/performance.d.cts +2 -2
  71. package/dist/performance.d.ts +2 -2
  72. package/dist/performance.js +8 -9
  73. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  74. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  75. package/dist/plugins.cjs +587 -203
  76. package/dist/plugins.d.cts +3 -3
  77. package/dist/plugins.d.ts +3 -3
  78. package/dist/plugins.js +35 -23
  79. package/dist/signal-EotCj4hS.d.cts +110 -0
  80. package/dist/signal-EotCj4hS.d.ts +110 -0
  81. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  82. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  83. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  84. package/dist/ssr.cjs +214 -73
  85. package/dist/ssr.d.cts +9 -3
  86. package/dist/ssr.d.ts +9 -3
  87. package/dist/ssr.js +11 -12
  88. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  89. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  90. package/dist/tagFactory-DFstCLQV.d.cts +117 -0
  91. package/dist/tagFactory-DkaNVUNV.d.ts +117 -0
  92. package/dist/testing.cjs +2481 -2153
  93. package/dist/testing.d.cts +56 -5
  94. package/dist/testing.d.ts +56 -5
  95. package/dist/testing.js +580 -307
  96. package/dist/ui.cjs +798 -328
  97. package/dist/ui.d.cts +40 -7
  98. package/dist/ui.d.ts +40 -7
  99. package/dist/ui.js +151 -56
  100. package/dist/widgets.cjs +319 -285
  101. package/dist/widgets.js +9 -10
  102. package/package.json +2 -2
  103. package/dist/chunk-2WLZ6757.js +0 -149
  104. package/dist/chunk-7NBDXVHS.js +0 -60
  105. package/dist/chunk-CCSJMTRN.js +0 -15
  106. package/dist/chunk-QKRPLZ2V.js +0 -108
  107. package/dist/chunk-WWV3SJ3L.js +0 -131
  108. package/dist/dispose-46BOMMQJ.js +0 -19
  109. package/dist/plugin-D30wlGW5.d.cts +0 -71
  110. package/dist/plugin-D30wlGW5.d.ts +0 -71
  111. package/dist/tagFactory-Bzupt4Pj.d.cts +0 -55
  112. package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
@@ -141,6 +141,90 @@ function reportError(error, context) {
141
141
  });
142
142
  }
143
143
 
144
+ // src/core/rendering/dispose.ts
145
+ var elementDisposers = /* @__PURE__ */ new WeakMap();
146
+ var MAX_DRAIN_TEARDOWNS = 1e4;
147
+ function reportDrainRunaway(label, executed, remaining) {
148
+ if (typeof console === "undefined") return;
149
+ console.error(
150
+ `[SibuJS ${label}] runaway cleanup: stopped after running ${executed} teardowns with ${remaining} still queued. Cleanup production did not stabilize before the teardown safety ceiling was reached \u2014 the remaining work was NOT run.`,
151
+ { executed, remaining }
152
+ );
153
+ }
154
+ var activeBindingCount = 0;
155
+ function resolveCapture(capture) {
156
+ let frame = capture;
157
+ while (frame?.closed) frame = frame.forwardTo;
158
+ return frame ?? null;
159
+ }
160
+ var registrationCaptures = [];
161
+ function currentDisposerCapture() {
162
+ return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
163
+ }
164
+ function beginDisposerCapture(capture) {
165
+ if (capture === null && registrationCaptures.length === 0) return false;
166
+ registrationCaptures.push(resolveCapture(capture));
167
+ return true;
168
+ }
169
+ function endDisposerCapture() {
170
+ registrationCaptures.pop();
171
+ }
172
+ function registerDisposer(node, teardown) {
173
+ let disposers = elementDisposers.get(node);
174
+ if (!disposers) {
175
+ disposers = [];
176
+ elementDisposers.set(node, disposers);
177
+ }
178
+ disposers.push(teardown);
179
+ if (DEV) activeBindingCount++;
180
+ const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
181
+ if (capture) capture.entries.push([node, teardown]);
182
+ }
183
+ function dispose(node) {
184
+ const stack = [node];
185
+ const order = [];
186
+ while (stack.length > 0) {
187
+ const current = stack.pop();
188
+ order.push(current);
189
+ const children = Array.from(current.childNodes);
190
+ for (let i = 0; i < children.length; i++) {
191
+ stack.push(children[i]);
192
+ }
193
+ }
194
+ for (let i = order.length - 1; i >= 0; i--) {
195
+ disposeNodeOwn(order[i]);
196
+ }
197
+ }
198
+ function disposeNodeOwn(node) {
199
+ if (!elementDisposers.has(node)) return;
200
+ let executed = 0;
201
+ let runaway = false;
202
+ while (!runaway) {
203
+ const pending = elementDisposers.get(node);
204
+ if (!pending || pending.length === 0) break;
205
+ const snapshot = pending.slice();
206
+ elementDisposers.delete(node);
207
+ if (DEV) activeBindingCount -= snapshot.length;
208
+ for (let i = 0; i < snapshot.length; i++) {
209
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
210
+ const rest = snapshot.slice(i);
211
+ const added = elementDisposers.get(node);
212
+ elementDisposers.set(node, added ? rest.concat(added) : rest);
213
+ if (DEV) activeBindingCount += rest.length;
214
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
215
+ runaway = true;
216
+ break;
217
+ }
218
+ executed++;
219
+ try {
220
+ snapshot[i]();
221
+ } catch (err) {
222
+ reportError(err, { phase: "cleanup", name: "disposer" });
223
+ }
224
+ }
225
+ }
226
+ }
227
+
144
228
  // src/reactivity/track-core.ts
145
229
  var POOL_MAX = 4096;
146
230
  var nodePool = [];
@@ -288,8 +372,18 @@ function untracked(fn) {
288
372
  var subscriberEpochCounter = 0;
289
373
  function retrack(effectFn, subscriber) {
290
374
  const prev = currentSubscriber;
375
+ let savedDepth = 0;
376
+ let savedSuspendSub = null;
377
+ if (trackingSuspended) {
378
+ savedDepth = suspendDepth;
379
+ savedSuspendSub = suspendSavedSub;
380
+ suspendDepth = 0;
381
+ suspendSavedSub = null;
382
+ trackingSuspended = false;
383
+ }
291
384
  currentSubscriber = subscriber;
292
385
  const sub = subscriber;
386
+ if (sub._cap === void 0) sub._cap = currentDisposerCapture();
293
387
  const epoch = ++subscriberEpochCounter;
294
388
  sub._epoch = epoch;
295
389
  sub._structDirty = false;
@@ -302,6 +396,11 @@ function retrack(effectFn, subscriber) {
302
396
  effectFn();
303
397
  } finally {
304
398
  currentSubscriber = prev;
399
+ if (savedDepth !== 0) {
400
+ suspendDepth = savedDepth;
401
+ suspendSavedSub = savedSuspendSub;
402
+ trackingSuspended = true;
403
+ }
305
404
  let node = sub.depsHead ?? null;
306
405
  while (node !== null) {
307
406
  const next = node.subNext;
@@ -321,11 +420,25 @@ function track(effectFn, subscriber) {
321
420
  if (!subscriber) return reactiveBinding(effectFn);
322
421
  cleanup(subscriber);
323
422
  const prev = currentSubscriber;
423
+ let savedDepth = 0;
424
+ let savedSuspendSub = null;
425
+ if (trackingSuspended) {
426
+ savedDepth = suspendDepth;
427
+ savedSuspendSub = suspendSavedSub;
428
+ suspendDepth = 0;
429
+ suspendSavedSub = null;
430
+ trackingSuspended = false;
431
+ }
324
432
  currentSubscriber = subscriber;
325
433
  try {
326
434
  effectFn();
327
435
  } finally {
328
436
  currentSubscriber = prev;
437
+ if (savedDepth !== 0) {
438
+ suspendDepth = savedDepth;
439
+ suspendSavedSub = savedSuspendSub;
440
+ trackingSuspended = true;
441
+ }
329
442
  const sub2 = subscriber;
330
443
  for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
331
444
  const sig = n.sig;
@@ -462,7 +575,12 @@ function drainQueue() {
462
575
  quarantined.add(sub);
463
576
  continue;
464
577
  }
465
- safeInvoke(sub);
578
+ const pushed = beginDisposerCapture(sub._cap ?? null);
579
+ try {
580
+ safeInvoke(sub);
581
+ } finally {
582
+ if (pushed) endDisposerCapture();
583
+ }
466
584
  }
467
585
  }
468
586
  function drainNotificationQueue() {
@@ -496,8 +614,9 @@ function propagateDirty(sub) {
496
614
  if (s._c) {
497
615
  const nSig = s._sig;
498
616
  if (nSig) {
499
- if (!nSig._d) {
617
+ if (!nSig._d || nSig._f === true) {
500
618
  nSig._d = true;
619
+ nSig._f = false;
501
620
  stack.push(nSig);
502
621
  }
503
622
  } else {
@@ -597,7 +716,7 @@ function forEachSubscriber(signal2, visit) {
597
716
  }
598
717
 
599
718
  // src/reactivity/track.ts
600
- var _runtimeVersion = true ? "4.4.0" : "dev";
719
+ var _runtimeVersion = true ? "4.6.0" : "dev";
601
720
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
602
721
  function resolveReactiveApi() {
603
722
  const g = globalThis;
@@ -937,6 +1056,27 @@ function signal(initial, options) {
937
1056
  return [get, set];
938
1057
  }
939
1058
 
1059
+ // src/utils/adoptThenable.ts
1060
+ function adoptThenable(value) {
1061
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
1062
+ let then;
1063
+ try {
1064
+ then = value.then;
1065
+ } catch (error) {
1066
+ return Promise.reject(error);
1067
+ }
1068
+ if (typeof then !== "function") return null;
1069
+ return new Promise((resolve, reject) => {
1070
+ queueMicrotask(() => {
1071
+ try {
1072
+ Reflect.apply(then, value, [resolve, reject]);
1073
+ } catch (error) {
1074
+ reject(error);
1075
+ }
1076
+ });
1077
+ });
1078
+ }
1079
+
940
1080
  // src/utils/globalSingleton.ts
941
1081
  function globalSingleton(key, create) {
942
1082
  const g = globalThis;
@@ -944,37 +1084,127 @@ function globalSingleton(key, create) {
944
1084
  }
945
1085
 
946
1086
  // src/plugins/plugin.ts
1087
+ var CANCELLED_BRAND = /* @__PURE__ */ Symbol.for("sibujs.plugins.installCancelled.v1");
1088
+ var _a;
1089
+ var PluginInstallCancelledError = class extends Error {
1090
+ constructor(pluginName) {
1091
+ super(`[Plugin] Installation of "${pluginName}" was cancelled before it was committed (registry reset).`);
1092
+ /**
1093
+ * Brand, not identity: the default registry is shared across duplicate copies
1094
+ * of this module, so the error a caller receives may come from another copy's
1095
+ * constructor. `instanceof` is taught to recognise the brand instead.
1096
+ */
1097
+ this[_a] = true;
1098
+ this.name = "PluginInstallCancelledError";
1099
+ this.pluginName = pluginName;
1100
+ }
1101
+ static [(_a = CANCELLED_BRAND, Symbol.hasInstance)](value) {
1102
+ return isPluginInstallCancelledError(value);
1103
+ }
1104
+ };
1105
+ function isPluginInstallCancelledError(value) {
1106
+ return typeof value === "object" && value !== null && value[CANCELLED_BRAND] === true;
1107
+ }
947
1108
  function createPluginRegistry() {
948
1109
  const installedPlugins = /* @__PURE__ */ new Set();
949
1110
  const hooks = { init: [], mount: [], unmount: [], error: [] };
950
1111
  const provided = /* @__PURE__ */ new Map();
1112
+ const installStack = /* @__PURE__ */ new Set();
1113
+ const pendingInstalls = /* @__PURE__ */ new Map();
1114
+ let generation = 0;
951
1115
  const registry = {
952
1116
  installedPlugins,
953
1117
  hooks,
954
1118
  provided,
1119
+ /**
1120
+ * Install a plugin as a transaction.
1121
+ *
1122
+ * Hooks and providers registered through `ctx` are staged and committed
1123
+ * only if `install()` returns; a throwing install leaves the registry
1124
+ * exactly as it found it (and can simply be retried). The plugin is marked
1125
+ * installed at commit, before its init hooks run.
1126
+ *
1127
+ * Each `plugin()` call is its own transaction: a dependency installed by a
1128
+ * nested `plugin()` call commits on its own and stays installed even if the
1129
+ * outer installation later fails.
1130
+ */
955
1131
  plugin(p, options) {
956
1132
  if (installedPlugins.has(p.name)) {
957
1133
  console.warn(`[Plugin] "${p.name}" is already installed.`);
958
1134
  return;
959
1135
  }
1136
+ if (installStack.has(p.name)) {
1137
+ throw new Error(`[Plugin] "${p.name}" is already being installed (recursive installation).`);
1138
+ }
1139
+ if (pendingInstalls.has(p.name)) {
1140
+ throw new Error(`[Plugin] "${p.name}" is already being installed (asynchronous installation in flight).`);
1141
+ }
1142
+ const staged = { init: [], mount: [], unmount: [], error: [] };
1143
+ const stagedProvided = /* @__PURE__ */ new Map();
1144
+ let committed = false;
1145
+ const myGeneration = generation;
1146
+ const stale = () => {
1147
+ if (myGeneration === generation) return false;
1148
+ if (DEV) devWarn(`plugin: "${p.name}" registered after the registry was reset; the registration was ignored.`);
1149
+ return true;
1150
+ };
1151
+ const target = () => stale() ? null : committed ? hooks : staged;
960
1152
  const ctx = {
961
- onInit: (cb) => hooks.init.push(cb),
962
- onMount: (cb) => hooks.mount.push(cb),
963
- onUnmount: (cb) => hooks.unmount.push(cb),
964
- onError: (cb) => hooks.error.push(cb),
965
- provide: (key, value) => provided.set(key, value)
1153
+ onInit: (cb) => void target()?.init.push(cb),
1154
+ onMount: (cb) => void target()?.mount.push(cb),
1155
+ onUnmount: (cb) => void target()?.unmount.push(cb),
1156
+ onError: (cb) => void target()?.error.push(cb),
1157
+ provide: (key, value) => {
1158
+ if (stale()) return;
1159
+ (committed ? provided : stagedProvided).set(key, value);
1160
+ }
966
1161
  };
967
- const initHooksBefore = hooks.init.length;
968
- p.install(ctx, options);
969
- installedPlugins.add(p.name);
970
- const justAdded = hooks.init.slice(initHooksBefore);
971
- for (const cb of justAdded) {
972
- try {
973
- cb();
974
- } catch (e) {
975
- console.error(`[Plugin] "${p.name}" init error:`, e);
1162
+ const commit = () => {
1163
+ if (myGeneration !== generation) return false;
1164
+ hooks.init.push(...staged.init);
1165
+ hooks.mount.push(...staged.mount);
1166
+ hooks.unmount.push(...staged.unmount);
1167
+ hooks.error.push(...staged.error);
1168
+ for (const [key, value] of stagedProvided) provided.set(key, value);
1169
+ installedPlugins.add(p.name);
1170
+ committed = true;
1171
+ if (pendingInstalls.get(p.name) === myGeneration) pendingInstalls.delete(p.name);
1172
+ for (const cb of staged.init.slice()) {
1173
+ try {
1174
+ cb();
1175
+ } catch (e) {
1176
+ console.error(`[Plugin] "${p.name}" init error:`, e);
1177
+ }
976
1178
  }
1179
+ return true;
1180
+ };
1181
+ installStack.add(p.name);
1182
+ let pending;
1183
+ try {
1184
+ pending = adoptThenable(p.install(ctx, options));
1185
+ } finally {
1186
+ installStack.delete(p.name);
977
1187
  }
1188
+ if (!pending) {
1189
+ if (!commit()) throw new PluginInstallCancelledError(p.name);
1190
+ return;
1191
+ }
1192
+ if (myGeneration === generation) pendingInstalls.set(p.name, myGeneration);
1193
+ const settled = pending.then(
1194
+ () => {
1195
+ if (commit()) return;
1196
+ if (pendingInstalls.get(p.name) === myGeneration) pendingInstalls.delete(p.name);
1197
+ throw new PluginInstallCancelledError(p.name);
1198
+ },
1199
+ (err) => {
1200
+ if (pendingInstalls.get(p.name) === myGeneration) pendingInstalls.delete(p.name);
1201
+ reportError(err, { phase: "async", name: `plugin(${p.name})` });
1202
+ throw err;
1203
+ }
1204
+ );
1205
+ settled.catch(() => {
1206
+ });
1207
+ return settled;
978
1208
  },
979
1209
  inject(key, defaultValue) {
980
1210
  if (provided.has(key)) return provided.get(key);
@@ -1012,6 +1242,8 @@ function createPluginRegistry() {
1012
1242
  }
1013
1243
  },
1014
1244
  reset() {
1245
+ generation++;
1246
+ pendingInstalls.clear();
1015
1247
  installedPlugins.clear();
1016
1248
  hooks.init.length = 0;
1017
1249
  hooks.mount.length = 0;
@@ -1084,10 +1316,14 @@ function derived(getter, options) {
1084
1316
  const cs = {};
1085
1317
  cs._d = false;
1086
1318
  cs._init = false;
1319
+ cs._f = false;
1087
1320
  cs._g = getter;
1088
1321
  cs.__v = 0;
1322
+ let evaluating = false;
1323
+ let disposed = false;
1324
+ let pendingError;
1089
1325
  const markDirty = () => {
1090
- if (cs._d) return;
1326
+ if (cs._d || disposed) return;
1091
1327
  cs._d = true;
1092
1328
  };
1093
1329
  markDirty._c = 1;
@@ -1110,33 +1346,53 @@ function derived(getter, options) {
1110
1346
  }
1111
1347
  }, markDirty);
1112
1348
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1113
- let evaluating = false;
1114
1349
  const validate = () => {
1115
- if (!cs._d) return;
1350
+ if (!cs._d || disposed || pendingError !== void 0) return;
1116
1351
  const oldValue = cs._v;
1117
1352
  evaluating = true;
1118
1353
  try {
1119
1354
  retrack2(recompute, markDirty);
1120
1355
  if (!Object.is(oldValue, cs._v)) cs.__v++;
1356
+ } catch (err) {
1357
+ pendingError = { error: err };
1358
+ cs.__v++;
1359
+ if (disposed) cs._d = true;
1360
+ else cs._f = true;
1121
1361
  } finally {
1122
1362
  evaluating = false;
1363
+ if (disposed) cleanup2(markDirty);
1123
1364
  }
1124
- if (hook && !Object.is(oldValue, cs._v)) {
1365
+ if (hook && !disposed && !Object.is(oldValue, cs._v)) {
1125
1366
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
1126
1367
  }
1127
1368
  };
1128
1369
  cs._validate = validate;
1370
+ const throwPending = () => {
1371
+ const { error } = pendingError;
1372
+ pendingError = void 0;
1373
+ if (disposed) cs._d = false;
1374
+ throw error;
1375
+ };
1129
1376
  function computedGetter() {
1130
1377
  if (evaluating) {
1131
1378
  throw new Error(
1132
1379
  `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
1133
1380
  );
1134
1381
  }
1135
- if (isTrackingSuspended2()) {
1136
- if (cs._d) validate();
1382
+ if (isTrackingSuspended2() || disposed) {
1383
+ if (cs._d) {
1384
+ validate();
1385
+ if (pendingError !== void 0) throwPending();
1386
+ }
1137
1387
  return cs._v;
1138
1388
  }
1139
- if (cs._d) validate();
1389
+ if (cs._d) {
1390
+ validate();
1391
+ if (pendingError !== void 0) {
1392
+ recordDependency2(cs);
1393
+ throwPending();
1394
+ }
1395
+ }
1140
1396
  recordDependency2(cs);
1141
1397
  return cs._v;
1142
1398
  }
@@ -1145,6 +1401,19 @@ function derived(getter, options) {
1145
1401
  cs.__name = debugName;
1146
1402
  }
1147
1403
  computedGetter.__signal = cs;
1404
+ computedGetter.dispose = () => {
1405
+ if (disposed) return;
1406
+ disposed = true;
1407
+ cs._d = pendingError !== void 0;
1408
+ cleanup2(markDirty);
1409
+ const h = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1410
+ if (h) {
1411
+ try {
1412
+ h.emit("computed:destroy", { signal: cs, getter: computedGetter });
1413
+ } catch {
1414
+ }
1415
+ }
1416
+ };
1148
1417
  if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
1149
1418
  return computedGetter;
1150
1419
  }
@@ -1462,6 +1731,14 @@ function setSafeAttribute(el, name, value, options = {}) {
1462
1731
  const html = isHtmlElement(el);
1463
1732
  const idlName = html ? name.toLowerCase() : name;
1464
1733
  if (typeof value === "boolean") {
1734
+ if (name.length > 5 && name.slice(0, 5).toLowerCase() === "aria-") {
1735
+ const token = value ? "true" : "false";
1736
+ if ((ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name)) !== token) {
1737
+ if (ns) el.setAttributeNS(ns, name, token);
1738
+ else el.setAttribute(name, token);
1739
+ }
1740
+ return true;
1741
+ }
1465
1742
  if (html && BOOLEAN_IDL_ATTRS.has(idlName) && idlName in el) {
1466
1743
  setProp(el, idlName, value);
1467
1744
  } else if (value) {
@@ -1507,70 +1784,6 @@ function bindAttribute(el, attr, getter) {
1507
1784
  return reactiveBinding2(commit, el);
1508
1785
  }
1509
1786
 
1510
- // src/core/rendering/dispose.ts
1511
- var elementDisposers = /* @__PURE__ */ new WeakMap();
1512
- var MAX_DRAIN_TEARDOWNS = 1e4;
1513
- function reportDrainRunaway(label, executed, remaining) {
1514
- if (typeof console === "undefined") return;
1515
- console.error(
1516
- `[SibuJS ${label}] runaway cleanup: stopped after running ${executed} teardowns with ${remaining} still queued. Cleanup production did not stabilize before the teardown safety ceiling was reached \u2014 the remaining work was NOT run.`,
1517
- { executed, remaining }
1518
- );
1519
- }
1520
- var activeBindingCount = 0;
1521
- function registerDisposer(node, teardown) {
1522
- let disposers = elementDisposers.get(node);
1523
- if (!disposers) {
1524
- disposers = [];
1525
- elementDisposers.set(node, disposers);
1526
- }
1527
- disposers.push(teardown);
1528
- if (DEV) activeBindingCount++;
1529
- }
1530
- function dispose(node) {
1531
- const stack = [node];
1532
- const order = [];
1533
- while (stack.length > 0) {
1534
- const current = stack.pop();
1535
- order.push(current);
1536
- const children = Array.from(current.childNodes);
1537
- for (let i = 0; i < children.length; i++) {
1538
- stack.push(children[i]);
1539
- }
1540
- }
1541
- for (let i = order.length - 1; i >= 0; i--) {
1542
- const current = order[i];
1543
- if (elementDisposers.has(current)) {
1544
- let executed = 0;
1545
- let runaway = false;
1546
- while (!runaway) {
1547
- const pending = elementDisposers.get(current);
1548
- if (!pending || pending.length === 0) break;
1549
- const snapshot = pending.slice();
1550
- elementDisposers.delete(current);
1551
- if (DEV) activeBindingCount -= snapshot.length;
1552
- for (let i2 = 0; i2 < snapshot.length; i2++) {
1553
- if (executed >= MAX_DRAIN_TEARDOWNS) {
1554
- const rest = snapshot.slice(i2);
1555
- const added = elementDisposers.get(current);
1556
- elementDisposers.set(current, added ? rest.concat(added) : rest);
1557
- if (DEV) activeBindingCount += rest.length;
1558
- reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
1559
- runaway = true;
1560
- break;
1561
- }
1562
- executed++;
1563
- try {
1564
- snapshot[i2]();
1565
- } catch (err) {
1566
- reportError(err, { phase: "cleanup", name: "disposer" });
1567
- }
1568
- }
1569
- }
1570
- }
1571
- }
1572
- }
1573
-
1574
1787
  // src/core/rendering/focusPreservation.ts
1575
1788
  var IDENTITY_ATTRS = ["data-focus-key", "id", "name"];
1576
1789
  var SELECTABLE_TYPES = /* @__PURE__ */ new Set(["text", "search", "url", "tel", "password", ""]);
@@ -1991,53 +2204,119 @@ var tagFactory = (tag, ns) => {
1991
2204
  };
1992
2205
 
1993
2206
  // src/ecosystem/ui/componentAdapter.ts
2207
+ var _themeLayers = globalSingleton(
2208
+ /* @__PURE__ */ Symbol.for("sibujs.themeLayers.v1"),
2209
+ () => /* @__PURE__ */ new WeakMap()
2210
+ );
2211
+ function commitThemeProperty(root, name, record) {
2212
+ const top = record.layers[record.layers.length - 1];
2213
+ if (top) {
2214
+ root.style.setProperty(name, top.value);
2215
+ return;
2216
+ }
2217
+ _themeLayers.get(root)?.delete(name);
2218
+ if (record.original.value === "") root.style.removeProperty(name);
2219
+ else root.style.setProperty(name, record.original.value, record.original.priority);
2220
+ }
2221
+ function setThemeLayer(root, name, handle, value) {
2222
+ let properties = _themeLayers.get(root);
2223
+ if (!properties) _themeLayers.set(root, properties = /* @__PURE__ */ new Map());
2224
+ let record = properties.get(name);
2225
+ if (!record) {
2226
+ record = {
2227
+ original: { value: root.style.getPropertyValue(name), priority: root.style.getPropertyPriority(name) },
2228
+ layers: []
2229
+ };
2230
+ properties.set(name, record);
2231
+ }
2232
+ const layer = record.layers.find((l) => l.handle === handle);
2233
+ if (layer) layer.value = value;
2234
+ else record.layers.push({ handle, value });
2235
+ commitThemeProperty(root, name, record);
2236
+ }
2237
+ function removeThemeLayer(root, name, handle) {
2238
+ const record = _themeLayers.get(root)?.get(name);
2239
+ if (!record) return;
2240
+ const index = record.layers.findIndex((l) => l.handle === handle);
2241
+ if (index === -1) return;
2242
+ record.layers.splice(index, 1);
2243
+ commitThemeProperty(root, name, record);
2244
+ }
2245
+ function ownString(map, key) {
2246
+ if (!map || key === void 0 || !Object.hasOwn(map, key)) return void 0;
2247
+ const value = map[key];
2248
+ return typeof value === "string" ? value : void 0;
2249
+ }
1994
2250
  function createTheme(initial) {
1995
2251
  const [getConfig, setConfig] = signal(initial);
1996
2252
  function resolveClass(component, variant) {
1997
2253
  const config = getConfig();
1998
2254
  const overrideKey = variant ? `${component}-${variant}` : component;
1999
- if (config.classOverrides?.[overrideKey]) {
2000
- return config.classOverrides[overrideKey];
2001
- }
2255
+ const override = ownString(config.classOverrides, overrideKey);
2256
+ if (override) return override;
2002
2257
  const base = `${config.prefix}-${component}`;
2003
2258
  return variant ? `${base} ${config.prefix}-${component}--${variant}` : base;
2004
2259
  }
2005
2260
  function setTheme(partial) {
2006
2261
  setConfig((prev) => ({ ...prev, ...partial }));
2007
2262
  }
2008
- return { config: getConfig, setTheme, resolveClass };
2263
+ function applyTo(root) {
2264
+ const handle = {};
2265
+ let applied = /* @__PURE__ */ new Set();
2266
+ const stop = effect(() => {
2267
+ const variables = getConfig().variables ?? {};
2268
+ const next = new Set(Object.keys(variables));
2269
+ for (const name of applied) {
2270
+ if (!next.has(name)) removeThemeLayer(root, name, handle);
2271
+ }
2272
+ for (const [name, value] of Object.entries(variables)) setThemeLayer(root, name, handle, value);
2273
+ applied = next;
2274
+ });
2275
+ let released = false;
2276
+ return () => {
2277
+ if (released) return;
2278
+ released = true;
2279
+ stop();
2280
+ for (const name of applied) removeThemeLayer(root, name, handle);
2281
+ applied.clear();
2282
+ };
2283
+ }
2284
+ return { config: getConfig, setTheme, resolveClass, applyTo };
2009
2285
  }
2010
2286
  function componentAdapter(config) {
2011
2287
  const theme = createTheme({ prefix: config.prefix });
2288
+ const reprefix = (cls, prefix) => prefix !== config.prefix && cls.startsWith(`${config.prefix}-`) ? `${prefix}-${cls.slice(config.prefix.length + 1)}` : cls;
2012
2289
  const components = {};
2013
2290
  for (const [name, mapping] of Object.entries(config.components)) {
2014
2291
  const factory = tagFactory(mapping.tag || "div");
2015
- components[name] = (props = {}) => {
2292
+ components[name] = (props = {}, children) => {
2016
2293
  const { variant, size, class: userClass, ...rest } = props;
2017
- const classes = [mapping.baseClass];
2018
- if (variant && mapping.variants?.[variant]) {
2019
- classes.push(mapping.variants[variant]);
2020
- }
2021
- if (size && mapping.sizes?.[size]) {
2022
- classes.push(mapping.sizes[size]);
2023
- }
2024
- let finalClass;
2025
- if (typeof userClass === "function") {
2026
- finalClass = () => {
2027
- const extra = userClass();
2028
- return extra ? `${classes.join(" ")} ${extra}` : classes.join(" ");
2029
- };
2030
- } else if (typeof userClass === "string" && userClass) {
2031
- finalClass = `${classes.join(" ")} ${userClass}`;
2032
- } else {
2033
- finalClass = classes.join(" ");
2034
- }
2294
+ const themeClasses = () => {
2295
+ const current = theme.config();
2296
+ const overrides = current.classOverrides ?? {};
2297
+ const pick = (key, fallback) => Object.hasOwn(overrides, key) ? overrides[key] : reprefix(fallback, current.prefix);
2298
+ const classes = [pick(name, mapping.baseClass)];
2299
+ const variantClass = ownString(mapping.variants, variant);
2300
+ if (variant && variantClass) {
2301
+ classes.push(pick(`${name}-${variant}`, variantClass));
2302
+ }
2303
+ const sizeClass = ownString(mapping.sizes, size);
2304
+ if (size && sizeClass) {
2305
+ classes.push(pick(`${name}-${size}`, sizeClass));
2306
+ }
2307
+ return classes.filter(Boolean).join(" ");
2308
+ };
2309
+ const finalClass = () => {
2310
+ const base = themeClasses();
2311
+ const extra = typeof userClass === "function" ? userClass() : userClass;
2312
+ return typeof extra === "string" && extra ? `${base} ${extra}` : base;
2313
+ };
2035
2314
  const mergedProps = {
2036
2315
  ...mapping.defaultProps,
2037
2316
  ...rest,
2038
2317
  class: finalClass
2039
2318
  };
2040
- return factory(mergedProps);
2319
+ return children === void 0 ? factory(mergedProps) : factory(mergedProps, children);
2041
2320
  };
2042
2321
  }
2043
2322
  return { name: config.name, components, theme };