schema-components 1.12.11 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/core/adapter.d.mts +8 -3
- package/dist/core/adapter.mjs +58 -14
- package/dist/core/constraints.d.mts +17 -0
- package/dist/core/constraints.mjs +150 -0
- package/dist/core/diagnostics.d.mts +2 -0
- package/dist/core/diagnostics.mjs +33 -0
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/formats.d.mts +33 -0
- package/dist/core/formats.mjs +67 -0
- package/dist/core/merge.d.mts +48 -0
- package/dist/core/merge.mjs +125 -0
- package/dist/core/normalise.d.mts +41 -0
- package/dist/core/normalise.mjs +2 -0
- package/dist/core/openapi30.d.mts +41 -0
- package/dist/core/openapi30.mjs +2 -0
- package/dist/core/ref.d.mts +2 -0
- package/dist/core/ref.mjs +165 -0
- package/dist/core/renderer.d.mts +2 -2
- package/dist/core/renderer.mjs +8 -0
- package/dist/core/swagger2.d.mts +11 -0
- package/dist/core/swagger2.mjs +2 -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 +58 -2
- package/dist/core/version.d.mts +2 -0
- package/dist/core/version.mjs +151 -0
- package/dist/core/walkBuilders.d.mts +66 -0
- package/dist/core/walkBuilders.mjs +152 -0
- package/dist/core/walker.d.mts +3 -10
- package/dist/core/walker.mjs +245 -233
- package/dist/diagnostics-DzbZmcLI.d.mts +64 -0
- 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/normalise-tL9FckAk.mjs +748 -0
- package/dist/openapi/ApiCallbacks.d.mts +16 -0
- package/dist/openapi/ApiCallbacks.mjs +34 -0
- package/dist/openapi/ApiLinks.d.mts +16 -0
- package/dist/openapi/ApiLinks.mjs +42 -0
- package/dist/openapi/ApiResponseHeaders.d.mts +16 -0
- package/dist/openapi/ApiResponseHeaders.mjs +35 -0
- package/dist/openapi/ApiSecurity.d.mts +19 -0
- package/dist/openapi/ApiSecurity.mjs +33 -0
- package/dist/openapi/bundle.d.mts +47 -0
- package/dist/openapi/bundle.mjs +95 -0
- package/dist/openapi/components.d.mts +7 -1
- package/dist/openapi/components.mjs +30 -6
- package/dist/openapi/parser.d.mts +59 -2
- package/dist/openapi/parser.mjs +189 -8
- package/dist/react/SchemaComponent.d.mts +13 -4
- package/dist/react/SchemaComponent.mjs +51 -91
- package/dist/react/SchemaView.d.mts +10 -2
- package/dist/react/SchemaView.mjs +33 -15
- 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/ref-DvWoULcy.d.mts +44 -0
- package/dist/renderer-BdSqllx5.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 +3 -2
- 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-k7FXfTVO.d.mts +335 -0
- package/dist/types-D_5ST7SS.d.mts +269 -0
- package/dist/version-B5NV-35j.d.mts +69 -0
- package/package.json +1 -1
- package/dist/types-BJzEgJdX.d.mts +0 -335
- /package/dist/{errors-DIKI2C78.d.mts → errors-C5zRC2PU.d.mts} +0 -0
package/dist/openapi/parser.mjs
CHANGED
|
@@ -35,12 +35,29 @@ const METHODS = [
|
|
|
35
35
|
"patch",
|
|
36
36
|
"delete"
|
|
37
37
|
];
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a path item, following a `$ref` to `components/pathItems/<Name>`
|
|
40
|
+
* (OpenAPI 3.1) if present. Returns `undefined` when the value is not a
|
|
41
|
+
* path item, the ref is malformed, or the target does not resolve.
|
|
42
|
+
*/
|
|
43
|
+
function resolvePathItem(parsed, pathItem) {
|
|
44
|
+
if (!isObject(pathItem)) return void 0;
|
|
45
|
+
const ref = getString(pathItem, "$ref");
|
|
46
|
+
if (ref === void 0) return pathItem;
|
|
47
|
+
return resolveRefInDoc(parsed.doc, ref) ?? void 0;
|
|
48
|
+
}
|
|
49
|
+
function lookupPathItem(parsed, path) {
|
|
50
|
+
const resolved = resolvePathItem(parsed, getProperty(getProperty(parsed.doc, "paths"), path));
|
|
51
|
+
if (resolved !== void 0) return resolved;
|
|
52
|
+
return resolvePathItem(parsed, getProperty(getProperty(parsed.doc, "webhooks"), path));
|
|
53
|
+
}
|
|
38
54
|
function listOperations(parsed) {
|
|
39
55
|
const operations = [];
|
|
40
56
|
const paths = getProperty(parsed.doc, "paths");
|
|
41
57
|
if (!isObject(paths)) return operations;
|
|
42
|
-
for (const [path,
|
|
43
|
-
|
|
58
|
+
for (const [path, rawPathItem] of Object.entries(paths)) {
|
|
59
|
+
const pathItem = resolvePathItem(parsed, rawPathItem);
|
|
60
|
+
if (pathItem === void 0) continue;
|
|
44
61
|
for (const method of METHODS) {
|
|
45
62
|
const operation = getProperty(pathItem, method);
|
|
46
63
|
if (!isObject(operation)) continue;
|
|
@@ -58,8 +75,8 @@ function listOperations(parsed) {
|
|
|
58
75
|
return operations;
|
|
59
76
|
}
|
|
60
77
|
function getParameters(parsed, path, method) {
|
|
61
|
-
const pathItem =
|
|
62
|
-
if (
|
|
78
|
+
const pathItem = lookupPathItem(parsed, path);
|
|
79
|
+
if (pathItem === void 0) return [];
|
|
63
80
|
const operation = getProperty(pathItem, method);
|
|
64
81
|
if (!isObject(operation)) return [];
|
|
65
82
|
const pathParams = extractParameterList(getProperty(pathItem, "parameters"));
|
|
@@ -99,7 +116,7 @@ function resolveParam(param) {
|
|
|
99
116
|
return param;
|
|
100
117
|
}
|
|
101
118
|
function getRequestBody(parsed, path, method) {
|
|
102
|
-
const requestBody = getProperty(getProperty(
|
|
119
|
+
const requestBody = getProperty(getProperty(lookupPathItem(parsed, path), method), "requestBody");
|
|
103
120
|
if (!isObject(requestBody)) return void 0;
|
|
104
121
|
const content = getProperty(requestBody, "content");
|
|
105
122
|
if (!isObject(content)) return {
|
|
@@ -118,7 +135,7 @@ function getRequestBody(parsed, path, method) {
|
|
|
118
135
|
};
|
|
119
136
|
}
|
|
120
137
|
function getResponses(parsed, path, method) {
|
|
121
|
-
const responses = getProperty(getProperty(
|
|
138
|
+
const responses = getProperty(getProperty(lookupPathItem(parsed, path), method), "responses");
|
|
122
139
|
if (!isObject(responses)) return [];
|
|
123
140
|
const result = [];
|
|
124
141
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
@@ -126,11 +143,13 @@ function getResponses(parsed, path, method) {
|
|
|
126
143
|
const content = getProperty(response, "content");
|
|
127
144
|
const contentTypes = isObject(content) ? Object.keys(content) : [];
|
|
128
145
|
const schema = isObject(content) ? extractSchemaFromContent(content) : void 0;
|
|
146
|
+
const headers = getResponseHeaders(response);
|
|
129
147
|
result.push({
|
|
130
148
|
statusCode,
|
|
131
149
|
description: getString(response, "description"),
|
|
132
150
|
contentTypes,
|
|
133
|
-
schema
|
|
151
|
+
schema,
|
|
152
|
+
headers
|
|
134
153
|
});
|
|
135
154
|
}
|
|
136
155
|
return result;
|
|
@@ -155,5 +174,167 @@ function resolveRefInDoc(doc, ref) {
|
|
|
155
174
|
}
|
|
156
175
|
return isObject(current) ? current : void 0;
|
|
157
176
|
}
|
|
177
|
+
function getSecurityRequirements(parsed, path, method) {
|
|
178
|
+
const opSecurity = getProperty(getProperty(lookupPathItem(parsed, path), method), "security");
|
|
179
|
+
const globalSecurity = getProperty(parsed.doc, "security");
|
|
180
|
+
const securityArray = Array.isArray(opSecurity) ? opSecurity : Array.isArray(globalSecurity) ? globalSecurity : [];
|
|
181
|
+
const result = [];
|
|
182
|
+
for (const entry of securityArray) {
|
|
183
|
+
if (!isObject(entry)) continue;
|
|
184
|
+
for (const [name, scopes] of Object.entries(entry)) result.push({
|
|
185
|
+
name,
|
|
186
|
+
scopes: Array.isArray(scopes) ? scopes.filter((s) => typeof s === "string") : []
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
function getSecuritySchemes(parsed) {
|
|
192
|
+
const result = /* @__PURE__ */ new Map();
|
|
193
|
+
const securitySchemes = getProperty(getProperty(parsed.doc, "components"), "securitySchemes");
|
|
194
|
+
if (!isObject(securitySchemes)) return result;
|
|
195
|
+
for (const [name, scheme] of Object.entries(securitySchemes)) {
|
|
196
|
+
if (!isObject(scheme)) continue;
|
|
197
|
+
const flowsProp = getProperty(scheme, "flows");
|
|
198
|
+
result.set(name, {
|
|
199
|
+
type: getString(scheme, "type") ?? "",
|
|
200
|
+
description: getString(scheme, "description"),
|
|
201
|
+
name: getString(scheme, "name"),
|
|
202
|
+
location: getString(scheme, "in"),
|
|
203
|
+
scheme: getString(scheme, "scheme"),
|
|
204
|
+
bearerFormat: getString(scheme, "bearerFormat"),
|
|
205
|
+
flows: isObject(flowsProp) ? flowsProp : void 0,
|
|
206
|
+
openIdConnectUrl: getString(scheme, "openIdConnectUrl")
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
function getResponseHeaders(response) {
|
|
212
|
+
const result = /* @__PURE__ */ new Map();
|
|
213
|
+
const headers = getProperty(response, "headers");
|
|
214
|
+
if (!isObject(headers)) return result;
|
|
215
|
+
for (const [name, headerObj] of Object.entries(headers)) {
|
|
216
|
+
if (!isObject(headerObj)) continue;
|
|
217
|
+
const ref = getString(headerObj, "$ref");
|
|
218
|
+
const header = (ref !== void 0 ? resolveRefInDoc(headerObj, ref) : void 0) ?? headerObj;
|
|
219
|
+
const schemaProp = getProperty(header, "schema");
|
|
220
|
+
result.set(name, {
|
|
221
|
+
name,
|
|
222
|
+
description: getString(header, "description"),
|
|
223
|
+
required: getProperty(header, "required") === true,
|
|
224
|
+
deprecated: getProperty(header, "deprecated") === true,
|
|
225
|
+
schema: isObject(schemaProp) ? schemaProp : void 0
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
function listWebhooks(parsed) {
|
|
231
|
+
const result = [];
|
|
232
|
+
const webhooks = getProperty(parsed.doc, "webhooks");
|
|
233
|
+
if (!isObject(webhooks)) return result;
|
|
234
|
+
for (const [name, hookItem] of Object.entries(webhooks)) {
|
|
235
|
+
if (!isObject(hookItem)) continue;
|
|
236
|
+
const operations = [];
|
|
237
|
+
for (const method of METHODS) {
|
|
238
|
+
const operation = getProperty(hookItem, method);
|
|
239
|
+
if (!isObject(operation)) continue;
|
|
240
|
+
operations.push({
|
|
241
|
+
path: name,
|
|
242
|
+
method,
|
|
243
|
+
operationId: getString(operation, "operationId"),
|
|
244
|
+
summary: getString(operation, "summary"),
|
|
245
|
+
description: getString(operation, "description"),
|
|
246
|
+
deprecated: getProperty(operation, "deprecated") === true,
|
|
247
|
+
operation
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
result.push({
|
|
251
|
+
name,
|
|
252
|
+
operations
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
function getExternalDocs(obj) {
|
|
258
|
+
const docs = getProperty(obj, "externalDocs");
|
|
259
|
+
if (!isObject(docs)) return void 0;
|
|
260
|
+
const url = getString(docs, "url");
|
|
261
|
+
if (typeof url !== "string") return void 0;
|
|
262
|
+
return {
|
|
263
|
+
url,
|
|
264
|
+
description: getString(docs, "description")
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function getXmlInfo(schema) {
|
|
268
|
+
const xml = getProperty(schema, "xml");
|
|
269
|
+
if (!isObject(xml)) return void 0;
|
|
270
|
+
return {
|
|
271
|
+
name: getString(xml, "name"),
|
|
272
|
+
namespace: getString(xml, "namespace"),
|
|
273
|
+
prefix: getString(xml, "prefix"),
|
|
274
|
+
attribute: getProperty(xml, "attribute") === true,
|
|
275
|
+
wrapped: getProperty(xml, "wrapped") === true
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function listCallbacks(parsed, path, method) {
|
|
279
|
+
const operation = getProperty(lookupPathItem(parsed, path), method);
|
|
280
|
+
if (!isObject(operation)) return [];
|
|
281
|
+
const callbacks = getProperty(operation, "callbacks");
|
|
282
|
+
if (!isObject(callbacks)) return [];
|
|
283
|
+
const result = [];
|
|
284
|
+
for (const [name, callbackItem] of Object.entries(callbacks)) {
|
|
285
|
+
if (!isObject(callbackItem)) continue;
|
|
286
|
+
const operations = [];
|
|
287
|
+
for (const [cbPath, cbPathItem] of Object.entries(callbackItem)) {
|
|
288
|
+
if (!isObject(cbPathItem)) continue;
|
|
289
|
+
for (const cbMethod of METHODS) {
|
|
290
|
+
const cbOp = getProperty(cbPathItem, cbMethod);
|
|
291
|
+
if (!isObject(cbOp)) continue;
|
|
292
|
+
const ref = getString(cbOp, "$ref");
|
|
293
|
+
const resolved = ref !== void 0 ? resolveRefInDoc(parsed.doc, ref) ?? cbOp : cbOp;
|
|
294
|
+
operations.push({
|
|
295
|
+
path: `${name}/${cbPath}`,
|
|
296
|
+
method: cbMethod,
|
|
297
|
+
operationId: getString(resolved, "operationId"),
|
|
298
|
+
summary: getString(resolved, "summary"),
|
|
299
|
+
description: getString(resolved, "description"),
|
|
300
|
+
deprecated: getProperty(resolved, "deprecated") === true,
|
|
301
|
+
operation: isObject(resolved) ? resolved : cbOp
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
result.push({
|
|
306
|
+
name,
|
|
307
|
+
operations
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
function getLinks(parsed, path, method, statusCode) {
|
|
313
|
+
const response = getProperty(getProperty(getProperty(lookupPathItem(parsed, path), method), "responses"), statusCode);
|
|
314
|
+
if (!isObject(response)) return [];
|
|
315
|
+
const links = getProperty(response, "links");
|
|
316
|
+
if (!isObject(links)) return [];
|
|
317
|
+
const result = [];
|
|
318
|
+
for (const [name, linkObj] of Object.entries(links)) {
|
|
319
|
+
if (!isObject(linkObj)) continue;
|
|
320
|
+
const ref = getString(linkObj, "$ref");
|
|
321
|
+
const resolved = ref !== void 0 ? resolveRefInDoc(parsed.doc, ref) ?? linkObj : linkObj;
|
|
322
|
+
const link = isObject(resolved) ? resolved : linkObj;
|
|
323
|
+
const params = getProperty(link, "parameters");
|
|
324
|
+
const paramMap = /* @__PURE__ */ new Map();
|
|
325
|
+
if (isObject(params)) {
|
|
326
|
+
for (const [paramName, paramValue] of Object.entries(params)) if (typeof paramValue === "string") paramMap.set(paramName, paramValue);
|
|
327
|
+
}
|
|
328
|
+
result.push({
|
|
329
|
+
name,
|
|
330
|
+
operationId: getString(link, "operationId"),
|
|
331
|
+
operationRef: getString(link, "operationRef"),
|
|
332
|
+
description: getString(link, "description"),
|
|
333
|
+
parameters: paramMap,
|
|
334
|
+
requestBody: getString(link, "requestBody")
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
158
339
|
//#endregion
|
|
159
|
-
export { getParameters, getRequestBody, getResponses, getSchema, listOperations, parseOpenApiDocument };
|
|
340
|
+
export { getExternalDocs, getLinks, getParameters, getRequestBody, getResponseHeaders, getResponses, getSchema, getSecurityRequirements, getSecuritySchemes, getXmlInfo, listCallbacks, listOperations, listWebhooks, parseOpenApiDocument };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
1
|
+
import { M as WalkedField, T as SchemaMeta, d as FieldOverrides, u as FieldOverride } from "../types-D_5ST7SS.mjs";
|
|
2
|
+
import { t as Diagnostic } from "../diagnostics-DzbZmcLI.mjs";
|
|
3
|
+
import { t as SchemaError } from "../errors-C5zRC2PU.mjs";
|
|
4
|
+
import { l as RenderProps, r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
|
|
5
|
+
import { c as ResolveOpenAPIRef, s as PathOfType, t as FromJSONSchema } from "../typeInference-k7FXfTVO.mjs";
|
|
3
6
|
import { z } from "zod";
|
|
4
|
-
import { ReactNode } from "react";
|
|
5
7
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
8
|
+
import { ReactNode } from "react";
|
|
6
9
|
|
|
7
10
|
//#region src/react/SchemaComponent.d.ts
|
|
8
11
|
/**
|
|
@@ -51,6 +54,10 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
51
54
|
onValidationError?: (error: unknown) => void;
|
|
52
55
|
/** Called when schema normalisation or rendering fails. */
|
|
53
56
|
onError?: (error: SchemaError) => void;
|
|
57
|
+
/** Called with each diagnostic emitted during schema processing. */
|
|
58
|
+
onDiagnostic?: (diagnostic: Diagnostic) => void;
|
|
59
|
+
/** When true, any diagnostic becomes a thrown error. */
|
|
60
|
+
strict?: boolean;
|
|
54
61
|
/** Per-field meta overrides — nested object mirroring schema shape. */
|
|
55
62
|
fields?: InferFields<T, Ref>;
|
|
56
63
|
/** Meta overrides applied to the root schema. */
|
|
@@ -72,6 +79,8 @@ declare function SchemaComponent<T = unknown, Ref extends string | undefined = u
|
|
|
72
79
|
validate,
|
|
73
80
|
onValidationError,
|
|
74
81
|
onError,
|
|
82
|
+
onDiagnostic,
|
|
83
|
+
strict,
|
|
75
84
|
fields,
|
|
76
85
|
meta: componentMeta,
|
|
77
86
|
readOnly,
|
|
@@ -79,7 +88,7 @@ declare function SchemaComponent<T = unknown, Ref extends string | undefined = u
|
|
|
79
88
|
description,
|
|
80
89
|
widgets: instanceWidgets
|
|
81
90
|
}: SchemaComponentProps<T, Ref>): ReactNode;
|
|
82
|
-
declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => ReactNode, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap): ReactNode;
|
|
91
|
+
declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => ReactNode, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap, depth?: number): ReactNode;
|
|
83
92
|
/**
|
|
84
93
|
* Infer the schema's output type for SchemaField path inference.
|
|
85
94
|
*/
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { isObject,
|
|
3
|
-
import { normaliseSchema } from "../core/adapter.mjs";
|
|
2
|
+
import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
|
|
4
3
|
import { SchemaFieldError, SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
|
|
4
|
+
import { normaliseSchema } from "../core/adapter.mjs";
|
|
5
5
|
import { getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
|
|
6
6
|
import { walk } from "../core/walker.mjs";
|
|
7
7
|
import { headlessResolver } from "./headless.mjs";
|
|
8
|
+
import { resolvePath, resolveValue, setNestedValue } from "./fieldPath.mjs";
|
|
8
9
|
import { z } from "zod";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
11
|
import { createContext, isValidElement, useCallback, useContext, useMemo } from "react";
|
|
10
|
-
import { jsx } from "react/jsx-runtime";
|
|
11
12
|
//#region src/react/SchemaComponent.tsx
|
|
12
13
|
/**
|
|
13
14
|
* <SchemaComponent> — renders UI from Zod, JSON Schema, or OpenAPI schemas.
|
|
@@ -45,7 +46,7 @@ const globalWidgets = /* @__PURE__ */ new Map();
|
|
|
45
46
|
function registerWidget(name, render) {
|
|
46
47
|
globalWidgets.set(name, render);
|
|
47
48
|
}
|
|
48
|
-
function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange, validate, onValidationError, onError, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets }) {
|
|
49
|
+
function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange, validate, onValidationError, onError, onDiagnostic, strict, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets }) {
|
|
49
50
|
const userResolver = useContext(UserResolverContext);
|
|
50
51
|
const contextWidgets = useContext(WidgetsContext);
|
|
51
52
|
const mergedMeta = useMemo(() => {
|
|
@@ -60,12 +61,16 @@ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange,
|
|
|
60
61
|
writeOnly,
|
|
61
62
|
description
|
|
62
63
|
]);
|
|
64
|
+
const diagnostics = onDiagnostic !== void 0 || strict === true ? {
|
|
65
|
+
...onDiagnostic !== void 0 ? { diagnostics: onDiagnostic } : {},
|
|
66
|
+
...strict !== void 0 ? { strict } : {}
|
|
67
|
+
} : void 0;
|
|
63
68
|
let jsonSchema;
|
|
64
69
|
let zodSchema;
|
|
65
70
|
let rootMeta;
|
|
66
71
|
let rootDocument;
|
|
67
72
|
try {
|
|
68
|
-
const normalised = normaliseSchema(schemaInput, refInput);
|
|
73
|
+
const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 ? { diagnostics } : void 0);
|
|
69
74
|
jsonSchema = normalised.jsonSchema;
|
|
70
75
|
zodSchema = normalised.zodSchema;
|
|
71
76
|
rootMeta = normalised.rootMeta;
|
|
@@ -95,16 +100,19 @@ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange,
|
|
|
95
100
|
onValidationError,
|
|
96
101
|
fields
|
|
97
102
|
]);
|
|
98
|
-
const
|
|
103
|
+
const walkOptions = {
|
|
99
104
|
componentMeta: mergedMeta,
|
|
100
105
|
rootMeta,
|
|
101
106
|
fieldOverrides: fields,
|
|
102
|
-
rootDocument
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
rootDocument,
|
|
108
|
+
...diagnostics !== void 0 ? { diagnostics } : {}
|
|
109
|
+
};
|
|
110
|
+
const tree = walk(jsonSchema, walkOptions);
|
|
111
|
+
const makeRenderChild = (currentDepth) => (childTree, childValue, childOnChange) => {
|
|
112
|
+
return renderField(childTree, childValue, childOnChange, userResolver, makeRenderChild(currentDepth + 1), instanceWidgets, contextWidgets, currentDepth + 1);
|
|
106
113
|
};
|
|
107
|
-
|
|
114
|
+
const renderChild = makeRenderChild(0);
|
|
115
|
+
return renderField(tree, value ?? tree.defaultValue, handleChange, userResolver, renderChild, instanceWidgets, contextWidgets, 0);
|
|
108
116
|
}
|
|
109
117
|
function runValidation(zodSchema, jsonSchema, value) {
|
|
110
118
|
if (zodSchema !== void 0 && isObject(zodSchema)) {
|
|
@@ -124,8 +132,17 @@ function runValidation(zodSchema, jsonSchema, value) {
|
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
/** Maximum rendering depth before treating a field as recursive. */
|
|
136
|
+
const MAX_RENDER_DEPTH = 10;
|
|
137
|
+
function renderField(tree, value, onChange, userResolver, renderChild, instanceWidgets, contextWidgets, depth = 0) {
|
|
138
|
+
if (depth >= MAX_RENDER_DEPTH) {
|
|
139
|
+
const refTarget = tree.type === "recursive" ? tree.refTarget : "";
|
|
140
|
+
return /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsxs("em", { children: [
|
|
141
|
+
"↻ ",
|
|
142
|
+
(typeof tree.meta.description === "string" ? tree.meta.description : refTarget) || "schema",
|
|
143
|
+
" (recursive)"
|
|
144
|
+
] }) });
|
|
145
|
+
}
|
|
129
146
|
const componentHint = tree.meta.component;
|
|
130
147
|
if (typeof componentHint === "string") {
|
|
131
148
|
const widget = instanceWidgets?.get(componentHint) ?? contextWidgets?.get(componentHint) ?? globalWidgets.get(componentHint);
|
|
@@ -166,13 +183,21 @@ function buildRenderProps(tree, value, onChange, renderChild) {
|
|
|
166
183
|
tree,
|
|
167
184
|
renderChild
|
|
168
185
|
};
|
|
169
|
-
if (tree.
|
|
170
|
-
if (tree.element !== void 0) props.element = tree.element;
|
|
171
|
-
if (tree.
|
|
172
|
-
if (tree.
|
|
173
|
-
if (tree.
|
|
174
|
-
if (tree.
|
|
175
|
-
if (tree.
|
|
186
|
+
if (tree.type === "enum") props.enumValues = tree.enumValues;
|
|
187
|
+
if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
|
|
188
|
+
if (tree.type === "object") props.fields = tree.fields;
|
|
189
|
+
if (tree.type === "union" || tree.type === "discriminatedUnion") props.options = tree.options;
|
|
190
|
+
if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
|
|
191
|
+
if (tree.type === "record") props.keyType = tree.keyType;
|
|
192
|
+
if (tree.type === "record") props.valueType = tree.valueType;
|
|
193
|
+
if (tree.type === "tuple") props.prefixItems = tree.prefixItems;
|
|
194
|
+
if (tree.type === "conditional") props.ifClause = tree.ifClause;
|
|
195
|
+
if (tree.type === "conditional" && tree.thenClause !== void 0) props.thenClause = tree.thenClause;
|
|
196
|
+
if (tree.type === "conditional" && tree.elseClause !== void 0) props.elseClause = tree.elseClause;
|
|
197
|
+
if (tree.type === "negation") props.negated = tree.negated;
|
|
198
|
+
if (tree.type === "recursive") props.refTarget = tree.refTarget;
|
|
199
|
+
if (tree.type === "literal") props.literalValues = tree.literalValues;
|
|
200
|
+
if (tree.examples !== void 0) props.examples = tree.examples;
|
|
176
201
|
return props;
|
|
177
202
|
}
|
|
178
203
|
function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange, meta: fieldMeta, validate, onValidationError }) {
|
|
@@ -215,78 +240,10 @@ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange
|
|
|
215
240
|
onChange,
|
|
216
241
|
onValidationError
|
|
217
242
|
]);
|
|
218
|
-
const
|
|
219
|
-
return renderField(childTree, childValue, childOnChange, userResolver,
|
|
243
|
+
const makeRenderChild = (currentDepth) => (childTree, childValue, childOnChange) => {
|
|
244
|
+
return renderField(childTree, childValue, childOnChange, userResolver, makeRenderChild(currentDepth + 1), void 0, contextWidgets, currentDepth + 1);
|
|
220
245
|
};
|
|
221
|
-
return renderField(fieldTree, fieldValue, handleChange, userResolver,
|
|
222
|
-
}
|
|
223
|
-
function resolvePath(tree, path) {
|
|
224
|
-
if (path.length === 0) return tree;
|
|
225
|
-
const parts = path.split(".");
|
|
226
|
-
let current = tree;
|
|
227
|
-
for (const part of parts) {
|
|
228
|
-
if (current === void 0) return void 0;
|
|
229
|
-
const bracketMatch = /^(.+)\[(\d+)\]$/.exec(part);
|
|
230
|
-
if (bracketMatch?.[1] !== void 0 && bracketMatch[2] !== void 0) {
|
|
231
|
-
const arrayField = bracketMatch[1];
|
|
232
|
-
if (current.fields !== void 0) current = current.fields[arrayField];
|
|
233
|
-
if (current?.element !== void 0) current = current.element;
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
if (current.fields !== void 0) current = current.fields[part];
|
|
237
|
-
else if (current.element !== void 0) current = current.element;
|
|
238
|
-
else return;
|
|
239
|
-
}
|
|
240
|
-
return current;
|
|
241
|
-
}
|
|
242
|
-
function resolveValue(root, path) {
|
|
243
|
-
if (path.length === 0) return root;
|
|
244
|
-
const parts = path.split(".");
|
|
245
|
-
let current = root;
|
|
246
|
-
for (const part of parts) {
|
|
247
|
-
if (typeof current !== "object" || current === null) return void 0;
|
|
248
|
-
const bracketMatch = /^(.+)\[(\d+)\]$/.exec(part);
|
|
249
|
-
if (bracketMatch?.[1] !== void 0 && bracketMatch[2] !== void 0) {
|
|
250
|
-
const key = bracketMatch[1];
|
|
251
|
-
const index = Number(bracketMatch[2]);
|
|
252
|
-
const arr = toRecord(current)[key];
|
|
253
|
-
if (Array.isArray(arr)) current = arr[index];
|
|
254
|
-
else return;
|
|
255
|
-
} else current = toRecord(current)[part];
|
|
256
|
-
}
|
|
257
|
-
return current;
|
|
258
|
-
}
|
|
259
|
-
function setNestedValue(root, path, leafValue) {
|
|
260
|
-
if (path.length === 0) return leafValue;
|
|
261
|
-
const parts = path.split(".");
|
|
262
|
-
const result = isObject(root) ? { ...toRecord(root) } : {};
|
|
263
|
-
let current = result;
|
|
264
|
-
for (let i = 0; i < parts.length; i++) {
|
|
265
|
-
const part = parts[i];
|
|
266
|
-
if (part === void 0) break;
|
|
267
|
-
const isLast = i === parts.length - 1;
|
|
268
|
-
const bracketMatch = /^(.+)\[(\d+)\]$/.exec(part);
|
|
269
|
-
if (bracketMatch?.[1] !== void 0 && bracketMatch[2] !== void 0) {
|
|
270
|
-
const key = bracketMatch[1];
|
|
271
|
-
const index = Number(bracketMatch[2]);
|
|
272
|
-
const existing = current[key];
|
|
273
|
-
const arr = Array.isArray(existing) ? existing.slice() : [];
|
|
274
|
-
if (isLast) arr[index] = leafValue;
|
|
275
|
-
current[key] = arr;
|
|
276
|
-
const nextCurrent = arr[index];
|
|
277
|
-
if (nextCurrent !== void 0 && isObject(nextCurrent)) current = toRecord(nextCurrent);
|
|
278
|
-
} else if (isLast) current[part] = leafValue;
|
|
279
|
-
else {
|
|
280
|
-
const existing = current[part];
|
|
281
|
-
const next = isObject(existing) ? { ...toRecord(existing) } : {};
|
|
282
|
-
current[part] = next;
|
|
283
|
-
current = next;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return result;
|
|
287
|
-
}
|
|
288
|
-
function isFieldErrorCallback(value) {
|
|
289
|
-
return typeof value === "function";
|
|
246
|
+
return renderField(fieldTree, fieldValue, handleChange, userResolver, makeRenderChild(0), void 0, contextWidgets, 0);
|
|
290
247
|
}
|
|
291
248
|
/**
|
|
292
249
|
* Dispatch Zod errors to per-field onValidationError callbacks.
|
|
@@ -315,6 +272,9 @@ function dispatchFieldErrors(fields, error) {
|
|
|
315
272
|
if (fieldErrors.length > 0 && isFieldErrorCallback(fieldCallback)) fieldCallback({ issues: fieldErrors });
|
|
316
273
|
}
|
|
317
274
|
}
|
|
275
|
+
function isFieldErrorCallback(value) {
|
|
276
|
+
return typeof value === "function";
|
|
277
|
+
}
|
|
318
278
|
function isCallable(value) {
|
|
319
279
|
return typeof value === "function";
|
|
320
280
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
|
|
2
|
+
import { t as Diagnostic } from "../diagnostics-DzbZmcLI.mjs";
|
|
3
|
+
import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
|
|
2
4
|
import { WidgetMap } from "./SchemaComponent.mjs";
|
|
3
5
|
import { ReactNode } from "react";
|
|
4
6
|
|
|
@@ -24,6 +26,10 @@ interface SchemaViewProps {
|
|
|
24
26
|
resolver?: ComponentResolver;
|
|
25
27
|
/** Instance-scoped widgets. */
|
|
26
28
|
widgets?: WidgetMap;
|
|
29
|
+
/** Called with each diagnostic emitted during schema processing. */
|
|
30
|
+
onDiagnostic?: (diagnostic: Diagnostic) => void;
|
|
31
|
+
/** When true, any diagnostic becomes a thrown error. */
|
|
32
|
+
strict?: boolean;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
35
|
* Server-safe schema renderer — no hooks, no context, no state.
|
|
@@ -39,7 +45,9 @@ declare function SchemaView({
|
|
|
39
45
|
meta: componentMeta,
|
|
40
46
|
description,
|
|
41
47
|
resolver,
|
|
42
|
-
widgets
|
|
48
|
+
widgets,
|
|
49
|
+
onDiagnostic,
|
|
50
|
+
strict
|
|
43
51
|
}: SchemaViewProps): ReactNode;
|
|
44
52
|
//#endregion
|
|
45
53
|
export { SchemaView, SchemaViewProps };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { normaliseSchema } from "../core/adapter.mjs";
|
|
2
1
|
import { SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
|
|
2
|
+
import { normaliseSchema } from "../core/adapter.mjs";
|
|
3
3
|
import { getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
|
|
4
4
|
import { walk } from "../core/walker.mjs";
|
|
5
5
|
import { headlessResolver } from "./headless.mjs";
|
|
6
|
-
import { isValidElement } from "react";
|
|
7
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { createElement, isValidElement } from "react";
|
|
8
8
|
//#region src/react/SchemaView.tsx
|
|
9
9
|
/**
|
|
10
10
|
* React Server Component for read-only schema rendering.
|
|
@@ -37,31 +37,42 @@ function noop() {}
|
|
|
37
37
|
* Always renders in read-only mode. For editable forms, use
|
|
38
38
|
* `<SchemaComponent>` with `"use client"`.
|
|
39
39
|
*/
|
|
40
|
-
function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: componentMeta, description, resolver, widgets }) {
|
|
40
|
+
function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: componentMeta, description, resolver, widgets, onDiagnostic, strict }) {
|
|
41
41
|
const mergedMeta = {
|
|
42
42
|
...componentMeta,
|
|
43
43
|
readOnly: true
|
|
44
44
|
};
|
|
45
45
|
if (description !== void 0) mergedMeta.description = description;
|
|
46
|
+
const diagnostics = onDiagnostic !== void 0 || strict === true ? {
|
|
47
|
+
...onDiagnostic !== void 0 ? { diagnostics: onDiagnostic } : {},
|
|
48
|
+
...strict !== void 0 ? { strict } : {}
|
|
49
|
+
} : void 0;
|
|
46
50
|
let jsonSchema;
|
|
47
51
|
let rootMeta;
|
|
48
52
|
let rootDocument;
|
|
49
53
|
try {
|
|
50
|
-
const normalised = normaliseSchema(schemaInput, refInput);
|
|
54
|
+
const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 ? { diagnostics } : void 0);
|
|
51
55
|
jsonSchema = normalised.jsonSchema;
|
|
52
56
|
rootMeta = normalised.rootMeta;
|
|
53
57
|
rootDocument = normalised.rootDocument;
|
|
54
58
|
} catch (err) {
|
|
55
59
|
throw new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", schemaInput, "unknown");
|
|
56
60
|
}
|
|
57
|
-
const
|
|
61
|
+
const walkOptions = {
|
|
58
62
|
componentMeta: mergedMeta,
|
|
59
63
|
rootMeta,
|
|
60
64
|
fieldOverrides: fields,
|
|
61
|
-
rootDocument
|
|
62
|
-
|
|
65
|
+
rootDocument,
|
|
66
|
+
...diagnostics !== void 0 ? { diagnostics } : {}
|
|
67
|
+
};
|
|
68
|
+
const tree = walk(jsonSchema, walkOptions);
|
|
63
69
|
const userResolver = resolver !== void 0 ? mergeResolvers(resolver, headlessResolver) : headlessResolver;
|
|
64
|
-
const
|
|
70
|
+
const MAX_SERVER_DEPTH = 10;
|
|
71
|
+
const makeRenderChild = (currentDepth) => (childTree, childValue) => {
|
|
72
|
+
if (currentDepth >= MAX_SERVER_DEPTH) return createElement("fieldset", null, createElement("em", null, `\u21bb ${typeof childTree.meta.description === "string" ? childTree.meta.description : childTree.type === "recursive" ? childTree.refTarget : "schema"} (recursive)`));
|
|
73
|
+
return renderFieldServer(childTree, childValue, userResolver, makeRenderChild(currentDepth + 1), widgets);
|
|
74
|
+
};
|
|
75
|
+
const renderChild = makeRenderChild(0);
|
|
65
76
|
return renderFieldServer(tree, value ?? tree.defaultValue, userResolver, renderChild, widgets);
|
|
66
77
|
}
|
|
67
78
|
function renderFieldServer(tree, value, resolver, renderChild, widgets) {
|
|
@@ -99,13 +110,20 @@ function renderFieldServer(tree, value, resolver, renderChild, widgets) {
|
|
|
99
110
|
tree,
|
|
100
111
|
renderChild: (childTree, childValue) => renderChild(childTree, childValue)
|
|
101
112
|
};
|
|
102
|
-
if (tree.
|
|
103
|
-
if (tree.element !== void 0) props.element = tree.element;
|
|
104
|
-
if (tree.
|
|
105
|
-
if (tree.
|
|
106
|
-
if (tree.
|
|
107
|
-
if (tree.
|
|
108
|
-
if (tree.
|
|
113
|
+
if (tree.type === "enum") props.enumValues = tree.enumValues;
|
|
114
|
+
if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
|
|
115
|
+
if (tree.type === "object") props.fields = tree.fields;
|
|
116
|
+
if (tree.type === "union" || tree.type === "discriminatedUnion") props.options = tree.options;
|
|
117
|
+
if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
|
|
118
|
+
if (tree.type === "record") props.keyType = tree.keyType;
|
|
119
|
+
if (tree.type === "record") props.valueType = tree.valueType;
|
|
120
|
+
if (tree.type === "tuple") props.prefixItems = tree.prefixItems;
|
|
121
|
+
if (tree.type === "conditional") props.ifClause = tree.ifClause;
|
|
122
|
+
if (tree.type === "conditional" && tree.thenClause !== void 0) props.thenClause = tree.thenClause;
|
|
123
|
+
if (tree.type === "conditional" && tree.elseClause !== void 0) props.elseClause = tree.elseClause;
|
|
124
|
+
if (tree.type === "negation") props.negated = tree.negated;
|
|
125
|
+
if (tree.type === "recursive") props.refTarget = tree.refTarget;
|
|
126
|
+
if (tree.type === "literal") props.literalValues = tree.literalValues;
|
|
109
127
|
try {
|
|
110
128
|
const result = renderFn(props);
|
|
111
129
|
if (result !== void 0 && result !== null) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { M as WalkedField } from "../types-D_5ST7SS.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/react/fieldPath.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a dot-separated path through a WalkedField tree.
|
|
6
|
+
* Supports array index notation: `field[0]`.
|
|
7
|
+
*/
|
|
8
|
+
declare function resolvePath(tree: WalkedField, path: string): WalkedField | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a dot-separated path through a data value.
|
|
11
|
+
* Supports array index notation: `field[0]`.
|
|
12
|
+
*/
|
|
13
|
+
declare function resolveValue(root: unknown, path: string): unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Set a value at a dot-separated path, producing a new root object.
|
|
16
|
+
* Does not mutate the input — returns a shallow-updated copy at each level.
|
|
17
|
+
*/
|
|
18
|
+
declare function setNestedValue(root: unknown, path: string, leafValue: unknown): unknown;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { resolvePath, resolveValue, setNestedValue };
|