schema-components 1.12.10 → 1.13.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/README.md +31 -0
- package/dist/core/adapter.d.mts +1 -1
- package/dist/core/adapter.mjs +37 -8
- package/dist/core/constraints.d.mts +16 -0
- package/dist/core/constraints.mjs +138 -0
- package/dist/core/merge.d.mts +32 -0
- package/dist/core/merge.mjs +96 -0
- package/dist/core/normalise.d.mts +40 -0
- package/dist/core/normalise.mjs +171 -0
- package/dist/core/openapi30.d.mts +38 -0
- package/dist/core/openapi30.mjs +223 -0
- package/dist/core/ref.d.mts +25 -0
- package/dist/core/ref.mjs +86 -0
- package/dist/core/renderer.d.mts +2 -2
- package/dist/core/renderer.mjs +8 -0
- package/dist/core/swagger2.d.mts +10 -0
- package/dist/core/swagger2.mjs +294 -0
- package/dist/core/typeInference.d.mts +2 -0
- package/dist/core/typeInference.mjs +1 -0
- package/dist/core/types.d.mts +2 -3
- package/dist/core/types.mjs +55 -2
- package/dist/core/version.d.mts +2 -0
- package/dist/core/version.mjs +79 -0
- package/dist/core/walkBuilders.d.mts +52 -0
- package/dist/core/walkBuilders.mjs +152 -0
- package/dist/core/walker.d.mts +3 -10
- package/dist/core/walker.mjs +143 -231
- package/dist/html/a11y.d.mts +5 -4
- package/dist/html/renderToHtml.d.mts +3 -3
- package/dist/html/renderToHtml.mjs +23 -379
- package/dist/html/renderToHtmlStream.d.mts +29 -47
- package/dist/html/renderToHtmlStream.mjs +33 -305
- package/dist/html/renderers.d.mts +14 -0
- package/dist/html/renderers.mjs +406 -0
- package/dist/html/streamRenderers.d.mts +13 -0
- package/dist/html/streamRenderers.mjs +243 -0
- package/dist/openapi/components.d.mts +2 -1
- package/dist/openapi/parser.d.mts +59 -2
- package/dist/openapi/parser.mjs +189 -8
- package/dist/react/SchemaComponent.d.mts +4 -2
- package/dist/react/SchemaComponent.mjs +39 -85
- package/dist/react/SchemaView.d.mts +2 -1
- package/dist/react/SchemaView.mjs +21 -9
- package/dist/react/fieldPath.d.mts +20 -0
- package/dist/react/fieldPath.mjs +81 -0
- package/dist/react/headless.d.mts +2 -4
- package/dist/react/headless.mjs +3 -492
- package/dist/react/headlessRenderers.d.mts +23 -0
- package/dist/react/headlessRenderers.mjs +507 -0
- package/dist/renderer-DseHeliw.d.mts +160 -0
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mantine.mjs +2 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/mui.mjs +2 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/radix.mjs +2 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/themes/shadcn.mjs +10 -6
- package/dist/typeInference-CRPqVwKu.d.mts +299 -0
- package/dist/types-ag2jYLqQ.d.mts +261 -0
- package/dist/version-CLchheaH.d.mts +40 -0
- package/package.json +1 -1
- package/dist/types-BJzEgJdX.d.mts +0 -335
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
2
2
|
import { getHtmlRenderFn, mergeHtmlResolvers } from "../core/renderer.mjs";
|
|
3
3
|
import { walk } from "../core/walker.mjs";
|
|
4
|
-
import { h,
|
|
5
|
-
import {
|
|
4
|
+
import { h, serialize } from "./html.mjs";
|
|
5
|
+
import { defaultHtmlResolver } from "./renderers.mjs";
|
|
6
6
|
//#region src/html/renderToHtml.ts
|
|
7
7
|
/**
|
|
8
|
-
* HTML renderer — produces semantic HTML from schemas
|
|
8
|
+
* HTML renderer entry point — produces semantic HTML from schemas.
|
|
9
9
|
*
|
|
10
10
|
* Framework-agnostic alternative to the React rendering pipeline.
|
|
11
11
|
* Uses the same walker and adapter (normalise → walk → render) but
|
|
@@ -24,372 +24,6 @@ import { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAt
|
|
|
24
24
|
* resolver: { string: (props) => h("b", {}, String(props.value)) },
|
|
25
25
|
* });
|
|
26
26
|
*/
|
|
27
|
-
function dateInputType(format) {
|
|
28
|
-
if (format === "date") return "date";
|
|
29
|
-
if (format === "time") return "time";
|
|
30
|
-
if (format === "date-time" || format === "datetime") return "datetime-local";
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Normalise a dot-separated path into a valid, `sc-` prefixed HTML ID.
|
|
34
|
-
* Dots in paths (from nested objects) become hyphens.
|
|
35
|
-
*/
|
|
36
|
-
function fieldId(path) {
|
|
37
|
-
return `sc-${path.replace(/\./g, "-")}`;
|
|
38
|
-
}
|
|
39
|
-
function renderStringHtml(props) {
|
|
40
|
-
if (props.readOnly) return serialize(renderStringReadOnly(props));
|
|
41
|
-
return serialize(renderStringEditable(props));
|
|
42
|
-
}
|
|
43
|
-
function renderStringReadOnly(props) {
|
|
44
|
-
const strValue = typeof props.value === "string" ? props.value : void 0;
|
|
45
|
-
if (strValue === void 0 || strValue.length === 0) return h("span", {
|
|
46
|
-
class: "sc-value sc-value--empty",
|
|
47
|
-
...ariaReadonlyAttrs()
|
|
48
|
-
}, "—");
|
|
49
|
-
const format = props.constraints.format;
|
|
50
|
-
if (format === "email") return h("a", {
|
|
51
|
-
class: "sc-value",
|
|
52
|
-
href: `mailto:${strValue}`,
|
|
53
|
-
...ariaReadonlyAttrs()
|
|
54
|
-
}, strValue);
|
|
55
|
-
if (format === "uri" || format === "url") return h("a", {
|
|
56
|
-
class: "sc-value",
|
|
57
|
-
href: strValue,
|
|
58
|
-
...ariaReadonlyAttrs()
|
|
59
|
-
}, strValue);
|
|
60
|
-
return h("span", {
|
|
61
|
-
class: "sc-value",
|
|
62
|
-
...ariaReadonlyAttrs()
|
|
63
|
-
}, strValue);
|
|
64
|
-
}
|
|
65
|
-
function renderStringEditable(props) {
|
|
66
|
-
const strValue = typeof props.value === "string" ? props.value : "";
|
|
67
|
-
const format = props.constraints.format;
|
|
68
|
-
const inputType = dateInputType(format) ?? (format === "email" ? "email" : format === "uri" ? "url" : "text");
|
|
69
|
-
const id = fieldId(props.path);
|
|
70
|
-
const attrs = {
|
|
71
|
-
class: "sc-input",
|
|
72
|
-
id,
|
|
73
|
-
type: inputType,
|
|
74
|
-
name: id
|
|
75
|
-
};
|
|
76
|
-
if (!props.writeOnly) attrs.value = strValue;
|
|
77
|
-
if (typeof props.meta.description === "string") attrs.placeholder = props.meta.description;
|
|
78
|
-
if (props.constraints.minLength !== void 0) attrs.minlength = String(props.constraints.minLength);
|
|
79
|
-
if (props.constraints.maxLength !== void 0) attrs.maxlength = String(props.constraints.maxLength);
|
|
80
|
-
Object.assign(attrs, ariaRequiredAttrs(props.tree));
|
|
81
|
-
Object.assign(attrs, ariaDescribedByAttrs(id, props.constraints));
|
|
82
|
-
return h("input", attrs);
|
|
83
|
-
}
|
|
84
|
-
function renderNumberHtml(props) {
|
|
85
|
-
if (props.readOnly) return serialize(renderNumberReadOnly(props));
|
|
86
|
-
return serialize(renderNumberEditable(props));
|
|
87
|
-
}
|
|
88
|
-
function renderNumberReadOnly(props) {
|
|
89
|
-
if (typeof props.value !== "number") return h("span", {
|
|
90
|
-
class: "sc-value sc-value--empty",
|
|
91
|
-
...ariaReadonlyAttrs()
|
|
92
|
-
}, "—");
|
|
93
|
-
return h("span", {
|
|
94
|
-
class: "sc-value",
|
|
95
|
-
...ariaReadonlyAttrs()
|
|
96
|
-
}, props.value.toLocaleString());
|
|
97
|
-
}
|
|
98
|
-
function renderNumberEditable(props) {
|
|
99
|
-
const numValue = typeof props.value === "number" ? String(props.value) : "";
|
|
100
|
-
const id = fieldId(props.path);
|
|
101
|
-
const attrs = {
|
|
102
|
-
class: "sc-input",
|
|
103
|
-
id,
|
|
104
|
-
type: "number",
|
|
105
|
-
name: id
|
|
106
|
-
};
|
|
107
|
-
if (!props.writeOnly) attrs.value = numValue;
|
|
108
|
-
if (props.constraints.minimum !== void 0) attrs.min = String(props.constraints.minimum);
|
|
109
|
-
if (props.constraints.maximum !== void 0) attrs.max = String(props.constraints.maximum);
|
|
110
|
-
Object.assign(attrs, ariaRequiredAttrs(props.tree));
|
|
111
|
-
Object.assign(attrs, ariaDescribedByAttrs(id, props.constraints));
|
|
112
|
-
return h("input", attrs);
|
|
113
|
-
}
|
|
114
|
-
function renderBooleanHtml(props) {
|
|
115
|
-
if (props.readOnly) return serialize(renderBooleanReadOnly(props));
|
|
116
|
-
return serialize(renderBooleanEditable(props));
|
|
117
|
-
}
|
|
118
|
-
function renderBooleanReadOnly(props) {
|
|
119
|
-
if (typeof props.value !== "boolean") return h("span", {
|
|
120
|
-
class: "sc-value sc-value--empty",
|
|
121
|
-
...ariaReadonlyAttrs()
|
|
122
|
-
}, "—");
|
|
123
|
-
return h("span", {
|
|
124
|
-
class: "sc-value sc-value--boolean",
|
|
125
|
-
...ariaReadonlyAttrs()
|
|
126
|
-
}, props.value ? "Yes" : "No");
|
|
127
|
-
}
|
|
128
|
-
function renderBooleanEditable(props) {
|
|
129
|
-
const id = fieldId(props.path);
|
|
130
|
-
const attrs = {
|
|
131
|
-
class: "sc-input",
|
|
132
|
-
id,
|
|
133
|
-
type: "checkbox",
|
|
134
|
-
name: id
|
|
135
|
-
};
|
|
136
|
-
if (!props.writeOnly && props.value === true) attrs.checked = true;
|
|
137
|
-
Object.assign(attrs, ariaRequiredAttrs(props.tree));
|
|
138
|
-
Object.assign(attrs, ariaLabelAttrs(props.meta.description));
|
|
139
|
-
return h("input", attrs);
|
|
140
|
-
}
|
|
141
|
-
function renderEnumHtml(props) {
|
|
142
|
-
if (props.readOnly) return serialize(renderEnumReadOnly(props));
|
|
143
|
-
return serialize(renderEnumEditable(props));
|
|
144
|
-
}
|
|
145
|
-
function renderEnumReadOnly(props) {
|
|
146
|
-
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
147
|
-
if (enumValue.length === 0) return h("span", {
|
|
148
|
-
class: "sc-value sc-value--empty",
|
|
149
|
-
...ariaReadonlyAttrs()
|
|
150
|
-
}, "—");
|
|
151
|
-
return h("span", {
|
|
152
|
-
class: "sc-value",
|
|
153
|
-
...ariaReadonlyAttrs()
|
|
154
|
-
}, enumValue);
|
|
155
|
-
}
|
|
156
|
-
function renderEnumEditable(props) {
|
|
157
|
-
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
158
|
-
const id = fieldId(props.path);
|
|
159
|
-
const selectedValue = props.writeOnly ? "" : enumValue;
|
|
160
|
-
const optionNodes = [h("option", { value: "" }, "Select…"), ...(props.enumValues ?? []).map((v) => {
|
|
161
|
-
const attrs = { value: v };
|
|
162
|
-
if (v === selectedValue) attrs.selected = true;
|
|
163
|
-
return h("option", attrs, v);
|
|
164
|
-
})];
|
|
165
|
-
const selectAttrs = {
|
|
166
|
-
class: "sc-input",
|
|
167
|
-
id,
|
|
168
|
-
name: id
|
|
169
|
-
};
|
|
170
|
-
Object.assign(selectAttrs, ariaRequiredAttrs(props.tree));
|
|
171
|
-
return h("select", selectAttrs, ...optionNodes);
|
|
172
|
-
}
|
|
173
|
-
function renderObjectHtml(props) {
|
|
174
|
-
return serialize(renderObjectNode(props));
|
|
175
|
-
}
|
|
176
|
-
function renderObjectNode(props) {
|
|
177
|
-
const fields = props.fields;
|
|
178
|
-
if (fields === void 0) return "";
|
|
179
|
-
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
180
|
-
const obj = isRecord(props.value) ? props.value : {};
|
|
181
|
-
const descriptionText = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
182
|
-
const legend = descriptionText !== void 0 ? h("legend", {}, descriptionText) : void 0;
|
|
183
|
-
const sortedEntries = Object.entries(fields).sort((a, b) => {
|
|
184
|
-
return (typeof a[1].meta.order === "number" ? a[1].meta.order : Infinity) - (typeof b[1].meta.order === "number" ? b[1].meta.order : Infinity);
|
|
185
|
-
}).filter(([, field]) => field.meta.visible !== false);
|
|
186
|
-
if (props.readOnly) {
|
|
187
|
-
const children = [];
|
|
188
|
-
if (legend !== void 0) children.push(legend);
|
|
189
|
-
for (const [key, field] of sortedEntries) {
|
|
190
|
-
const label = typeof field.meta.description === "string" ? field.meta.description : key;
|
|
191
|
-
const childValue = obj[key];
|
|
192
|
-
const childHtml = props.renderChild(field, childValue, props.path ? `${props.path}.${key}` : key);
|
|
193
|
-
children.push(h("dt", { class: "sc-label" }, label));
|
|
194
|
-
children.push(h("dd", { class: "sc-value" }, raw(childHtml)));
|
|
195
|
-
}
|
|
196
|
-
const dlAttrs = { class: "sc-object" };
|
|
197
|
-
Object.assign(dlAttrs, ariaLabelAttrs(descriptionText));
|
|
198
|
-
return h("dl", dlAttrs, ...children);
|
|
199
|
-
}
|
|
200
|
-
const children = [];
|
|
201
|
-
if (legend !== void 0) children.push(legend);
|
|
202
|
-
for (const [key, field] of sortedEntries) {
|
|
203
|
-
const label = typeof field.meta.description === "string" ? field.meta.description : key;
|
|
204
|
-
const fieldId = buildInputId(props.path, key);
|
|
205
|
-
const childPath = props.path ? `${props.path}.${key}` : key;
|
|
206
|
-
const childValue = obj[key];
|
|
207
|
-
const childHtml = props.renderChild(field, childValue, childPath);
|
|
208
|
-
const required = requiredIndicator(field);
|
|
209
|
-
const labelContent = [label];
|
|
210
|
-
if (required !== void 0) labelContent.push(required);
|
|
211
|
-
const fieldChildren = [h("label", {
|
|
212
|
-
class: "sc-label",
|
|
213
|
-
for: fieldId
|
|
214
|
-
}, ...labelContent), raw(childHtml)];
|
|
215
|
-
const hint = buildHintElement(fieldId, field.constraints);
|
|
216
|
-
if (hint !== void 0) fieldChildren.push(hint);
|
|
217
|
-
children.push(h("div", { class: "sc-field" }, ...fieldChildren));
|
|
218
|
-
}
|
|
219
|
-
const fieldsetAttrs = { class: "sc-object" };
|
|
220
|
-
Object.assign(fieldsetAttrs, ariaLabelAttrs(descriptionText));
|
|
221
|
-
return h("fieldset", fieldsetAttrs, ...children);
|
|
222
|
-
}
|
|
223
|
-
function renderArrayHtml(props) {
|
|
224
|
-
return serialize(renderArrayNode(props));
|
|
225
|
-
}
|
|
226
|
-
function renderArrayNode(props) {
|
|
227
|
-
const arr = Array.isArray(props.value) ? props.value : [];
|
|
228
|
-
const element = props.element;
|
|
229
|
-
if (element === void 0) return "";
|
|
230
|
-
const items = arr.map((item) => {
|
|
231
|
-
return h("li", { class: "sc-item" }, raw(props.renderChild(element, item)));
|
|
232
|
-
});
|
|
233
|
-
if (props.readOnly) return h("ul", { class: "sc-array" }, ...items);
|
|
234
|
-
return h("div", { class: "sc-array" }, ...arr.map((item) => {
|
|
235
|
-
return h("div", {}, raw(props.renderChild(element, item)));
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
function renderRecordHtml(props) {
|
|
239
|
-
return serialize(renderRecordNode(props));
|
|
240
|
-
}
|
|
241
|
-
function renderRecordNode(props) {
|
|
242
|
-
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
243
|
-
const obj = isRecord(props.value) ? props.value : {};
|
|
244
|
-
const valueType = props.valueType;
|
|
245
|
-
if (valueType === void 0) return "";
|
|
246
|
-
const attrs = {
|
|
247
|
-
class: "sc-record",
|
|
248
|
-
role: "group"
|
|
249
|
-
};
|
|
250
|
-
if (props.readOnly) {
|
|
251
|
-
const children = [];
|
|
252
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
253
|
-
const childHtml = props.renderChild(valueType, val, key);
|
|
254
|
-
children.push(h("dt", { class: "sc-label" }, key));
|
|
255
|
-
children.push(h("dd", { class: "sc-value" }, raw(childHtml)));
|
|
256
|
-
}
|
|
257
|
-
return h("dl", attrs, ...children);
|
|
258
|
-
}
|
|
259
|
-
const children = [];
|
|
260
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
261
|
-
const childHtml = props.renderChild(valueType, val, key);
|
|
262
|
-
children.push(h("div", { class: "sc-field" }, h("label", { class: "sc-label" }, key), raw(childHtml)));
|
|
263
|
-
}
|
|
264
|
-
return h("div", attrs, ...children);
|
|
265
|
-
}
|
|
266
|
-
function renderLiteralHtml(props) {
|
|
267
|
-
const values = props.tree.literalValues;
|
|
268
|
-
if (values === void 0 || values.length === 0) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
269
|
-
return serialize(h("span", { class: "sc-value" }, values.map((v) => v === null ? "null" : String(v)).join(", ")));
|
|
270
|
-
}
|
|
271
|
-
function renderUnionHtml(props) {
|
|
272
|
-
const options = props.options;
|
|
273
|
-
if (options === void 0 || options.length === 0) {
|
|
274
|
-
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
275
|
-
return serialize(h("span", { class: "sc-value" }, JSON.stringify(props.value)));
|
|
276
|
-
}
|
|
277
|
-
const matched = matchUnionOption(options, props.value);
|
|
278
|
-
if (matched !== void 0) return props.renderChild(matched, props.value);
|
|
279
|
-
const firstOption = options[0];
|
|
280
|
-
if (firstOption !== void 0) return props.renderChild(firstOption, props.value);
|
|
281
|
-
return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
282
|
-
}
|
|
283
|
-
function renderDiscriminatedUnionHtml(props) {
|
|
284
|
-
const options = props.options;
|
|
285
|
-
const discriminator = props.discriminator;
|
|
286
|
-
if (options === void 0 || options.length === 0) {
|
|
287
|
-
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
288
|
-
return serialize(h("span", { class: "sc-value" }, JSON.stringify(props.value)));
|
|
289
|
-
}
|
|
290
|
-
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
291
|
-
const obj = isRecord(props.value) ? props.value : {};
|
|
292
|
-
const discKey = discriminator ?? "";
|
|
293
|
-
const currentDiscriminatorValue = typeof obj[discKey] === "string" ? obj[discKey] : void 0;
|
|
294
|
-
const optionLabels = options.map((opt) => {
|
|
295
|
-
const discriminatorField = opt.fields?.[discKey];
|
|
296
|
-
if (discriminatorField !== void 0) {
|
|
297
|
-
const constVal = discriminatorField.literalValues?.[0];
|
|
298
|
-
if (typeof constVal === "string") return constVal;
|
|
299
|
-
}
|
|
300
|
-
return typeof opt.meta.title === "string" ? opt.meta.title : opt.type;
|
|
301
|
-
});
|
|
302
|
-
let activeIndex = 0;
|
|
303
|
-
if (currentDiscriminatorValue !== void 0) {
|
|
304
|
-
const found = optionLabels.indexOf(currentDiscriminatorValue);
|
|
305
|
-
if (found !== -1) activeIndex = found;
|
|
306
|
-
}
|
|
307
|
-
const activeOption = options[activeIndex];
|
|
308
|
-
if (props.readOnly) {
|
|
309
|
-
if (activeOption !== void 0) return props.renderChild(activeOption, props.value);
|
|
310
|
-
return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
311
|
-
}
|
|
312
|
-
const panelId = `sc-${props.path}-panel`;
|
|
313
|
-
const children = [h("div", {
|
|
314
|
-
role: "tablist",
|
|
315
|
-
class: "sc-tabs",
|
|
316
|
-
"aria-label": "Select variant"
|
|
317
|
-
}, ...options.map((_opt, i) => {
|
|
318
|
-
return h("button", {
|
|
319
|
-
type: "button",
|
|
320
|
-
role: "tab",
|
|
321
|
-
class: i === activeIndex ? "sc-tab sc-tab--active" : "sc-tab",
|
|
322
|
-
id: `sc-${props.path}-tab-${String(i)}`,
|
|
323
|
-
"aria-selected": i === activeIndex ? "true" : void 0,
|
|
324
|
-
"aria-controls": panelId,
|
|
325
|
-
tabindex: i === activeIndex ? "0" : "-1"
|
|
326
|
-
}, optionLabels[i]);
|
|
327
|
-
}))];
|
|
328
|
-
if (activeOption !== void 0) {
|
|
329
|
-
const childHtml = props.renderChild(activeOption, props.value);
|
|
330
|
-
children.push(h("div", {
|
|
331
|
-
role: "tabpanel",
|
|
332
|
-
id: panelId,
|
|
333
|
-
"aria-labelledby": `sc-${props.path}-tab-${String(activeIndex)}`
|
|
334
|
-
}, raw(childHtml)));
|
|
335
|
-
}
|
|
336
|
-
return serialize(h("div", { class: "sc-discriminated-union" }, ...children));
|
|
337
|
-
}
|
|
338
|
-
function renderFileHtml(props) {
|
|
339
|
-
const id = fieldId(props.path);
|
|
340
|
-
const accept = props.constraints.mimeTypes?.join(",");
|
|
341
|
-
if (props.readOnly) return serialize(h("span", {
|
|
342
|
-
class: "sc-value",
|
|
343
|
-
id,
|
|
344
|
-
...ariaReadonlyAttrs()
|
|
345
|
-
}, "File field"));
|
|
346
|
-
const attrs = {
|
|
347
|
-
class: "sc-input",
|
|
348
|
-
id,
|
|
349
|
-
type: "file",
|
|
350
|
-
name: id
|
|
351
|
-
};
|
|
352
|
-
if (accept !== void 0) attrs.accept = accept;
|
|
353
|
-
Object.assign(attrs, ariaRequiredAttrs(props.tree));
|
|
354
|
-
if (typeof props.meta.description === "string") Object.assign(attrs, ariaLabelAttrs(props.meta.description));
|
|
355
|
-
return serialize(h("input", attrs));
|
|
356
|
-
}
|
|
357
|
-
function renderUnknownHtml(props) {
|
|
358
|
-
if (props.readOnly) {
|
|
359
|
-
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
360
|
-
if (typeof props.value === "string") return serialize(h("span", { class: "sc-value" }, props.value));
|
|
361
|
-
return serialize(h("span", { class: "sc-value" }, JSON.stringify(props.value)));
|
|
362
|
-
}
|
|
363
|
-
const strValue = typeof props.value === "string" ? props.value : "";
|
|
364
|
-
const attrs = {
|
|
365
|
-
class: "sc-input",
|
|
366
|
-
type: "text",
|
|
367
|
-
name: props.path
|
|
368
|
-
};
|
|
369
|
-
if (!props.writeOnly) attrs.value = strValue;
|
|
370
|
-
return serialize(h("input", attrs));
|
|
371
|
-
}
|
|
372
|
-
function matchUnionOption(options, value) {
|
|
373
|
-
if (typeof value === "string") return options.find((o) => o.type === "string" || o.type === "enum");
|
|
374
|
-
if (typeof value === "number") return options.find((o) => o.type === "number");
|
|
375
|
-
if (typeof value === "boolean") return options.find((o) => o.type === "boolean");
|
|
376
|
-
if (Array.isArray(value)) return options.find((o) => o.type === "array");
|
|
377
|
-
if (typeof value === "object" && value !== null) return options.find((o) => o.type === "object");
|
|
378
|
-
}
|
|
379
|
-
const defaultHtmlResolver = {
|
|
380
|
-
string: renderStringHtml,
|
|
381
|
-
number: renderNumberHtml,
|
|
382
|
-
boolean: renderBooleanHtml,
|
|
383
|
-
enum: renderEnumHtml,
|
|
384
|
-
object: renderObjectHtml,
|
|
385
|
-
array: renderArrayHtml,
|
|
386
|
-
record: renderRecordHtml,
|
|
387
|
-
literal: renderLiteralHtml,
|
|
388
|
-
union: renderUnionHtml,
|
|
389
|
-
discriminatedUnion: renderDiscriminatedUnionHtml,
|
|
390
|
-
file: renderFileHtml,
|
|
391
|
-
unknown: renderUnknownHtml
|
|
392
|
-
};
|
|
393
27
|
/**
|
|
394
28
|
* Render a schema to an HTML string.
|
|
395
29
|
*
|
|
@@ -410,9 +44,12 @@ function renderToHtml(schema, options = {}) {
|
|
|
410
44
|
rootDocument
|
|
411
45
|
});
|
|
412
46
|
const resolver = options.resolver ?? defaultHtmlResolver;
|
|
413
|
-
const
|
|
414
|
-
|
|
47
|
+
const MAX_HTML_DEPTH = 10;
|
|
48
|
+
const makeRenderChild = (currentDepth) => (childTree, childValue, pathSuffix) => {
|
|
49
|
+
if (currentDepth >= MAX_HTML_DEPTH) return `<fieldset class="sc-recursive"><em>\u21bb ${typeof childTree.meta.description === "string" ? childTree.meta.description : "schema"} (recursive)</em></fieldset>`;
|
|
50
|
+
return renderFieldHtml(childTree, childValue, resolver, pathSuffix ?? childTree.meta.description ?? "", makeRenderChild(currentDepth + 1));
|
|
415
51
|
};
|
|
52
|
+
const renderChild = makeRenderChild(0);
|
|
416
53
|
return renderFieldHtml(tree, options.value ?? tree.defaultValue, resolver, "", renderChild);
|
|
417
54
|
}
|
|
418
55
|
function renderFieldHtml(tree, value, resolver, path, renderChild) {
|
|
@@ -431,17 +68,24 @@ function renderFieldHtml(tree, value, resolver, path, renderChild) {
|
|
|
431
68
|
tree,
|
|
432
69
|
renderChild
|
|
433
70
|
};
|
|
434
|
-
if (tree.
|
|
435
|
-
if (tree.element !== void 0) props.element = tree.element;
|
|
436
|
-
if (tree.
|
|
437
|
-
if (tree.
|
|
438
|
-
if (tree.
|
|
439
|
-
if (tree.
|
|
440
|
-
if (tree.
|
|
71
|
+
if (tree.type === "enum") props.enumValues = tree.enumValues;
|
|
72
|
+
if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
|
|
73
|
+
if (tree.type === "object") props.fields = tree.fields;
|
|
74
|
+
if (tree.type === "union" || tree.type === "discriminatedUnion") props.options = tree.options;
|
|
75
|
+
if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
|
|
76
|
+
if (tree.type === "record") props.keyType = tree.keyType;
|
|
77
|
+
if (tree.type === "record") props.valueType = tree.valueType;
|
|
78
|
+
if (tree.type === "tuple") props.prefixItems = tree.prefixItems;
|
|
79
|
+
if (tree.type === "conditional") props.ifClause = tree.ifClause;
|
|
80
|
+
if (tree.type === "conditional" && tree.thenClause !== void 0) props.thenClause = tree.thenClause;
|
|
81
|
+
if (tree.type === "conditional" && tree.elseClause !== void 0) props.elseClause = tree.elseClause;
|
|
82
|
+
if (tree.type === "negation") props.negated = tree.negated;
|
|
83
|
+
if (tree.type === "literal") props.literalValues = tree.literalValues;
|
|
84
|
+
if (tree.examples !== void 0) props.examples = tree.examples;
|
|
441
85
|
return renderFn(props);
|
|
442
86
|
}
|
|
443
87
|
if (effectiveValue === void 0 || effectiveValue === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
444
88
|
return serialize(h("span", { class: "sc-value" }, typeof effectiveValue === "string" ? effectiveValue : JSON.stringify(effectiveValue)));
|
|
445
89
|
}
|
|
446
90
|
//#endregion
|
|
447
|
-
export {
|
|
91
|
+
export { renderToHtml };
|
|
@@ -1,57 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { w as SchemaMeta } from "../types-ag2jYLqQ.mjs";
|
|
2
|
+
import { o as HtmlResolver } from "../renderer-DseHeliw.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/html/renderToHtmlStream.d.ts
|
|
5
|
+
interface StreamRenderOptions {
|
|
6
|
+
/** The data value to render. */
|
|
7
|
+
value?: unknown;
|
|
8
|
+
/** For OpenAPI: a ref string like "#/components/schemas/User". */
|
|
9
|
+
ref?: string;
|
|
10
|
+
/** Per-field meta overrides. */
|
|
11
|
+
fields?: Record<string, unknown>;
|
|
12
|
+
/** Meta overrides applied to the root schema. */
|
|
13
|
+
meta?: SchemaMeta;
|
|
14
|
+
/** Force all fields read-only. */
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
/** Force all fields as inputs. */
|
|
17
|
+
writeOnly?: boolean;
|
|
18
|
+
/** Root description. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Custom HTML resolver. Falls back to defaultHtmlResolver. */
|
|
21
|
+
resolver?: HtmlResolver;
|
|
22
|
+
}
|
|
4
23
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Same rendering pipeline as `renderToHtml` but yields string fragments
|
|
8
|
-
* as each field/element is produced instead of building the entire string
|
|
9
|
-
* in memory. Use for server-side rendering where you want to start
|
|
10
|
-
* flushing the response before the full schema is rendered.
|
|
11
|
-
*
|
|
12
|
-
* Three output formats:
|
|
13
|
-
*
|
|
14
|
-
* - `renderToHtmlChunks(schema, options)` → sync `Iterable<string>`
|
|
15
|
-
* - `renderToHtmlStream(schema, options)` → async `AsyncIterable<string>`
|
|
16
|
-
* - `renderToHtmlReadable(schema, options)` → web `ReadableStream<string>`
|
|
17
|
-
*
|
|
18
|
-
* Chunk boundaries:
|
|
19
|
-
* - Object: opening tag, one chunk per field, closing tag
|
|
20
|
-
* - Array: opening tag, one chunk per item, closing tag
|
|
21
|
-
* - Record: opening tag, one chunk per entry, closing tag
|
|
22
|
-
* - Leaf types (string, number, boolean, enum, literal, unknown):
|
|
23
|
-
* rendered entirely as one chunk
|
|
24
|
-
*
|
|
25
|
-
* All HTML construction uses `h()` from `html.ts` — the streaming module
|
|
26
|
-
* manually yields the opening tag, then children, then the closing tag.
|
|
24
|
+
* Render a schema as an iterable of HTML string chunks.
|
|
25
|
+
* Each chunk is a self-contained HTML fragment.
|
|
27
26
|
*/
|
|
28
|
-
|
|
27
|
+
declare function renderToHtmlChunks(schema: unknown, options?: StreamRenderOptions): Iterable<string>;
|
|
29
28
|
/**
|
|
30
|
-
* Render a schema
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* all chunks produces the same output as `renderToHtml`.
|
|
34
|
-
*
|
|
35
|
-
* @returns Sync iterable of HTML string chunks
|
|
29
|
+
* Render a schema as an async iterable of HTML string chunks.
|
|
30
|
+
* Yields `undefined` between chunks to allow the event loop to process
|
|
31
|
+
* other tasks (cooperative scheduling).
|
|
36
32
|
*/
|
|
37
|
-
declare function
|
|
33
|
+
declare function renderToHtmlStream(schema: unknown, options?: StreamRenderOptions): AsyncIterable<string>;
|
|
38
34
|
/**
|
|
39
|
-
* Render a schema
|
|
40
|
-
*
|
|
41
|
-
* Identical chunk boundaries to `renderToHtmlChunks` but yields via
|
|
42
|
-
* an async generator. Use with `for await...of` or pipe to a response.
|
|
43
|
-
*
|
|
44
|
-
* @returns Async iterable of HTML string chunks
|
|
45
|
-
*/
|
|
46
|
-
declare function renderToHtmlStream(schema: unknown, options?: StreamRenderOptions): AsyncIterable<string, void, undefined>;
|
|
47
|
-
/**
|
|
48
|
-
* Render a schema to a web `ReadableStream<string>`.
|
|
49
|
-
*
|
|
50
|
-
* ```ts
|
|
51
|
-
* return new Response(renderToHtmlReadable(schema, { value }), {
|
|
52
|
-
* headers: { "Content-Type": "text/html" },
|
|
53
|
-
* });
|
|
54
|
-
* ```
|
|
35
|
+
* Render a schema as a web `ReadableStream<string>`.
|
|
36
|
+
* Uses the async rendering pipeline internally.
|
|
55
37
|
*/
|
|
56
38
|
declare function renderToHtmlReadable(schema: unknown, options?: StreamRenderOptions): ReadableStream<string>;
|
|
57
39
|
//#endregion
|