schema-components 1.24.0 → 1.26.1

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.
Files changed (35) hide show
  1. package/dist/adapter-MiEFkRVV.d.mts +172 -0
  2. package/dist/core/adapter.d.mts +2 -141
  3. package/dist/core/adapter.mjs +39 -5
  4. package/dist/core/errors.d.mts +1 -1
  5. package/dist/core/limits.d.mts +1 -1
  6. package/dist/core/normalise.d.mts +1 -1
  7. package/dist/core/ref.d.mts +1 -1
  8. package/dist/core/renderer.d.mts +1 -1
  9. package/dist/core/typeInference.d.mts +1 -1
  10. package/dist/core/version.d.mts +1 -1
  11. package/dist/core/walkBuilders.d.mts +1 -1
  12. package/dist/html/a11y.d.mts +1 -1
  13. package/dist/html/renderToHtml.d.mts +1 -1
  14. package/dist/html/renderToHtmlStream.d.mts +1 -1
  15. package/dist/html/renderers.d.mts +1 -1
  16. package/dist/html/streamRenderers.d.mts +1 -1
  17. package/dist/openapi/components.mjs +2 -2
  18. package/dist/react/SchemaComponent.d.mts +73 -63
  19. package/dist/react/SchemaComponent.mjs +23 -31
  20. package/dist/react/SchemaView.d.mts +23 -19
  21. package/dist/react/SchemaView.mjs +6 -3
  22. package/dist/react/headless.d.mts +1 -1
  23. package/dist/react/headlessRenderers.d.mts +1 -1
  24. package/dist/react/headlessRenderers.mjs +1 -1
  25. package/dist/themes/mantine.d.mts +1 -1
  26. package/dist/themes/mui.d.mts +1 -1
  27. package/dist/themes/mui.mjs +1 -1
  28. package/dist/themes/radix.d.mts +1 -1
  29. package/dist/themes/shadcn.d.mts +1 -1
  30. package/package.json +6 -2
  31. /package/dist/{errors-g_MCTQel.d.mts → errors-DQSIK4n1.d.mts} +0 -0
  32. /package/dist/{limits-Cw5QZND8.d.mts → limits-DJhgx5Ay.d.mts} +0 -0
  33. /package/dist/{ref-DCDuswPe.d.mts → ref-TdeMfaV_.d.mts} +0 -0
  34. /package/dist/{renderer-CXJ8y0qw.d.mts → renderer-Ul9taFYp.d.mts} +0 -0
  35. /package/dist/{version-BFTVLsdb.d.mts → version-ZzL5R6cS.d.mts} +0 -0
