sibujs 4.13.1 → 4.14.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 (107) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +15 -2
  3. package/dist/browser.js +9 -7
  4. package/dist/build.cjs +880 -281
  5. package/dist/build.d.cts +68 -3
  6. package/dist/build.d.ts +68 -3
  7. package/dist/build.js +88 -30
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +13 -13
  10. package/dist/cdn.full.global.js +12 -12
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-5RIBLY2N.js → chunk-2TTRW2TE.js} +6 -6
  13. package/dist/{chunk-J6FW5TV6.js → chunk-3HEX6QJQ.js} +5 -3
  14. package/dist/chunk-3HZOSMPX.js +90 -0
  15. package/dist/{chunk-QKFVMIAV.js → chunk-4FJSAUKB.js} +11 -3
  16. package/dist/{chunk-OIKD7YDY.js → chunk-4TQDTWWN.js} +6 -5
  17. package/dist/{chunk-5XUGJM3J.js → chunk-7ILEHTVX.js} +1 -1
  18. package/dist/{chunk-EU7T7EB3.js → chunk-A454RAEK.js} +4 -4
  19. package/dist/{chunk-QC3NP47A.js → chunk-AE32RGWX.js} +10 -6
  20. package/dist/{chunk-EXXSN3BW.js → chunk-B3HOT4TK.js} +1 -1
  21. package/dist/{chunk-QZ47OZ25.js → chunk-BKZ5N6NK.js} +8 -6
  22. package/dist/{chunk-2O6JJCUR.js → chunk-BNXRWZZV.js} +49 -8
  23. package/dist/chunk-BOYKGGVD.js +2015 -0
  24. package/dist/{chunk-22OPSWMF.js → chunk-CCKUNHVU.js} +2 -2
  25. package/dist/{chunk-CPQKWCJ2.js → chunk-CJ7KS3DF.js} +16 -10
  26. package/dist/chunk-CNPT6NKV.js +1859 -0
  27. package/dist/{chunk-2NC56RGY.js → chunk-F67F77IA.js} +2 -2
  28. package/dist/{chunk-TUCPL2HB.js → chunk-GIJ33UJZ.js} +1 -1
  29. package/dist/chunk-GVAMIU6K.js +195 -0
  30. package/dist/{chunk-K4LNUT5L.js → chunk-HQIAL7F2.js} +8 -6
  31. package/dist/{chunk-C73VBPEF.js → chunk-JTFQ7IL4.js} +8 -8
  32. package/dist/{chunk-AM6DZLBM.js → chunk-LJMX4SQ7.js} +2 -2
  33. package/dist/{chunk-4UTGDVSA.js → chunk-MIRKTVH4.js} +7 -14
  34. package/dist/{chunk-YFTBCCVH.js → chunk-N7A6FKWW.js} +5 -3
  35. package/dist/chunk-NT3ZYB5S.js +167 -0
  36. package/dist/{chunk-VFPLWUT7.js → chunk-QS6GMWDD.js} +151 -86
  37. package/dist/chunk-R5U7XKVJ.js +16 -0
  38. package/dist/{chunk-ADZHPN2B.js → chunk-RVR5NPFL.js} +3 -3
  39. package/dist/{chunk-F2UWOZ6J.js → chunk-RVWJGWCL.js} +2 -2
  40. package/dist/{chunk-NYNYSPK7.js → chunk-S37PZD6I.js} +30 -45
  41. package/dist/{chunk-G3Z2FEBR.js → chunk-SVULV7O4.js} +119 -26
  42. package/dist/{chunk-PBJYCE2Z.js → chunk-TLS4NVIB.js} +5 -5
  43. package/dist/{chunk-KC62VURU.js → chunk-UUIKJJIK.js} +10 -8
  44. package/dist/chunk-UYTYNCMZ.js +199 -0
  45. package/dist/chunk-VM2U5LEL.js +2596 -0
  46. package/dist/{chunk-J4X7ICH4.js → chunk-Y4LUM2XS.js} +8 -6
  47. package/dist/data.cjs +265 -35
  48. package/dist/data.d.cts +31 -1
  49. package/dist/data.d.ts +31 -1
  50. package/dist/data.js +16 -10
  51. package/dist/devtools.cjs +15 -2
  52. package/dist/devtools.js +9 -7
  53. package/dist/{dispose-GEIG2KOF.js → dispose-EIHHHDC7.js} +3 -1
  54. package/dist/ecosystem.cjs +62 -23
  55. package/dist/ecosystem.js +14 -12
  56. package/dist/extras.cjs +3520 -270
  57. package/dist/extras.d.cts +3 -3
  58. package/dist/extras.d.ts +3 -3
  59. package/dist/extras.js +39 -29
  60. package/dist/index.cjs +442 -253
  61. package/dist/index.d.cts +93 -283
  62. package/dist/index.d.ts +93 -283
  63. package/dist/index.js +26 -16
  64. package/dist/motion.cjs +12 -1
  65. package/dist/motion.js +6 -4
  66. package/dist/patterns.cjs +15 -2
  67. package/dist/patterns.js +10 -8
  68. package/dist/performance.cjs +36 -2
  69. package/dist/performance.d.cts +2 -2
  70. package/dist/performance.d.ts +2 -2
  71. package/dist/performance.js +14 -10
  72. package/dist/plugins.cjs +5225 -1807
  73. package/dist/plugins.d.cts +8 -834
  74. package/dist/plugins.d.ts +8 -834
  75. package/dist/plugins.js +64 -2329
  76. package/dist/prerenderServer-MXJEVWA3.js +350 -0
  77. package/dist/renderRoute-DP20XwMS.d.cts +86 -0
  78. package/dist/renderRoute-fxgsm2Ir.d.ts +86 -0
  79. package/dist/routeHead-CE9SV-cH.d.cts +995 -0
  80. package/dist/routeHead-DzVxPviY.d.ts +995 -0
  81. package/dist/{routeMiddleware-B1sp4P7a.d.ts → routeMiddleware-BySbueoE.d.ts} +0 -25
  82. package/dist/{routeMiddleware-CedQTsLK.d.cts → routeMiddleware-CijZD5eW.d.cts} +0 -25
  83. package/dist/ssr-JpGpHnpC.d.cts +841 -0
  84. package/dist/ssr-JpGpHnpC.d.ts +841 -0
  85. package/dist/{ssr-VBM6J5SJ.js → ssr-NECEU4X4.js} +15 -6
  86. package/dist/ssr-dom.cjs +1862 -0
  87. package/dist/ssr-dom.d.cts +341 -0
  88. package/dist/ssr-dom.d.ts +341 -0
  89. package/dist/ssr-dom.js +19 -0
  90. package/dist/ssr.cjs +7630 -2064
  91. package/dist/ssr.d.cts +63 -5
  92. package/dist/ssr.d.ts +63 -5
  93. package/dist/ssr.js +136 -14
  94. package/dist/testing.cjs +6543 -87
  95. package/dist/testing.d.cts +80 -1
  96. package/dist/testing.d.ts +80 -1
  97. package/dist/testing.js +117 -4
  98. package/dist/ui.cjs +36 -2
  99. package/dist/ui.js +17 -14
  100. package/dist/widgets.cjs +14 -1
  101. package/dist/widgets.js +11 -9
  102. package/package.json +9 -2
  103. package/dist/chunk-2F4PSJ5Z.js +0 -765
  104. package/dist/chunk-7XHATCIH.js +0 -121
  105. package/dist/chunk-B36ADEIR.js +0 -21
  106. package/dist/ssr-Db0Sgdu3.d.cts +0 -245
  107. package/dist/ssr-Db0Sgdu3.d.ts +0 -245
@@ -1,765 +0,0 @@
1
- import {
2
- embedSerializationTail,
3
- embedSerializedAttribute,
4
- getEmbedRecord
5
- } from "./chunk-AM6DZLBM.js";
6
- import {
7
- readOwnGlobal
8
- } from "./chunk-62BDYGAG.js";
9
- import {
10
- isCodeTextElement,
11
- isForbiddenMetaEntry,
12
- isForbiddenMetaSnapshot,
13
- isRefusedAnimationAttribute,
14
- resolveMetaRefreshPolicy
15
- } from "./chunk-F2UWOZ6J.js";
16
- import {
17
- canonicalAttrName,
18
- isEventHandlerAttr,
19
- isHtmlContentAttribute,
20
- resolveAttributeValue,
21
- sanitizeUrl
22
- } from "./chunk-EXXSN3BW.js";
23
- import {
24
- getSSRStore
25
- } from "./chunk-7XHATCIH.js";
26
- import {
27
- DEV,
28
- dispose,
29
- replaceChildrenSafely
30
- } from "./chunk-NYNYSPK7.js";
31
-
32
- // src/utils/headEntry.ts
33
- var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
34
- function isEmittableHeadAttrName(name) {
35
- if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
36
- const canonical = canonicalAttrName(name);
37
- if (isEventHandlerAttr(canonical)) return false;
38
- if (isHtmlContentAttribute(canonical)) return false;
39
- return true;
40
- }
41
- function sanitizeHeadAttrValue(canonical, value) {
42
- return resolveAttributeValue(canonical, value);
43
- }
44
- function findDuplicateAttributeName(names) {
45
- const seen = /* @__PURE__ */ new Set();
46
- for (const name of names) {
47
- const canonical = canonicalAttrName(name);
48
- if (seen.has(canonical)) return canonical;
49
- seen.add(canonical);
50
- }
51
- return null;
52
- }
53
- function planEntry(props, resolver, applyRefreshPolicy) {
54
- const rawNames = Object.keys(props);
55
- const duplicate = findDuplicateAttributeName(rawNames);
56
- if (duplicate !== null) {
57
- return { kind: "drop", reason: `duplicate case-insensitive attribute ${JSON.stringify(duplicate)}` };
58
- }
59
- const attributes = /* @__PURE__ */ new Map();
60
- let reactive = false;
61
- for (const name of rawNames) {
62
- if (!Object.hasOwn(props, name)) continue;
63
- if (!isEmittableHeadAttrName(name)) continue;
64
- const authored = props[name];
65
- if (resolver.isReactiveValue?.(authored)) reactive = true;
66
- const canonical = canonicalAttrName(name);
67
- const sanitized = sanitizeHeadAttrValue(canonical, resolver.resolveValue(name, authored));
68
- if (sanitized === null) continue;
69
- attributes.set(canonical, sanitized);
70
- }
71
- if (attributes.size === 0) return { kind: "drop", reason: "no effective attributes remain" };
72
- const refresh = applyRefreshPolicy ? resolveMetaRefreshPolicy(attributes) : { kind: "not-refresh" };
73
- if (refresh.kind === "forbidden") return { kind: "drop", reason: refresh.reason };
74
- if (reactive && (refresh.kind === "allowed" || refresh.kind === "delay-only")) {
75
- return { kind: "drop", reason: "a reactive meta entry may not publish a native refresh directive" };
76
- }
77
- return { kind: "publish", attributes, refresh };
78
- }
79
- function planMetaEntry(props, resolver) {
80
- return planEntry(props, resolver, true);
81
- }
82
- function planHeadElementEntry(props, resolver) {
83
- return planEntry(props, resolver, false);
84
- }
85
- var STATIC_VALUE_RESOLVER = {
86
- resolveValue: (_name, value) => String(value)
87
- };
88
- function escapeHeadAttrValue(value) {
89
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
90
- }
91
- function serializeHeadEntry(tag, attrs) {
92
- const plan = tag === "meta" ? planMetaEntry(attrs, STATIC_VALUE_RESOLVER) : planHeadElementEntry(attrs, STATIC_VALUE_RESOLVER);
93
- if (plan.kind === "drop") return "";
94
- const pairs = Array.from(plan.attributes, ([name, value]) => `${name}="${escapeHeadAttrValue(value)}"`).join(" ");
95
- return `<${tag} ${pairs} />`;
96
- }
97
-
98
- // src/platform/ssr.ts
99
- function isForbiddenSsrAttr(lowerName) {
100
- return isHtmlContentAttribute(lowerName);
101
- }
102
- var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
103
- function isSafeAttrName(name) {
104
- return SAFE_ATTR_NAME.test(name);
105
- }
106
- function ssrErrorComment(err) {
107
- if (DEV) {
108
- const msg = escapeHtml(err instanceof Error ? err.message : String(err));
109
- return `<!--SSR error: ${safeCommentText(msg)}-->`;
110
- }
111
- return "<!--SSR error-->";
112
- }
113
- function safeCommentText(text) {
114
- return text.replace(/-->/g, "--&gt;").replace(/--!>/g, "--!&gt;").replace(/<!--/g, "&lt;!--").replace(/--$/g, "--&#45;");
115
- }
116
- var VOID_ELEMENTS = /* @__PURE__ */ new Set([
117
- "area",
118
- "base",
119
- "br",
120
- "col",
121
- "embed",
122
- "hr",
123
- "img",
124
- "input",
125
- "link",
126
- "meta",
127
- "param",
128
- "source",
129
- "track",
130
- "wbr"
131
- ]);
132
- function findNativeGetter(proto, name) {
133
- for (let p = proto; p; p = Object.getPrototypeOf(p)) {
134
- const get = Object.getOwnPropertyDescriptor(p, name)?.get;
135
- if (get) return get;
136
- }
137
- return null;
138
- }
139
- var nativeAccessorsByRealm = /* @__PURE__ */ new WeakMap();
140
- function nativeAccessors() {
141
- const realm = Element;
142
- let accessors = nativeAccessorsByRealm.get(realm);
143
- if (!accessors) {
144
- accessors = {
145
- attributes: findNativeGetter(Element.prototype, "attributes"),
146
- tagName: findNativeGetter(Element.prototype, "tagName"),
147
- childNodes: findNativeGetter(Node.prototype, "childNodes"),
148
- getAttribute: Element.prototype.getAttribute
149
- };
150
- nativeAccessorsByRealm.set(realm, accessors);
151
- }
152
- return accessors;
153
- }
154
- function attributesOf(el) {
155
- const get = nativeAccessors().attributes;
156
- return get ? get.call(el) : el.attributes;
157
- }
158
- function tagNameOf(el) {
159
- const get = nativeAccessors().tagName;
160
- return get ? get.call(el) : el.tagName;
161
- }
162
- function childNodesOf(node) {
163
- const get = nativeAccessors().childNodes;
164
- return get ? get.call(node) : node.childNodes;
165
- }
166
- function getAttributeOf(el, name) {
167
- return nativeAccessors().getAttribute.call(el, name);
168
- }
169
- var SVG_NS = "http://www.w3.org/2000/svg";
170
- var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
171
- var XLINK_NS = "http://www.w3.org/1999/xlink";
172
- var XML_NS = "http://www.w3.org/XML/1998/namespace";
173
- var XMLNS_NS = "http://www.w3.org/2000/xmlns/";
174
- var BREAKOUT_TAGS = /* @__PURE__ */ new Set([
175
- "b",
176
- "big",
177
- "blockquote",
178
- "body",
179
- "br",
180
- "center",
181
- "code",
182
- "dd",
183
- "div",
184
- "dl",
185
- "dt",
186
- "em",
187
- "embed",
188
- "h1",
189
- "h2",
190
- "h3",
191
- "h4",
192
- "h5",
193
- "h6",
194
- "head",
195
- "hr",
196
- "i",
197
- "img",
198
- "li",
199
- "listing",
200
- "menu",
201
- "meta",
202
- "nobr",
203
- "ol",
204
- "p",
205
- "pre",
206
- "ruby",
207
- "s",
208
- "small",
209
- "span",
210
- "strong",
211
- "strike",
212
- "sub",
213
- "sup",
214
- "table",
215
- "tt",
216
- "u",
217
- "ul",
218
- "var"
219
- ]);
220
- function isBreakout(lowerName, element) {
221
- if (BREAKOUT_TAGS.has(lowerName)) return true;
222
- return lowerName === "font" && (element.hasAttribute("color") || element.hasAttribute("face") || element.hasAttribute("size"));
223
- }
224
- function fitsParseContext(ns, lowerName, element, context) {
225
- if (ns === SVG_NS) {
226
- if (context === 1 /* Svg */) return !isBreakout(lowerName, element);
227
- return lowerName === "svg" && (context === 0 /* Html */ || context === 3 /* Annotation */);
228
- }
229
- if (ns === MATHML_NS) {
230
- if (context === 2 /* Math */ || context === 3 /* Annotation */) return !isBreakout(lowerName, element);
231
- return lowerName === "math" && context === 0 /* Html */;
232
- }
233
- return context === 0 /* Html */;
234
- }
235
- function childParseContext(ns, lowerName, element) {
236
- if (ns === SVG_NS) {
237
- return lowerName === "foreignobject" || lowerName === "desc" || lowerName === "title" ? 0 /* Html */ : 1 /* Svg */;
238
- }
239
- if (ns === MATHML_NS) {
240
- if (lowerName === "mi" || lowerName === "mo" || lowerName === "mn" || lowerName === "ms" || lowerName === "mtext") {
241
- return 0 /* Html */;
242
- }
243
- if (lowerName === "annotation-xml") {
244
- const encoding = (element.getAttribute("encoding") ?? "").toLowerCase();
245
- return encoding === "text/html" || encoding === "application/xhtml+xml" ? 0 /* Html */ : 3 /* Annotation */;
246
- }
247
- return 2 /* Math */;
248
- }
249
- return 0 /* Html */;
250
- }
251
- function foreignAttrName(attr) {
252
- const ns = attr.namespaceURI;
253
- if (ns === XLINK_NS) return `xlink:${attr.localName}`;
254
- if (ns === XML_NS) return `xml:${attr.localName}`;
255
- if (ns === XMLNS_NS) return attr.localName === "xmlns" ? "xmlns" : `xmlns:${attr.localName}`;
256
- return attr.name;
257
- }
258
- function serializeOpenTag(element, tag, foreign) {
259
- let html = `<${tag}`;
260
- const meta = tag === "meta" && !foreign ? /* @__PURE__ */ new Map() : null;
261
- const embed = tag === "iframe" && !foreign ? getEmbedRecord(element) : void 0;
262
- const embedSeen = embed ? { sandbox: false, referrer: false, title: false, fullscreen: false } : null;
263
- const attrs = attributesOf(element);
264
- for (let i = 0; i < attrs.length; i++) {
265
- const attr = attrs[i];
266
- const rawName = foreign ? foreignAttrName(attr) : attr.name;
267
- if (!isSafeAttrName(rawName)) continue;
268
- if (isEventHandlerAttr(rawName)) continue;
269
- if (isForbiddenSsrAttr(rawName)) continue;
270
- const lowerName = rawName.toLowerCase();
271
- let value = resolveAttributeValue(lowerName, attr.value);
272
- if (value === null) continue;
273
- if (foreign && isRefusedAnimationAttribute(element, rawName, value)) continue;
274
- if (embed && embedSeen) {
275
- value = embedSerializedAttribute(embed, embedSeen, lowerName, value);
276
- if (value === null) continue;
277
- }
278
- meta?.set(rawName, value);
279
- html += ` ${rawName}="${escapeAttr(value)}"`;
280
- }
281
- if (meta && isForbiddenMetaSnapshot(meta)) return null;
282
- if (embed && embedSeen) {
283
- const tail = embedSerializationTail(embed, embedSeen);
284
- if (tail === null) return null;
285
- html += tail;
286
- }
287
- if (!getAttributeOf(element, "data-sibu-hydrate")) {
288
- html += ` data-sibu-ssr="true"`;
289
- }
290
- return html;
291
- }
292
- function serializeNode(node, context) {
293
- if (node.nodeType === 3) return escapeHtml(node.textContent || "");
294
- if (node.nodeType === 8) return `<!--${safeCommentText(node.textContent || "")}-->`;
295
- let foreign = false;
296
- if (!(node instanceof HTMLElement)) {
297
- const ns2 = node.nodeType === 1 ? node.namespaceURI : null;
298
- if (ns2 !== SVG_NS && ns2 !== MATHML_NS) return escapeHtml(node.textContent || "");
299
- foreign = true;
300
- }
301
- const element = node;
302
- const tag = foreign ? element.localName : tagNameOf(element).toLowerCase();
303
- if (isCodeTextElement(tag)) return DEV ? `<!--ssr:${tag}-stripped-->` : "";
304
- if (!/^[a-z][a-z0-9-]*$/i.test(tag)) return DEV ? "<!--ssr:invalid-tag-->" : "";
305
- const ns = foreign ? element.namespaceURI : null;
306
- const lowerName = tag.toLowerCase();
307
- if (!fitsParseContext(ns, lowerName, element, context)) return DEV ? `<!--ssr:${tag}-misplaced-->` : "";
308
- const openTag = serializeOpenTag(element, tag, foreign);
309
- if (openTag === null) return DEV ? `<!--ssr:${tag}-stripped-->` : "";
310
- const children = childNodesOf(element);
311
- if (foreign ? children.length === 0 : VOID_ELEMENTS.has(tag)) return `${openTag} />`;
312
- return {
313
- open: `${openTag}>`,
314
- close: `</${tag}>`,
315
- children: Array.from(children),
316
- context: childParseContext(ns, lowerName, element)
317
- };
318
- }
319
- function renderToString(element) {
320
- return renderNodeToString(element, 0 /* Html */);
321
- }
322
- function renderNodeToString(element, context) {
323
- if (element instanceof DocumentFragment) {
324
- return Array.from(element.childNodes).map((child) => {
325
- try {
326
- return renderNodeToString(child, context);
327
- } catch (err) {
328
- return ssrErrorComment(err);
329
- }
330
- }).join("");
331
- }
332
- const frame = serializeNode(element, context);
333
- if (typeof frame === "string") return frame;
334
- let html = frame.open;
335
- for (const child of frame.children) {
336
- try {
337
- html += renderNodeToString(child, frame.context);
338
- } catch (err) {
339
- html += ssrErrorComment(err);
340
- }
341
- }
342
- return html + frame.close;
343
- }
344
- function hydrate(component, container, options = {}) {
345
- const clientTree = component();
346
- if (options.diagnostics) {
347
- const mismatches = [];
348
- collectMismatches(container.firstElementChild, clientTree, "", mismatches);
349
- if (mismatches.length > 0) {
350
- const first = mismatches[0];
351
- if (options.onMismatch) {
352
- options.onMismatch(first);
353
- } else if (DEV) {
354
- console.warn(
355
- `[SibuJS hydration] ${first.message}
356
- at ${first.path}
357
- server: ${first.serverValue}
358
- client: ${first.clientValue}`
359
- );
360
- }
361
- }
362
- }
363
- replaceChildrenSafely(container, clientTree);
364
- container.setAttribute("data-sibu-hydrated", "true");
365
- }
366
- function directTextOf(node) {
367
- let out = "";
368
- for (const child of Array.from(node.childNodes)) {
369
- if (child.nodeType === 3) out += child.nodeValue ?? "";
370
- }
371
- return out.replace(/\s+/g, " ").trim();
372
- }
373
- function disposeAndReplace(outgoing, next) {
374
- dispose(outgoing);
375
- outgoing.replaceWith(next);
376
- }
377
- function collectMismatches(serverNode, clientNode, path, out, max = 5) {
378
- if (out.length >= max) return;
379
- const nodePath = path || clientNode?.tagName?.toLowerCase() || "(root)";
380
- if (!serverNode && clientNode) {
381
- out.push({
382
- kind: "child-count",
383
- path: nodePath,
384
- serverValue: "(missing)",
385
- clientValue: clientNode.tagName.toLowerCase(),
386
- message: "Client rendered a node that the server did not emit."
387
- });
388
- return;
389
- }
390
- if (serverNode && !clientNode) {
391
- out.push({
392
- kind: "child-count",
393
- path: nodePath,
394
- serverValue: serverNode.tagName.toLowerCase(),
395
- clientValue: "(missing)",
396
- message: "Server rendered a node that the client did not produce."
397
- });
398
- return;
399
- }
400
- if (!serverNode || !clientNode) return;
401
- if (serverNode.tagName !== clientNode.tagName) {
402
- out.push({
403
- kind: "tag",
404
- path: nodePath,
405
- serverValue: serverNode.tagName.toLowerCase(),
406
- clientValue: clientNode.tagName.toLowerCase(),
407
- message: "Element tag mismatch \u2014 server and client disagree on the element type."
408
- });
409
- return;
410
- }
411
- const skipAttrs = /* @__PURE__ */ new Set(["data-sibu-ssr", "data-sibu-hydrated", "data-sibu-island"]);
412
- const serverAttrs = /* @__PURE__ */ new Map();
413
- for (const a of Array.from(serverNode.attributes)) {
414
- if (!skipAttrs.has(a.name)) serverAttrs.set(a.name, a.value);
415
- }
416
- const clientAttrs = /* @__PURE__ */ new Map();
417
- for (const a of Array.from(clientNode.attributes)) {
418
- if (!skipAttrs.has(a.name)) clientAttrs.set(a.name, a.value);
419
- }
420
- for (const [name, value] of serverAttrs) {
421
- if (out.length >= max) return;
422
- if (!clientAttrs.has(name)) {
423
- out.push({
424
- kind: "attribute",
425
- path: `${nodePath}[${name}]`,
426
- serverValue: value,
427
- clientValue: "(missing)",
428
- message: `Attribute "${name}" present on server but missing on client.`
429
- });
430
- } else if (clientAttrs.get(name) !== value) {
431
- out.push({
432
- kind: "attribute",
433
- path: `${nodePath}[${name}]`,
434
- serverValue: value,
435
- clientValue: clientAttrs.get(name) ?? "",
436
- message: `Attribute "${name}" differs between server and client.`
437
- });
438
- }
439
- }
440
- for (const [name, value] of clientAttrs) {
441
- if (out.length >= max) return;
442
- if (!serverAttrs.has(name)) {
443
- out.push({
444
- kind: "attribute",
445
- path: `${nodePath}[${name}]`,
446
- serverValue: "(missing)",
447
- clientValue: value,
448
- message: `Attribute "${name}" present on client but missing on server.`
449
- });
450
- }
451
- }
452
- const serverText = directTextOf(serverNode);
453
- const clientText = directTextOf(clientNode);
454
- if (serverText !== clientText) {
455
- out.push({
456
- kind: "text",
457
- path: nodePath,
458
- serverValue: serverText,
459
- clientValue: clientText,
460
- message: "Text content differs between server and client."
461
- });
462
- if (out.length >= max) return;
463
- }
464
- const serverChildren = Array.from(serverNode.children);
465
- const clientChildren = Array.from(clientNode.children);
466
- const max2 = Math.max(serverChildren.length, clientChildren.length);
467
- for (let i = 0; i < max2; i++) {
468
- if (out.length >= max) return;
469
- const childPath = `${nodePath} > ${clientChildren[i]?.tagName?.toLowerCase() ?? serverChildren[i]?.tagName?.toLowerCase() ?? "?"}:nth-child(${i + 1})`;
470
- collectMismatches(serverChildren[i] ?? null, clientChildren[i] ?? null, childPath, out, max);
471
- }
472
- }
473
- function trustHTML(html) {
474
- return html;
475
- }
476
- function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
477
- if (!attrs) return "";
478
- const out = [];
479
- for (const rawKey of Object.keys(attrs)) {
480
- if (!Object.hasOwn(attrs, rawKey)) continue;
481
- if (!isSafeAttrName(rawKey)) continue;
482
- if (!allowEventHandlers && isEventHandlerAttr(rawKey)) continue;
483
- if (isForbiddenSsrAttr(rawKey)) continue;
484
- const value = resolveAttributeValue(rawKey.toLowerCase(), String(attrs[rawKey]));
485
- if (value === null) continue;
486
- out.push(`${rawKey}="${escapeAttr(value)}"`);
487
- }
488
- return out.join(" ");
489
- }
490
- function isDangerousMetaRefresh(metaProps) {
491
- return isForbiddenMetaEntry(metaProps);
492
- }
493
- function renderToDocument(component, options = {}) {
494
- let content;
495
- try {
496
- content = renderToString(component());
497
- } catch (err) {
498
- content = ssrErrorComment(err);
499
- }
500
- const metaTags = (options.meta || []).map((attrs) => serializeHeadEntry("meta", attrs)).filter(Boolean).join("\n ");
501
- const linkTags = (options.links || []).map((attrs) => serializeHeadEntry("link", attrs)).filter(Boolean).join("\n ");
502
- const scriptTags = (options.scripts || []).map((src) => {
503
- const safe = sanitizeUrl(String(src));
504
- if (!safe) return "";
505
- return `<script src="${escapeAttr(safe)}"></script>`;
506
- }).filter(Boolean).join("\n ");
507
- const bodyAttrPairs = buildAttrString(options.bodyAttrs);
508
- const bodyAttrs = bodyAttrPairs ? ` ${bodyAttrPairs}` : "";
509
- return `<!DOCTYPE html>
510
- <html>
511
- <head>
512
- <meta charset="UTF-8" />
513
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
514
- ${options.title != null ? `<title>${escapeHtml(options.title)}</title>` : ""}
515
- ${metaTags}
516
- ${linkTags}
517
- ${options.headExtra || ""}
518
- </head>
519
- <body${bodyAttrs}>
520
- <div id="app">${content}</div>
521
- ${scriptTags}
522
- </body>
523
- </html>`;
524
- }
525
- function renderToStream(element) {
526
- return streamNode(element, 0 /* Html */);
527
- }
528
- async function* streamNode(element, context) {
529
- if (element instanceof DocumentFragment) {
530
- for (const child of Array.from(element.childNodes)) {
531
- try {
532
- yield* streamNode(child, context);
533
- } catch (err) {
534
- yield ssrErrorComment(err);
535
- }
536
- }
537
- return;
538
- }
539
- const frame = serializeNode(element, context);
540
- if (typeof frame === "string") {
541
- if (frame) yield frame;
542
- return;
543
- }
544
- yield frame.open;
545
- for (const child of frame.children) {
546
- try {
547
- yield* streamNode(child, frame.context);
548
- } catch (err) {
549
- yield ssrErrorComment(err);
550
- }
551
- }
552
- yield frame.close;
553
- }
554
- async function collectStream(stream) {
555
- let result = "";
556
- for await (const chunk of stream) {
557
- result += chunk;
558
- }
559
- return result;
560
- }
561
- function renderToReadableStream(element) {
562
- const generator = renderToStream(element);
563
- return new ReadableStream({
564
- async pull(controller) {
565
- const { value, done } = await generator.next();
566
- if (done) {
567
- controller.close();
568
- } else {
569
- controller.enqueue(value);
570
- }
571
- },
572
- async cancel() {
573
- await generator.return(void 0);
574
- }
575
- });
576
- }
577
- var SAFE_ID = /^[A-Za-z0-9_-]+$/;
578
- function island(id, component) {
579
- if (!SAFE_ID.test(id)) {
580
- throw new Error(`[SibuJS SSR] island: id must match [A-Za-z0-9_-]+ (got: ${JSON.stringify(id.slice(0, 32))})`);
581
- }
582
- const el = component();
583
- el.setAttribute("data-sibu-island", id);
584
- return el;
585
- }
586
- function isAlreadyHydrated(element) {
587
- return element.hasAttribute("data-sibu-hydrated");
588
- }
589
- function hydrateIslands(container, islands) {
590
- const markers = container.querySelectorAll("[data-sibu-island]");
591
- for (const marker of Array.from(markers)) {
592
- if (isAlreadyHydrated(marker)) continue;
593
- const id = marker.getAttribute("data-sibu-island") ?? "";
594
- if (!Object.hasOwn(islands, id)) continue;
595
- const factory = islands[id];
596
- if (typeof factory !== "function") continue;
597
- const clientTree = factory();
598
- clientTree.setAttribute("data-sibu-island", id);
599
- clientTree.setAttribute("data-sibu-hydrated", "true");
600
- disposeAndReplace(marker, clientTree);
601
- }
602
- container.setAttribute("data-sibu-hydrated", "partial");
603
- }
604
- function hydrateProgressively(container, islands, options) {
605
- const markers = container.querySelectorAll("[data-sibu-island]");
606
- const cleanups = [];
607
- for (const marker of Array.from(markers)) {
608
- if (isAlreadyHydrated(marker)) continue;
609
- const id = marker.getAttribute("data-sibu-island") ?? "";
610
- if (!Object.hasOwn(islands, id)) continue;
611
- const factory = islands[id];
612
- if (typeof factory !== "function") continue;
613
- const observer = new IntersectionObserver(
614
- (entries) => {
615
- for (const entry of entries) {
616
- if (entry.isIntersecting) {
617
- observer.disconnect();
618
- if (isAlreadyHydrated(marker) || marker.parentNode === null) break;
619
- const clientTree = factory();
620
- clientTree.setAttribute("data-sibu-island", id);
621
- clientTree.setAttribute("data-sibu-hydrated", "true");
622
- disposeAndReplace(marker, clientTree);
623
- break;
624
- }
625
- }
626
- },
627
- { rootMargin: "200px", ...options }
628
- );
629
- observer.observe(marker);
630
- cleanups.push(() => observer.disconnect());
631
- }
632
- container.setAttribute("data-sibu-hydrated", "progressive");
633
- return () => {
634
- for (const cleanup of cleanups) cleanup();
635
- };
636
- }
637
- function resetSSRState() {
638
- getSSRStore().suspenseIdCounter = 0;
639
- }
640
- function noop() {
641
- }
642
- function ssrSuspense(props) {
643
- const store = getSSRStore();
644
- const id = `sibu-sus-${store.suspenseIdCounter++}`;
645
- const timeoutMs = props.timeoutMs ?? 3e4;
646
- const fallbackEl = props.fallback();
647
- const wrapper = document.createElement("div");
648
- wrapper.setAttribute("data-sibu-suspense-id", id);
649
- wrapper.appendChild(fallbackEl);
650
- const fallbackHtml = renderToString(fallbackEl);
651
- let timer;
652
- const timeoutPromise = new Promise((_, reject) => {
653
- timer = setTimeout(() => reject(new Error(`[SibuJS SSR] ssrSuspense timed out after ${timeoutMs}ms`)), timeoutMs);
654
- });
655
- let contentPromise;
656
- try {
657
- contentPromise = Promise.resolve(props.content());
658
- } catch (err) {
659
- contentPromise = Promise.reject(err);
660
- }
661
- const toFallback = (err) => {
662
- if (DEV) console.warn("[SibuJS SSR] ssrSuspense rejected:", err);
663
- return { id, html: fallbackHtml };
664
- };
665
- const clearTimer = () => {
666
- if (timer !== void 0) clearTimeout(timer);
667
- };
668
- const raced = Promise.race([contentPromise, timeoutPromise]);
669
- const promise = raced.then(
670
- (resolvedEl) => {
671
- clearTimer();
672
- try {
673
- return { id, html: renderToString(resolvedEl) };
674
- } catch (err) {
675
- return toFallback(err);
676
- }
677
- },
678
- (err) => {
679
- clearTimer();
680
- return toFallback(err);
681
- }
682
- );
683
- promise.catch(noop);
684
- return { element: wrapper, promise };
685
- }
686
- var SAFE_SUSPENSE_ID = /^[A-Za-z0-9_-]+$/;
687
- function suspenseSwapScript(id, nonce) {
688
- if (!SAFE_SUSPENSE_ID.test(id)) {
689
- throw new Error(
690
- `[SibuJS SSR] suspenseSwapScript: id must match [A-Za-z0-9_-]+ (got: ${JSON.stringify(id.slice(0, 32))})`
691
- );
692
- }
693
- const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
694
- return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(f.firstChild)f.removeChild(f.firstChild);while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
695
- }
696
- async function* renderToSuspenseStream(element, pendingBoundaries = [], options) {
697
- yield* renderToStream(element);
698
- if (pendingBoundaries.length > 0) {
699
- const resolved = await Promise.all(pendingBoundaries);
700
- for (const { id, html } of resolved) {
701
- if (!SAFE_SUSPENSE_ID.test(id)) continue;
702
- yield `<div hidden id="sibu-resolved-${id}">${html}</div>`;
703
- yield suspenseSwapScript(id, options?.nonce);
704
- }
705
- }
706
- }
707
- var SSR_DATA_ATTR = "__SIBU_SSR_DATA__";
708
- function escapeScriptJson(json) {
709
- return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
710
- }
711
- var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
712
- function serializeState(state, nonce, options) {
713
- const rawJson = JSON.stringify(state);
714
- const maxBytes = options?.maxBytes ?? DEFAULT_MAX_SSR_BYTES;
715
- const byteLen = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(rawJson).byteLength : Buffer.byteLength(rawJson, "utf8");
716
- if (byteLen > maxBytes) {
717
- throw new Error(`[SibuJS SSR] serializeState: payload (${byteLen} bytes) exceeds maxBytes (${maxBytes})`);
718
- }
719
- const json = escapeScriptJson(rawJson);
720
- const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
721
- return `<script${nonceAttr}>window.${SSR_DATA_ATTR}=${json}</script>`;
722
- }
723
- function deserializeState(validate) {
724
- if (typeof window === "undefined") return void 0;
725
- if (DEV && !validate) {
726
- console.warn(
727
- "[SibuJS SSR] deserializeState() called without a validate guard \u2014 tampered SSR payloads will not be detected."
728
- );
729
- }
730
- const raw = readOwnGlobal(SSR_DATA_ATTR);
731
- if (raw === void 0) return void 0;
732
- if (validate && !validate(raw)) return void 0;
733
- return raw;
734
- }
735
- function escapeHtml(str) {
736
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
737
- }
738
- function escapeAttr(str) {
739
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
740
- }
741
-
742
- export {
743
- planMetaEntry,
744
- planHeadElementEntry,
745
- STATIC_VALUE_RESOLVER,
746
- serializeHeadEntry,
747
- renderToString,
748
- hydrate,
749
- trustHTML,
750
- isDangerousMetaRefresh,
751
- renderToDocument,
752
- renderToStream,
753
- collectStream,
754
- renderToReadableStream,
755
- island,
756
- hydrateIslands,
757
- hydrateProgressively,
758
- resetSSRState,
759
- ssrSuspense,
760
- suspenseSwapScript,
761
- renderToSuspenseStream,
762
- escapeScriptJson,
763
- serializeState,
764
- deserializeState
765
- };