sibujs 4.10.1 → 4.12.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 (79) hide show
  1. package/README.md +2 -2
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.js +4 -4
  4. package/dist/build.cjs +19 -1
  5. package/dist/build.js +13 -12
  6. package/dist/cdn.dev.global.js +12 -12
  7. package/dist/cdn.full.dev.global.js +13 -13
  8. package/dist/cdn.full.global.js +12 -12
  9. package/dist/cdn.global.js +12 -12
  10. package/dist/{chunk-FC6KCJSA.js → chunk-2F4PSJ5Z.js} +162 -67
  11. package/dist/{chunk-JZMLRF2G.js → chunk-653KGQS2.js} +3 -3
  12. package/dist/chunk-AM6DZLBM.js +181 -0
  13. package/dist/{chunk-NROLUUCO.js → chunk-B2Y5BJSP.js} +1 -1
  14. package/dist/{chunk-WHZKRSHQ.js → chunk-CI4W6XAU.js} +4 -4
  15. package/dist/{chunk-GEQNWNYS.js → chunk-DFZL3AX7.js} +6 -6
  16. package/dist/{chunk-6DYRY2NF.js → chunk-EMB5PG7V.js} +1 -1
  17. package/dist/{chunk-3ACAAQIN.js → chunk-F2UWOZ6J.js} +4 -0
  18. package/dist/{chunk-DA2LJXK3.js → chunk-HXT2RNH5.js} +1 -1
  19. package/dist/{chunk-BNZDDNBR.js → chunk-IHA62NGJ.js} +1 -1
  20. package/dist/{chunk-42REZXE4.js → chunk-LEUEKK4G.js} +2 -2
  21. package/dist/{chunk-CYUU3XXG.js → chunk-LXBMVGVB.js} +3 -3
  22. package/dist/{chunk-LYM6TT2R.js → chunk-LYTUTXQP.js} +4 -4
  23. package/dist/{chunk-2D47RVVK.js → chunk-NAVLCP4S.js} +4 -4
  24. package/dist/{chunk-MJZUXQHR.js → chunk-NB7TFSJE.js} +5 -5
  25. package/dist/{chunk-ADJEMTRC.js → chunk-QLG5IK7F.js} +4 -4
  26. package/dist/{chunk-LK3VK47W.js → chunk-RK7EPQPG.js} +1 -1
  27. package/dist/{chunk-YNE4FSVX.js → chunk-T5O2CUHV.js} +1 -1
  28. package/dist/{chunk-GVVZPMA5.js → chunk-WFRQ6D7H.js} +1 -1
  29. package/dist/{chunk-2T73SWKF.js → chunk-WH4U2VUE.js} +5 -3
  30. package/dist/{chunk-DS6PFIZK.js → chunk-XX4BRMWF.js} +15 -3
  31. package/dist/{chunk-HZQEL575.js → chunk-ZJCHY2IB.js} +1 -1
  32. package/dist/{chunk-CID4BYBF.js → chunk-ZPY2UAKM.js} +3 -3
  33. package/dist/{chunk-CZINTERN.js → chunk-ZTYCCBU3.js} +19 -11
  34. package/dist/data.cjs +1 -1
  35. package/dist/data.js +5 -5
  36. package/dist/devtools.cjs +1 -1
  37. package/dist/devtools.js +4 -4
  38. package/dist/ecosystem.cjs +9 -1
  39. package/dist/ecosystem.d.cts +1 -1
  40. package/dist/ecosystem.d.ts +1 -1
  41. package/dist/ecosystem.js +8 -8
  42. package/dist/extras.cjs +368 -154
  43. package/dist/extras.d.cts +2 -2
  44. package/dist/extras.d.ts +2 -2
  45. package/dist/extras.js +23 -22
  46. package/dist/index.cjs +19 -1
  47. package/dist/index.d.cts +91 -90
  48. package/dist/index.d.ts +91 -90
  49. package/dist/index.js +13 -12
  50. package/dist/motion.cjs +1 -1
  51. package/dist/motion.js +3 -3
  52. package/dist/patterns.cjs +1 -1
  53. package/dist/patterns.js +5 -5
  54. package/dist/performance.cjs +1 -1
  55. package/dist/performance.d.cts +2 -2
  56. package/dist/performance.d.ts +2 -2
  57. package/dist/performance.js +7 -6
  58. package/dist/plugins.cjs +482 -120
  59. package/dist/plugins.d.cts +20 -6
  60. package/dist/plugins.d.ts +20 -6
  61. package/dist/plugins.js +153 -23
  62. package/dist/{ssr-C8hgIAw7.d.cts → ssr-Db0Sgdu3.d.cts} +7 -4
  63. package/dist/{ssr-C8hgIAw7.d.ts → ssr-Db0Sgdu3.d.ts} +7 -4
  64. package/dist/{ssr-5OWAH3U2.js → ssr-VBM6J5SJ.js} +4 -2
  65. package/dist/ssr.cjs +346 -128
  66. package/dist/ssr.d.cts +1 -1
  67. package/dist/ssr.d.ts +1 -1
  68. package/dist/ssr.js +10 -9
  69. package/dist/{tagFactory-BL2fymez.d.ts → tagFactory-Bnd_Nph9.d.ts} +1 -1
  70. package/dist/{tagFactory-8qL9LCIx.d.cts → tagFactory-DkytnJws.d.cts} +1 -1
  71. package/dist/testing.cjs +1 -1
  72. package/dist/testing.js +2 -2
  73. package/dist/ui.cjs +251 -24
  74. package/dist/ui.d.cts +111 -1
  75. package/dist/ui.d.ts +111 -1
  76. package/dist/ui.js +130 -8
  77. package/dist/widgets.cjs +1 -1
  78. package/dist/widgets.js +6 -6
  79. package/package.json +1 -1
package/dist/plugins.cjs CHANGED
@@ -699,6 +699,9 @@ function contextualAttributeRefusal(el, name, value, origin) {
699
699
  if (SVG_ANIMATION_ELEMENTS.has(local) && el.namespaceURI === SVG_NS) return animationRefusal(name, value);
700
700
  return ContextualRefusal.None;
701
701
  }
702
+ function isRefusedAnimationAttribute(el, name, value) {
703
+ return SVG_ANIMATION_ELEMENTS.has(el.localName) && el.namespaceURI === SVG_NS && animationRefusal(name, value) !== ContextualRefusal.None;
704
+ }
702
705
  function settleAttributeProvenance(el, name, origin) {
703
706
  if (el.localName !== "link" || canonicalAttrName(name) !== "href") return;
704
707
  if (origin !== "static" && el.hasAttribute("href")) runtimeHrefLinks.add(el);
@@ -966,6 +969,140 @@ var init_ssr_context = __esm({
966
969
  }
967
970
  });
968
971
 
972
+ // src/utils/globalSingleton.ts
973
+ function globalSingleton(key, create) {
974
+ const g2 = globalThis;
975
+ return g2[key] ?? (g2[key] = create());
976
+ }
977
+ var init_globalSingleton = __esm({
978
+ "src/utils/globalSingleton.ts"() {
979
+ "use strict";
980
+ }
981
+ });
982
+
983
+ // src/utils/embedPolicy.ts
984
+ function currentEmbedAllowlist() {
985
+ const scoped = getRequestStore()?.caches?.get(REQUEST_CACHE)?.get("allowed");
986
+ return scoped ?? state2.allowed;
987
+ }
988
+ function resolveEmbedSrc(value, allowed = currentEmbedAllowlist()) {
989
+ if (typeof value !== "string" || sanitizeUrl(value) === "") return null;
990
+ let url;
991
+ try {
992
+ url = new URL(value);
993
+ } catch {
994
+ return null;
995
+ }
996
+ if (url.protocol !== "https:" || url.username || url.password) return null;
997
+ if (typeof location !== "undefined" && url.origin === location.origin) return null;
998
+ for (const entry of allowed) {
999
+ if (url.origin !== entry.origin) continue;
1000
+ const path2 = entry.path;
1001
+ if (path2 === "" || url.pathname === path2 || url.pathname.startsWith(path2.endsWith("/") ? path2 : `${path2}/`)) {
1002
+ return url.href;
1003
+ }
1004
+ }
1005
+ return null;
1006
+ }
1007
+ function narrowEmbedSandbox(value) {
1008
+ const out = [];
1009
+ for (const token of value.toLowerCase().split(/[\t\n\f\r ]+/)) {
1010
+ if (token && DEFAULT_EMBED_SANDBOX.includes(token) && !out.includes(token)) out.push(token);
1011
+ }
1012
+ return out.join(" ");
1013
+ }
1014
+ function narrowEmbedAllow(value) {
1015
+ const granted = [];
1016
+ const denied = [];
1017
+ for (const directive of value.split(";")) {
1018
+ const tokens = directive.trim().split(/[\t\n\f\r ]+/);
1019
+ const feature = tokens[0];
1020
+ if (!feature) continue;
1021
+ const list = tokens.slice(1).map((token) => token.toLowerCase());
1022
+ const grantsSrc = (list.length === 0 || list.includes("'src'") || list.includes("*")) && !list.includes("'none'");
1023
+ if (!grantsSrc) denied.push(feature);
1024
+ else if (EMBED_FEATURES.has(feature) && !granted.includes(feature)) granted.push(feature);
1025
+ }
1026
+ return granted.filter((feature) => !denied.includes(feature)).join("; ");
1027
+ }
1028
+ function getEmbedRecord(frame) {
1029
+ return state2.frames.get(frame);
1030
+ }
1031
+ function intersect(current, permitted, separator) {
1032
+ const allowed = permitted.split(separator);
1033
+ return current.split(separator).filter((token) => token && allowed.includes(token)).join(separator);
1034
+ }
1035
+ function embedSerializedAttribute(record, seen, name, value) {
1036
+ switch (name) {
1037
+ case "src":
1038
+ return resolveEmbedSrc(value, record.allowed);
1039
+ case "title":
1040
+ if (!value.trim()) return null;
1041
+ seen.title = true;
1042
+ return value;
1043
+ case "sandbox":
1044
+ seen.sandbox = true;
1045
+ return intersect(narrowEmbedSandbox(value), record.sandbox, " ");
1046
+ case "allow": {
1047
+ const allow = intersect(narrowEmbedAllow(value), record.allow, "; ");
1048
+ if (allow.split("; ").includes("fullscreen")) seen.fullscreen = true;
1049
+ return allow || null;
1050
+ }
1051
+ case "referrerpolicy":
1052
+ seen.referrer = true;
1053
+ return value.trim().toLowerCase() === "no-referrer" ? "no-referrer" : record.referrer;
1054
+ case "loading":
1055
+ return value === "eager" ? "eager" : "lazy";
1056
+ case "id":
1057
+ case "class":
1058
+ case "style":
1059
+ case "width":
1060
+ case "height":
1061
+ return value;
1062
+ default:
1063
+ return name.startsWith("data-") || name.startsWith("aria-") ? value : null;
1064
+ }
1065
+ }
1066
+ function embedSerializationTail(record, seen) {
1067
+ if (!seen.title) return null;
1068
+ let tail = "";
1069
+ if (!seen.sandbox) tail += ` sandbox="${record.sandbox}"`;
1070
+ if (!seen.referrer) tail += ` referrerpolicy="${record.referrer}"`;
1071
+ if (seen.fullscreen) tail += ' allowfullscreen=""';
1072
+ return tail;
1073
+ }
1074
+ var state2, REQUEST_CACHE, DEFAULT_EMBED_SANDBOX, EMBED_FEATURES;
1075
+ var init_embedPolicy = __esm({
1076
+ "src/utils/embedPolicy.ts"() {
1077
+ "use strict";
1078
+ init_ssr_context();
1079
+ init_globalSingleton();
1080
+ init_sanitize();
1081
+ state2 = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.embed.v2"), () => ({
1082
+ allowed: [],
1083
+ frames: /* @__PURE__ */ new WeakMap()
1084
+ }));
1085
+ REQUEST_CACHE = "embed";
1086
+ DEFAULT_EMBED_SANDBOX = [
1087
+ "allow-scripts",
1088
+ "allow-same-origin",
1089
+ "allow-presentation",
1090
+ "allow-popups",
1091
+ "allow-popups-to-escape-sandbox"
1092
+ ];
1093
+ EMBED_FEATURES = /* @__PURE__ */ new Set([
1094
+ "accelerometer",
1095
+ "autoplay",
1096
+ "clipboard-write",
1097
+ "encrypted-media",
1098
+ "fullscreen",
1099
+ "gyroscope",
1100
+ "picture-in-picture",
1101
+ "web-share"
1102
+ ]);
1103
+ }
1104
+ });
1105
+
969
1106
  // src/utils/guards.ts
970
1107
  function isUnsafeKey(key) {
971
1108
  return UNSAFE_KEYS.has(key);
@@ -1133,69 +1270,129 @@ function childNodesOf(node) {
1133
1270
  function getAttributeOf(el, name) {
1134
1271
  return nativeAccessors().getAttribute.call(el, name);
1135
1272
  }
1136
- function serializeOpenTag(element, tag) {
1273
+ function isBreakout(lowerName, element) {
1274
+ if (BREAKOUT_TAGS.has(lowerName)) return true;
1275
+ return lowerName === "font" && (element.hasAttribute("color") || element.hasAttribute("face") || element.hasAttribute("size"));
1276
+ }
1277
+ function fitsParseContext(ns, lowerName, element, context) {
1278
+ if (ns === SVG_NS4) {
1279
+ if (context === 1 /* Svg */) return !isBreakout(lowerName, element);
1280
+ return lowerName === "svg" && (context === 0 /* Html */ || context === 3 /* Annotation */);
1281
+ }
1282
+ if (ns === MATHML_NS) {
1283
+ if (context === 2 /* Math */ || context === 3 /* Annotation */) return !isBreakout(lowerName, element);
1284
+ return lowerName === "math" && context === 0 /* Html */;
1285
+ }
1286
+ return context === 0 /* Html */;
1287
+ }
1288
+ function childParseContext(ns, lowerName, element) {
1289
+ if (ns === SVG_NS4) {
1290
+ return lowerName === "foreignobject" || lowerName === "desc" || lowerName === "title" ? 0 /* Html */ : 1 /* Svg */;
1291
+ }
1292
+ if (ns === MATHML_NS) {
1293
+ if (lowerName === "mi" || lowerName === "mo" || lowerName === "mn" || lowerName === "ms" || lowerName === "mtext") {
1294
+ return 0 /* Html */;
1295
+ }
1296
+ if (lowerName === "annotation-xml") {
1297
+ const encoding = (element.getAttribute("encoding") ?? "").toLowerCase();
1298
+ return encoding === "text/html" || encoding === "application/xhtml+xml" ? 0 /* Html */ : 3 /* Annotation */;
1299
+ }
1300
+ return 2 /* Math */;
1301
+ }
1302
+ return 0 /* Html */;
1303
+ }
1304
+ function foreignAttrName(attr) {
1305
+ const ns = attr.namespaceURI;
1306
+ if (ns === XLINK_NS2) return `xlink:${attr.localName}`;
1307
+ if (ns === XML_NS2) return `xml:${attr.localName}`;
1308
+ if (ns === XMLNS_NS) return attr.localName === "xmlns" ? "xmlns" : `xmlns:${attr.localName}`;
1309
+ return attr.name;
1310
+ }
1311
+ function serializeOpenTag(element, tag, foreign) {
1137
1312
  let html2 = `<${tag}`;
1138
- const meta2 = tag === "meta" ? /* @__PURE__ */ new Map() : null;
1313
+ const meta2 = tag === "meta" && !foreign ? /* @__PURE__ */ new Map() : null;
1314
+ const embed2 = tag === "iframe" && !foreign ? getEmbedRecord(element) : void 0;
1315
+ const embedSeen = embed2 ? { sandbox: false, referrer: false, title: false, fullscreen: false } : null;
1139
1316
  const attrs = attributesOf(element);
1140
1317
  for (let i2 = 0; i2 < attrs.length; i2++) {
1141
1318
  const attr = attrs[i2];
1142
- const rawName = attr.name;
1319
+ const rawName = foreign ? foreignAttrName(attr) : attr.name;
1143
1320
  if (!isSafeAttrName(rawName)) continue;
1144
1321
  if (isEventHandlerAttr(rawName)) continue;
1145
1322
  if (isForbiddenSsrAttr(rawName)) continue;
1146
- const value = resolveAttributeValue(rawName.toLowerCase(), attr.value);
1323
+ const lowerName = rawName.toLowerCase();
1324
+ let value = resolveAttributeValue(lowerName, attr.value);
1147
1325
  if (value === null) continue;
1326
+ if (foreign && isRefusedAnimationAttribute(element, rawName, value)) continue;
1327
+ if (embed2 && embedSeen) {
1328
+ value = embedSerializedAttribute(embed2, embedSeen, lowerName, value);
1329
+ if (value === null) continue;
1330
+ }
1148
1331
  meta2?.set(rawName, value);
1149
1332
  html2 += ` ${rawName}="${escapeAttr(value)}"`;
1150
1333
  }
1151
1334
  if (meta2 && isForbiddenMetaSnapshot(meta2)) return null;
1335
+ if (embed2 && embedSeen) {
1336
+ const tail = embedSerializationTail(embed2, embedSeen);
1337
+ if (tail === null) return null;
1338
+ html2 += tail;
1339
+ }
1152
1340
  if (!getAttributeOf(element, "data-sibu-hydrate")) {
1153
1341
  html2 += ` data-sibu-ssr="true"`;
1154
1342
  }
1155
1343
  return html2;
1156
1344
  }
1345
+ function serializeNode(node, context) {
1346
+ if (node.nodeType === 3) return escapeHtml(node.textContent || "");
1347
+ if (node.nodeType === 8) return `<!--${safeCommentText(node.textContent || "")}-->`;
1348
+ let foreign = false;
1349
+ if (!(node instanceof HTMLElement)) {
1350
+ const ns2 = node.nodeType === 1 ? node.namespaceURI : null;
1351
+ if (ns2 !== SVG_NS4 && ns2 !== MATHML_NS) return escapeHtml(node.textContent || "");
1352
+ foreign = true;
1353
+ }
1354
+ const element = node;
1355
+ const tag = foreign ? element.localName : tagNameOf(element).toLowerCase();
1356
+ if (isCodeTextElement(tag)) return DEV ? `<!--ssr:${tag}-stripped-->` : "";
1357
+ if (!/^[a-z][a-z0-9-]*$/i.test(tag)) return DEV ? "<!--ssr:invalid-tag-->" : "";
1358
+ const ns = foreign ? element.namespaceURI : null;
1359
+ const lowerName = tag.toLowerCase();
1360
+ if (!fitsParseContext(ns, lowerName, element, context)) return DEV ? `<!--ssr:${tag}-misplaced-->` : "";
1361
+ const openTag = serializeOpenTag(element, tag, foreign);
1362
+ if (openTag === null) return DEV ? `<!--ssr:${tag}-stripped-->` : "";
1363
+ const children = childNodesOf(element);
1364
+ if (foreign ? children.length === 0 : VOID_ELEMENTS.has(tag)) return `${openTag} />`;
1365
+ return {
1366
+ open: `${openTag}>`,
1367
+ close: `</${tag}>`,
1368
+ children: Array.from(children),
1369
+ context: childParseContext(ns, lowerName, element)
1370
+ };
1371
+ }
1157
1372
  function renderToString(element) {
1373
+ return renderNodeToString(element, 0 /* Html */);
1374
+ }
1375
+ function renderNodeToString(element, context) {
1158
1376
  if (element instanceof DocumentFragment) {
1159
1377
  return Array.from(element.childNodes).map((child) => {
1160
1378
  try {
1161
- return renderToString(child);
1379
+ return renderNodeToString(child, context);
1162
1380
  } catch (err) {
1163
1381
  return ssrErrorComment(err);
1164
1382
  }
1165
1383
  }).join("");
1166
1384
  }
1167
- if (element.nodeType === 3) {
1168
- return escapeHtml(element.textContent || "");
1169
- }
1170
- if (element.nodeType === 8) {
1171
- return `<!--${safeCommentText(element.textContent || "")}-->`;
1172
- }
1173
- if (!(element instanceof HTMLElement)) {
1174
- return escapeHtml(element.textContent || "");
1175
- }
1176
- const tag = tagNameOf(element).toLowerCase();
1177
- if (isCodeTextElement(tag)) {
1178
- return DEV ? `<!--ssr:${tag}-stripped-->` : "";
1179
- }
1180
- if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
1181
- return DEV ? "<!--ssr:invalid-tag-->" : "";
1182
- }
1183
- const openTag = serializeOpenTag(element, tag);
1184
- if (openTag === null) return DEV ? `<!--ssr:${tag}-stripped-->` : "";
1185
- let html2 = openTag;
1186
- if (VOID_ELEMENTS.has(tag)) {
1187
- return `${html2} />`;
1188
- }
1189
- html2 += ">";
1190
- for (const child of Array.from(childNodesOf(element))) {
1385
+ const frame = serializeNode(element, context);
1386
+ if (typeof frame === "string") return frame;
1387
+ let html2 = frame.open;
1388
+ for (const child of frame.children) {
1191
1389
  try {
1192
- html2 += renderToString(child);
1390
+ html2 += renderNodeToString(child, frame.context);
1193
1391
  } catch (err) {
1194
1392
  html2 += ssrErrorComment(err);
1195
1393
  }
1196
1394
  }
1197
- html2 += `</${tag}>`;
1198
- return html2;
1395
+ return html2 + frame.close;
1199
1396
  }
1200
1397
  function hydrate(component, container, options = {}) {
1201
1398
  const clientTree = component();
@@ -1378,56 +1575,34 @@ function renderToDocument(component, options = {}) {
1378
1575
  </body>
1379
1576
  </html>`;
1380
1577
  }
1381
- async function* renderToStream(element) {
1578
+ function renderToStream(element) {
1579
+ return streamNode(element, 0 /* Html */);
1580
+ }
1581
+ async function* streamNode(element, context) {
1382
1582
  if (element instanceof DocumentFragment) {
1383
1583
  for (const child of Array.from(element.childNodes)) {
1384
1584
  try {
1385
- yield* renderToStream(child);
1585
+ yield* streamNode(child, context);
1386
1586
  } catch (err) {
1387
1587
  yield ssrErrorComment(err);
1388
1588
  }
1389
1589
  }
1390
1590
  return;
1391
1591
  }
1392
- if (element.nodeType === 3) {
1393
- yield escapeHtml(element.textContent || "");
1394
- return;
1395
- }
1396
- if (element.nodeType === 8) {
1397
- yield `<!--${safeCommentText(element.textContent || "")}-->`;
1592
+ const frame = serializeNode(element, context);
1593
+ if (typeof frame === "string") {
1594
+ if (frame) yield frame;
1398
1595
  return;
1399
1596
  }
1400
- if (!(element instanceof HTMLElement)) {
1401
- yield escapeHtml(element.textContent || "");
1402
- return;
1403
- }
1404
- const tag = tagNameOf(element).toLowerCase();
1405
- if (isCodeTextElement(tag)) {
1406
- if (DEV) yield `<!--ssr:${tag}-stripped-->`;
1407
- return;
1408
- }
1409
- if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
1410
- if (DEV) yield "<!--ssr:invalid-tag-->";
1411
- return;
1412
- }
1413
- const openTag = serializeOpenTag(element, tag);
1414
- if (openTag === null) {
1415
- if (DEV) yield `<!--ssr:${tag}-stripped-->`;
1416
- return;
1417
- }
1418
- if (VOID_ELEMENTS.has(tag)) {
1419
- yield `${openTag} />`;
1420
- return;
1421
- }
1422
- yield `${openTag}>`;
1423
- for (const child of Array.from(childNodesOf(element))) {
1597
+ yield frame.open;
1598
+ for (const child of frame.children) {
1424
1599
  try {
1425
- yield* renderToStream(child);
1600
+ yield* streamNode(child, frame.context);
1426
1601
  } catch (err) {
1427
1602
  yield ssrErrorComment(err);
1428
1603
  }
1429
1604
  }
1430
- yield `</${tag}>`;
1605
+ yield frame.close;
1431
1606
  }
1432
1607
  async function collectStream(stream) {
1433
1608
  let result = "";
@@ -1583,8 +1758,8 @@ async function* renderToSuspenseStream(element, pendingBoundaries = [], options)
1583
1758
  function escapeScriptJson(json) {
1584
1759
  return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1585
1760
  }
1586
- function serializeState(state2, nonce, options) {
1587
- const rawJson = JSON.stringify(state2);
1761
+ function serializeState(state3, nonce, options) {
1762
+ const rawJson = JSON.stringify(state3);
1588
1763
  const maxBytes = options?.maxBytes ?? DEFAULT_MAX_SSR_BYTES;
1589
1764
  const byteLen = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(rawJson).byteLength : Buffer.byteLength(rawJson, "utf8");
1590
1765
  if (byteLen > maxBytes) {
@@ -1612,7 +1787,7 @@ function escapeHtml(str) {
1612
1787
  function escapeAttr(str) {
1613
1788
  return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1614
1789
  }
1615
- var SAFE_ATTR_NAME, VOID_ELEMENTS, nativeAccessorsByRealm, SAFE_ID, SAFE_SUSPENSE_ID, SSR_DATA_ATTR, DEFAULT_MAX_SSR_BYTES;
1790
+ var SAFE_ATTR_NAME, VOID_ELEMENTS, nativeAccessorsByRealm, SVG_NS4, MATHML_NS, XLINK_NS2, XML_NS2, XMLNS_NS, BREAKOUT_TAGS, SAFE_ID, SAFE_SUSPENSE_ID, SSR_DATA_ATTR, DEFAULT_MAX_SSR_BYTES;
1616
1791
  var init_ssr = __esm({
1617
1792
  "src/platform/ssr.ts"() {
1618
1793
  "use strict";
@@ -1620,6 +1795,7 @@ var init_ssr = __esm({
1620
1795
  init_dispose();
1621
1796
  init_ssr_context();
1622
1797
  init_elementPolicy();
1798
+ init_embedPolicy();
1623
1799
  init_guards();
1624
1800
  init_headEntry();
1625
1801
  init_metaRefresh();
@@ -1642,6 +1818,57 @@ var init_ssr = __esm({
1642
1818
  "wbr"
1643
1819
  ]);
1644
1820
  nativeAccessorsByRealm = /* @__PURE__ */ new WeakMap();
1821
+ SVG_NS4 = "http://www.w3.org/2000/svg";
1822
+ MATHML_NS = "http://www.w3.org/1998/Math/MathML";
1823
+ XLINK_NS2 = "http://www.w3.org/1999/xlink";
1824
+ XML_NS2 = "http://www.w3.org/XML/1998/namespace";
1825
+ XMLNS_NS = "http://www.w3.org/2000/xmlns/";
1826
+ BREAKOUT_TAGS = /* @__PURE__ */ new Set([
1827
+ "b",
1828
+ "big",
1829
+ "blockquote",
1830
+ "body",
1831
+ "br",
1832
+ "center",
1833
+ "code",
1834
+ "dd",
1835
+ "div",
1836
+ "dl",
1837
+ "dt",
1838
+ "em",
1839
+ "embed",
1840
+ "h1",
1841
+ "h2",
1842
+ "h3",
1843
+ "h4",
1844
+ "h5",
1845
+ "h6",
1846
+ "head",
1847
+ "hr",
1848
+ "i",
1849
+ "img",
1850
+ "li",
1851
+ "listing",
1852
+ "menu",
1853
+ "meta",
1854
+ "nobr",
1855
+ "ol",
1856
+ "p",
1857
+ "pre",
1858
+ "ruby",
1859
+ "s",
1860
+ "small",
1861
+ "span",
1862
+ "strong",
1863
+ "strike",
1864
+ "sub",
1865
+ "sup",
1866
+ "table",
1867
+ "tt",
1868
+ "u",
1869
+ "ul",
1870
+ "var"
1871
+ ]);
1645
1872
  SAFE_ID = /^[A-Za-z0-9_-]+$/;
1646
1873
  SAFE_SUSPENSE_ID = /^[A-Za-z0-9_-]+$/;
1647
1874
  SSR_DATA_ATTR = "__SIBU_SSR_DATA__";
@@ -2381,7 +2608,7 @@ function forEachSubscriber(signal2, visit) {
2381
2608
  }
2382
2609
 
2383
2610
  // src/reactivity/track.ts
2384
- var _runtimeVersion = true ? "4.10.1" : "dev";
2611
+ var _runtimeVersion = true ? "4.12.0" : "dev";
2385
2612
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
2386
2613
  function resolveReactiveApi() {
2387
2614
  const g2 = globalThis;
@@ -2641,6 +2868,16 @@ function bindChildNode(placeholder, getter) {
2641
2868
  init_elementPolicy();
2642
2869
  init_sanitize();
2643
2870
  init_dispose();
2871
+
2872
+ // src/core/rendering/requireDocument.ts
2873
+ init_dev();
2874
+ function noDocument() {
2875
+ throw new Error(
2876
+ "[SibuJS] renderToString needs a DOM: register happy-dom or jsdom globals first." + (DEV ? ' There is no global `document`, and SibuJS builds live DOM nodes, on the server too. For example: import { GlobalRegistrator } from "@happy-dom/global-registrator"; GlobalRegistrator.register(); See SSR in AGENTS.md \xA710.' : "")
2877
+ );
2878
+ }
2879
+
2880
+ // src/core/rendering/tagFactory.ts
2644
2881
  var SVG_NS3 = "http://www.w3.org/2000/svg";
2645
2882
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
2646
2883
  "config",
@@ -2833,6 +3070,7 @@ function tagFactory(tag, ns) {
2833
3070
  if (blocked) {
2834
3071
  throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
2835
3072
  }
3073
+ if (typeof document === "undefined") noDocument();
2836
3074
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
2837
3075
  if (first === void 0) return el;
2838
3076
  if (typeof first === "string") {
@@ -3137,7 +3375,7 @@ function emitDevtools(hook, event, payload) {
3137
3375
  // src/core/signals/signal.ts
3138
3376
  var _g = globalThis;
3139
3377
  function signal(initial, options) {
3140
- const state2 = {
3378
+ const state3 = {
3141
3379
  value: initial,
3142
3380
  __v: 0,
3143
3381
  __sc: 0,
@@ -3150,71 +3388,64 @@ function signal(initial, options) {
3150
3388
  };
3151
3389
  const debugName = DEV ? options?.name : void 0;
3152
3390
  const equalsFn = options?.equals;
3153
- if (debugName) state2.__name = debugName;
3391
+ if (debugName) state3.__name = debugName;
3154
3392
  function get() {
3155
- recordDependency2(state2);
3156
- return state2.value;
3393
+ recordDependency2(state3);
3394
+ return state3.value;
3157
3395
  }
3158
- get.__signal = state2;
3396
+ get.__signal = state3;
3159
3397
  if (debugName) get.__name = debugName;
3160
3398
  let set;
3161
3399
  if (equalsFn) {
3162
3400
  set = (next) => {
3163
- const prev = state2.value;
3401
+ const prev = state3.value;
3164
3402
  const newValue = typeof next === "function" ? next(prev) : next;
3165
3403
  if (equalsFn(prev, newValue)) return;
3166
- state2.value = newValue;
3167
- state2.__v++;
3404
+ state3.value = newValue;
3405
+ state3.__v++;
3168
3406
  if (DEV) {
3169
3407
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3170
- if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3408
+ if (hook) emitDevtools(hook, "signal:update", { signal: state3, name: debugName, oldValue: prev, newValue });
3171
3409
  }
3172
- if (!enqueueBatchedSignal(state2)) {
3173
- notifySubscribers2(state2);
3410
+ if (!enqueueBatchedSignal(state3)) {
3411
+ notifySubscribers2(state3);
3174
3412
  }
3175
3413
  };
3176
3414
  } else if (DEV) {
3177
3415
  set = (next) => {
3178
- const prev = state2.value;
3416
+ const prev = state3.value;
3179
3417
  const newValue = typeof next === "function" ? next(prev) : next;
3180
3418
  if (Object.is(newValue, prev)) return;
3181
- state2.value = newValue;
3182
- state2.__v++;
3419
+ state3.value = newValue;
3420
+ state3.__v++;
3183
3421
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3184
- if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3185
- if (!enqueueBatchedSignal(state2)) {
3186
- notifySubscribers2(state2);
3422
+ if (hook) emitDevtools(hook, "signal:update", { signal: state3, name: debugName, oldValue: prev, newValue });
3423
+ if (!enqueueBatchedSignal(state3)) {
3424
+ notifySubscribers2(state3);
3187
3425
  }
3188
3426
  };
3189
3427
  } else {
3190
3428
  set = (next) => {
3191
- const prev = state2.value;
3429
+ const prev = state3.value;
3192
3430
  const newValue = typeof next === "function" ? next(prev) : next;
3193
3431
  if (Object.is(newValue, prev)) return;
3194
- state2.value = newValue;
3195
- state2.__v++;
3196
- if (!enqueueBatchedSignal(state2)) {
3197
- notifySubscribers2(state2);
3432
+ state3.value = newValue;
3433
+ state3.__v++;
3434
+ if (!enqueueBatchedSignal(state3)) {
3435
+ notifySubscribers2(state3);
3198
3436
  }
3199
3437
  };
3200
3438
  }
3201
3439
  if (DEV) {
3202
3440
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3203
- if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
3441
+ if (hook) emitDevtools(hook, "signal:create", { signal: state3, name: debugName, getter: get, initial });
3204
3442
  }
3205
3443
  return [get, set];
3206
3444
  }
3207
3445
 
3208
3446
  // src/plugins/i18n.ts
3209
3447
  init_ssr_context();
3210
-
3211
- // src/utils/globalSingleton.ts
3212
- function globalSingleton(key, create) {
3213
- const g2 = globalThis;
3214
- return g2[key] ?? (g2[key] = create());
3215
- }
3216
-
3217
- // src/plugins/i18n.ts
3448
+ init_globalSingleton();
3218
3449
  var _i18n = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.i18n.v1"), () => ({
3219
3450
  locale: signal("en"),
3220
3451
  locales: /* @__PURE__ */ Object.create(null),
@@ -3458,11 +3689,28 @@ function normalizePathname(path2) {
3458
3689
  function parseQuery(query) {
3459
3690
  return Object.fromEntries(new URLSearchParams(query));
3460
3691
  }
3692
+ function parseQueryAll(query) {
3693
+ const all = /* @__PURE__ */ new Map();
3694
+ for (const [key, value] of new URLSearchParams(query)) {
3695
+ const values = all.get(key);
3696
+ if (values) values.push(value);
3697
+ else all.set(key, [value]);
3698
+ }
3699
+ return Object.fromEntries(all);
3700
+ }
3701
+ function serializeQuery(query) {
3702
+ const search = new URLSearchParams();
3703
+ for (const [key, value] of Object.entries(query)) {
3704
+ if (Array.isArray(value)) for (const v of value) search.append(key, String(v));
3705
+ else search.append(key, String(value));
3706
+ }
3707
+ return search.toString();
3708
+ }
3461
3709
  function paramsIdentity(params) {
3462
3710
  return JSON.stringify(Object.entries(params).sort(([a2], [b2]) => a2 < b2 ? -1 : a2 > b2 ? 1 : 0));
3463
3711
  }
3464
3712
  function queryIdentity(query) {
3465
- return paramsIdentity(typeof query === "string" ? parseQuery(query) : query);
3713
+ return paramsIdentity(typeof query === "string" ? parseQueryAll(query) : query);
3466
3714
  }
3467
3715
  function isPathnameAncestor(target, current) {
3468
3716
  if (target === current) return true;
@@ -4138,6 +4386,7 @@ var _SibuRouter = class _SibuRouter {
4138
4386
  path: "/",
4139
4387
  params: {},
4140
4388
  query: {},
4389
+ queryAll: {},
4141
4390
  hash: "",
4142
4391
  meta: {},
4143
4392
  matched: []
@@ -4164,6 +4413,7 @@ var _SibuRouter = class _SibuRouter {
4164
4413
  createRouteContext(fullPath) {
4165
4414
  const { path: path2, query: queryString, hash } = splitRouteUrl(fullPath);
4166
4415
  const query = parseQuery(queryString);
4416
+ const queryAll = parseQueryAll(queryString);
4167
4417
  const match = this.matcher.match(path2 || "/");
4168
4418
  const params = match?.params || {};
4169
4419
  const meta2 = match?.route.meta || {};
@@ -4172,6 +4422,7 @@ var _SibuRouter = class _SibuRouter {
4172
4422
  path: path2 || "/",
4173
4423
  params,
4174
4424
  query,
4425
+ queryAll,
4175
4426
  hash,
4176
4427
  meta: meta2,
4177
4428
  matched
@@ -4317,8 +4568,9 @@ var _SibuRouter = class _SibuRouter {
4317
4568
  path2 = path2.replace(new RegExp(`:${key}(?=[/?#]|$)`, "g"), encodeURIComponent(value));
4318
4569
  }
4319
4570
  }
4320
- if (to.query && Object.keys(to.query).length > 0) {
4321
- path2 += `?${new URLSearchParams(to.query).toString()}`;
4571
+ const search = to.query ? serializeQuery(to.query) : "";
4572
+ if (search) {
4573
+ path2 += `?${search}`;
4322
4574
  }
4323
4575
  if (to.hash) {
4324
4576
  path2 += `#${to.hash}`;
@@ -4327,10 +4579,13 @@ var _SibuRouter = class _SibuRouter {
4327
4579
  }
4328
4580
  isSameRoute(from, to) {
4329
4581
  if (from.matched.length === 0 && to.matched.length > 0) return false;
4330
- return from.path === to.path && paramsIdentity(from.params) === paramsIdentity(to.params) && queryIdentity(from.query) === queryIdentity(to.query) && from.hash === to.hash;
4582
+ return from.path === to.path && paramsIdentity(from.params) === paramsIdentity(to.params) && queryIdentity(from.queryAll) === queryIdentity(to.queryAll) && from.hash === to.hash;
4331
4583
  }
4332
4584
  updateHistory(to, options) {
4333
- const fullPath = this.options.base + to.path + (Object.keys(to.query).length ? `?${new URLSearchParams(to.query).toString()}` : "") + (to.hash ? `#${to.hash}` : "");
4585
+ const search = serializeQuery(to.queryAll);
4586
+ const routeUrl = to.path + (search ? `?${search}` : "") + (to.hash ? `#${to.hash}` : "");
4587
+ const loc = globalThis.location;
4588
+ const fullPath = this.options.mode === "hash" ? `${loc ? loc.pathname + loc.search : ""}#${routeUrl}` : this.options.base + routeUrl;
4334
4589
  const h = globalThis.history;
4335
4590
  if (!h) return;
4336
4591
  if (options.replace) {
@@ -4513,6 +4768,65 @@ function abortReasonOf(signal2) {
4513
4768
  }
4514
4769
  var ROUTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.router.v1");
4515
4770
  var _routerRef = globalThis[ROUTER_KEY] ?? (globalThis[ROUTER_KEY] = { current: null });
4771
+ var [routerRevision, setRouterRevision] = signal(0);
4772
+ var awaitingParent = /* @__PURE__ */ new Map();
4773
+ var parentObserver = null;
4774
+ function stopParentObserverIfIdle() {
4775
+ if (parentObserver && awaitingParent.size === 0) {
4776
+ parentObserver.disconnect();
4777
+ parentObserver = null;
4778
+ }
4779
+ }
4780
+ function whenAnchorAttached(anchor, run) {
4781
+ let cancelled = false;
4782
+ queueMicrotask(() => {
4783
+ if (cancelled) return;
4784
+ if (anchor.parentNode) {
4785
+ cancelled = true;
4786
+ run();
4787
+ return;
4788
+ }
4789
+ if (typeof MutationObserver === "undefined" || typeof document === "undefined") return;
4790
+ awaitingParent.set(anchor, () => {
4791
+ cancelled = true;
4792
+ run();
4793
+ });
4794
+ if (!parentObserver) {
4795
+ parentObserver = new MutationObserver(() => {
4796
+ for (const [node, cb] of awaitingParent) {
4797
+ if (!node.parentNode) continue;
4798
+ awaitingParent.delete(node);
4799
+ cb();
4800
+ }
4801
+ stopParentObserverIfIdle();
4802
+ });
4803
+ parentObserver.observe(document, { childList: true, subtree: true });
4804
+ }
4805
+ });
4806
+ return () => {
4807
+ if (cancelled) return;
4808
+ cancelled = true;
4809
+ awaitingParent.delete(anchor);
4810
+ stopParentObserverIfIdle();
4811
+ };
4812
+ }
4813
+ function attachRetry(anchor, rerun) {
4814
+ const run = bindOwnerScope2(rerun);
4815
+ let cancelWait = null;
4816
+ return {
4817
+ request: () => {
4818
+ if (cancelWait) return;
4819
+ cancelWait = whenAnchorAttached(anchor, () => {
4820
+ cancelWait = null;
4821
+ run();
4822
+ });
4823
+ },
4824
+ cancel: () => {
4825
+ cancelWait?.();
4826
+ cancelWait = null;
4827
+ }
4828
+ };
4829
+ }
4516
4830
  function normalizeRoutes(routes) {
4517
4831
  return routes.map((route2) => {
4518
4832
  const normalizedChildren = route2.children && route2.children.length > 0 ? normalizeRoutes(route2.children) : route2.children;
@@ -4544,6 +4858,7 @@ function createRouter(routesOrOptions, options = {}) {
4544
4858
  }
4545
4859
  _routerRef.current = new SibuRouter(routes, options);
4546
4860
  ensureRouterPagehide();
4861
+ setRouterRevision((n) => n + 1);
4547
4862
  return _routerRef.current;
4548
4863
  }
4549
4864
  function setRoutes(routes) {
@@ -4699,6 +5014,9 @@ function Route() {
4699
5014
  let currentKey = null;
4700
5015
  let routeTorn = false;
4701
5016
  const commitTarget = (seq) => routeTorn || seq !== navSeq ? null : anchor.parentNode;
5017
+ const retryIfDetached = (seq) => {
5018
+ if (!routeTorn && seq === navSeq && !anchor.parentNode) attachWait.request();
5019
+ };
4702
5020
  const release = (node) => {
4703
5021
  if (!node) return;
4704
5022
  disposeOwned(node);
@@ -4781,6 +5099,7 @@ function Route() {
4781
5099
  anchor.parentNode.insertBefore(errorNode, anchor.nextSibling);
4782
5100
  };
4783
5101
  const update = async () => {
5102
+ routerRevision();
4784
5103
  if (routeTorn || !_routerRef.current) return;
4785
5104
  const seq = ++navSeq;
4786
5105
  const route2 = scopedRoute(_routerRef.current);
@@ -4814,11 +5133,15 @@ function Route() {
4814
5133
  showLoading();
4815
5134
  }
4816
5135
  const plan = await _routerRef.current.loadPlan(routeDef, route2.path);
4817
- if (!commitTarget(seq)) return;
5136
+ if (!commitTarget(seq)) {
5137
+ retryIfDetached(seq);
5138
+ return;
5139
+ }
4818
5140
  const node = await instantiateScoped(_routerRef.current, plan, routeDef, route2.path, route2);
4819
5141
  const parent = commitTarget(seq);
4820
5142
  if (!parent) {
4821
5143
  release(node);
5144
+ retryIfDetached(seq);
4822
5145
  return;
4823
5146
  }
4824
5147
  if (node) {
@@ -4832,15 +5155,31 @@ function Route() {
4832
5155
  if (routeTorn || seq !== navSeq) return;
4833
5156
  hideLoading();
4834
5157
  console.error("[Route] Component error:", error);
4835
- showError(error instanceof Error ? error : new Error(String(error)), routeDef);
5158
+ fail(seq, error, routeDef);
4836
5159
  }
4837
5160
  }
4838
5161
  } catch (error) {
4839
5162
  if (seq !== navSeq) return;
4840
5163
  if (routeTorn) return;
4841
5164
  console.error("[Route] Update failed:", error);
4842
- showError(error instanceof Error ? error : new Error(String(error)));
5165
+ fail(seq, error);
5166
+ }
5167
+ };
5168
+ let unshownFailure = null;
5169
+ const fail = (seq, error, routeDef) => {
5170
+ const err = error instanceof Error ? error : new Error(String(error));
5171
+ if (anchor.parentNode) {
5172
+ showError(err, routeDef);
5173
+ return;
4843
5174
  }
5175
+ unshownFailure = { seq, error: err, routeDef };
5176
+ attachWait.request();
5177
+ };
5178
+ const onAttached = () => {
5179
+ const failure = unshownFailure;
5180
+ unshownFailure = null;
5181
+ if (failure && failure.seq === navSeq) showError(failure.error, failure.routeDef);
5182
+ else update();
4844
5183
  };
4845
5184
  let routeInitialized = false;
4846
5185
  const originalUpdate = update;
@@ -4848,6 +5187,7 @@ function Route() {
4848
5187
  await originalUpdate();
4849
5188
  routeInitialized = true;
4850
5189
  };
5190
+ const attachWait = attachRetry(anchor, onAttached);
4851
5191
  const routeTeardown = track2(wrappedUpdate);
4852
5192
  if (!routeInitialized) {
4853
5193
  queueMicrotask(
@@ -4861,6 +5201,8 @@ function Route() {
4861
5201
  routeTorn = true;
4862
5202
  navSeq++;
4863
5203
  routeTeardown();
5204
+ attachWait.cancel();
5205
+ unshownFailure = null;
4864
5206
  cleanupNodes();
4865
5207
  currentTopRoute = null;
4866
5208
  currentKey = null;
@@ -4884,6 +5226,7 @@ function KeepAliveRoute(options) {
4884
5226
  let updateSeq = 0;
4885
5227
  let kaTorn = false;
4886
5228
  const update = async () => {
5229
+ routerRevision();
4887
5230
  if (kaTorn || !_routerRef.current) return;
4888
5231
  const seq = ++updateSeq;
4889
5232
  const router2 = _routerRef.current;
@@ -4901,13 +5244,16 @@ function KeepAliveRoute(options) {
4901
5244
  return;
4902
5245
  }
4903
5246
  if (!("component" in routeDef)) return;
4904
- const cacheKey = JSON.stringify([route2.path, queryIdentity(route2.query), route2.hash]);
5247
+ const cacheKey = JSON.stringify([route2.path, queryIdentity(route2.queryAll), route2.hash]);
4905
5248
  const shouldCache = !includeNames || routeDef.name != null && includeNames.includes(routeDef.name);
4906
5249
  if (cacheKey === currentKey && currentNode) {
4907
5250
  updateInstanceRoute(currentNode, route2);
4908
5251
  return;
4909
5252
  }
4910
- if (!anchor.parentNode) return;
5253
+ if (!anchor.parentNode) {
5254
+ attachWait.request();
5255
+ return;
5256
+ }
4911
5257
  let node = null;
4912
5258
  let fromCache = false;
4913
5259
  if (shouldCache && cache.has(cacheKey)) {
@@ -4932,6 +5278,7 @@ function KeepAliveRoute(options) {
4932
5278
  const parent = anchor.parentNode;
4933
5279
  if (!parent) {
4934
5280
  if (!fromCache) disposeOwned(node);
5281
+ attachWait.request();
4935
5282
  return;
4936
5283
  }
4937
5284
  if (currentNode && currentNode !== node) {
@@ -4970,6 +5317,7 @@ function KeepAliveRoute(options) {
4970
5317
  await update();
4971
5318
  initialized = true;
4972
5319
  };
5320
+ const attachWait = attachRetry(anchor, update);
4973
5321
  const kaTeardown = track2(wrappedUpdate);
4974
5322
  if (!initialized) {
4975
5323
  queueMicrotask(
@@ -4983,6 +5331,7 @@ function KeepAliveRoute(options) {
4983
5331
  kaTorn = true;
4984
5332
  updateSeq++;
4985
5333
  kaTeardown();
5334
+ attachWait.cancel();
4986
5335
  for (const node of cache.values()) {
4987
5336
  disposeOwned(node);
4988
5337
  if (node.parentNode) node.parentNode.removeChild(node);
@@ -5011,7 +5360,7 @@ function RouterLink(props, children) {
5011
5360
  const targetPath = normalizePathname(hrefPathRaw);
5012
5361
  const targetQuery = queryIdentity(hrefQueryRaw);
5013
5362
  const link2 = document.createElement("a");
5014
- link2.href = href;
5363
+ link2.href = kind === "internal" && _routerRef.current["options"].mode === "hash" ? `#${href}` : href;
5015
5364
  if (target) {
5016
5365
  link2.target = target;
5017
5366
  if (target === "_blank") {
@@ -5027,7 +5376,7 @@ function RouterLink(props, children) {
5027
5376
  const route2 = routeGetter();
5028
5377
  const currentPath = normalizePathname(route2.path);
5029
5378
  const isActive = kind === "internal" && isPathnameAncestor(targetPath, currentPath);
5030
- const isExactActive = kind === "internal" && currentPath === targetPath && queryIdentity(route2.query) === targetQuery && route2.hash === hrefHash;
5379
+ const isExactActive = kind === "internal" && currentPath === targetPath && queryIdentity(route2.queryAll) === targetQuery && route2.hash === hrefHash;
5031
5380
  const classes = [];
5032
5381
  if (isActive) {
5033
5382
  if (activeClass) classes.push(activeClass);
@@ -5222,12 +5571,16 @@ function Outlet() {
5222
5571
  currentKey = null;
5223
5572
  };
5224
5573
  const commitTarget = (seq) => outletTorn || seq !== navSeq ? null : anchor.parentNode;
5574
+ const retryIfDetached = (seq) => {
5575
+ if (!outletTorn && seq === navSeq && !anchor.parentNode) attachWait.request();
5576
+ };
5225
5577
  const release = (node) => {
5226
5578
  if (!node) return;
5227
5579
  disposeOwned(node);
5228
5580
  node.parentNode?.removeChild(node);
5229
5581
  };
5230
5582
  const update = async () => {
5583
+ routerRevision();
5231
5584
  if (outletTorn || !_routerRef.current) return;
5232
5585
  const seq = ++navSeq;
5233
5586
  const route2 = scopedRoute(_routerRef.current);
@@ -5248,11 +5601,15 @@ function Outlet() {
5248
5601
  try {
5249
5602
  const cacheKey = `${route2.path}\0${childRoute.path}`;
5250
5603
  const plan = await _routerRef.current.loadPlan(childRoute, cacheKey);
5251
- if (!commitTarget(seq)) return;
5604
+ if (!commitTarget(seq)) {
5605
+ retryIfDetached(seq);
5606
+ return;
5607
+ }
5252
5608
  const node = await instantiateScoped(_routerRef.current, plan, childRoute, cacheKey, route2);
5253
5609
  const parent = commitTarget(seq);
5254
5610
  if (!parent) {
5255
5611
  release(node);
5612
+ retryIfDetached(seq);
5256
5613
  return;
5257
5614
  }
5258
5615
  if (!node) return;
@@ -5266,6 +5623,7 @@ function Outlet() {
5266
5623
  console.error("[Outlet] Failed to render child route:", error);
5267
5624
  }
5268
5625
  };
5626
+ const attachWait = attachRetry(anchor, update);
5269
5627
  const outletTeardown = track2(update);
5270
5628
  if (!anchor.parentNode) {
5271
5629
  queueMicrotask(
@@ -5279,6 +5637,7 @@ function Outlet() {
5279
5637
  outletTorn = true;
5280
5638
  navSeq++;
5281
5639
  outletTeardown();
5640
+ attachWait.cancel();
5282
5641
  if (currentNode) {
5283
5642
  disposeOwned(currentNode);
5284
5643
  if (currentNode.parentNode) currentNode.parentNode.removeChild(currentNode);
@@ -5306,6 +5665,7 @@ function routerState() {
5306
5665
  currentPath: () => scopedRoute(router2).path,
5307
5666
  params: () => scopedRoute(router2).params,
5308
5667
  query: () => scopedRoute(router2).query,
5668
+ queryAll: () => scopedRoute(router2).queryAll,
5309
5669
  hash: () => scopedRoute(router2).hash,
5310
5670
  meta: () => scopedRoute(router2).meta,
5311
5671
  isNavigating: () => router2.isNavigating,
@@ -5548,7 +5908,7 @@ function renderRouteToString(url, routes, _options) {
5548
5908
  };
5549
5909
  }
5550
5910
  function renderRouteToDocument(url, routes, options) {
5551
- const { html: html2, state: state2 } = renderRouteToString(url, routes, options);
5911
+ const { html: html2, state: state3 } = renderRouteToString(url, routes, options);
5552
5912
  const opts = options || {};
5553
5913
  const metaTags = (opts.meta || []).map((attrs) => serializeHeadEntry("meta", attrs)).filter(Boolean).join("\n ");
5554
5914
  const linkTags = (opts.links || []).map((attrs) => serializeHeadEntry("link", attrs)).filter(Boolean).join("\n ");
@@ -5557,7 +5917,7 @@ function renderRouteToDocument(url, routes, options) {
5557
5917
  if (!safe) return "";
5558
5918
  return `<script src="${escapeAttrLocal(safe)}"></script>`;
5559
5919
  }).filter(Boolean).join("\n ");
5560
- const stateScript = serializeRouteState(state2, opts.nonce);
5920
+ const stateScript = serializeRouteState(state3, opts.nonce);
5561
5921
  return `<!DOCTYPE html>
5562
5922
  <html>
5563
5923
  <head>
@@ -5575,8 +5935,8 @@ function renderRouteToDocument(url, routes, options) {
5575
5935
  </body>
5576
5936
  </html>`;
5577
5937
  }
5578
- function serializeRouteState(state2, nonce) {
5579
- const json = escapeScriptJson(JSON.stringify(state2));
5938
+ function serializeRouteState(state3, nonce) {
5939
+ const json = escapeScriptJson(JSON.stringify(state3));
5580
5940
  const nonceAttr = nonce ? ` nonce="${escapeAttrLocal(nonce)}"` : "";
5581
5941
  return `<script${nonceAttr}>window.${SSR_ROUTE_STATE_KEY}=${json}</script>`;
5582
5942
  }
@@ -5655,6 +6015,7 @@ function adoptThenable(value) {
5655
6015
  }
5656
6016
 
5657
6017
  // src/plugins/plugin.ts
6018
+ init_globalSingleton();
5658
6019
  var CANCELLED_BRAND = /* @__PURE__ */ Symbol.for("sibujs.plugins.installCancelled.v1");
5659
6020
  var _a;
5660
6021
  var PluginInstallCancelledError = class extends Error {
@@ -6021,7 +6382,7 @@ var CDN_ENTRY_POINTS = {
6021
6382
  };
6022
6383
  var packageInfo = {
6023
6384
  name: "sibujs",
6024
- version: true ? "4.10.1" : "dev",
6385
+ version: true ? "4.12.0" : "dev",
6025
6386
  entryPoints: MODULE_ENTRY_POINTS,
6026
6387
  /**
6027
6388
  * Generate the package.json `exports` map. Module entries resolve to the
@@ -6166,7 +6527,8 @@ var env = {
6166
6527
  };
6167
6528
 
6168
6529
  // src/plugins/versioning.ts
6169
- var VERSION = true ? "4.10.1" : "dev";
6530
+ init_globalSingleton();
6531
+ var VERSION = true ? "4.12.0" : "dev";
6170
6532
  var NUM = "(?:0|[1-9]\\d*)";
6171
6533
  var PRE_ID = `(?:${NUM}|\\d*[a-zA-Z-][0-9a-zA-Z-]*)`;
6172
6534
  var BUILD_ID = "[0-9a-zA-Z-]+";