@takazudo/zfb 3.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +57 -3
- package/dist/config.js +23 -12
- package/dist/config.js.map +1 -1
- package/dist/content.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js.map +1 -1
- package/dist/island-boundary.d.ts +2 -2
- package/dist/island-boundary.js +4 -2
- package/dist/island-boundary.js.map +1 -1
- package/dist/island.d.ts +8 -4
- package/dist/island.js +1 -1
- package/dist/island.js.map +1 -1
- package/dist/jsx-types.d.ts +1 -1
- package/dist/jsx-types.js.map +1 -1
- package/dist/plugins.d.ts +18 -4
- package/dist/plugins.js.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slugify.js.map +1 -1
- package/dist/zudo-react/description.d.ts +18 -0
- package/dist/zudo-react/description.js +41 -0
- package/dist/zudo-react/description.js.map +1 -1
- package/dist/zudo-react/dom-bindings.d.ts +2 -1
- package/dist/zudo-react/dom-bindings.js +16 -22
- package/dist/zudo-react/dom-bindings.js.map +1 -1
- package/dist/zudo-react/forms.js.map +1 -1
- package/dist/zudo-react/hydrate.js +52 -31
- package/dist/zudo-react/hydrate.js.map +1 -1
- package/dist/zudo-react/index.d.ts +2 -1
- package/dist/zudo-react/index.js.map +1 -1
- package/dist/zudo-react/jsx-dev-runtime.d.ts +2 -6
- package/dist/zudo-react/jsx-dev-runtime.js +3 -3
- package/dist/zudo-react/jsx-dev-runtime.js.map +1 -1
- package/dist/zudo-react/jsx-runtime.js +2 -2
- package/dist/zudo-react/jsx-runtime.js.map +1 -1
- package/dist/zudo-react/jsx-types.d.ts +73 -26
- package/dist/zudo-react/jsx-types.js.map +1 -1
- package/dist/zudo-react/props-transport.d.ts +2 -0
- package/dist/zudo-react/props-transport.js +42 -14
- package/dist/zudo-react/props-transport.js.map +1 -1
- package/dist/zudo-react/raw-html.d.ts +1 -0
- package/dist/zudo-react/raw-html.js +260 -0
- package/dist/zudo-react/raw-html.js.map +1 -0
- package/dist/zudo-react/reactive.js.map +1 -1
- package/dist/zudo-react/render-html.d.ts +11 -1
- package/dist/zudo-react/render-html.js +83 -56
- package/dist/zudo-react/render-html.js.map +1 -1
- package/dist/zudo-react/root.d.ts +9 -1
- package/dist/zudo-react/root.js +45 -3
- package/dist/zudo-react/root.js.map +1 -1
- package/dist/zudo-react/server.d.ts +2 -0
- package/dist/zudo-react/server.js.map +1 -1
- package/dist/zudo-react/style.d.ts +10 -0
- package/dist/zudo-react/style.js +525 -0
- package/dist/zudo-react/style.js.map +1 -0
- package/dist/zudo-react/testing.d.ts +27 -0
- package/dist/zudo-react/testing.js +117 -0
- package/dist/zudo-react/testing.js.map +1 -0
- package/dist/zudo-react/vocabulary.d.ts +7 -0
- package/dist/zudo-react/vocabulary.js +64 -5
- package/dist/zudo-react/vocabulary.js.map +1 -1
- package/package.json +16 -10
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// Reserved-boundary guard for rawHtml payloads, shared by the server renderer
|
|
2
|
+
// and the client runtime so both accept exactly the same payloads.
|
|
3
|
+
//
|
|
4
|
+
// Rule: a payload is rejected when it can create a reserved boundary, decided
|
|
5
|
+
// by an HTML-tokenizer pass (WHATWG tokenization, data state), not a text regex:
|
|
6
|
+
// - a start or end tag carrying an attribute whose ASCII-lowercased name is
|
|
7
|
+
// `data-zfb-island` or `data-zfb-island-skip-ssr` (any value form, or none);
|
|
8
|
+
// - a comment, including a bogus `<!…>`, `<?…>` or `</…>` comment, whose data
|
|
9
|
+
// contains `zr:1:` (hydration matches `zr:1:` / `/zr:1:` comment prefixes);
|
|
10
|
+
// - a payload that ends inside a tag, comment or `</`, which would absorb the
|
|
11
|
+
// renderer's closing boundary marker.
|
|
12
|
+
// Marker-like text in character data or quoted attribute values is inert and
|
|
13
|
+
// renders as written. The tokenizer models the data state only, so payloads it
|
|
14
|
+
// cannot model take the textual rule, which rejects any `data-zfb-island` or
|
|
15
|
+
// `zr:1:` occurrence: `noscript`/`xmp`/`noembed`/`noframes`/`plaintext` hosts,
|
|
16
|
+
// and payloads naming a RAWTEXT/RCDATA/script/PLAINTEXT element (its end tag may
|
|
17
|
+
// also close a raw-text ancestor) or a CDATA section (foreign content).
|
|
18
|
+
// `script`/`style` hosts keep the documented marker and closing-tag rules.
|
|
19
|
+
// Script additionally checks whether HTML tokenization consumes its renderer
|
|
20
|
+
// closing tag; style is RAWTEXT and has no script escape states.
|
|
21
|
+
const reservedAttributes = new Set(["data-zfb-island", "data-zfb-island-skip-ssr"]);
|
|
22
|
+
const rawTextHosts = new Set(["noscript", "xmp", "noembed", "noframes", "plaintext"]);
|
|
23
|
+
const unmodeled = /<\/?(?:script|style|textarea|title|xmp|iframe|noembed|noframes|noscript|plaintext)|<!\[cdata\[/i;
|
|
24
|
+
const space = /[\t\n\f\r ]/;
|
|
25
|
+
const nameEnd = /[\t\n\f\r />=]/;
|
|
26
|
+
const unquotedEnd = /[\t\n\f\r >]/;
|
|
27
|
+
export function rawHtmlReserved(payload, tag) {
|
|
28
|
+
if (tag === "script" || tag === "style")
|
|
29
|
+
return (/\x3c!--\/?zr:1:|data-zfb-island(?:-skip-ssr)?\s*=/.test(payload) ||
|
|
30
|
+
(tag === "script" && scriptConsumesClosingTag(payload)));
|
|
31
|
+
if (rawTextHosts.has(tag) || unmodeled.test(payload))
|
|
32
|
+
return /data-zfb-island|zr:1:/i.test(payload);
|
|
33
|
+
let i = 0;
|
|
34
|
+
for (;;) {
|
|
35
|
+
const lt = payload.indexOf("<", i);
|
|
36
|
+
if (lt < 0)
|
|
37
|
+
return false;
|
|
38
|
+
const c = payload[lt + 1];
|
|
39
|
+
let start;
|
|
40
|
+
if (c === undefined)
|
|
41
|
+
return false;
|
|
42
|
+
if (/[a-z]/i.test(c) || (c === "/" && /[a-z]/i.test(payload[lt + 2] ?? ""))) {
|
|
43
|
+
i = tagEnd(payload, c === "/" ? lt + 2 : lt + 1);
|
|
44
|
+
if (i < 0)
|
|
45
|
+
return true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (c === "!" && payload.startsWith("--", lt + 2)) {
|
|
49
|
+
start = lt + 4;
|
|
50
|
+
if (payload[start] === ">" || payload.startsWith("->", start)) {
|
|
51
|
+
i = start;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const dashes = payload.indexOf("-->", start);
|
|
55
|
+
const bang = payload.indexOf("--!>", start);
|
|
56
|
+
const end = dashes < 0 || (bang >= 0 && bang < dashes) ? bang : dashes;
|
|
57
|
+
if (end < 0 || payload.slice(start, end).includes("zr:1:"))
|
|
58
|
+
return true;
|
|
59
|
+
i = end;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (c === "/") {
|
|
63
|
+
// `</` followed by the closing marker's `<` would open a bogus comment.
|
|
64
|
+
if (lt + 2 >= payload.length)
|
|
65
|
+
return true;
|
|
66
|
+
if (payload[lt + 2] === ">") {
|
|
67
|
+
i = lt + 3;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
start = lt + 2;
|
|
71
|
+
}
|
|
72
|
+
else if (c === "!" || c === "?")
|
|
73
|
+
start = lt + 1;
|
|
74
|
+
else {
|
|
75
|
+
i = lt + 1;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const end = payload.indexOf(">", start);
|
|
79
|
+
if (end < 0 || payload.slice(start, end).includes("zr:1:"))
|
|
80
|
+
return true;
|
|
81
|
+
i = end;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Walk the WHATWG script-data escape states with the exact suffix emitted by
|
|
85
|
+
// the renderer. A `</script>` in double-escaped text only exits that state;
|
|
86
|
+
// it does not close the element. Incomplete names and `<!--` tails must be
|
|
87
|
+
// evaluated with the suffix attached, rather than classified by payload alone.
|
|
88
|
+
function scriptConsumesClosingTag(payload) {
|
|
89
|
+
// The HTML input stream normalizes CR and CRLF to LF before tokenization.
|
|
90
|
+
const source = `${payload.replace(/\r\n?/g, "\n")}</script>`;
|
|
91
|
+
let state = "data";
|
|
92
|
+
let endFallback = "data";
|
|
93
|
+
let buffer = "";
|
|
94
|
+
for (let i = 0; i < source.length; i++) {
|
|
95
|
+
const c = source[i];
|
|
96
|
+
const alpha = /[a-zA-Z]/.test(c);
|
|
97
|
+
const delimiter = c === "/" || c === ">" || /[\t\n\f ]/.test(c);
|
|
98
|
+
switch (state) {
|
|
99
|
+
case "data":
|
|
100
|
+
if (c === "<")
|
|
101
|
+
state = "less";
|
|
102
|
+
break;
|
|
103
|
+
case "less":
|
|
104
|
+
if (c === "/") {
|
|
105
|
+
endFallback = "data";
|
|
106
|
+
state = "endOpen";
|
|
107
|
+
}
|
|
108
|
+
else if (c === "!")
|
|
109
|
+
state = "escapeStart";
|
|
110
|
+
else {
|
|
111
|
+
state = "data";
|
|
112
|
+
i--;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
case "escapeStart":
|
|
116
|
+
if (c === "-")
|
|
117
|
+
state = "escapeStartDash";
|
|
118
|
+
else {
|
|
119
|
+
state = "data";
|
|
120
|
+
i--;
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case "escapeStartDash":
|
|
124
|
+
if (c === "-")
|
|
125
|
+
state = "escapedDashDash";
|
|
126
|
+
else {
|
|
127
|
+
state = "data";
|
|
128
|
+
i--;
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case "escaped":
|
|
132
|
+
case "escapedDash":
|
|
133
|
+
case "escapedDashDash":
|
|
134
|
+
if (c === "<")
|
|
135
|
+
state = "escapedLess";
|
|
136
|
+
else if (state === "escapedDashDash" && c === ">")
|
|
137
|
+
state = "data";
|
|
138
|
+
else if (c === "-")
|
|
139
|
+
state = state === "escaped" ? "escapedDash" : "escapedDashDash";
|
|
140
|
+
else
|
|
141
|
+
state = "escaped";
|
|
142
|
+
break;
|
|
143
|
+
case "escapedLess":
|
|
144
|
+
if (c === "/") {
|
|
145
|
+
endFallback = "escaped";
|
|
146
|
+
state = "endOpen";
|
|
147
|
+
}
|
|
148
|
+
else if (alpha) {
|
|
149
|
+
buffer = "";
|
|
150
|
+
state = "doubleStart";
|
|
151
|
+
i--;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
state = "escaped";
|
|
155
|
+
i--;
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
case "endOpen":
|
|
159
|
+
if (alpha) {
|
|
160
|
+
buffer = "";
|
|
161
|
+
state = "endName";
|
|
162
|
+
i--;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
state = endFallback;
|
|
166
|
+
i--;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
case "endName":
|
|
170
|
+
if (alpha)
|
|
171
|
+
buffer = buffer.length < 7 ? buffer + c.toLowerCase() : "!";
|
|
172
|
+
else if (delimiter && buffer === "script" && c === ">")
|
|
173
|
+
return false;
|
|
174
|
+
else {
|
|
175
|
+
state = endFallback;
|
|
176
|
+
i--;
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case "doubleStart":
|
|
180
|
+
case "doubleEnd":
|
|
181
|
+
if (alpha)
|
|
182
|
+
buffer = buffer.length < 7 ? buffer + c.toLowerCase() : "!";
|
|
183
|
+
else if (delimiter)
|
|
184
|
+
state =
|
|
185
|
+
buffer === "script"
|
|
186
|
+
? state === "doubleStart"
|
|
187
|
+
? "double"
|
|
188
|
+
: "escaped"
|
|
189
|
+
: state === "doubleStart"
|
|
190
|
+
? "escaped"
|
|
191
|
+
: "double";
|
|
192
|
+
else {
|
|
193
|
+
state = state === "doubleStart" ? "escaped" : "double";
|
|
194
|
+
i--;
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
case "double":
|
|
198
|
+
case "doubleDash":
|
|
199
|
+
case "doubleDashDash":
|
|
200
|
+
if (c === "<")
|
|
201
|
+
state = "doubleLess";
|
|
202
|
+
else if (state === "doubleDashDash" && c === ">")
|
|
203
|
+
state = "data";
|
|
204
|
+
else if (c === "-")
|
|
205
|
+
state = state === "double" ? "doubleDash" : "doubleDashDash";
|
|
206
|
+
else
|
|
207
|
+
state = "double";
|
|
208
|
+
break;
|
|
209
|
+
case "doubleLess":
|
|
210
|
+
if (c === "/") {
|
|
211
|
+
buffer = "";
|
|
212
|
+
state = "doubleEnd";
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
state = "double";
|
|
216
|
+
i--;
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
// Returns the index just past the tag's `>`, or -1 when the tag is unterminated
|
|
224
|
+
// or carries a reserved attribute.
|
|
225
|
+
function tagEnd(s, j) {
|
|
226
|
+
const n = s.length;
|
|
227
|
+
while (j < n && !space.test(s[j]) && s[j] !== "/" && s[j] !== ">")
|
|
228
|
+
j++;
|
|
229
|
+
for (;;) {
|
|
230
|
+
while (j < n && (space.test(s[j]) || s[j] === "/"))
|
|
231
|
+
j++;
|
|
232
|
+
if (j >= n)
|
|
233
|
+
return -1;
|
|
234
|
+
if (s[j] === ">")
|
|
235
|
+
return j + 1;
|
|
236
|
+
const start = j++;
|
|
237
|
+
while (j < n && !nameEnd.test(s[j]))
|
|
238
|
+
j++;
|
|
239
|
+
if (reservedAttributes.has(s.slice(start, j).toLowerCase()))
|
|
240
|
+
return -1;
|
|
241
|
+
while (j < n && space.test(s[j]))
|
|
242
|
+
j++;
|
|
243
|
+
if (s[j] !== "=")
|
|
244
|
+
continue;
|
|
245
|
+
j++;
|
|
246
|
+
while (j < n && space.test(s[j]))
|
|
247
|
+
j++;
|
|
248
|
+
const quote = s[j];
|
|
249
|
+
if (quote === '"' || quote === "'") {
|
|
250
|
+
const close = s.indexOf(quote, j + 1);
|
|
251
|
+
if (close < 0)
|
|
252
|
+
return -1;
|
|
253
|
+
j = close + 1;
|
|
254
|
+
}
|
|
255
|
+
else
|
|
256
|
+
while (j < n && !unquotedEnd.test(s[j]))
|
|
257
|
+
j++;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=raw-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-html.js","sourceRoot":"","sources":["../../src/zudo-react/raw-html.ts"],"names":[],"mappings":"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","sourcesContent":["// Reserved-boundary guard for rawHtml payloads, shared by the server renderer\n// and the client runtime so both accept exactly the same payloads.\n//\n// Rule: a payload is rejected when it can create a reserved boundary, decided\n// by an HTML-tokenizer pass (WHATWG tokenization, data state), not a text regex:\n// - a start or end tag carrying an attribute whose ASCII-lowercased name is\n// `data-zfb-island` or `data-zfb-island-skip-ssr` (any value form, or none);\n// - a comment, including a bogus `<!…>`, `<?…>` or `</…>` comment, whose data\n// contains `zr:1:` (hydration matches `zr:1:` / `/zr:1:` comment prefixes);\n// - a payload that ends inside a tag, comment or `</`, which would absorb the\n// renderer's closing boundary marker.\n// Marker-like text in character data or quoted attribute values is inert and\n// renders as written. The tokenizer models the data state only, so payloads it\n// cannot model take the textual rule, which rejects any `data-zfb-island` or\n// `zr:1:` occurrence: `noscript`/`xmp`/`noembed`/`noframes`/`plaintext` hosts,\n// and payloads naming a RAWTEXT/RCDATA/script/PLAINTEXT element (its end tag may\n// also close a raw-text ancestor) or a CDATA section (foreign content).\n// `script`/`style` hosts keep the documented marker and closing-tag rules.\n// Script additionally checks whether HTML tokenization consumes its renderer\n// closing tag; style is RAWTEXT and has no script escape states.\n\nconst reservedAttributes = new Set([\"data-zfb-island\", \"data-zfb-island-skip-ssr\"]);\nconst rawTextHosts = new Set([\"noscript\", \"xmp\", \"noembed\", \"noframes\", \"plaintext\"]);\nconst unmodeled =\n /<\\/?(?:script|style|textarea|title|xmp|iframe|noembed|noframes|noscript|plaintext)|<!\\[cdata\\[/i;\nconst space = /[\\t\\n\\f\\r ]/;\nconst nameEnd = /[\\t\\n\\f\\r />=]/;\nconst unquotedEnd = /[\\t\\n\\f\\r >]/;\n\nexport function rawHtmlReserved(payload: string, tag: string): boolean {\n if (tag === \"script\" || tag === \"style\")\n return (\n /\\x3c!--\\/?zr:1:|data-zfb-island(?:-skip-ssr)?\\s*=/.test(payload) ||\n (tag === \"script\" && scriptConsumesClosingTag(payload))\n );\n if (rawTextHosts.has(tag) || unmodeled.test(payload))\n return /data-zfb-island|zr:1:/i.test(payload);\n let i = 0;\n for (;;) {\n const lt = payload.indexOf(\"<\", i);\n if (lt < 0) return false;\n const c = payload[lt + 1];\n let start: number;\n if (c === undefined) return false;\n if (/[a-z]/i.test(c) || (c === \"/\" && /[a-z]/i.test(payload[lt + 2] ?? \"\"))) {\n i = tagEnd(payload, c === \"/\" ? lt + 2 : lt + 1);\n if (i < 0) return true;\n continue;\n }\n if (c === \"!\" && payload.startsWith(\"--\", lt + 2)) {\n start = lt + 4;\n if (payload[start] === \">\" || payload.startsWith(\"->\", start)) {\n i = start;\n continue;\n }\n const dashes = payload.indexOf(\"-->\", start);\n const bang = payload.indexOf(\"--!>\", start);\n const end = dashes < 0 || (bang >= 0 && bang < dashes) ? bang : dashes;\n if (end < 0 || payload.slice(start, end).includes(\"zr:1:\")) return true;\n i = end;\n continue;\n }\n if (c === \"/\") {\n // `</` followed by the closing marker's `<` would open a bogus comment.\n if (lt + 2 >= payload.length) return true;\n if (payload[lt + 2] === \">\") {\n i = lt + 3;\n continue;\n }\n start = lt + 2;\n } else if (c === \"!\" || c === \"?\") start = lt + 1;\n else {\n i = lt + 1;\n continue;\n }\n const end = payload.indexOf(\">\", start);\n if (end < 0 || payload.slice(start, end).includes(\"zr:1:\")) return true;\n i = end;\n }\n}\n\n// Walk the WHATWG script-data escape states with the exact suffix emitted by\n// the renderer. A `</script>` in double-escaped text only exits that state;\n// it does not close the element. Incomplete names and `<!--` tails must be\n// evaluated with the suffix attached, rather than classified by payload alone.\nfunction scriptConsumesClosingTag(payload: string): boolean {\n // The HTML input stream normalizes CR and CRLF to LF before tokenization.\n const source = `${payload.replace(/\\r\\n?/g, \"\\n\")}</script>`;\n type State =\n | \"data\"\n | \"less\"\n | \"endOpen\"\n | \"endName\"\n | \"escapeStart\"\n | \"escapeStartDash\"\n | \"escaped\"\n | \"escapedDash\"\n | \"escapedDashDash\"\n | \"escapedLess\"\n | \"doubleStart\"\n | \"double\"\n | \"doubleDash\"\n | \"doubleDashDash\"\n | \"doubleLess\"\n | \"doubleEnd\";\n let state: State = \"data\";\n let endFallback: \"data\" | \"escaped\" = \"data\";\n let buffer = \"\";\n for (let i = 0; i < source.length; i++) {\n const c = source[i]!;\n const alpha = /[a-zA-Z]/.test(c);\n const delimiter = c === \"/\" || c === \">\" || /[\\t\\n\\f ]/.test(c);\n switch (state) {\n case \"data\":\n if (c === \"<\") state = \"less\";\n break;\n case \"less\":\n if (c === \"/\") {\n endFallback = \"data\";\n state = \"endOpen\";\n } else if (c === \"!\") state = \"escapeStart\";\n else {\n state = \"data\";\n i--;\n }\n break;\n case \"escapeStart\":\n if (c === \"-\") state = \"escapeStartDash\";\n else {\n state = \"data\";\n i--;\n }\n break;\n case \"escapeStartDash\":\n if (c === \"-\") state = \"escapedDashDash\";\n else {\n state = \"data\";\n i--;\n }\n break;\n case \"escaped\":\n case \"escapedDash\":\n case \"escapedDashDash\":\n if (c === \"<\") state = \"escapedLess\";\n else if (state === \"escapedDashDash\" && c === \">\") state = \"data\";\n else if (c === \"-\") state = state === \"escaped\" ? \"escapedDash\" : \"escapedDashDash\";\n else state = \"escaped\";\n break;\n case \"escapedLess\":\n if (c === \"/\") {\n endFallback = \"escaped\";\n state = \"endOpen\";\n } else if (alpha) {\n buffer = \"\";\n state = \"doubleStart\";\n i--;\n } else {\n state = \"escaped\";\n i--;\n }\n break;\n case \"endOpen\":\n if (alpha) {\n buffer = \"\";\n state = \"endName\";\n i--;\n } else {\n state = endFallback;\n i--;\n }\n break;\n case \"endName\":\n if (alpha) buffer = buffer.length < 7 ? buffer + c.toLowerCase() : \"!\";\n else if (delimiter && buffer === \"script\" && c === \">\") return false;\n else {\n state = endFallback;\n i--;\n }\n break;\n case \"doubleStart\":\n case \"doubleEnd\":\n if (alpha) buffer = buffer.length < 7 ? buffer + c.toLowerCase() : \"!\";\n else if (delimiter)\n state =\n buffer === \"script\"\n ? state === \"doubleStart\"\n ? \"double\"\n : \"escaped\"\n : state === \"doubleStart\"\n ? \"escaped\"\n : \"double\";\n else {\n state = state === \"doubleStart\" ? \"escaped\" : \"double\";\n i--;\n }\n break;\n case \"double\":\n case \"doubleDash\":\n case \"doubleDashDash\":\n if (c === \"<\") state = \"doubleLess\";\n else if (state === \"doubleDashDash\" && c === \">\") state = \"data\";\n else if (c === \"-\") state = state === \"double\" ? \"doubleDash\" : \"doubleDashDash\";\n else state = \"double\";\n break;\n case \"doubleLess\":\n if (c === \"/\") {\n buffer = \"\";\n state = \"doubleEnd\";\n } else {\n state = \"double\";\n i--;\n }\n break;\n }\n }\n return true;\n}\n\n// Returns the index just past the tag's `>`, or -1 when the tag is unterminated\n// or carries a reserved attribute.\nfunction tagEnd(s: string, j: number): number {\n const n = s.length;\n while (j < n && !space.test(s[j]!) && s[j] !== \"/\" && s[j] !== \">\") j++;\n for (;;) {\n while (j < n && (space.test(s[j]!) || s[j] === \"/\")) j++;\n if (j >= n) return -1;\n if (s[j] === \">\") return j + 1;\n const start = j++;\n while (j < n && !nameEnd.test(s[j]!)) j++;\n if (reservedAttributes.has(s.slice(start, j).toLowerCase())) return -1;\n while (j < n && space.test(s[j]!)) j++;\n if (s[j] !== \"=\") continue;\n j++;\n while (j < n && space.test(s[j]!)) j++;\n const quote = s[j];\n if (quote === '\"' || quote === \"'\") {\n const close = s.indexOf(quote, j + 1);\n if (close < 0) return -1;\n j = close + 1;\n } else while (j < n && !unquotedEnd.test(s[j]!)) j++;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../src/zudo-react/reactive.ts"],"names":[],"mappings":"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","sourcesContent":["import { enqueue, type Job, type Phase } from \"./scheduler.js\";\nimport type { ReadonlySignal, Signal } from \"./reactive-types.js\";\n\ninterface Source {\n readonly subscribers: Set<Observer>;\n}\ninterface Observer {\n readonly id: string;\n readonly dependencies: Set<Source>;\n invalidate(): void;\n}\nlet currentObserver: Observer | null = null;\nlet evaluating = 0;\nconst evaluationStack: Computed<unknown>[] = [];\nlet nextId = 0;\n\n// The quoted own key is the retained runtime definition witness.\nconst core = { \"@takazudo/zfb/zudo-react/runtime-definition-v1\": true, nextId: () => ++nextId };\n\nfunction track(source: Source): void {\n if (!currentObserver) return;\n source.subscribers.add(currentObserver);\n currentObserver.dependencies.add(source);\n}\nfunction detach(observer: Observer): void {\n for (const source of observer.dependencies) source.subscribers.delete(observer);\n observer.dependencies.clear();\n}\nfunction untracked<T>(fn: () => T): T {\n const previous = currentObserver;\n currentObserver = null;\n try {\n return fn();\n } finally {\n currentObserver = previous;\n }\n}\nexport { untracked };\n\nclass Writable<T> implements Signal<T>, Source {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly $$zudoWritable = \"zudo-react.writable.v1\" as const;\n readonly subscribers = new Set<Observer>();\n constructor(private current: T) {\n if (!core[\"@takazudo/zfb/zudo-react/runtime-definition-v1\"])\n throw new Error(\"ZR_RUNTIME_DEFINITION\");\n }\n get value(): T {\n track(this);\n return this.current;\n }\n set value(value: T) {\n if (evaluating > 0) throw new Error(\"ZR_COMPUTED_WRITE: write during computed evaluation\");\n if (Object.is(value, this.current)) return;\n this.current = value;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n}\n\nclass Computed<T> implements ReadonlySignal<T>, Source, Observer {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly id = `computed:${core.nextId()}`;\n readonly subscribers = new Set<Observer>();\n readonly dependencies = new Set<Source>();\n private cached!: T;\n private dirty = true;\n private evaluatingNow = false;\n constructor(private readonly read: () => T) {}\n get value(): T {\n if (this.evaluatingNow) {\n throw new Error(\n `ZR_COMPUTED_CYCLE: ${[...evaluationStack.map((entry) => entry.id), this.id].join(\" -> \")}`,\n );\n }\n if (this.dirty) this.evaluate();\n track(this);\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n return this.cached;\n }\n private evaluate(): void {\n detachAndRelease(this);\n const previous = currentObserver;\n currentObserver = this;\n this.evaluatingNow = true;\n evaluationStack.push(this as Computed<unknown>);\n evaluating++;\n try {\n this.cached = this.read();\n this.dirty = false;\n } catch (error) {\n detachAndRelease(this);\n throw error;\n } finally {\n evaluating--;\n evaluationStack.pop();\n this.evaluatingNow = false;\n currentObserver = previous;\n }\n }\n invalidate(): void {\n if (this.dirty) return;\n this.dirty = true;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n release(): void {\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n }\n}\n\nfunction release(source: Source): void {\n if (source instanceof Computed) source.release();\n}\nfunction detachAndRelease(observer: Observer): void {\n const sources = [...observer.dependencies];\n detach(observer);\n for (const source of sources) release(source);\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n return new Writable(initial);\n}\nexport function computed<T>(read: () => T): ReadonlySignal<T> {\n return new Computed(read);\n}\nexport function readSnapshot<T>(value: ReadonlySignal<T>): T {\n return untracked(() => value.value);\n}\n\nexport interface Subscription {\n readonly id: string;\n run(): void;\n dispose(): void;\n}\nexport function subscribe(\n callback: () => void,\n options: { phase?: Phase; id?: string; active?: () => boolean; report?: Job[\"report\"] } = {},\n): Subscription {\n let disposed = false;\n const observer: Observer = {\n id: options.id ?? `subscriber:${core.nextId()}`,\n dependencies: new Set(),\n invalidate() {\n enqueue(job);\n },\n };\n const job: Job = {\n id: observer.id,\n phase: options.phase ?? \"commit\",\n active: () => !disposed && (options.active?.() ?? true),\n run: () => subscription.run(),\n ...(options.report ? { report: options.report } : {}),\n };\n const subscription: Subscription = {\n id: observer.id,\n run() {\n if (!job.active()) return;\n detachAndRelease(observer);\n const previous = currentObserver;\n currentObserver = observer;\n try {\n callback();\n } finally {\n currentObserver = previous;\n }\n },\n dispose() {\n disposed = true;\n detachAndRelease(observer);\n },\n };\n return subscription;\n}\n\n/** Internal test introspection; absent from every public entry. */\nexport function subscriberCount(value: ReadonlySignal<unknown>): number {\n return value instanceof Writable || value instanceof Computed ? value.subscribers.size : 0;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../src/zudo-react/reactive.ts"],"names":[],"mappings":"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","sourcesContent":["import { enqueue, type Job, type Phase } from \"./scheduler.js\";\nimport type { ReadonlySignal, Signal } from \"./reactive-types.js\";\n\ninterface Source {\n readonly subscribers: Set<Observer>;\n}\ninterface Observer {\n readonly id: string;\n readonly dependencies: Set<Source>;\n invalidate(): void;\n}\nlet currentObserver: Observer | null = null;\nlet evaluating = 0;\nconst evaluationStack: Computed<unknown>[] = [];\nlet nextId = 0;\n\n// The quoted own key is the retained runtime definition witness.\nconst core = { \"@takazudo/zfb/zudo-react/runtime-definition-v1\": true, nextId: () => ++nextId };\n\nfunction track(source: Source): void {\n if (!currentObserver) return;\n source.subscribers.add(currentObserver);\n currentObserver.dependencies.add(source);\n}\nfunction detach(observer: Observer): void {\n for (const source of observer.dependencies) source.subscribers.delete(observer);\n observer.dependencies.clear();\n}\nfunction untracked<T>(fn: () => T): T {\n const previous = currentObserver;\n currentObserver = null;\n try {\n return fn();\n } finally {\n currentObserver = previous;\n }\n}\nexport { untracked };\n\nclass Writable<T> implements Signal<T>, Source {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly $$zudoWritable = \"zudo-react.writable.v1\" as const;\n readonly subscribers = new Set<Observer>();\n constructor(private current: T) {\n if (!core[\"@takazudo/zfb/zudo-react/runtime-definition-v1\"])\n throw new Error(\"ZR_RUNTIME_DEFINITION\");\n }\n get value(): T {\n track(this);\n return this.current;\n }\n set value(value: T) {\n if (evaluating > 0) throw new Error(\"ZR_COMPUTED_WRITE: write during computed evaluation\");\n if (Object.is(value, this.current)) return;\n this.current = value;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n}\n\nclass Computed<T> implements ReadonlySignal<T>, Source, Observer {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly id = `computed:${core.nextId()}`;\n readonly subscribers = new Set<Observer>();\n readonly dependencies = new Set<Source>();\n private cached!: T;\n private dirty = true;\n private evaluatingNow = false;\n constructor(private readonly read: () => T) {}\n get value(): T {\n if (this.evaluatingNow) {\n throw new Error(\n `ZR_COMPUTED_CYCLE: ${[...evaluationStack.map((entry) => entry.id), this.id].join(\" -> \")}`,\n );\n }\n if (this.dirty) this.evaluate();\n track(this);\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n return this.cached;\n }\n private evaluate(): void {\n detachAndRelease(this);\n const previous = currentObserver;\n currentObserver = this;\n this.evaluatingNow = true;\n evaluationStack.push(this as Computed<unknown>);\n evaluating++;\n try {\n this.cached = this.read();\n this.dirty = false;\n } catch (error) {\n detachAndRelease(this);\n throw error;\n } finally {\n evaluating--;\n evaluationStack.pop();\n this.evaluatingNow = false;\n currentObserver = previous;\n }\n }\n invalidate(): void {\n if (this.dirty) return;\n this.dirty = true;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n release(): void {\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n }\n}\n\nfunction release(source: Source): void {\n if (source instanceof Computed) source.release();\n}\nfunction detachAndRelease(observer: Observer): void {\n const sources = [...observer.dependencies];\n detach(observer);\n for (const source of sources) release(source);\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n return new Writable(initial);\n}\nexport function computed<T>(read: () => T): ReadonlySignal<T> {\n return new Computed(read);\n}\nexport function readSnapshot<T>(value: ReadonlySignal<T>): T {\n return untracked(() => value.value);\n}\n\nexport interface Subscription {\n readonly id: string;\n run(): void;\n dispose(): void;\n}\nexport function subscribe(\n callback: () => void,\n options: { phase?: Phase; id?: string; active?: () => boolean; report?: Job[\"report\"] } = {},\n): Subscription {\n let disposed = false;\n const observer: Observer = {\n id: options.id ?? `subscriber:${core.nextId()}`,\n dependencies: new Set(),\n invalidate() {\n enqueue(job);\n },\n };\n const job: Job = {\n id: observer.id,\n phase: options.phase ?? \"commit\",\n active: () => !disposed && (options.active?.() ?? true),\n run: () => subscription.run(),\n ...(options.report ? { report: options.report } : {}),\n };\n const subscription: Subscription = {\n id: observer.id,\n run() {\n if (!job.active()) return;\n detachAndRelease(observer);\n const previous = currentObserver;\n currentObserver = observer;\n try {\n callback();\n } finally {\n currentObserver = previous;\n }\n },\n dispose() {\n disposed = true;\n detachAndRelease(observer);\n },\n };\n return subscription;\n}\n\n/** Internal test introspection; absent from every public entry. */\nexport function subscriberCount(value: ReadonlySignal<unknown>): number {\n return value instanceof Writable || value instanceof Computed ? value.subscribers.size : 0;\n}\n"]}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import { type Child } from "./description.js";
|
|
1
|
+
import { type Child, type DescriptionSite } from "./description.js";
|
|
2
2
|
import type { RenderOptions } from "./server.js";
|
|
3
|
+
export interface RenderDiagnostic {
|
|
4
|
+
readonly code: string;
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly component: string;
|
|
7
|
+
readonly spelling?: {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly suggestion?: string;
|
|
10
|
+
};
|
|
11
|
+
readonly site?: DescriptionSite;
|
|
12
|
+
}
|
|
3
13
|
export declare function renderHtml(node: Child, options: RenderOptions): string;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { serializeStyle } from "./style.js";
|
|
2
|
+
import { Fragment, descriptionSite, isDescription, } from "./description.js";
|
|
2
3
|
import { escapeAttribute, escapeText } from "./escape.js";
|
|
4
|
+
import { rawHtmlReserved } from "./raw-html.js";
|
|
3
5
|
import { readSnapshot } from "./reactive.js";
|
|
4
6
|
import { createScope, withScope } from "./scope.js";
|
|
5
|
-
import {
|
|
7
|
+
import { emptyChildren, normalizeProps } from "./props-transport.js";
|
|
6
8
|
import { islandRootType } from "./island-root-type.js";
|
|
7
9
|
import { isShowType, isForType, showProps, forProps, keyed, keyPayload, view, } from "./structure.js";
|
|
8
|
-
import { booleanAttrs, commonAttrs, dialectSuggestion, formProps, htmlAttrs, htmlTags, isDialectProp, reactiveModelSuggestion, svgAttrs, svgTags, voidTags, } from "./vocabulary.js";
|
|
10
|
+
import { attributeError, attributeText, booleanAttrs, commonAttrs, dialectSuggestion, emptyIframeChildren, formProps, htmlAttrs, htmlTags, isDialectProp, reactiveModelSuggestion, svgAttrs, svgTags, voidTags, } from "./vocabulary.js";
|
|
9
11
|
const words = (value) => new Set(value.split(" "));
|
|
10
12
|
const reserved = words("key ref children rawHtml");
|
|
11
13
|
const sensitive = words("noscript xmp iframe noembed noframes plaintext");
|
|
@@ -17,8 +19,19 @@ const tableChildren = {
|
|
|
17
19
|
tr: words("td th"),
|
|
18
20
|
colgroup: words("col"),
|
|
19
21
|
};
|
|
20
|
-
function fail(code, context, detail) {
|
|
21
|
-
|
|
22
|
+
function fail(code, context, detail, spelling) {
|
|
23
|
+
const component = context.identity?.component ?? "static render";
|
|
24
|
+
const site = context.node && descriptionSite(context.node);
|
|
25
|
+
const diagnostic = {
|
|
26
|
+
code,
|
|
27
|
+
path: context.path,
|
|
28
|
+
component,
|
|
29
|
+
...(spelling && { spelling }),
|
|
30
|
+
...(site && { site }),
|
|
31
|
+
};
|
|
32
|
+
throw Object.assign(new TypeError(`${code}: ${detail} at ${context.path} in ${component}`), {
|
|
33
|
+
diagnostic,
|
|
34
|
+
});
|
|
22
35
|
}
|
|
23
36
|
function reactive(value) {
|
|
24
37
|
return (typeof value === "object" &&
|
|
@@ -62,24 +75,14 @@ function renderText(value, context) {
|
|
|
62
75
|
fail("ZR_CHILD", context, `invalid scalar ${typeof value}`);
|
|
63
76
|
}
|
|
64
77
|
function style(value, context) {
|
|
65
|
-
|
|
66
|
-
return value;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let result = "";
|
|
73
|
-
for (const [name, entry] of Object.entries(value)) {
|
|
74
|
-
if (!/^(--[a-zA-Z0-9_-]+|[a-z][a-z0-9-]*)$/.test(name) || /[A-Z]/.test(name))
|
|
75
|
-
fail("ZR_STYLE", context, `invalid property ${name}`);
|
|
76
|
-
if (entry == null)
|
|
77
|
-
continue;
|
|
78
|
-
if (typeof entry !== "string" && !(typeof entry === "number" && Number.isFinite(entry)))
|
|
79
|
-
fail("ZR_STYLE", context, `invalid value for ${name}`);
|
|
80
|
-
result += `${name}:${entry};`;
|
|
78
|
+
try {
|
|
79
|
+
return serializeStyle(value);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error instanceof TypeError)
|
|
83
|
+
fail("ZR_STYLE", context, error.message.replace(/^ZR_STYLE: /, ""));
|
|
84
|
+
throw error;
|
|
81
85
|
}
|
|
82
|
-
return result;
|
|
83
86
|
}
|
|
84
87
|
function attributes(tag, props, context, namespace, custom) {
|
|
85
88
|
let output = "";
|
|
@@ -90,7 +93,7 @@ function attributes(tag, props, context, namespace, custom) {
|
|
|
90
93
|
continue;
|
|
91
94
|
if (isDialectProp(name, namespace, custom)) {
|
|
92
95
|
const suggestion = dialectSuggestion(name, namespace, custom);
|
|
93
|
-
fail("ZR_PROP_DIALECT", context, `${tag}.${name}${suggestion ? ` (use \`${suggestion}\` instead of \`${name}\`)` : ""}
|
|
96
|
+
fail("ZR_PROP_DIALECT", context, `${tag}.${name}${suggestion ? ` (use \`${suggestion}\` instead of \`${name}\`)` : ""}`, suggestion ? { name, suggestion } : { name });
|
|
94
97
|
}
|
|
95
98
|
if (name.startsWith("on:")) {
|
|
96
99
|
if (!/^on:[A-Za-z][A-Za-z0-9_-]*(?::capture)?$/.test(name))
|
|
@@ -116,35 +119,28 @@ function attributes(tag, props, context, namespace, custom) {
|
|
|
116
119
|
const suggestion = reactiveModelSuggestion(tag, name, String(read(props.type) ?? "text"), custom);
|
|
117
120
|
fail("ZR_MODEL_UNSUPPORTED", context, `${tag}.${name} requires ${suggestion ?? "a static value"}`);
|
|
118
121
|
}
|
|
122
|
+
if (/^on[a-z]/.test(name) && typeof value === "function")
|
|
123
|
+
fail("ZR_PROP_DIALECT", context, `${tag}.${name} must use on:${name.slice(2)}`, {
|
|
124
|
+
name,
|
|
125
|
+
suggestion: `on:${name.slice(2)}`,
|
|
126
|
+
});
|
|
127
|
+
const error = attributeError(name, value, custom);
|
|
128
|
+
if (error)
|
|
129
|
+
fail("ZR_ATTRIBUTE", context, `${tag}.${name} ${error}`);
|
|
119
130
|
if (value == null)
|
|
120
131
|
continue;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
output += ` style="${escapeAttribute(style(value, context))}"`;
|
|
127
|
-
else if (booleanAttrs.has(name)) {
|
|
128
|
-
if (typeof value !== "boolean")
|
|
129
|
-
fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a boolean`);
|
|
130
|
-
if (value)
|
|
131
|
-
output += ` ${name}`;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
if (typeof value !== "string" &&
|
|
135
|
-
typeof value !== "boolean" &&
|
|
136
|
-
!(typeof value === "number" && Number.isFinite(value)))
|
|
137
|
-
fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a scalar`);
|
|
138
|
-
if (typeof value === "boolean" && !name.startsWith("aria-") && !name.startsWith("data-"))
|
|
139
|
-
fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a string`);
|
|
140
|
-
if (/^on[a-z]/.test(name) && typeof value !== "string")
|
|
141
|
-
fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a string`);
|
|
142
|
-
output += ` ${name}="${escapeAttribute(String(value))}"`;
|
|
143
|
-
}
|
|
132
|
+
const text = name === "style" ? style(value, context) : attributeText(name, value);
|
|
133
|
+
if (text === null)
|
|
134
|
+
continue;
|
|
135
|
+
output +=
|
|
136
|
+
booleanAttrs.has(name) && value === true ? ` ${name}` : ` ${name}="${escapeAttribute(text)}"`;
|
|
144
137
|
}
|
|
145
138
|
return output;
|
|
146
139
|
}
|
|
147
140
|
function restricted(value, context, namespace, parent) {
|
|
141
|
+
return tracked(value, context, () => restrictedValue(value, context, namespace, parent));
|
|
142
|
+
}
|
|
143
|
+
function restrictedValue(value, context, namespace, parent) {
|
|
148
144
|
if (value == null || typeof value === "boolean")
|
|
149
145
|
return "";
|
|
150
146
|
if (Array.isArray(value))
|
|
@@ -169,7 +165,7 @@ function element(description, context, namespace, parent) {
|
|
|
169
165
|
const childNamespace = tag === "foreignObject" ? "html" : elementNamespace;
|
|
170
166
|
if (elementNamespace === "svg" ? !svgTags.has(tag) : !custom && !htmlTags.has(tag))
|
|
171
167
|
fail("ZR_TAG", context, tag);
|
|
172
|
-
if (context.identity && (tag === "template" || sensitive.has(tag)))
|
|
168
|
+
if (context.identity && (tag === "template" || (tag !== "iframe" && sensitive.has(tag))))
|
|
173
169
|
fail("ZR_PARSER_CONTEXT", context, tag);
|
|
174
170
|
if (parent in tableChildren && !tableChildren[parent]?.has(tag))
|
|
175
171
|
fail("ZR_PARSER_CONTEXT", context, `${parent} > ${tag}`);
|
|
@@ -184,6 +180,12 @@ function element(description, context, namespace, parent) {
|
|
|
184
180
|
: `form:${context.nextFormId++}`;
|
|
185
181
|
const source = description.props;
|
|
186
182
|
const props = { ...source };
|
|
183
|
+
if (tag === "iframe") {
|
|
184
|
+
if (Object.hasOwn(props, "rawHtml"))
|
|
185
|
+
fail("ZR_RAW_HTML", context, "iframe forbids rawHtml");
|
|
186
|
+
if (!emptyIframeChildren(props.children))
|
|
187
|
+
fail("ZR_PARSER_CONTEXT", context, "iframe requires empty children");
|
|
188
|
+
}
|
|
187
189
|
const hasValue = source.modelValue !== undefined;
|
|
188
190
|
const hasChecked = source.modelChecked !== undefined;
|
|
189
191
|
if ((hasValue && hasChecked) ||
|
|
@@ -311,7 +313,7 @@ function element(description, context, namespace, parent) {
|
|
|
311
313
|
const payload = read(raw);
|
|
312
314
|
if (typeof payload !== "string")
|
|
313
315
|
fail("ZR_RAW_HTML", context, `${tag} requires string rawHtml`);
|
|
314
|
-
if (
|
|
316
|
+
if (rawHtmlReserved(payload, tag))
|
|
315
317
|
fail("ZR_RAW_HTML", context, "reserved boundary in rawHtml");
|
|
316
318
|
if ((tag === "script" && /<\/script/i.test(payload)) ||
|
|
317
319
|
(tag === "style" && /<\/style/i.test(payload)))
|
|
@@ -324,6 +326,9 @@ function element(description, context, namespace, parent) {
|
|
|
324
326
|
fail("ZR_MODEL_VALUE", context, "textarea requires string");
|
|
325
327
|
content = `${value.startsWith("\n") ? "\n" : ""}${escapeText(value)}`;
|
|
326
328
|
}
|
|
329
|
+
else if (tag === "iframe") {
|
|
330
|
+
// The shell has no owned children; srcdoc belongs to the browser's foreign document.
|
|
331
|
+
}
|
|
327
332
|
else if (tag === "script" || tag === "style") {
|
|
328
333
|
if (hasChildren)
|
|
329
334
|
fail("ZR_RAW_HTML", context, `${tag} requires rawHtml`);
|
|
@@ -364,7 +369,22 @@ function element(description, context, namespace, parent) {
|
|
|
364
369
|
context.formId = previousFormId;
|
|
365
370
|
return `<${tag}${attrs}>${voidTags.has(tag) ? "" : `${content}</${tag}>`}`;
|
|
366
371
|
}
|
|
372
|
+
// A failure is attributed only to the value being rendered: a description's own site,
|
|
373
|
+
// or no site at all for scalars, promises and other non-description children.
|
|
374
|
+
function tracked(value, context, fn) {
|
|
375
|
+
const prior = context.node;
|
|
376
|
+
context.node = isDescription(value) ? value : undefined;
|
|
377
|
+
try {
|
|
378
|
+
return fn();
|
|
379
|
+
}
|
|
380
|
+
finally {
|
|
381
|
+
context.node = prior;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
367
384
|
function render(value, context, namespace, parent) {
|
|
385
|
+
return tracked(value, context, () => renderValue(value, context, namespace, parent));
|
|
386
|
+
}
|
|
387
|
+
function renderValue(value, context, namespace, parent) {
|
|
368
388
|
if (value == null || typeof value === "boolean")
|
|
369
389
|
return "";
|
|
370
390
|
if (Array.isArray(value))
|
|
@@ -453,9 +473,6 @@ function render(value, context, namespace, parent) {
|
|
|
453
473
|
context.scope = prior;
|
|
454
474
|
}
|
|
455
475
|
}
|
|
456
|
-
function empty(value) {
|
|
457
|
-
return (value == null || typeof value === "boolean" || (Array.isArray(value) && value.every(empty)));
|
|
458
|
-
}
|
|
459
476
|
function island(description, context, namespace, parent) {
|
|
460
477
|
if (context.boundary)
|
|
461
478
|
fail("ZR_NESTED_ISLAND", context, "nested island");
|
|
@@ -470,13 +487,13 @@ function island(description, context, namespace, parent) {
|
|
|
470
487
|
const component = child.type.displayName ?? child.type.name;
|
|
471
488
|
if (component !== options.identity.component)
|
|
472
489
|
fail("ZR_ISLAND_IDENTITY", context, `expected ${options.identity.component}, got ${component}`);
|
|
473
|
-
if (Object.hasOwn(child.props, "children") && !
|
|
490
|
+
if (Object.hasOwn(child.props, "children") && !emptyChildren(child.props.children))
|
|
474
491
|
fail("ZR_ISLAND_CHILD", context, "nonempty children");
|
|
475
|
-
|
|
476
|
-
delete props.children;
|
|
492
|
+
let props;
|
|
477
493
|
let payload;
|
|
478
494
|
try {
|
|
479
|
-
|
|
495
|
+
props = normalizeProps(child.props);
|
|
496
|
+
payload = JSON.stringify(props);
|
|
480
497
|
}
|
|
481
498
|
catch (error) {
|
|
482
499
|
throw new TypeError(`ZR_ISLAND_PROPS ${component}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -486,8 +503,18 @@ function island(description, context, namespace, parent) {
|
|
|
486
503
|
(when === "media" && !options.media?.trim()) ||
|
|
487
504
|
(when !== "media" && options.media !== undefined))
|
|
488
505
|
fail("ZR_ISLAND_WHEN", context, when);
|
|
506
|
+
if (options.persist !== undefined &&
|
|
507
|
+
(typeof options.persist !== "string" ||
|
|
508
|
+
!options.persist.trim() ||
|
|
509
|
+
options.persist !== options.persist.trim() ||
|
|
510
|
+
/[\u0000-\u001f\u007f]/.test(options.persist)))
|
|
511
|
+
fail("ZR_ISLAND_PERSIST", context, "persist must be a nonempty string without surrounding whitespace or control characters");
|
|
512
|
+
if (options.persistProps !== undefined && typeof options.persistProps !== "boolean")
|
|
513
|
+
fail("ZR_ISLAND_PERSIST", context, "persistProps must be a boolean");
|
|
514
|
+
if (options.persistProps !== undefined && options.persist === undefined)
|
|
515
|
+
fail("ZR_ISLAND_PERSIST", context, "persistProps requires persist");
|
|
489
516
|
const marker = options.skipSsr ? "data-zfb-island-skip-ssr" : "data-zfb-island";
|
|
490
|
-
const attrs = ` ${marker}="${escapeAttribute(options.identity.component)}" data-when="${when}"${when === "media" ? ` data-media="${escapeAttribute(options.media)}"` : ""} data-zfb-transport="json/1" data-zfb-protocol="zudo-react/1" data-zfb-build="${escapeAttribute(options.identity.build)}" data-props="${escapeAttribute(payload)}"`;
|
|
517
|
+
const attrs = ` ${marker}="${escapeAttribute(options.identity.component)}" data-when="${when}"${when === "media" ? ` data-media="${escapeAttribute(options.media)}"` : ""} data-zfb-transport="json/1" data-zfb-protocol="zudo-react/1" data-zfb-build="${escapeAttribute(options.identity.build)}" data-props="${escapeAttribute(payload)}"${options.persist === undefined ? "" : ` data-zfb-transition-persist="${escapeAttribute(options.persist)}"`}${options.persistProps === true ? ' data-zfb-transition-persist-props="true"' : ""}`;
|
|
491
518
|
const local = {
|
|
492
519
|
radioGroups: new Map(),
|
|
493
520
|
radioNames: new Map(),
|