schema-components 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -6
- package/dist/{SchemaComponent-B__6-5-E.d.mts → SchemaComponent-CRgCVDhz.d.mts} +27 -15
- package/dist/{SchemaComponent-BxzzsHsK.mjs → SchemaComponent-Cga5oJfP.mjs} +3 -3
- package/dist/core/renderField.mjs +41 -7
- package/dist/html/streamRenderers.d.mts +12 -3
- package/dist/html/streamRenderers.mjs +56 -10
- package/dist/lit/SchemaComponent.d.mts +2 -2
- package/dist/lit/SchemaComponent.mjs +1 -1
- package/dist/lit/SchemaField.d.mts +1 -1
- package/dist/lit/SchemaField.mjs +1 -1
- package/dist/lit/SchemaView.mjs +1 -1
- package/dist/lit/defaultResolver.mjs +1 -1
- package/dist/lit/registry.mjs +1 -1
- package/dist/preact/SchemaComponent.d.mts +1 -1
- package/dist/react/SchemaComponent.d.mts +1 -1
- package/dist/react/SchemaComponent.mjs +6 -6
- package/dist/react/SchemaView.d.mts +16 -11
- package/dist/react/SchemaView.mjs +2 -2
- package/dist/solid/SchemaComponent.d.mts +6 -6
- package/dist/solid/SchemaComponent.mjs +1 -1
- package/dist/solid/SchemaField.d.mts +3 -3
- package/dist/solid/SchemaField.mjs +1 -1
- package/dist/solid/SchemaView.d.mts +5 -5
- package/dist/solid/SchemaView.mjs +1 -1
- package/package.json +5 -3
- package/src/svelte/SchemaComponent.svelte +3 -3
- package/src/svelte/SchemaField.svelte +3 -3
- package/src/svelte/SchemaView.svelte +3 -3
- package/src/vue/SchemaComponent.vue +274 -0
- package/src/vue/SchemaErrorBoundary.vue +60 -0
- package/src/vue/SchemaField.vue +178 -0
- package/src/vue/SchemaProvider.vue +39 -0
- package/src/vue/SchemaView.vue +198 -0
- package/src/vue/VNodeHost.ts +32 -0
- package/src/vue/contexts.ts +116 -0
- package/src/vue/eventTargets.ts +35 -0
- package/src/vue/headless.ts +61 -0
- package/src/vue/idPrefix.ts +79 -0
- package/src/vue/renderField.ts +182 -0
- package/src/vue/renderers.ts +1297 -0
- package/src/vue/resolver.ts +45 -0
- package/src/vue/types.ts +140 -0
- package/src/vue/vue-shim.d.ts +25 -0
- package/src/vue/widget.ts +51 -0
package/README.md
CHANGED
|
@@ -117,6 +117,41 @@ For yarn, use the `resolutions` field with the same value.
|
|
|
117
117
|
|
|
118
118
|
The test suite is parametrised with a `unit-preact` Vitest project that runs the same files under `preact/compat` aliasing. Run it via `pnpm test:preact` from the repo root, or directly with `pnpm exec vitest run --project=unit-preact` from `packages/core`. A small set of tests (the ones bound to the three known limitations above) fail intentionally; the remaining ~99% pass under both runtimes and establish the cross-runtime regression boundary.
|
|
119
119
|
|
|
120
|
+
### Vue support
|
|
121
|
+
|
|
122
|
+
Vue 3.5+ is supported as an optional peer dependency. The Vue adapter ships as **source** under `schema-components/vue/*` — the `.vue` Single File Components are not pre-compiled into the published tarball. Consumers need a Vite or webpack toolchain with `@vitejs/plugin-vue` (or equivalent) to compile the SFCs at their own build step.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// vite.config.ts
|
|
126
|
+
import { defineConfig } from "vite";
|
|
127
|
+
import vue from "@vitejs/plugin-vue";
|
|
128
|
+
|
|
129
|
+
export default defineConfig({
|
|
130
|
+
plugins: [vue()],
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```vue
|
|
135
|
+
<script setup lang="ts">
|
|
136
|
+
import { ref } from "vue";
|
|
137
|
+
import SchemaComponent from "schema-components/vue/SchemaComponent.vue";
|
|
138
|
+
import { z } from "zod";
|
|
139
|
+
|
|
140
|
+
const userSchema = z.object({
|
|
141
|
+
name: z.string(),
|
|
142
|
+
email: z.email(),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const user = ref({ name: "Ada", email: "ada@example.com" });
|
|
146
|
+
</script>
|
|
147
|
+
|
|
148
|
+
<template>
|
|
149
|
+
<SchemaComponent :schema="userSchema" v-model="user" />
|
|
150
|
+
</template>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The `./vue/*` export subpath resolves to the source tree (`src/vue/*.ts`) and `./vue/*.vue` resolves to the `.vue` SFCs under `src/vue/`. The same pattern is used by the Svelte adapter — both rely on the consumer's bundler to handle the framework-specific compilation step.
|
|
154
|
+
|
|
120
155
|
## `SchemaComponent`
|
|
121
156
|
|
|
122
157
|
The single entry point. Accepts Zod schemas, JSON Schema objects, or OpenAPI documents:
|
|
@@ -130,8 +165,8 @@ import { SchemaComponent } from "schema-components/react/SchemaComponent";
|
|
|
130
165
|
// JSON Schema object
|
|
131
166
|
<SchemaComponent schema={{ type: "object", properties: { name: { type: "string" } } }} value={data} />
|
|
132
167
|
|
|
133
|
-
// OpenAPI document +
|
|
134
|
-
<SchemaComponent schema={openApiSpec}
|
|
168
|
+
// OpenAPI document + schemaRef
|
|
169
|
+
<SchemaComponent schema={openApiSpec} schemaRef="#/components/schemas/User" value={data} />
|
|
135
170
|
```
|
|
136
171
|
|
|
137
172
|
### Props
|
|
@@ -143,7 +178,7 @@ import { SchemaComponent } from "schema-components/react/SchemaComponent";
|
|
|
143
178
|
| `onChange` | `(value: unknown) => void` | Callback when value changes |
|
|
144
179
|
| `readOnly` | `boolean` | Force read-only presentation |
|
|
145
180
|
| `writeOnly` | `boolean` | Force write-only (blank inputs) |
|
|
146
|
-
| `
|
|
181
|
+
| `schemaRef` | `string` | JSON Pointer into OpenAPI document |
|
|
147
182
|
| `fields` | `InferFields<T>` | Type-safe per-field overrides |
|
|
148
183
|
| `widgets` | `WidgetMap` | Instance-scoped widget overrides |
|
|
149
184
|
| `validate` | `boolean` | Enable Zod validation on change |
|
|
@@ -222,7 +257,7 @@ const jsonSchema = {
|
|
|
222
257
|
}}
|
|
223
258
|
/>
|
|
224
259
|
|
|
225
|
-
// OpenAPI as const +
|
|
260
|
+
// OpenAPI as const + schemaRef — full autocomplete
|
|
226
261
|
const spec = {
|
|
227
262
|
openapi: "3.1.0",
|
|
228
263
|
components: {
|
|
@@ -241,9 +276,9 @@ const spec = {
|
|
|
241
276
|
|
|
242
277
|
<SchemaComponent
|
|
243
278
|
schema={spec}
|
|
244
|
-
|
|
279
|
+
schemaRef="#/components/schemas/User"
|
|
245
280
|
fields={{
|
|
246
|
-
id: { readOnly: true }, // ✓ inferred through
|
|
281
|
+
id: { readOnly: true }, // ✓ inferred through schemaRef
|
|
247
282
|
}}
|
|
248
283
|
/>
|
|
249
284
|
```
|
|
@@ -66,7 +66,7 @@ declare function __clearGlobalWidgets(): void;
|
|
|
66
66
|
*
|
|
67
67
|
* @group Components
|
|
68
68
|
*/
|
|
69
|
-
interface SchemaComponentProps<T = unknown,
|
|
69
|
+
interface SchemaComponentProps<T = unknown, SchemaRef extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
|
|
70
70
|
/**
|
|
71
71
|
* Zod schema, JSON Schema object, or OpenAPI document.
|
|
72
72
|
*
|
|
@@ -78,8 +78,16 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
78
78
|
* — the static rejection surfaces the same failure at compile time.
|
|
79
79
|
*/
|
|
80
80
|
schema: RejectUnrepresentableZod<T>;
|
|
81
|
-
/**
|
|
82
|
-
|
|
81
|
+
/**
|
|
82
|
+
* For OpenAPI / JSON Schema documents: a `$ref` string pointing at
|
|
83
|
+
* the sub-schema to render — e.g. `"#/components/schemas/User"` or
|
|
84
|
+
* `"/users/post"`.
|
|
85
|
+
*
|
|
86
|
+
* Named `schemaRef` (not `ref`) so the prop survives the React /
|
|
87
|
+
* `preact/compat` `createElement` boundary, which strips the
|
|
88
|
+
* reserved `ref` name from the vnode prop bag.
|
|
89
|
+
*/
|
|
90
|
+
schemaRef?: SchemaRef;
|
|
83
91
|
/**
|
|
84
92
|
* Which side of every transform / pipe / codec to render.
|
|
85
93
|
*
|
|
@@ -103,8 +111,8 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
103
111
|
io?: Mode;
|
|
104
112
|
/**
|
|
105
113
|
* Current value to render. Typed against
|
|
106
|
-
* `InferSchemaValue<T,
|
|
107
|
-
* inferred shape for the chosen `io` direction.
|
|
114
|
+
* `InferSchemaValue<T, SchemaRef, Mode>` so the prop tracks the
|
|
115
|
+
* schema's inferred shape for the chosen `io` direction.
|
|
108
116
|
*
|
|
109
117
|
* Falls back to `unknown` when the schema's value type cannot be
|
|
110
118
|
* statically inferred (runtime `Record<string, unknown>` JSON
|
|
@@ -119,7 +127,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
119
127
|
* <SchemaComponent schema={userSchema} value={user} readOnly />
|
|
120
128
|
* ```
|
|
121
129
|
*/
|
|
122
|
-
value?: InferSchemaValue<T,
|
|
130
|
+
value?: InferSchemaValue<T, SchemaRef, Mode>;
|
|
123
131
|
/**
|
|
124
132
|
* Called when the value changes (editable fields). The parameter
|
|
125
133
|
* shares the same shape as {@link SchemaComponentProps.value} so
|
|
@@ -129,7 +137,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
129
137
|
* Falls back to `unknown` for schemas whose value type cannot be
|
|
130
138
|
* statically inferred — see {@link SchemaComponentProps.value}.
|
|
131
139
|
*/
|
|
132
|
-
onChange?: (value: InferSchemaValue<T,
|
|
140
|
+
onChange?: (value: InferSchemaValue<T, SchemaRef, Mode>) => void;
|
|
133
141
|
/** Run schema.safeParse() on change and surface errors via onValidationError. */
|
|
134
142
|
validate?: boolean;
|
|
135
143
|
/** Called with the ZodError when validation fails. */
|
|
@@ -141,7 +149,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
141
149
|
/** When true, any diagnostic becomes a thrown error. */
|
|
142
150
|
strict?: boolean;
|
|
143
151
|
/** Per-field meta overrides — nested object mirroring schema shape. */
|
|
144
|
-
fields?: InferFields<T,
|
|
152
|
+
fields?: InferFields<T, SchemaRef>;
|
|
145
153
|
/** Meta overrides applied to the root schema. */
|
|
146
154
|
meta?: SchemaMeta;
|
|
147
155
|
/** Convenience: sets readOnly on all fields. */
|
|
@@ -183,7 +191,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
183
191
|
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
184
192
|
* ```
|
|
185
193
|
*/
|
|
186
|
-
declare function SchemaComponent<T = unknown,
|
|
194
|
+
declare function SchemaComponent<T = unknown, SchemaRef extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(props: SchemaComponentProps<T, SchemaRef, Mode>): ReactNode;
|
|
187
195
|
/**
|
|
188
196
|
* Append a child path suffix to a parent path. When the suffix is omitted
|
|
189
197
|
* (e.g. transparent wrappers like union options), the parent path is
|
|
@@ -229,7 +237,7 @@ type InferSchemaType<T> = T extends z.ZodType ? z.infer<T> : T extends object ?
|
|
|
229
237
|
*
|
|
230
238
|
* @group Components
|
|
231
239
|
*/
|
|
232
|
-
interface SchemaFieldProps<T = unknown,
|
|
240
|
+
interface SchemaFieldProps<T = unknown, SchemaRef extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)> {
|
|
233
241
|
/**
|
|
234
242
|
* Dot-separated path to the field (e.g. "address.city").
|
|
235
243
|
* When the schema is a Zod schema or typed `as const`, only valid
|
|
@@ -241,8 +249,12 @@ interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefin
|
|
|
241
249
|
* unrepresentable-Zod rejection as {@link SchemaComponentProps.schema}.
|
|
242
250
|
*/
|
|
243
251
|
schema: RejectUnrepresentableZod<T>;
|
|
244
|
-
/**
|
|
245
|
-
|
|
252
|
+
/**
|
|
253
|
+
* For OpenAPI / JSON Schema documents: a `$ref` string. Named
|
|
254
|
+
* `schemaRef` (not `ref`) to avoid the React / `preact/compat`
|
|
255
|
+
* reserved prop name. See {@link SchemaComponentProps.schemaRef}.
|
|
256
|
+
*/
|
|
257
|
+
schemaRef?: SchemaRef;
|
|
246
258
|
/** Current value of the field at the given path. */
|
|
247
259
|
value?: unknown;
|
|
248
260
|
/** Called with the updated root value when this field changes. */
|
|
@@ -263,15 +275,15 @@ interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefin
|
|
|
263
275
|
*
|
|
264
276
|
* @group Components
|
|
265
277
|
*/
|
|
266
|
-
declare function SchemaField<T = unknown,
|
|
278
|
+
declare function SchemaField<T = unknown, SchemaRef extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)>({
|
|
267
279
|
path,
|
|
268
280
|
schema: schemaInput,
|
|
269
|
-
|
|
281
|
+
schemaRef: schemaRefInput,
|
|
270
282
|
value,
|
|
271
283
|
onChange,
|
|
272
284
|
meta: fieldMeta,
|
|
273
285
|
validate,
|
|
274
286
|
onValidationError
|
|
275
|
-
}: SchemaFieldProps<T,
|
|
287
|
+
}: SchemaFieldProps<T, SchemaRef, P>): ReactNode;
|
|
276
288
|
//#endregion
|
|
277
289
|
export { SchemaProvider as a, registerWidget as c, SchemaFieldProps as i, renderField as l, SchemaComponentProps as n, __clearGlobalWidgets as o, SchemaField as r, joinPath as s, SchemaComponent as t, sanitisePrefix as u };
|
|
@@ -95,7 +95,7 @@ var SchemaField = class extends SchemaComponent {
|
|
|
95
95
|
let rootMeta;
|
|
96
96
|
let rootDocument;
|
|
97
97
|
try {
|
|
98
|
-
const normalised = normaliseSchema(this.schema, this.
|
|
98
|
+
const normalised = normaliseSchema(this.schema, this.schemaRef);
|
|
99
99
|
jsonSchema = normalised.jsonSchema;
|
|
100
100
|
rootMeta = normalised.rootMeta;
|
|
101
101
|
rootDocument = normalised.rootDocument;
|
|
@@ -504,7 +504,7 @@ var SchemaComponent = class extends LitElement {
|
|
|
504
504
|
widgets: { attribute: false },
|
|
505
505
|
fields: { attribute: false },
|
|
506
506
|
meta: { attribute: false },
|
|
507
|
-
|
|
507
|
+
schemaRef: { attribute: false },
|
|
508
508
|
io: { attribute: false },
|
|
509
509
|
idPrefix: { attribute: false },
|
|
510
510
|
onDiagnostic: { attribute: false },
|
|
@@ -533,7 +533,7 @@ var SchemaComponent = class extends LitElement {
|
|
|
533
533
|
...diagnosticsOptions !== void 0 ? { diagnostics: diagnosticsOptions } : {},
|
|
534
534
|
...this.io !== void 0 ? { io: this.io } : {}
|
|
535
535
|
} : void 0;
|
|
536
|
-
const normalised = normaliseSchema(this.schema, this.
|
|
536
|
+
const normalised = normaliseSchema(this.schema, this.schemaRef, normaliseOptions);
|
|
537
537
|
jsonSchema = normalised.jsonSchema;
|
|
538
538
|
rootMeta = normalised.rootMeta;
|
|
539
539
|
rootDocument = normalised.rootDocument;
|
|
@@ -6,13 +6,14 @@ import { SchemaRenderError } from "./errors.mjs";
|
|
|
6
6
|
*
|
|
7
7
|
* Centralises the dispatch loop shared by the React `SchemaComponent` /
|
|
8
8
|
* `SchemaView` renderers, the synchronous HTML renderer in
|
|
9
|
-
* `renderToHtml`,
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* `renderToHtml`, the streaming HTML renderer in `streamRenderers.ts`
|
|
10
|
+
* (for its leaf path — see "Streaming integration" below), and (in
|
|
11
|
+
* the future) Vue / Solid / Svelte / Lit adapters. The dispatcher is
|
|
12
|
+
* intentionally framework-agnostic: it neither imports React nor
|
|
13
|
+
* produces HTML strings directly. Each adapter supplies a small
|
|
14
|
+
* {@link DispatchConfig} describing how to build per-field props, how
|
|
15
|
+
* to handle a successful or absent resolver lookup, and (optionally)
|
|
16
|
+
* how to handle widget overrides and the recursion-depth cap.
|
|
16
17
|
*
|
|
17
18
|
* The dispatch order is fixed and matches the historic React-side
|
|
18
19
|
* behaviour so the React, HTML, and future adapters all observe the
|
|
@@ -32,6 +33,39 @@ import { SchemaRenderError } from "./errors.mjs";
|
|
|
32
33
|
* The helpers that find render functions, merge resolvers, and build
|
|
33
34
|
* the per-field props live in {@link "./renderer.ts"} and are reused
|
|
34
35
|
* here — `core/renderField.ts` is purely the dispatch shell.
|
|
36
|
+
*
|
|
37
|
+
* # Streaming integration (design choice B)
|
|
38
|
+
*
|
|
39
|
+
* The streaming HTML renderer (`html/streamRenderers.ts` +
|
|
40
|
+
* `html/renderToHtmlStream.ts`) consumes this dispatcher for leaf
|
|
41
|
+
* field types — `string`, `number`, `boolean`, `enum`, `literal`,
|
|
42
|
+
* `file`, `unknown` — and for variants without a dedicated streaming
|
|
43
|
+
* generator (`null`, `tuple`, `conditional`, `negation`, `never`).
|
|
44
|
+
* Container types (`object`, `array`, `record`, `union`,
|
|
45
|
+
* `discriminatedUnion`) keep bespoke generator implementations
|
|
46
|
+
* because the dispatcher's single-output contract cannot express the
|
|
47
|
+
* "yield opening tag → recurse into children → yield closing tag"
|
|
48
|
+
* chunk-boundary semantics that streaming depends on.
|
|
49
|
+
*
|
|
50
|
+
* We deliberately chose this approach (the Phase 1 agent's "option
|
|
51
|
+
* B" — leaves dispatch through the shared loop, containers keep their
|
|
52
|
+
* own iteration) over the alternative of building a generator-output
|
|
53
|
+
* mode into the dispatcher itself. Approach B preserves the existing
|
|
54
|
+
* chunk boundaries byte-for-byte while still eliminating the duplicate
|
|
55
|
+
* resolver-lookup logic that previously lived in `renderLeaf`. A
|
|
56
|
+
* generator-aware dispatcher would require either a parallel "stream
|
|
57
|
+
* resolver" shape or a unified return type wide enough to cover both
|
|
58
|
+
* single-output and iterable cases — neither of which is justified by
|
|
59
|
+
* the small amount of dispatch logic the leaf path needs.
|
|
60
|
+
*
|
|
61
|
+
* The streaming `streamField` function performs its own depth check
|
|
62
|
+
* before invoking the dispatcher for leaf paths. The check appears
|
|
63
|
+
* textually in both places (streamField and this dispatcher) but at
|
|
64
|
+
* runtime fires exactly once per recursion step: streamField's guard
|
|
65
|
+
* filters the streaming path, and the dispatcher's guard remains in
|
|
66
|
+
* place for the sync HTML and React callers that do not pre-filter
|
|
67
|
+
* depth themselves. See `html/streamRenderers.ts` for the matching
|
|
68
|
+
* commentary.
|
|
35
69
|
*/
|
|
36
70
|
/**
|
|
37
71
|
* Framework-agnostic dispatch loop shared by the React, HTML, and
|
|
@@ -19,10 +19,19 @@ declare function yieldClose(el: HtmlElement): string;
|
|
|
19
19
|
/**
|
|
20
20
|
* Render a leaf {@link WalkedField} entirely as a single HTML chunk.
|
|
21
21
|
* Used inside the streaming generators when descent into containers is
|
|
22
|
-
* complete.
|
|
23
|
-
*
|
|
22
|
+
* complete.
|
|
23
|
+
*
|
|
24
|
+
* Delegates to the framework-agnostic {@link dispatchRenderField}
|
|
25
|
+
* dispatcher so resolver lookup, the (unused) widget step, error
|
|
26
|
+
* wrapping, and the unresolved-type fallback share one implementation
|
|
27
|
+
* with the sync HTML renderer and the React adapter.
|
|
28
|
+
*
|
|
29
|
+
* @param depth - Current recursion depth — defaults to `0` so the
|
|
30
|
+
* public signature stays additive. Callers inside the streaming
|
|
31
|
+
* pipeline thread the live `currentDepth` so the dispatcher's
|
|
32
|
+
* internal depth check is consistent with the streamField gate.
|
|
24
33
|
*/
|
|
25
|
-
declare function renderLeaf(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string): string;
|
|
34
|
+
declare function renderLeaf(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string, depth?: number): string;
|
|
26
35
|
/**
|
|
27
36
|
* Drain {@link streamField} into a single string. Used when a streamed
|
|
28
37
|
* sub-tree needs to be embedded inside a non-streaming chunk (e.g. as
|
|
@@ -3,6 +3,7 @@ import "../core/limits.mjs";
|
|
|
3
3
|
import { emitDiagnostic } from "../core/diagnostics.mjs";
|
|
4
4
|
import { SC_CLASSES } from "../core/cssClasses.mjs";
|
|
5
5
|
import { panelIdFor, tabIdFor } from "../core/idPath.mjs";
|
|
6
|
+
import { dispatchRenderField } from "../core/renderField.mjs";
|
|
6
7
|
import { getHtmlRenderFn } from "../core/renderer.mjs";
|
|
7
8
|
import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
|
|
8
9
|
import { VOID_ELEMENTS, h, raw, serialize, serializeAttributes } from "./html.mjs";
|
|
@@ -29,14 +30,15 @@ function yieldClose(el) {
|
|
|
29
30
|
return `</${el.tag}>`;
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* Build the per-leaf {@link HtmlRenderProps} bundle handed to resolver
|
|
34
|
+
* render functions and (in future) to widget renderers. The streaming
|
|
35
|
+
* pipeline never recurses through `renderChild` for leaves — container
|
|
36
|
+
* recursion happens through the streamField generators — so the
|
|
37
|
+
* supplied `renderChild` is the constant `() => ""` stub matching the
|
|
38
|
+
* historic streaming-leaf shape.
|
|
36
39
|
*/
|
|
37
|
-
function
|
|
38
|
-
const
|
|
39
|
-
if (renderFn !== void 0) return renderFn({
|
|
40
|
+
function buildLeafProps(tree, value, path) {
|
|
41
|
+
const props = {
|
|
40
42
|
value,
|
|
41
43
|
readOnly: tree.editability === "presentation",
|
|
42
44
|
writeOnly: tree.editability === "input",
|
|
@@ -45,11 +47,55 @@ function renderLeaf(tree, value, mergedResolver, path) {
|
|
|
45
47
|
path,
|
|
46
48
|
tree,
|
|
47
49
|
renderChild: () => ""
|
|
48
|
-
}
|
|
50
|
+
};
|
|
51
|
+
if (tree.examples !== void 0) props.examples = tree.examples;
|
|
52
|
+
return props;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the streaming-friendly placeholder a {@link dispatchRenderField}
|
|
56
|
+
* fallback emits when no resolver handled the leaf type. The sync HTML
|
|
57
|
+
* dispatcher throws in this position — streaming must keep producing
|
|
58
|
+
* output, so the streaming adapter renders the same `<span>` shape
|
|
59
|
+
* `renderLeaf` historically produced for unresolved types.
|
|
60
|
+
*/
|
|
61
|
+
function leafFallbackHtml(value) {
|
|
49
62
|
if (value === void 0 || value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
50
63
|
return serialize(h("span", { class: SC_CLASSES.value }, typeof value === "string" ? value : JSON.stringify(value)));
|
|
51
64
|
}
|
|
52
65
|
/**
|
|
66
|
+
* Render a leaf {@link WalkedField} entirely as a single HTML chunk.
|
|
67
|
+
* Used inside the streaming generators when descent into containers is
|
|
68
|
+
* complete.
|
|
69
|
+
*
|
|
70
|
+
* Delegates to the framework-agnostic {@link dispatchRenderField}
|
|
71
|
+
* dispatcher so resolver lookup, the (unused) widget step, error
|
|
72
|
+
* wrapping, and the unresolved-type fallback share one implementation
|
|
73
|
+
* with the sync HTML renderer and the React adapter.
|
|
74
|
+
*
|
|
75
|
+
* @param depth - Current recursion depth — defaults to `0` so the
|
|
76
|
+
* public signature stays additive. Callers inside the streaming
|
|
77
|
+
* pipeline thread the live `currentDepth` so the dispatcher's
|
|
78
|
+
* internal depth check is consistent with the streamField gate.
|
|
79
|
+
*/
|
|
80
|
+
function renderLeaf(tree, value, mergedResolver, path, depth = 0) {
|
|
81
|
+
return dispatchRenderField({
|
|
82
|
+
tree,
|
|
83
|
+
value,
|
|
84
|
+
path,
|
|
85
|
+
depth,
|
|
86
|
+
resolver: mergedResolver,
|
|
87
|
+
config: {
|
|
88
|
+
buildProps: (fieldTree, fieldPath) => buildLeafProps(fieldTree, value, fieldPath),
|
|
89
|
+
lookupRenderFn: (type, htmlResolver) => getHtmlRenderFn(type, htmlResolver),
|
|
90
|
+
recursionSentinel: (fieldTree) => {
|
|
91
|
+
return recursionSentinelHtml(typeof fieldTree.meta.description === "string" ? fieldTree.meta.description : "schema");
|
|
92
|
+
},
|
|
93
|
+
fallback: (_fieldTree, fieldValue) => leafFallbackHtml(fieldValue),
|
|
94
|
+
coerceResult: (result) => typeof result === "string" ? result : void 0
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
53
99
|
* Drain {@link streamField} into a single string. Used when a streamed
|
|
54
100
|
* sub-tree needs to be embedded inside a non-streaming chunk (e.g. as
|
|
55
101
|
* children of a parent element).
|
|
@@ -87,7 +133,7 @@ function* streamField(tree, value, mergedResolver, path, rawResolver, currentDep
|
|
|
87
133
|
const effectiveValue = value ?? tree.defaultValue;
|
|
88
134
|
const type = tree.type;
|
|
89
135
|
if (type === "string" || type === "number" || type === "boolean" || type === "enum" || type === "literal" || type === "file" || type === "unknown") {
|
|
90
|
-
yield renderLeaf(tree, effectiveValue, mergedResolver, path);
|
|
136
|
+
yield renderLeaf(tree, effectiveValue, mergedResolver, path, currentDepth);
|
|
91
137
|
return;
|
|
92
138
|
}
|
|
93
139
|
if (type === "union") {
|
|
@@ -110,7 +156,7 @@ function* streamField(tree, value, mergedResolver, path, rawResolver, currentDep
|
|
|
110
156
|
yield* streamRecord(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics);
|
|
111
157
|
return;
|
|
112
158
|
}
|
|
113
|
-
yield renderLeaf(tree, value, mergedResolver, path);
|
|
159
|
+
yield renderLeaf(tree, value, mergedResolver, path, currentDepth);
|
|
114
160
|
}
|
|
115
161
|
function* streamObject(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
|
|
116
162
|
if (tree.type !== "object") return;
|
|
@@ -66,7 +66,7 @@ declare class SchemaComponent extends LitElement {
|
|
|
66
66
|
meta: {
|
|
67
67
|
attribute: boolean;
|
|
68
68
|
};
|
|
69
|
-
|
|
69
|
+
schemaRef: {
|
|
70
70
|
attribute: boolean;
|
|
71
71
|
};
|
|
72
72
|
io: {
|
|
@@ -94,7 +94,7 @@ declare class SchemaComponent extends LitElement {
|
|
|
94
94
|
* values are seeded in the constructor.
|
|
95
95
|
*/
|
|
96
96
|
schema: unknown;
|
|
97
|
-
|
|
97
|
+
schemaRef: string | undefined;
|
|
98
98
|
io: SchemaIoSide | undefined;
|
|
99
99
|
value: unknown;
|
|
100
100
|
resolver: LitComponentResolver | undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as SchemaComponent } from "../SchemaComponent-
|
|
1
|
+
import { t as SchemaComponent } from "../SchemaComponent-Cga5oJfP.mjs";
|
|
2
2
|
export { SchemaComponent };
|
package/dist/lit/SchemaField.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { o as SchemaField } from "../SchemaComponent-
|
|
1
|
+
import { o as SchemaField } from "../SchemaComponent-Cga5oJfP.mjs";
|
|
2
2
|
export { SchemaField };
|
package/dist/lit/SchemaView.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { s as SchemaView } from "../SchemaComponent-
|
|
1
|
+
import { s as SchemaView } from "../SchemaComponent-Cga5oJfP.mjs";
|
|
2
2
|
export { SchemaView };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as TYPE_TO_CANONICAL_TAG, r as createDefaultLitResolver } from "../SchemaComponent-
|
|
1
|
+
import { n as TYPE_TO_CANONICAL_TAG, r as createDefaultLitResolver } from "../SchemaComponent-Cga5oJfP.mjs";
|
|
2
2
|
export { TYPE_TO_CANONICAL_TAG, createDefaultLitResolver };
|
package/dist/lit/registry.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as registerSchemaComponents, i as BUILT_IN_ELEMENTS } from "../SchemaComponent-
|
|
1
|
+
import { a as registerSchemaComponents, i as BUILT_IN_ELEMENTS } from "../SchemaComponent-Cga5oJfP.mjs";
|
|
2
2
|
export { BUILT_IN_ELEMENTS, registerSchemaComponents };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { a as InferredValue, i as InferredOutputValue, r as InferredInputValue, t as InferFields } from "../inferValue-eAnh50EM.mjs";
|
|
2
|
-
import { a as SchemaProvider, c as registerWidget, i as SchemaFieldProps, n as SchemaComponentProps, r as SchemaField, t as SchemaComponent } from "../SchemaComponent-
|
|
2
|
+
import { a as SchemaProvider, c as registerWidget, i as SchemaFieldProps, n as SchemaComponentProps, r as SchemaField, t as SchemaComponent } from "../SchemaComponent-CRgCVDhz.mjs";
|
|
3
3
|
export { type InferFields, type InferredInputValue, type InferredOutputValue, type InferredValue, SchemaComponent, type SchemaComponentProps, SchemaField, type SchemaFieldProps, SchemaProvider, registerWidget };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { a as InferredValue, i as InferredOutputValue, r as InferredInputValue, t as InferFields } from "../inferValue-eAnh50EM.mjs";
|
|
2
|
-
import { a as SchemaProvider, c as registerWidget, i as SchemaFieldProps, l as renderField, n as SchemaComponentProps, o as __clearGlobalWidgets, r as SchemaField, s as joinPath, t as SchemaComponent, u as sanitisePrefix } from "../SchemaComponent-
|
|
2
|
+
import { a as SchemaProvider, c as registerWidget, i as SchemaFieldProps, l as renderField, n as SchemaComponentProps, o as __clearGlobalWidgets, r as SchemaField, s as joinPath, t as SchemaComponent, u as sanitisePrefix } from "../SchemaComponent-CRgCVDhz.mjs";
|
|
3
3
|
export { InferFields, InferredInputValue, InferredOutputValue, InferredValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, __clearGlobalWidgets, joinPath, registerWidget, renderField, sanitisePrefix };
|
|
@@ -21,7 +21,7 @@ import { createContext, isValidElement, useCallback, useContext, useId, useMemo
|
|
|
21
21
|
* The `fields` prop type is inferred from the `schema` prop:
|
|
22
22
|
* - Zod schemas → `FieldOverrides<z.infer<T>>` (full autocomplete)
|
|
23
23
|
* - JSON Schema `as const` → `FieldOverrides<FromJSONSchema<T>>` (full autocomplete)
|
|
24
|
-
* - OpenAPI `as const` + `
|
|
24
|
+
* - OpenAPI `as const` + `schemaRef` → `FieldOverrides<ResolveOpenAPIRef<T, SchemaRef>>`
|
|
25
25
|
* - Runtime schemas → `Record<string, FieldOverride>` (no autocomplete)
|
|
26
26
|
*/
|
|
27
27
|
const UserResolverContext = createContext(void 0);
|
|
@@ -103,7 +103,7 @@ function __clearGlobalWidgets() {
|
|
|
103
103
|
* ```
|
|
104
104
|
*/
|
|
105
105
|
function SchemaComponent(props) {
|
|
106
|
-
const { schema: schemaInput,
|
|
106
|
+
const { schema: schemaInput, schemaRef: schemaRefInput, io, value, onChange, validate, onValidationError, onError, onDiagnostic, strict, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets, idPrefix } = props;
|
|
107
107
|
const userResolver = useContext(UserResolverContext);
|
|
108
108
|
const contextWidgets = useContext(WidgetsContext);
|
|
109
109
|
const generatedId = useId();
|
|
@@ -129,7 +129,7 @@ function SchemaComponent(props) {
|
|
|
129
129
|
let rootMeta;
|
|
130
130
|
let rootDocument;
|
|
131
131
|
try {
|
|
132
|
-
const normalised = normaliseSchema(schemaInput,
|
|
132
|
+
const normalised = normaliseSchema(schemaInput, schemaRefInput, diagnostics !== void 0 || io !== void 0 ? {
|
|
133
133
|
...diagnostics !== void 0 ? { diagnostics } : {},
|
|
134
134
|
...io !== void 0 ? { io } : {}
|
|
135
135
|
} : void 0);
|
|
@@ -336,7 +336,7 @@ function renderField(tree, value, onChange, userResolver, renderChild, path, ins
|
|
|
336
336
|
*
|
|
337
337
|
* @group Components
|
|
338
338
|
*/
|
|
339
|
-
function SchemaField({ path, schema: schemaInput,
|
|
339
|
+
function SchemaField({ path, schema: schemaInput, schemaRef: schemaRefInput, value, onChange, meta: fieldMeta, validate, onValidationError }) {
|
|
340
340
|
const userResolver = useContext(UserResolverContext);
|
|
341
341
|
const contextWidgets = useContext(WidgetsContext);
|
|
342
342
|
const generatedId = useId();
|
|
@@ -345,7 +345,7 @@ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange
|
|
|
345
345
|
let rootMeta;
|
|
346
346
|
let rootDocument;
|
|
347
347
|
try {
|
|
348
|
-
const normalised = normaliseSchema(schemaInput,
|
|
348
|
+
const normalised = normaliseSchema(schemaInput, schemaRefInput);
|
|
349
349
|
jsonSchema = normalised.jsonSchema;
|
|
350
350
|
zodSchema = normalised.zodSchema;
|
|
351
351
|
rootMeta = normalised.rootMeta;
|
|
@@ -389,7 +389,7 @@ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange
|
|
|
389
389
|
* Walks the fields override tree and matches errors by path prefix.
|
|
390
390
|
*
|
|
391
391
|
* The runtime shape of `fields` is always `Record<string, FieldOverride>`
|
|
392
|
-
* after `InferFields<T,
|
|
392
|
+
* after `InferFields<T, SchemaRef>` is erased — the typed variants
|
|
393
393
|
* (`FieldOverrides<U>`) and the loose `Record<string, FieldOverride>`
|
|
394
394
|
* fallback share the same structural shape, so the dispatch logic only
|
|
395
395
|
* needs the loose record. The previous parameter union
|
|
@@ -16,7 +16,7 @@ import { ReactNode } from "react";
|
|
|
16
16
|
*
|
|
17
17
|
* @group Components
|
|
18
18
|
*/
|
|
19
|
-
interface SchemaViewProps<T = unknown,
|
|
19
|
+
interface SchemaViewProps<T = unknown, SchemaRef extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
|
|
20
20
|
/**
|
|
21
21
|
* Zod schema, JSON Schema object, or OpenAPI document.
|
|
22
22
|
*
|
|
@@ -25,8 +25,13 @@ interface SchemaViewProps<T = unknown, Ref extends string | undefined = undefine
|
|
|
25
25
|
* {@link RejectUnrepresentableZod}.
|
|
26
26
|
*/
|
|
27
27
|
schema: RejectUnrepresentableZod<T>;
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* For OpenAPI / JSON Schema documents: a `$ref` string like
|
|
30
|
+
* `"#/components/schemas/User"`. Named `schemaRef` (not `ref`) to
|
|
31
|
+
* avoid the React / `preact/compat` reserved prop name. See
|
|
32
|
+
* {@link SchemaComponentProps.schemaRef}.
|
|
33
|
+
*/
|
|
34
|
+
schemaRef?: SchemaRef;
|
|
30
35
|
/**
|
|
31
36
|
* Which side of every transform / pipe / codec to render. Mirrors
|
|
32
37
|
* `<SchemaComponent io>`. Defaults to `"output"` — the inferred
|
|
@@ -39,18 +44,18 @@ interface SchemaViewProps<T = unknown, Ref extends string | undefined = undefine
|
|
|
39
44
|
io?: Mode;
|
|
40
45
|
/**
|
|
41
46
|
* Current value to render. Typed against
|
|
42
|
-
* `InferSchemaValue<T,
|
|
43
|
-
* inferred shape for the chosen `io` direction. Falls back
|
|
44
|
-
* `unknown` for runtime schemas where the value type cannot be
|
|
47
|
+
* `InferSchemaValue<T, SchemaRef, Mode>` so the prop tracks the
|
|
48
|
+
* schema's inferred shape for the chosen `io` direction. Falls back
|
|
49
|
+
* to `unknown` for runtime schemas where the value type cannot be
|
|
45
50
|
* statically inferred.
|
|
46
51
|
*/
|
|
47
|
-
value?: InferredValue<T,
|
|
52
|
+
value?: InferredValue<T, SchemaRef, undefined, Mode>;
|
|
48
53
|
/**
|
|
49
54
|
* Per-field meta overrides — nested object mirroring schema shape.
|
|
50
55
|
* Typed against {@link InferFields} so a typed `schema` prop drives
|
|
51
56
|
* autocomplete on the override map, matching `<SchemaComponent>`.
|
|
52
57
|
*/
|
|
53
|
-
fields?: InferFields<T,
|
|
58
|
+
fields?: InferFields<T, SchemaRef>;
|
|
54
59
|
/** Meta overrides applied to the root schema. */
|
|
55
60
|
meta?: SchemaMeta;
|
|
56
61
|
/** Convenience: sets description on the root. */
|
|
@@ -96,9 +101,9 @@ interface SchemaViewProps<T = unknown, Ref extends string | undefined = undefine
|
|
|
96
101
|
* }
|
|
97
102
|
* ```
|
|
98
103
|
*/
|
|
99
|
-
declare function SchemaView<T = unknown,
|
|
104
|
+
declare function SchemaView<T = unknown, SchemaRef extends string | undefined = undefined, Mode extends SchemaIoSide = "output">({
|
|
100
105
|
schema: schemaInput,
|
|
101
|
-
|
|
106
|
+
schemaRef: schemaRefInput,
|
|
102
107
|
io,
|
|
103
108
|
value,
|
|
104
109
|
fields,
|
|
@@ -109,6 +114,6 @@ declare function SchemaView<T = unknown, Ref extends string | undefined = undefi
|
|
|
109
114
|
onDiagnostic,
|
|
110
115
|
strict,
|
|
111
116
|
idPrefix
|
|
112
|
-
}: SchemaViewProps<T,
|
|
117
|
+
}: SchemaViewProps<T, SchemaRef, Mode>): ReactNode;
|
|
113
118
|
//#endregion
|
|
114
119
|
export { SchemaView, SchemaViewProps };
|
|
@@ -58,7 +58,7 @@ import { isValidElement, useId } from "react";
|
|
|
58
58
|
* }
|
|
59
59
|
* ```
|
|
60
60
|
*/
|
|
61
|
-
function SchemaView({ schema: schemaInput,
|
|
61
|
+
function SchemaView({ schema: schemaInput, schemaRef: schemaRefInput, io, value, fields, meta: componentMeta, description, resolver, widgets, onDiagnostic, strict, idPrefix }) {
|
|
62
62
|
const generatedId = useId();
|
|
63
63
|
const rootPath = idPrefix ?? sanitisePrefix(generatedId);
|
|
64
64
|
const mergedMeta = {
|
|
@@ -74,7 +74,7 @@ function SchemaView({ schema: schemaInput, ref: refInput, io, value, fields, met
|
|
|
74
74
|
let rootMeta;
|
|
75
75
|
let rootDocument;
|
|
76
76
|
try {
|
|
77
|
-
const normalised = normaliseSchema(schemaInput,
|
|
77
|
+
const normalised = normaliseSchema(schemaInput, schemaRefInput, diagnostics !== void 0 || io !== void 0 ? {
|
|
78
78
|
...diagnostics !== void 0 ? { diagnostics } : {},
|
|
79
79
|
...io !== void 0 ? { io } : {}
|
|
80
80
|
} : void 0);
|