sibujs 3.4.0 → 4.0.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 (98) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +118 -15
  3. package/dist/browser.cjs +410 -101
  4. package/dist/browser.d.cts +50 -5
  5. package/dist/browser.d.ts +50 -5
  6. package/dist/browser.js +8 -6
  7. package/dist/build.cjs +777 -385
  8. package/dist/build.js +15 -15
  9. package/dist/cdn.global.js +11 -7
  10. package/dist/{chunk-JCO7AHNU.js → chunk-3EKJK5FZ.js} +73 -34
  11. package/dist/chunk-4MFZMLUU.js +147 -0
  12. package/dist/chunk-52XFPGSN.js +123 -0
  13. package/dist/chunk-5INI7D2L.js +194 -0
  14. package/dist/{chunk-OAVXAFAY.js → chunk-6LG2T7LO.js} +5 -5
  15. package/dist/{chunk-OOUQUCNN.js → chunk-ACBT6UZS.js} +57 -22
  16. package/dist/{chunk-F7HW2NI5.js → chunk-AW6HPJ3L.js} +1 -1
  17. package/dist/chunk-DHDZ7IVN.js +109 -0
  18. package/dist/{chunk-JNGGM2XE.js → chunk-DQVUBVNX.js} +16 -25
  19. package/dist/{chunk-AXFWGY32.js → chunk-DR7H6UGM.js} +232 -67
  20. package/dist/{chunk-WSGLJYFI.js → chunk-E62UADKL.js} +9 -3
  21. package/dist/{chunk-3Z2NGWQI.js → chunk-ES2GERY2.js} +1 -1
  22. package/dist/{chunk-S3V6ZXPI.js → chunk-EWEE7NBF.js} +115 -41
  23. package/dist/{chunk-3DVGCW7O.js → chunk-F64ZPCFE.js} +8 -8
  24. package/dist/{chunk-APFRCIRI.js → chunk-GCSGGA3Q.js} +295 -198
  25. package/dist/{chunk-EX77FXTT.js → chunk-HYCCIYNS.js} +228 -51
  26. package/dist/{chunk-ELXMRSVN.js → chunk-NF3LYCQH.js} +259 -124
  27. package/dist/chunk-O3QQ3XOL.js +61 -0
  28. package/dist/{chunk-BUKYCWHX.js → chunk-PBQEW5VW.js} +14 -10
  29. package/dist/{chunk-H5FI5WDP.js → chunk-RRIDJQ5C.js} +3 -3
  30. package/dist/{chunk-S3NFJO6L.js → chunk-TIRZCERI.js} +36 -5
  31. package/dist/{chunk-ODXVQ3CS.js → chunk-VFHIVEPF.js} +309 -188
  32. package/dist/chunk-VPP2FONR.js +142 -0
  33. package/dist/{chunk-KZBEVFXZ.js → chunk-W55YHPEP.js} +5 -3
  34. package/dist/{chunk-FKQG7FTY.js → chunk-WN6WDFCB.js} +4 -4
  35. package/dist/{chunk-CEBWDD63.js → chunk-WOOMUJFT.js} +228 -86
  36. package/dist/{chunk-ITWCTZYH.js → chunk-Z2CE42DB.js} +19 -25
  37. package/dist/{contracts-ey_Qh8ef.d.cts → contracts-Hg6ITAEF.d.cts} +6 -6
  38. package/dist/{contracts-ey_Qh8ef.d.ts → contracts-Hg6ITAEF.d.ts} +6 -6
  39. package/dist/{customElement-CPfIrbvg.d.ts → customElement-CKLoO63w.d.cts} +73 -11
  40. package/dist/{customElement-CPfIrbvg.d.cts → customElement-CKLoO63w.d.ts} +73 -11
  41. package/dist/data.cjs +483 -205
  42. package/dist/data.d.cts +139 -7
  43. package/dist/data.d.ts +139 -7
  44. package/dist/data.js +9 -9
  45. package/dist/devtools.cjs +222 -87
  46. package/dist/devtools.js +8 -8
  47. package/dist/dispose-4FZ6OFXR.js +19 -0
  48. package/dist/ecosystem.cjs +419 -175
  49. package/dist/ecosystem.js +11 -11
  50. package/dist/extras.cjs +1981 -765
  51. package/dist/extras.d.cts +7 -7
  52. package/dist/extras.d.ts +7 -7
  53. package/dist/extras.js +29 -26
  54. package/dist/index.cjs +784 -385
  55. package/dist/index.d.cts +299 -28
  56. package/dist/index.d.ts +299 -28
  57. package/dist/index.js +33 -18
  58. package/dist/motion.cjs +180 -50
  59. package/dist/motion.d.cts +26 -0
  60. package/dist/motion.d.ts +26 -0
  61. package/dist/motion.js +5 -5
  62. package/dist/patterns.cjs +437 -170
  63. package/dist/patterns.d.cts +60 -13
  64. package/dist/patterns.d.ts +60 -13
  65. package/dist/patterns.js +7 -7
  66. package/dist/performance.cjs +333 -148
  67. package/dist/performance.d.cts +55 -7
  68. package/dist/performance.d.ts +55 -7
  69. package/dist/performance.js +9 -9
  70. package/dist/plugins.cjs +1374 -576
  71. package/dist/plugins.d.cts +227 -9
  72. package/dist/plugins.d.ts +227 -9
  73. package/dist/plugins.js +613 -321
  74. package/dist/{ssr-D62yFwuw.d.cts → ssr-BiPRdZ6n.d.cts} +44 -15
  75. package/dist/{ssr-D62yFwuw.d.ts → ssr-BiPRdZ6n.d.ts} +44 -15
  76. package/dist/{ssr-2PPULEK2.js → ssr-JFP5535P.js} +5 -4
  77. package/dist/ssr.cjs +1104 -443
  78. package/dist/ssr.d.cts +230 -20
  79. package/dist/ssr.d.ts +230 -20
  80. package/dist/ssr.js +15 -12
  81. package/dist/{startup-0Qv6aosO.d.cts → startup-BMpaiMhP.d.cts} +1 -1
  82. package/dist/{startup-0Qv6aosO.d.ts → startup-BMpaiMhP.d.ts} +1 -1
  83. package/dist/testing.cjs +205 -63
  84. package/dist/testing.d.cts +1 -1
  85. package/dist/testing.d.ts +1 -1
  86. package/dist/testing.js +8 -10
  87. package/dist/ui.cjs +640 -199
  88. package/dist/ui.d.cts +2 -2
  89. package/dist/ui.d.ts +2 -2
  90. package/dist/ui.js +10 -10
  91. package/dist/widgets.cjs +207 -82
  92. package/dist/widgets.js +9 -9
  93. package/package.json +12 -7
  94. package/dist/chunk-5VGSK6D2.js +0 -80
  95. package/dist/chunk-COY6PUD2.js +0 -34
  96. package/dist/chunk-K35BAR4P.js +0 -96
  97. package/dist/chunk-L3GAGWCC.js +0 -111
  98. package/dist/chunk-RT3IYFN2.js +0 -38
package/dist/index.cjs CHANGED
@@ -26,8 +26,9 @@ __export(index_exports, {
26
26
  Fragment: () => Fragment,
27
27
  KeepAlive: () => KeepAlive,
28
28
  Loading: () => Loading,
29
+ MAX_DRAIN_TEARDOWNS: () => MAX_DRAIN_TEARDOWNS,
29
30
  Portal: () => Portal,
30
- SVG_NS: () => SVG_NS,
31
+ SVG_NS: () => SVG_NS2,
31
32
  Suspense: () => Suspense,
32
33
  __resetIdCounter: () => __resetIdCounter,
33
34
  a: () => a,
@@ -103,6 +104,7 @@ __export(index_exports, {
103
104
  g: () => g,
104
105
  getAction: () => getAction,
105
106
  getRequestScopedCache: () => getRequestScopedCache,
107
+ getRuntimeErrorHandler: () => getRuntimeErrorHandler,
106
108
  getSSRStore: () => getSSRStore,
107
109
  getSlot: () => getSlot,
108
110
  h1: () => h1,
@@ -176,6 +178,9 @@ __export(index_exports, {
176
178
  registerComponent: () => registerComponent,
177
179
  registerDisposer: () => registerDisposer,
178
180
  registerIsland: () => registerIsland,
181
+ replaceChildrenSafely: () => replaceChildrenSafely,
182
+ reportDrainRunaway: () => reportDrainRunaway,
183
+ reportError: () => reportError,
179
184
  resolveComponent: () => resolveComponent,
180
185
  retrack: () => retrack2,
181
186
  rp: () => rp,
@@ -189,6 +194,7 @@ __export(index_exports, {
189
194
  select: () => select,
190
195
  setGlobalErrorHandler: () => setGlobalErrorHandler,
191
196
  setMaxDrainIterations: () => setMaxDrainIterations2,
197
+ setRuntimeErrorHandler: () => setRuntimeErrorHandler,
192
198
  show: () => show,
193
199
  signal: () => signal,
194
200
  slot: () => slot,
@@ -228,6 +234,7 @@ __export(index_exports, {
228
234
  u: () => u,
229
235
  ul: () => ul,
230
236
  unregisterComponent: () => unregisterComponent,
237
+ unregisterDisposer: () => unregisterDisposer,
231
238
  unregisterIsland: () => unregisterIsland,
232
239
  untracked: () => untracked2,
233
240
  use: () => use,
@@ -240,6 +247,103 @@ __export(index_exports, {
240
247
  });
241
248
  module.exports = __toCommonJS(index_exports);
242
249
 
250
+ // src/core/errors.ts
251
+ var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
252
+ function state() {
253
+ const g2 = globalThis;
254
+ const existing = g2[ERROR_STATE_KEY];
255
+ if (existing) return existing;
256
+ const created = { handler: null, handlerDepth: 0 };
257
+ g2[ERROR_STATE_KEY] = created;
258
+ return created;
259
+ }
260
+ function setRuntimeErrorHandler(handler) {
261
+ const s2 = state();
262
+ const prev = s2.handler;
263
+ s2.handler = handler;
264
+ return prev;
265
+ }
266
+ function getRuntimeErrorHandler() {
267
+ return state().handler;
268
+ }
269
+ function normalizeError(value) {
270
+ if (value instanceof Error) return value;
271
+ const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
272
+ return new Error(message, { cause: value });
273
+ }
274
+ function safeStringify(value) {
275
+ if (typeof value === "symbol") return value.toString();
276
+ try {
277
+ return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
278
+ } catch {
279
+ return Object.prototype.toString.call(value);
280
+ }
281
+ }
282
+ function describe(context2) {
283
+ return context2.name ? `${context2.phase} "${context2.name}"` : context2.phase;
284
+ }
285
+ var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
286
+ function untrackedRun(fn) {
287
+ const api = globalThis[REACTIVE_REGISTRY_KEY];
288
+ return api?.untracked ? api.untracked(fn) : fn();
289
+ }
290
+ var PROPAGATE_EVENT = "sibu:error-propagate";
291
+ function claimedByBoundary(error, context2) {
292
+ const raw = context2.node;
293
+ if (!raw || typeof CustomEvent === "undefined") return false;
294
+ const target = resolveEventTarget(raw);
295
+ if (!target) return false;
296
+ try {
297
+ const event = new CustomEvent(PROPAGATE_EVENT, {
298
+ bubbles: true,
299
+ cancelable: true,
300
+ detail: { error, context: context2 }
301
+ });
302
+ return target.dispatchEvent(event) === false;
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+ function resolveEventTarget(node) {
308
+ const ELEMENT_NODE = 1;
309
+ let current = node;
310
+ while (current) {
311
+ if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
312
+ return current;
313
+ }
314
+ current = current.parentNode;
315
+ }
316
+ const self = node;
317
+ return typeof self.dispatchEvent === "function" ? self : null;
318
+ }
319
+ function defaultReport(error, context2) {
320
+ if (typeof console === "undefined") return;
321
+ console.error(`[SibuJS] Uncaught error during ${describe(context2)}: ${error.message}`, error);
322
+ }
323
+ function reportError(error, context2) {
324
+ const err = normalizeError(error);
325
+ untrackedRun(() => {
326
+ if (claimedByBoundary(err, context2)) return;
327
+ const s2 = state();
328
+ const handler = s2.handler;
329
+ if (handler && s2.handlerDepth === 0) {
330
+ s2.handlerDepth++;
331
+ try {
332
+ handler(error instanceof Error ? error : err, context2);
333
+ return;
334
+ } catch (handlerError) {
335
+ if (typeof console !== "undefined") {
336
+ const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
337
+ console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
338
+ }
339
+ } finally {
340
+ s2.handlerDepth--;
341
+ }
342
+ }
343
+ defaultReport(err, context2);
344
+ });
345
+ }
346
+
243
347
  // src/core/dev.ts
244
348
  function isDev() {
245
349
  return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
@@ -264,6 +368,14 @@ function devWarn(message) {
264
368
 
265
369
  // src/core/rendering/dispose.ts
266
370
  var elementDisposers = /* @__PURE__ */ new WeakMap();
371
+ var MAX_DRAIN_TEARDOWNS = 1e4;
372
+ function reportDrainRunaway(label2, executed, remaining) {
373
+ if (typeof console === "undefined") return;
374
+ console.error(
375
+ `[SibuJS ${label2}] 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.`,
376
+ { executed, remaining }
377
+ );
378
+ }
267
379
  var _isDev2 = isDev();
268
380
  var activeBindingCount = 0;
269
381
  function registerDisposer(node, teardown) {
@@ -275,6 +387,15 @@ function registerDisposer(node, teardown) {
275
387
  disposers.push(teardown);
276
388
  if (_isDev2) activeBindingCount++;
277
389
  }
390
+ function unregisterDisposer(node, teardown) {
391
+ const disposers = elementDisposers.get(node);
392
+ if (!disposers) return;
393
+ const index = disposers.indexOf(teardown);
394
+ if (index === -1) return;
395
+ disposers.splice(index, 1);
396
+ if (_isDev2) activeBindingCount--;
397
+ if (disposers.length === 0) elementDisposers.delete(node);
398
+ }
278
399
  function dispose(node) {
279
400
  const stack = [node];
280
401
  const order = [];
@@ -288,40 +409,47 @@ function dispose(node) {
288
409
  }
289
410
  for (let i2 = order.length - 1; i2 >= 0; i2--) {
290
411
  const current = order[i2];
291
- const disposers = elementDisposers.get(current);
292
- if (disposers) {
293
- const snapshot = disposers.slice();
294
- elementDisposers.delete(current);
295
- if (_isDev2) activeBindingCount -= snapshot.length;
296
- for (const d of snapshot) {
297
- try {
298
- d();
299
- } catch (err) {
300
- if (_isDev2 && typeof console !== "undefined") {
301
- console.warn("[SibuJS] Disposer threw during cleanup:", err);
302
- }
303
- }
304
- }
305
- let extraPasses = 0;
306
- while (extraPasses++ < 8) {
307
- const added = elementDisposers.get(current);
308
- if (!added || added.length === 0) break;
309
- const moreSnapshot = added.slice();
412
+ if (elementDisposers.has(current)) {
413
+ let executed = 0;
414
+ let runaway = false;
415
+ while (!runaway) {
416
+ const pending = elementDisposers.get(current);
417
+ if (!pending || pending.length === 0) break;
418
+ const snapshot = pending.slice();
310
419
  elementDisposers.delete(current);
311
- if (_isDev2) activeBindingCount -= moreSnapshot.length;
312
- for (const d of moreSnapshot) {
420
+ if (_isDev2) activeBindingCount -= snapshot.length;
421
+ for (let i3 = 0; i3 < snapshot.length; i3++) {
422
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
423
+ const rest = snapshot.slice(i3);
424
+ const added = elementDisposers.get(current);
425
+ elementDisposers.set(current, added ? rest.concat(added) : rest);
426
+ if (_isDev2) activeBindingCount += rest.length;
427
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
428
+ runaway = true;
429
+ break;
430
+ }
431
+ executed++;
313
432
  try {
314
- d();
433
+ snapshot[i3]();
315
434
  } catch (err) {
316
- if (_isDev2 && typeof console !== "undefined") {
317
- console.warn("[SibuJS] Disposer threw during cleanup:", err);
318
- }
435
+ reportError(err, { phase: "cleanup", name: "disposer" });
319
436
  }
320
437
  }
321
438
  }
322
439
  }
323
440
  }
324
441
  }
442
+ function replaceChildrenSafely(parent, ...next) {
443
+ for (let i2 = 0; i2 < next.length; i2++) {
444
+ const node = next[i2];
445
+ node.parentNode?.removeChild(node);
446
+ }
447
+ const current = Array.from(parent.childNodes);
448
+ for (let i2 = 0; i2 < current.length; i2++) {
449
+ dispose(current[i2]);
450
+ }
451
+ parent.replaceChildren(...next);
452
+ }
325
453
  function checkLeaks(warnThreshold = 0) {
326
454
  if (!_isDev2) return 0;
327
455
  if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
@@ -342,10 +470,12 @@ function isEventHandlerAttr(name) {
342
470
  return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
343
471
  }
344
472
  var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
473
+ var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
345
474
  function sanitizeUrl(url) {
346
- const trimmed = stripControlChars(url).trim();
347
- if (!trimmed) return "";
348
- const lower = trimmed.toLowerCase();
475
+ const probe = stripControlChars(url).trim();
476
+ if (!probe) return "";
477
+ const output2 = url.replace(OUTER_TRIM, "");
478
+ const lower = probe.toLowerCase();
349
479
  let schemeEnd = -1;
350
480
  for (let i2 = 0; i2 < lower.length; i2++) {
351
481
  const ch = lower.charCodeAt(i2);
@@ -355,12 +485,13 @@ function sanitizeUrl(url) {
355
485
  }
356
486
  if (ch === 47 || ch === 63 || ch === 35) break;
357
487
  }
358
- if (schemeEnd === -1) return trimmed;
488
+ if (schemeEnd === -1) return output2;
359
489
  const scheme = lower.slice(0, schemeEnd + 1);
360
- if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
490
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
361
491
  if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
362
- return trimmed;
492
+ return output2;
363
493
  }
494
+ var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
364
495
  function sanitizeSrcset(value) {
365
496
  const parts = value.split(",");
366
497
  const out = [];
@@ -369,6 +500,7 @@ function sanitizeSrcset(value) {
369
500
  if (!part) continue;
370
501
  const m = part.match(/^(\S+)(\s+.+)?$/);
371
502
  if (!m) continue;
503
+ if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
372
504
  const safe = sanitizeUrl(m[1]);
373
505
  if (!safe) continue;
374
506
  out.push(m[2] ? `${safe}${m[2]}` : safe);
@@ -376,23 +508,83 @@ function sanitizeSrcset(value) {
376
508
  return out.join(", ");
377
509
  }
378
510
  var CSS_DANGER_GATE = /[(:@\\]/;
511
+ function isCssWhitespace(ch) {
512
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
513
+ }
514
+ function isHexDigit(ch) {
515
+ return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
516
+ }
517
+ function decodeCssEscapes(value) {
518
+ let out = "";
519
+ for (let i2 = 0; i2 < value.length; i2++) {
520
+ const ch = value[i2];
521
+ if (ch !== "\\") {
522
+ out += ch;
523
+ continue;
524
+ }
525
+ if (i2 + 1 >= value.length) break;
526
+ const next = value[i2 + 1];
527
+ if (next === "\n" || next === "\f") {
528
+ i2 += 1;
529
+ continue;
530
+ }
531
+ if (next === "\r") {
532
+ i2 += value[i2 + 2] === "\n" ? 2 : 1;
533
+ continue;
534
+ }
535
+ if (isHexDigit(next)) {
536
+ let hex = "";
537
+ let j = i2 + 1;
538
+ while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
539
+ hex += value[j];
540
+ j++;
541
+ }
542
+ if (j < value.length && isCssWhitespace(value[j])) {
543
+ j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
544
+ }
545
+ const code2 = Number.parseInt(hex, 16);
546
+ const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
547
+ out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
548
+ i2 = j - 1;
549
+ continue;
550
+ }
551
+ out += next;
552
+ i2 += 1;
553
+ }
554
+ return out;
555
+ }
379
556
  function sanitizeCSSValue(value) {
380
557
  if (!CSS_DANGER_GATE.test(value)) return value;
381
- const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
382
- const code2 = Number.parseInt(hex, 16);
383
- if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
384
- try {
385
- return String.fromCodePoint(code2);
386
- } catch {
387
- return "";
388
- }
389
- });
390
- const lower = decoded.toLowerCase().replace(/\s+/g, "");
558
+ const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
559
+ const lower = normalized.toLowerCase().replace(/\s+/g, "");
391
560
  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")) {
392
561
  return "";
393
562
  }
394
563
  return value;
395
564
  }
565
+ function sanitizeStyleAttribute(cssText) {
566
+ const input2 = String(cssText);
567
+ if (input2.trim() === "") return "";
568
+ if (typeof document === "undefined") {
569
+ return sanitizeCSSValue(input2) === "" ? "" : input2;
570
+ }
571
+ const probe = document.createElement("div");
572
+ try {
573
+ probe.style.cssText = input2;
574
+ } catch {
575
+ return "";
576
+ }
577
+ const declarations = [];
578
+ for (let i2 = 0; i2 < probe.style.length; i2++) {
579
+ const property = probe.style[i2];
580
+ const value = probe.style.getPropertyValue(property);
581
+ if (sanitizeCSSValue(value) === "") continue;
582
+ if (sanitizeCSSValue(`${property}:${value}`) === "") continue;
583
+ const priority = probe.style.getPropertyPriority(property);
584
+ declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
585
+ }
586
+ return declarations.join("; ");
587
+ }
396
588
  var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
397
589
  "href",
398
590
  "xlink:href",
@@ -405,20 +597,101 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
405
597
  "background",
406
598
  "srcset",
407
599
  "ping",
408
- "data"
600
+ "data",
601
+ // `<html manifest>` is a URL sink too. It was carried only by the SSR
602
+ // serializers' private copy of this set; folding that copy into this one would
603
+ // otherwise have silently dropped its coverage.
604
+ "manifest"
409
605
  ]);
410
- function isUrlAttribute(attr) {
411
- return URL_ATTRIBUTES.has(attr.toLowerCase());
606
+ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
607
+ function isHtmlContentAttribute(name) {
608
+ return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
412
609
  }
413
610
  function sanitizeAttributeString(attr, value) {
414
611
  const lower = attr.toLowerCase();
415
612
  if (lower === "srcset") return sanitizeSrcset(value);
416
613
  if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
614
+ if (lower === "style") return sanitizeStyleAttribute(value);
417
615
  return value;
418
616
  }
419
617
 
420
- // src/reactivity/track-core.ts
618
+ // src/utils/setSafeAttribute.ts
421
619
  var _isDev3 = isDev();
620
+ var HTML_NS = "http://www.w3.org/1999/xhtml";
621
+ var SVG_NS = "http://www.w3.org/2000/svg";
622
+ var XLINK_NS = "http://www.w3.org/1999/xlink";
623
+ var XML_NS = "http://www.w3.org/XML/1998/namespace";
624
+ var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
625
+ function isHtmlElement(el) {
626
+ return el.namespaceURI === null || el.namespaceURI === HTML_NS;
627
+ }
628
+ function setProp(el, key, val) {
629
+ el[key] = val;
630
+ }
631
+ function namespaceFor(el, name) {
632
+ if (el.namespaceURI !== SVG_NS) return null;
633
+ const lower = name.toLowerCase();
634
+ if (lower.startsWith("xlink:")) return XLINK_NS;
635
+ if (lower.startsWith("xml:")) return XML_NS;
636
+ return null;
637
+ }
638
+ function setSafeAttribute(el, name, value, options = {}) {
639
+ const ns = namespaceFor(el, name);
640
+ const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
641
+ if (isEventHandlerAttr(name)) {
642
+ if (_isDev3) {
643
+ devWarn(
644
+ `${options.label ?? "setSafeAttribute"}: refusing to set event-handler attribute "${name}". Its value would be evaluated as JavaScript. Use on:{ ${name.slice(2)}: fn } instead.`
645
+ );
646
+ }
647
+ if (ns) el.removeAttributeNS(ns, localName);
648
+ else el.removeAttribute(name);
649
+ return false;
650
+ }
651
+ if (value == null) {
652
+ if (ns) el.removeAttributeNS(ns, localName);
653
+ else el.removeAttribute(name);
654
+ return true;
655
+ }
656
+ if (isHtmlContentAttribute(name)) {
657
+ if (_isDev3) {
658
+ devWarn(
659
+ `${options.label ?? "setSafeAttribute"}: refusing to set "${name}". The browser parses this attribute as a nested HTML document, so a generic string value cannot be made safe \u2014 attribute escaping is undone before the parse. Build the frame's content another way.`
660
+ );
661
+ }
662
+ if (ns) el.removeAttributeNS(ns, localName);
663
+ else el.removeAttribute(name);
664
+ return false;
665
+ }
666
+ const html3 = isHtmlElement(el);
667
+ const idlName = html3 ? name.toLowerCase() : name;
668
+ if (typeof value === "boolean") {
669
+ if (html3 && BOOLEAN_IDL_ATTRS.has(idlName) && idlName in el) {
670
+ setProp(el, idlName, value);
671
+ } else if (value) {
672
+ if (ns) el.setAttributeNS(ns, name, "");
673
+ else el.setAttribute(name, "");
674
+ } else if (ns) {
675
+ el.removeAttributeNS(ns, localName);
676
+ } else {
677
+ el.removeAttribute(name);
678
+ }
679
+ return true;
680
+ }
681
+ const str = String(value);
682
+ if (options.syncValueProperty !== false && html3 && (idlName === "value" || idlName === "checked") && idlName in el) {
683
+ setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
684
+ return true;
685
+ }
686
+ const safe = sanitizeAttributeString(name, str);
687
+ const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
688
+ if (current === safe) return true;
689
+ if (ns) el.setAttributeNS(ns, name, safe);
690
+ else el.setAttribute(name, safe);
691
+ return true;
692
+ }
693
+
694
+ // src/reactivity/track-core.ts
422
695
  var POOL_MAX = 4096;
423
696
  var nodePool = [];
424
697
  function createNode() {
@@ -426,6 +699,7 @@ function createNode() {
426
699
  sig: null,
427
700
  sub: null,
428
701
  epoch: 0,
702
+ depVersion: 0,
429
703
  sigPrev: null,
430
704
  sigNext: null,
431
705
  subPrev: null,
@@ -510,8 +784,27 @@ function safeInvoke(sub2) {
510
784
  try {
511
785
  sub2();
512
786
  } catch (err) {
513
- if (_isDev3) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
787
+ const s2 = sub2;
788
+ reportError(err, { phase: s2._errorPhase ?? "effect", name: s2.__name, node: s2._errorNode });
789
+ }
790
+ }
791
+ function depsChanged(sub2) {
792
+ let node = sub2.depsHead ?? null;
793
+ if (node === null) return true;
794
+ while (node !== null) {
795
+ const sig = node.sig;
796
+ if (sig === null) return true;
797
+ if (sig._d === true && sig._validate !== void 0) {
798
+ try {
799
+ sig._validate();
800
+ } catch {
801
+ return true;
802
+ }
803
+ }
804
+ if (node.depVersion !== sig.__v) return true;
805
+ node = node.subNext;
514
806
  }
807
+ return false;
515
808
  }
516
809
  var suspendDepth = 0;
517
810
  var trackingSuspended = false;
@@ -593,7 +886,7 @@ function track(effectFn, subscriber) {
593
886
  const sub2 = subscriber;
594
887
  return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
595
888
  }
596
- function reactiveBinding(commit) {
889
+ function reactiveBinding(commit, ownerNode) {
597
890
  const run = () => {
598
891
  const s2 = subscriber;
599
892
  if (s2._disposed || s2._reentrant) return;
@@ -609,13 +902,17 @@ function reactiveBinding(commit) {
609
902
  subscriber.depsTail = null;
610
903
  subscriber._epoch = 0;
611
904
  subscriber._structDirty = false;
905
+ subscriber._hasComputedDep = false;
612
906
  subscriber._runEpoch = 0;
613
907
  subscriber._runs = 0;
614
908
  subscriber._reentrant = false;
615
909
  subscriber._disposed = false;
910
+ subscriber._errorPhase = "binding";
911
+ subscriber._errorNode = ownerNode;
616
912
  run();
617
913
  return subscriber._dispose ?? (subscriber._dispose = () => {
618
914
  subscriber._disposed = true;
915
+ subscriber._errorNode = void 0;
619
916
  cleanup(subscriber);
620
917
  });
621
918
  }
@@ -627,9 +924,12 @@ function recordDependency(signal2) {
627
924
  const active = sig.__activeNode ?? null;
628
925
  if (active !== null && active.sub === sub2) {
629
926
  active.epoch = epoch;
927
+ active.depVersion = sig.__v ?? Number.NaN;
630
928
  return;
631
929
  }
930
+ if (sig._validate !== void 0) sub2._hasComputedDep = true;
632
931
  const node = allocNode(signal2, sub2, epoch);
932
+ node.depVersion = sig.__v ?? Number.NaN;
633
933
  node.prevActive = active;
634
934
  sig.__activeNode = node;
635
935
  linkSub(sub2, node);
@@ -648,9 +948,10 @@ function cleanup(subscriber) {
648
948
  node = next;
649
949
  }
650
950
  }
651
- var maxSubscriberRepeats = 50;
951
+ var maxSubscriberRepeats = 1e3;
652
952
  var maxDrainIterations = 1e6;
653
953
  var drainEpoch = 0;
954
+ var quarantined = /* @__PURE__ */ new Set();
654
955
  function setMaxSubscriberRepeats(n) {
655
956
  const prev = maxSubscriberRepeats;
656
957
  if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
@@ -672,19 +973,21 @@ function tickRepeat(sub2) {
672
973
  return s2._runs > maxSubscriberRepeats;
673
974
  }
674
975
  function cycleError(sub2) {
675
- if (typeof console !== "undefined") {
676
- const name = sub2.__name ?? "<unnamed>";
677
- console.error(
678
- `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
679
- );
680
- }
976
+ const name = sub2.__name ?? "<unnamed>";
977
+ reportError(
978
+ new Error(
979
+ `subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. This subscriber is quarantined for the rest of this update; other pending work still runs.`
980
+ ),
981
+ { phase: "scheduler", name }
982
+ );
681
983
  }
682
984
  function absoluteDrainError() {
683
- if (typeof console !== "undefined") {
684
- console.error(
685
- `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
686
- );
687
- }
985
+ reportError(
986
+ new Error(
987
+ `Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
988
+ ),
989
+ { phase: "scheduler" }
990
+ );
688
991
  }
689
992
  function drainQueue() {
690
993
  let i2 = 0;
@@ -694,11 +997,14 @@ function drainQueue() {
694
997
  break;
695
998
  }
696
999
  const sub2 = pendingQueue[i2++];
1000
+ pendingSet.delete(sub2);
1001
+ if (quarantined.size > 0 && quarantined.has(sub2)) continue;
1002
+ if (sub2._hasComputedDep === true && !depsChanged(sub2)) continue;
697
1003
  if (tickRepeat(sub2)) {
698
1004
  cycleError(sub2);
699
- break;
1005
+ quarantined.add(sub2);
1006
+ continue;
700
1007
  }
701
- pendingSet.delete(sub2);
702
1008
  safeInvoke(sub2);
703
1009
  }
704
1010
  }
@@ -713,6 +1019,7 @@ function drainNotificationQueue() {
713
1019
  if (notifyDepth === 0) {
714
1020
  pendingQueue.length = 0;
715
1021
  pendingSet.clear();
1022
+ if (quarantined.size > 0) quarantined.clear();
716
1023
  }
717
1024
  }
718
1025
  }
@@ -806,6 +1113,7 @@ function notifySubscribers(signal2) {
806
1113
  if (notifyDepth === 0) {
807
1114
  pendingQueue.length = 0;
808
1115
  pendingSet.clear();
1116
+ if (quarantined.size > 0) quarantined.clear();
809
1117
  }
810
1118
  }
811
1119
  }
@@ -833,7 +1141,7 @@ function forEachSubscriber(signal2, visit) {
833
1141
 
834
1142
  // src/reactivity/track.ts
835
1143
  var _isDev4 = isDev();
836
- var _runtimeVersion = true ? "3.4.0" : "dev";
1144
+ var _runtimeVersion = true ? "4.0.0" : "dev";
837
1145
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
838
1146
  function resolveReactiveApi() {
839
1147
  const g2 = globalThis;
@@ -890,16 +1198,9 @@ var getSubscriberDeps2 = API.getSubscriberDeps;
890
1198
  var forEachSubscriber2 = API.forEachSubscriber;
891
1199
 
892
1200
  // src/reactivity/bindAttribute.ts
893
- var _isDev5 = isDev();
894
- function setProp(el, key, val) {
895
- el[key] = val;
896
- }
897
1201
  function bindAttribute(el, attr, getter) {
898
1202
  if (isEventHandlerAttr(attr)) {
899
- if (_isDev5)
900
- devWarn(
901
- `bindAttribute: refusing to bind event-handler attribute "${attr}". Use on:{ ${attr.slice(2)}: fn } instead.`
902
- );
1203
+ setSafeAttribute(el, attr, "", { label: `bindAttribute("${attr}")` });
903
1204
  return () => {
904
1205
  };
905
1206
  }
@@ -908,28 +1209,12 @@ function bindAttribute(el, attr, getter) {
908
1209
  try {
909
1210
  value = getter();
910
1211
  } catch (err) {
911
- if (_isDev5)
912
- devWarn(`bindAttribute: getter for "${attr}" threw: ${err instanceof Error ? err.message : String(err)}`);
1212
+ reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
913
1213
  return;
914
1214
  }
915
- if (typeof value === "boolean") {
916
- if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
917
- setProp(el, attr, value);
918
- } else if (value) {
919
- el.setAttribute(attr, "");
920
- } else {
921
- el.removeAttribute(attr);
922
- }
923
- return;
924
- }
925
- const str = String(value);
926
- if ((attr === "value" || attr === "checked") && attr in el) {
927
- setProp(el, attr, attr === "checked" ? Boolean(value) : str);
928
- } else {
929
- el.setAttribute(attr, sanitizeAttributeString(attr, str));
930
- }
1215
+ setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
931
1216
  }
932
- return reactiveBinding2(commit);
1217
+ return reactiveBinding2(commit, el);
933
1218
  }
934
1219
  function bindDynamic(el, nameGetter, valueGetter) {
935
1220
  let prevName = null;
@@ -938,29 +1223,24 @@ function bindDynamic(el, nameGetter, valueGetter) {
938
1223
  try {
939
1224
  name = typeof nameGetter === "function" ? nameGetter() : nameGetter;
940
1225
  } catch (err) {
941
- if (_isDev5) devWarn(`bindDynamic: name getter threw: ${err instanceof Error ? err.message : String(err)}`);
1226
+ reportError(err, { phase: "binding", name: "bindDynamic (name getter)", node: el });
942
1227
  return;
943
1228
  }
944
1229
  let value;
945
1230
  try {
946
1231
  value = typeof valueGetter === "function" ? valueGetter() : valueGetter;
947
1232
  } catch (err) {
948
- if (_isDev5) devWarn(`bindDynamic: value getter threw: ${err instanceof Error ? err.message : String(err)}`);
1233
+ reportError(err, { phase: "binding", name: "bindDynamic (value getter)", node: el });
949
1234
  return;
950
1235
  }
951
- if (isEventHandlerAttr(name)) return;
952
1236
  if (prevName !== null && prevName !== name) {
953
1237
  el.removeAttribute(prevName);
1238
+ prevName = null;
954
1239
  }
955
- const str = String(value);
956
- if ((name === "value" || name === "checked") && name in el) {
957
- setProp(el, name, name === "checked" ? Boolean(value) : str);
958
- } else {
959
- el.setAttribute(name, sanitizeAttributeString(name, str));
960
- }
1240
+ if (!setSafeAttribute(el, name, value, { label: "bindDynamic" })) return;
961
1241
  prevName = name;
962
1242
  }
963
- const teardown = reactiveBinding2(commit);
1243
+ const teardown = reactiveBinding2(commit, el);
964
1244
  return () => {
965
1245
  teardown();
966
1246
  if (prevName !== null) {
@@ -970,7 +1250,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
970
1250
  }
971
1251
 
972
1252
  // src/reactivity/bindChildNode.ts
973
- var _isDev6 = isDev();
1253
+ var _isDev5 = isDev();
974
1254
  function bindChildNode(placeholder, getter) {
975
1255
  let lastNodes = [];
976
1256
  function commit() {
@@ -978,7 +1258,7 @@ function bindChildNode(placeholder, getter) {
978
1258
  try {
979
1259
  result = getter();
980
1260
  } catch (err) {
981
- if (_isDev6) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
1261
+ reportError(err, { phase: "binding", name: "bindChildNode", node: placeholder });
982
1262
  return;
983
1263
  }
984
1264
  if (result == null || typeof result === "boolean") {
@@ -1004,7 +1284,7 @@ function bindChildNode(placeholder, getter) {
1004
1284
  if (item == null || typeof item === "boolean") continue;
1005
1285
  const node = item instanceof Node ? item : document.createTextNode(String(item));
1006
1286
  if (seen.has(node)) {
1007
- if (_isDev6)
1287
+ if (_isDev5)
1008
1288
  devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
1009
1289
  continue;
1010
1290
  }
@@ -1039,12 +1319,12 @@ function bindChildNode(placeholder, getter) {
1039
1319
  }
1040
1320
  lastNodes = newNodes;
1041
1321
  }
1042
- return reactiveBinding2(commit);
1322
+ return reactiveBinding2(commit, placeholder);
1043
1323
  }
1044
1324
 
1045
1325
  // src/core/rendering/tagFactory.ts
1046
- var SVG_NS = "http://www.w3.org/2000/svg";
1047
- var _isDev7 = isDev();
1326
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
1327
+ var _isDev6 = isDev();
1048
1328
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
1049
1329
  function isBlockedTag(tag) {
1050
1330
  return BLOCKED_TAGS.has(tag.toLowerCase());
@@ -1062,9 +1342,6 @@ var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
1062
1342
  "self",
1063
1343
  "frames"
1064
1344
  ]);
1065
- function setProp2(el, key, val) {
1066
- el[key] = val;
1067
- }
1068
1345
  function looksLikeClassList(s2) {
1069
1346
  const t = s2.trim();
1070
1347
  if (!t) return false;
@@ -1088,13 +1365,13 @@ function toKebab(prop) {
1088
1365
  function applyStyle(el, style2) {
1089
1366
  if (typeof style2 === "function") {
1090
1367
  const teardown = track2(() => {
1091
- el.setAttribute("style", style2());
1368
+ el.setAttribute("style", sanitizeStyleAttribute(String(style2())));
1092
1369
  });
1093
1370
  registerDisposer(el, teardown);
1094
1371
  return;
1095
1372
  }
1096
1373
  if (typeof style2 === "string") {
1097
- el.setAttribute("style", style2);
1374
+ el.setAttribute("style", sanitizeStyleAttribute(style2));
1098
1375
  return;
1099
1376
  }
1100
1377
  const htmlEl = el;
@@ -1215,7 +1492,7 @@ var tagFactory = (tag, ns) => {
1215
1492
  appendChildren(el, second);
1216
1493
  return el;
1217
1494
  }
1218
- if (_isDev7 && looksLikeClassList(first)) {
1495
+ if (_isDev6 && looksLikeClassList(first)) {
1219
1496
  devWarn(
1220
1497
  `tagFactory: lone string "${first}" looks like a class list but is being rendered as TEXT. For a class, use ${tag}({ class: "${first}" }) \u2014 or ${tag}("${first}", children) to set the class AND add children.`
1221
1498
  );
@@ -1236,7 +1513,7 @@ var tagFactory = (tag, ns) => {
1236
1513
  if (pClass != null) applyClass(el, pClass);
1237
1514
  const pId = props.id;
1238
1515
  if (pId != null) {
1239
- if (_isDev7 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1516
+ if (_isDev6 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1240
1517
  devWarn(
1241
1518
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
1242
1519
  );
@@ -1251,7 +1528,7 @@ var tagFactory = (tag, ns) => {
1251
1528
  const handler = pOn[ev];
1252
1529
  if (typeof handler === "function") {
1253
1530
  el.addEventListener(ev, handler);
1254
- } else if (_isDev7) {
1531
+ } else if (_isDev6) {
1255
1532
  devWarn(
1256
1533
  `tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
1257
1534
  );
@@ -1279,16 +1556,8 @@ var tagFactory = (tag, ns) => {
1279
1556
  if (isEventHandlerAttr(key)) continue;
1280
1557
  if (typeof value === "function") {
1281
1558
  registerDisposer(el, bindAttribute(el, key, value));
1282
- } else if (typeof value === "boolean") {
1283
- if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
1284
- setProp2(el, key, value);
1285
- } else if (value) {
1286
- el.setAttribute(key, "");
1287
- } else {
1288
- el.removeAttribute(key);
1289
- }
1290
1559
  } else {
1291
- el.setAttribute(key, sanitizeAttributeString(key, String(value)));
1560
+ setSafeAttribute(el, key, value, { syncValueProperty: false, label: "tagFactory" });
1292
1561
  }
1293
1562
  }
1294
1563
  }
@@ -1373,7 +1642,7 @@ var param = tagFactory("param");
1373
1642
  var picture = tagFactory("picture");
1374
1643
  var portal = tagFactory("portal");
1375
1644
  var source = tagFactory("source");
1376
- var svg = tagFactory("svg", SVG_NS);
1645
+ var svg = tagFactory("svg", SVG_NS2);
1377
1646
  var math = tagFactory("math");
1378
1647
  var canvas = tagFactory("canvas");
1379
1648
  var noscript = tagFactory("noscript");
@@ -1414,26 +1683,26 @@ var base = tagFactory("base");
1414
1683
  var link = tagFactory("link");
1415
1684
  var meta = tagFactory("meta");
1416
1685
  var style = tagFactory("style");
1417
- var circle = tagFactory("circle", SVG_NS);
1418
- var ellipse = tagFactory("ellipse", SVG_NS);
1419
- var g = tagFactory("g", SVG_NS);
1420
- var line = tagFactory("line", SVG_NS);
1421
- var path = tagFactory("path", SVG_NS);
1422
- var polygon = tagFactory("polygon", SVG_NS);
1423
- var polyline = tagFactory("polyline", SVG_NS);
1424
- var rect = tagFactory("rect", SVG_NS);
1425
- var text = tagFactory("text", SVG_NS);
1426
- var tspan = tagFactory("tspan", SVG_NS);
1427
- var defs = tagFactory("defs", SVG_NS);
1428
- var clipPath = tagFactory("clipPath", SVG_NS);
1429
- var mask = tagFactory("mask", SVG_NS);
1430
- var pattern = tagFactory("pattern", SVG_NS);
1431
- var linearGradient = tagFactory("linearGradient", SVG_NS);
1432
- var radialGradient = tagFactory("radialGradient", SVG_NS);
1433
- var stop = tagFactory("stop", SVG_NS);
1434
- var use = tagFactory("use", SVG_NS);
1435
- var symbol = tagFactory("symbol", SVG_NS);
1436
- var marker = tagFactory("marker", SVG_NS);
1686
+ var circle = tagFactory("circle", SVG_NS2);
1687
+ var ellipse = tagFactory("ellipse", SVG_NS2);
1688
+ var g = tagFactory("g", SVG_NS2);
1689
+ var line = tagFactory("line", SVG_NS2);
1690
+ var path = tagFactory("path", SVG_NS2);
1691
+ var polygon = tagFactory("polygon", SVG_NS2);
1692
+ var polyline = tagFactory("polyline", SVG_NS2);
1693
+ var rect = tagFactory("rect", SVG_NS2);
1694
+ var text = tagFactory("text", SVG_NS2);
1695
+ var tspan = tagFactory("tspan", SVG_NS2);
1696
+ var defs = tagFactory("defs", SVG_NS2);
1697
+ var clipPath = tagFactory("clipPath", SVG_NS2);
1698
+ var mask = tagFactory("mask", SVG_NS2);
1699
+ var pattern = tagFactory("pattern", SVG_NS2);
1700
+ var linearGradient = tagFactory("linearGradient", SVG_NS2);
1701
+ var radialGradient = tagFactory("radialGradient", SVG_NS2);
1702
+ var stop = tagFactory("stop", SVG_NS2);
1703
+ var use = tagFactory("use", SVG_NS2);
1704
+ var symbol = tagFactory("symbol", SVG_NS2);
1705
+ var marker = tagFactory("marker", SVG_NS2);
1437
1706
  var center = tagFactory("center");
1438
1707
  var font = tagFactory("font");
1439
1708
  var marquee = tagFactory("marquee");
@@ -1442,18 +1711,14 @@ var customElement = (tagName) => tagFactory(tagName);
1442
1711
  // src/core/rendering/lazy.ts
1443
1712
  var PENDING_ERROR = "__sibuPendingError";
1444
1713
  function dispatchPropagate(node, error) {
1445
- const fire = () => {
1446
- try {
1447
- if (!node.parentNode) return false;
1448
- node.dispatchEvent(new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error } }));
1449
- return true;
1450
- } catch {
1451
- return false;
1452
- }
1714
+ const report = () => {
1715
+ if (!node.parentNode) return false;
1716
+ reportError(error, { phase: "render", name: "lazy", node });
1717
+ return true;
1453
1718
  };
1454
- if (node.parentNode && fire()) return;
1719
+ if (report()) return;
1455
1720
  queueMicrotask(() => {
1456
- if (fire()) return;
1721
+ if (report()) return;
1457
1722
  node[PENDING_ERROR] = error;
1458
1723
  });
1459
1724
  }
@@ -1478,12 +1743,14 @@ function lazy(importFn) {
1478
1743
  if (disposed) return;
1479
1744
  cached = mod.default;
1480
1745
  const rendered = cached();
1481
- container.replaceChildren(rendered);
1746
+ replaceChildrenSafely(container, rendered);
1482
1747
  }).catch((err) => {
1483
1748
  if (disposed) return;
1484
1749
  const errorObj = err instanceof Error ? err : new Error(String(err));
1485
- devWarn(`[SibuJS] lazy() failed to load component: ${errorObj.message}`);
1486
- container.replaceChildren(div({ class: "sibu-lazy-error" }, `Failed to load component: ${errorObj.message}`));
1750
+ replaceChildrenSafely(
1751
+ container,
1752
+ div({ class: "sibu-lazy-error" }, `Failed to load component: ${errorObj.message}`)
1753
+ );
1487
1754
  dispatchPropagate(container, errorObj);
1488
1755
  });
1489
1756
  container.appendChild(span("sibu-lazy-loading", "Loading..."));
@@ -1515,7 +1782,7 @@ function Suspense({ nodes, fallback }) {
1515
1782
  childEl = el;
1516
1783
  if (el.classList.contains("sibu-lazy")) {
1517
1784
  if (!el.querySelector(".sibu-lazy-loading")) {
1518
- container.replaceChildren(el);
1785
+ replaceChildrenSafely(container, el);
1519
1786
  return;
1520
1787
  }
1521
1788
  observer = new MutationObserver(() => {
@@ -1524,16 +1791,15 @@ function Suspense({ nodes, fallback }) {
1524
1791
  if (!loading) {
1525
1792
  observer?.disconnect();
1526
1793
  observer = null;
1527
- container.replaceChildren(el);
1794
+ replaceChildrenSafely(container, el);
1528
1795
  }
1529
1796
  });
1530
1797
  observer.observe(el, { childList: true, subtree: true });
1531
1798
  } else {
1532
- container.replaceChildren(el);
1799
+ replaceChildrenSafely(container, el);
1533
1800
  }
1534
1801
  } catch (err) {
1535
1802
  const errorObj = err instanceof Error ? err : new Error(String(err));
1536
- devWarn(`[SibuJS] Suspense nodes() threw: ${errorObj.message}`);
1537
1803
  dispatchPropagate(container, errorObj);
1538
1804
  }
1539
1805
  });
@@ -1545,7 +1811,8 @@ function safeCall(cb, hookName) {
1545
1811
  try {
1546
1812
  return cb();
1547
1813
  } catch (err) {
1548
- devWarn(`${hookName}: callback threw: ${err instanceof Error ? err.message : String(err)}`);
1814
+ const phase = hookName.toLowerCase().includes("unmount") ? "cleanup" : "effect";
1815
+ reportError(err, { phase, name: hookName });
1549
1816
  return void 0;
1550
1817
  }
1551
1818
  }
@@ -1779,20 +2046,35 @@ function detectSSRShared() {
1779
2046
  const getBuiltin = process.getBuiltinModule;
1780
2047
  if (typeof getBuiltin === "function") {
1781
2048
  mod = getBuiltin("node:async_hooks");
1782
- } else {
1783
- const req = Function("return typeof require==='function'?require:null")();
1784
- if (req) mod = req("node:async_hooks");
2049
+ } else if (typeof require === "function") {
2050
+ mod = require("async_hooks");
1785
2051
  }
1786
2052
  if (mod) detected = new mod.AsyncLocalStorage();
1787
2053
  }
1788
2054
  } catch {
1789
2055
  detected = null;
1790
2056
  }
1791
- return { als: detected, fallbackStore: { ssr: false, suspenseIdCounter: 0 } };
2057
+ return {
2058
+ als: detected,
2059
+ fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
2060
+ fallbackDepth: 0
2061
+ };
1792
2062
  }
1793
2063
  var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
1794
2064
  var als = _shared.als;
1795
2065
  var fallbackStore = _shared.fallbackStore;
2066
+ _shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
2067
+ var _alsWarned = false;
2068
+ function warnMissingAsyncLocalStorage() {
2069
+ if (_alsWarned || als) return;
2070
+ const isNode = typeof process !== "undefined" && !!process.versions?.node;
2071
+ if (!isNode) return;
2072
+ _alsWarned = true;
2073
+ const version = process.versions.node;
2074
+ devWarn(
2075
+ `SSR request isolation is UNAVAILABLE on this runtime (Node ${version}). AsyncLocalStorage could not be loaded, so concurrent requests share one SSR store: request state and the query cache can bleed between them. Node >= 22.3 is required for isolated SSR under ESM; the CommonJS build works on older versions. A fully synchronous render is unaffected.`
2076
+ );
2077
+ }
1796
2078
  function getSSRStore() {
1797
2079
  if (als) {
1798
2080
  const s2 = als.getStore();
@@ -1821,19 +2103,25 @@ function disableSSR() {
1821
2103
  getSSRStore().ssr = false;
1822
2104
  }
1823
2105
  function runInSSRContext(fn) {
1824
- const store2 = { ssr: true, suspenseIdCounter: 0 };
2106
+ const store2 = { ssr: true, suspenseIdCounter: 0, locale: void 0 };
1825
2107
  if (als) {
1826
2108
  return als.run(store2, fn);
1827
2109
  }
2110
+ warnMissingAsyncLocalStorage();
1828
2111
  const prevSSR = fallbackStore.ssr;
1829
2112
  const prevCounter = fallbackStore.suspenseIdCounter;
2113
+ const prevLocale = fallbackStore.locale;
1830
2114
  fallbackStore.ssr = true;
1831
2115
  fallbackStore.suspenseIdCounter = 0;
2116
+ fallbackStore.locale = void 0;
2117
+ _shared.fallbackDepth++;
1832
2118
  try {
1833
2119
  return fn();
1834
2120
  } finally {
2121
+ _shared.fallbackDepth--;
1835
2122
  fallbackStore.ssr = prevSSR;
1836
2123
  fallbackStore.suspenseIdCounter = prevCounter;
2124
+ fallbackStore.locale = prevLocale;
1837
2125
  }
1838
2126
  }
1839
2127
  function withSSR(fn) {
@@ -1874,7 +2162,7 @@ function flushUserCleanups(ctx) {
1874
2162
  try {
1875
2163
  list[i2]();
1876
2164
  } catch (err) {
1877
- if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
2165
+ reportError(err, { phase: "cleanup" });
1878
2166
  }
1879
2167
  }
1880
2168
  }
@@ -1887,11 +2175,12 @@ function drainReruns(ctx) {
1887
2175
  } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
1888
2176
  if (ctx.rerunPending) {
1889
2177
  ctx.rerunPending = false;
1890
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1891
- console.error(
1892
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1893
- );
1894
- }
2178
+ reportError(
2179
+ new Error(
2180
+ `effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
2181
+ ),
2182
+ { phase: "effect" }
2183
+ );
1895
2184
  }
1896
2185
  }
1897
2186
  function disposeEffect(ctx) {
@@ -1968,6 +2257,8 @@ function effect(effectFn, options) {
1968
2257
  sub2.depsTail = null;
1969
2258
  sub2._epoch = 0;
1970
2259
  sub2._structDirty = false;
2260
+ sub2._hasComputedDep = false;
2261
+ sub2._errorPhase = "effect";
1971
2262
  sub2._runEpoch = 0;
1972
2263
  sub2._runs = 0;
1973
2264
  ctx.subscriber = sub2;
@@ -2036,70 +2327,72 @@ var isBatching = API2.isBatching;
2036
2327
 
2037
2328
  // src/core/signals/signal.ts
2038
2329
  var _g2 = globalThis;
2039
- var _isDev8 = isDev();
2330
+ var _isDev7 = isDev();
2040
2331
  function signal(initial, options) {
2041
- const state = {
2332
+ const state2 = {
2042
2333
  value: initial,
2043
2334
  __v: 0,
2044
2335
  __sc: 0,
2045
2336
  subsHead: null,
2046
2337
  subsTail: null,
2047
2338
  __activeNode: null,
2048
- __name: void 0
2339
+ __name: void 0,
2340
+ _d: false,
2341
+ _validate: void 0
2049
2342
  };
2050
- const debugName = _isDev8 ? options?.name : void 0;
2343
+ const debugName = _isDev7 ? options?.name : void 0;
2051
2344
  const equalsFn = options?.equals;
2052
- if (debugName) state.__name = debugName;
2345
+ if (debugName) state2.__name = debugName;
2053
2346
  function get() {
2054
- recordDependency2(state);
2055
- return state.value;
2347
+ recordDependency2(state2);
2348
+ return state2.value;
2056
2349
  }
2057
- get.__signal = state;
2350
+ get.__signal = state2;
2058
2351
  if (debugName) get.__name = debugName;
2059
2352
  let set;
2060
2353
  if (equalsFn) {
2061
2354
  set = (next) => {
2062
- const prev = state.value;
2355
+ const prev = state2.value;
2063
2356
  const newValue = typeof next === "function" ? next(prev) : next;
2064
2357
  if (equalsFn(prev, newValue)) return;
2065
- state.value = newValue;
2066
- state.__v++;
2067
- if (_isDev8) {
2358
+ state2.value = newValue;
2359
+ state2.__v++;
2360
+ if (_isDev7) {
2068
2361
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2069
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
2362
+ if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
2070
2363
  }
2071
- if (!enqueueBatchedSignal(state)) {
2072
- notifySubscribers2(state);
2364
+ if (!enqueueBatchedSignal(state2)) {
2365
+ notifySubscribers2(state2);
2073
2366
  }
2074
2367
  };
2075
- } else if (_isDev8) {
2368
+ } else if (_isDev7) {
2076
2369
  set = (next) => {
2077
- const prev = state.value;
2370
+ const prev = state2.value;
2078
2371
  const newValue = typeof next === "function" ? next(prev) : next;
2079
2372
  if (Object.is(newValue, prev)) return;
2080
- state.value = newValue;
2081
- state.__v++;
2373
+ state2.value = newValue;
2374
+ state2.__v++;
2082
2375
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2083
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
2084
- if (!enqueueBatchedSignal(state)) {
2085
- notifySubscribers2(state);
2376
+ if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
2377
+ if (!enqueueBatchedSignal(state2)) {
2378
+ notifySubscribers2(state2);
2086
2379
  }
2087
2380
  };
2088
2381
  } else {
2089
2382
  set = (next) => {
2090
- const prev = state.value;
2383
+ const prev = state2.value;
2091
2384
  const newValue = typeof next === "function" ? next(prev) : next;
2092
2385
  if (Object.is(newValue, prev)) return;
2093
- state.value = newValue;
2094
- state.__v++;
2095
- if (!enqueueBatchedSignal(state)) {
2096
- notifySubscribers2(state);
2386
+ state2.value = newValue;
2387
+ state2.__v++;
2388
+ if (!enqueueBatchedSignal(state2)) {
2389
+ notifySubscribers2(state2);
2097
2390
  }
2098
2391
  };
2099
2392
  }
2100
- if (_isDev8) {
2393
+ if (_isDev7) {
2101
2394
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2102
- if (hook) hook.emit("signal:create", { signal: state, name: debugName, getter: get, initial });
2395
+ if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
2103
2396
  }
2104
2397
  return [get, set];
2105
2398
  }
@@ -2311,14 +2604,14 @@ function parseStack(stack) {
2311
2604
  }
2312
2605
  return frames;
2313
2606
  }
2314
- function normalizeError(err) {
2607
+ function normalizeError2(err) {
2315
2608
  if (err instanceof Error) {
2316
2609
  const code2 = err.code ?? err.name ?? "ERROR";
2317
2610
  const message = err.message || "Unknown error";
2318
2611
  const stack = err.stack ?? "";
2319
2612
  const frames = parseStack(stack);
2320
2613
  const rawCause = err.cause;
2321
- const cause = rawCause != null ? normalizeError(rawCause) : null;
2614
+ const cause = rawCause != null ? normalizeError2(rawCause) : null;
2322
2615
  return { code: code2, message, stack, frames, cause };
2323
2616
  }
2324
2617
  return {
@@ -2414,7 +2707,7 @@ function renderMetadata(meta2) {
2414
2707
  function ErrorDisplay(props) {
2415
2708
  injectStyles();
2416
2709
  const severity = props.severity ?? "error";
2417
- const normalized = normalizeError(props.error);
2710
+ const normalized = normalizeError2(props.error);
2418
2711
  const showDetails = props.alwaysShowDetails ?? isDev();
2419
2712
  const headline = props.title ?? normalized.message;
2420
2713
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
@@ -2664,6 +2957,7 @@ var errorBoundaryStyles = `
2664
2957
  background: #3a3a4e;
2665
2958
  }
2666
2959
  `;
2960
+ var RESET_KEY_THREW = /* @__PURE__ */ Symbol("sibujs.ErrorBoundary.resetKeyThrew");
2667
2961
  var stylesInjected = false;
2668
2962
  function injectStyles2() {
2669
2963
  if (!stylesInjected && typeof document !== "undefined") {
@@ -2672,29 +2966,6 @@ function injectStyles2() {
2672
2966
  stylesInjected = true;
2673
2967
  }
2674
2968
  }
2675
- var FALLBACK_CACHE_MAX = 50;
2676
- var fallbackCache = /* @__PURE__ */ new WeakMap();
2677
- function getMemoizedFallback(fallbackFn, error, retry) {
2678
- let cache2 = fallbackCache.get(fallbackFn);
2679
- if (!cache2) {
2680
- cache2 = /* @__PURE__ */ new Map();
2681
- fallbackCache.set(fallbackFn, cache2);
2682
- }
2683
- const key = error.message;
2684
- let factory = cache2.get(key);
2685
- if (factory) {
2686
- cache2.delete(key);
2687
- cache2.set(key, factory);
2688
- } else {
2689
- factory = () => fallbackFn(error, retry);
2690
- cache2.set(key, factory);
2691
- if (cache2.size > FALLBACK_CACHE_MAX) {
2692
- const oldestKey = cache2.keys().next().value;
2693
- if (oldestKey !== void 0) cache2.delete(oldestKey);
2694
- }
2695
- }
2696
- return factory();
2697
- }
2698
2969
  function ErrorBoundary(optionsOrChildren, maybeChildren) {
2699
2970
  const children = typeof optionsOrChildren === "function" ? optionsOrChildren : maybeChildren;
2700
2971
  const options = typeof optionsOrChildren === "function" ? {} : optionsOrChildren;
@@ -2702,43 +2973,61 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
2702
2973
  injectStyles2();
2703
2974
  const [error, setError] = signal(null);
2704
2975
  const retry = () => {
2705
- if (fallback) {
2706
- const cur = error();
2707
- const inner = fallbackCache.get(fallback);
2708
- if (cur && inner) inner.delete(cur.message);
2709
- }
2976
+ stopResetKeysWatcher();
2710
2977
  setError(null);
2711
2978
  };
2712
2979
  let resetKeysTeardown = null;
2713
- if (resetKeys && resetKeys.length > 0) {
2714
- let initialized = false;
2980
+ const stopResetKeysWatcher = () => {
2981
+ if (!resetKeysTeardown) return;
2982
+ const teardown = resetKeysTeardown;
2983
+ resetKeysTeardown = null;
2984
+ teardown();
2985
+ };
2986
+ const readResetKeyValues = (keys) => {
2987
+ const values = new Array(keys.length);
2988
+ for (let i2 = 0; i2 < keys.length; i2++) {
2989
+ try {
2990
+ values[i2] = keys[i2]();
2991
+ } catch (err) {
2992
+ reportError(err, { phase: "effect", name: "ErrorBoundary.resetKeys" });
2993
+ values[i2] = RESET_KEY_THREW;
2994
+ }
2995
+ }
2996
+ return values;
2997
+ };
2998
+ const startResetKeysWatcher = () => {
2999
+ const keys = resetKeys;
3000
+ if (!keys || keys.length === 0) return;
3001
+ stopResetKeysWatcher();
3002
+ let baseline = null;
2715
3003
  resetKeysTeardown = effect(() => {
2716
- for (const k of resetKeys) {
2717
- try {
2718
- k();
2719
- } catch (err) {
2720
- if (typeof console !== "undefined") {
2721
- console.warn("[SibuJS ErrorBoundary] resetKeys getter threw:", err);
3004
+ const current = readResetKeyValues(keys);
3005
+ if (baseline === null) {
3006
+ baseline = current;
3007
+ return;
3008
+ }
3009
+ let changed = current.length !== baseline.length;
3010
+ if (!changed) {
3011
+ for (let i2 = 0; i2 < current.length; i2++) {
3012
+ if (!Object.is(current[i2], baseline[i2])) {
3013
+ changed = true;
3014
+ break;
2722
3015
  }
2723
3016
  }
2724
3017
  }
2725
- if (!initialized) {
2726
- initialized = true;
2727
- return;
2728
- }
2729
- if (error() !== null) retry();
3018
+ baseline = current;
3019
+ if (changed) retry();
2730
3020
  });
2731
- }
3021
+ };
2732
3022
  const handleError = (e) => {
2733
- const errorObj = e instanceof Error ? e : new Error(String(e));
3023
+ const errorObj = normalizeError(e);
2734
3024
  setError(errorObj);
3025
+ startResetKeysWatcher();
2735
3026
  if (onError) {
2736
3027
  try {
2737
3028
  onError(errorObj);
2738
3029
  } catch (cbErr) {
2739
- if (typeof console !== "undefined") {
2740
- console.error("[SibuJS ErrorBoundary] onError callback threw:", cbErr);
2741
- }
3030
+ reportError(cbErr, { phase: "render", name: "ErrorBoundary onError callback" });
2742
3031
  }
2743
3032
  }
2744
3033
  return errorObj;
@@ -2749,18 +3038,14 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
2749
3038
  const tryRenderFallback = (err) => {
2750
3039
  const fn = fallback || defaultFallback;
2751
3040
  try {
2752
- return getMemoizedFallback(fn, err, retry);
3041
+ return fn(err, retry);
2753
3042
  } catch (fallbackError) {
2754
- const propagateError = fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError));
2755
3043
  queueMicrotask(() => {
2756
- if (container.parentNode) {
2757
- container.dispatchEvent(
2758
- new CustomEvent("sibu:error-propagate", {
2759
- bubbles: true,
2760
- detail: { error: propagateError }
2761
- })
2762
- );
2763
- }
3044
+ reportError(fallbackError, {
3045
+ phase: "render",
3046
+ name: "ErrorBoundary fallback",
3047
+ node: container.parentNode ? container : void 0
3048
+ });
2764
3049
  });
2765
3050
  return document.createComment("error-boundary-failed");
2766
3051
  }
@@ -2777,11 +3062,20 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
2777
3062
  if (result && typeof result.then === "function") {
2778
3063
  const asyncContainer = div({ class: "sibu-error-async" });
2779
3064
  asyncContainer.appendChild(span({ class: "sibu-lazy-loading", nodes: "Loading..." }));
3065
+ let asyncDisposed = false;
3066
+ registerDisposer(asyncContainer, () => {
3067
+ asyncDisposed = true;
3068
+ });
2780
3069
  result.then((el) => {
2781
- asyncContainer.replaceChildren(el);
3070
+ if (asyncDisposed) {
3071
+ dispose(el);
3072
+ return;
3073
+ }
3074
+ replaceChildrenSafely(asyncContainer, el);
2782
3075
  }).catch((e) => {
3076
+ if (asyncDisposed) return;
2783
3077
  const err = handleError(e);
2784
- asyncContainer.replaceChildren(tryRenderFallback(err));
3078
+ replaceChildrenSafely(asyncContainer, tryRenderFallback(err));
2785
3079
  });
2786
3080
  return asyncContainer;
2787
3081
  }
@@ -2794,12 +3088,12 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
2794
3088
  });
2795
3089
  const propagateListener = (e) => {
2796
3090
  if (error()) return;
2797
- e.stopPropagation();
2798
3091
  const customEvent = e;
2799
3092
  const propagatedError = customEvent.detail?.error;
2800
- if (propagatedError) {
2801
- handleError(propagatedError);
2802
- }
3093
+ if (!propagatedError) return;
3094
+ e.preventDefault();
3095
+ e.stopPropagation();
3096
+ handleError(propagatedError);
2803
3097
  };
2804
3098
  container.addEventListener("sibu:error-propagate", propagateListener);
2805
3099
  onMount(() => {
@@ -2827,7 +3121,7 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
2827
3121
  return void 0;
2828
3122
  }, container);
2829
3123
  registerDisposer(container, () => {
2830
- if (resetKeysTeardown) resetKeysTeardown();
3124
+ stopResetKeysWatcher();
2831
3125
  container.removeEventListener("sibu:error-propagate", propagateListener);
2832
3126
  });
2833
3127
  return container;
@@ -3077,10 +3371,18 @@ function setGlobalErrorHandler(handler) {
3077
3371
  }
3078
3372
 
3079
3373
  // src/core/rendering/context.ts
3374
+ var _isDev8 = isDev();
3080
3375
  function context(defaultValue) {
3081
3376
  const [getValue, setValue] = signal(defaultValue);
3377
+ const warnIfSSR = (method) => {
3378
+ if (!_isDev8 || !isSSR()) return;
3379
+ devWarn(
3380
+ `context.${method}() called during SSR. SibuJS context() is application-global \u2014 it is NOT isolated per request, so a concurrent request can observe this value. Do not use context() for request-specific SSR data; pass it explicitly or use request-scoped storage instead.`
3381
+ );
3382
+ };
3082
3383
  const ctx = {
3083
3384
  provide(value) {
3385
+ warnIfSSR("provide");
3084
3386
  const previous = getValue();
3085
3387
  setValue(value);
3086
3388
  return () => setValue(previous);
@@ -3092,13 +3394,21 @@ function context(defaultValue) {
3092
3394
  return getValue();
3093
3395
  },
3094
3396
  set(value) {
3397
+ warnIfSSR("set");
3095
3398
  setValue(value);
3096
3399
  },
3097
3400
  withContext(value, fn) {
3401
+ warnIfSSR("withContext");
3098
3402
  const previous = getValue();
3099
3403
  setValue(value);
3100
3404
  try {
3101
- return fn();
3405
+ const result = fn();
3406
+ if (_isDev8 && result !== null && typeof result?.then === "function") {
3407
+ devWarn(
3408
+ "context.withContext() received an async callback. Scoping is synchronous only \u2014 the previous value is restored as soon as the callback returns its promise, so anything after an `await` runs outside the scope."
3409
+ );
3410
+ }
3411
+ return result;
3102
3412
  } finally {
3103
3413
  setValue(previous);
3104
3414
  }
@@ -3219,10 +3529,7 @@ function DynamicComponent(is) {
3219
3529
  } else {
3220
3530
  el = resolveComponent(target);
3221
3531
  }
3222
- for (const child of Array.from(container.childNodes)) {
3223
- dispose(child);
3224
- }
3225
- container.replaceChildren(el);
3532
+ replaceChildrenSafely(container, el);
3226
3533
  }
3227
3534
  const untrack = track2(render);
3228
3535
  registerDisposer(container, untrack);
@@ -3283,6 +3590,10 @@ function each(getArray, render, options) {
3283
3590
  let oldLen = 0;
3284
3591
  let nodeMap = /* @__PURE__ */ new Map();
3285
3592
  let workMap = /* @__PURE__ */ new Map();
3593
+ const pendingRows = [];
3594
+ const pendingItems = [];
3595
+ const pendingIndices = [];
3596
+ let pendingCount = 0;
3286
3597
  let newNodes = [];
3287
3598
  let newKeysBuf = [];
3288
3599
  let isStableBuf = new Uint8Array(0);
@@ -3292,8 +3603,10 @@ function each(getArray, render, options) {
3292
3603
  const keyIndexMap = /* @__PURE__ */ new Map();
3293
3604
  let initialized = false;
3294
3605
  let sentinelInserted = false;
3606
+ let rangeDisposed = false;
3295
3607
  const keyFn = options.key;
3296
3608
  const update = () => {
3609
+ if (rangeDisposed) return;
3297
3610
  const arr = getArray();
3298
3611
  const newLen = arr.length;
3299
3612
  const parent = anchor.parentNode;
@@ -3322,49 +3635,44 @@ function each(getArray, render, options) {
3322
3635
  }
3323
3636
  keyIndexMap.set(newKeys[i2], i2);
3324
3637
  }
3638
+ pendingCount = 0;
3325
3639
  for (let i2 = 0; i2 < newLen; i2++) {
3326
3640
  const key = newKeys[i2];
3327
3641
  const existing = nodeMap.get(key);
3328
- let node;
3642
+ let row;
3329
3643
  if (existing !== void 0) {
3330
- node = existing;
3644
+ row = existing;
3645
+ pendingRows[pendingCount] = row;
3646
+ pendingItems[pendingCount] = arr[i2];
3647
+ pendingIndices[pendingCount] = i2;
3648
+ pendingCount++;
3331
3649
  } else {
3332
- const itemKey = key;
3333
- const itemGetter = () => untracked2(() => getArray()[keyIndexMap.get(itemKey)]);
3334
- const indexGetter = () => keyIndexMap.get(itemKey);
3650
+ const [itemGetter, setItem] = signal(arr[i2]);
3651
+ const [indexGetter, setIndex] = signal(i2);
3652
+ let node;
3335
3653
  try {
3336
3654
  node = resolveNodeChild(render(itemGetter, indexGetter));
3337
3655
  } catch (err) {
3338
- if (_isDev9) {
3339
- devWarn(
3340
- `each: render threw for item at index ${i2} (key="${newKeys[i2]}"): ${err instanceof Error ? err.message : String(err)}`
3341
- );
3342
- }
3656
+ const key2 = newKeys[i2];
3343
3657
  node = document.createComment(`each:error:${i2}`);
3344
- const errorObj = err instanceof Error ? err : new Error(String(err));
3345
3658
  queueMicrotask(() => {
3346
- try {
3347
- const target = anchor.parentNode;
3348
- if (target?.dispatchEvent) {
3349
- target.dispatchEvent(
3350
- new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
3351
- );
3352
- } else if (_isDev9) {
3353
- devWarn(`each: error not surfaced \u2014 anchor detached: ${errorObj.message}`);
3354
- }
3355
- } catch {
3356
- }
3659
+ reportError(err, {
3660
+ phase: "render",
3661
+ name: `each row (key="${String(key2)}")`,
3662
+ node: anchor.parentNode ? anchor : void 0
3663
+ });
3357
3664
  });
3358
3665
  }
3666
+ row = { node, item: itemGetter, setItem, index: indexGetter, setIndex };
3359
3667
  }
3360
- workMap.set(key, node);
3361
- newNodes[i2] = node;
3668
+ workMap.set(key, row);
3669
+ newNodes[i2] = row.node;
3362
3670
  }
3363
- for (const [key, node] of nodeMap) {
3671
+ for (const [key, row] of nodeMap) {
3364
3672
  if (!workMap.has(key)) {
3365
- dispose(node);
3366
- if (node.parentNode) {
3367
- parent.removeChild(node);
3673
+ dispose(row.node);
3674
+ if (row.node.parentNode) {
3675
+ parent.removeChild(row.node);
3368
3676
  }
3369
3677
  }
3370
3678
  }
@@ -3373,6 +3681,8 @@ function each(getArray, render, options) {
3373
3681
  const tmp2 = nodeMap;
3374
3682
  nodeMap = workMap;
3375
3683
  workMap = tmp2;
3684
+ initialized = true;
3685
+ pendingCount = 0;
3376
3686
  return;
3377
3687
  }
3378
3688
  oldKeyIndex.clear();
@@ -3422,12 +3732,47 @@ function each(getArray, render, options) {
3422
3732
  nodeMap = workMap;
3423
3733
  workMap = tmp;
3424
3734
  initialized = true;
3735
+ flushRowCells();
3425
3736
  };
3737
+ function flushRowCells() {
3738
+ if (pendingCount === 0) return;
3739
+ const count = pendingCount;
3740
+ pendingCount = 0;
3741
+ batch(() => {
3742
+ for (let i2 = 0; i2 < count; i2++) {
3743
+ const row = pendingRows[i2];
3744
+ row.setItem(pendingItems[i2]);
3745
+ row.setIndex(pendingIndices[i2]);
3746
+ pendingRows[i2] = void 0;
3747
+ pendingItems[i2] = void 0;
3748
+ }
3749
+ });
3750
+ }
3426
3751
  const untrack = track2(update);
3427
- registerDisposer(anchor, untrack);
3752
+ const disposeRange = () => {
3753
+ if (rangeDisposed) return;
3754
+ rangeDisposed = true;
3755
+ untrack();
3756
+ for (const row of nodeMap.values()) {
3757
+ dispose(row.node);
3758
+ row.node.parentNode?.removeChild(row.node);
3759
+ }
3760
+ if (end.parentNode) end.parentNode.removeChild(end);
3761
+ sentinelInserted = false;
3762
+ nodeMap.clear();
3763
+ workMap.clear();
3764
+ keyIndexMap.clear();
3765
+ oldKeyIndex.clear();
3766
+ oldLen = 0;
3767
+ pendingRows.length = 0;
3768
+ pendingItems.length = 0;
3769
+ pendingIndices.length = 0;
3770
+ pendingCount = 0;
3771
+ };
3772
+ registerDisposer(anchor, disposeRange);
3428
3773
  if (!initialized) {
3429
3774
  queueMicrotask(() => {
3430
- if (!initialized && anchor.parentNode) {
3775
+ if (!initialized && !rangeDisposed && anchor.parentNode) {
3431
3776
  update();
3432
3777
  }
3433
3778
  });
@@ -3717,7 +4062,7 @@ function parseTemplate(strings) {
3717
4062
  return parseChildren();
3718
4063
  }
3719
4064
  function executeElement(tmpl, values) {
3720
- const el = tmpl.svg ? document.createElementNS(SVG_NS, tmpl.tag) : document.createElement(tmpl.tag);
4065
+ const el = tmpl.svg ? document.createElementNS(SVG_NS2, tmpl.tag) : document.createElement(tmpl.tag);
3721
4066
  for (let i2 = 0; i2 < tmpl.attrs.length; i2++) {
3722
4067
  const attr = tmpl.attrs[i2];
3723
4068
  switch (attr.t) {
@@ -3726,20 +4071,11 @@ function executeElement(tmpl, values) {
3726
4071
  break;
3727
4072
  case 1: {
3728
4073
  const name = attr.name;
3729
- const lname = name.toLowerCase();
3730
- if (isEventHandlerAttr(name)) break;
3731
4074
  const val = values[attr.idx];
3732
4075
  if (typeof val === "function") {
3733
4076
  registerDisposer(el, bindAttribute(el, name, val));
3734
- } else if (val != null) {
3735
- const str = String(val);
3736
- if (lname === "srcset") {
3737
- el.setAttribute(name, sanitizeSrcset(str));
3738
- } else if (isUrlAttribute(lname)) {
3739
- el.setAttribute(name, sanitizeUrl(str));
3740
- } else {
3741
- el.setAttribute(name, str);
3742
- }
4077
+ } else {
4078
+ setSafeAttribute(el, name, val, { syncValueProperty: false, label: "html" });
3743
4079
  }
3744
4080
  break;
3745
4081
  }
@@ -3749,14 +4085,7 @@ function executeElement(tmpl, values) {
3749
4085
  const ev = values[attr.exprs[j]];
3750
4086
  val += (ev == null ? "" : String(ev)) + attr.statics[j + 1];
3751
4087
  }
3752
- const lname2 = attr.name.toLowerCase();
3753
- if (lname2 === "srcset") {
3754
- el.setAttribute(attr.name, sanitizeSrcset(val));
3755
- } else if (isUrlAttribute(lname2)) {
3756
- el.setAttribute(attr.name, sanitizeUrl(val));
3757
- } else {
3758
- el.setAttribute(attr.name, val);
3759
- }
4088
+ setSafeAttribute(el, attr.name, val, { syncValueProperty: false, label: "html" });
3760
4089
  break;
3761
4090
  }
3762
4091
  case 3: {
@@ -3982,20 +4311,12 @@ function Portal(nodes, target) {
3982
4311
  portalContent = nodes();
3983
4312
  container.appendChild(portalContent);
3984
4313
  } catch (err) {
3985
- if (typeof console !== "undefined") {
3986
- console.error("[Portal] Render error:", err);
3987
- }
3988
- const errorObj = err instanceof Error ? err : new Error(String(err));
3989
4314
  queueMicrotask(() => {
3990
- try {
3991
- const target2 = anchor.parentNode;
3992
- if (target2?.dispatchEvent) {
3993
- target2.dispatchEvent(
3994
- new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
3995
- );
3996
- }
3997
- } catch {
3998
- }
4315
+ reportError(err, {
4316
+ phase: "render",
4317
+ name: "Portal",
4318
+ node: anchor.parentNode ? anchor : void 0
4319
+ });
3999
4320
  });
4000
4321
  }
4001
4322
  });
@@ -4092,9 +4413,10 @@ function array(initial = []) {
4092
4413
  function reactiveArray(initial = []) {
4093
4414
  let data2 = [...initial];
4094
4415
  let snapshot = null;
4095
- const signal2 = {};
4416
+ const signal2 = { __v: 0 };
4096
4417
  function notify() {
4097
4418
  snapshot = null;
4419
+ signal2.__v++;
4098
4420
  if (!enqueueBatchedSignal(signal2)) {
4099
4421
  notifySubscribers2(signal2);
4100
4422
  }
@@ -4214,17 +4536,34 @@ function asyncDerived(factory, initial) {
4214
4536
  const [error, setError] = signal(null);
4215
4537
  const [tick, setTick] = signal(0);
4216
4538
  let runId = 0;
4217
- effect(() => {
4539
+ let disposed = false;
4540
+ let controller = null;
4541
+ const abortInFlight = () => {
4542
+ if (controller === null) return;
4543
+ const c = controller;
4544
+ controller = null;
4545
+ try {
4546
+ c.abort();
4547
+ } catch {
4548
+ }
4549
+ };
4550
+ const stopEffect = effect(() => {
4218
4551
  tick();
4552
+ if (disposed) return;
4553
+ abortInFlight();
4219
4554
  const currentRun = ++runId;
4555
+ const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
4556
+ controller = ac;
4220
4557
  batch(() => {
4221
4558
  setLoading(true);
4222
4559
  setError(null);
4223
4560
  });
4561
+ const stale = () => disposed || currentRun !== runId;
4224
4562
  let promise;
4225
4563
  try {
4226
- promise = factory();
4564
+ promise = factory({ signal: ac ? ac.signal : void 0 });
4227
4565
  } catch (err) {
4566
+ if (stale()) return;
4228
4567
  batch(() => {
4229
4568
  setError(err);
4230
4569
  setLoading(false);
@@ -4233,14 +4572,16 @@ function asyncDerived(factory, initial) {
4233
4572
  }
4234
4573
  promise.then(
4235
4574
  (result) => {
4236
- if (currentRun !== runId) return;
4575
+ if (stale()) return;
4576
+ controller = null;
4237
4577
  batch(() => {
4238
4578
  setValue(result);
4239
4579
  setLoading(false);
4240
4580
  });
4241
4581
  },
4242
4582
  (err) => {
4243
- if (currentRun !== runId) return;
4583
+ if (stale()) return;
4584
+ controller = null;
4244
4585
  batch(() => {
4245
4586
  setError(err);
4246
4587
  setLoading(false);
@@ -4252,7 +4593,16 @@ function asyncDerived(factory, initial) {
4252
4593
  value,
4253
4594
  loading,
4254
4595
  error,
4255
- refresh: () => setTick((n) => n + 1)
4596
+ refresh: () => {
4597
+ if (disposed) return;
4598
+ setTick((n) => n + 1);
4599
+ },
4600
+ dispose: () => {
4601
+ if (disposed) return;
4602
+ disposed = true;
4603
+ stopEffect();
4604
+ abortInFlight();
4605
+ }
4256
4606
  };
4257
4607
  }
4258
4608
 
@@ -4372,6 +4722,21 @@ function derived(getter, options) {
4372
4722
  }, markDirty);
4373
4723
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
4374
4724
  let evaluating = false;
4725
+ const validate = () => {
4726
+ if (!cs._d) return;
4727
+ const oldValue = cs._v;
4728
+ evaluating = true;
4729
+ try {
4730
+ retrack2(recompute, markDirty);
4731
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
4732
+ } finally {
4733
+ evaluating = false;
4734
+ }
4735
+ if (hook && !Object.is(oldValue, cs._v)) {
4736
+ hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
4737
+ }
4738
+ };
4739
+ cs._validate = validate;
4375
4740
  function computedGetter() {
4376
4741
  if (evaluating) {
4377
4742
  throw new Error(
@@ -4379,32 +4744,11 @@ function derived(getter, options) {
4379
4744
  );
4380
4745
  }
4381
4746
  if (isTrackingSuspended2()) {
4382
- if (cs._d) {
4383
- const prev = cs._v;
4384
- evaluating = true;
4385
- try {
4386
- retrack2(recompute, markDirty);
4387
- if (!Object.is(prev, cs._v)) cs.__v++;
4388
- } finally {
4389
- evaluating = false;
4390
- }
4391
- }
4747
+ if (cs._d) validate();
4392
4748
  return cs._v;
4393
4749
  }
4750
+ if (cs._d) validate();
4394
4751
  recordDependency2(cs);
4395
- if (cs._d) {
4396
- const oldValue = cs._v;
4397
- evaluating = true;
4398
- try {
4399
- retrack2(recompute, markDirty);
4400
- if (!Object.is(oldValue, cs._v)) cs.__v++;
4401
- } finally {
4402
- evaluating = false;
4403
- }
4404
- if (hook && oldValue !== cs._v) {
4405
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
4406
- }
4407
- }
4408
4752
  return cs._v;
4409
4753
  }
4410
4754
  if (debugName) {
@@ -4594,6 +4938,28 @@ function strictEffect(fn) {
4594
4938
  }
4595
4939
 
4596
4940
  // src/platform/enhance.ts
4941
+ var ENHANCED_ATTR = "data-sibu-enhanced";
4942
+ var enhancementOwner = /* @__PURE__ */ new WeakMap();
4943
+ function drainTeardowns(teardowns, label2) {
4944
+ let executed = 0;
4945
+ while (teardowns.length > 0) {
4946
+ const batch2 = teardowns.splice(0);
4947
+ for (let i2 = 0; i2 < batch2.length; i2++) {
4948
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
4949
+ const remaining = batch2.length - i2 + teardowns.length;
4950
+ teardowns.length = 0;
4951
+ reportDrainRunaway(label2, executed, remaining);
4952
+ return;
4953
+ }
4954
+ executed++;
4955
+ try {
4956
+ batch2[i2]();
4957
+ } catch (err) {
4958
+ if (typeof console !== "undefined") console.error(`[SibuJS ${label2}] teardown error:`, err);
4959
+ }
4960
+ }
4961
+ }
4962
+ }
4597
4963
  function resolveTarget(root, target) {
4598
4964
  if (target == null) return root;
4599
4965
  if (typeof target !== "string") return target;
@@ -4636,7 +5002,7 @@ function enhance(target, setup) {
4636
5002
  return () => {
4637
5003
  };
4638
5004
  }
4639
- if (root.getAttribute("data-sibu-enhanced") === "true") {
5005
+ if (root.getAttribute(ENHANCED_ATTR) === "true") {
4640
5006
  if (isDev() && typeof console !== "undefined") {
4641
5007
  console.warn("[SibuJS enhance] element is already enhanced; ignoring re-enhance.", root);
4642
5008
  }
@@ -4644,6 +5010,7 @@ function enhance(target, setup) {
4644
5010
  };
4645
5011
  }
4646
5012
  const teardowns = [];
5013
+ const owner = /* @__PURE__ */ Symbol("sibujs.enhance");
4647
5014
  let disposed = false;
4648
5015
  const bind = (target_, fn) => {
4649
5016
  const el = resolveTarget(root, target_);
@@ -4690,11 +5057,7 @@ function enhance(target, setup) {
4690
5057
  effect(() => {
4691
5058
  const v = value();
4692
5059
  const next = v == null ? null : String(v);
4693
- if (next === null) {
4694
- if (el.hasAttribute(name)) el.removeAttribute(name);
4695
- } else if (el.getAttribute(name) !== next) {
4696
- el.setAttribute(name, next);
4697
- }
5060
+ setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()" });
4698
5061
  })
4699
5062
  );
4700
5063
  });
@@ -4721,10 +5084,10 @@ function enhance(target, setup) {
4721
5084
  });
4722
5085
  });
4723
5086
  },
4724
- model: (t, state, options) => {
5087
+ model: (t, state2, options) => {
4725
5088
  bind(t, (el) => {
4726
5089
  const control = el;
4727
- const [get, set] = state;
5090
+ const [get, set] = state2;
4728
5091
  const evt = options?.event ?? (control instanceof HTMLSelectElement || control instanceof HTMLInputElement && (control.type === "checkbox" || control.type === "radio") ? "change" : "input");
4729
5092
  teardowns.push(
4730
5093
  effect(() => {
@@ -4740,29 +5103,50 @@ function enhance(target, setup) {
4740
5103
  teardowns.push(fn);
4741
5104
  }
4742
5105
  };
4743
- const extra = setup(ctx);
5106
+ let extra;
5107
+ try {
5108
+ extra = setup(ctx);
5109
+ } catch (err) {
5110
+ drainTeardowns(teardowns, "enhance");
5111
+ throw err;
5112
+ }
4744
5113
  if (typeof extra === "function") teardowns.push(extra);
4745
5114
  const dispose2 = () => {
4746
5115
  if (disposed) return;
4747
5116
  disposed = true;
4748
- for (const fn of teardowns.splice(0)) {
4749
- try {
4750
- fn();
4751
- } catch (err) {
4752
- if (typeof console !== "undefined") console.error("[SibuJS enhance] teardown error:", err);
4753
- }
5117
+ if (enhancementOwner.get(root) === owner) {
5118
+ enhancementOwner.delete(root);
5119
+ root.removeAttribute(ENHANCED_ATTR);
4754
5120
  }
5121
+ unregisterDisposer(root, dispose2);
5122
+ drainTeardowns(teardowns, "enhance");
4755
5123
  };
4756
5124
  registerDisposer(root, dispose2);
4757
- root.setAttribute("data-sibu-enhanced", "true");
5125
+ enhancementOwner.set(root, owner);
5126
+ root.setAttribute(ENHANCED_ATTR, "true");
4758
5127
  return dispose2;
4759
5128
  }
4760
5129
  function enhanceAll(selector, setup) {
4761
5130
  if (typeof document === "undefined") return () => {
4762
5131
  };
4763
- const disposers = Array.from(document.querySelectorAll(selector)).map((el) => enhance(el, setup));
5132
+ const disposers = [];
5133
+ try {
5134
+ for (const el of Array.from(document.querySelectorAll(selector))) {
5135
+ disposers.push(enhance(el, setup));
5136
+ }
5137
+ } catch (err) {
5138
+ for (let i2 = disposers.length - 1; i2 >= 0; i2--) {
5139
+ try {
5140
+ disposers[i2]();
5141
+ } catch (rollbackErr) {
5142
+ if (typeof console !== "undefined") console.error("[SibuJS enhanceAll] rollback error:", rollbackErr);
5143
+ }
5144
+ }
5145
+ disposers.length = 0;
5146
+ throw err;
5147
+ }
4764
5148
  return () => {
4765
- for (const d of disposers) d();
5149
+ for (const d of disposers.splice(0)) d();
4766
5150
  };
4767
5151
  }
4768
5152
 
@@ -4802,6 +5186,7 @@ function mountIslands(root = typeof document !== "undefined" ? document : null,
4802
5186
  };
4803
5187
  const cancels = [];
4804
5188
  const disposers = [];
5189
+ let torndown = false;
4805
5190
  for (const el of Array.from(root.querySelectorAll("[data-sibu-island]"))) {
4806
5191
  if (el.getAttribute("data-sibu-enhanced") === "true") continue;
4807
5192
  const name = el.getAttribute("data-sibu-island") ?? "";
@@ -4815,17 +5200,23 @@ function mountIslands(root = typeof document !== "undefined" ? document : null,
4815
5200
  }
4816
5201
  let activated = false;
4817
5202
  const activate = () => {
4818
- if (activated) return;
5203
+ if (activated || torndown) return;
4819
5204
  activated = true;
4820
5205
  const reg = registry.get(name);
4821
5206
  if (!reg) return;
4822
5207
  resolveSetup(reg).then((setup) => {
5208
+ if (torndown) return;
4823
5209
  if (!setup) {
4824
5210
  if (typeof console !== "undefined") console.warn(`[SibuJS islands] "${name}" loader produced no setup.`);
4825
5211
  return;
4826
5212
  }
4827
5213
  try {
4828
- disposers.push(enhance(el, setup));
5214
+ const disposeIsland = enhance(el, setup);
5215
+ if (torndown) {
5216
+ disposeIsland();
5217
+ return;
5218
+ }
5219
+ disposers.push(disposeIsland);
4829
5220
  el.setAttribute("data-sibu-hydrated", "true");
4830
5221
  } catch (err) {
4831
5222
  if (typeof console !== "undefined") console.error(`[SibuJS islands] "${name}" failed to mount:`, err);
@@ -4838,6 +5229,7 @@ function mountIslands(root = typeof document !== "undefined" ? document : null,
4838
5229
  cancels.push(schedule(strategy, el, activate, options));
4839
5230
  }
4840
5231
  return () => {
5232
+ torndown = true;
4841
5233
  for (const c of cancels.splice(0)) c();
4842
5234
  for (const d of disposers.splice(0)) d();
4843
5235
  };
@@ -5013,6 +5405,7 @@ function nextTick() {
5013
5405
  Fragment,
5014
5406
  KeepAlive,
5015
5407
  Loading,
5408
+ MAX_DRAIN_TEARDOWNS,
5016
5409
  Portal,
5017
5410
  SVG_NS,
5018
5411
  Suspense,
@@ -5090,6 +5483,7 @@ function nextTick() {
5090
5483
  g,
5091
5484
  getAction,
5092
5485
  getRequestScopedCache,
5486
+ getRuntimeErrorHandler,
5093
5487
  getSSRStore,
5094
5488
  getSlot,
5095
5489
  h1,
@@ -5163,6 +5557,9 @@ function nextTick() {
5163
5557
  registerComponent,
5164
5558
  registerDisposer,
5165
5559
  registerIsland,
5560
+ replaceChildrenSafely,
5561
+ reportDrainRunaway,
5562
+ reportError,
5166
5563
  resolveComponent,
5167
5564
  retrack,
5168
5565
  rp,
@@ -5176,6 +5573,7 @@ function nextTick() {
5176
5573
  select,
5177
5574
  setGlobalErrorHandler,
5178
5575
  setMaxDrainIterations,
5576
+ setRuntimeErrorHandler,
5179
5577
  show,
5180
5578
  signal,
5181
5579
  slot,
@@ -5215,6 +5613,7 @@ function nextTick() {
5215
5613
  u,
5216
5614
  ul,
5217
5615
  unregisterComponent,
5616
+ unregisterDisposer,
5218
5617
  unregisterIsland,
5219
5618
  untracked,
5220
5619
  use,