schema-components 1.20.0 → 1.22.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.
Files changed (77) hide show
  1. package/README.md +1 -1
  2. package/dist/core/adapter.d.mts +28 -4
  3. package/dist/core/adapter.mjs +408 -71
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/constraints.mjs +0 -2
  6. package/dist/core/diagnostics.d.mts +1 -1
  7. package/dist/core/errors.d.mts +1 -1
  8. package/dist/core/errors.mjs +9 -15
  9. package/dist/core/fieldOrder.d.mts +1 -1
  10. package/dist/core/formats.d.mts +22 -1
  11. package/dist/core/formats.mjs +21 -0
  12. package/dist/core/limits.d.mts +2 -0
  13. package/dist/core/limits.mjs +23 -0
  14. package/dist/core/merge.d.mts +11 -2
  15. package/dist/core/merge.mjs +11 -0
  16. package/dist/core/normalise.d.mts +36 -4
  17. package/dist/core/normalise.mjs +2 -2
  18. package/dist/core/openapi30.d.mts +24 -1
  19. package/dist/core/openapi30.mjs +2 -2
  20. package/dist/core/ref.d.mts +1 -1
  21. package/dist/core/ref.mjs +35 -9
  22. package/dist/core/renderer.d.mts +1 -1
  23. package/dist/core/renderer.mjs +0 -2
  24. package/dist/core/swagger2.d.mts +1 -1
  25. package/dist/core/swagger2.mjs +1 -1
  26. package/dist/core/typeInference.d.mts +2 -2
  27. package/dist/core/types.d.mts +2 -2
  28. package/dist/core/types.mjs +1 -4
  29. package/dist/core/version.d.mts +1 -1
  30. package/dist/core/walkBuilders.d.mts +13 -5
  31. package/dist/core/walkBuilders.mjs +11 -3
  32. package/dist/core/walker.d.mts +1 -1
  33. package/dist/core/walker.mjs +110 -26
  34. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-D0QCYGv0.d.mts} +1 -1
  35. package/dist/{errors-C2iABcn9.d.mts → errors-DpFwqs5C.d.mts} +7 -11
  36. package/dist/html/a11y.d.mts +2 -2
  37. package/dist/html/a11y.mjs +10 -3
  38. package/dist/html/renderToHtml.d.mts +10 -3
  39. package/dist/html/renderToHtml.mjs +13 -3
  40. package/dist/html/renderToHtmlStream.d.mts +2 -2
  41. package/dist/html/renderers.d.mts +2 -2
  42. package/dist/html/renderers.mjs +1 -6
  43. package/dist/html/streamRenderers.d.mts +5 -4
  44. package/dist/html/streamRenderers.mjs +91 -30
  45. package/dist/limits-Cw5QZND8.d.mts +29 -0
  46. package/dist/{normalise-CMMEl4cd.mjs → normalise-DVEJQmF7.mjs} +791 -141
  47. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  48. package/dist/openapi/ApiLinks.d.mts +1 -1
  49. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  50. package/dist/openapi/ApiSecurity.d.mts +1 -1
  51. package/dist/openapi/ApiSecurity.mjs +127 -7
  52. package/dist/openapi/components.d.mts +175 -21
  53. package/dist/openapi/components.mjs +145 -21
  54. package/dist/openapi/parser.d.mts +1 -1
  55. package/dist/openapi/parser.mjs +74 -7
  56. package/dist/openapi/resolve.d.mts +70 -12
  57. package/dist/openapi/resolve.mjs +265 -42
  58. package/dist/react/SchemaComponent.d.mts +100 -35
  59. package/dist/react/SchemaComponent.mjs +66 -24
  60. package/dist/react/SchemaView.d.mts +3 -3
  61. package/dist/react/SchemaView.mjs +2 -2
  62. package/dist/react/fieldPath.d.mts +1 -1
  63. package/dist/react/headless.d.mts +1 -1
  64. package/dist/react/headless.mjs +1 -2
  65. package/dist/react/headlessRenderers.d.mts +3 -4
  66. package/dist/react/headlessRenderers.mjs +11 -31
  67. package/dist/{ref-C8JbwfiS.d.mts → ref-D-_JBZkF.d.mts} +7 -2
  68. package/dist/{renderer-SOIbJBtk.d.mts → renderer-BaRlQIuN.d.mts} +2 -2
  69. package/dist/themes/mantine.d.mts +1 -1
  70. package/dist/themes/mui.d.mts +1 -1
  71. package/dist/themes/radix.d.mts +1 -1
  72. package/dist/themes/shadcn.d.mts +1 -1
  73. package/dist/typeInference-DkcUHfaM.d.mts +982 -0
  74. package/dist/{types-C9zw9wbX.d.mts → types-BrRMV0en.d.mts} +15 -12
  75. package/package.json +1 -3
  76. package/dist/typeInference-CDoD_LZ_.d.mts +0 -533
  77. /package/dist/{version-D-u7aMfy.d.mts → version-D2jfdX6E.d.mts} +0 -0
