sibujs 3.4.1 → 4.0.1
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.
- package/LICENSE +21 -21
- package/README.md +118 -15
- package/dist/browser.cjs +410 -101
- package/dist/browser.d.cts +50 -5
- package/dist/browser.d.ts +50 -5
- package/dist/browser.js +8 -6
- package/dist/build.cjs +784 -392
- package/dist/build.js +15 -15
- package/dist/cdn.global.js +11 -7
- package/dist/{chunk-PBZWJQNY.js → chunk-52ERIVFD.js} +8 -8
- package/dist/chunk-52XFPGSN.js +123 -0
- package/dist/{chunk-Y22QAKJX.js → chunk-5BVKQX2F.js} +4 -4
- package/dist/chunk-5INI7D2L.js +194 -0
- package/dist/{chunk-4I3M326N.js → chunk-AD5YSZAN.js} +19 -25
- package/dist/{chunk-F7HW2NI5.js → chunk-AW6HPJ3L.js} +1 -1
- package/dist/chunk-DHDZ7IVN.js +109 -0
- package/dist/{chunk-VQZDHAEK.js → chunk-E72G5JSK.js} +1 -1
- package/dist/{chunk-MPE75KOA.js → chunk-HPXTUPJD.js} +259 -124
- package/dist/{chunk-EX77FXTT.js → chunk-HYCCIYNS.js} +228 -51
- package/dist/{chunk-FWFEBYQ6.js → chunk-IKVUUE7D.js} +115 -41
- package/dist/{chunk-5MFF5EPT.js → chunk-J4WZMCIW.js} +5 -5
- package/dist/{chunk-BQLVTAUZ.js → chunk-JYXQCELN.js} +57 -22
- package/dist/{chunk-WEZ5SP7L.js → chunk-PHB3MCK4.js} +73 -34
- package/dist/chunk-PHRH66Y7.js +147 -0
- package/dist/{chunk-S2VW4NBA.js → chunk-PJDSI2K7.js} +295 -198
- package/dist/{chunk-QHPUFNHZ.js → chunk-PTLJ7NQX.js} +3 -3
- package/dist/{chunk-NXR2YFZB.js → chunk-QD4I7ZGZ.js} +24 -34
- package/dist/{chunk-S3NFJO6L.js → chunk-TIRZCERI.js} +36 -5
- package/dist/{chunk-7PNKW3FJ.js → chunk-TNEHT3NG.js} +232 -67
- package/dist/{chunk-335KJEN2.js → chunk-TVURNWDK.js} +5 -3
- package/dist/{chunk-CV7FAQ2R.js → chunk-UNAYBND2.js} +9 -3
- package/dist/chunk-VPP2FONR.js +142 -0
- package/dist/{chunk-X4JS367G.js → chunk-XDOMTQHM.js} +14 -10
- package/dist/chunk-XET2RUJF.js +61 -0
- package/dist/{chunk-3FFCUIGV.js → chunk-XZMCVZW6.js} +309 -188
- package/dist/{chunk-YDWP3ZOU.js → chunk-YLV26GAJ.js} +228 -86
- package/dist/{contracts-ey_Qh8ef.d.ts → contracts-Hg6ITAEF.d.cts} +6 -6
- package/dist/{contracts-ey_Qh8ef.d.cts → contracts-Hg6ITAEF.d.ts} +6 -6
- package/dist/{customElement-CPfIrbvg.d.cts → customElement-CKLoO63w.d.cts} +73 -11
- package/dist/{customElement-CPfIrbvg.d.ts → customElement-CKLoO63w.d.ts} +73 -11
- package/dist/data.cjs +483 -205
- package/dist/data.d.cts +139 -7
- package/dist/data.d.ts +139 -7
- package/dist/data.js +9 -9
- package/dist/devtools.cjs +222 -87
- package/dist/devtools.js +8 -8
- package/dist/dispose-4FZ6OFXR.js +19 -0
- package/dist/ecosystem.cjs +426 -182
- package/dist/ecosystem.js +11 -11
- package/dist/extras.cjs +1988 -772
- package/dist/extras.d.cts +7 -7
- package/dist/extras.d.ts +7 -7
- package/dist/extras.js +29 -26
- package/dist/index.cjs +791 -392
- package/dist/index.d.cts +299 -28
- package/dist/index.d.ts +299 -28
- package/dist/index.js +33 -18
- package/dist/motion.cjs +180 -50
- package/dist/motion.d.cts +26 -0
- package/dist/motion.d.ts +26 -0
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +437 -170
- package/dist/patterns.d.cts +60 -13
- package/dist/patterns.d.ts +60 -13
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +333 -148
- package/dist/performance.d.cts +55 -7
- package/dist/performance.d.ts +55 -7
- package/dist/performance.js +9 -9
- package/dist/plugins.cjs +1378 -583
- package/dist/plugins.d.cts +227 -9
- package/dist/plugins.d.ts +227 -9
- package/dist/plugins.js +610 -321
- package/dist/{ssr-D62yFwuw.d.cts → ssr-BiPRdZ6n.d.cts} +44 -15
- package/dist/{ssr-D62yFwuw.d.ts → ssr-BiPRdZ6n.d.ts} +44 -15
- package/dist/{ssr-2PPULEK2.js → ssr-JFP5535P.js} +5 -4
- package/dist/ssr.cjs +1111 -450
- package/dist/ssr.d.cts +230 -20
- package/dist/ssr.d.ts +230 -20
- package/dist/ssr.js +15 -12
- package/dist/{startup-0Qv6aosO.d.cts → startup-BMpaiMhP.d.cts} +1 -1
- package/dist/{startup-0Qv6aosO.d.ts → startup-BMpaiMhP.d.ts} +1 -1
- package/dist/testing.cjs +205 -63
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +8 -10
- package/dist/ui.cjs +640 -199
- package/dist/ui.d.cts +2 -2
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +10 -10
- package/dist/widgets.cjs +207 -82
- package/dist/widgets.js +9 -9
- package/package.json +12 -7
- package/dist/chunk-5VGSK6D2.js +0 -80
- package/dist/chunk-6CAJU5TP.js +0 -38
- package/dist/chunk-COY6PUD2.js +0 -34
- package/dist/chunk-DLBFEAIS.js +0 -96
- package/dist/chunk-L3GAGWCC.js +0 -111
|
@@ -1,39 +1,195 @@
|
|
|
1
1
|
import {
|
|
2
|
+
canonicalAttrName,
|
|
2
3
|
isEventHandlerAttr,
|
|
3
|
-
|
|
4
|
+
isHtmlContentAttribute,
|
|
5
|
+
isPolicyAttribute,
|
|
6
|
+
sanitizeAttributeString,
|
|
4
7
|
sanitizeUrl,
|
|
5
8
|
stripControlChars
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-5INI7D2L.js";
|
|
7
10
|
import {
|
|
8
11
|
getSSRStore
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TIRZCERI.js";
|
|
13
|
+
import {
|
|
14
|
+
dispose,
|
|
15
|
+
replaceChildrenSafely
|
|
16
|
+
} from "./chunk-DHDZ7IVN.js";
|
|
10
17
|
import {
|
|
11
18
|
isDev
|
|
12
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VPP2FONR.js";
|
|
20
|
+
|
|
21
|
+
// src/utils/metaRefresh.ts
|
|
22
|
+
function isAsciiWhitespace(ch) {
|
|
23
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
24
|
+
}
|
|
25
|
+
function trimAsciiWhitespace(value) {
|
|
26
|
+
let start = 0;
|
|
27
|
+
let end = value.length;
|
|
28
|
+
while (start < end && isAsciiWhitespace(value[start])) start++;
|
|
29
|
+
while (end > start && isAsciiWhitespace(value[end - 1])) end--;
|
|
30
|
+
return value.slice(start, end);
|
|
31
|
+
}
|
|
32
|
+
var forbidden = (reason) => ({ kind: "forbidden", reason });
|
|
33
|
+
function parseMetaRefreshContent(content) {
|
|
34
|
+
const raw = trimAsciiWhitespace(stripControlChars(content));
|
|
35
|
+
if (raw === "") return forbidden("empty content");
|
|
36
|
+
let i = 0;
|
|
37
|
+
while (i < raw.length && raw[i] >= "0" && raw[i] <= "9") i++;
|
|
38
|
+
if (i === 0) return forbidden("missing or non-numeric delay");
|
|
39
|
+
const delay = Number.parseInt(raw.slice(0, i), 10);
|
|
40
|
+
if (!Number.isFinite(delay)) return forbidden("unparseable delay");
|
|
41
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
42
|
+
if (i === raw.length) return { kind: "delay-only", delay };
|
|
43
|
+
if (raw[i] !== ";" && raw[i] !== ",") return forbidden("unexpected text after delay");
|
|
44
|
+
i++;
|
|
45
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
46
|
+
const keyStart = i;
|
|
47
|
+
while (i < raw.length && !isAsciiWhitespace(raw[i]) && raw[i] !== "=") i++;
|
|
48
|
+
const key = raw.slice(keyStart, i).toLowerCase();
|
|
49
|
+
if (key !== "url") return forbidden(`unsupported refresh key ${JSON.stringify(key)}`);
|
|
50
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
51
|
+
if (raw[i] !== "=") return forbidden("missing '=' after url");
|
|
52
|
+
i++;
|
|
53
|
+
while (i < raw.length && isAsciiWhitespace(raw[i])) i++;
|
|
54
|
+
let destination;
|
|
55
|
+
const quote = raw[i];
|
|
56
|
+
if (quote === '"' || quote === "'") {
|
|
57
|
+
const close = raw.indexOf(quote, i + 1);
|
|
58
|
+
if (close === -1) return forbidden("unterminated quoted destination");
|
|
59
|
+
destination = raw.slice(i + 1, close);
|
|
60
|
+
const rest = trimAsciiWhitespace(raw.slice(close + 1));
|
|
61
|
+
if (rest !== "") return forbidden("trailing text after quoted destination");
|
|
62
|
+
} else {
|
|
63
|
+
destination = trimAsciiWhitespace(raw.slice(i));
|
|
64
|
+
if (/[;,]/.test(destination)) return forbidden("multiple refresh assignments");
|
|
65
|
+
}
|
|
66
|
+
if (destination === "") return forbidden("empty destination");
|
|
67
|
+
const sanitizedDestination = sanitizeUrl(destination);
|
|
68
|
+
if (sanitizedDestination === "") return forbidden("destination uses a disallowed protocol");
|
|
69
|
+
return { kind: "allowed", delay, destination, sanitizedDestination };
|
|
70
|
+
}
|
|
71
|
+
function resolveMetaRefreshPolicy(attributes) {
|
|
72
|
+
let httpEquiv;
|
|
73
|
+
let content;
|
|
74
|
+
let duplicateName;
|
|
75
|
+
const seen = /* @__PURE__ */ new Set();
|
|
76
|
+
for (const [key, value] of attributes) {
|
|
77
|
+
const canonical = canonicalAttrName(key);
|
|
78
|
+
if (seen.has(canonical)) duplicateName = canonical;
|
|
79
|
+
seen.add(canonical);
|
|
80
|
+
if (canonical === "http-equiv") httpEquiv = value;
|
|
81
|
+
else if (canonical === "content") content = value;
|
|
82
|
+
}
|
|
83
|
+
if (duplicateName !== void 0) {
|
|
84
|
+
return forbidden(`duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}`);
|
|
85
|
+
}
|
|
86
|
+
if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
|
|
87
|
+
if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
|
|
88
|
+
return { kind: "not-refresh" };
|
|
89
|
+
}
|
|
90
|
+
if (typeof content !== "string") return { kind: "not-refresh" };
|
|
91
|
+
return parseMetaRefreshContent(content);
|
|
92
|
+
}
|
|
93
|
+
function normalizeMetaAttributes(props) {
|
|
94
|
+
const out = /* @__PURE__ */ new Map();
|
|
95
|
+
const seen = /* @__PURE__ */ new Set();
|
|
96
|
+
for (const key of Object.keys(props)) {
|
|
97
|
+
const canonical = canonicalAttrName(key);
|
|
98
|
+
if (seen.has(canonical)) return null;
|
|
99
|
+
seen.add(canonical);
|
|
100
|
+
out.set(key, props[key]);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
function isForbiddenMetaEntry(props) {
|
|
105
|
+
const normalized = normalizeMetaAttributes(props);
|
|
106
|
+
if (normalized === null) return true;
|
|
107
|
+
return resolveMetaRefreshPolicy(normalized).kind === "forbidden";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/utils/headEntry.ts
|
|
111
|
+
var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
112
|
+
function isEmittableHeadAttrName(name) {
|
|
113
|
+
if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
|
|
114
|
+
const canonical = canonicalAttrName(name);
|
|
115
|
+
if (isEventHandlerAttr(canonical)) return false;
|
|
116
|
+
if (isHtmlContentAttribute(canonical)) return false;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
function sanitizeHeadAttrValue(canonical, value) {
|
|
120
|
+
if (!isPolicyAttribute(canonical)) return value;
|
|
121
|
+
const safe = sanitizeAttributeString(canonical, value);
|
|
122
|
+
return safe === "" ? null : safe;
|
|
123
|
+
}
|
|
124
|
+
function findDuplicateAttributeName(names) {
|
|
125
|
+
const seen = /* @__PURE__ */ new Set();
|
|
126
|
+
for (const name of names) {
|
|
127
|
+
const canonical = canonicalAttrName(name);
|
|
128
|
+
if (seen.has(canonical)) return canonical;
|
|
129
|
+
seen.add(canonical);
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
function planEntry(props, resolver, applyRefreshPolicy) {
|
|
134
|
+
const rawNames = Object.keys(props);
|
|
135
|
+
const duplicate = findDuplicateAttributeName(rawNames);
|
|
136
|
+
if (duplicate !== null) {
|
|
137
|
+
return { kind: "drop", reason: `duplicate case-insensitive attribute ${JSON.stringify(duplicate)}` };
|
|
138
|
+
}
|
|
139
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
140
|
+
let reactive = false;
|
|
141
|
+
for (const name of rawNames) {
|
|
142
|
+
if (!Object.hasOwn(props, name)) continue;
|
|
143
|
+
if (!isEmittableHeadAttrName(name)) continue;
|
|
144
|
+
const authored = props[name];
|
|
145
|
+
if (resolver.isReactiveValue?.(authored)) reactive = true;
|
|
146
|
+
const canonical = canonicalAttrName(name);
|
|
147
|
+
const sanitized = sanitizeHeadAttrValue(canonical, resolver.resolveValue(name, authored));
|
|
148
|
+
if (sanitized === null) continue;
|
|
149
|
+
attributes.set(canonical, sanitized);
|
|
150
|
+
}
|
|
151
|
+
if (attributes.size === 0) return { kind: "drop", reason: "no effective attributes remain" };
|
|
152
|
+
const refresh = applyRefreshPolicy ? resolveMetaRefreshPolicy(attributes) : { kind: "not-refresh" };
|
|
153
|
+
if (refresh.kind === "forbidden") return { kind: "drop", reason: refresh.reason };
|
|
154
|
+
if (reactive && (refresh.kind === "allowed" || refresh.kind === "delay-only")) {
|
|
155
|
+
return { kind: "drop", reason: "a reactive meta entry may not publish a native refresh directive" };
|
|
156
|
+
}
|
|
157
|
+
return { kind: "publish", attributes, refresh };
|
|
158
|
+
}
|
|
159
|
+
function planMetaEntry(props, resolver) {
|
|
160
|
+
return planEntry(props, resolver, true);
|
|
161
|
+
}
|
|
162
|
+
function planHeadElementEntry(props, resolver) {
|
|
163
|
+
return planEntry(props, resolver, false);
|
|
164
|
+
}
|
|
165
|
+
var STATIC_VALUE_RESOLVER = {
|
|
166
|
+
resolveValue: (_name, value) => String(value)
|
|
167
|
+
};
|
|
168
|
+
function escapeHeadAttrValue(value) {
|
|
169
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
170
|
+
}
|
|
171
|
+
function serializeHeadEntry(tag, attrs) {
|
|
172
|
+
const plan = tag === "meta" ? planMetaEntry(attrs, STATIC_VALUE_RESOLVER) : planHeadElementEntry(attrs, STATIC_VALUE_RESOLVER);
|
|
173
|
+
if (plan.kind === "drop") return "";
|
|
174
|
+
const pairs = Array.from(plan.attributes, ([name, value]) => `${name}="${escapeHeadAttrValue(value)}"`).join(" ");
|
|
175
|
+
return `<${tag} ${pairs} />`;
|
|
176
|
+
}
|
|
13
177
|
|
|
14
178
|
// src/platform/ssr.ts
|
|
15
|
-
function
|
|
16
|
-
return
|
|
179
|
+
function isPolicyAttr(lowerName) {
|
|
180
|
+
return isPolicyAttribute(lowerName);
|
|
181
|
+
}
|
|
182
|
+
function isForbiddenSsrAttr(lowerName) {
|
|
183
|
+
return isHtmlContentAttribute(lowerName);
|
|
184
|
+
}
|
|
185
|
+
function sanitizeSsrAttributeValue(lowerName, value) {
|
|
186
|
+
return sanitizeAttributeString(lowerName, value);
|
|
17
187
|
}
|
|
18
188
|
var _isDev = isDev();
|
|
19
189
|
var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
20
190
|
function isSafeAttrName(name) {
|
|
21
191
|
return SAFE_ATTR_NAME.test(name);
|
|
22
192
|
}
|
|
23
|
-
var URL_ATTRS = /* @__PURE__ */ new Set([
|
|
24
|
-
"href",
|
|
25
|
-
"src",
|
|
26
|
-
"action",
|
|
27
|
-
"formaction",
|
|
28
|
-
"cite",
|
|
29
|
-
"poster",
|
|
30
|
-
"background",
|
|
31
|
-
"srcset",
|
|
32
|
-
"ping",
|
|
33
|
-
"manifest",
|
|
34
|
-
"data",
|
|
35
|
-
"xlink:href"
|
|
36
|
-
]);
|
|
37
193
|
function ssrErrorComment(err) {
|
|
38
194
|
if (_isDev) {
|
|
39
195
|
const msg = escapeHtml(err instanceof Error ? err.message : String(err));
|
|
@@ -91,10 +247,11 @@ function renderToString(element) {
|
|
|
91
247
|
const rawName = attr.name;
|
|
92
248
|
if (!isSafeAttrName(rawName)) continue;
|
|
93
249
|
if (isEventHandlerAttr(rawName)) continue;
|
|
250
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
94
251
|
const lowerName = rawName.toLowerCase();
|
|
95
252
|
let value = attr.value;
|
|
96
|
-
if (
|
|
97
|
-
value =
|
|
253
|
+
if (isPolicyAttr(lowerName)) {
|
|
254
|
+
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
98
255
|
if (!value) continue;
|
|
99
256
|
}
|
|
100
257
|
html += ` ${rawName}="${escapeAttr(value)}"`;
|
|
@@ -135,9 +292,20 @@ function hydrate(component, container, options = {}) {
|
|
|
135
292
|
}
|
|
136
293
|
}
|
|
137
294
|
}
|
|
138
|
-
container
|
|
295
|
+
replaceChildrenSafely(container, clientTree);
|
|
139
296
|
container.setAttribute("data-sibu-hydrated", "true");
|
|
140
297
|
}
|
|
298
|
+
function directTextOf(node) {
|
|
299
|
+
let out = "";
|
|
300
|
+
for (const child of Array.from(node.childNodes)) {
|
|
301
|
+
if (child.nodeType === 3) out += child.nodeValue ?? "";
|
|
302
|
+
}
|
|
303
|
+
return out.replace(/\s+/g, " ").trim();
|
|
304
|
+
}
|
|
305
|
+
function disposeAndReplace(outgoing, next) {
|
|
306
|
+
dispose(outgoing);
|
|
307
|
+
outgoing.replaceWith(next);
|
|
308
|
+
}
|
|
141
309
|
function collectMismatches(serverNode, clientNode, path, out, max = 5) {
|
|
142
310
|
if (out.length >= max) return;
|
|
143
311
|
const nodePath = path || clientNode?.tagName?.toLowerCase() || "(root)";
|
|
@@ -213,6 +381,18 @@ function collectMismatches(serverNode, clientNode, path, out, max = 5) {
|
|
|
213
381
|
});
|
|
214
382
|
}
|
|
215
383
|
}
|
|
384
|
+
const serverText = directTextOf(serverNode);
|
|
385
|
+
const clientText = directTextOf(clientNode);
|
|
386
|
+
if (serverText !== clientText) {
|
|
387
|
+
out.push({
|
|
388
|
+
kind: "text",
|
|
389
|
+
path: nodePath,
|
|
390
|
+
serverValue: serverText,
|
|
391
|
+
clientValue: clientText,
|
|
392
|
+
message: "Text content differs between server and client."
|
|
393
|
+
});
|
|
394
|
+
if (out.length >= max) return;
|
|
395
|
+
}
|
|
216
396
|
const serverChildren = Array.from(serverNode.children);
|
|
217
397
|
const clientChildren = Array.from(clientNode.children);
|
|
218
398
|
const max2 = Math.max(serverChildren.length, clientChildren.length);
|
|
@@ -232,10 +412,11 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
232
412
|
if (!Object.hasOwn(attrs, rawKey)) continue;
|
|
233
413
|
if (!isSafeAttrName(rawKey)) continue;
|
|
234
414
|
if (!allowEventHandlers && isEventHandlerAttr(rawKey)) continue;
|
|
415
|
+
if (isForbiddenSsrAttr(rawKey)) continue;
|
|
235
416
|
const lowerKey = rawKey.toLowerCase();
|
|
236
417
|
let value = String(attrs[rawKey]);
|
|
237
|
-
if (
|
|
238
|
-
value =
|
|
418
|
+
if (isPolicyAttr(lowerKey)) {
|
|
419
|
+
value = sanitizeSsrAttributeValue(lowerKey, value);
|
|
239
420
|
if (!value) continue;
|
|
240
421
|
}
|
|
241
422
|
out.push(`${rawKey}="${escapeAttr(value)}"`);
|
|
@@ -243,18 +424,7 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
243
424
|
return out.join(" ");
|
|
244
425
|
}
|
|
245
426
|
function isDangerousMetaRefresh(metaProps) {
|
|
246
|
-
|
|
247
|
-
let content;
|
|
248
|
-
for (const k in metaProps) {
|
|
249
|
-
const lk = k.toLowerCase();
|
|
250
|
-
if (lk === "http-equiv") httpEquiv = metaProps[k];
|
|
251
|
-
else if (lk === "content") content = metaProps[k];
|
|
252
|
-
}
|
|
253
|
-
if (typeof httpEquiv !== "string") return false;
|
|
254
|
-
if (httpEquiv.toLowerCase() !== "refresh") return false;
|
|
255
|
-
if (typeof content !== "string") return false;
|
|
256
|
-
const normalized = stripControlChars(content).toLowerCase();
|
|
257
|
-
return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
|
|
427
|
+
return isForbiddenMetaEntry(metaProps);
|
|
258
428
|
}
|
|
259
429
|
function renderToDocument(component, options = {}) {
|
|
260
430
|
let content;
|
|
@@ -263,15 +433,8 @@ function renderToDocument(component, options = {}) {
|
|
|
263
433
|
} catch (err) {
|
|
264
434
|
content = ssrErrorComment(err);
|
|
265
435
|
}
|
|
266
|
-
const metaTags = (options.meta || []).map((attrs) =>
|
|
267
|
-
|
|
268
|
-
const pairs = buildAttrString(attrs);
|
|
269
|
-
return pairs ? `<meta ${pairs} />` : "";
|
|
270
|
-
}).filter(Boolean).join("\n ");
|
|
271
|
-
const linkTags = (options.links || []).map((attrs) => {
|
|
272
|
-
const pairs = buildAttrString(attrs);
|
|
273
|
-
return pairs ? `<link ${pairs} />` : "";
|
|
274
|
-
}).filter(Boolean).join("\n ");
|
|
436
|
+
const metaTags = (options.meta || []).map((attrs) => serializeHeadEntry("meta", attrs)).filter(Boolean).join("\n ");
|
|
437
|
+
const linkTags = (options.links || []).map((attrs) => serializeHeadEntry("link", attrs)).filter(Boolean).join("\n ");
|
|
275
438
|
const scriptTags = (options.scripts || []).map((src) => {
|
|
276
439
|
const safe = sanitizeUrl(String(src));
|
|
277
440
|
if (!safe) return "";
|
|
@@ -332,14 +495,18 @@ async function* renderToStream(element) {
|
|
|
332
495
|
const rawName = attr.name;
|
|
333
496
|
if (!isSafeAttrName(rawName)) continue;
|
|
334
497
|
if (isEventHandlerAttr(rawName)) continue;
|
|
498
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
335
499
|
const lowerName = rawName.toLowerCase();
|
|
336
500
|
let value = attr.value;
|
|
337
|
-
if (
|
|
338
|
-
value =
|
|
501
|
+
if (isPolicyAttr(lowerName)) {
|
|
502
|
+
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
339
503
|
if (!value) continue;
|
|
340
504
|
}
|
|
341
505
|
openTag += ` ${rawName}="${escapeAttr(value)}"`;
|
|
342
506
|
}
|
|
507
|
+
if (element.dataset && !element.dataset.sibuHydrate) {
|
|
508
|
+
openTag += ` data-sibu-ssr="true"`;
|
|
509
|
+
}
|
|
343
510
|
if (VOID_ELEMENTS.has(tag)) {
|
|
344
511
|
yield `${openTag} />`;
|
|
345
512
|
return;
|
|
@@ -386,9 +553,13 @@ function island(id, component) {
|
|
|
386
553
|
el.setAttribute("data-sibu-island", id);
|
|
387
554
|
return el;
|
|
388
555
|
}
|
|
556
|
+
function isAlreadyHydrated(element) {
|
|
557
|
+
return element.hasAttribute("data-sibu-hydrated");
|
|
558
|
+
}
|
|
389
559
|
function hydrateIslands(container, islands) {
|
|
390
560
|
const markers = container.querySelectorAll("[data-sibu-island]");
|
|
391
561
|
for (const marker of Array.from(markers)) {
|
|
562
|
+
if (isAlreadyHydrated(marker)) continue;
|
|
392
563
|
const id = marker.getAttribute("data-sibu-island") ?? "";
|
|
393
564
|
if (!Object.hasOwn(islands, id)) continue;
|
|
394
565
|
const factory = islands[id];
|
|
@@ -396,7 +567,7 @@ function hydrateIslands(container, islands) {
|
|
|
396
567
|
const clientTree = factory();
|
|
397
568
|
clientTree.setAttribute("data-sibu-island", id);
|
|
398
569
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
399
|
-
marker
|
|
570
|
+
disposeAndReplace(marker, clientTree);
|
|
400
571
|
}
|
|
401
572
|
container.setAttribute("data-sibu-hydrated", "partial");
|
|
402
573
|
}
|
|
@@ -404,6 +575,7 @@ function hydrateProgressively(container, islands, options) {
|
|
|
404
575
|
const markers = container.querySelectorAll("[data-sibu-island]");
|
|
405
576
|
const cleanups = [];
|
|
406
577
|
for (const marker of Array.from(markers)) {
|
|
578
|
+
if (isAlreadyHydrated(marker)) continue;
|
|
407
579
|
const id = marker.getAttribute("data-sibu-island") ?? "";
|
|
408
580
|
if (!Object.hasOwn(islands, id)) continue;
|
|
409
581
|
const factory = islands[id];
|
|
@@ -413,10 +585,11 @@ function hydrateProgressively(container, islands, options) {
|
|
|
413
585
|
for (const entry of entries) {
|
|
414
586
|
if (entry.isIntersecting) {
|
|
415
587
|
observer.disconnect();
|
|
588
|
+
if (isAlreadyHydrated(marker) || marker.parentNode === null) break;
|
|
416
589
|
const clientTree = factory();
|
|
417
590
|
clientTree.setAttribute("data-sibu-island", id);
|
|
418
591
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
419
|
-
marker
|
|
592
|
+
disposeAndReplace(marker, clientTree);
|
|
420
593
|
break;
|
|
421
594
|
}
|
|
422
595
|
}
|
|
@@ -472,7 +645,7 @@ function suspenseSwapScript(id, nonce) {
|
|
|
472
645
|
);
|
|
473
646
|
}
|
|
474
647
|
const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
|
|
475
|
-
return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
|
|
648
|
+
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>`;
|
|
476
649
|
}
|
|
477
650
|
async function* renderToSuspenseStream(element, pendingBoundaries = [], options) {
|
|
478
651
|
yield* renderToStream(element);
|
|
@@ -522,6 +695,10 @@ function escapeAttr(str) {
|
|
|
522
695
|
}
|
|
523
696
|
|
|
524
697
|
export {
|
|
698
|
+
planMetaEntry,
|
|
699
|
+
planHeadElementEntry,
|
|
700
|
+
STATIC_VALUE_RESOLVER,
|
|
701
|
+
serializeHeadEntry,
|
|
525
702
|
renderToString,
|
|
526
703
|
hydrate,
|
|
527
704
|
trustHTML,
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isUnsafeKey
|
|
3
3
|
} from "./chunk-H3SRKIYX.js";
|
|
4
|
+
import {
|
|
5
|
+
acquireTitle
|
|
6
|
+
} from "./chunk-52XFPGSN.js";
|
|
4
7
|
import {
|
|
5
8
|
effect
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XDOMTQHM.js";
|
|
7
10
|
import {
|
|
8
11
|
batch,
|
|
9
12
|
signal
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TVURNWDK.js";
|
|
14
|
+
import {
|
|
15
|
+
reportError
|
|
16
|
+
} from "./chunk-VPP2FONR.js";
|
|
11
17
|
|
|
12
18
|
// src/browser/media.ts
|
|
13
19
|
function media(query) {
|
|
@@ -280,6 +286,7 @@ function permissions(name) {
|
|
|
280
286
|
};
|
|
281
287
|
status.addEventListener("change", onChange);
|
|
282
288
|
}).catch(() => {
|
|
289
|
+
if (disposed) return;
|
|
283
290
|
setState("unsupported");
|
|
284
291
|
});
|
|
285
292
|
function dispose() {
|
|
@@ -298,20 +305,25 @@ function clipboard() {
|
|
|
298
305
|
const [text, setText] = signal("");
|
|
299
306
|
const [copied, setCopied] = signal(false);
|
|
300
307
|
let copiedTimer = null;
|
|
308
|
+
let disposed = false;
|
|
301
309
|
async function copy(value) {
|
|
310
|
+
if (disposed) return;
|
|
302
311
|
if (typeof navigator === "undefined" || !navigator.clipboard) {
|
|
303
312
|
return;
|
|
304
313
|
}
|
|
305
314
|
await navigator.clipboard.writeText(value);
|
|
315
|
+
if (disposed) return;
|
|
306
316
|
setText(value);
|
|
307
317
|
setCopied(true);
|
|
308
318
|
if (copiedTimer !== null) clearTimeout(copiedTimer);
|
|
309
319
|
copiedTimer = setTimeout(() => {
|
|
320
|
+
if (disposed) return;
|
|
310
321
|
setCopied(false);
|
|
311
322
|
copiedTimer = null;
|
|
312
323
|
}, 2e3);
|
|
313
324
|
}
|
|
314
325
|
function dispose() {
|
|
326
|
+
disposed = true;
|
|
315
327
|
if (copiedTimer !== null) {
|
|
316
328
|
clearTimeout(copiedTimer);
|
|
317
329
|
copiedTimer = null;
|
|
@@ -437,20 +449,21 @@ function title(value) {
|
|
|
437
449
|
return () => {
|
|
438
450
|
};
|
|
439
451
|
}
|
|
440
|
-
const previousTitle = document.title;
|
|
441
452
|
if (typeof value === "function") {
|
|
453
|
+
let lease2 = null;
|
|
442
454
|
const cleanup = effect(() => {
|
|
443
|
-
|
|
455
|
+
const next = value();
|
|
456
|
+
if (lease2) lease2.set(next);
|
|
457
|
+
else lease2 = acquireTitle(next);
|
|
444
458
|
});
|
|
445
459
|
return () => {
|
|
446
460
|
cleanup();
|
|
447
|
-
|
|
461
|
+
lease2?.release();
|
|
462
|
+
lease2 = null;
|
|
448
463
|
};
|
|
449
464
|
}
|
|
450
|
-
|
|
451
|
-
return () =>
|
|
452
|
-
document.title = previousTitle;
|
|
453
|
-
};
|
|
465
|
+
const lease = acquireTitle(value);
|
|
466
|
+
return () => lease.release();
|
|
454
467
|
}
|
|
455
468
|
|
|
456
469
|
// src/browser/colorScheme.ts
|
|
@@ -764,51 +777,112 @@ function wakeLock() {
|
|
|
764
777
|
};
|
|
765
778
|
}
|
|
766
779
|
const api = navigator.wakeLock;
|
|
767
|
-
let
|
|
780
|
+
let current = null;
|
|
781
|
+
let desire = false;
|
|
782
|
+
let generation = 0;
|
|
783
|
+
let inflight = null;
|
|
768
784
|
let disposed = false;
|
|
785
|
+
function publishActive(value) {
|
|
786
|
+
if (disposed) return;
|
|
787
|
+
setActive(value);
|
|
788
|
+
}
|
|
789
|
+
function syncReleasedState() {
|
|
790
|
+
if (current?.released) {
|
|
791
|
+
current = null;
|
|
792
|
+
publishActive(false);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
function discard(sentinel) {
|
|
796
|
+
if (sentinel.released) return;
|
|
797
|
+
sentinel.release().catch((err) => {
|
|
798
|
+
reportError(err, { phase: "async", name: "wakeLock(discard)" });
|
|
799
|
+
});
|
|
800
|
+
}
|
|
769
801
|
async function request() {
|
|
770
802
|
if (disposed) return;
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
sentinel = s;
|
|
779
|
-
setActive(true);
|
|
780
|
-
sentinel.addEventListener("release", () => {
|
|
781
|
-
setActive(false);
|
|
782
|
-
});
|
|
783
|
-
} catch {
|
|
784
|
-
setActive(false);
|
|
803
|
+
desire = true;
|
|
804
|
+
syncReleasedState();
|
|
805
|
+
if (current && !current.released) {
|
|
806
|
+
publishActive(true);
|
|
807
|
+
return;
|
|
785
808
|
}
|
|
809
|
+
if (inflight) return inflight;
|
|
810
|
+
const myGeneration = ++generation;
|
|
811
|
+
const attempt = (async () => {
|
|
812
|
+
try {
|
|
813
|
+
const sentinel = await api.request("screen");
|
|
814
|
+
if (disposed || !desire || generation !== myGeneration) {
|
|
815
|
+
discard(sentinel);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
if (sentinel.released) {
|
|
819
|
+
publishActive(false);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
try {
|
|
823
|
+
sentinel.addEventListener("release", () => {
|
|
824
|
+
if (current !== sentinel) return;
|
|
825
|
+
current = null;
|
|
826
|
+
publishActive(false);
|
|
827
|
+
});
|
|
828
|
+
} catch (listenerError) {
|
|
829
|
+
discard(sentinel);
|
|
830
|
+
publishActive(false);
|
|
831
|
+
reportError(listenerError, { phase: "async", name: "wakeLock(listener registration)" });
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (sentinel.released) {
|
|
835
|
+
publishActive(false);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
current = sentinel;
|
|
839
|
+
publishActive(true);
|
|
840
|
+
} catch (err) {
|
|
841
|
+
if (generation === myGeneration) publishActive(false);
|
|
842
|
+
reportError(err, { phase: "async", name: "wakeLock(request)" });
|
|
843
|
+
}
|
|
844
|
+
})();
|
|
845
|
+
inflight = attempt;
|
|
846
|
+
void attempt.finally(() => {
|
|
847
|
+
if (inflight === attempt) inflight = null;
|
|
848
|
+
}).catch(() => {
|
|
849
|
+
});
|
|
850
|
+
return attempt;
|
|
786
851
|
}
|
|
787
852
|
async function release() {
|
|
788
|
-
|
|
789
|
-
|
|
853
|
+
desire = false;
|
|
854
|
+
generation++;
|
|
855
|
+
inflight = null;
|
|
856
|
+
const held = current;
|
|
857
|
+
current = null;
|
|
858
|
+
publishActive(false);
|
|
859
|
+
if (held && !held.released) {
|
|
860
|
+
await held.release();
|
|
790
861
|
}
|
|
791
|
-
sentinel = null;
|
|
792
|
-
setActive(false);
|
|
793
862
|
}
|
|
794
863
|
const onVisibility = () => {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
});
|
|
801
|
-
}
|
|
864
|
+
syncReleasedState();
|
|
865
|
+
if (!desire || current || document.hidden) return;
|
|
866
|
+
request().catch((err) => {
|
|
867
|
+
reportError(err, { phase: "async", name: "wakeLock(visibility re-acquire)" });
|
|
868
|
+
});
|
|
802
869
|
};
|
|
803
870
|
document.addEventListener("visibilitychange", onVisibility);
|
|
804
871
|
function dispose() {
|
|
805
|
-
disposed
|
|
872
|
+
if (disposed) return;
|
|
873
|
+
desire = false;
|
|
874
|
+
generation++;
|
|
875
|
+
inflight = null;
|
|
806
876
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
877
|
+
const held = current;
|
|
878
|
+
current = null;
|
|
879
|
+
setActive(false);
|
|
880
|
+
disposed = true;
|
|
881
|
+
if (held && !held.released) {
|
|
882
|
+
held.release().catch((err) => {
|
|
883
|
+
reportError(err, { phase: "async", name: "wakeLock(dispose release)" });
|
|
884
|
+
});
|
|
885
|
+
}
|
|
812
886
|
}
|
|
813
887
|
return { active, request, release, dispose };
|
|
814
888
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
watch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PTLJ7NQX.js";
|
|
4
4
|
import {
|
|
5
5
|
createId
|
|
6
6
|
} from "./chunk-CCSJMTRN.js";
|
|
7
7
|
import {
|
|
8
8
|
derived
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AD5YSZAN.js";
|
|
10
10
|
import {
|
|
11
11
|
stripHtml
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-5INI7D2L.js";
|
|
13
13
|
import {
|
|
14
14
|
effect
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XDOMTQHM.js";
|
|
16
16
|
import {
|
|
17
17
|
batch,
|
|
18
18
|
signal
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TVURNWDK.js";
|
|
20
20
|
|
|
21
21
|
// src/widgets/Combobox.ts
|
|
22
22
|
var boundComboboxes = /* @__PURE__ */ new WeakMap();
|