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/build.cjs CHANGED
@@ -1063,8 +1063,9 @@ __export(index_exports, {
1063
1063
  Fragment: () => Fragment,
1064
1064
  KeepAlive: () => KeepAlive,
1065
1065
  Loading: () => Loading,
1066
+ MAX_DRAIN_TEARDOWNS: () => MAX_DRAIN_TEARDOWNS,
1066
1067
  Portal: () => Portal,
1067
- SVG_NS: () => SVG_NS,
1068
+ SVG_NS: () => SVG_NS2,
1068
1069
  Suspense: () => Suspense,
1069
1070
  __resetIdCounter: () => __resetIdCounter,
1070
1071
  a: () => a,
@@ -1140,6 +1141,7 @@ __export(index_exports, {
1140
1141
  g: () => g,
1141
1142
  getAction: () => getAction,
1142
1143
  getRequestScopedCache: () => getRequestScopedCache,
1144
+ getRuntimeErrorHandler: () => getRuntimeErrorHandler,
1143
1145
  getSSRStore: () => getSSRStore,
1144
1146
  getSlot: () => getSlot,
1145
1147
  h1: () => h1,
@@ -1213,6 +1215,9 @@ __export(index_exports, {
1213
1215
  registerComponent: () => registerComponent,
1214
1216
  registerDisposer: () => registerDisposer,
1215
1217
  registerIsland: () => registerIsland,
1218
+ replaceChildrenSafely: () => replaceChildrenSafely,
1219
+ reportDrainRunaway: () => reportDrainRunaway,
1220
+ reportError: () => reportError,
1216
1221
  resolveComponent: () => resolveComponent,
1217
1222
  retrack: () => retrack2,
1218
1223
  rp: () => rp,
@@ -1226,6 +1231,7 @@ __export(index_exports, {
1226
1231
  select: () => select,
1227
1232
  setGlobalErrorHandler: () => setGlobalErrorHandler,
1228
1233
  setMaxDrainIterations: () => setMaxDrainIterations2,
1234
+ setRuntimeErrorHandler: () => setRuntimeErrorHandler,
1229
1235
  show: () => show,
1230
1236
  signal: () => signal,
1231
1237
  slot: () => slot,
@@ -1265,6 +1271,7 @@ __export(index_exports, {
1265
1271
  u: () => u,
1266
1272
  ul: () => ul,
1267
1273
  unregisterComponent: () => unregisterComponent,
1274
+ unregisterDisposer: () => unregisterDisposer,
1268
1275
  unregisterIsland: () => unregisterIsland,
1269
1276
  untracked: () => untracked2,
1270
1277
  use: () => use,
@@ -1276,6 +1283,103 @@ __export(index_exports, {
1276
1283
  writable: () => writable
1277
1284
  });
1278
1285
 
1286
+ // src/core/errors.ts
1287
+ var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
1288
+ function state() {
1289
+ const g2 = globalThis;
1290
+ const existing = g2[ERROR_STATE_KEY];
1291
+ if (existing) return existing;
1292
+ const created = { handler: null, handlerDepth: 0 };
1293
+ g2[ERROR_STATE_KEY] = created;
1294
+ return created;
1295
+ }
1296
+ function setRuntimeErrorHandler(handler) {
1297
+ const s2 = state();
1298
+ const prev = s2.handler;
1299
+ s2.handler = handler;
1300
+ return prev;
1301
+ }
1302
+ function getRuntimeErrorHandler() {
1303
+ return state().handler;
1304
+ }
1305
+ function normalizeError(value) {
1306
+ if (value instanceof Error) return value;
1307
+ const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
1308
+ return new Error(message, { cause: value });
1309
+ }
1310
+ function safeStringify(value) {
1311
+ if (typeof value === "symbol") return value.toString();
1312
+ try {
1313
+ return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
1314
+ } catch {
1315
+ return Object.prototype.toString.call(value);
1316
+ }
1317
+ }
1318
+ function describe(context2) {
1319
+ return context2.name ? `${context2.phase} "${context2.name}"` : context2.phase;
1320
+ }
1321
+ var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
1322
+ function untrackedRun(fn) {
1323
+ const api = globalThis[REACTIVE_REGISTRY_KEY];
1324
+ return api?.untracked ? api.untracked(fn) : fn();
1325
+ }
1326
+ var PROPAGATE_EVENT = "sibu:error-propagate";
1327
+ function claimedByBoundary(error, context2) {
1328
+ const raw = context2.node;
1329
+ if (!raw || typeof CustomEvent === "undefined") return false;
1330
+ const target = resolveEventTarget(raw);
1331
+ if (!target) return false;
1332
+ try {
1333
+ const event = new CustomEvent(PROPAGATE_EVENT, {
1334
+ bubbles: true,
1335
+ cancelable: true,
1336
+ detail: { error, context: context2 }
1337
+ });
1338
+ return target.dispatchEvent(event) === false;
1339
+ } catch {
1340
+ return false;
1341
+ }
1342
+ }
1343
+ function resolveEventTarget(node) {
1344
+ const ELEMENT_NODE = 1;
1345
+ let current = node;
1346
+ while (current) {
1347
+ if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
1348
+ return current;
1349
+ }
1350
+ current = current.parentNode;
1351
+ }
1352
+ const self = node;
1353
+ return typeof self.dispatchEvent === "function" ? self : null;
1354
+ }
1355
+ function defaultReport(error, context2) {
1356
+ if (typeof console === "undefined") return;
1357
+ console.error(`[SibuJS] Uncaught error during ${describe(context2)}: ${error.message}`, error);
1358
+ }
1359
+ function reportError(error, context2) {
1360
+ const err = normalizeError(error);
1361
+ untrackedRun(() => {
1362
+ if (claimedByBoundary(err, context2)) return;
1363
+ const s2 = state();
1364
+ const handler = s2.handler;
1365
+ if (handler && s2.handlerDepth === 0) {
1366
+ s2.handlerDepth++;
1367
+ try {
1368
+ handler(error instanceof Error ? error : err, context2);
1369
+ return;
1370
+ } catch (handlerError) {
1371
+ if (typeof console !== "undefined") {
1372
+ const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
1373
+ console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
1374
+ }
1375
+ } finally {
1376
+ s2.handlerDepth--;
1377
+ }
1378
+ }
1379
+ defaultReport(err, context2);
1380
+ });
1381
+ }
1382
+
1279
1383
  // src/core/dev.ts
1280
1384
  function isDev() {
1281
1385
  return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
@@ -1300,6 +1404,14 @@ function devWarn(message) {
1300
1404
 
1301
1405
  // src/core/rendering/dispose.ts
1302
1406
  var elementDisposers = /* @__PURE__ */ new WeakMap();
1407
+ var MAX_DRAIN_TEARDOWNS = 1e4;
1408
+ function reportDrainRunaway(label2, executed, remaining) {
1409
+ if (typeof console === "undefined") return;
1410
+ console.error(
1411
+ `[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.`,
1412
+ { executed, remaining }
1413
+ );
1414
+ }
1303
1415
  var _isDev2 = isDev();
1304
1416
  var activeBindingCount = 0;
1305
1417
  function registerDisposer(node, teardown) {
@@ -1311,6 +1423,15 @@ function registerDisposer(node, teardown) {
1311
1423
  disposers.push(teardown);
1312
1424
  if (_isDev2) activeBindingCount++;
1313
1425
  }
1426
+ function unregisterDisposer(node, teardown) {
1427
+ const disposers = elementDisposers.get(node);
1428
+ if (!disposers) return;
1429
+ const index = disposers.indexOf(teardown);
1430
+ if (index === -1) return;
1431
+ disposers.splice(index, 1);
1432
+ if (_isDev2) activeBindingCount--;
1433
+ if (disposers.length === 0) elementDisposers.delete(node);
1434
+ }
1314
1435
  function dispose(node) {
1315
1436
  const stack = [node];
1316
1437
  const order = [];
@@ -1324,40 +1445,47 @@ function dispose(node) {
1324
1445
  }
1325
1446
  for (let i2 = order.length - 1; i2 >= 0; i2--) {
1326
1447
  const current = order[i2];
1327
- const disposers = elementDisposers.get(current);
1328
- if (disposers) {
1329
- const snapshot = disposers.slice();
1330
- elementDisposers.delete(current);
1331
- if (_isDev2) activeBindingCount -= snapshot.length;
1332
- for (const d of snapshot) {
1333
- try {
1334
- d();
1335
- } catch (err) {
1336
- if (_isDev2 && typeof console !== "undefined") {
1337
- console.warn("[SibuJS] Disposer threw during cleanup:", err);
1338
- }
1339
- }
1340
- }
1341
- let extraPasses = 0;
1342
- while (extraPasses++ < 8) {
1343
- const added = elementDisposers.get(current);
1344
- if (!added || added.length === 0) break;
1345
- const moreSnapshot = added.slice();
1448
+ if (elementDisposers.has(current)) {
1449
+ let executed = 0;
1450
+ let runaway = false;
1451
+ while (!runaway) {
1452
+ const pending = elementDisposers.get(current);
1453
+ if (!pending || pending.length === 0) break;
1454
+ const snapshot = pending.slice();
1346
1455
  elementDisposers.delete(current);
1347
- if (_isDev2) activeBindingCount -= moreSnapshot.length;
1348
- for (const d of moreSnapshot) {
1456
+ if (_isDev2) activeBindingCount -= snapshot.length;
1457
+ for (let i3 = 0; i3 < snapshot.length; i3++) {
1458
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
1459
+ const rest = snapshot.slice(i3);
1460
+ const added = elementDisposers.get(current);
1461
+ elementDisposers.set(current, added ? rest.concat(added) : rest);
1462
+ if (_isDev2) activeBindingCount += rest.length;
1463
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
1464
+ runaway = true;
1465
+ break;
1466
+ }
1467
+ executed++;
1349
1468
  try {
1350
- d();
1469
+ snapshot[i3]();
1351
1470
  } catch (err) {
1352
- if (_isDev2 && typeof console !== "undefined") {
1353
- console.warn("[SibuJS] Disposer threw during cleanup:", err);
1354
- }
1471
+ reportError(err, { phase: "cleanup", name: "disposer" });
1355
1472
  }
1356
1473
  }
1357
1474
  }
1358
1475
  }
1359
1476
  }
1360
1477
  }
1478
+ function replaceChildrenSafely(parent, ...next) {
1479
+ for (let i2 = 0; i2 < next.length; i2++) {
1480
+ const node = next[i2];
1481
+ node.parentNode?.removeChild(node);
1482
+ }
1483
+ const current = Array.from(parent.childNodes);
1484
+ for (let i2 = 0; i2 < current.length; i2++) {
1485
+ dispose(current[i2]);
1486
+ }
1487
+ parent.replaceChildren(...next);
1488
+ }
1361
1489
  function checkLeaks(warnThreshold = 0) {
1362
1490
  if (!_isDev2) return 0;
1363
1491
  if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
@@ -1378,10 +1506,12 @@ function isEventHandlerAttr(name) {
1378
1506
  return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
1379
1507
  }
1380
1508
  var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1509
+ var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
1381
1510
  function sanitizeUrl(url) {
1382
- const trimmed = stripControlChars(url).trim();
1383
- if (!trimmed) return "";
1384
- const lower = trimmed.toLowerCase();
1511
+ const probe = stripControlChars(url).trim();
1512
+ if (!probe) return "";
1513
+ const output2 = url.replace(OUTER_TRIM, "");
1514
+ const lower = probe.toLowerCase();
1385
1515
  let schemeEnd = -1;
1386
1516
  for (let i2 = 0; i2 < lower.length; i2++) {
1387
1517
  const ch = lower.charCodeAt(i2);
@@ -1391,12 +1521,13 @@ function sanitizeUrl(url) {
1391
1521
  }
1392
1522
  if (ch === 47 || ch === 63 || ch === 35) break;
1393
1523
  }
1394
- if (schemeEnd === -1) return trimmed;
1524
+ if (schemeEnd === -1) return output2;
1395
1525
  const scheme = lower.slice(0, schemeEnd + 1);
1396
- if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
1526
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
1397
1527
  if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1398
- return trimmed;
1528
+ return output2;
1399
1529
  }
1530
+ var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
1400
1531
  function sanitizeSrcset(value) {
1401
1532
  const parts = value.split(",");
1402
1533
  const out = [];
@@ -1405,6 +1536,7 @@ function sanitizeSrcset(value) {
1405
1536
  if (!part) continue;
1406
1537
  const m = part.match(/^(\S+)(\s+.+)?$/);
1407
1538
  if (!m) continue;
1539
+ if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
1408
1540
  const safe = sanitizeUrl(m[1]);
1409
1541
  if (!safe) continue;
1410
1542
  out.push(m[2] ? `${safe}${m[2]}` : safe);
@@ -1412,23 +1544,83 @@ function sanitizeSrcset(value) {
1412
1544
  return out.join(", ");
1413
1545
  }
1414
1546
  var CSS_DANGER_GATE = /[(:@\\]/;
1547
+ function isCssWhitespace(ch) {
1548
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
1549
+ }
1550
+ function isHexDigit(ch) {
1551
+ return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
1552
+ }
1553
+ function decodeCssEscapes(value) {
1554
+ let out = "";
1555
+ for (let i2 = 0; i2 < value.length; i2++) {
1556
+ const ch = value[i2];
1557
+ if (ch !== "\\") {
1558
+ out += ch;
1559
+ continue;
1560
+ }
1561
+ if (i2 + 1 >= value.length) break;
1562
+ const next = value[i2 + 1];
1563
+ if (next === "\n" || next === "\f") {
1564
+ i2 += 1;
1565
+ continue;
1566
+ }
1567
+ if (next === "\r") {
1568
+ i2 += value[i2 + 2] === "\n" ? 2 : 1;
1569
+ continue;
1570
+ }
1571
+ if (isHexDigit(next)) {
1572
+ let hex = "";
1573
+ let j = i2 + 1;
1574
+ while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
1575
+ hex += value[j];
1576
+ j++;
1577
+ }
1578
+ if (j < value.length && isCssWhitespace(value[j])) {
1579
+ j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
1580
+ }
1581
+ const code2 = Number.parseInt(hex, 16);
1582
+ const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
1583
+ out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
1584
+ i2 = j - 1;
1585
+ continue;
1586
+ }
1587
+ out += next;
1588
+ i2 += 1;
1589
+ }
1590
+ return out;
1591
+ }
1415
1592
  function sanitizeCSSValue(value) {
1416
1593
  if (!CSS_DANGER_GATE.test(value)) return value;
1417
- const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
1418
- const code2 = Number.parseInt(hex, 16);
1419
- if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
1420
- try {
1421
- return String.fromCodePoint(code2);
1422
- } catch {
1423
- return "";
1424
- }
1425
- });
1426
- const lower = decoded.toLowerCase().replace(/\s+/g, "");
1594
+ const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
1595
+ const lower = normalized.toLowerCase().replace(/\s+/g, "");
1427
1596
  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")) {
1428
1597
  return "";
1429
1598
  }
1430
1599
  return value;
1431
1600
  }
1601
+ function sanitizeStyleAttribute(cssText) {
1602
+ const input2 = String(cssText);
1603
+ if (input2.trim() === "") return "";
1604
+ if (typeof document === "undefined") {
1605
+ return sanitizeCSSValue(input2) === "" ? "" : input2;
1606
+ }
1607
+ const probe = document.createElement("div");
1608
+ try {
1609
+ probe.style.cssText = input2;
1610
+ } catch {
1611
+ return "";
1612
+ }
1613
+ const declarations = [];
1614
+ for (let i2 = 0; i2 < probe.style.length; i2++) {
1615
+ const property = probe.style[i2];
1616
+ const value = probe.style.getPropertyValue(property);
1617
+ if (sanitizeCSSValue(value) === "") continue;
1618
+ if (sanitizeCSSValue(`${property}:${value}`) === "") continue;
1619
+ const priority = probe.style.getPropertyPriority(property);
1620
+ declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
1621
+ }
1622
+ return declarations.join("; ");
1623
+ }
1432
1624
  var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1433
1625
  "href",
1434
1626
  "xlink:href",
@@ -1441,20 +1633,101 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1441
1633
  "background",
1442
1634
  "srcset",
1443
1635
  "ping",
1444
- "data"
1636
+ "data",
1637
+ // `<html manifest>` is a URL sink too. It was carried only by the SSR
1638
+ // serializers' private copy of this set; folding that copy into this one would
1639
+ // otherwise have silently dropped its coverage.
1640
+ "manifest"
1445
1641
  ]);
1446
- function isUrlAttribute(attr) {
1447
- return URL_ATTRIBUTES.has(attr.toLowerCase());
1642
+ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
1643
+ function isHtmlContentAttribute(name) {
1644
+ return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
1448
1645
  }
1449
1646
  function sanitizeAttributeString(attr, value) {
1450
1647
  const lower = attr.toLowerCase();
1451
1648
  if (lower === "srcset") return sanitizeSrcset(value);
1452
1649
  if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1650
+ if (lower === "style") return sanitizeStyleAttribute(value);
1453
1651
  return value;
1454
1652
  }
1455
1653
 
1456
- // src/reactivity/track-core.ts
1654
+ // src/utils/setSafeAttribute.ts
1457
1655
  var _isDev3 = isDev();
1656
+ var HTML_NS = "http://www.w3.org/1999/xhtml";
1657
+ var SVG_NS = "http://www.w3.org/2000/svg";
1658
+ var XLINK_NS = "http://www.w3.org/1999/xlink";
1659
+ var XML_NS = "http://www.w3.org/XML/1998/namespace";
1660
+ var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
1661
+ function isHtmlElement(el) {
1662
+ return el.namespaceURI === null || el.namespaceURI === HTML_NS;
1663
+ }
1664
+ function setProp(el, key, val) {
1665
+ el[key] = val;
1666
+ }
1667
+ function namespaceFor(el, name) {
1668
+ if (el.namespaceURI !== SVG_NS) return null;
1669
+ const lower = name.toLowerCase();
1670
+ if (lower.startsWith("xlink:")) return XLINK_NS;
1671
+ if (lower.startsWith("xml:")) return XML_NS;
1672
+ return null;
1673
+ }
1674
+ function setSafeAttribute(el, name, value, options = {}) {
1675
+ const ns = namespaceFor(el, name);
1676
+ const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
1677
+ if (isEventHandlerAttr(name)) {
1678
+ if (_isDev3) {
1679
+ devWarn(
1680
+ `${options.label ?? "setSafeAttribute"}: refusing to set event-handler attribute "${name}". Its value would be evaluated as JavaScript. Use on:{ ${name.slice(2)}: fn } instead.`
1681
+ );
1682
+ }
1683
+ if (ns) el.removeAttributeNS(ns, localName);
1684
+ else el.removeAttribute(name);
1685
+ return false;
1686
+ }
1687
+ if (value == null) {
1688
+ if (ns) el.removeAttributeNS(ns, localName);
1689
+ else el.removeAttribute(name);
1690
+ return true;
1691
+ }
1692
+ if (isHtmlContentAttribute(name)) {
1693
+ if (_isDev3) {
1694
+ devWarn(
1695
+ `${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.`
1696
+ );
1697
+ }
1698
+ if (ns) el.removeAttributeNS(ns, localName);
1699
+ else el.removeAttribute(name);
1700
+ return false;
1701
+ }
1702
+ const html3 = isHtmlElement(el);
1703
+ const idlName = html3 ? name.toLowerCase() : name;
1704
+ if (typeof value === "boolean") {
1705
+ if (html3 && BOOLEAN_IDL_ATTRS.has(idlName) && idlName in el) {
1706
+ setProp(el, idlName, value);
1707
+ } else if (value) {
1708
+ if (ns) el.setAttributeNS(ns, name, "");
1709
+ else el.setAttribute(name, "");
1710
+ } else if (ns) {
1711
+ el.removeAttributeNS(ns, localName);
1712
+ } else {
1713
+ el.removeAttribute(name);
1714
+ }
1715
+ return true;
1716
+ }
1717
+ const str = String(value);
1718
+ if (options.syncValueProperty !== false && html3 && (idlName === "value" || idlName === "checked") && idlName in el) {
1719
+ setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
1720
+ return true;
1721
+ }
1722
+ const safe = sanitizeAttributeString(name, str);
1723
+ const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
1724
+ if (current === safe) return true;
1725
+ if (ns) el.setAttributeNS(ns, name, safe);
1726
+ else el.setAttribute(name, safe);
1727
+ return true;
1728
+ }
1729
+
1730
+ // src/reactivity/track-core.ts
1458
1731
  var POOL_MAX = 4096;
1459
1732
  var nodePool = [];
1460
1733
  function createNode() {
@@ -1462,6 +1735,7 @@ function createNode() {
1462
1735
  sig: null,
1463
1736
  sub: null,
1464
1737
  epoch: 0,
1738
+ depVersion: 0,
1465
1739
  sigPrev: null,
1466
1740
  sigNext: null,
1467
1741
  subPrev: null,
@@ -1546,8 +1820,27 @@ function safeInvoke(sub2) {
1546
1820
  try {
1547
1821
  sub2();
1548
1822
  } catch (err) {
1549
- if (_isDev3) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
1823
+ const s2 = sub2;
1824
+ reportError(err, { phase: s2._errorPhase ?? "effect", name: s2.__name, node: s2._errorNode });
1825
+ }
1826
+ }
1827
+ function depsChanged(sub2) {
1828
+ let node = sub2.depsHead ?? null;
1829
+ if (node === null) return true;
1830
+ while (node !== null) {
1831
+ const sig = node.sig;
1832
+ if (sig === null) return true;
1833
+ if (sig._d === true && sig._validate !== void 0) {
1834
+ try {
1835
+ sig._validate();
1836
+ } catch {
1837
+ return true;
1838
+ }
1839
+ }
1840
+ if (node.depVersion !== sig.__v) return true;
1841
+ node = node.subNext;
1550
1842
  }
1843
+ return false;
1551
1844
  }
1552
1845
  var suspendDepth = 0;
1553
1846
  var trackingSuspended = false;
@@ -1629,7 +1922,7 @@ function track(effectFn, subscriber) {
1629
1922
  const sub2 = subscriber;
1630
1923
  return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
1631
1924
  }
1632
- function reactiveBinding(commit) {
1925
+ function reactiveBinding(commit, ownerNode) {
1633
1926
  const run = () => {
1634
1927
  const s2 = subscriber;
1635
1928
  if (s2._disposed || s2._reentrant) return;
@@ -1645,13 +1938,17 @@ function reactiveBinding(commit) {
1645
1938
  subscriber.depsTail = null;
1646
1939
  subscriber._epoch = 0;
1647
1940
  subscriber._structDirty = false;
1941
+ subscriber._hasComputedDep = false;
1648
1942
  subscriber._runEpoch = 0;
1649
1943
  subscriber._runs = 0;
1650
1944
  subscriber._reentrant = false;
1651
1945
  subscriber._disposed = false;
1946
+ subscriber._errorPhase = "binding";
1947
+ subscriber._errorNode = ownerNode;
1652
1948
  run();
1653
1949
  return subscriber._dispose ?? (subscriber._dispose = () => {
1654
1950
  subscriber._disposed = true;
1951
+ subscriber._errorNode = void 0;
1655
1952
  cleanup(subscriber);
1656
1953
  });
1657
1954
  }
@@ -1663,9 +1960,12 @@ function recordDependency(signal2) {
1663
1960
  const active = sig.__activeNode ?? null;
1664
1961
  if (active !== null && active.sub === sub2) {
1665
1962
  active.epoch = epoch;
1963
+ active.depVersion = sig.__v ?? Number.NaN;
1666
1964
  return;
1667
1965
  }
1966
+ if (sig._validate !== void 0) sub2._hasComputedDep = true;
1668
1967
  const node = allocNode(signal2, sub2, epoch);
1968
+ node.depVersion = sig.__v ?? Number.NaN;
1669
1969
  node.prevActive = active;
1670
1970
  sig.__activeNode = node;
1671
1971
  linkSub(sub2, node);
@@ -1684,9 +1984,10 @@ function cleanup(subscriber) {
1684
1984
  node = next;
1685
1985
  }
1686
1986
  }
1687
- var maxSubscriberRepeats = 50;
1987
+ var maxSubscriberRepeats = 1e3;
1688
1988
  var maxDrainIterations = 1e6;
1689
1989
  var drainEpoch = 0;
1990
+ var quarantined = /* @__PURE__ */ new Set();
1690
1991
  function setMaxSubscriberRepeats(n) {
1691
1992
  const prev = maxSubscriberRepeats;
1692
1993
  if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
@@ -1708,19 +2009,21 @@ function tickRepeat(sub2) {
1708
2009
  return s2._runs > maxSubscriberRepeats;
1709
2010
  }
1710
2011
  function cycleError(sub2) {
1711
- if (typeof console !== "undefined") {
1712
- const name = sub2.__name ?? "<unnamed>";
1713
- console.error(
1714
- `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
1715
- );
1716
- }
2012
+ const name = sub2.__name ?? "<unnamed>";
2013
+ reportError(
2014
+ new Error(
2015
+ `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.`
2016
+ ),
2017
+ { phase: "scheduler", name }
2018
+ );
1717
2019
  }
1718
2020
  function absoluteDrainError() {
1719
- if (typeof console !== "undefined") {
1720
- console.error(
1721
- `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
1722
- );
1723
- }
2021
+ reportError(
2022
+ new Error(
2023
+ `Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
2024
+ ),
2025
+ { phase: "scheduler" }
2026
+ );
1724
2027
  }
1725
2028
  function drainQueue() {
1726
2029
  let i2 = 0;
@@ -1730,11 +2033,14 @@ function drainQueue() {
1730
2033
  break;
1731
2034
  }
1732
2035
  const sub2 = pendingQueue[i2++];
2036
+ pendingSet.delete(sub2);
2037
+ if (quarantined.size > 0 && quarantined.has(sub2)) continue;
2038
+ if (sub2._hasComputedDep === true && !depsChanged(sub2)) continue;
1733
2039
  if (tickRepeat(sub2)) {
1734
2040
  cycleError(sub2);
1735
- break;
2041
+ quarantined.add(sub2);
2042
+ continue;
1736
2043
  }
1737
- pendingSet.delete(sub2);
1738
2044
  safeInvoke(sub2);
1739
2045
  }
1740
2046
  }
@@ -1749,6 +2055,7 @@ function drainNotificationQueue() {
1749
2055
  if (notifyDepth === 0) {
1750
2056
  pendingQueue.length = 0;
1751
2057
  pendingSet.clear();
2058
+ if (quarantined.size > 0) quarantined.clear();
1752
2059
  }
1753
2060
  }
1754
2061
  }
@@ -1842,6 +2149,7 @@ function notifySubscribers(signal2) {
1842
2149
  if (notifyDepth === 0) {
1843
2150
  pendingQueue.length = 0;
1844
2151
  pendingSet.clear();
2152
+ if (quarantined.size > 0) quarantined.clear();
1845
2153
  }
1846
2154
  }
1847
2155
  }
@@ -1869,7 +2177,7 @@ function forEachSubscriber(signal2, visit) {
1869
2177
 
1870
2178
  // src/reactivity/track.ts
1871
2179
  var _isDev4 = isDev();
1872
- var _runtimeVersion = true ? "3.4.0" : "dev";
2180
+ var _runtimeVersion = true ? "4.0.0" : "dev";
1873
2181
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
1874
2182
  function resolveReactiveApi() {
1875
2183
  const g2 = globalThis;
@@ -1926,16 +2234,9 @@ var getSubscriberDeps2 = API.getSubscriberDeps;
1926
2234
  var forEachSubscriber2 = API.forEachSubscriber;
1927
2235
 
1928
2236
  // src/reactivity/bindAttribute.ts
1929
- var _isDev5 = isDev();
1930
- function setProp(el, key, val) {
1931
- el[key] = val;
1932
- }
1933
2237
  function bindAttribute(el, attr, getter) {
1934
2238
  if (isEventHandlerAttr(attr)) {
1935
- if (_isDev5)
1936
- devWarn(
1937
- `bindAttribute: refusing to bind event-handler attribute "${attr}". Use on:{ ${attr.slice(2)}: fn } instead.`
1938
- );
2239
+ setSafeAttribute(el, attr, "", { label: `bindAttribute("${attr}")` });
1939
2240
  return () => {
1940
2241
  };
1941
2242
  }
@@ -1944,28 +2245,12 @@ function bindAttribute(el, attr, getter) {
1944
2245
  try {
1945
2246
  value = getter();
1946
2247
  } catch (err) {
1947
- if (_isDev5)
1948
- devWarn(`bindAttribute: getter for "${attr}" threw: ${err instanceof Error ? err.message : String(err)}`);
2248
+ reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
1949
2249
  return;
1950
2250
  }
1951
- if (typeof value === "boolean") {
1952
- if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
1953
- setProp(el, attr, value);
1954
- } else if (value) {
1955
- el.setAttribute(attr, "");
1956
- } else {
1957
- el.removeAttribute(attr);
1958
- }
1959
- return;
1960
- }
1961
- const str = String(value);
1962
- if ((attr === "value" || attr === "checked") && attr in el) {
1963
- setProp(el, attr, attr === "checked" ? Boolean(value) : str);
1964
- } else {
1965
- el.setAttribute(attr, sanitizeAttributeString(attr, str));
1966
- }
2251
+ setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
1967
2252
  }
1968
- return reactiveBinding2(commit);
2253
+ return reactiveBinding2(commit, el);
1969
2254
  }
1970
2255
  function bindDynamic(el, nameGetter, valueGetter) {
1971
2256
  let prevName = null;
@@ -1974,29 +2259,24 @@ function bindDynamic(el, nameGetter, valueGetter) {
1974
2259
  try {
1975
2260
  name = typeof nameGetter === "function" ? nameGetter() : nameGetter;
1976
2261
  } catch (err) {
1977
- if (_isDev5) devWarn(`bindDynamic: name getter threw: ${err instanceof Error ? err.message : String(err)}`);
2262
+ reportError(err, { phase: "binding", name: "bindDynamic (name getter)", node: el });
1978
2263
  return;
1979
2264
  }
1980
2265
  let value;
1981
2266
  try {
1982
2267
  value = typeof valueGetter === "function" ? valueGetter() : valueGetter;
1983
2268
  } catch (err) {
1984
- if (_isDev5) devWarn(`bindDynamic: value getter threw: ${err instanceof Error ? err.message : String(err)}`);
2269
+ reportError(err, { phase: "binding", name: "bindDynamic (value getter)", node: el });
1985
2270
  return;
1986
2271
  }
1987
- if (isEventHandlerAttr(name)) return;
1988
2272
  if (prevName !== null && prevName !== name) {
1989
2273
  el.removeAttribute(prevName);
2274
+ prevName = null;
1990
2275
  }
1991
- const str = String(value);
1992
- if ((name === "value" || name === "checked") && name in el) {
1993
- setProp(el, name, name === "checked" ? Boolean(value) : str);
1994
- } else {
1995
- el.setAttribute(name, sanitizeAttributeString(name, str));
1996
- }
2276
+ if (!setSafeAttribute(el, name, value, { label: "bindDynamic" })) return;
1997
2277
  prevName = name;
1998
2278
  }
1999
- const teardown = reactiveBinding2(commit);
2279
+ const teardown = reactiveBinding2(commit, el);
2000
2280
  return () => {
2001
2281
  teardown();
2002
2282
  if (prevName !== null) {
@@ -2006,7 +2286,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
2006
2286
  }
2007
2287
 
2008
2288
  // src/reactivity/bindChildNode.ts
2009
- var _isDev6 = isDev();
2289
+ var _isDev5 = isDev();
2010
2290
  function bindChildNode(placeholder, getter) {
2011
2291
  let lastNodes = [];
2012
2292
  function commit() {
@@ -2014,7 +2294,7 @@ function bindChildNode(placeholder, getter) {
2014
2294
  try {
2015
2295
  result = getter();
2016
2296
  } catch (err) {
2017
- if (_isDev6) devWarn(`bindChildNode: getter threw: ${err instanceof Error ? err.message : String(err)}`);
2297
+ reportError(err, { phase: "binding", name: "bindChildNode", node: placeholder });
2018
2298
  return;
2019
2299
  }
2020
2300
  if (result == null || typeof result === "boolean") {
@@ -2040,7 +2320,7 @@ function bindChildNode(placeholder, getter) {
2040
2320
  if (item == null || typeof item === "boolean") continue;
2041
2321
  const node = item instanceof Node ? item : document.createTextNode(String(item));
2042
2322
  if (seen.has(node)) {
2043
- if (_isDev6)
2323
+ if (_isDev5)
2044
2324
  devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
2045
2325
  continue;
2046
2326
  }
@@ -2075,12 +2355,12 @@ function bindChildNode(placeholder, getter) {
2075
2355
  }
2076
2356
  lastNodes = newNodes;
2077
2357
  }
2078
- return reactiveBinding2(commit);
2358
+ return reactiveBinding2(commit, placeholder);
2079
2359
  }
2080
2360
 
2081
2361
  // src/core/rendering/tagFactory.ts
2082
- var SVG_NS = "http://www.w3.org/2000/svg";
2083
- var _isDev7 = isDev();
2362
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
2363
+ var _isDev6 = isDev();
2084
2364
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
2085
2365
  function isBlockedTag(tag) {
2086
2366
  return BLOCKED_TAGS.has(tag.toLowerCase());
@@ -2098,9 +2378,6 @@ var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
2098
2378
  "self",
2099
2379
  "frames"
2100
2380
  ]);
2101
- function setProp2(el, key, val) {
2102
- el[key] = val;
2103
- }
2104
2381
  function looksLikeClassList(s2) {
2105
2382
  const t = s2.trim();
2106
2383
  if (!t) return false;
@@ -2124,13 +2401,13 @@ function toKebab(prop) {
2124
2401
  function applyStyle(el, style2) {
2125
2402
  if (typeof style2 === "function") {
2126
2403
  const teardown = track2(() => {
2127
- el.setAttribute("style", style2());
2404
+ el.setAttribute("style", sanitizeStyleAttribute(String(style2())));
2128
2405
  });
2129
2406
  registerDisposer(el, teardown);
2130
2407
  return;
2131
2408
  }
2132
2409
  if (typeof style2 === "string") {
2133
- el.setAttribute("style", style2);
2410
+ el.setAttribute("style", sanitizeStyleAttribute(style2));
2134
2411
  return;
2135
2412
  }
2136
2413
  const htmlEl = el;
@@ -2251,7 +2528,7 @@ var tagFactory = (tag, ns) => {
2251
2528
  appendChildren(el, second);
2252
2529
  return el;
2253
2530
  }
2254
- if (_isDev7 && looksLikeClassList(first)) {
2531
+ if (_isDev6 && looksLikeClassList(first)) {
2255
2532
  devWarn(
2256
2533
  `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.`
2257
2534
  );
@@ -2272,7 +2549,7 @@ var tagFactory = (tag, ns) => {
2272
2549
  if (pClass != null) applyClass(el, pClass);
2273
2550
  const pId = props.id;
2274
2551
  if (pId != null) {
2275
- if (_isDev7 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
2552
+ if (_isDev6 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
2276
2553
  devWarn(
2277
2554
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
2278
2555
  );
@@ -2287,7 +2564,7 @@ var tagFactory = (tag, ns) => {
2287
2564
  const handler = pOn[ev];
2288
2565
  if (typeof handler === "function") {
2289
2566
  el.addEventListener(ev, handler);
2290
- } else if (_isDev7) {
2567
+ } else if (_isDev6) {
2291
2568
  devWarn(
2292
2569
  `tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
2293
2570
  );
@@ -2315,16 +2592,8 @@ var tagFactory = (tag, ns) => {
2315
2592
  if (isEventHandlerAttr(key)) continue;
2316
2593
  if (typeof value === "function") {
2317
2594
  registerDisposer(el, bindAttribute(el, key, value));
2318
- } else if (typeof value === "boolean") {
2319
- if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
2320
- setProp2(el, key, value);
2321
- } else if (value) {
2322
- el.setAttribute(key, "");
2323
- } else {
2324
- el.removeAttribute(key);
2325
- }
2326
2595
  } else {
2327
- el.setAttribute(key, sanitizeAttributeString(key, String(value)));
2596
+ setSafeAttribute(el, key, value, { syncValueProperty: false, label: "tagFactory" });
2328
2597
  }
2329
2598
  }
2330
2599
  }
@@ -2409,7 +2678,7 @@ var param = tagFactory("param");
2409
2678
  var picture = tagFactory("picture");
2410
2679
  var portal = tagFactory("portal");
2411
2680
  var source = tagFactory("source");
2412
- var svg = tagFactory("svg", SVG_NS);
2681
+ var svg = tagFactory("svg", SVG_NS2);
2413
2682
  var math = tagFactory("math");
2414
2683
  var canvas = tagFactory("canvas");
2415
2684
  var noscript = tagFactory("noscript");
@@ -2450,26 +2719,26 @@ var base = tagFactory("base");
2450
2719
  var link = tagFactory("link");
2451
2720
  var meta = tagFactory("meta");
2452
2721
  var style = tagFactory("style");
2453
- var circle = tagFactory("circle", SVG_NS);
2454
- var ellipse = tagFactory("ellipse", SVG_NS);
2455
- var g = tagFactory("g", SVG_NS);
2456
- var line = tagFactory("line", SVG_NS);
2457
- var path = tagFactory("path", SVG_NS);
2458
- var polygon = tagFactory("polygon", SVG_NS);
2459
- var polyline = tagFactory("polyline", SVG_NS);
2460
- var rect = tagFactory("rect", SVG_NS);
2461
- var text = tagFactory("text", SVG_NS);
2462
- var tspan = tagFactory("tspan", SVG_NS);
2463
- var defs = tagFactory("defs", SVG_NS);
2464
- var clipPath = tagFactory("clipPath", SVG_NS);
2465
- var mask = tagFactory("mask", SVG_NS);
2466
- var pattern = tagFactory("pattern", SVG_NS);
2467
- var linearGradient = tagFactory("linearGradient", SVG_NS);
2468
- var radialGradient = tagFactory("radialGradient", SVG_NS);
2469
- var stop = tagFactory("stop", SVG_NS);
2470
- var use = tagFactory("use", SVG_NS);
2471
- var symbol = tagFactory("symbol", SVG_NS);
2472
- var marker = tagFactory("marker", SVG_NS);
2722
+ var circle = tagFactory("circle", SVG_NS2);
2723
+ var ellipse = tagFactory("ellipse", SVG_NS2);
2724
+ var g = tagFactory("g", SVG_NS2);
2725
+ var line = tagFactory("line", SVG_NS2);
2726
+ var path = tagFactory("path", SVG_NS2);
2727
+ var polygon = tagFactory("polygon", SVG_NS2);
2728
+ var polyline = tagFactory("polyline", SVG_NS2);
2729
+ var rect = tagFactory("rect", SVG_NS2);
2730
+ var text = tagFactory("text", SVG_NS2);
2731
+ var tspan = tagFactory("tspan", SVG_NS2);
2732
+ var defs = tagFactory("defs", SVG_NS2);
2733
+ var clipPath = tagFactory("clipPath", SVG_NS2);
2734
+ var mask = tagFactory("mask", SVG_NS2);
2735
+ var pattern = tagFactory("pattern", SVG_NS2);
2736
+ var linearGradient = tagFactory("linearGradient", SVG_NS2);
2737
+ var radialGradient = tagFactory("radialGradient", SVG_NS2);
2738
+ var stop = tagFactory("stop", SVG_NS2);
2739
+ var use = tagFactory("use", SVG_NS2);
2740
+ var symbol = tagFactory("symbol", SVG_NS2);
2741
+ var marker = tagFactory("marker", SVG_NS2);
2473
2742
  var center = tagFactory("center");
2474
2743
  var font = tagFactory("font");
2475
2744
  var marquee = tagFactory("marquee");
@@ -2478,18 +2747,14 @@ var customElement = (tagName) => tagFactory(tagName);
2478
2747
  // src/core/rendering/lazy.ts
2479
2748
  var PENDING_ERROR = "__sibuPendingError";
2480
2749
  function dispatchPropagate(node, error) {
2481
- const fire = () => {
2482
- try {
2483
- if (!node.parentNode) return false;
2484
- node.dispatchEvent(new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error } }));
2485
- return true;
2486
- } catch {
2487
- return false;
2488
- }
2750
+ const report = () => {
2751
+ if (!node.parentNode) return false;
2752
+ reportError(error, { phase: "render", name: "lazy", node });
2753
+ return true;
2489
2754
  };
2490
- if (node.parentNode && fire()) return;
2755
+ if (report()) return;
2491
2756
  queueMicrotask(() => {
2492
- if (fire()) return;
2757
+ if (report()) return;
2493
2758
  node[PENDING_ERROR] = error;
2494
2759
  });
2495
2760
  }
@@ -2514,12 +2779,14 @@ function lazy(importFn) {
2514
2779
  if (disposed) return;
2515
2780
  cached = mod.default;
2516
2781
  const rendered = cached();
2517
- container.replaceChildren(rendered);
2782
+ replaceChildrenSafely(container, rendered);
2518
2783
  }).catch((err) => {
2519
2784
  if (disposed) return;
2520
2785
  const errorObj = err instanceof Error ? err : new Error(String(err));
2521
- devWarn(`[SibuJS] lazy() failed to load component: ${errorObj.message}`);
2522
- container.replaceChildren(div({ class: "sibu-lazy-error" }, `Failed to load component: ${errorObj.message}`));
2786
+ replaceChildrenSafely(
2787
+ container,
2788
+ div({ class: "sibu-lazy-error" }, `Failed to load component: ${errorObj.message}`)
2789
+ );
2523
2790
  dispatchPropagate(container, errorObj);
2524
2791
  });
2525
2792
  container.appendChild(span("sibu-lazy-loading", "Loading..."));
@@ -2551,7 +2818,7 @@ function Suspense({ nodes, fallback }) {
2551
2818
  childEl = el;
2552
2819
  if (el.classList.contains("sibu-lazy")) {
2553
2820
  if (!el.querySelector(".sibu-lazy-loading")) {
2554
- container.replaceChildren(el);
2821
+ replaceChildrenSafely(container, el);
2555
2822
  return;
2556
2823
  }
2557
2824
  observer = new MutationObserver(() => {
@@ -2560,16 +2827,15 @@ function Suspense({ nodes, fallback }) {
2560
2827
  if (!loading) {
2561
2828
  observer?.disconnect();
2562
2829
  observer = null;
2563
- container.replaceChildren(el);
2830
+ replaceChildrenSafely(container, el);
2564
2831
  }
2565
2832
  });
2566
2833
  observer.observe(el, { childList: true, subtree: true });
2567
2834
  } else {
2568
- container.replaceChildren(el);
2835
+ replaceChildrenSafely(container, el);
2569
2836
  }
2570
2837
  } catch (err) {
2571
2838
  const errorObj = err instanceof Error ? err : new Error(String(err));
2572
- devWarn(`[SibuJS] Suspense nodes() threw: ${errorObj.message}`);
2573
2839
  dispatchPropagate(container, errorObj);
2574
2840
  }
2575
2841
  });
@@ -2581,7 +2847,8 @@ function safeCall(cb, hookName) {
2581
2847
  try {
2582
2848
  return cb();
2583
2849
  } catch (err) {
2584
- devWarn(`${hookName}: callback threw: ${err instanceof Error ? err.message : String(err)}`);
2850
+ const phase = hookName.toLowerCase().includes("unmount") ? "cleanup" : "effect";
2851
+ reportError(err, { phase, name: hookName });
2585
2852
  return void 0;
2586
2853
  }
2587
2854
  }
@@ -2815,20 +3082,35 @@ function detectSSRShared() {
2815
3082
  const getBuiltin = process.getBuiltinModule;
2816
3083
  if (typeof getBuiltin === "function") {
2817
3084
  mod = getBuiltin("node:async_hooks");
2818
- } else {
2819
- const req = Function("return typeof require==='function'?require:null")();
2820
- if (req) mod = req("node:async_hooks");
3085
+ } else if (typeof require === "function") {
3086
+ mod = require("async_hooks");
2821
3087
  }
2822
3088
  if (mod) detected = new mod.AsyncLocalStorage();
2823
3089
  }
2824
3090
  } catch {
2825
3091
  detected = null;
2826
3092
  }
2827
- return { als: detected, fallbackStore: { ssr: false, suspenseIdCounter: 0 } };
3093
+ return {
3094
+ als: detected,
3095
+ fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
3096
+ fallbackDepth: 0
3097
+ };
2828
3098
  }
2829
3099
  var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
2830
3100
  var als = _shared.als;
2831
3101
  var fallbackStore = _shared.fallbackStore;
3102
+ _shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
3103
+ var _alsWarned = false;
3104
+ function warnMissingAsyncLocalStorage() {
3105
+ if (_alsWarned || als) return;
3106
+ const isNode = typeof process !== "undefined" && !!process.versions?.node;
3107
+ if (!isNode) return;
3108
+ _alsWarned = true;
3109
+ const version = process.versions.node;
3110
+ devWarn(
3111
+ `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.`
3112
+ );
3113
+ }
2832
3114
  function getSSRStore() {
2833
3115
  if (als) {
2834
3116
  const s2 = als.getStore();
@@ -2857,19 +3139,25 @@ function disableSSR() {
2857
3139
  getSSRStore().ssr = false;
2858
3140
  }
2859
3141
  function runInSSRContext(fn) {
2860
- const store2 = { ssr: true, suspenseIdCounter: 0 };
3142
+ const store2 = { ssr: true, suspenseIdCounter: 0, locale: void 0 };
2861
3143
  if (als) {
2862
3144
  return als.run(store2, fn);
2863
3145
  }
3146
+ warnMissingAsyncLocalStorage();
2864
3147
  const prevSSR = fallbackStore.ssr;
2865
3148
  const prevCounter = fallbackStore.suspenseIdCounter;
3149
+ const prevLocale = fallbackStore.locale;
2866
3150
  fallbackStore.ssr = true;
2867
3151
  fallbackStore.suspenseIdCounter = 0;
3152
+ fallbackStore.locale = void 0;
3153
+ _shared.fallbackDepth++;
2868
3154
  try {
2869
3155
  return fn();
2870
3156
  } finally {
3157
+ _shared.fallbackDepth--;
2871
3158
  fallbackStore.ssr = prevSSR;
2872
3159
  fallbackStore.suspenseIdCounter = prevCounter;
3160
+ fallbackStore.locale = prevLocale;
2873
3161
  }
2874
3162
  }
2875
3163
  function withSSR(fn) {
@@ -2910,7 +3198,7 @@ function flushUserCleanups(ctx) {
2910
3198
  try {
2911
3199
  list[i2]();
2912
3200
  } catch (err) {
2913
- if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
3201
+ reportError(err, { phase: "cleanup" });
2914
3202
  }
2915
3203
  }
2916
3204
  }
@@ -2923,11 +3211,12 @@ function drainReruns(ctx) {
2923
3211
  } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
2924
3212
  if (ctx.rerunPending) {
2925
3213
  ctx.rerunPending = false;
2926
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
2927
- console.error(
2928
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
2929
- );
2930
- }
3214
+ reportError(
3215
+ new Error(
3216
+ `effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
3217
+ ),
3218
+ { phase: "effect" }
3219
+ );
2931
3220
  }
2932
3221
  }
2933
3222
  function disposeEffect(ctx) {
@@ -3004,6 +3293,8 @@ function effect(effectFn, options) {
3004
3293
  sub2.depsTail = null;
3005
3294
  sub2._epoch = 0;
3006
3295
  sub2._structDirty = false;
3296
+ sub2._hasComputedDep = false;
3297
+ sub2._errorPhase = "effect";
3007
3298
  sub2._runEpoch = 0;
3008
3299
  sub2._runs = 0;
3009
3300
  ctx.subscriber = sub2;
@@ -3072,70 +3363,72 @@ var isBatching = API2.isBatching;
3072
3363
 
3073
3364
  // src/core/signals/signal.ts
3074
3365
  var _g2 = globalThis;
3075
- var _isDev8 = isDev();
3366
+ var _isDev7 = isDev();
3076
3367
  function signal(initial, options) {
3077
- const state = {
3368
+ const state2 = {
3078
3369
  value: initial,
3079
3370
  __v: 0,
3080
3371
  __sc: 0,
3081
3372
  subsHead: null,
3082
3373
  subsTail: null,
3083
3374
  __activeNode: null,
3084
- __name: void 0
3375
+ __name: void 0,
3376
+ _d: false,
3377
+ _validate: void 0
3085
3378
  };
3086
- const debugName = _isDev8 ? options?.name : void 0;
3379
+ const debugName = _isDev7 ? options?.name : void 0;
3087
3380
  const equalsFn = options?.equals;
3088
- if (debugName) state.__name = debugName;
3381
+ if (debugName) state2.__name = debugName;
3089
3382
  function get() {
3090
- recordDependency2(state);
3091
- return state.value;
3383
+ recordDependency2(state2);
3384
+ return state2.value;
3092
3385
  }
3093
- get.__signal = state;
3386
+ get.__signal = state2;
3094
3387
  if (debugName) get.__name = debugName;
3095
3388
  let set;
3096
3389
  if (equalsFn) {
3097
3390
  set = (next) => {
3098
- const prev = state.value;
3391
+ const prev = state2.value;
3099
3392
  const newValue = typeof next === "function" ? next(prev) : next;
3100
3393
  if (equalsFn(prev, newValue)) return;
3101
- state.value = newValue;
3102
- state.__v++;
3103
- if (_isDev8) {
3394
+ state2.value = newValue;
3395
+ state2.__v++;
3396
+ if (_isDev7) {
3104
3397
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3105
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
3398
+ if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3106
3399
  }
3107
- if (!enqueueBatchedSignal(state)) {
3108
- notifySubscribers2(state);
3400
+ if (!enqueueBatchedSignal(state2)) {
3401
+ notifySubscribers2(state2);
3109
3402
  }
3110
3403
  };
3111
- } else if (_isDev8) {
3404
+ } else if (_isDev7) {
3112
3405
  set = (next) => {
3113
- const prev = state.value;
3406
+ const prev = state2.value;
3114
3407
  const newValue = typeof next === "function" ? next(prev) : next;
3115
3408
  if (Object.is(newValue, prev)) return;
3116
- state.value = newValue;
3117
- state.__v++;
3409
+ state2.value = newValue;
3410
+ state2.__v++;
3118
3411
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3119
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
3120
- if (!enqueueBatchedSignal(state)) {
3121
- notifySubscribers2(state);
3412
+ if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3413
+ if (!enqueueBatchedSignal(state2)) {
3414
+ notifySubscribers2(state2);
3122
3415
  }
3123
3416
  };
3124
3417
  } else {
3125
3418
  set = (next) => {
3126
- const prev = state.value;
3419
+ const prev = state2.value;
3127
3420
  const newValue = typeof next === "function" ? next(prev) : next;
3128
3421
  if (Object.is(newValue, prev)) return;
3129
- state.value = newValue;
3130
- state.__v++;
3131
- if (!enqueueBatchedSignal(state)) {
3132
- notifySubscribers2(state);
3422
+ state2.value = newValue;
3423
+ state2.__v++;
3424
+ if (!enqueueBatchedSignal(state2)) {
3425
+ notifySubscribers2(state2);
3133
3426
  }
3134
3427
  };
3135
3428
  }
3136
- if (_isDev8) {
3429
+ if (_isDev7) {
3137
3430
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3138
- if (hook) hook.emit("signal:create", { signal: state, name: debugName, getter: get, initial });
3431
+ if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
3139
3432
  }
3140
3433
  return [get, set];
3141
3434
  }
@@ -3347,14 +3640,14 @@ function parseStack(stack) {
3347
3640
  }
3348
3641
  return frames;
3349
3642
  }
3350
- function normalizeError(err) {
3643
+ function normalizeError2(err) {
3351
3644
  if (err instanceof Error) {
3352
3645
  const code2 = err.code ?? err.name ?? "ERROR";
3353
3646
  const message = err.message || "Unknown error";
3354
3647
  const stack = err.stack ?? "";
3355
3648
  const frames = parseStack(stack);
3356
3649
  const rawCause = err.cause;
3357
- const cause = rawCause != null ? normalizeError(rawCause) : null;
3650
+ const cause = rawCause != null ? normalizeError2(rawCause) : null;
3358
3651
  return { code: code2, message, stack, frames, cause };
3359
3652
  }
3360
3653
  return {
@@ -3450,7 +3743,7 @@ function renderMetadata(meta2) {
3450
3743
  function ErrorDisplay(props) {
3451
3744
  injectStyles();
3452
3745
  const severity = props.severity ?? "error";
3453
- const normalized = normalizeError(props.error);
3746
+ const normalized = normalizeError2(props.error);
3454
3747
  const showDetails = props.alwaysShowDetails ?? isDev();
3455
3748
  const headline = props.title ?? normalized.message;
3456
3749
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
@@ -3700,6 +3993,7 @@ var errorBoundaryStyles = `
3700
3993
  background: #3a3a4e;
3701
3994
  }
3702
3995
  `;
3996
+ var RESET_KEY_THREW = /* @__PURE__ */ Symbol("sibujs.ErrorBoundary.resetKeyThrew");
3703
3997
  var stylesInjected = false;
3704
3998
  function injectStyles2() {
3705
3999
  if (!stylesInjected && typeof document !== "undefined") {
@@ -3708,29 +4002,6 @@ function injectStyles2() {
3708
4002
  stylesInjected = true;
3709
4003
  }
3710
4004
  }
3711
- var FALLBACK_CACHE_MAX = 50;
3712
- var fallbackCache = /* @__PURE__ */ new WeakMap();
3713
- function getMemoizedFallback(fallbackFn, error, retry) {
3714
- let cache2 = fallbackCache.get(fallbackFn);
3715
- if (!cache2) {
3716
- cache2 = /* @__PURE__ */ new Map();
3717
- fallbackCache.set(fallbackFn, cache2);
3718
- }
3719
- const key = error.message;
3720
- let factory = cache2.get(key);
3721
- if (factory) {
3722
- cache2.delete(key);
3723
- cache2.set(key, factory);
3724
- } else {
3725
- factory = () => fallbackFn(error, retry);
3726
- cache2.set(key, factory);
3727
- if (cache2.size > FALLBACK_CACHE_MAX) {
3728
- const oldestKey = cache2.keys().next().value;
3729
- if (oldestKey !== void 0) cache2.delete(oldestKey);
3730
- }
3731
- }
3732
- return factory();
3733
- }
3734
4005
  function ErrorBoundary(optionsOrChildren, maybeChildren) {
3735
4006
  const children = typeof optionsOrChildren === "function" ? optionsOrChildren : maybeChildren;
3736
4007
  const options = typeof optionsOrChildren === "function" ? {} : optionsOrChildren;
@@ -3738,43 +4009,61 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
3738
4009
  injectStyles2();
3739
4010
  const [error, setError] = signal(null);
3740
4011
  const retry = () => {
3741
- if (fallback) {
3742
- const cur = error();
3743
- const inner = fallbackCache.get(fallback);
3744
- if (cur && inner) inner.delete(cur.message);
3745
- }
4012
+ stopResetKeysWatcher();
3746
4013
  setError(null);
3747
4014
  };
3748
4015
  let resetKeysTeardown = null;
3749
- if (resetKeys && resetKeys.length > 0) {
3750
- let initialized = false;
4016
+ const stopResetKeysWatcher = () => {
4017
+ if (!resetKeysTeardown) return;
4018
+ const teardown = resetKeysTeardown;
4019
+ resetKeysTeardown = null;
4020
+ teardown();
4021
+ };
4022
+ const readResetKeyValues = (keys) => {
4023
+ const values = new Array(keys.length);
4024
+ for (let i2 = 0; i2 < keys.length; i2++) {
4025
+ try {
4026
+ values[i2] = keys[i2]();
4027
+ } catch (err) {
4028
+ reportError(err, { phase: "effect", name: "ErrorBoundary.resetKeys" });
4029
+ values[i2] = RESET_KEY_THREW;
4030
+ }
4031
+ }
4032
+ return values;
4033
+ };
4034
+ const startResetKeysWatcher = () => {
4035
+ const keys = resetKeys;
4036
+ if (!keys || keys.length === 0) return;
4037
+ stopResetKeysWatcher();
4038
+ let baseline = null;
3751
4039
  resetKeysTeardown = effect(() => {
3752
- for (const k of resetKeys) {
3753
- try {
3754
- k();
3755
- } catch (err) {
3756
- if (typeof console !== "undefined") {
3757
- console.warn("[SibuJS ErrorBoundary] resetKeys getter threw:", err);
4040
+ const current = readResetKeyValues(keys);
4041
+ if (baseline === null) {
4042
+ baseline = current;
4043
+ return;
4044
+ }
4045
+ let changed = current.length !== baseline.length;
4046
+ if (!changed) {
4047
+ for (let i2 = 0; i2 < current.length; i2++) {
4048
+ if (!Object.is(current[i2], baseline[i2])) {
4049
+ changed = true;
4050
+ break;
3758
4051
  }
3759
4052
  }
3760
4053
  }
3761
- if (!initialized) {
3762
- initialized = true;
3763
- return;
3764
- }
3765
- if (error() !== null) retry();
4054
+ baseline = current;
4055
+ if (changed) retry();
3766
4056
  });
3767
- }
4057
+ };
3768
4058
  const handleError = (e) => {
3769
- const errorObj = e instanceof Error ? e : new Error(String(e));
4059
+ const errorObj = normalizeError(e);
3770
4060
  setError(errorObj);
4061
+ startResetKeysWatcher();
3771
4062
  if (onError) {
3772
4063
  try {
3773
4064
  onError(errorObj);
3774
4065
  } catch (cbErr) {
3775
- if (typeof console !== "undefined") {
3776
- console.error("[SibuJS ErrorBoundary] onError callback threw:", cbErr);
3777
- }
4066
+ reportError(cbErr, { phase: "render", name: "ErrorBoundary onError callback" });
3778
4067
  }
3779
4068
  }
3780
4069
  return errorObj;
@@ -3785,18 +4074,14 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
3785
4074
  const tryRenderFallback = (err) => {
3786
4075
  const fn = fallback || defaultFallback;
3787
4076
  try {
3788
- return getMemoizedFallback(fn, err, retry);
4077
+ return fn(err, retry);
3789
4078
  } catch (fallbackError) {
3790
- const propagateError = fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError));
3791
4079
  queueMicrotask(() => {
3792
- if (container.parentNode) {
3793
- container.dispatchEvent(
3794
- new CustomEvent("sibu:error-propagate", {
3795
- bubbles: true,
3796
- detail: { error: propagateError }
3797
- })
3798
- );
3799
- }
4080
+ reportError(fallbackError, {
4081
+ phase: "render",
4082
+ name: "ErrorBoundary fallback",
4083
+ node: container.parentNode ? container : void 0
4084
+ });
3800
4085
  });
3801
4086
  return document.createComment("error-boundary-failed");
3802
4087
  }
@@ -3813,11 +4098,20 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
3813
4098
  if (result && typeof result.then === "function") {
3814
4099
  const asyncContainer = div({ class: "sibu-error-async" });
3815
4100
  asyncContainer.appendChild(span({ class: "sibu-lazy-loading", nodes: "Loading..." }));
4101
+ let asyncDisposed = false;
4102
+ registerDisposer(asyncContainer, () => {
4103
+ asyncDisposed = true;
4104
+ });
3816
4105
  result.then((el) => {
3817
- asyncContainer.replaceChildren(el);
4106
+ if (asyncDisposed) {
4107
+ dispose(el);
4108
+ return;
4109
+ }
4110
+ replaceChildrenSafely(asyncContainer, el);
3818
4111
  }).catch((e) => {
4112
+ if (asyncDisposed) return;
3819
4113
  const err = handleError(e);
3820
- asyncContainer.replaceChildren(tryRenderFallback(err));
4114
+ replaceChildrenSafely(asyncContainer, tryRenderFallback(err));
3821
4115
  });
3822
4116
  return asyncContainer;
3823
4117
  }
@@ -3830,12 +4124,12 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
3830
4124
  });
3831
4125
  const propagateListener = (e) => {
3832
4126
  if (error()) return;
3833
- e.stopPropagation();
3834
4127
  const customEvent = e;
3835
4128
  const propagatedError = customEvent.detail?.error;
3836
- if (propagatedError) {
3837
- handleError(propagatedError);
3838
- }
4129
+ if (!propagatedError) return;
4130
+ e.preventDefault();
4131
+ e.stopPropagation();
4132
+ handleError(propagatedError);
3839
4133
  };
3840
4134
  container.addEventListener("sibu:error-propagate", propagateListener);
3841
4135
  onMount(() => {
@@ -3863,7 +4157,7 @@ function ErrorBoundary(optionsOrChildren, maybeChildren) {
3863
4157
  return void 0;
3864
4158
  }, container);
3865
4159
  registerDisposer(container, () => {
3866
- if (resetKeysTeardown) resetKeysTeardown();
4160
+ stopResetKeysWatcher();
3867
4161
  container.removeEventListener("sibu:error-propagate", propagateListener);
3868
4162
  });
3869
4163
  return container;
@@ -4113,10 +4407,18 @@ function setGlobalErrorHandler(handler) {
4113
4407
  }
4114
4408
 
4115
4409
  // src/core/rendering/context.ts
4410
+ var _isDev8 = isDev();
4116
4411
  function context(defaultValue) {
4117
4412
  const [getValue, setValue] = signal(defaultValue);
4413
+ const warnIfSSR = (method) => {
4414
+ if (!_isDev8 || !isSSR()) return;
4415
+ devWarn(
4416
+ `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.`
4417
+ );
4418
+ };
4118
4419
  const ctx = {
4119
4420
  provide(value) {
4421
+ warnIfSSR("provide");
4120
4422
  const previous = getValue();
4121
4423
  setValue(value);
4122
4424
  return () => setValue(previous);
@@ -4128,13 +4430,21 @@ function context(defaultValue) {
4128
4430
  return getValue();
4129
4431
  },
4130
4432
  set(value) {
4433
+ warnIfSSR("set");
4131
4434
  setValue(value);
4132
4435
  },
4133
4436
  withContext(value, fn) {
4437
+ warnIfSSR("withContext");
4134
4438
  const previous = getValue();
4135
4439
  setValue(value);
4136
4440
  try {
4137
- return fn();
4441
+ const result = fn();
4442
+ if (_isDev8 && result !== null && typeof result?.then === "function") {
4443
+ devWarn(
4444
+ "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."
4445
+ );
4446
+ }
4447
+ return result;
4138
4448
  } finally {
4139
4449
  setValue(previous);
4140
4450
  }
@@ -4255,10 +4565,7 @@ function DynamicComponent(is) {
4255
4565
  } else {
4256
4566
  el = resolveComponent(target);
4257
4567
  }
4258
- for (const child of Array.from(container.childNodes)) {
4259
- dispose(child);
4260
- }
4261
- container.replaceChildren(el);
4568
+ replaceChildrenSafely(container, el);
4262
4569
  }
4263
4570
  const untrack = track2(render);
4264
4571
  registerDisposer(container, untrack);
@@ -4319,6 +4626,10 @@ function each(getArray, render, options) {
4319
4626
  let oldLen = 0;
4320
4627
  let nodeMap = /* @__PURE__ */ new Map();
4321
4628
  let workMap = /* @__PURE__ */ new Map();
4629
+ const pendingRows = [];
4630
+ const pendingItems = [];
4631
+ const pendingIndices = [];
4632
+ let pendingCount = 0;
4322
4633
  let newNodes = [];
4323
4634
  let newKeysBuf = [];
4324
4635
  let isStableBuf = new Uint8Array(0);
@@ -4328,8 +4639,10 @@ function each(getArray, render, options) {
4328
4639
  const keyIndexMap = /* @__PURE__ */ new Map();
4329
4640
  let initialized = false;
4330
4641
  let sentinelInserted = false;
4642
+ let rangeDisposed = false;
4331
4643
  const keyFn = options.key;
4332
4644
  const update = () => {
4645
+ if (rangeDisposed) return;
4333
4646
  const arr = getArray();
4334
4647
  const newLen = arr.length;
4335
4648
  const parent = anchor.parentNode;
@@ -4358,49 +4671,44 @@ function each(getArray, render, options) {
4358
4671
  }
4359
4672
  keyIndexMap.set(newKeys[i2], i2);
4360
4673
  }
4674
+ pendingCount = 0;
4361
4675
  for (let i2 = 0; i2 < newLen; i2++) {
4362
4676
  const key = newKeys[i2];
4363
4677
  const existing = nodeMap.get(key);
4364
- let node;
4678
+ let row;
4365
4679
  if (existing !== void 0) {
4366
- node = existing;
4680
+ row = existing;
4681
+ pendingRows[pendingCount] = row;
4682
+ pendingItems[pendingCount] = arr[i2];
4683
+ pendingIndices[pendingCount] = i2;
4684
+ pendingCount++;
4367
4685
  } else {
4368
- const itemKey = key;
4369
- const itemGetter = () => untracked2(() => getArray()[keyIndexMap.get(itemKey)]);
4370
- const indexGetter = () => keyIndexMap.get(itemKey);
4686
+ const [itemGetter, setItem] = signal(arr[i2]);
4687
+ const [indexGetter, setIndex] = signal(i2);
4688
+ let node;
4371
4689
  try {
4372
4690
  node = resolveNodeChild(render(itemGetter, indexGetter));
4373
4691
  } catch (err) {
4374
- if (_isDev9) {
4375
- devWarn(
4376
- `each: render threw for item at index ${i2} (key="${newKeys[i2]}"): ${err instanceof Error ? err.message : String(err)}`
4377
- );
4378
- }
4692
+ const key2 = newKeys[i2];
4379
4693
  node = document.createComment(`each:error:${i2}`);
4380
- const errorObj = err instanceof Error ? err : new Error(String(err));
4381
4694
  queueMicrotask(() => {
4382
- try {
4383
- const target = anchor.parentNode;
4384
- if (target?.dispatchEvent) {
4385
- target.dispatchEvent(
4386
- new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
4387
- );
4388
- } else if (_isDev9) {
4389
- devWarn(`each: error not surfaced \u2014 anchor detached: ${errorObj.message}`);
4390
- }
4391
- } catch {
4392
- }
4695
+ reportError(err, {
4696
+ phase: "render",
4697
+ name: `each row (key="${String(key2)}")`,
4698
+ node: anchor.parentNode ? anchor : void 0
4699
+ });
4393
4700
  });
4394
4701
  }
4702
+ row = { node, item: itemGetter, setItem, index: indexGetter, setIndex };
4395
4703
  }
4396
- workMap.set(key, node);
4397
- newNodes[i2] = node;
4704
+ workMap.set(key, row);
4705
+ newNodes[i2] = row.node;
4398
4706
  }
4399
- for (const [key, node] of nodeMap) {
4707
+ for (const [key, row] of nodeMap) {
4400
4708
  if (!workMap.has(key)) {
4401
- dispose(node);
4402
- if (node.parentNode) {
4403
- parent.removeChild(node);
4709
+ dispose(row.node);
4710
+ if (row.node.parentNode) {
4711
+ parent.removeChild(row.node);
4404
4712
  }
4405
4713
  }
4406
4714
  }
@@ -4409,6 +4717,8 @@ function each(getArray, render, options) {
4409
4717
  const tmp2 = nodeMap;
4410
4718
  nodeMap = workMap;
4411
4719
  workMap = tmp2;
4720
+ initialized = true;
4721
+ pendingCount = 0;
4412
4722
  return;
4413
4723
  }
4414
4724
  oldKeyIndex.clear();
@@ -4458,12 +4768,47 @@ function each(getArray, render, options) {
4458
4768
  nodeMap = workMap;
4459
4769
  workMap = tmp;
4460
4770
  initialized = true;
4771
+ flushRowCells();
4461
4772
  };
4773
+ function flushRowCells() {
4774
+ if (pendingCount === 0) return;
4775
+ const count = pendingCount;
4776
+ pendingCount = 0;
4777
+ batch(() => {
4778
+ for (let i2 = 0; i2 < count; i2++) {
4779
+ const row = pendingRows[i2];
4780
+ row.setItem(pendingItems[i2]);
4781
+ row.setIndex(pendingIndices[i2]);
4782
+ pendingRows[i2] = void 0;
4783
+ pendingItems[i2] = void 0;
4784
+ }
4785
+ });
4786
+ }
4462
4787
  const untrack = track2(update);
4463
- registerDisposer(anchor, untrack);
4788
+ const disposeRange = () => {
4789
+ if (rangeDisposed) return;
4790
+ rangeDisposed = true;
4791
+ untrack();
4792
+ for (const row of nodeMap.values()) {
4793
+ dispose(row.node);
4794
+ row.node.parentNode?.removeChild(row.node);
4795
+ }
4796
+ if (end.parentNode) end.parentNode.removeChild(end);
4797
+ sentinelInserted = false;
4798
+ nodeMap.clear();
4799
+ workMap.clear();
4800
+ keyIndexMap.clear();
4801
+ oldKeyIndex.clear();
4802
+ oldLen = 0;
4803
+ pendingRows.length = 0;
4804
+ pendingItems.length = 0;
4805
+ pendingIndices.length = 0;
4806
+ pendingCount = 0;
4807
+ };
4808
+ registerDisposer(anchor, disposeRange);
4464
4809
  if (!initialized) {
4465
4810
  queueMicrotask(() => {
4466
- if (!initialized && anchor.parentNode) {
4811
+ if (!initialized && !rangeDisposed && anchor.parentNode) {
4467
4812
  update();
4468
4813
  }
4469
4814
  });
@@ -4753,7 +5098,7 @@ function parseTemplate(strings) {
4753
5098
  return parseChildren();
4754
5099
  }
4755
5100
  function executeElement(tmpl, values) {
4756
- const el = tmpl.svg ? document.createElementNS(SVG_NS, tmpl.tag) : document.createElement(tmpl.tag);
5101
+ const el = tmpl.svg ? document.createElementNS(SVG_NS2, tmpl.tag) : document.createElement(tmpl.tag);
4757
5102
  for (let i2 = 0; i2 < tmpl.attrs.length; i2++) {
4758
5103
  const attr = tmpl.attrs[i2];
4759
5104
  switch (attr.t) {
@@ -4762,20 +5107,11 @@ function executeElement(tmpl, values) {
4762
5107
  break;
4763
5108
  case 1: {
4764
5109
  const name = attr.name;
4765
- const lname = name.toLowerCase();
4766
- if (isEventHandlerAttr(name)) break;
4767
5110
  const val = values[attr.idx];
4768
5111
  if (typeof val === "function") {
4769
5112
  registerDisposer(el, bindAttribute(el, name, val));
4770
- } else if (val != null) {
4771
- const str = String(val);
4772
- if (lname === "srcset") {
4773
- el.setAttribute(name, sanitizeSrcset(str));
4774
- } else if (isUrlAttribute(lname)) {
4775
- el.setAttribute(name, sanitizeUrl(str));
4776
- } else {
4777
- el.setAttribute(name, str);
4778
- }
5113
+ } else {
5114
+ setSafeAttribute(el, name, val, { syncValueProperty: false, label: "html" });
4779
5115
  }
4780
5116
  break;
4781
5117
  }
@@ -4785,14 +5121,7 @@ function executeElement(tmpl, values) {
4785
5121
  const ev = values[attr.exprs[j]];
4786
5122
  val += (ev == null ? "" : String(ev)) + attr.statics[j + 1];
4787
5123
  }
4788
- const lname2 = attr.name.toLowerCase();
4789
- if (lname2 === "srcset") {
4790
- el.setAttribute(attr.name, sanitizeSrcset(val));
4791
- } else if (isUrlAttribute(lname2)) {
4792
- el.setAttribute(attr.name, sanitizeUrl(val));
4793
- } else {
4794
- el.setAttribute(attr.name, val);
4795
- }
5124
+ setSafeAttribute(el, attr.name, val, { syncValueProperty: false, label: "html" });
4796
5125
  break;
4797
5126
  }
4798
5127
  case 3: {
@@ -5018,20 +5347,12 @@ function Portal(nodes, target) {
5018
5347
  portalContent = nodes();
5019
5348
  container.appendChild(portalContent);
5020
5349
  } catch (err) {
5021
- if (typeof console !== "undefined") {
5022
- console.error("[Portal] Render error:", err);
5023
- }
5024
- const errorObj = err instanceof Error ? err : new Error(String(err));
5025
5350
  queueMicrotask(() => {
5026
- try {
5027
- const target2 = anchor.parentNode;
5028
- if (target2?.dispatchEvent) {
5029
- target2.dispatchEvent(
5030
- new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
5031
- );
5032
- }
5033
- } catch {
5034
- }
5351
+ reportError(err, {
5352
+ phase: "render",
5353
+ name: "Portal",
5354
+ node: anchor.parentNode ? anchor : void 0
5355
+ });
5035
5356
  });
5036
5357
  }
5037
5358
  });
@@ -5128,9 +5449,10 @@ function array(initial = []) {
5128
5449
  function reactiveArray(initial = []) {
5129
5450
  let data2 = [...initial];
5130
5451
  let snapshot = null;
5131
- const signal2 = {};
5452
+ const signal2 = { __v: 0 };
5132
5453
  function notify() {
5133
5454
  snapshot = null;
5455
+ signal2.__v++;
5134
5456
  if (!enqueueBatchedSignal(signal2)) {
5135
5457
  notifySubscribers2(signal2);
5136
5458
  }
@@ -5250,17 +5572,34 @@ function asyncDerived(factory, initial) {
5250
5572
  const [error, setError] = signal(null);
5251
5573
  const [tick, setTick] = signal(0);
5252
5574
  let runId = 0;
5253
- effect(() => {
5575
+ let disposed = false;
5576
+ let controller = null;
5577
+ const abortInFlight = () => {
5578
+ if (controller === null) return;
5579
+ const c = controller;
5580
+ controller = null;
5581
+ try {
5582
+ c.abort();
5583
+ } catch {
5584
+ }
5585
+ };
5586
+ const stopEffect = effect(() => {
5254
5587
  tick();
5588
+ if (disposed) return;
5589
+ abortInFlight();
5255
5590
  const currentRun = ++runId;
5591
+ const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
5592
+ controller = ac;
5256
5593
  batch(() => {
5257
5594
  setLoading(true);
5258
5595
  setError(null);
5259
5596
  });
5597
+ const stale = () => disposed || currentRun !== runId;
5260
5598
  let promise;
5261
5599
  try {
5262
- promise = factory();
5600
+ promise = factory({ signal: ac ? ac.signal : void 0 });
5263
5601
  } catch (err) {
5602
+ if (stale()) return;
5264
5603
  batch(() => {
5265
5604
  setError(err);
5266
5605
  setLoading(false);
@@ -5269,14 +5608,16 @@ function asyncDerived(factory, initial) {
5269
5608
  }
5270
5609
  promise.then(
5271
5610
  (result) => {
5272
- if (currentRun !== runId) return;
5611
+ if (stale()) return;
5612
+ controller = null;
5273
5613
  batch(() => {
5274
5614
  setValue(result);
5275
5615
  setLoading(false);
5276
5616
  });
5277
5617
  },
5278
5618
  (err) => {
5279
- if (currentRun !== runId) return;
5619
+ if (stale()) return;
5620
+ controller = null;
5280
5621
  batch(() => {
5281
5622
  setError(err);
5282
5623
  setLoading(false);
@@ -5288,7 +5629,16 @@ function asyncDerived(factory, initial) {
5288
5629
  value,
5289
5630
  loading,
5290
5631
  error,
5291
- refresh: () => setTick((n) => n + 1)
5632
+ refresh: () => {
5633
+ if (disposed) return;
5634
+ setTick((n) => n + 1);
5635
+ },
5636
+ dispose: () => {
5637
+ if (disposed) return;
5638
+ disposed = true;
5639
+ stopEffect();
5640
+ abortInFlight();
5641
+ }
5292
5642
  };
5293
5643
  }
5294
5644
 
@@ -5408,6 +5758,21 @@ function derived(getter, options) {
5408
5758
  }, markDirty);
5409
5759
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
5410
5760
  let evaluating = false;
5761
+ const validate = () => {
5762
+ if (!cs._d) return;
5763
+ const oldValue = cs._v;
5764
+ evaluating = true;
5765
+ try {
5766
+ retrack2(recompute, markDirty);
5767
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
5768
+ } finally {
5769
+ evaluating = false;
5770
+ }
5771
+ if (hook && !Object.is(oldValue, cs._v)) {
5772
+ hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
5773
+ }
5774
+ };
5775
+ cs._validate = validate;
5411
5776
  function computedGetter() {
5412
5777
  if (evaluating) {
5413
5778
  throw new Error(
@@ -5415,32 +5780,11 @@ function derived(getter, options) {
5415
5780
  );
5416
5781
  }
5417
5782
  if (isTrackingSuspended2()) {
5418
- if (cs._d) {
5419
- const prev = cs._v;
5420
- evaluating = true;
5421
- try {
5422
- retrack2(recompute, markDirty);
5423
- if (!Object.is(prev, cs._v)) cs.__v++;
5424
- } finally {
5425
- evaluating = false;
5426
- }
5427
- }
5783
+ if (cs._d) validate();
5428
5784
  return cs._v;
5429
5785
  }
5786
+ if (cs._d) validate();
5430
5787
  recordDependency2(cs);
5431
- if (cs._d) {
5432
- const oldValue = cs._v;
5433
- evaluating = true;
5434
- try {
5435
- retrack2(recompute, markDirty);
5436
- if (!Object.is(oldValue, cs._v)) cs.__v++;
5437
- } finally {
5438
- evaluating = false;
5439
- }
5440
- if (hook && oldValue !== cs._v) {
5441
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
5442
- }
5443
- }
5444
5788
  return cs._v;
5445
5789
  }
5446
5790
  if (debugName) {
@@ -5630,6 +5974,28 @@ function strictEffect(fn) {
5630
5974
  }
5631
5975
 
5632
5976
  // src/platform/enhance.ts
5977
+ var ENHANCED_ATTR = "data-sibu-enhanced";
5978
+ var enhancementOwner = /* @__PURE__ */ new WeakMap();
5979
+ function drainTeardowns(teardowns, label2) {
5980
+ let executed = 0;
5981
+ while (teardowns.length > 0) {
5982
+ const batch2 = teardowns.splice(0);
5983
+ for (let i2 = 0; i2 < batch2.length; i2++) {
5984
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
5985
+ const remaining = batch2.length - i2 + teardowns.length;
5986
+ teardowns.length = 0;
5987
+ reportDrainRunaway(label2, executed, remaining);
5988
+ return;
5989
+ }
5990
+ executed++;
5991
+ try {
5992
+ batch2[i2]();
5993
+ } catch (err) {
5994
+ if (typeof console !== "undefined") console.error(`[SibuJS ${label2}] teardown error:`, err);
5995
+ }
5996
+ }
5997
+ }
5998
+ }
5633
5999
  function resolveTarget(root, target) {
5634
6000
  if (target == null) return root;
5635
6001
  if (typeof target !== "string") return target;
@@ -5672,7 +6038,7 @@ function enhance(target, setup) {
5672
6038
  return () => {
5673
6039
  };
5674
6040
  }
5675
- if (root.getAttribute("data-sibu-enhanced") === "true") {
6041
+ if (root.getAttribute(ENHANCED_ATTR) === "true") {
5676
6042
  if (isDev() && typeof console !== "undefined") {
5677
6043
  console.warn("[SibuJS enhance] element is already enhanced; ignoring re-enhance.", root);
5678
6044
  }
@@ -5680,6 +6046,7 @@ function enhance(target, setup) {
5680
6046
  };
5681
6047
  }
5682
6048
  const teardowns = [];
6049
+ const owner = /* @__PURE__ */ Symbol("sibujs.enhance");
5683
6050
  let disposed = false;
5684
6051
  const bind = (target_, fn) => {
5685
6052
  const el = resolveTarget(root, target_);
@@ -5726,11 +6093,7 @@ function enhance(target, setup) {
5726
6093
  effect(() => {
5727
6094
  const v = value();
5728
6095
  const next = v == null ? null : String(v);
5729
- if (next === null) {
5730
- if (el.hasAttribute(name)) el.removeAttribute(name);
5731
- } else if (el.getAttribute(name) !== next) {
5732
- el.setAttribute(name, next);
5733
- }
6096
+ setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()" });
5734
6097
  })
5735
6098
  );
5736
6099
  });
@@ -5757,10 +6120,10 @@ function enhance(target, setup) {
5757
6120
  });
5758
6121
  });
5759
6122
  },
5760
- model: (t, state, options) => {
6123
+ model: (t, state2, options) => {
5761
6124
  bind(t, (el) => {
5762
6125
  const control = el;
5763
- const [get, set] = state;
6126
+ const [get, set] = state2;
5764
6127
  const evt = options?.event ?? (control instanceof HTMLSelectElement || control instanceof HTMLInputElement && (control.type === "checkbox" || control.type === "radio") ? "change" : "input");
5765
6128
  teardowns.push(
5766
6129
  effect(() => {
@@ -5776,29 +6139,50 @@ function enhance(target, setup) {
5776
6139
  teardowns.push(fn);
5777
6140
  }
5778
6141
  };
5779
- const extra = setup(ctx);
6142
+ let extra;
6143
+ try {
6144
+ extra = setup(ctx);
6145
+ } catch (err) {
6146
+ drainTeardowns(teardowns, "enhance");
6147
+ throw err;
6148
+ }
5780
6149
  if (typeof extra === "function") teardowns.push(extra);
5781
6150
  const dispose2 = () => {
5782
6151
  if (disposed) return;
5783
6152
  disposed = true;
5784
- for (const fn of teardowns.splice(0)) {
5785
- try {
5786
- fn();
5787
- } catch (err) {
5788
- if (typeof console !== "undefined") console.error("[SibuJS enhance] teardown error:", err);
5789
- }
6153
+ if (enhancementOwner.get(root) === owner) {
6154
+ enhancementOwner.delete(root);
6155
+ root.removeAttribute(ENHANCED_ATTR);
5790
6156
  }
6157
+ unregisterDisposer(root, dispose2);
6158
+ drainTeardowns(teardowns, "enhance");
5791
6159
  };
5792
6160
  registerDisposer(root, dispose2);
5793
- root.setAttribute("data-sibu-enhanced", "true");
6161
+ enhancementOwner.set(root, owner);
6162
+ root.setAttribute(ENHANCED_ATTR, "true");
5794
6163
  return dispose2;
5795
6164
  }
5796
6165
  function enhanceAll(selector, setup) {
5797
6166
  if (typeof document === "undefined") return () => {
5798
6167
  };
5799
- const disposers = Array.from(document.querySelectorAll(selector)).map((el) => enhance(el, setup));
6168
+ const disposers = [];
6169
+ try {
6170
+ for (const el of Array.from(document.querySelectorAll(selector))) {
6171
+ disposers.push(enhance(el, setup));
6172
+ }
6173
+ } catch (err) {
6174
+ for (let i2 = disposers.length - 1; i2 >= 0; i2--) {
6175
+ try {
6176
+ disposers[i2]();
6177
+ } catch (rollbackErr) {
6178
+ if (typeof console !== "undefined") console.error("[SibuJS enhanceAll] rollback error:", rollbackErr);
6179
+ }
6180
+ }
6181
+ disposers.length = 0;
6182
+ throw err;
6183
+ }
5800
6184
  return () => {
5801
- for (const d of disposers) d();
6185
+ for (const d of disposers.splice(0)) d();
5802
6186
  };
5803
6187
  }
5804
6188
 
@@ -5838,6 +6222,7 @@ function mountIslands(root = typeof document !== "undefined" ? document : null,
5838
6222
  };
5839
6223
  const cancels = [];
5840
6224
  const disposers = [];
6225
+ let torndown = false;
5841
6226
  for (const el of Array.from(root.querySelectorAll("[data-sibu-island]"))) {
5842
6227
  if (el.getAttribute("data-sibu-enhanced") === "true") continue;
5843
6228
  const name = el.getAttribute("data-sibu-island") ?? "";
@@ -5851,17 +6236,23 @@ function mountIslands(root = typeof document !== "undefined" ? document : null,
5851
6236
  }
5852
6237
  let activated = false;
5853
6238
  const activate = () => {
5854
- if (activated) return;
6239
+ if (activated || torndown) return;
5855
6240
  activated = true;
5856
6241
  const reg = registry.get(name);
5857
6242
  if (!reg) return;
5858
6243
  resolveSetup(reg).then((setup) => {
6244
+ if (torndown) return;
5859
6245
  if (!setup) {
5860
6246
  if (typeof console !== "undefined") console.warn(`[SibuJS islands] "${name}" loader produced no setup.`);
5861
6247
  return;
5862
6248
  }
5863
6249
  try {
5864
- disposers.push(enhance(el, setup));
6250
+ const disposeIsland = enhance(el, setup);
6251
+ if (torndown) {
6252
+ disposeIsland();
6253
+ return;
6254
+ }
6255
+ disposers.push(disposeIsland);
5865
6256
  el.setAttribute("data-sibu-hydrated", "true");
5866
6257
  } catch (err) {
5867
6258
  if (typeof console !== "undefined") console.error(`[SibuJS islands] "${name}" failed to mount:`, err);
@@ -5874,6 +6265,7 @@ function mountIslands(root = typeof document !== "undefined" ? document : null,
5874
6265
  cancels.push(schedule(strategy, el, activate, options));
5875
6266
  }
5876
6267
  return () => {
6268
+ torndown = true;
5877
6269
  for (const c of cancels.splice(0)) c();
5878
6270
  for (const d of disposers.splice(0)) d();
5879
6271
  };