@@ -0,0 +1,172 @@
1
+ import { m as JsonObject, w as SchemaMeta } from "./types-BTB73MB8.mjs";
2
+ import { i as DiagnosticsOptions } from "./diagnostics-Cbwak-ZX.mjs";
3
+
4
+ //#region src/core/adapter.d.ts
5
+ type SchemaInput = Record<string, unknown>;
6
+ type SchemaKind = "zod4" | "zod3" | "jsonSchema" | "openapi" | "unsupported-schema-lib";
7
+ /**
8
+ * Classify the input schema by its structural markers.
9
+ *
10
+ * - `zod4` — has a `_zod` marker (further validation that `_zod.def` is a
11
+ * non-null object happens inside `normaliseZod4`).
12
+ * - `zod3` — has `_def` and no `_zod`. The `typeName` field is no longer
13
+ * required: any `_def` without `_zod` is treated as a probable Zod 3
14
+ * schema. Third-party libraries that expose `_def` without `_zod` are
15
+ * nearly always Zod 3 forks; surfacing the migration message is the
16
+ * correct response.
17
+ * - `openapi` — has `openapi` or `swagger` at the root.
18
+ * - `unsupported-schema-lib` — has `parse` and `safeParse` callables but
19
+ * no `_zod` and no `_def` marker. This catches Standard Schema
20
+ * implementations (valibot, arktype, etc.) that would otherwise flow
21
+ * through as "malformed JSON Schema".
22
+ * - `jsonSchema` — fallback for anything that does not match the above.
23
+ */
24
+ declare function detectSchemaKind(input: unknown): SchemaKind;
25
+ /**
26
+ * Wraps z.toJSONSchema() for a runtime-validated Zod schema.
27
+ *
28
+ * The _zod guard in normaliseZod4 has confirmed this is a valid Zod schema,
29
+ * but TypeScript cannot represent "has _zod.def" as the $ZodType parameter
30
+ * that z.toJSONSchema expects. This is the library boundary equivalent of
31
+ * object → Record<string, unknown> — the type mismatch is genuinely unavoidable.
32
+ *
33
+ * # Options
34
+ *
35
+ * `z.toJSONSchema` is invoked with an explicit options object rather than
36
+ * Zod's defaults so the conversion contract is pinned and stable:
37
+ *
38
+ * - `target: "draft-2020-12"` — matches the walker's draft target.
39
+ * - `unrepresentable: "throw"` — keeps the unrepresentable-type rules in
40
+ * the classifier table firing instead of silently emitting `{}`.
41
+ * - `cycles: "ref"` — converts cyclic graphs into $ref pairs rather than
42
+ * throwing. Cycles in user schemas surface through the walker's $ref
43
+ * resolution rather than the adapter.
44
+ * - `io` — selects which side of every transform / pipe / codec is
45
+ * converted. Defaults to `"output"` (the OUTPUT side); pass `"input"`
46
+ * to render the INPUT side instead. The input side is invisible to
47
+ * the converted schema when `io: "output"` is in force, even though
48
+ * `safeParse` on the same Zod schema consumes the input shape. For
49
+ * transforms this divergence is fatal and the call throws via
50
+ * `Transforms cannot be represented`; for `z.codec(...)` the call
51
+ * succeeds but only the selected side is rendered. Consumers receive
52
+ * a `zod-codec-output-only` diagnostic in the codec case so the
53
+ * asymmetry is visible — see `screenPreConversion`.
54
+ *
55
+ * # Error classification
56
+ *
57
+ * Any exception thrown by z.toJSONSchema is classified into a
58
+ * SchemaNormalisationError so the caller does not have to re-parse error
59
+ * message strings. The classification covers:
60
+ *
61
+ * - Nested Zod 3 schemas inside a Zod 4 tree → zod3-unsupported.
62
+ * Detected structurally (presence of `_def.typeName` markers anywhere
63
+ * in the schema tree) so the check works across V8, JavaScriptCore,
64
+ * and SpiderMonkey, none of which agree on the wording of
65
+ * "Cannot read properties of undefined".
66
+ * - Transforms → zod-transform-unsupported. This also catches `z.codec(…)`
67
+ * because Zod implements codecs as a pipe + transform internally, so
68
+ * they trip the same processor when round-tripping is forced. (Plain
69
+ * `z.toJSONSchema(codec)` itself does NOT throw because Zod picks one
70
+ * side of the codec; the static rejection in `typeInference.ts` is the
71
+ * compile-time guard.)
72
+ * - Dynamic catch values whose handler throws → zod-type-unrepresentable
73
+ * with zodType "dynamic-catch".
74
+ * - Unrepresentable types — bigint, date, map, set, symbol, function, custom,
75
+ * undefined, void, NaN, and the literal-only forms `z.literal(undefined)`
76
+ * ("undefined-literal") and `z.literal(<bigint>)` ("bigint-literal") →
77
+ * zod-type-unrepresentable.
78
+ * - The catch-all "Non-representable type encountered: <type>" fallback Zod
79
+ * emits for any new schema kind without a registered processor →
80
+ * zod-type-unrepresentable with zodType set to the offending def.type.
81
+ * - Cycle detected (`cycles: "throw"`) → zod-cycle-detected.
82
+ * - Duplicate schema id → zod-duplicate-id.
83
+ * - "Unprocessed schema. This is a bug in Zod." → zod-conversion-bug.
84
+ * - "Error converting schema to JSON." → zod-conversion-failed (explicit
85
+ * classification rather than the generic fallback so the contract test
86
+ * protects the prefix from drift).
87
+ * - Anything else → zod-conversion-failed.
88
+ *
89
+ * The original error is preserved on each classified error via the `cause`
90
+ * field so consumers can still inspect the Zod stack trace.
91
+ */
92
+ /**
93
+ * IO side passed to {@link callToJsonSchema}. The Zod runtime accepts
94
+ * `"input" | "output"` for the corresponding `io` option on
95
+ * `z.toJSONSchema`. Defaults to `"output"` everywhere in the adapter
96
+ * pipeline; the parameter exists so a future renderer or component
97
+ * (currently SchemaComponent — see TODO below) can request the input
98
+ * side without forking the helper.
99
+ */
100
+ type SchemaIoSide = "input" | "output";
101
+ /**
102
+ * True when `value` is a Zod schema implemented as a codec
103
+ * (`z.codec(...)`). Detection looks for the `$ZodCodec` marker on the
104
+ * schema's `_zod.traits` Set — the same structural check used by Zod
105
+ * itself in `to-json-schema.ts`'s `isTransforming` helper.
106
+ *
107
+ * Promoted from a duplicated local helper in `react/SchemaComponent.tsx`
108
+ * so the validation boundary inside `runValidation` can branch on
109
+ * codec-vs-not-codec without re-implementing the trait check. The
110
+ * shared helper anchors a single source of truth for codec detection:
111
+ * any future change to Zod's trait naming flows through here, not
112
+ * through two parallel copies.
113
+ *
114
+ * Returns `false` for non-objects, plain JSON Schema inputs, OpenAPI
115
+ * documents, or Zod schemas of any other kind. This is structural
116
+ * rather than nominal — a Zod 4 codec produced by any path that ends
117
+ * up tagging `_zod.traits` with `$ZodCodec` is recognised, including
118
+ * schemas wrapped by user-defined helpers.
119
+ */
120
+ declare function isCodecSchema(value: unknown): boolean;
121
+ /**
122
+ * Exposed for unit testing — lets the contract test enumerate every rule's
123
+ * `prefix` value and assert mutual non-prefixing.
124
+ */
125
+ declare const __CLASSIFIER_RULES_FOR_TEST: readonly {
126
+ readonly prefix: string;
127
+ }[];
128
+ interface NormalisedSchema {
129
+ /** JSON Schema object — the authoritative schema for rendering. */
130
+ jsonSchema: JsonObject;
131
+ /** Original Zod schema, if input was Zod. Used for validation. */
132
+ zodSchema?: unknown;
133
+ /** Root-level metadata. */
134
+ rootMeta: SchemaMeta | undefined;
135
+ /** The root document for $ref resolution. */
136
+ rootDocument: JsonObject;
137
+ }
138
+ interface NormaliseOptions {
139
+ /** Diagnostics channel for surfacing silent fallbacks. */
140
+ diagnostics?: DiagnosticsOptions;
141
+ /**
142
+ * Side of every transform / pipe / codec to render. Defaults to
143
+ * `"output"`, matching `z.toJSONSchema`'s default and the
144
+ * historic behaviour of the adapter. Passing `"input"` flips the
145
+ * conversion so consumers rendering the input shape of a
146
+ * `z.codec(...)` chain receive that side instead of the output
147
+ * side. Only the Zod 4 branch consults this option — JSON Schema
148
+ * and OpenAPI inputs are already a single canonical shape.
149
+ */
150
+ io?: SchemaIoSide;
151
+ }
152
+ declare function normaliseSchema(input: unknown, ref?: string, options?: NormaliseOptions): NormalisedSchema;
153
+ /**
154
+ * Surface root-level metadata from the JSON Schema into the `rootMeta`
155
+ * shape consumed by the walker. Pulls `readOnly`, `writeOnly`,
156
+ * `description`, `title`, `deprecated`, `examples`, and `default`
157
+ * directly from the schema root.
158
+ *
159
+ * `examples` is forwarded only when present as an array (per JSON Schema
160
+ * Draft 2020-12 — Draft 04's `example` singular is normalised upstream).
161
+ * `default` is forwarded for any value the schema declares (any JSON
162
+ * value, including `null` and `false`); the presence check uses `in`
163
+ * so a literal `false` or `null` default is preserved.
164
+ *
165
+ * `examples` and `default` ride on the `[key: string]: unknown` index
166
+ * signature of {@link SchemaMeta}. They are not declared as named fields
167
+ * on `SchemaMeta` because that type lives in `types.ts` and is shared
168
+ * with the walker; the index signature is the agreed extension point.
169
+ */
170
+ declare function extractRootMetaFromJson(jsonSchema: JsonObject): SchemaMeta | undefined;
171
+ //#endregion
172
+ export { SchemaKind as a, extractRootMetaFromJson as c, SchemaIoSide as i, isCodecSchema as l, NormalisedSchema as n, __CLASSIFIER_RULES_FOR_TEST as o, SchemaInput as r, detectSchemaKind as s, NormaliseOptions as t, normaliseSchema as u };
@@ -1,142 +1,3 @@
1
1
  import { m as JsonObject, w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-Cbwak-ZX.mjs";
3
-
4
- //#region src/core/adapter.d.ts
5
- type SchemaInput = Record<string, unknown>;
6
- type SchemaKind = "zod4" | "zod3" | "jsonSchema" | "openapi" | "unsupported-schema-lib";
7
- /**
8
- * Classify the input schema by its structural markers.
9
- *
10
- * - `zod4` — has a `_zod` marker (further validation that `_zod.def` is a
11
- * non-null object happens inside `normaliseZod4`).
12
- * - `zod3` — has `_def` and no `_zod`. The `typeName` field is no longer
13
- * required: any `_def` without `_zod` is treated as a probable Zod 3
14
- * schema. Third-party libraries that expose `_def` without `_zod` are
15
- * nearly always Zod 3 forks; surfacing the migration message is the
16
- * correct response.
17
- * - `openapi` — has `openapi` or `swagger` at the root.
18
- * - `unsupported-schema-lib` — has `parse` and `safeParse` callables but
19
- * no `_zod` and no `_def` marker. This catches Standard Schema
20
- * implementations (valibot, arktype, etc.) that would otherwise flow
21
- * through as "malformed JSON Schema".
22
- * - `jsonSchema` — fallback for anything that does not match the above.
23
- */
24
- declare function detectSchemaKind(input: unknown): SchemaKind;
25
- /**
26
- * Wraps z.toJSONSchema() for a runtime-validated Zod schema.
27
- *
28
- * The _zod guard in normaliseZod4 has confirmed this is a valid Zod schema,
29
- * but TypeScript cannot represent "has _zod.def" as the $ZodType parameter
30
- * that z.toJSONSchema expects. This is the library boundary equivalent of
31
- * object → Record<string, unknown> — the type mismatch is genuinely unavoidable.
32
- *
33
- * # Options
34
- *
35
- * `z.toJSONSchema` is invoked with an explicit options object rather than
36
- * Zod's defaults so the conversion contract is pinned and stable:
37
- *
38
- * - `target: "draft-2020-12"` — matches the walker's draft target.
39
- * - `unrepresentable: "throw"` — keeps the unrepresentable-type rules in
40
- * the classifier table firing instead of silently emitting `{}`.
41
- * - `cycles: "ref"` — converts cyclic graphs into $ref pairs rather than
42
- * throwing. Cycles in user schemas surface through the walker's $ref
43
- * resolution rather than the adapter.
44
- * - `io` — selects which side of every transform / pipe / codec is
45
- * converted. Defaults to `"output"` (the OUTPUT side); pass `"input"`
46
- * to render the INPUT side instead. The input side is invisible to
47
- * the converted schema when `io: "output"` is in force, even though
48
- * `safeParse` on the same Zod schema consumes the input shape. For
49
- * transforms this divergence is fatal and the call throws via
50
- * `Transforms cannot be represented`; for `z.codec(...)` the call
51
- * succeeds but only the selected side is rendered. Consumers receive
52
- * a `zod-codec-output-only` diagnostic in the codec case so the
53
- * asymmetry is visible — see `screenPreConversion`.
54
- *
55
- * # Error classification
56
- *
57
- * Any exception thrown by z.toJSONSchema is classified into a
58
- * SchemaNormalisationError so the caller does not have to re-parse error
59
- * message strings. The classification covers:
60
- *
61
- * - Nested Zod 3 schemas inside a Zod 4 tree → zod3-unsupported.
62
- * Detected structurally (presence of `_def.typeName` markers anywhere
63
- * in the schema tree) so the check works across V8, JavaScriptCore,
64
- * and SpiderMonkey, none of which agree on the wording of
65
- * "Cannot read properties of undefined".
66
- * - Transforms → zod-transform-unsupported. This also catches `z.codec(…)`
67
- * because Zod implements codecs as a pipe + transform internally, so
68
- * they trip the same processor when round-tripping is forced. (Plain
69
- * `z.toJSONSchema(codec)` itself does NOT throw because Zod picks one
70
- * side of the codec; the static rejection in `typeInference.ts` is the
71
- * compile-time guard.)
72
- * - Dynamic catch values whose handler throws → zod-type-unrepresentable
73
- * with zodType "dynamic-catch".
74
- * - Unrepresentable types — bigint, date, map, set, symbol, function, custom,
75
- * undefined, void, NaN, and the literal-only forms `z.literal(undefined)`
76
- * ("undefined-literal") and `z.literal(<bigint>)` ("bigint-literal") →
77
- * zod-type-unrepresentable.
78
- * - The catch-all "Non-representable type encountered: <type>" fallback Zod
79
- * emits for any new schema kind without a registered processor →
80
- * zod-type-unrepresentable with zodType set to the offending def.type.
81
- * - Cycle detected (`cycles: "throw"`) → zod-cycle-detected.
82
- * - Duplicate schema id → zod-duplicate-id.
83
- * - "Unprocessed schema. This is a bug in Zod." → zod-conversion-bug.
84
- * - "Error converting schema to JSON." → zod-conversion-failed (explicit
85
- * classification rather than the generic fallback so the contract test
86
- * protects the prefix from drift).
87
- * - Anything else → zod-conversion-failed.
88
- *
89
- * The original error is preserved on each classified error via the `cause`
90
- * field so consumers can still inspect the Zod stack trace.
91
- */
92
- /**
93
- * IO side passed to {@link callToJsonSchema}. The Zod runtime accepts
94
- * `"input" | "output"` for the corresponding `io` option on
95
- * `z.toJSONSchema`. Defaults to `"output"` everywhere in the adapter
96
- * pipeline; the parameter exists so a future renderer or component
97
- * (currently SchemaComponent — see TODO below) can request the input
98
- * side without forking the helper.
99
- */
100
- type SchemaIoSide = "input" | "output";
101
- /**
102
- * Exposed for unit testing — lets the contract test enumerate every rule's
103
- * `prefix` value and assert mutual non-prefixing.
104
- */
105
- declare const __CLASSIFIER_RULES_FOR_TEST: readonly {
106
- readonly prefix: string;
107
- }[];
108
- interface NormalisedSchema {
109
- /** JSON Schema object — the authoritative schema for rendering. */
110
- jsonSchema: JsonObject;
111
- /** Original Zod schema, if input was Zod. Used for validation. */
112
- zodSchema?: unknown;
113
- /** Root-level metadata. */
114
- rootMeta: SchemaMeta | undefined;
115
- /** The root document for $ref resolution. */
116
- rootDocument: JsonObject;
117
- }
118
- interface NormaliseOptions {
119
- /** Diagnostics channel for surfacing silent fallbacks. */
120
- diagnostics?: DiagnosticsOptions;
121
- }
122
- declare function normaliseSchema(input: unknown, ref?: string, options?: NormaliseOptions): NormalisedSchema;
123
- /**
124
- * Surface root-level metadata from the JSON Schema into the `rootMeta`
125
- * shape consumed by the walker. Pulls `readOnly`, `writeOnly`,
126
- * `description`, `title`, `deprecated`, `examples`, and `default`
127
- * directly from the schema root.
128
- *
129
- * `examples` is forwarded only when present as an array (per JSON Schema
130
- * Draft 2020-12 — Draft 04's `example` singular is normalised upstream).
131
- * `default` is forwarded for any value the schema declares (any JSON
132
- * value, including `null` and `false`); the presence check uses `in`
133
- * so a literal `false` or `null` default is preserved.
134
- *
135
- * `examples` and `default` ride on the `[key: string]: unknown` index
136
- * signature of {@link SchemaMeta}. They are not declared as named fields
137
- * on `SchemaMeta` because that type lives in `types.ts` and is shared
138
- * with the walker; the index signature is the agreed extension point.
139
- */
140
- declare function extractRootMetaFromJson(jsonSchema: JsonObject): SchemaMeta | undefined;
141
- //#endregion
142
- export { type JsonObject, NormaliseOptions, NormalisedSchema, SchemaInput, SchemaIoSide, SchemaKind, type SchemaMeta, __CLASSIFIER_RULES_FOR_TEST, detectSchemaKind, extractRootMetaFromJson, normaliseSchema };
2
+ import { a as SchemaKind, c as extractRootMetaFromJson, i as SchemaIoSide, l as isCodecSchema, n as NormalisedSchema, o as __CLASSIFIER_RULES_FOR_TEST, r as SchemaInput, s as detectSchemaKind, t as NormaliseOptions, u as normaliseSchema } from "../adapter-MiEFkRVV.mjs";
3
+ export { JsonObject, NormaliseOptions, NormalisedSchema, SchemaInput, SchemaIoSide, SchemaKind, SchemaMeta, __CLASSIFIER_RULES_FOR_TEST, detectSchemaKind, extractRootMetaFromJson, isCodecSchema, normaliseSchema };
@@ -80,6 +80,15 @@ function extractStandardSchemaVendor(input) {
80
80
  const vendor = getProperty(getProperty(input, "~standard"), "vendor");
81
81
  return typeof vendor === "string" && vendor.length > 0 ? vendor : void 0;
82
82
  }
83
+ /**
84
+ * The `io` parameter is propagated by `normaliseSchema` →
85
+ * `normaliseZod4` so the renderer can select between the OUTPUT side
86
+ * (default — historic behaviour) and the INPUT side of every
87
+ * transform / pipe / codec. Pinning the option here lets the
88
+ * consumer-facing `<SchemaComponent io="…">` and
89
+ * `<SchemaView io="…">` props decide a single conversion direction
90
+ * for the whole tree without a fork at every Zod call site.
91
+ */
83
92
  function callToJsonSchema(schema, io = "output") {
84
93
  try {
85
94
  return z.toJSONSchema(schema, {
@@ -282,6 +291,30 @@ function hasTrait(zod, traitName) {
282
291
  return false;
283
292
  }
284
293
  /**
294
+ * True when `value` is a Zod schema implemented as a codec
295
+ * (`z.codec(...)`). Detection looks for the `$ZodCodec` marker on the
296
+ * schema's `_zod.traits` Set — the same structural check used by Zod
297
+ * itself in `to-json-schema.ts`'s `isTransforming` helper.
298
+ *
299
+ * Promoted from a duplicated local helper in `react/SchemaComponent.tsx`
300
+ * so the validation boundary inside `runValidation` can branch on
301
+ * codec-vs-not-codec without re-implementing the trait check. The
302
+ * shared helper anchors a single source of truth for codec detection:
303
+ * any future change to Zod's trait naming flows through here, not
304
+ * through two parallel copies.
305
+ *
306
+ * Returns `false` for non-objects, plain JSON Schema inputs, OpenAPI
307
+ * documents, or Zod schemas of any other kind. This is structural
308
+ * rather than nominal — a Zod 4 codec produced by any path that ends
309
+ * up tagging `_zod.traits` with `$ZodCodec` is recognised, including
310
+ * schemas wrapped by user-defined helpers.
311
+ */
312
+ function isCodecSchema(value) {
313
+ const zod = getProperty(value, "_zod");
314
+ if (!isObject(zod)) return false;
315
+ return hasTrait(zod, "$ZodCodec");
316
+ }
317
+ /**
285
318
  * Type guard narrowing `unknown` to a zero-argument function returning
286
319
  * `unknown`. The narrowing is genuinely structural: `typeof === "function"`
287
320
  * at runtime is exactly the membership test we want, and Zod has no
@@ -593,7 +626,8 @@ function classifyZodConversionError(err, schema) {
593
626
  const __CLASSIFIER_RULES_FOR_TEST = CLASSIFIER_RULES;
594
627
  function normaliseSchema(input, ref, options) {
595
628
  const usesDiagnostics = options?.diagnostics !== void 0;
596
- const cacheEligible = ref === void 0 && isObject(input) && !usesDiagnostics;
629
+ const nonDefaultIo = options?.io !== void 0 && options.io !== "output";
630
+ const cacheEligible = ref === void 0 && isObject(input) && !usesDiagnostics && !nonDefaultIo;
597
631
  if (cacheEligible) {
598
632
  const cached = schemaCache.get(input);
599
633
  if (cached !== void 0) return cached;
@@ -602,7 +636,7 @@ function normaliseSchema(input, ref, options) {
602
636
  let result;
603
637
  switch (kind) {
604
638
  case "zod4":
605
- result = normaliseZod4(input, options?.diagnostics);
639
+ result = normaliseZod4(input, options?.diagnostics, options?.io);
606
640
  break;
607
641
  case "zod3":
608
642
  result = normaliseZod3(input);
@@ -623,12 +657,12 @@ function normaliseSchema(input, ref, options) {
623
657
  if (cacheEligible) schemaCache.set(input, result);
624
658
  return result;
625
659
  }
626
- function normaliseZod4(input, diagnostics) {
660
+ function normaliseZod4(input, diagnostics, io) {
627
661
  const zod = getProperty(input, "_zod");
628
662
  if (!isObject(zod)) throw new SchemaNormalisationError("Input is not a valid Zod 4 schema: `_zod` is present but is not an object. schema-components expected a Zod 4 schema produced by the `zod` package version 4 or later. See the Zod 4 migration guide at https://zod.dev/v4/migration or run: pnpm add zod@^4", input, "unsupported-schema");
629
663
  if (!isObject(getProperty(zod, "def"))) throw new SchemaNormalisationError("Input is not a valid Zod 4 schema: `_zod.def` is missing or not an object. schema-components expected a Zod 4 schema produced by the `zod` package version 4 or later. See the Zod 4 migration guide at https://zod.dev/v4/migration or run: pnpm add zod@^4", input, "unsupported-schema");
630
664
  screenPreConversion(input, diagnostics);
631
- const jsonSchema = callToJsonSchema(input);
665
+ const jsonSchema = callToJsonSchema(input, io);
632
666
  if (!isObject(jsonSchema)) throw new SchemaNormalisationError("z.toJSONSchema() did not produce an object", input, "invalid-zod");
633
667
  return {
634
668
  jsonSchema,
@@ -780,4 +814,4 @@ function extractRootMetaFromJson(jsonSchema) {
780
814
  return Object.keys(meta).length > 0 ? meta : void 0;
781
815
  }
782
816
  //#endregion
783
- export { __CLASSIFIER_RULES_FOR_TEST, detectSchemaKind, extractRootMetaFromJson, normaliseSchema };
817
+ export { __CLASSIFIER_RULES_FOR_TEST, detectSchemaKind, extractRootMetaFromJson, isCodecSchema, normaliseSchema };
@@ -1,2 +1,2 @@
1
- import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-g_MCTQel.mjs";
1
+ import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-DQSIK4n1.mjs";
2
2
  export { SchemaError, SchemaFieldError, SchemaNormalisationError, SchemaRenderError };
@@ -1,2 +1,2 @@
1
- import { i as MaxRefDepth, n as MAX_REF_DEPTH, r as MAX_RENDER_DEPTH, t as MAX_PATH_ITEM_REF_HOPS } from "../limits-Cw5QZND8.mjs";
1
+ import { i as MaxRefDepth, n as MAX_REF_DEPTH, r as MAX_RENDER_DEPTH, t as MAX_PATH_ITEM_REF_HOPS } from "../limits-DJhgx5Ay.mjs";
2
2
  export { MAX_PATH_ITEM_REF_HOPS, MAX_REF_DEPTH, MAX_RENDER_DEPTH, MaxRefDepth };
@@ -1,5 +1,5 @@
1
1
  import { i as DiagnosticsOptions } from "../diagnostics-Cbwak-ZX.mjs";
2
- import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-BFTVLsdb.mjs";
2
+ import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-ZzL5R6cS.mjs";
3
3
 
4
4
  //#region src/core/normalise.d.ts
5
5
  type NodeTransform = (node: Record<string, unknown>) => Record<string, unknown>;
@@ -1,2 +1,2 @@
1
- import { a as dereference, i as countDistinctRefs, n as RECURSIVE_ANCHOR_SENTINEL, o as findAnchor, r as RefOptions, s as resolveRef, t as ExternalResolver } from "../ref-DCDuswPe.mjs";
1
+ import { a as dereference, i as countDistinctRefs, n as RECURSIVE_ANCHOR_SENTINEL, o as findAnchor, r as RefOptions, s as resolveRef, t as ExternalResolver } from "../ref-TdeMfaV_.mjs";
2
2
  export { ExternalResolver, RECURSIVE_ANCHOR_SENTINEL, RefOptions, countDistinctRefs, dereference, findAnchor, resolveRef };
@@ -1,2 +1,2 @@
1
- import { a as HtmlRenderProps, c as RenderFunction, d as getHtmlRenderFn, f as getRenderFunction, h as typeToKey, i as HtmlRenderFunction, l as RenderProps, m as mergeResolvers, n as BaseFieldProps, o as HtmlResolver, p as mergeHtmlResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as buildRenderProps } from "../renderer-CXJ8y0qw.mjs";
1
+ import { a as HtmlRenderProps, c as RenderFunction, d as getHtmlRenderFn, f as getRenderFunction, h as typeToKey, i as HtmlRenderFunction, l as RenderProps, m as mergeResolvers, n as BaseFieldProps, o as HtmlResolver, p as mergeHtmlResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as buildRenderProps } from "../renderer-Ul9taFYp.mjs";
2
2
  export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
@@ -1,5 +1,5 @@
1
1
  import { d as FieldOverrides, u as FieldOverride } from "../types-BTB73MB8.mjs";
2
- import { i as MaxRefDepth } from "../limits-Cw5QZND8.mjs";
2
+ import { i as MaxRefDepth } from "../limits-DJhgx5Ay.mjs";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/core/typeInference.d.ts
@@ -1,2 +1,2 @@
1
- import { a as detectJsonSchemaDraft, c as inferJsonSchemaDraftWithReason, d as isSwagger2, f as matchJsonSchemaDraftUri, i as OpenApiVersionInfo, l as isOpenApi30, n as JsonSchemaDialectInfo, o as detectOpenApiVersion, p as readJsonSchemaDialect, r as JsonSchemaDraft, s as inferJsonSchemaDraft, t as InferredDraft, u as isOpenApi31 } from "../version-BFTVLsdb.mjs";
1
+ import { a as detectJsonSchemaDraft, c as inferJsonSchemaDraftWithReason, d as isSwagger2, f as matchJsonSchemaDraftUri, i as OpenApiVersionInfo, l as isOpenApi30, n as JsonSchemaDialectInfo, o as detectOpenApiVersion, p as readJsonSchemaDialect, r as JsonSchemaDraft, s as inferJsonSchemaDraft, t as InferredDraft, u as isOpenApi31 } from "../version-ZzL5R6cS.mjs";
2
2
  export { InferredDraft, JsonSchemaDialectInfo, JsonSchemaDraft, OpenApiVersionInfo, detectJsonSchemaDraft, detectOpenApiVersion, inferJsonSchemaDraft, inferJsonSchemaDraftWithReason, isOpenApi30, isOpenApi31, isSwagger2, matchJsonSchemaDraftUri, readJsonSchemaDialect };
@@ -1,6 +1,6 @@
1
1
  import { A as UnknownField, D as StringField, b as NumberField, c as FieldBase, j as WalkedField, o as Editability, p as FileField, r as BooleanField, v as NullField, w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
2
  import { i as DiagnosticsOptions } from "../diagnostics-Cbwak-ZX.mjs";
3
- import { t as ExternalResolver } from "../ref-DCDuswPe.mjs";
3
+ import { t as ExternalResolver } from "../ref-TdeMfaV_.mjs";
4
4
 
5
5
  //#region src/core/walkBuilders.d.ts
6
6
  declare function getString(obj: Record<string, unknown>, key: string): string | undefined;
@@ -1,5 +1,5 @@
1
1
  import { j as WalkedField } from "../types-BTB73MB8.mjs";
2
- import { t as AllConstraints } from "../renderer-CXJ8y0qw.mjs";
2
+ import { t as AllConstraints } from "../renderer-Ul9taFYp.mjs";
3
3
  import { HtmlAttributes, HtmlNode } from "./html.mjs";
4
4
 
5
5
  //#region src/html/a11y.d.ts
@@ -1,5 +1,5 @@
1
1
  import { w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
- import { o as HtmlResolver } from "../renderer-CXJ8y0qw.mjs";
2
+ import { o as HtmlResolver } from "../renderer-Ul9taFYp.mjs";
3
3
 
4
4
  //#region src/html/renderToHtml.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
- import { o as HtmlResolver } from "../renderer-CXJ8y0qw.mjs";
2
+ import { o as HtmlResolver } from "../renderer-Ul9taFYp.mjs";
3
3
 
4
4
  //#region src/html/renderToHtmlStream.d.ts
5
5
  interface StreamRenderOptions {
@@ -1,5 +1,5 @@
1
1
  import { dateInputType } from "../core/formats.mjs";
2
- import { o as HtmlResolver } from "../renderer-CXJ8y0qw.mjs";
2
+ import { o as HtmlResolver } from "../renderer-Ul9taFYp.mjs";
3
3
  import { matchUnionOption } from "../core/unionMatch.mjs";
4
4
 
5
5
  //#region src/html/renderers.d.ts
@@ -1,6 +1,6 @@
1
1
  import { j as WalkedField } from "../types-BTB73MB8.mjs";
2
2
  import { i as DiagnosticsOptions } from "../diagnostics-Cbwak-ZX.mjs";
3
- import { o as HtmlResolver } from "../renderer-CXJ8y0qw.mjs";
3
+ import { o as HtmlResolver } from "../renderer-Ul9taFYp.mjs";
4
4
  import { HtmlElement } from "./html.mjs";
5
5
 
6
6
  //#region src/html/streamRenderers.d.ts
@@ -2,15 +2,15 @@ import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
2
2
  import { emitDiagnostic } from "../core/diagnostics.mjs";
3
3
  import { extractRootMetaFromJson } from "../core/adapter.mjs";
4
4
  import { walk } from "../core/walker.mjs";
5
+ import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
5
6
  import { ApiCallbacks } from "./ApiCallbacks.mjs";
6
7
  import { ApiLinks } from "./ApiLinks.mjs";
7
8
  import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
8
9
  import { ApiSecurity } from "./ApiSecurity.mjs";
9
10
  import { getExternalDocs, getLinks, getSecurityRequirements, getSecuritySchemes, getXmlInfo, listCallbacks, listWebhooks } from "./parser.mjs";
10
- import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
11
11
  import { getParsed, resolveOperationFromParsed, resolveParametersFromParsed, resolveRequestBodyFromParsed, resolveResponseFromParsed, toDoc } from "./resolve.mjs";
12
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
12
  import { useId } from "react";
13
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
14
  //#region src/openapi/components.tsx
15
15
  /**
16
16
  * OpenAPI React components with type-safe generics.
@@ -1,11 +1,12 @@
1
1
  import { d as FieldOverrides, j as WalkedField, u as FieldOverride, w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
2
  import { t as Diagnostic } from "../diagnostics-Cbwak-ZX.mjs";
3
- import { t as SchemaError } from "../errors-g_MCTQel.mjs";
4
- import { l as RenderProps, r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
3
+ import { i as SchemaIoSide } from "../adapter-MiEFkRVV.mjs";
4
+ import { t as SchemaError } from "../errors-DQSIK4n1.mjs";
5
+ import { l as RenderProps, r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
5
6
  import { FromJSONSchema, FromJSONSchemaMode, IsSwagger2Doc, PathOfType, RejectUnrepresentableZod, ResolveOpenAPIRef, TypeAtPath, __SchemaInferenceFellBack } from "../core/typeInference.mjs";
6
7
  import { z } from "zod";
7
- import * as _$react_jsx_runtime0 from "react/jsx-runtime";
8
8
  import { ReactNode } from "react";
9
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
9
10
 
10
11
  //#region src/react/SchemaComponent.d.ts
11
12
  /**
@@ -65,28 +66,36 @@ type InferSchemaValue<T, Ref extends string | undefined, Mode extends FromJSONSc
65
66
  type NarrowAtPath<V, P extends string | undefined> = P extends string ? TypeAtPath<V, P> : V;
66
67
  /**
67
68
  * Public alias mapping a schema input to the rendered value type.
68
- * Use to narrow a runtime callback inside the body of an `onChange`
69
- * handler:
70
- *
71
- * ```tsx
72
- * <SchemaComponent
73
- * schema={userSchema}
74
- * onChange={(v) => {
75
- * const user = v as InferredOutputValue<typeof userSchema>;
76
- * // ...narrowly typed access on `user`
77
- * }}
78
- * />
79
- * ```
80
69
  *
81
- * The `onChange` argument is typed `unknown` at the props boundary
82
- * because the walker propagates `unknown` values through the render
83
- * pipeline. Narrowing on the consumer side is therefore an explicit
84
- * step and never a silent contract gap.
70
+ * Picks the OUTPUT side (server client) of every transform / pipe /
71
+ * codec. For an `<SchemaComponent io="output">` or `<SchemaView
72
+ * io="output">` (both defaults), this is the inferred shape of
73
+ * `value` and the parameter of `onChange`.
85
74
  */
86
75
  type InferredOutputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "output">, P>;
87
- /** Companion to {@link InferredOutputValue} for `"input"`-mode shapes. */
76
+ /**
77
+ * Companion to {@link InferredOutputValue} for `"input"`-mode shapes.
78
+ *
79
+ * Picks the INPUT side (client → server) of every transform / pipe /
80
+ * codec. Surfaces as the inferred shape of `value` / `onChange` when
81
+ * a consumer renders `<SchemaComponent io="input">`. For JSON Schema
82
+ * inputs with `readOnly`/`writeOnly` annotations, the INPUT mode
83
+ * omits properties marked `readOnly: true`.
84
+ */
88
85
  type InferredInputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "input">, P>;
89
- interface SchemaComponentProps<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined> {
86
+ /**
87
+ * Resolve the schema-driven value type for either I/O direction.
88
+ *
89
+ * Thin convenience over {@link InferredOutputValue} /
90
+ * {@link InferredInputValue} so consumers that decide between the
91
+ * two at the type level (e.g. a generic wrapper component) can pass
92
+ * the chosen direction as a type argument rather than branch on it
93
+ * with conditional types. Falls back to `unknown` when the schema's
94
+ * value type cannot be statically inferred, identical to the
95
+ * underlying helpers.
96
+ */
97
+ type InferredValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> = NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>;
98
+ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
90
99
  /**
91
100
  * Zod schema, JSON Schema object, or OpenAPI document.
92
101
  *
@@ -116,56 +125,57 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
116
125
  */
117
126
  path?: P;
118
127
  /**
119
- * Current value to render.
128
+ * Which side of every transform / pipe / codec to render.
120
129
  *
121
- * TYPE BOUNDARY NOTE: kept as `unknown` at the props boundary so
122
- * existing call sites including legitimate edge-case fixtures
123
- * that pass deliberately invalid values to exercise fallback code
124
- * paths continue to typecheck. Use {@link InferredOutputValue}
125
- * to narrow on the consumer side:
130
+ * - `"output"` (default) renderer draws the OUTPUT side of the
131
+ * schema. For a `z.codec(z.string(), z.number(), …)` chain
132
+ * this renders a number input. `value` and `onChange` therefore
133
+ * carry the OUTPUT shape, and `validate` runs `safeEncode`
134
+ * (the reverse direction) so user-supplied OUTPUT values are
135
+ * validated against the codec.
136
+ * - `"input"` — renderer draws the INPUT side instead. For the
137
+ * same codec this renders a string input, `value` and
138
+ * `onChange` carry the INPUT shape, and `validate` runs
139
+ * `safeParse` (the forward direction).
140
+ *
141
+ * The choice is propagated through `normaliseSchema` →
142
+ * `normaliseZod4` → `z.toJSONSchema(..., { io })` so a single
143
+ * source of truth drives both the rendered JSON Schema shape and
144
+ * the validation direction. Has no effect for plain JSON Schema
145
+ * or OpenAPI inputs — those advertise a single canonical shape.
146
+ */
147
+ io?: Mode;
148
+ /**
149
+ * Current value to render. Typed against `InferSchemaValue<T,
150
+ * Ref, Mode>` so the prop tracks the schema's inferred shape for
151
+ * the chosen `io` direction. Narrowed further by `P` when a
152
+ * `path` is supplied (currently type-level only — see the JSDoc
153
+ * on {@link SchemaComponentProps.path}).
154
+ *
155
+ * Falls back to `unknown` when the schema's value type cannot be
156
+ * statically inferred (runtime `Record<string, unknown>` JSON
157
+ * Schemas, OpenAPI documents without a ref, etc.), so untyped
158
+ * call sites still compile.
159
+ *
160
+ * Use {@link InferredOutputValue} or {@link InferredInputValue}
161
+ * to narrow a value declared at the call site:
126
162
  *
127
163
  * ```tsx
128
164
  * const user: InferredOutputValue<typeof userSchema> = { ... };
129
165
  * <SchemaComponent schema={userSchema} value={user} readOnly />
130
166
  * ```
131
- *
132
- * The narrowing is fully expressible through the helper alias
133
- * without forcing every existing caller to update their value
134
- * shapes for `exactOptionalPropertyTypes` / enum literal widening.
135
- *
136
- * TODO(round7-integration): promote to
137
- * `NarrowAtPath<InferSchemaValue<T, Ref, "output">, P>` once the
138
- * test fixtures (headless union, discriminated union,
139
- * schemaview equivalence, type-inference issue fixes) are
140
- * migrated to either narrow their fixtures or accept the loose
141
- * boundary. The retype cascades through call sites that
142
- * intentionally pass invalid values to exercise fallback paths
143
- * (`value={undefined}`, off-discriminator values, etc.) and
144
- * through fixtures whose enum/literal types widen at the call
145
- * site. Coordinated migration is required.
146
167
  */
147
- value?: unknown;
168
+ value?: NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>;
148
169
  /**
149
- * Called when the value changes (editable fields).
170
+ * Called when the value changes (editable fields). The parameter
171
+ * shares the same shape as {@link SchemaComponentProps.value} so
172
+ * a controlled component can round-trip the value through React
173
+ * state without re-shaping.
150
174
  *
151
- * TYPE BOUNDARY NOTE: the parameter is typed `unknown` rather
152
- * than the inferred input shape because the walker pipeline only
153
- * propagates `unknown` values and a narrow contravariant callback
154
- * signature is not assignable from an `unknown`-emitting source
155
- * without an unsafe boundary cast. The {@link InferredInputValue}
156
- * alias is the recommended way for callers to narrow on the
157
- * consumer side — `onChange={(v) => { const u = v as InferredInputValue<typeof schema>; ... }}`.
158
- *
159
- * TODO(round7-integration): promote to
160
- * `(value: NarrowAtPath<InferSchemaValue<T, Ref, "input">, P>) => void`
161
- * alongside the `value` retype. The contravariant boundary needs
162
- * an internal cast at the only call site (`onChange?.(nextValue)`
163
- * in `handleChange`) that the project's no-`as` rule disallows
164
- * without explicit justification. The right place to introduce
165
- * the cast is a tiny typed boundary helper accompanied by the
166
- * fixture migration noted above.
175
+ * Falls back to `unknown` for schemas whose value type cannot be
176
+ * statically inferred see {@link SchemaComponentProps.value}.
167
177
  */
168
- onChange?: (value: unknown) => void;
178
+ onChange?: (value: NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>) => void;
169
179
  /** Run schema.safeParse() on change and surface errors via onValidationError. */
170
180
  validate?: boolean;
171
181
  /** Called with the ZodError when validation fails. */
@@ -196,7 +206,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
196
206
  */
197
207
  idPrefix?: string;
198
208
  }
199
- declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined>(props: SchemaComponentProps<T, Ref, P>): ReactNode;
209
+ declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(props: SchemaComponentProps<T, Ref, P, Mode>): ReactNode;
200
210
  /**
201
211
  * Append a child path suffix to a parent path. When the suffix is omitted
202
212
  * (e.g. transparent wrappers like union options), the parent path is
@@ -255,4 +265,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
255
265
  onValidationError
256
266
  }: SchemaFieldProps<T, Ref, P>): ReactNode;
257
267
  //#endregion
258
- export { InferredInputValue, InferredOutputValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };
268
+ export { InferredInputValue, InferredOutputValue, InferredValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };
@@ -1,15 +1,15 @@
1
1
  "use client";
2
- import { getProperty, isObject, toRecordOrUndefined } from "../core/guards.mjs";
2
+ import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
3
3
  import "../core/limits.mjs";
4
4
  import { SchemaFieldError, SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
5
- import { normaliseSchema } from "../core/adapter.mjs";
5
+ import { isCodecSchema, normaliseSchema } from "../core/adapter.mjs";
6
6
  import { buildRenderProps, getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
7
7
  import { walk } from "../core/walker.mjs";
8
8
  import { headlessResolver } from "./headless.mjs";
9
9
  import { resolvePath, resolveValue, setNestedValue } from "./fieldPath.mjs";
10
10
  import { z } from "zod";
11
- import { jsx, jsxs } from "react/jsx-runtime";
12
11
  import { createContext, isValidElement, useCallback, useContext, useId, useMemo } from "react";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
13
  //#region src/react/SchemaComponent.tsx
14
14
  /**
15
15
  * <SchemaComponent> — renders UI from Zod, JSON Schema, or OpenAPI schemas.
@@ -48,7 +48,7 @@ function registerWidget(name, render) {
48
48
  globalWidgets.set(name, render);
49
49
  }
50
50
  function SchemaComponent(props) {
51
- const { schema: schemaInput, ref: refInput, value, onChange, validate, onValidationError, onError, onDiagnostic, strict, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets, idPrefix } = props;
51
+ const { schema: schemaInput, ref: refInput, io, value, onChange, validate, onValidationError, onError, onDiagnostic, strict, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets, idPrefix } = props;
52
52
  const userResolver = useContext(UserResolverContext);
53
53
  const contextWidgets = useContext(WidgetsContext);
54
54
  const generatedId = useId();
@@ -74,7 +74,10 @@ function SchemaComponent(props) {
74
74
  let rootMeta;
75
75
  let rootDocument;
76
76
  try {
77
- const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 ? { diagnostics } : void 0);
77
+ const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 || io !== void 0 ? {
78
+ ...diagnostics !== void 0 ? { diagnostics } : {},
79
+ ...io !== void 0 ? { io } : {}
80
+ } : void 0);
78
81
  jsonSchema = normalised.jsonSchema;
79
82
  zodSchema = normalised.zodSchema;
80
83
  rootMeta = normalised.rootMeta;
@@ -92,7 +95,7 @@ function SchemaComponent(props) {
92
95
  if (validate) {
93
96
  let error;
94
97
  try {
95
- error = runValidation(zodSchema, jsonSchema, nextValue, onDiagnostic);
98
+ error = runValidation(zodSchema, jsonSchema, nextValue, io, onDiagnostic);
96
99
  } catch (err) {
97
100
  const normalised = err instanceof SchemaNormalisationError ? err : new SchemaNormalisationError(err instanceof Error ? err.message : "Fallback validation failed", schemaInput, "zod-conversion-failed", void 0, err);
98
101
  if (onError !== void 0) {
@@ -106,11 +109,12 @@ function SchemaComponent(props) {
106
109
  dispatchFieldErrors(fieldsRecord, error);
107
110
  }
108
111
  }
109
- onChange?.(nextValue);
112
+ if (onChange !== void 0) onChange(nextValue);
110
113
  }, [
111
114
  validate,
112
115
  zodSchema,
113
116
  jsonSchema,
117
+ io,
114
118
  onChange,
115
119
  onValidationError,
116
120
  fieldsRecord,
@@ -174,10 +178,19 @@ function sanitisePrefix(value) {
174
178
  * to surface somewhere — diagnostics if the consumer opted in, an error
175
179
  * otherwise — so the caller can route it through `onError` / an error
176
180
  * boundary rather than have validation quietly disappear.
181
+ *
182
+ * The `io` argument mirrors the prop on `<SchemaComponent>` and
183
+ * `<SchemaView>`. It determines which Zod entry point validates a
184
+ * codec: `safeEncode` for the OUTPUT side (the default, matching the
185
+ * renderer's default direction), `safeParse` for the INPUT side. For
186
+ * non-codec schemas the choice is irrelevant — both `safeEncode` and
187
+ * `safeParse` behave identically — so `safeParse` is used
188
+ * unconditionally.
177
189
  */
178
- function runValidation(zodSchema, jsonSchema, value, onDiagnostic) {
190
+ function runValidation(zodSchema, jsonSchema, value, io, onDiagnostic) {
179
191
  if (zodSchema !== void 0 && isObject(zodSchema)) {
180
- const validateFn = isCodecSchema(zodSchema) ? zodSchema.safeEncode : zodSchema.safeParse;
192
+ const resolvedIo = io ?? "output";
193
+ const validateFn = isCodecSchema(zodSchema) && resolvedIo === "output" ? zodSchema.safeEncode : zodSchema.safeParse;
181
194
  if (isCallable(validateFn)) {
182
195
  const result = validateFn(value);
183
196
  if (isObject(result) && "success" in result && result.success !== true) return result.error;
@@ -270,7 +283,7 @@ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange
270
283
  const handleChange = useCallback((nextFieldValue) => {
271
284
  const newRootValue = setNestedValue(value, path, nextFieldValue);
272
285
  if (validate) {
273
- const error = runValidation(zodSchema, jsonSchema, newRootValue);
286
+ const error = runValidation(zodSchema, jsonSchema, newRootValue, void 0);
274
287
  if (error !== void 0) onValidationError?.(error);
275
288
  }
276
289
  onChange?.(newRootValue);
@@ -330,26 +343,5 @@ function isFieldErrorCallback(value) {
330
343
  function isCallable(value) {
331
344
  return typeof value === "function";
332
345
  }
333
- /**
334
- * True when `value` is a Zod schema implemented as a codec.
335
- *
336
- * Detection looks for `"$ZodCodec"` in the schema's `_zod.traits`
337
- * Set, mirroring the runtime detection used inside
338
- * `core/adapter.ts` (`isCodecSchema` there) and Zod's own
339
- * `isTransforming` helper. Duplicated here rather than imported
340
- * because adapter.ts does not export the helper and is outside this
341
- * fix-cycle's owned files.
342
- *
343
- * TODO(round7-integration): replace with an `import` once
344
- * `isCodecSchema` is exported from `core/adapter.ts` (or promoted to
345
- * `core/guards.ts`) by a coordinating commit.
346
- */
347
- function isCodecSchema(value) {
348
- const zod = getProperty(value, "_zod");
349
- if (!isObject(zod)) return false;
350
- const traits = zod.traits;
351
- if (traits instanceof Set) return traits.has("$ZodCodec");
352
- return false;
353
- }
354
346
  //#endregion
355
347
  export { SchemaComponent, SchemaField, SchemaProvider, joinPath, registerWidget, renderField, sanitisePrefix };
@@ -1,37 +1,40 @@
1
1
  import { w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
2
  import { t as Diagnostic } from "../diagnostics-Cbwak-ZX.mjs";
3
- import { r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
3
+ import { i as SchemaIoSide } from "../adapter-MiEFkRVV.mjs";
4
+ import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
4
5
  import { RejectUnrepresentableZod } from "../core/typeInference.mjs";
5
- import { WidgetMap } from "./SchemaComponent.mjs";
6
+ import { InferredValue, WidgetMap } from "./SchemaComponent.mjs";
6
7
  import { ReactNode } from "react";
7
8
 
8
9
  //#region src/react/SchemaView.d.ts
9
- interface SchemaViewProps<T = unknown, Ref extends string | undefined = undefined> {
10
+ interface SchemaViewProps<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
10
11
  /**
11
12
  * Zod schema, JSON Schema object, or OpenAPI document.
12
13
  *
13
14
  * Subject to the same compile-time rejection of unrepresentable
14
- * Zod 4 types as {@link SchemaComponentProps.schema} — see
15
+ * Zod 4 types as the `schema` prop on `<SchemaComponent>` — see
15
16
  * {@link RejectUnrepresentableZod}.
16
17
  */
17
18
  schema: RejectUnrepresentableZod<T>;
18
19
  /** For OpenAPI: a ref string like "#/components/schemas/User". */
19
20
  ref?: Ref;
20
21
  /**
21
- * Current value to render.
22
- *
23
- * TYPE BOUNDARY NOTE: mirrors `SchemaComponentProps.value` kept
24
- * as `unknown` so the same boundary holds for both the editable
25
- * (`SchemaComponent`) and read-only (`SchemaView`) entry points.
26
- * The {@link InferredOutputValue} alias is the recommended way
27
- * for callers to narrow on the consumer side.
28
- *
29
- * TODO(round7-integration): promote to
30
- * `InferSchemaValue<T, Ref, "output">` alongside the matching
31
- * `SchemaComponent` change. See the type-boundary note on
32
- * `SchemaComponentProps.value` for the migration coordination.
22
+ * Which side of every transform / pipe / codec to render. Mirrors
23
+ * `<SchemaComponent io>`. Defaults to `"output"` — the inferred
24
+ * shape consumers receive from the server. Switch to `"input"`
25
+ * to render the INPUT side (omits `readOnly` properties for
26
+ * JSON Schema inputs, picks the input half of a `z.codec(...)`).
27
+ * Has no effect for plain JSON Schema or OpenAPI inputs without
28
+ * codec/transform constructs.
29
+ */
30
+ io?: Mode;
31
+ /**
32
+ * Current value to render. Typed against `InferSchemaValue<T,
33
+ * Ref, Mode>` so the prop tracks the schema's inferred shape for
34
+ * the chosen `io` direction. Falls back to `unknown` for runtime
35
+ * schemas where the value type cannot be statically inferred.
33
36
  */
34
- value?: unknown;
37
+ value?: InferredValue<T, Ref, undefined, Mode>;
35
38
  /** Per-field meta overrides. */
36
39
  fields?: Record<string, unknown>;
37
40
  /** Meta overrides applied to the root schema. */
@@ -64,9 +67,10 @@ interface SchemaViewProps<T = unknown, Ref extends string | undefined = undefine
64
67
  * Always renders in read-only mode. For editable forms, use
65
68
  * `<SchemaComponent>` with `"use client"`.
66
69
  */
67
- declare function SchemaView<T = unknown, Ref extends string | undefined = undefined>({
70
+ declare function SchemaView<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output">({
68
71
  schema: schemaInput,
69
72
  ref: refInput,
73
+ io,
70
74
  value,
71
75
  fields,
72
76
  meta: componentMeta,
@@ -76,6 +80,6 @@ declare function SchemaView<T = unknown, Ref extends string | undefined = undefi
76
80
  onDiagnostic,
77
81
  strict,
78
82
  idPrefix
79
- }: SchemaViewProps<T, Ref>): ReactNode;
83
+ }: SchemaViewProps<T, Ref, Mode>): ReactNode;
80
84
  //#endregion
81
85
  export { SchemaView, SchemaViewProps };
@@ -5,8 +5,8 @@ import { buildRenderProps, getRenderFunction, mergeResolvers } from "../core/ren
5
5
  import { walk } from "../core/walker.mjs";
6
6
  import { headlessResolver } from "./headless.mjs";
7
7
  import { joinPath, sanitisePrefix } from "./SchemaComponent.mjs";
8
- import { jsx } from "react/jsx-runtime";
9
8
  import { createElement, isValidElement, useId } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
10
  //#region src/react/SchemaView.tsx
11
11
  /**
12
12
  * React Server Component for read-only schema rendering.
@@ -42,7 +42,7 @@ import { createElement, isValidElement, useId } from "react";
42
42
  * Always renders in read-only mode. For editable forms, use
43
43
  * `<SchemaComponent>` with `"use client"`.
44
44
  */
45
- function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: componentMeta, description, resolver, widgets, onDiagnostic, strict, idPrefix }) {
45
+ function SchemaView({ schema: schemaInput, ref: refInput, io, value, fields, meta: componentMeta, description, resolver, widgets, onDiagnostic, strict, idPrefix }) {
46
46
  const generatedId = useId();
47
47
  const rootPath = idPrefix ?? sanitisePrefix(generatedId);
48
48
  const mergedMeta = {
@@ -58,7 +58,10 @@ function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: c
58
58
  let rootMeta;
59
59
  let rootDocument;
60
60
  try {
61
- const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 ? { diagnostics } : void 0);
61
+ const normalised = normaliseSchema(schemaInput, refInput, diagnostics !== void 0 || io !== void 0 ? {
62
+ ...diagnostics !== void 0 ? { diagnostics } : {},
63
+ ...io !== void 0 ? { io } : {}
64
+ } : void 0);
62
65
  jsonSchema = normalised.jsonSchema;
63
66
  rootMeta = normalised.rootMeta;
64
67
  rootDocument = normalised.rootDocument;
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
1
+ import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
2
2
 
3
3
  //#region src/react/headless.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { j as WalkedField } from "../types-BTB73MB8.mjs";
2
- import { l as RenderProps } from "../renderer-CXJ8y0qw.mjs";
2
+ import { l as RenderProps } from "../renderer-Ul9taFYp.mjs";
3
3
  import { ReactNode } from "react";
4
4
 
5
5
  //#region src/react/headlessRenderers.d.ts
@@ -7,8 +7,8 @@ import { fieldDomId, panelIdFor, tabIdFor } from "../core/idPath.mjs";
7
7
  import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
8
8
  import { displayJsonValue } from "../core/walkBuilders.mjs";
9
9
  import { buildAriaAttrs } from "./a11y.mjs";
10
- import { jsx, jsxs } from "react/jsx-runtime";
11
10
  import { isValidElement, useCallback, useEffect, useRef } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
12
  //#region src/react/headlessRenderers.tsx
13
13
  /**
14
14
  * Headless renderer functions — one per schema type.
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
1
+ import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
2
2
 
3
3
  //#region src/themes/mantine.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
1
+ import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
2
2
 
3
3
  //#region src/themes/mui.d.ts
4
4
  /**
@@ -2,8 +2,8 @@ import { isObject } from "../core/guards.mjs";
2
2
  import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
3
3
  import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
4
4
  import { headlessResolver } from "../react/headless.mjs";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
5
  import { isValidElement } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/themes/mui.tsx
8
8
  function ariaRequired(tree) {
9
9
  return { required: tree.isOptional === false };
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
1
+ import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
2
2
 
3
3
  //#region src/themes/radix.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
1
+ import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
2
2
 
3
3
  //#region src/themes/shadcn.d.ts
4
4
  declare const shadcnResolver: ComponentResolver;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-components",
3
- "version": "1.24.0",
3
+ "version": "1.26.1",
4
4
  "description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
5
5
  "type": "module",
6
6
  "exports": {
@@ -40,6 +40,7 @@
40
40
  "_test:e2e": "vitest run --project=e2e",
41
41
  "_test:coverage": "vitest run --project=unit --coverage",
42
42
  "_build": "tsdown && cp src/html/styles.css dist/html/styles.css",
43
+ "_typedoc": "typedoc",
43
44
  "typecheck": "turbo run _typecheck",
44
45
  "lint": "turbo run _lint",
45
46
  "lint:fix": "turbo run _lint:fix",
@@ -47,7 +48,8 @@
47
48
  "test:coverage": "turbo run _test:coverage",
48
49
  "check": "turbo run _check",
49
50
  "validate": "turbo run _validate",
50
- "build": "turbo run _build"
51
+ "build": "turbo run _build",
52
+ "typedoc": "turbo run _typedoc"
51
53
  },
52
54
  "keywords": [
53
55
  "react",
@@ -91,6 +93,8 @@
91
93
  "react-dom": "19.2.6",
92
94
  "tsdown": "0.22.0",
93
95
  "tslib": "2.8.1",
96
+ "typedoc": "0.28.19",
97
+ "typedoc-plugin-missing-exports": "4.1.3",
94
98
  "typescript": "6.0.3",
95
99
  "typescript-eslint": "8.59.2",
96
100
  "vitest": "4.1.5",
File without changes