@@ -1,7 +1,10 @@
1
1
  import { getProperty, isObject } from "../core/guards.mjs";
2
+ import "../core/limits.mjs";
3
+ import { emitDiagnostic } from "../core/diagnostics.mjs";
4
+ import { isPrototypePollutingKey } from "../core/uri.mjs";
2
5
  import { detectOpenApiVersion } from "../core/version.mjs";
3
- import { a as normaliseOpenApiSchemas } from "../normalise-CMMEl4cd.mjs";
4
- import { getParameters, getRequestBody, getResponses, listOperations, parseOpenApiDocument } from "./parser.mjs";
6
+ import { a as normaliseOpenApiSchemas } from "../normalise-DVEJQmF7.mjs";
7
+ import { getParameters, getRequestBody, getResponses, listOperations, listWebhooks, parseOpenApiDocument } from "./parser.mjs";
5
8
  //#region src/openapi/resolve.ts
6
9
  /**
7
10
  * OpenAPI document resolution and caching.
@@ -24,25 +27,173 @@ const docCache = /* @__PURE__ */ new WeakMap();
24
27
  * same form `<SchemaComponent>` does, keeping the OpenAPI components on
25
28
  * the same pipeline as the top-level adapter.
26
29
  *
27
- * The cache is keyed by the caller-supplied document so subsequent calls
28
- * with the same input bypass both normalisation and parsing.
30
+ * When `diagnostics` is supplied, normalisation events
31
+ * (`duplicate-body-parameter`, `dropped-swagger-feature`,
32
+ * `unknown-json-schema-dialect`, `divisible-by-conflict`,
33
+ * `relative-ref-resolved`, etc.) are forwarded to the sink. Passing
34
+ * diagnostics also bypasses the cache so each call observes the
35
+ * normalisation pipeline running against the supplied sink — caching
36
+ * would silently swallow every emission after the first.
37
+ *
38
+ * The cache is keyed by the caller-supplied document so subsequent
39
+ * cache-eligible calls with the same input bypass both normalisation
40
+ * and parsing.
29
41
  */
30
- function getParsed(doc) {
31
- const cached = docCache.get(doc);
32
- if (cached !== void 0) return cached;
42
+ function getParsed(doc, diagnostics) {
43
+ if (diagnostics === void 0) {
44
+ const cached = docCache.get(doc);
45
+ if (cached !== void 0) return cached;
46
+ }
33
47
  const version = detectOpenApiVersion(doc);
34
- const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version) : doc;
48
+ if (diagnostics !== void 0 && version?.major === 3 && docHasXmlAnywhere(doc)) emitDiagnostic(diagnostics, {
49
+ code: "dropped-swagger-feature",
50
+ message: `OpenAPI ${String(version.major)}.${String(version.minor)} xml Schema Object metadata is not rendered and will be ignored`,
51
+ pointer: "",
52
+ detail: {
53
+ feature: "xml",
54
+ source: "openapi-3.x"
55
+ }
56
+ });
57
+ const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version, diagnostics) : doc;
58
+ if (diagnostics !== void 0) {
59
+ validateSecuritySchemeTypes(normalisedDoc, diagnostics);
60
+ detectUnsupportedCrossSchemaRefs(normalisedDoc, diagnostics);
61
+ }
35
62
  const parsed = parseOpenApiDocument(normalisedDoc);
36
- docCache.set(doc, parsed);
37
- if (normalisedDoc !== doc) docCache.set(normalisedDoc, parsed);
63
+ if (diagnostics === void 0) {
64
+ docCache.set(doc, parsed);
65
+ if (normalisedDoc !== doc) docCache.set(normalisedDoc, parsed);
66
+ }
38
67
  return parsed;
39
68
  }
40
69
  /**
41
- * Coerce an unknown value to a record, returning an empty record
42
- * for non-objects.
70
+ * Coerce an unknown value to a record, returning `undefined` when the
71
+ * value is not a plain object. Callers MUST handle the `undefined` case
72
+ * explicitly — typically by rendering a "doc not an object" diagnostic
73
+ * and short-circuiting, never by silently substituting `{}`.
74
+ *
75
+ * A previous implementation fell back to `{}` for non-objects, which
76
+ * masked configuration mistakes (passing a string, `null`, an array, or
77
+ * `undefined` as the OpenAPI document) as an empty document with no
78
+ * operations.
43
79
  */
44
80
  function toDoc(value) {
45
- return isObject(value) ? value : {};
81
+ return isObject(value) ? value : void 0;
82
+ }
83
+ /**
84
+ * Known security scheme types per the OpenAPI 3.0/3.1 specification.
85
+ * `mutualTLS` was added in OpenAPI 3.1. Unknown values surface a
86
+ * `unknown-security-scheme-type` diagnostic so authors notice typos
87
+ * (e.g. `mutalTLS`) that would otherwise render with no warning.
88
+ */
89
+ const KNOWN_SECURITY_SCHEME_TYPES = new Set([
90
+ "apiKey",
91
+ "http",
92
+ "oauth2",
93
+ "openIdConnect",
94
+ "mutualTLS"
95
+ ]);
96
+ /**
97
+ * Validate every `components.securitySchemes.<name>.type` against the
98
+ * canonical OpenAPI security scheme types and emit
99
+ * `unknown-security-scheme-type` for each entry whose type is not
100
+ * recognised. Runs after normalisation so Swagger 2.0 documents (which
101
+ * are already translated to OAS 3.x shapes by `translateSwaggerSecurityScheme`)
102
+ * are validated alongside native 3.x documents.
103
+ */
104
+ function validateSecuritySchemeTypes(doc, diagnostics) {
105
+ const components = doc.components;
106
+ if (!isObject(components)) return;
107
+ const schemes = components.securitySchemes;
108
+ if (!isObject(schemes)) return;
109
+ for (const [name, scheme] of Object.entries(schemes)) {
110
+ if (!isObject(scheme)) continue;
111
+ const type = scheme.type;
112
+ if (typeof type !== "string") {
113
+ emitDiagnostic(diagnostics, {
114
+ code: "unknown-security-scheme-type",
115
+ message: `Security scheme "${name}" has no type or a non-string type`,
116
+ pointer: `/components/securitySchemes/${name}/type`,
117
+ detail: {
118
+ name,
119
+ type
120
+ }
121
+ });
122
+ continue;
123
+ }
124
+ if (!KNOWN_SECURITY_SCHEME_TYPES.has(type)) emitDiagnostic(diagnostics, {
125
+ code: "unknown-security-scheme-type",
126
+ message: `Security scheme "${name}" declares unknown type "${type}"`,
127
+ pointer: `/components/securitySchemes/${name}/type`,
128
+ detail: {
129
+ name,
130
+ type
131
+ }
132
+ });
133
+ }
134
+ }
135
+ /**
136
+ * Detect any `$ref` strings that survived normalisation in a non-
137
+ * fragment shape (anything not starting with `#/` or `#`). After
138
+ * `normaliseOpenApiSchemas` runs `resolveRelativeRefs`, every relative
139
+ * `$ref` within a Schema Object is rewritten to an absolute fragment.
140
+ * Refs that *cross* Schema Object boundaries — for example, a relative
141
+ * ref inside one component schema pointing into another via a sibling
142
+ * `$id` — cannot be resolved by the current pipeline (this is a
143
+ * documented limitation; see the JSDoc on this function).
144
+ *
145
+ * Emit a single diagnostic per offending ref so consumers notice
146
+ * silently broken references rather than discovering them only when
147
+ * the walker fails to render the target.
148
+ *
149
+ * NOTE: We can't determine "crossing" cleanly from the parser alone —
150
+ * doing so would require modelling every Schema Object's $id scope.
151
+ * As a pragmatic approximation, any surviving non-`#`-prefixed `$ref`
152
+ * is treated as cross-Schema-Object unsupported. False positives
153
+ * (legitimate external refs that the consumer planned to bundle later)
154
+ * are still useful — they confirm an unresolved reference is present.
155
+ */
156
+ function detectUnsupportedCrossSchemaRefs(doc, diagnostics) {
157
+ const seenRefs = /* @__PURE__ */ new Set();
158
+ const walk = (node, pointer) => {
159
+ if (Array.isArray(node)) {
160
+ for (const [index, item] of node.entries()) walk(item, `${pointer}/${String(index)}`);
161
+ return;
162
+ }
163
+ if (!isObject(node)) return;
164
+ const ref = node.$ref;
165
+ if (typeof ref === "string" && !ref.startsWith("#") && !seenRefs.has(ref)) {
166
+ seenRefs.add(ref);
167
+ emitDiagnostic(diagnostics, {
168
+ code: "cross-schema-relative-ref-unsupported",
169
+ message: `Relative \`$ref\` "${ref}" was not resolved during normalisation; cross-Schema-Object relative refs are not currently supported`,
170
+ pointer,
171
+ detail: { ref }
172
+ });
173
+ return;
174
+ }
175
+ for (const [key, value] of Object.entries(node)) walk(value, `${pointer}/${key}`);
176
+ };
177
+ walk(doc, "");
178
+ }
179
+ /**
180
+ * Recursively check whether any node in an OpenAPI document carries an
181
+ * `xml` annotation. Walks both objects and arrays so the check works
182
+ * for schemas in `components/schemas`, inline `paths`/`webhooks`
183
+ * schemas, request bodies, responses, headers, and parameters. Used
184
+ * by `getParsed` to surface the dropped-feature diagnostic for OAS
185
+ * 3.0/3.1 — the Swagger 2.0 path has its own detection in
186
+ * `swagger2.ts`.
187
+ */
188
+ function docHasXmlAnywhere(node) {
189
+ if (Array.isArray(node)) {
190
+ for (const item of node) if (docHasXmlAnywhere(item)) return true;
191
+ return false;
192
+ }
193
+ if (!isObject(node)) return false;
194
+ if ("xml" in node && isObject(node.xml)) return true;
195
+ for (const value of Object.values(node)) if (docHasXmlAnywhere(value)) return true;
196
+ return false;
46
197
  }
