schema-components 1.21.0 → 1.23.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 (91) hide show
  1. package/README.md +3 -1
  2. package/dist/core/adapter.d.mts +115 -4
  3. package/dist/core/adapter.mjs +405 -75
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/constraints.mjs +0 -7
  6. package/dist/core/cssClasses.d.mts +52 -0
  7. package/dist/core/cssClasses.mjs +51 -0
  8. package/dist/core/diagnostics.d.mts +1 -1
  9. package/dist/core/errors.d.mts +1 -1
  10. package/dist/core/errors.mjs +5 -13
  11. package/dist/core/fieldOrder.d.mts +1 -1
  12. package/dist/core/formats.d.mts +30 -2
  13. package/dist/core/formats.mjs +33 -1
  14. package/dist/core/idPath.d.mts +54 -0
  15. package/dist/core/idPath.mjs +66 -0
  16. package/dist/core/limits.d.mts +2 -0
  17. package/dist/core/limits.mjs +23 -0
  18. package/dist/core/merge.d.mts +10 -1
  19. package/dist/core/merge.mjs +49 -10
  20. package/dist/core/normalise.d.mts +40 -3
  21. package/dist/core/normalise.mjs +2 -2
  22. package/dist/core/openapi30.d.mts +15 -1
  23. package/dist/core/openapi30.mjs +2 -2
  24. package/dist/core/openapiConstants.d.mts +67 -0
  25. package/dist/core/openapiConstants.mjs +90 -0
  26. package/dist/core/ref.d.mts +2 -2
  27. package/dist/core/ref.mjs +85 -6
  28. package/dist/core/refChain.d.mts +70 -0
  29. package/dist/core/refChain.mjs +44 -0
  30. package/dist/core/renderer.d.mts +1 -1
  31. package/dist/core/renderer.mjs +0 -2
  32. package/dist/core/swagger2.d.mts +1 -1
  33. package/dist/core/swagger2.mjs +1 -1
  34. package/dist/core/typeInference.d.mts +982 -2
  35. package/dist/core/types.d.mts +2 -2
  36. package/dist/core/types.mjs +1 -4
  37. package/dist/core/unionMatch.d.mts +36 -0
  38. package/dist/core/unionMatch.mjs +53 -0
  39. package/dist/core/version.d.mts +1 -1
  40. package/dist/core/version.mjs +29 -17
  41. package/dist/core/walkBuilders.d.mts +23 -4
  42. package/dist/core/walkBuilders.mjs +27 -7
  43. package/dist/core/walker.d.mts +1 -1
  44. package/dist/core/walker.mjs +123 -47
  45. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-BS2kaUyE.d.mts} +1 -1
  46. package/dist/{errors-QEwOtQAA.d.mts → errors-g_MCTQel.d.mts} +10 -16
  47. package/dist/html/a11y.d.mts +9 -4
  48. package/dist/html/a11y.mjs +10 -12
  49. package/dist/html/renderToHtml.d.mts +10 -3
  50. package/dist/html/renderToHtml.mjs +13 -3
  51. package/dist/html/renderToHtmlStream.d.mts +2 -2
  52. package/dist/html/renderToHtmlStream.mjs +12 -1
  53. package/dist/html/renderers.d.mts +43 -8
  54. package/dist/html/renderers.mjs +136 -116
  55. package/dist/html/streamRenderers.d.mts +6 -6
  56. package/dist/html/streamRenderers.mjs +129 -89
  57. package/dist/limits-Cw5QZND8.d.mts +29 -0
  58. package/dist/{normalise-DaSrnr8g.mjs → normalise-DCYp06Sr.mjs} +770 -227
  59. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  60. package/dist/openapi/ApiLinks.d.mts +1 -1
  61. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  62. package/dist/openapi/ApiSecurity.d.mts +1 -1
  63. package/dist/openapi/ApiSecurity.mjs +16 -2
  64. package/dist/openapi/components.d.mts +234 -23
  65. package/dist/openapi/components.mjs +183 -52
  66. package/dist/openapi/parser.d.mts +9 -8
  67. package/dist/openapi/parser.mjs +252 -70
  68. package/dist/openapi/resolve.d.mts +31 -15
  69. package/dist/openapi/resolve.mjs +260 -40
  70. package/dist/react/SchemaComponent.d.mts +126 -36
  71. package/dist/react/SchemaComponent.mjs +95 -57
  72. package/dist/react/SchemaView.d.mts +30 -10
  73. package/dist/react/SchemaView.mjs +2 -2
  74. package/dist/react/a11y.d.mts +21 -0
  75. package/dist/react/a11y.mjs +24 -0
  76. package/dist/react/fieldPath.d.mts +1 -1
  77. package/dist/react/headless.d.mts +1 -1
  78. package/dist/react/headless.mjs +1 -2
  79. package/dist/react/headlessRenderers.d.mts +9 -11
  80. package/dist/react/headlessRenderers.mjs +51 -102
  81. package/dist/{ref-si8ViYun.d.mts → ref-DjLEKa_E.d.mts} +38 -3
  82. package/dist/{renderer-DI6ZYf7a.d.mts → renderer-CXJ8y0qw.d.mts} +2 -2
  83. package/dist/themes/mantine.d.mts +1 -1
  84. package/dist/themes/mui.d.mts +1 -1
  85. package/dist/themes/radix.d.mts +1 -1
  86. package/dist/themes/shadcn.d.mts +1 -1
  87. package/dist/themes/shadcn.mjs +2 -1
  88. package/dist/{types-BnxPEElk.d.mts → types-BTB73MB8.d.mts} +35 -14
  89. package/dist/{version-D-u7aMfy.d.mts → version-BFTVLsdb.d.mts} +7 -1
  90. package/package.json +1 -3
  91. package/dist/typeInference-Bxw3NOG1.d.mts +0 -647
@@ -1,8 +1,11 @@
1
1
  import { getProperty, isObject } from "../core/guards.mjs";
2
+ import "../core/limits.mjs";
3
+ import { emitDiagnostic } from "../core/diagnostics.mjs";
2
4
  import { isPrototypePollutingKey } from "../core/uri.mjs";
3
5
  import { detectOpenApiVersion } from "../core/version.mjs";
4
- import { a as normaliseOpenApiSchemas } from "../normalise-DaSrnr8g.mjs";
5
- import { getParameters, getRequestBody, getResponses, listOperations, parseOpenApiDocument } from "./parser.mjs";
6
+ import { o as normaliseOpenApiSchemas, r as documentContainsKeyword } from "../normalise-DCYp06Sr.mjs";
7
+ import { resolveRefChain } from "../core/refChain.mjs";
8
+ import { getParameters, getRequestBody, getResponses, listOperations, listWebhooks, parseOpenApiDocument } from "./parser.mjs";
6
9
  //#region src/openapi/resolve.ts
7
10
  /**
8
11
  * OpenAPI document resolution and caching.
@@ -25,38 +28,205 @@ const docCache = /* @__PURE__ */ new WeakMap();
25
28
  * same form `<SchemaComponent>` does, keeping the OpenAPI components on
26
29
  * the same pipeline as the top-level adapter.
27
30
  *
28
- * When `diagnostics` is supplied, normalisation events
29
- * (`duplicate-body-parameter`, `dropped-swagger-feature`,
30
- * `unknown-json-schema-dialect`, `divisible-by-conflict`,
31
- * `relative-ref-resolved`, etc.) are forwarded to the sink. Passing
32
- * diagnostics also bypasses the cache so each call observes the
33
- * normalisation pipeline running against the supplied sink — caching
34
- * would silently swallow every emission after the first.
31
+ * ### Caching and diagnostics
35
32
  *
36
- * The cache is keyed by the caller-supplied document so subsequent
37
- * cache-eligible calls with the same input bypass both normalisation
38
- * and parsing.
33
+ * Normalisation runs at most once per document identity. The full set
34
+ * of doc-level diagnostics emitted during that single run is captured
35
+ * into the cache alongside the parsed result. Each caller-supplied
36
+ * sink receives the captured diagnostics exactly once per cached
37
+ * entry, no matter how many times `getParsed` is called with that
38
+ * `(doc, sink)` pair.
39
+ *
40
+ * The previous implementation bypassed the cache whenever
41
+ * `diagnostics` was supplied and re-ran the entire normalisation
42
+ * pipeline against the new sink. That fired every doc-level
43
+ * diagnostic once per call, so a parent like `ApiWebhooks` that
44
+ * renders `ApiWebhook` per webhook entry caused N-fold emission of a
45
+ * single real cause. With the new strategy, cardinality stays at one
46
+ * per real cause regardless of how many child renders share the
47
+ * sink.
48
+ *
49
+ * Strict mode is treated as a per-call invariant — see
50
+ * {@link replayCapturedDiagnostics} for the rationale.
39
51
  */
40
52
  function getParsed(doc, diagnostics) {
41
- if (diagnostics === void 0) {
42
- const cached = docCache.get(doc);
43
- if (cached !== void 0) return cached;
53
+ let cached = docCache.get(doc);
54
+ if (cached === void 0) {
55
+ cached = buildCachedParse(doc);
56
+ docCache.set(doc, cached);
57
+ if (cached.parsed.doc !== doc) docCache.set(cached.parsed.doc, cached);
44
58
  }
59
+ if (diagnostics?.diagnostics !== void 0 || diagnostics?.strict === true) replayCapturedDiagnostics(cached, diagnostics);
60
+ return cached.parsed;
61
+ }
62
+ /**
63
+ * Run the normalisation, validation, and parse pipeline against a doc
64
+ * once, capturing every emitted diagnostic into a private array. The
65
+ * private array becomes the source of truth for later replay through
66
+ * caller-supplied sinks.
67
+ *
68
+ * The internal capturing sink does NOT set `strict`. Letting strict
69
+ * mode throw mid-walk would leave the cache empty and force every
70
+ * subsequent caller to re-run the pipeline from scratch — and the
71
+ * defect this caching strategy fixes was precisely that kind of
72
+ * re-running. Strict is enforced instead during replay
73
+ * (see {@link replayCapturedDiagnostics}).
74
+ */
75
+ function buildCachedParse(doc) {
76
+ const captured = [];
77
+ const captureOpts = { diagnostics: (d) => captured.push(d) };
45
78
  const version = detectOpenApiVersion(doc);
46
- const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version, diagnostics) : doc;
47
- const parsed = parseOpenApiDocument(normalisedDoc);
48
- if (diagnostics === void 0) {
49
- docCache.set(doc, parsed);
50
- if (normalisedDoc !== doc) docCache.set(normalisedDoc, parsed);
51
- }
52
- return parsed;
79
+ if (version?.major === 3 && documentContainsKeyword(doc, "xml")) emitDiagnostic(captureOpts, {
80
+ code: "dropped-swagger-feature",
81
+ message: `OpenAPI ${String(version.major)}.${String(version.minor)} xml Schema Object metadata is not rendered and will be ignored`,
82
+ pointer: "",
83
+ detail: {
84
+ feature: "xml",
85
+ source: "openapi-3.x"
86
+ }
87
+ });
88
+ const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version, captureOpts) : doc;
89
+ validateSecuritySchemeTypes(normalisedDoc, captureOpts);
90
+ detectUnsupportedCrossSchemaRefs(normalisedDoc, captureOpts);
91
+ return {
92
+ parsed: parseOpenApiDocument(normalisedDoc),
93
+ diagnostics: captured,
94
+ notifiedSinks: /* @__PURE__ */ new WeakSet()
95
+ };
53
96
  }
54
97
  /**
55
- * Coerce an unknown value to a record, returning an empty record
56
- * for non-objects.
98
+ * Replay each captured diagnostic through the caller-supplied options.
99
+ *
100
+ * Strict mode is treated as a per-call invariant: when `strict` is set
101
+ * we always run the replay so the first captured diagnostic throws,
102
+ * matching the historical fail-fast contract regardless of how many
103
+ * times the cache has previously notified the sink.
104
+ *
105
+ * Non-strict, sink-bearing callers de-duplicate at the function-
106
+ * identity boundary: a second call with the same `(doc, sink)` pair
107
+ * short-circuits because the sink has already seen every captured
108
+ * diagnostic. This is the cardinality-1 guarantee that fixes the
109
+ * N-fold emission caused by parent-fans-out-into-children renders.
110
+ *
111
+ * The sink is only marked notified after a successful replay so a
112
+ * strict throw mid-replay does not silence a follow-up non-strict
113
+ * call that still wants the full captured set.
114
+ */
115
+ function replayCapturedDiagnostics(cached, opts) {
116
+ const sink = opts.diagnostics;
117
+ if (!(opts.strict === true) && sink !== void 0 && cached.notifiedSinks.has(sink)) return;
118
+ for (const diagnostic of cached.diagnostics) emitDiagnostic(opts, diagnostic);
119
+ if (sink !== void 0) cached.notifiedSinks.add(sink);
120
+ }
121
+ /**
122
+ * Coerce an unknown value to a record, returning `undefined` when the
123
+ * value is not a plain object. Callers MUST handle the `undefined` case
124
+ * explicitly — typically by rendering a "doc not an object" diagnostic
125
+ * and short-circuiting, never by silently substituting `{}`.
126
+ *
127
+ * A previous implementation fell back to `{}` for non-objects, which
128
+ * masked configuration mistakes (passing a string, `null`, an array, or
129
+ * `undefined` as the OpenAPI document) as an empty document with no
130
+ * operations.
57
131
  */
58
132
  function toDoc(value) {
59
- return isObject(value) ? value : {};
133
+ return isObject(value) ? value : void 0;
134
+ }
135
+ /**
136
+ * Known security scheme types per the OpenAPI 3.0/3.1 specification.
137
+ * `mutualTLS` was added in OpenAPI 3.1. Unknown values surface a
138
+ * `unknown-security-scheme-type` diagnostic so authors notice typos
139
+ * (e.g. `mutalTLS`) that would otherwise render with no warning.
140
+ */
141
+ const KNOWN_SECURITY_SCHEME_TYPES = new Set([
142
+ "apiKey",
143
+ "http",
144
+ "oauth2",
145
+ "openIdConnect",
146
+ "mutualTLS"
147
+ ]);
148
+ /**
149
+ * Validate every `components.securitySchemes.<name>.type` against the
150
+ * canonical OpenAPI security scheme types and emit
151
+ * `unknown-security-scheme-type` for each entry whose type is not
152
+ * recognised. Runs after normalisation so Swagger 2.0 documents (which
153
+ * are already translated to OAS 3.x shapes by `translateSwaggerSecurityScheme`)
154
+ * are validated alongside native 3.x documents.
155
+ */
156
+ function validateSecuritySchemeTypes(doc, diagnostics) {
157
+ const components = doc.components;
158
+ if (!isObject(components)) return;
159
+ const schemes = components.securitySchemes;
160
+ if (!isObject(schemes)) return;
161
+ for (const [name, scheme] of Object.entries(schemes)) {
162
+ if (!isObject(scheme)) continue;
163
+ const type = scheme.type;
164
+ if (typeof type !== "string") {
165
+ emitDiagnostic(diagnostics, {
166
+ code: "unknown-security-scheme-type",
167
+ message: `Security scheme "${name}" has no type or a non-string type`,
168
+ pointer: `/components/securitySchemes/${name}/type`,
169
+ detail: {
170
+ name,
171
+ type
172
+ }
173
+ });
174
+ continue;
175
+ }
176
+ if (!KNOWN_SECURITY_SCHEME_TYPES.has(type)) emitDiagnostic(diagnostics, {
177
+ code: "unknown-security-scheme-type",
178
+ message: `Security scheme "${name}" declares unknown type "${type}"`,
179
+ pointer: `/components/securitySchemes/${name}/type`,
180
+ detail: {
181
+ name,
182
+ type
183
+ }
184
+ });
185
+ }
186
+ }
187
+ /**
188
+ * Detect any `$ref` strings that survived normalisation in a non-
189
+ * fragment shape (anything not starting with `#/` or `#`). After
190
+ * `normaliseOpenApiSchemas` runs `resolveRelativeRefs`, every relative
191
+ * `$ref` within a Schema Object is rewritten to an absolute fragment.
192
+ * Refs that *cross* Schema Object boundaries — for example, a relative
193
+ * ref inside one component schema pointing into another via a sibling
194
+ * `$id` — cannot be resolved by the current pipeline (this is a
195
+ * documented limitation; see the JSDoc on this function).
196
+ *
197
+ * Emit a single diagnostic per offending ref so consumers notice
198
+ * silently broken references rather than discovering them only when
199
+ * the walker fails to render the target.
200
+ *
201
+ * NOTE: We can't determine "crossing" cleanly from the parser alone —
202
+ * doing so would require modelling every Schema Object's $id scope.
203
+ * As a pragmatic approximation, any surviving non-`#`-prefixed `$ref`
204
+ * is treated as cross-Schema-Object unsupported. False positives
205
+ * (legitimate external refs that the consumer planned to bundle later)
206
+ * are still useful — they confirm an unresolved reference is present.
207
+ */
208
+ function detectUnsupportedCrossSchemaRefs(doc, diagnostics) {
209
+ const seenRefs = /* @__PURE__ */ new Set();
210
+ const walk = (node, pointer) => {
211
+ if (Array.isArray(node)) {
212
+ for (const [index, item] of node.entries()) walk(item, `${pointer}/${String(index)}`);
213
+ return;
214
+ }
215
+ if (!isObject(node)) return;
216
+ const ref = node.$ref;
217
+ if (typeof ref === "string" && !ref.startsWith("#") && !seenRefs.has(ref)) {
218
+ seenRefs.add(ref);
219
+ emitDiagnostic(diagnostics, {
220
+ code: "cross-schema-relative-ref-unsupported",
221
+ message: `Relative \`$ref\` "${ref}" was not resolved during normalisation; cross-Schema-Object relative refs are not currently supported`,
222
+ pointer,
223
+ detail: { ref }
224
+ });
225
+ return;
226
+ }
227
+ for (const [key, value] of Object.entries(node)) walk(value, `${pointer}/${key}`);
228
+ };
229
+ walk(doc, "");
60
230
  }
61
231
  /**
62
232
  * Look up a Path Item Object on the (already-normalised) parsed document,
@@ -67,23 +237,73 @@ function toDoc(value) {
67
237
  * Implemented inside `resolve.ts` to avoid touching `parser.ts` while
68
238
  * still surfacing path-item-level metadata to the React layer.
69
239
  */
70
- function lookupPathItemNode(parsed, path) {
71
- return resolvePathItemNode(parsed, getProperty(getProperty(parsed.doc, "paths"), path));
240
+ function lookupPathItemNode(parsed, path, diagnostics) {
241
+ const paths = getProperty(parsed.doc, "paths");
242
+ const webhooks = getProperty(parsed.doc, "webhooks");
243
+ const pathsEntry = getProperty(paths, path);
244
+ const webhooksEntry = getProperty(webhooks, path);
245
+ if (isObject(pathsEntry) && isObject(webhooksEntry)) emitDiagnostic(diagnostics, {
246
+ code: "path-webhook-name-collision",
247
+ message: `Identifier "${path}" appears in both \`paths\` and \`webhooks\`; \`paths\` takes precedence`,
248
+ pointer: `/paths/${path.replace(/~/g, "~0").replace(/\//g, "~1")}`,
249
+ detail: { name: path }
250
+ });
251
+ const fromPaths = resolvePathItemNode(parsed, pathsEntry, diagnostics);
252
+ if (fromPaths !== void 0) return fromPaths;
253
+ return resolvePathItemNode(parsed, webhooksEntry, diagnostics);
72
254
  }
73
- function resolvePathItemNode(parsed, pathItem) {
74
- if (!isObject(pathItem)) return void 0;
75
- const ref = getProperty(pathItem, "$ref");
76
- if (typeof ref !== "string") return pathItem;
77
- if (!ref.startsWith("#/")) return pathItem;
255
+ /**
256
+ * Resolve a fragment `$ref` (must start with `#/`) against the parsed
257
+ * document by walking the JSON Pointer one segment at a time. Returns
258
+ * the resolved node only when every segment lands on a plain object;
259
+ * returns `undefined` when any intermediate segment is missing or
260
+ * non-object, or when the final node is not an object.
261
+ *
262
+ * Rejects `__proto__`, `constructor`, `prototype` segments — walking
263
+ * into any of these reads `Object.prototype` and would let a crafted
264
+ * pathItems `$ref` smuggle properties from the runtime prototype
265
+ * chain into the resolved Path Item Object.
266
+ */
267
+ function lookupFragmentRef(parsed, ref) {
268
+ if (!ref.startsWith("#/")) return void 0;
78
269
  const parts = ref.slice(2).split("/");
79
- let current = parsed.doc;
270
+ let node = parsed.doc;
80
271
  for (const part of parts) {
81
- if (!isObject(current)) return void 0;
272
+ if (!isObject(node)) return void 0;
82
273
  const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
83
274
  if (isPrototypePollutingKey(decoded)) return void 0;
84
- current = current[decoded];
275
+ node = node[decoded];
85
276
  }
86
- return isObject(current) ? current : pathItem;
277
+ return isObject(node) ? node : void 0;
278
+ }
279
+ function resolvePathItemNode(parsed, pathItem, diagnostics) {
280
+ if (!isObject(pathItem)) return void 0;
281
+ return resolveRefChain(pathItem, {
282
+ lookup: (ref) => lookupFragmentRef(parsed, ref),
283
+ extractRef: (node) => {
284
+ const ref = getProperty(node, "$ref");
285
+ if (typeof ref !== "string") return void 0;
286
+ if (!ref.startsWith("#/")) return void 0;
287
+ return ref;
288
+ },
289
+ onCycle: (ref) => {
290
+ emitDiagnostic(diagnostics, {
291
+ code: "cyclic-path-item-ref",
292
+ message: `Cyclic Path Item Object $ref "${ref}"`,
293
+ pointer: ref,
294
+ detail: { ref }
295
+ });
296
+ },
297
+ onDepthExceeded: () => {
298
+ emitDiagnostic(diagnostics, {
299
+ code: "path-item-ref-too-deep",
300
+ message: `Path Item Object $ref chain exceeded ${String(8)} hops`,
301
+ pointer: "",
302
+ detail: { maxHops: 8 }
303
+ });
304
+ },
305
+ maxHops: 8
306
+ });
87
307
  }
88
308
  function extractPathItemInfo(pathItem) {
89
309
  const summary = pathItem.summary;
@@ -103,10 +323,10 @@ function extractPathItemInfo(pathItem) {
103
323
  * normalisation pipeline (every re-run would emit each diagnostic
104
324
  * again into the sink).
105
325
  */
106
- function resolveOperationFromParsed(parsed, path, method) {
107
- const operation = listOperations(parsed).find((op) => op.path === path && op.method === method);
326
+ function resolveOperationFromParsed(parsed, path, method, diagnostics) {
327
+ const pathItemNode = lookupPathItemNode(parsed, path, diagnostics);
328
+ const operation = [...listOperations(parsed), ...listWebhooks(parsed).flatMap((w) => w.operations)].find((op) => op.path === path && op.method === method);
108
329
  if (operation === void 0) throw new Error(`Operation not found: ${method.toUpperCase()} ${path}`);
109
- const pathItemNode = lookupPathItemNode(parsed, path);
110
330
  if (pathItemNode === void 0) throw new Error(`Path item missing for ${method.toUpperCase()} ${path}`);
111
331
  return {
112
332
  operation,
@@ -124,7 +344,7 @@ function resolveOperationFromParsed(parsed, path, method) {
124
344
  * events surface to the caller's sink.
125
345
  */
126
346
  function resolveOperation(doc, path, method, diagnostics) {
127
- return resolveOperationFromParsed(getParsed(doc, diagnostics), path, method);
347
+ return resolveOperationFromParsed(getParsed(doc, diagnostics), path, method, diagnostics);
128
348
  }
129
349
  /**
130
350
  * Resolve parameters against an already-parsed document. See
@@ -1,8 +1,8 @@
1
- import { M as WalkedField, T as SchemaMeta, d as FieldOverrides, u as FieldOverride } from "../types-BnxPEElk.mjs";
2
- import { t as Diagnostic } from "../diagnostics-CbBPsxSt.mjs";
3
- import { t as SchemaError } from "../errors-QEwOtQAA.mjs";
4
- import { l as RenderProps, r as ComponentResolver } from "../renderer-DI6ZYf7a.mjs";
5
- import { c as PathOfType, l as RejectUnrepresentableZod, n as FromJSONSchema, u as ResolveOpenAPIRef } from "../typeInference-Bxw3NOG1.mjs";
1
+ import { d as FieldOverrides, j as WalkedField, u as FieldOverride, w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
+ import { t as Diagnostic } from "../diagnostics-BS2kaUyE.mjs";
3
+ import { t as SchemaError } from "../errors-g_MCTQel.mjs";
4
+ import { l as RenderProps, r as ComponentResolver } from "../renderer-CXJ8y0qw.mjs";
5
+ import { FromJSONSchema, FromJSONSchemaMode, IsSwagger2Doc, PathOfType, RejectUnrepresentableZod, ResolveOpenAPIRef, TypeAtPath, __SchemaInferenceFellBack } from "../core/typeInference.mjs";
6
6
  import { z } from "zod";
7
7
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
8
8
  import { ReactNode } from "react";
@@ -36,10 +36,57 @@ declare function SchemaProvider({
36
36
  * or `<SchemaProvider>` instead.
37
37
  */
38
38
  declare function registerWidget(name: string, render: (props: RenderProps) => unknown): void;
39
- type InferFields<T, Ref extends string | undefined> = T extends z.ZodType ? FieldOverrides<z.infer<T>> : T extends {
39
+ type InferFields<T, Ref extends string | undefined> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : 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> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : 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,71 @@ 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
+ *
136
+ * TODO(round7-integration): promote to
137
+ * `NarrowAtPath<InferSchemaValue<T, Ref, "output">, P>` once the
138
+ * round-7 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
+ */
57
147
  value?: unknown;
58
- /** Called when the value changes (editable fields). */
148
+ /**
149
+ * Called when the value changes (editable fields).
150
+ *
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.
167
+ */
59
168
  onChange?: (value: unknown) => void;
60
169
  /** Run schema.safeParse() on change and surface errors via onValidationError. */
61
170
  validate?: boolean;
@@ -87,35 +196,16 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
87
196
  */
88
197
  idPrefix?: string;
89
198
  }
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";
199
+ declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined>(props: SchemaComponentProps<T, Ref, P>): ReactNode;
115
200
  /**
116
201
  * Append a child path suffix to a parent path. When the suffix is omitted
117
202
  * (e.g. transparent wrappers like union options), the parent path is
118
203
  * returned unchanged so the child inherits the parent's id.
204
+ *
205
+ * Bracketed array indices like `[0]` append directly so `tags` + `[0]`
206
+ * becomes `tags[0]` rather than `tags.[0]` — matching the canonical form
207
+ * used by `html/a11y.ts` `joinPath` and `react/fieldPath.ts` `resolvePath`,
208
+ * which already parses bracket notation when navigating WalkedField trees.
119
209
  */
120
210
  declare function joinPath(parent: string, suffix: string | undefined): string;
121
211
  /**
@@ -154,7 +244,7 @@ interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefin
154
244
  validate?: boolean;
155
245
  onValidationError?: (error: unknown) => void;
156
246
  }
157
- declare function SchemaField<T = unknown, Ref extends string | undefined = undefined, P extends string = string>({
247
+ declare function SchemaField<T = unknown, Ref extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)>({
158
248
  path,
159
249
  schema: schemaInput,
160
250
  ref: refInput,
@@ -165,4 +255,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
165
255
  onValidationError
166
256
  }: SchemaFieldProps<T, Ref, P>): ReactNode;
167
257
  //#endregion
168
- export { ROOT_PATH, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };
258
+ export { InferredInputValue, InferredOutputValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };