schema-components 2.0.1 → 2.1.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 +98 -1
- package/dist/SchemaComponent-B__6-5-E.d.mts +277 -0
- package/dist/SchemaComponent-BxzzsHsK.mjs +668 -0
- package/dist/adapter-ktQaheWB.d.mts +213 -0
- package/dist/constructorTypes-BdCiMS6e.d.mts +30 -0
- package/dist/core/adapter.d.mts +3 -213
- package/dist/core/adapter.mjs +33 -25
- package/dist/core/constraintHint.d.mts +1 -1
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/contexts.d.mts +71 -0
- package/dist/core/contexts.mjs +1 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/{react → core}/fieldPath.d.mts +2 -2
- package/dist/{react → core}/fieldPath.mjs +3 -3
- package/dist/core/formats.d.mts +1 -1
- package/dist/core/guards.d.mts +2 -2
- package/dist/core/guards.mjs +2 -2
- package/dist/core/inferValue.d.mts +1 -1
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/merge.d.mts +1 -1
- package/dist/core/normalise.d.mts +6 -6
- package/dist/core/normalise.mjs +1 -1
- package/dist/core/openapi30.d.mts +1 -1
- package/dist/core/openapi30.mjs +1 -1
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/renderField.d.mts +147 -0
- package/dist/core/renderField.mjs +81 -0
- package/dist/core/renderer.d.mts +2 -199
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +1 -981
- package/dist/core/types.d.mts +1 -1
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/uri.d.mts +2 -2
- package/dist/core/uri.mjs +2 -2
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +4 -4
- package/dist/core/walkBuilders.mjs +1 -1
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +3 -3
- package/dist/{errors-Dki7tji4.d.mts → errors-DbaI04x2.d.mts} +1 -1
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/html.d.mts +10 -8
- package/dist/html/renderToHtml.d.mts +5 -5
- package/dist/html/renderToHtml.mjs +45 -24
- package/dist/html/renderToHtmlStream.d.mts +5 -5
- package/dist/html/renderers.d.mts +1 -1
- package/dist/html/streamRenderers.d.mts +3 -3
- package/dist/{inferValue-PPXWJpbN.d.mts → inferValue-eAnh50EM.d.mts} +6 -6
- package/dist/lit/SchemaComponent.d.mts +125 -0
- package/dist/lit/SchemaComponent.mjs +2 -0
- package/dist/lit/SchemaField.d.mts +65 -0
- package/dist/lit/SchemaField.mjs +2 -0
- package/dist/lit/SchemaView.d.mts +14 -0
- package/dist/lit/SchemaView.mjs +2 -0
- package/dist/lit/constructorTypes.d.mts +2 -0
- package/dist/lit/constructorTypes.mjs +1 -0
- package/dist/lit/contexts.d.mts +78 -0
- package/dist/lit/contexts.mjs +238 -0
- package/dist/lit/defaultResolver.d.mts +33 -0
- package/dist/lit/defaultResolver.mjs +2 -0
- package/dist/lit/registry.d.mts +66 -0
- package/dist/lit/registry.mjs +2 -0
- package/dist/lit/renderers/baseElement.d.mts +131 -0
- package/dist/lit/renderers/baseElement.mjs +109 -0
- package/dist/lit/renderers/recordHelpers.d.mts +25 -0
- package/dist/lit/renderers/recordHelpers.mjs +55 -0
- package/dist/lit/renderers/scArray.d.mts +14 -0
- package/dist/lit/renderers/scArray.mjs +86 -0
- package/dist/lit/renderers/scBoolean.d.mts +15 -0
- package/dist/lit/renderers/scBoolean.mjs +47 -0
- package/dist/lit/renderers/scConditional.d.mts +23 -0
- package/dist/lit/renderers/scConditional.mjs +65 -0
- package/dist/lit/renderers/scDiscriminated.d.mts +23 -0
- package/dist/lit/renderers/scDiscriminated.mjs +138 -0
- package/dist/lit/renderers/scEnum.d.mts +16 -0
- package/dist/lit/renderers/scEnum.mjs +66 -0
- package/dist/lit/renderers/scFile.d.mts +15 -0
- package/dist/lit/renderers/scFile.mjs +53 -0
- package/dist/lit/renderers/scLiteralNullNever.d.mts +30 -0
- package/dist/lit/renderers/scLiteralNullNever.mjs +57 -0
- package/dist/lit/renderers/scNumber.d.mts +15 -0
- package/dist/lit/renderers/scNumber.mjs +64 -0
- package/dist/lit/renderers/scObject.d.mts +14 -0
- package/dist/lit/renderers/scObject.mjs +57 -0
- package/dist/lit/renderers/scRecord.d.mts +14 -0
- package/dist/lit/renderers/scRecord.mjs +112 -0
- package/dist/lit/renderers/scString.d.mts +19 -0
- package/dist/lit/renderers/scString.mjs +165 -0
- package/dist/lit/renderers/scTuple.d.mts +14 -0
- package/dist/lit/renderers/scTuple.mjs +58 -0
- package/dist/lit/renderers/scUnion.d.mts +14 -0
- package/dist/lit/renderers/scUnion.mjs +44 -0
- package/dist/lit/renderers/scUnknown.d.mts +15 -0
- package/dist/lit/renderers/scUnknown.mjs +45 -0
- package/dist/lit/ssr.d.mts +37 -0
- package/dist/lit/ssr.mjs +9565 -0
- package/dist/lit/types.d.mts +2 -0
- package/dist/lit/types.mjs +1 -0
- package/dist/lit/widget.d.mts +71 -0
- package/dist/lit/widget.mjs +87 -0
- package/dist/{normalise-DB-Xtjmn.mjs → normalise-BkePrJ4v.mjs} +6 -6
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/components.d.mts +5 -5
- package/dist/openapi/components.mjs +1 -1
- package/dist/openapi/parser.d.mts +2 -2
- package/dist/openapi/resolve.d.mts +1 -1
- package/dist/openapi/resolve.mjs +1 -1
- package/dist/preact/SchemaComponent.d.mts +3 -0
- package/dist/preact/SchemaComponent.mjs +26 -0
- package/dist/preact/SchemaErrorBoundary.d.mts +2 -0
- package/dist/preact/SchemaErrorBoundary.mjs +20 -0
- package/dist/preact/SchemaView.d.mts +2 -0
- package/dist/preact/SchemaView.mjs +22 -0
- package/dist/preact/headless.d.mts +2 -0
- package/dist/preact/headless.mjs +18 -0
- package/dist/react/SchemaComponent.d.mts +3 -270
- package/dist/react/SchemaComponent.mjs +48 -39
- package/dist/react/SchemaErrorBoundary.mjs +7 -4
- package/dist/react/SchemaView.d.mts +11 -10
- package/dist/react/SchemaView.mjs +32 -29
- package/dist/react/a11y.d.mts +2 -2
- package/dist/react/fieldShell.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/{ref-DdsbekXX.d.mts → ref-DWrQG1Er.d.mts} +1 -1
- package/dist/renderer-ab9E52Bp.d.mts +245 -0
- package/dist/solid/SchemaComponent.d.mts +136 -0
- package/dist/solid/SchemaComponent.mjs +391 -0
- package/dist/solid/SchemaErrorBoundary.d.mts +38 -0
- package/dist/solid/SchemaErrorBoundary.mjs +57 -0
- package/dist/solid/SchemaField.d.mts +40 -0
- package/dist/solid/SchemaField.mjs +113 -0
- package/dist/solid/SchemaView.d.mts +54 -0
- package/dist/solid/SchemaView.mjs +168 -0
- package/dist/solid/a11y.d.mts +70 -0
- package/dist/solid/a11y.mjs +71 -0
- package/dist/solid/contexts.d.mts +37 -0
- package/dist/solid/contexts.mjs +66 -0
- package/dist/solid/headless.d.mts +10 -0
- package/dist/solid/headless.mjs +27 -0
- package/dist/solid/renderers.d.mts +79 -0
- package/dist/solid/renderers.mjs +840 -0
- package/dist/solid/types.d.mts +90 -0
- package/dist/solid/types.mjs +1 -0
- package/dist/solid/widget.d.mts +29 -0
- package/dist/solid/widget.mjs +35 -0
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/typeInference-Y8tNEQJk.d.mts +983 -0
- package/dist/types-BCy7K3nk.d.mts +125 -0
- package/package.json +71 -1
- package/src/svelte/SchemaComponent.svelte +427 -0
- package/src/svelte/SchemaErrorBoundary.svelte +66 -0
- package/src/svelte/SchemaField.svelte +216 -0
- package/src/svelte/SchemaProvider.svelte +46 -0
- package/src/svelte/SchemaView.svelte +244 -0
- package/src/svelte/a11y.ts +112 -0
- package/src/svelte/contexts.ts +79 -0
- package/src/svelte/dispatch.ts +267 -0
- package/src/svelte/headless.ts +73 -0
- package/src/svelte/headlessFns.ts +124 -0
- package/src/svelte/renderers/Array.svelte +98 -0
- package/src/svelte/renderers/Boolean.svelte +43 -0
- package/src/svelte/renderers/Conditional.svelte +67 -0
- package/src/svelte/renderers/DiscriminatedUnion.svelte +197 -0
- package/src/svelte/renderers/Enum.svelte +53 -0
- package/src/svelte/renderers/Fallback.svelte +24 -0
- package/src/svelte/renderers/File.svelte +46 -0
- package/src/svelte/renderers/Literal.svelte +29 -0
- package/src/svelte/renderers/Mount.svelte +24 -0
- package/src/svelte/renderers/Negation.svelte +35 -0
- package/src/svelte/renderers/Never.svelte +24 -0
- package/src/svelte/renderers/Null.svelte +19 -0
- package/src/svelte/renderers/Number.svelte +68 -0
- package/src/svelte/renderers/Object.svelte +74 -0
- package/src/svelte/renderers/Record.svelte +134 -0
- package/src/svelte/renderers/RecursionSentinel.svelte +27 -0
- package/src/svelte/renderers/String.svelte +152 -0
- package/src/svelte/renderers/Tuple.svelte +84 -0
- package/src/svelte/renderers/Union.svelte +49 -0
- package/src/svelte/renderers/Unknown.svelte +42 -0
- package/src/svelte/svelte-modules.d.ts +25 -0
- package/src/svelte/types.ts +238 -0
- package/src/svelte/widget.ts +62 -0
- /package/dist/{diagnostics-BTrm3O6J.d.mts → diagnostics-mftUZI7c.d.mts} +0 -0
- /package/dist/{limits-x4OiyJxh.d.mts → limits-Vv9hUbI_.d.mts} +0 -0
- /package/dist/{types-BrYbjC7_.d.mts → types-BBQaEPfE.d.mts} +0 -0
- /package/dist/{version-DL8U5RuA.d.mts → version-BEBx10ND.d.mts} +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
|
|
2
|
+
import "../core/limits.mjs";
|
|
3
|
+
import { SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
|
|
4
|
+
import { isCodecSchema, normaliseSchema } from "../core/adapter.mjs";
|
|
5
|
+
import { walk } from "../core/walker.mjs";
|
|
6
|
+
import { UserResolverContext, WidgetsContext } from "./contexts.mjs";
|
|
7
|
+
import { headlessSolidResolver } from "./headless.mjs";
|
|
8
|
+
import { lookupGlobalSolidWidget } from "./widget.mjs";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { createMemo, createUniqueId, splitProps, useContext } from "solid-js";
|
|
11
|
+
import { jsx, jsxs } from "solid-js/jsx-runtime";
|
|
12
|
+
//#region src/solid/SchemaComponent.tsx
|
|
13
|
+
/** @jsxImportSource solid-js */
|
|
14
|
+
/**
|
|
15
|
+
* `<SchemaComponent>` — Solid renderer for Zod, JSON Schema, or OpenAPI
|
|
16
|
+
* inputs.
|
|
17
|
+
*
|
|
18
|
+
* Auto-detects the input format, normalises it to canonical JSON Schema
|
|
19
|
+
* via the shared adapter, walks the JSON Schema tree, and delegates
|
|
20
|
+
* per-field rendering to the {@link SolidComponentResolver} supplied via
|
|
21
|
+
* {@link SchemaProvider} — falling back to the headless renderer when
|
|
22
|
+
* no provider sits above this component.
|
|
23
|
+
*
|
|
24
|
+
* Key differences from the React adapter:
|
|
25
|
+
*
|
|
26
|
+
* - State is held in plain Solid signals (`createSignal`) rather than
|
|
27
|
+
* React's `useState`; the renderer does not keep its own copy of
|
|
28
|
+
* `value` — controlled mode is the only supported pattern, matching
|
|
29
|
+
* the React surface.
|
|
30
|
+
* - There are no per-render hooks. `useId` is replaced by
|
|
31
|
+
* `createUniqueId()`; `useMemo` / `useCallback` are unnecessary
|
|
32
|
+
* because Solid's fine-grained reactivity already avoids the
|
|
33
|
+
* re-allocation costs they exist to mitigate in React.
|
|
34
|
+
* - `splitProps` separates the renderer-internal props from those
|
|
35
|
+
* forwarded to the host primitive.
|
|
36
|
+
* - Context propagation uses Solid's `createContext` /
|
|
37
|
+
* `<Context.Provider>` (re-exported via `solid/contexts.ts`) — the
|
|
38
|
+
* public API matches the React `<SchemaProvider>` shape.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Provide a theme resolver and scoped widgets to every
|
|
42
|
+
* `<SchemaComponent>` rendered inside the subtree.
|
|
43
|
+
*
|
|
44
|
+
* Wrap an application (or a region of it) with `<SchemaProvider>` so a
|
|
45
|
+
* single resolver — typically a custom Solid `SolidComponentResolver`
|
|
46
|
+
* — drives every schema render. Without a provider the headless
|
|
47
|
+
* resolver is used.
|
|
48
|
+
*
|
|
49
|
+
* @group Components
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* import { SchemaProvider, SchemaComponent } from "schema-components/solid/SchemaComponent";
|
|
53
|
+
*
|
|
54
|
+
* <SchemaProvider resolver={customResolver}>
|
|
55
|
+
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
56
|
+
* </SchemaProvider>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
function SchemaProvider(props) {
|
|
60
|
+
return /* @__PURE__ */ jsx(UserResolverContext.Provider, {
|
|
61
|
+
value: props.resolver,
|
|
62
|
+
children: /* @__PURE__ */ jsx(WidgetsContext.Provider, {
|
|
63
|
+
value: props.widgets,
|
|
64
|
+
children: props.children
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Append a child path suffix to a parent path. When the suffix is
|
|
70
|
+
* omitted (e.g. transparent wrappers like union options) the parent
|
|
71
|
+
* path is returned unchanged so the child inherits the parent's id.
|
|
72
|
+
*
|
|
73
|
+
* Bracketed array indices like `"[0]"` append directly so `tags` +
|
|
74
|
+
* `"[0]"` becomes `tags[0]` rather than `tags.[0]` — matching the
|
|
75
|
+
* canonical form used by every shared id helper in `core/idPath.ts`.
|
|
76
|
+
*/
|
|
77
|
+
function joinPath(parent, suffix) {
|
|
78
|
+
if (suffix === void 0 || suffix.length === 0) return parent;
|
|
79
|
+
if (parent.length === 0) return suffix;
|
|
80
|
+
if (suffix.startsWith("[")) return `${parent}${suffix}`;
|
|
81
|
+
return `${parent}.${suffix}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Normalise a `createUniqueId()` value into a DOM-id-safe prefix.
|
|
85
|
+
* Solid's `createUniqueId()` already returns a usable id, but consumers
|
|
86
|
+
* may also pass a label-derived string. Replace any run of
|
|
87
|
+
* non-alphanumeric characters with a single hyphen and trim leading
|
|
88
|
+
* and trailing hyphens.
|
|
89
|
+
*/
|
|
90
|
+
function sanitisePrefix(value) {
|
|
91
|
+
const sanitised = value.replace(/[^a-zA-Z0-9_]+/g, "-").replace(/^-+|-+$/g, "");
|
|
92
|
+
if (sanitised.length === 0) throw new Error(`Cannot derive a DOM-safe id prefix from "${value}". Pass an explicit idPrefix prop.`);
|
|
93
|
+
return sanitised;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Render a single walked field through the resolved widget / resolver /
|
|
97
|
+
* headless pipeline. Used internally by {@link SchemaComponent} and
|
|
98
|
+
* exported so other Solid-side components (e.g. theme adapters) can
|
|
99
|
+
* dispatch into the same fallback chain.
|
|
100
|
+
*/
|
|
101
|
+
function renderField(tree, value, onChange, userResolver, renderChild, path, instanceWidgets, contextWidgets, depth = 0) {
|
|
102
|
+
if (path.length === 0) throw new Error("renderField requires a non-empty path. Pass the root path (derived from `idPrefix` or `createUniqueId()`) for the root field, and use renderChild's pathSuffix to derive child paths.");
|
|
103
|
+
if (depth >= 10) return /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsxs("em", { children: [
|
|
104
|
+
"↻ ",
|
|
105
|
+
typeof tree.meta.description === "string" ? tree.meta.description : "schema",
|
|
106
|
+
" (recursive)"
|
|
107
|
+
] }) });
|
|
108
|
+
const componentHint = tree.meta.component;
|
|
109
|
+
if (typeof componentHint === "string") {
|
|
110
|
+
const widget = instanceWidgets?.get(componentHint) ?? contextWidgets?.get(componentHint) ?? lookupGlobalSolidWidget(componentHint);
|
|
111
|
+
if (widget !== void 0) {
|
|
112
|
+
const result = widget(buildSolidProps(tree, value, onChange, renderChild, path));
|
|
113
|
+
if (result !== null && result !== void 0) return result;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const resolver = userResolver !== void 0 ? mergeSolidResolvers(userResolver, headlessSolidResolver) : headlessSolidResolver;
|
|
117
|
+
const renderFn = getSolidRenderFunction(tree.type, resolver);
|
|
118
|
+
if (renderFn !== void 0) {
|
|
119
|
+
let result;
|
|
120
|
+
try {
|
|
121
|
+
result = renderFn(buildSolidProps(tree, value, onChange, renderChild, path));
|
|
122
|
+
} catch (err) {
|
|
123
|
+
throw new SchemaRenderError(err instanceof Error ? err.message : `Render function threw for type "${tree.type}"`, tree, tree.type, err);
|
|
124
|
+
}
|
|
125
|
+
if (result !== null && result !== void 0) return result;
|
|
126
|
+
}
|
|
127
|
+
if (value === void 0 || value === null) return /* @__PURE__ */ jsx("span", { children: "—" });
|
|
128
|
+
return /* @__PURE__ */ jsx("span", { children: typeof value === "string" ? value : JSON.stringify(value) });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the Solid render props for a single field. Inlines the
|
|
132
|
+
* editability resolution from `core/renderer.ts` `buildRenderProps`
|
|
133
|
+
* because the React variant of that helper types its `renderChild`
|
|
134
|
+
* parameter against React's `renderChild` signature (returns
|
|
135
|
+
* `unknown`); Solid's signature returns `JSX.Element`, so we
|
|
136
|
+
* compose the same shape locally rather than route through a function
|
|
137
|
+
* whose static type does not accept the Solid signature.
|
|
138
|
+
*
|
|
139
|
+
* Editability resolution matches `buildRenderProps`:
|
|
140
|
+
*
|
|
141
|
+
* - `readOnly` is forced to `true` if `onChange` was supplied as the
|
|
142
|
+
* no-op (i.e. the caller passed `undefined`); the dispatcher does
|
|
143
|
+
* not currently exercise the read-only path on this surface (the
|
|
144
|
+
* Solid `<SchemaView>` component handles that) but the contract is
|
|
145
|
+
* the same.
|
|
146
|
+
* - `writeOnly` is taken from `tree.editability === "input"`.
|
|
147
|
+
*/
|
|
148
|
+
function buildSolidProps(tree, value, onChange, renderChild, path) {
|
|
149
|
+
const props = {
|
|
150
|
+
value,
|
|
151
|
+
readOnly: tree.editability === "presentation",
|
|
152
|
+
writeOnly: tree.editability === "input",
|
|
153
|
+
meta: tree.meta,
|
|
154
|
+
constraints: tree.constraints,
|
|
155
|
+
path,
|
|
156
|
+
tree,
|
|
157
|
+
onChange,
|
|
158
|
+
renderChild
|
|
159
|
+
};
|
|
160
|
+
if (tree.examples !== void 0) props.examples = tree.examples;
|
|
161
|
+
return props;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Merge two `SolidComponentResolver` instances — user values win,
|
|
165
|
+
* fallback fills gaps. Local parallel to `core/renderer.ts`
|
|
166
|
+
* `mergeResolvers` (which is typed against React's resolver shape).
|
|
167
|
+
*/
|
|
168
|
+
function mergeSolidResolvers(user, fallback) {
|
|
169
|
+
const merged = {};
|
|
170
|
+
for (const key of [
|
|
171
|
+
"string",
|
|
172
|
+
"number",
|
|
173
|
+
"boolean",
|
|
174
|
+
"null",
|
|
175
|
+
"enum",
|
|
176
|
+
"object",
|
|
177
|
+
"array",
|
|
178
|
+
"tuple",
|
|
179
|
+
"record",
|
|
180
|
+
"union",
|
|
181
|
+
"discriminatedUnion",
|
|
182
|
+
"conditional",
|
|
183
|
+
"negation",
|
|
184
|
+
"literal",
|
|
185
|
+
"file",
|
|
186
|
+
"never",
|
|
187
|
+
"unknown"
|
|
188
|
+
]) {
|
|
189
|
+
const fn = user[key] ?? fallback[key];
|
|
190
|
+
if (fn !== void 0) merged[key] = fn;
|
|
191
|
+
}
|
|
192
|
+
return merged;
|
|
193
|
+
}
|
|
194
|
+
/** Look up the render function for a schema type in a Solid resolver. */
|
|
195
|
+
function getSolidRenderFunction(type, resolver) {
|
|
196
|
+
return resolver[type];
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Render an editable (or read-only) UI from a Zod schema, JSON Schema,
|
|
200
|
+
* or OpenAPI document.
|
|
201
|
+
*
|
|
202
|
+
* Auto-detects the input format, normalises to JSON Schema via the
|
|
203
|
+
* shared adapter, walks the JSON Schema tree, and delegates per-field
|
|
204
|
+
* rendering to the {@link SolidComponentResolver} supplied via
|
|
205
|
+
* {@link SchemaProvider} — falling back to the headless renderer when
|
|
206
|
+
* no provider is present.
|
|
207
|
+
*
|
|
208
|
+
* Pass `readOnly` to render a presentational view instead of inputs.
|
|
209
|
+
*
|
|
210
|
+
* @group Components
|
|
211
|
+
* @example
|
|
212
|
+
* ```tsx
|
|
213
|
+
* import { z } from "zod";
|
|
214
|
+
* import { SchemaComponent } from "schema-components/solid/SchemaComponent";
|
|
215
|
+
*
|
|
216
|
+
* const userSchema = z.object({ name: z.string(), email: z.email() });
|
|
217
|
+
*
|
|
218
|
+
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
function SchemaComponent(props) {
|
|
222
|
+
const [, rest] = splitProps(props, ["schema"]);
|
|
223
|
+
const schemaInput = props.schema;
|
|
224
|
+
const generatedId = createUniqueId();
|
|
225
|
+
const userResolver = readUserResolver();
|
|
226
|
+
const contextWidgets = readWidgets();
|
|
227
|
+
const computed = createMemo(() => {
|
|
228
|
+
const componentMeta = rest.meta;
|
|
229
|
+
const readOnly = rest.readOnly;
|
|
230
|
+
const writeOnly = rest.writeOnly;
|
|
231
|
+
const description = rest.description;
|
|
232
|
+
const onDiagnostic = rest.onDiagnostic;
|
|
233
|
+
const strict = rest.strict;
|
|
234
|
+
const io = rest.io;
|
|
235
|
+
const refInput = rest.ref;
|
|
236
|
+
const onError = rest.onError;
|
|
237
|
+
const idPrefix = rest.idPrefix;
|
|
238
|
+
const mergedMeta = { ...componentMeta };
|
|
239
|
+
if (readOnly === true) mergedMeta.readOnly = true;
|
|
240
|
+
if (writeOnly === true) mergedMeta.writeOnly = true;
|
|
241
|
+
if (description !== void 0) mergedMeta.description = description;
|
|
242
|
+
const diagnostics = onDiagnostic !== void 0 || strict === true ? {
|
|
243
|
+
...onDiagnostic !== void 0 ? { diagnostics: onDiagnostic } : {},
|
|
244
|
+
...strict !== void 0 ? { strict } : {}
|
|
245
|
+
} : void 0;
|
|
246
|
+
let jsonSchema;
|
|
247
|
+
let zodSchema;
|
|
248
|
+
let rootMeta;
|
|
249
|
+
let rootDocument;
|
|
250
|
+
let normaliseError;
|
|
251
|
+
try {
|
|
252
|
+
const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 || io !== void 0 ? {
|
|
253
|
+
...diagnostics !== void 0 ? { diagnostics } : {},
|
|
254
|
+
...io !== void 0 ? { io } : {}
|
|
255
|
+
} : void 0);
|
|
256
|
+
jsonSchema = normalised.jsonSchema;
|
|
257
|
+
zodSchema = normalised.zodSchema;
|
|
258
|
+
rootMeta = normalised.rootMeta;
|
|
259
|
+
rootDocument = normalised.rootDocument;
|
|
260
|
+
} catch (err) {
|
|
261
|
+
normaliseError = err instanceof SchemaNormalisationError ? err : new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", schemaInput, "unknown");
|
|
262
|
+
}
|
|
263
|
+
const rootPath = idPrefix ?? sanitisePrefix(generatedId);
|
|
264
|
+
return {
|
|
265
|
+
jsonSchema,
|
|
266
|
+
zodSchema,
|
|
267
|
+
rootMeta,
|
|
268
|
+
rootDocument,
|
|
269
|
+
mergedMeta,
|
|
270
|
+
diagnostics,
|
|
271
|
+
normaliseError,
|
|
272
|
+
rootPath,
|
|
273
|
+
onError,
|
|
274
|
+
io
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
const renderTree = () => {
|
|
278
|
+
const { jsonSchema, zodSchema, rootMeta, rootDocument, mergedMeta, diagnostics, normaliseError, rootPath, onError, io } = computed();
|
|
279
|
+
if (normaliseError !== void 0) {
|
|
280
|
+
if (onError !== void 0) {
|
|
281
|
+
onError(normaliseError);
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
throw normaliseError;
|
|
285
|
+
}
|
|
286
|
+
if (jsonSchema === void 0 || rootDocument === void 0) return null;
|
|
287
|
+
const fieldsRecord = toRecordOrUndefined(rest.fields);
|
|
288
|
+
const tree = walk(jsonSchema, {
|
|
289
|
+
componentMeta: mergedMeta,
|
|
290
|
+
rootMeta,
|
|
291
|
+
fieldOverrides: fieldsRecord,
|
|
292
|
+
rootDocument,
|
|
293
|
+
...diagnostics !== void 0 ? { diagnostics } : {}
|
|
294
|
+
});
|
|
295
|
+
const handleChange = (nextValue) => {
|
|
296
|
+
if (rest.validate === true) {
|
|
297
|
+
const error = runValidation(zodSchema, jsonSchema, nextValue, io, rest.onDiagnostic);
|
|
298
|
+
if (error !== void 0) {
|
|
299
|
+
rest.onValidationError?.(error);
|
|
300
|
+
dispatchFieldErrors(fieldsRecord, error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (rest.onChange !== void 0) rest.onChange(nextValue);
|
|
304
|
+
};
|
|
305
|
+
const makeRenderChild = (currentDepth, parentPath) => (childTree, childValue, childOnChange, pathSuffix) => {
|
|
306
|
+
const childPath = joinPath(parentPath, pathSuffix);
|
|
307
|
+
return renderField(childTree, childValue, childOnChange, userResolver, makeRenderChild(currentDepth + 1, childPath), childPath, rest.widgets, contextWidgets, currentDepth + 1);
|
|
308
|
+
};
|
|
309
|
+
const renderChild = makeRenderChild(0, rootPath);
|
|
310
|
+
return renderField(tree, rest.value ?? tree.defaultValue, handleChange, userResolver, renderChild, rootPath, rest.widgets, contextWidgets, 0);
|
|
311
|
+
};
|
|
312
|
+
return renderTree();
|
|
313
|
+
}
|
|
314
|
+
function readUserResolver() {
|
|
315
|
+
return useContext(UserResolverContext);
|
|
316
|
+
}
|
|
317
|
+
function readWidgets() {
|
|
318
|
+
return useContext(WidgetsContext);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Run validation against the supplied value.
|
|
322
|
+
*
|
|
323
|
+
* Mirrors the React adapter's `runValidation`. Returns the validation
|
|
324
|
+
* error on failure or `undefined` when the value is valid. Throws
|
|
325
|
+
* `SchemaNormalisationError` (kind `zod-conversion-failed`) when the
|
|
326
|
+
* `z.fromJSONSchema` fallback path is taken AND no diagnostic sink
|
|
327
|
+
* is wired up — mirroring the React adapter's no-silent-fallback
|
|
328
|
+
* contract.
|
|
329
|
+
*/
|
|
330
|
+
function runValidation(zodSchema, jsonSchema, value, io, onDiagnostic) {
|
|
331
|
+
if (zodSchema !== void 0 && isObject(zodSchema)) {
|
|
332
|
+
const resolvedIo = io ?? "output";
|
|
333
|
+
const validateFn = isCodecSchema(zodSchema) && resolvedIo === "output" ? zodSchema.safeEncode : zodSchema.safeParse;
|
|
334
|
+
if (isCallable(validateFn)) {
|
|
335
|
+
const result = validateFn(value);
|
|
336
|
+
if (isObject(result) && "success" in result && result.success !== true) return result.error;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
let parsed;
|
|
341
|
+
try {
|
|
342
|
+
parsed = z.fromJSONSchema(jsonSchema);
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (onDiagnostic !== void 0) {
|
|
345
|
+
onDiagnostic({
|
|
346
|
+
code: "unsupported-type",
|
|
347
|
+
message: `Skipping fallback validation: z.fromJSONSchema could not round-trip the normalised JSON Schema. Original message: ${err instanceof Error ? err.message : "z.fromJSONSchema threw a non-Error value"}`,
|
|
348
|
+
pointer: "",
|
|
349
|
+
detail: { source: "z.fromJSONSchema" }
|
|
350
|
+
});
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
throw new SchemaNormalisationError(`Fallback validation failed: z.fromJSONSchema could not round-trip the normalised JSON Schema. Original message: ${err instanceof Error ? err.message : "z.fromJSONSchema threw a non-Error value"}`, jsonSchema, "zod-conversion-failed", void 0, err);
|
|
354
|
+
}
|
|
355
|
+
if (isObject(parsed)) {
|
|
356
|
+
const safeParseFn = parsed.safeParse;
|
|
357
|
+
if (isCallable(safeParseFn)) {
|
|
358
|
+
const result = safeParseFn(value);
|
|
359
|
+
if (isObject(result) && "success" in result && result.success !== true) return result.error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function isCallable(value) {
|
|
364
|
+
return typeof value === "function";
|
|
365
|
+
}
|
|
366
|
+
function dispatchFieldErrors(fields, error) {
|
|
367
|
+
if (fields === void 0 || !isObject(error)) return;
|
|
368
|
+
if (!("issues" in error)) return;
|
|
369
|
+
const issues = error.issues;
|
|
370
|
+
if (!Array.isArray(issues)) return;
|
|
371
|
+
for (const [key, override] of Object.entries(fields)) {
|
|
372
|
+
if (override === void 0 || typeof override !== "object") continue;
|
|
373
|
+
if (override === null) continue;
|
|
374
|
+
if (!("onValidationError" in override)) continue;
|
|
375
|
+
const fieldCallback = override.onValidationError;
|
|
376
|
+
if (typeof fieldCallback !== "function") continue;
|
|
377
|
+
const fieldErrors = issues.filter((issue) => {
|
|
378
|
+
if (!isObject(issue)) return false;
|
|
379
|
+
if (!("path" in issue)) return false;
|
|
380
|
+
const path = issue.path;
|
|
381
|
+
if (!Array.isArray(path)) return false;
|
|
382
|
+
return path[0] === key;
|
|
383
|
+
});
|
|
384
|
+
if (fieldErrors.length > 0 && isFieldErrorCallback(fieldCallback)) fieldCallback({ issues: fieldErrors });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function isFieldErrorCallback(value) {
|
|
388
|
+
return typeof value === "function";
|
|
389
|
+
}
|
|
390
|
+
//#endregion
|
|
391
|
+
export { SchemaComponent, SchemaProvider, joinPath, renderField, sanitisePrefix };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JSX } from "solid-js";
|
|
2
|
+
|
|
3
|
+
//#region src/solid/SchemaErrorBoundary.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Props accepted by {@link SchemaErrorBoundary}.
|
|
6
|
+
*
|
|
7
|
+
* @group Components
|
|
8
|
+
*/
|
|
9
|
+
interface SchemaErrorBoundaryProps {
|
|
10
|
+
/**
|
|
11
|
+
* Called with the caught error and a `reset` callback that clears
|
|
12
|
+
* the error state so children can re-mount. Mirrors the React
|
|
13
|
+
* adapter's signature.
|
|
14
|
+
*/
|
|
15
|
+
fallback: (error: Error, reset: () => void) => JSX.Element;
|
|
16
|
+
children: JSX.Element;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Catch rendering errors from `<SchemaComponent>`, theme adapters, and
|
|
20
|
+
* any descendant. Wraps Solid's built-in `<ErrorBoundary>` so the API
|
|
21
|
+
* shape matches `react/SchemaErrorBoundary.tsx`.
|
|
22
|
+
*
|
|
23
|
+
* Solid's `ErrorBoundary` accepts `unknown` errors. The wrapper
|
|
24
|
+
* coerces non-`Error` throws into a generic `Error` with the original
|
|
25
|
+
* value's stringified form so consumers always receive an `Error`
|
|
26
|
+
* instance.
|
|
27
|
+
*
|
|
28
|
+
* @group Components
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <SchemaErrorBoundary fallback={(error) => <p>{error.message}</p>}>
|
|
32
|
+
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
33
|
+
* </SchemaErrorBoundary>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function SchemaErrorBoundary(props: SchemaErrorBoundaryProps): JSX.Element;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { SchemaErrorBoundary, SchemaErrorBoundaryProps };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { SchemaError } from "../core/errors.mjs";
|
|
2
|
+
import { ErrorBoundary } from "solid-js";
|
|
3
|
+
import { jsx } from "solid-js/jsx-runtime";
|
|
4
|
+
//#region src/solid/SchemaErrorBoundary.tsx
|
|
5
|
+
/** @jsxImportSource solid-js */
|
|
6
|
+
/**
|
|
7
|
+
* Solid error boundary for schema-components.
|
|
8
|
+
*
|
|
9
|
+
* Wraps children in Solid's built-in `<ErrorBoundary>` and forwards the
|
|
10
|
+
* caught error to a consumer-supplied `fallback` callback. Catches
|
|
11
|
+
* render errors from `<SchemaComponent>`, theme adapters, and any
|
|
12
|
+
* descendant — without this boundary, a throwing render function
|
|
13
|
+
* propagates to the nearest enclosing root and tears down the entire
|
|
14
|
+
* subtree.
|
|
15
|
+
*
|
|
16
|
+
* `SchemaError` instances (the structured failures emitted by
|
|
17
|
+
* `<SchemaComponent>`'s normalisation and validation pipeline) are
|
|
18
|
+
* routed without console noise — the consumer's `onError` prop on the
|
|
19
|
+
* upstream `<SchemaComponent>` already handled them. Other errors are
|
|
20
|
+
* logged once to `console.error` for unhandled-failure visibility,
|
|
21
|
+
* matching the React adapter's behaviour.
|
|
22
|
+
*
|
|
23
|
+
* Does not catch:
|
|
24
|
+
* - Errors thrown from event handlers (onChange, etc.).
|
|
25
|
+
* - Asynchronous errors.
|
|
26
|
+
* - Errors in server-side rendering (Solid Start has its own model).
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Catch rendering errors from `<SchemaComponent>`, theme adapters, and
|
|
30
|
+
* any descendant. Wraps Solid's built-in `<ErrorBoundary>` so the API
|
|
31
|
+
* shape matches `react/SchemaErrorBoundary.tsx`.
|
|
32
|
+
*
|
|
33
|
+
* Solid's `ErrorBoundary` accepts `unknown` errors. The wrapper
|
|
34
|
+
* coerces non-`Error` throws into a generic `Error` with the original
|
|
35
|
+
* value's stringified form so consumers always receive an `Error`
|
|
36
|
+
* instance.
|
|
37
|
+
*
|
|
38
|
+
* @group Components
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <SchemaErrorBoundary fallback={(error) => <p>{error.message}</p>}>
|
|
42
|
+
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
43
|
+
* </SchemaErrorBoundary>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function SchemaErrorBoundary(props) {
|
|
47
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
48
|
+
fallback: (err, reset) => {
|
|
49
|
+
const error = err instanceof Error ? err : new Error(typeof err === "string" ? err : "Unknown render error");
|
|
50
|
+
if (!(error instanceof SchemaError)) console.error("[schema-components] Unhandled render error:", error);
|
|
51
|
+
return props.fallback(error, reset);
|
|
52
|
+
},
|
|
53
|
+
children: props.children
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { SchemaErrorBoundary };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { w as SchemaMeta } from "../types-BBQaEPfE.mjs";
|
|
2
|
+
import { d as PathOfType, f as RejectUnrepresentableZod, r as FromJSONSchema } from "../typeInference-Y8tNEQJk.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { JSX } from "solid-js";
|
|
5
|
+
|
|
6
|
+
//#region src/solid/SchemaField.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Infer the schema's output type for SchemaField path inference.
|
|
9
|
+
*/
|
|
10
|
+
type InferSchemaType<T> = T extends z.ZodType ? z.infer<T> : T extends object ? unknown extends FromJSONSchema<T> ? unknown : FromJSONSchema<T> : unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Props accepted by {@link SchemaField}. The generic `P` constrains
|
|
13
|
+
* `path` to dot-paths reachable through the schema's inferred value
|
|
14
|
+
* type — typed schemas get autocomplete; runtime schemas fall back to
|
|
15
|
+
* `string`.
|
|
16
|
+
*
|
|
17
|
+
* @group Components
|
|
18
|
+
*/
|
|
19
|
+
interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)> {
|
|
20
|
+
path: P;
|
|
21
|
+
schema: RejectUnrepresentableZod<T>;
|
|
22
|
+
ref?: Ref;
|
|
23
|
+
value?: unknown;
|
|
24
|
+
onChange?: (value: unknown) => void;
|
|
25
|
+
meta?: SchemaMeta;
|
|
26
|
+
validate?: boolean;
|
|
27
|
+
onValidationError?: (error: unknown) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Render a single field from a schema by dot-separated `path`.
|
|
31
|
+
*
|
|
32
|
+
* Walks the full schema tree, resolves the field at the supplied
|
|
33
|
+
* `path`, then renders only that field through the same resolver
|
|
34
|
+
* pipeline as `<SchemaComponent>`.
|
|
35
|
+
*
|
|
36
|
+
* @group Components
|
|
37
|
+
*/
|
|
38
|
+
declare function SchemaField<T = unknown, Ref extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)>(props: SchemaFieldProps<T, Ref, P>): JSX.Element;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { SchemaField, SchemaFieldProps };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { isObject } from "../core/guards.mjs";
|
|
2
|
+
import { SchemaFieldError, SchemaNormalisationError } from "../core/errors.mjs";
|
|
3
|
+
import { isCodecSchema, normaliseSchema } from "../core/adapter.mjs";
|
|
4
|
+
import { resolvePath, resolveValue, setNestedValue } from "../core/fieldPath.mjs";
|
|
5
|
+
import { walk } from "../core/walker.mjs";
|
|
6
|
+
import { UserResolverContext, WidgetsContext } from "./contexts.mjs";
|
|
7
|
+
import { joinPath, renderField, sanitisePrefix } from "./SchemaComponent.mjs";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { createUniqueId, useContext } from "solid-js";
|
|
10
|
+
//#region src/solid/SchemaField.tsx
|
|
11
|
+
/** @jsxImportSource solid-js */
|
|
12
|
+
/**
|
|
13
|
+
* `<SchemaField>` — render a single field from a Solid schema by
|
|
14
|
+
* dot-separated `path`.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors the React adapter's `<SchemaField>`. Walks the full schema
|
|
17
|
+
* tree, resolves the field at the supplied `path`, then renders only
|
|
18
|
+
* that field through the same resolver pipeline as `<SchemaComponent>`.
|
|
19
|
+
*
|
|
20
|
+
* Reads context bindings (`UserResolverContext`, `WidgetsContext`)
|
|
21
|
+
* exposed by `<SchemaProvider>`, so a single provider drives every
|
|
22
|
+
* `<SchemaField>` inside its subtree just as it does for
|
|
23
|
+
* `<SchemaComponent>`.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Render a single field from a schema by dot-separated `path`.
|
|
27
|
+
*
|
|
28
|
+
* Walks the full schema tree, resolves the field at the supplied
|
|
29
|
+
* `path`, then renders only that field through the same resolver
|
|
30
|
+
* pipeline as `<SchemaComponent>`.
|
|
31
|
+
*
|
|
32
|
+
* @group Components
|
|
33
|
+
*/
|
|
34
|
+
function SchemaField(props) {
|
|
35
|
+
const generatedId = createUniqueId();
|
|
36
|
+
const userResolver = useContext(UserResolverContext);
|
|
37
|
+
const contextWidgets = useContext(WidgetsContext);
|
|
38
|
+
let jsonSchema;
|
|
39
|
+
let zodSchema;
|
|
40
|
+
let rootMeta;
|
|
41
|
+
let rootDocument;
|
|
42
|
+
try {
|
|
43
|
+
const normalised = normaliseSchema(props.schema, props.ref);
|
|
44
|
+
jsonSchema = normalised.jsonSchema;
|
|
45
|
+
zodSchema = normalised.zodSchema;
|
|
46
|
+
rootMeta = normalised.rootMeta;
|
|
47
|
+
rootDocument = normalised.rootDocument;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err instanceof SchemaNormalisationError) throw err;
|
|
50
|
+
throw new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", props.schema, "unknown");
|
|
51
|
+
}
|
|
52
|
+
const walkOptions = {
|
|
53
|
+
componentMeta: props.meta,
|
|
54
|
+
rootMeta,
|
|
55
|
+
rootDocument
|
|
56
|
+
};
|
|
57
|
+
const fieldTree = resolvePath(walk(jsonSchema, walkOptions), props.path);
|
|
58
|
+
if (fieldTree === void 0) throw new SchemaFieldError(`Field not found: ${props.path}`, props.schema, props.path);
|
|
59
|
+
const fieldValue = resolveValue(props.value, props.path);
|
|
60
|
+
const handleChange = (nextFieldValue) => {
|
|
61
|
+
const newRootValue = setNestedValue(props.value, props.path, nextFieldValue);
|
|
62
|
+
if (props.validate === true) {
|
|
63
|
+
const error = runFieldValidation(zodSchema, jsonSchema, newRootValue);
|
|
64
|
+
if (error !== void 0) props.onValidationError?.(error);
|
|
65
|
+
}
|
|
66
|
+
props.onChange?.(newRootValue);
|
|
67
|
+
};
|
|
68
|
+
const rootPath = joinPath(sanitisePrefix(generatedId), props.path);
|
|
69
|
+
const makeRenderChild = (currentDepth, parentPath) => (childTree, childValue, childOnChange, pathSuffix) => {
|
|
70
|
+
const childPath = joinPath(parentPath, pathSuffix);
|
|
71
|
+
return renderField(childTree, childValue, childOnChange, userResolver, makeRenderChild(currentDepth + 1, childPath), childPath, void 0, contextWidgets, currentDepth + 1);
|
|
72
|
+
};
|
|
73
|
+
return renderField(fieldTree, fieldValue, handleChange, userResolver, makeRenderChild(0, rootPath), rootPath, void 0, contextWidgets, 0);
|
|
74
|
+
}
|
|
75
|
+
function isCallable(value) {
|
|
76
|
+
return typeof value === "function";
|
|
77
|
+
}
|
|
78
|
+
function runFieldValidation(zodSchema, jsonSchema, value, io, onDiagnostic) {
|
|
79
|
+
if (zodSchema !== void 0 && isObject(zodSchema)) {
|
|
80
|
+
const resolvedIo = io ?? "output";
|
|
81
|
+
const validateFn = isCodecSchema(zodSchema) && resolvedIo === "output" ? zodSchema.safeEncode : zodSchema.safeParse;
|
|
82
|
+
if (isCallable(validateFn)) {
|
|
83
|
+
const result = validateFn(value);
|
|
84
|
+
if (isObject(result) && "success" in result && result.success !== true) return result.error;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let parsed;
|
|
89
|
+
try {
|
|
90
|
+
parsed = z.fromJSONSchema(jsonSchema);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
const message = err instanceof Error ? err.message : "z.fromJSONSchema threw a non-Error value";
|
|
93
|
+
if (onDiagnostic !== void 0) {
|
|
94
|
+
onDiagnostic({
|
|
95
|
+
code: "unsupported-type",
|
|
96
|
+
message: `Skipping fallback validation: z.fromJSONSchema could not round-trip the normalised JSON Schema. Original message: ${message}`,
|
|
97
|
+
pointer: "",
|
|
98
|
+
detail: { source: "z.fromJSONSchema" }
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
throw new SchemaNormalisationError(`Fallback validation failed: z.fromJSONSchema could not round-trip the normalised JSON Schema. Original message: ${message}`, jsonSchema, "zod-conversion-failed", void 0, err);
|
|
103
|
+
}
|
|
104
|
+
if (isObject(parsed)) {
|
|
105
|
+
const safeParseFn = parsed.safeParse;
|
|
106
|
+
if (isCallable(safeParseFn)) {
|
|
107
|
+
const result = safeParseFn(value);
|
|
108
|
+
if (isObject(result) && "success" in result && result.success !== true) return result.error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { SchemaField };
|