47
198
  /**
48
199
  * Look up a Path Item Object on the (already-normalised) parsed document,
@@ -53,22 +204,46 @@ function toDoc(value) {
53
204
  * Implemented inside `resolve.ts` to avoid touching `parser.ts` while
54
205
  * still surfacing path-item-level metadata to the React layer.
55
206
  */
56
- function lookupPathItemNode(parsed, path) {
57
- return resolvePathItemNode(parsed, getProperty(getProperty(parsed.doc, "paths"), path));
207
+ function lookupPathItemNode(parsed, path, diagnostics) {
208
+ const fromPaths = resolvePathItemNode(parsed, getProperty(getProperty(parsed.doc, "paths"), path), diagnostics);
209
+ if (fromPaths !== void 0) return fromPaths;
210
+ return resolvePathItemNode(parsed, getProperty(getProperty(parsed.doc, "webhooks"), path), diagnostics);
58
211
  }
59
- function resolvePathItemNode(parsed, pathItem) {
212
+ function resolvePathItemNode(parsed, pathItem, diagnostics) {
60
213
  if (!isObject(pathItem)) return void 0;
61
- const ref = getProperty(pathItem, "$ref");
62
- if (typeof ref !== "string") return pathItem;
63
- if (!ref.startsWith("#/")) return pathItem;
64
- const parts = ref.slice(2).split("/");
65
- let current = parsed.doc;
66
- for (const part of parts) {
67
- if (!isObject(current)) return void 0;
68
- const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
69
- current = current[decoded];
214
+ const visited = /* @__PURE__ */ new Set();
215
+ let current = pathItem;
216
+ for (let hop = 0; hop < 8; hop++) {
217
+ const ref = getProperty(current, "$ref");
218
+ if (typeof ref !== "string") return current;
219
+ if (!ref.startsWith("#/")) return current;
220
+ if (visited.has(ref)) {
221
+ emitDiagnostic(diagnostics, {
222
+ code: "cyclic-path-item-ref",
223
+ message: `Cyclic Path Item Object $ref "${ref}"`,
224
+ pointer: ref,
225
+ detail: { ref }
226
+ });
227
+ return;
228
+ }
229
+ visited.add(ref);
230
+ const parts = ref.slice(2).split("/");
231
+ let node = parsed.doc;
232
+ for (const part of parts) {
233
+ if (!isObject(node)) return void 0;
234
+ const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
235
+ if (isPrototypePollutingKey(decoded)) return void 0;
236
+ node = node[decoded];
237
+ }
238
+ if (!isObject(node)) return current;
239
+ current = node;
70
240
  }
71
- return isObject(current) ? current : pathItem;
241
+ emitDiagnostic(diagnostics, {
242
+ code: "path-item-ref-too-deep",
243
+ message: `Path Item Object $ref chain exceeded ${String(8)} hops`,
244
+ pointer: "",
245
+ detail: { maxHops: 8 }
246
+ });
72
247
  }
73
248
  function extractPathItemInfo(pathItem) {
74
249
  const summary = pathItem.summary;
@@ -79,14 +254,19 @@ function extractPathItemInfo(pathItem) {
79
254
  };
80
255
  }
81
256
  /**
82
- * Resolve an operation from an OpenAPI document by path and method.
83
- * Throws if the operation is not found.
257
+ * Resolve an operation against an already-parsed document. Throws if
258
+ * the operation is not found.
259
+ *
260
+ * Used by callers that have already obtained a parsed document via
261
+ * {@link getParsed} — most importantly the React components, which
262
+ * supply `diagnostics` to `getParsed` and must avoid re-running the
263
+ * normalisation pipeline (every re-run would emit each diagnostic
264
+ * again into the sink).
84
265
  */
85
- function resolveOperation(doc, path, method) {
86
- const parsed = getParsed(doc);
87
- const operation = listOperations(parsed).find((op) => op.path === path && op.method === method);
266
+ function resolveOperationFromParsed(parsed, path, method, diagnostics) {
267
+ const pathItemNode = lookupPathItemNode(parsed, path, diagnostics);
268
+ const operation = [...listOperations(parsed), ...listWebhooks(parsed).flatMap((w) => w.operations)].find((op) => op.path === path && op.method === method);
88
269
  if (operation === void 0) throw new Error(`Operation not found: ${method.toUpperCase()} ${path}`);
89
- const pathItemNode = lookupPathItemNode(parsed, path);
90
270
  if (pathItemNode === void 0) throw new Error(`Path item missing for ${method.toUpperCase()} ${path}`);
91
271
  return {
92
272
  operation,
@@ -97,30 +277,73 @@ function resolveOperation(doc, path, method) {
97
277
  };
98
278
  }
99
279
  /**
280
+ * Resolve an operation from an OpenAPI document by path and method.
281
+ * Throws if the operation is not found.
282
+ *
283
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
284
+ * events surface to the caller's sink.
285
+ */
286
+ function resolveOperation(doc, path, method, diagnostics) {
287
+ return resolveOperationFromParsed(getParsed(doc, diagnostics), path, method, diagnostics);
288
+ }
289
+ /**
290
+ * Resolve parameters against an already-parsed document. See
291
+ * {@link resolveOperationFromParsed} for the rationale.
292
+ */
293
+ function resolveParametersFromParsed(parsed, path, method) {
294
+ return getParameters(parsed, path, method);
295
+ }
296
+ /**
100
297
  * Resolve parameters for an operation. Returns empty array if none.
298
+ *
299
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
300
+ * events surface to the caller's sink.
101
301
  */
102
- function resolveParameters(doc, path, method) {
103
- return getParameters(getParsed(doc), path, method);
302
+ function resolveParameters(doc, path, method, diagnostics) {
303
+ return resolveParametersFromParsed(getParsed(doc, diagnostics), path, method);
304
+ }
305
+ /**
306
+ * Resolve a request body against an already-parsed document. See
307
+ * {@link resolveOperationFromParsed} for the rationale.
308
+ */
309
+ function resolveRequestBodyFromParsed(parsed, path, method) {
310
+ return getRequestBody(parsed, path, method);
104
311
  }
105
312
  /**
106
313
  * Resolve request body for an operation. Returns undefined if none.
314
+ *
315
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
316
+ * events surface to the caller's sink.
107
317
  */
108
- function resolveRequestBody(doc, path, method) {
109
- return getRequestBody(getParsed(doc), path, method);
318
+ function resolveRequestBody(doc, path, method, diagnostics) {
319
+ return resolveRequestBodyFromParsed(getParsed(doc, diagnostics), path, method);
110
320
  }
111
321
  /**
112
- * Resolve a specific response by status code. Throws if not found.
322
+ * Resolve a specific response against an already-parsed document. See
323
+ * {@link resolveOperationFromParsed} for the rationale.
113
324
  */
114
- function resolveResponse(doc, path, method, statusCode) {
115
- const response = getResponses(getParsed(doc), path, method).find((r) => r.statusCode === statusCode);
325
+ function resolveResponseFromParsed(parsed, path, method, statusCode) {
326
+ const response = getResponses(parsed, path, method).find((r) => r.statusCode === statusCode);
116
327
  if (response === void 0) throw new Error(`Response not found: ${statusCode}`);
117
328
  return response;
118
329
  }
119
330
  /**
331
+ * Resolve a specific response by status code. Throws if not found.
332
+ *
333
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
334
+ * events surface to the caller's sink.
335
+ */
336
+ function resolveResponse(doc, path, method, statusCode, diagnostics) {
337
+ return resolveResponseFromParsed(getParsed(doc, diagnostics), path, method, statusCode);
338
+ }
339
+ /**
120
340
  * Resolve all responses for an operation.
341
+ *
342
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
343
+ * events surface to the caller's sink.
121
344
  */
122
- function resolveResponses(doc, path, method) {
123
- return getResponses(getParsed(doc), path, method);
345
+ function resolveResponses(doc, path, method, diagnostics) {
346
+ return getResponses(getParsed(doc, diagnostics), path, method);
124
347
  }
125
348
  //#endregion
126
- export { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, resolveResponses, toDoc };
349
+ export { getParsed, resolveOperation, resolveOperationFromParsed, resolveParameters, resolveParametersFromParsed, resolveRequestBody, resolveRequestBodyFromParsed, resolveResponse, resolveResponseFromParsed, resolveResponses, toDoc };
@@ -1,8 +1,8 @@
1
- import { M as WalkedField, T as SchemaMeta, d as FieldOverrides, u as FieldOverride } from "../types-C9zw9wbX.mjs";
2
- import { t as Diagnostic } from "../diagnostics-CbBPsxSt.mjs";
3
- import { t as SchemaError } from "../errors-C2iABcn9.mjs";
4
- import { l as RenderProps, r as ComponentResolver } from "../renderer-SOIbJBtk.mjs";
5
- import { c as PathOfType, l as RejectUnrepresentableZod, n as FromJSONSchema, u as ResolveOpenAPIRef } from "../typeInference-CDoD_LZ_.mjs";
1
+ import { d as FieldOverrides, j as WalkedField, u as FieldOverride, w as SchemaMeta } from "../types-BrRMV0en.mjs";
2
+ import { t as Diagnostic } from "../diagnostics-D0QCYGv0.mjs";
3
+ import { t as SchemaError } from "../errors-DpFwqs5C.mjs";
4
+ import { l as RenderProps, r as ComponentResolver } from "../renderer-BaRlQIuN.mjs";
5
+ import { d as RejectUnrepresentableZod, f as ResolveOpenAPIRef, i as FromJSONSchemaMode, p as TypeAtPath, r as FromJSONSchema, u as PathOfType } from "../typeInference-DkcUHfaM.mjs";
6
6
  import { z } from "zod";
7
7
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
8
8
  import { ReactNode } from "react";
@@ -39,7 +39,54 @@ declare function registerWidget(name: string, render: (props: RenderProps) => un
39
39
  type InferFields<T, Ref extends string | undefined> = T extends z.ZodType ? FieldOverrides<z.infer<T>> : T extends {
40
40
  openapi: unknown;
41
41
  } ? Ref extends string ? FieldOverrides<ResolveOpenAPIRef<T & Record<string, unknown>, Ref>> : Record<string, FieldOverride> : T extends object ? unknown extends FromJSONSchema<T> ? Record<string, FieldOverride> : FieldOverrides<FromJSONSchema<T>> : Record<string, FieldOverride>;
42
- interface SchemaComponentProps<T = unknown, Ref extends string | undefined = undefined> {
42
+ /**
43
+ * Infer the data type carried by the schema input.
44
+ *
45
+ * Mirrors {@link InferFields}'s dispatch order: Zod schema → `z.infer`,
46
+ * OpenAPI doc + ref → `ResolveOpenAPIRef`, plain JSON Schema object →
47
+ * `FromJSONSchema`, everything else → `unknown`. The `Mode` parameter
48
+ * is plumbed through to `FromJSONSchema` / `ResolveOpenAPIRef` so
49
+ * `readOnly` / `writeOnly` keywords participate in the inferred
50
+ * object shape — `"output"` for the rendered value, `"input"` for the
51
+ * `onChange` argument.
52
+ *
53
+ * When the schema's value type cannot be statically determined (e.g.
54
+ * a runtime `Record<string, unknown>` JSON Schema, or an OpenAPI doc
55
+ * without a ref), the result falls back to `unknown` so callers can
56
+ * still supply arbitrary values.
57
+ */
58
+ type InferSchemaValue<T, Ref extends string | undefined, Mode extends FromJSONSchemaMode> = T extends z.ZodType ? Mode extends "input" ? z.input<T> : z.output<T> : T extends {
59
+ openapi: unknown;
60
+ } ? Ref extends string ? ResolveOpenAPIRef<T & Record<string, unknown>, Ref, [], Mode> : unknown : T extends object ? FromJSONSchema<T, Record<string, never>, [], Mode> | (unknown extends FromJSONSchema<T> ? unknown : never) extends infer V ? V : unknown : unknown;
61
+ /**
62
+ * Narrow an inferred value type to the sub-shape at `P`, or return
63
+ * the original value type when `P` is `undefined` (no path supplied).
64
+ */
65
+ type NarrowAtPath<V, P extends string | undefined> = P extends string ? TypeAtPath<V, P> : V;
66
+ /**
67
+ * 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
+ *
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.
85
+ */
86
+ 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. */
88
+ 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> {
43
90
  /**
44
91
  * Zod schema, JSON Schema object, or OpenAPI document.
45
92
  *
@@ -53,9 +100,51 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
53
100
  schema: RejectUnrepresentableZod<T>;
54
101
  /** For OpenAPI: a ref string like "#/components/schemas/User" or "/users/post". */
55
102
  ref?: Ref;
56
- /** Current value to render. */
103
+ /**
104
+ * Optional dot-separated path used purely for type narrowing.
105
+ * When the schema is typed, the path is restricted to the valid
106
+ * dot-paths reachable through the schema's inferred value.
107
+ *
108
+ * RUNTIME CAVEAT: this prop is currently type-level only. The
109
+ * render pipeline still operates on the root schema; sub-path
110
+ * value resolution is provided by the dedicated `<SchemaField>`
111
+ * component, which already implements `resolvePath` /
112
+ * `setNestedValue`. The `path` prop here documents intent at the
113
+ * type level (and prepares the API surface) so consumers can
114
+ * declare narrow typed wrappers without runtime regressions. Use
115
+ * `<SchemaField>` when a sub-path render is required at runtime.
116
+ */
117
+ path?: P;
118
+ /**
119
+ * Current value to render.
120
+ *
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:
126
+ *
127
+ * ```tsx
128
+ * const user: InferredOutputValue<typeof userSchema> = { ... };
129
+ * <SchemaComponent schema={userSchema} value={user} readOnly />
130
+ * ```
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
+ */
57
136
  value?: unknown;
58
- /** Called when the value changes (editable fields). */
137
+ /**
138
+ * Called when the value changes (editable fields).
139
+ *
140
+ * TYPE BOUNDARY NOTE: the parameter is typed `unknown` rather
141
+ * than the inferred input shape because the walker pipeline only
142
+ * propagates `unknown` values and a narrow contravariant callback
143
+ * signature is not assignable from an `unknown`-emitting source
144
+ * without an unsafe boundary cast. The {@link InferredInputValue}
145
+ * alias is the recommended way for callers to narrow on the
146
+ * consumer side — `onChange={(v) => { const u = v as InferredInputValue<typeof schema>; ... }}`.
147
+ */
59
148
  onChange?: (value: unknown) => void;
60
149
  /** Run schema.safeParse() on change and surface errors via onValidationError. */
61
150
  validate?: boolean;
@@ -87,31 +176,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
87
176
  */
88
177
  idPrefix?: string;
89
178
  }
90
- declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined>({
91
- schema: schemaInput,
92
- ref: refInput,
93
- value,
94
- onChange,
95
- validate,
96
- onValidationError,
97
- onError,
98
- onDiagnostic,
99
- strict,
100
- fields,
101
- meta: componentMeta,
102
- readOnly,
103
- writeOnly,
104
- description,
105
- widgets: instanceWidgets,
106
- idPrefix
107
- }: SchemaComponentProps<T, Ref>): ReactNode;
108
- /**
109
- * Default root-path sentinel used when no `idPrefix` is supplied AND the
110
- * component is rendered outside a React tree (e.g. server-side bundling
111
- * test harnesses). Production callers receive a `useId()`-derived prefix
112
- * that is unique per instance.
113
- */
114
- declare const ROOT_PATH = "root";
179
+ declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined>(props: SchemaComponentProps<T, Ref, P>): ReactNode;
115
180
  /**
116
181
  * Append a child path suffix to a parent path. When the suffix is omitted
117
182
  * (e.g. transparent wrappers like union options), the parent path is
@@ -154,7 +219,7 @@ interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefin
154
219
  validate?: boolean;
155
220
  onValidationError?: (error: unknown) => void;
156
221
  }
157
- declare function SchemaField<T = unknown, Ref extends string | undefined = undefined, P extends string = string>({
222
+ declare function SchemaField<T = unknown, Ref extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)>({
158
223
  path,
159
224
  schema: schemaInput,
160
225
  ref: refInput,
@@ -165,4 +230,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
165
230
  onValidationError
166
231
  }: SchemaFieldProps<T, Ref, P>): ReactNode;
167
232
  //#endregion
168
- export { ROOT_PATH, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };
233
+ export { InferredInputValue, InferredOutputValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };