schema-components 1.20.0 → 1.21.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 (58) hide show
  1. package/dist/core/adapter.d.mts +9 -2
  2. package/dist/core/adapter.mjs +220 -64
  3. package/dist/core/constraints.d.mts +1 -1
  4. package/dist/core/constraints.mjs +0 -2
  5. package/dist/core/errors.d.mts +1 -1
  6. package/dist/core/errors.mjs +9 -15
  7. package/dist/core/fieldOrder.d.mts +1 -1
  8. package/dist/core/merge.d.mts +10 -1
  9. package/dist/core/merge.mjs +11 -0
  10. package/dist/core/normalise.d.mts +7 -1
  11. package/dist/core/normalise.mjs +1 -1
  12. package/dist/core/openapi30.d.mts +24 -1
  13. package/dist/core/openapi30.mjs +2 -2
  14. package/dist/core/ref.d.mts +1 -1
  15. package/dist/core/ref.mjs +34 -9
  16. package/dist/core/renderer.d.mts +1 -1
  17. package/dist/core/swagger2.mjs +1 -1
  18. package/dist/core/typeInference.d.mts +1 -1
  19. package/dist/core/types.d.mts +1 -1
  20. package/dist/core/walkBuilders.d.mts +12 -4
  21. package/dist/core/walkBuilders.mjs +11 -3
  22. package/dist/core/walker.d.mts +1 -1
  23. package/dist/core/walker.mjs +32 -25
  24. package/dist/{errors-C2iABcn9.d.mts → errors-QEwOtQAA.d.mts} +7 -11
  25. package/dist/html/a11y.d.mts +2 -2
  26. package/dist/html/renderToHtml.d.mts +2 -2
  27. package/dist/html/renderToHtmlStream.d.mts +2 -2
  28. package/dist/html/renderers.d.mts +2 -2
  29. package/dist/html/renderers.mjs +1 -1
  30. package/dist/html/streamRenderers.d.mts +2 -2
  31. package/dist/{normalise-CMMEl4cd.mjs → normalise-DaSrnr8g.mjs} +325 -28
  32. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  33. package/dist/openapi/ApiLinks.d.mts +1 -1
  34. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  35. package/dist/openapi/ApiSecurity.d.mts +1 -1
  36. package/dist/openapi/ApiSecurity.mjs +113 -7
  37. package/dist/openapi/components.d.mts +32 -10
  38. package/dist/openapi/components.mjs +22 -12
  39. package/dist/openapi/parser.d.mts +1 -1
  40. package/dist/openapi/parser.mjs +39 -4
  41. package/dist/openapi/resolve.d.mts +60 -9
  42. package/dist/openapi/resolve.mjs +86 -23
  43. package/dist/react/SchemaComponent.d.mts +4 -4
  44. package/dist/react/SchemaComponent.mjs +32 -4
  45. package/dist/react/SchemaView.d.mts +2 -2
  46. package/dist/react/fieldPath.d.mts +1 -1
  47. package/dist/react/headless.d.mts +1 -1
  48. package/dist/react/headlessRenderers.d.mts +2 -2
  49. package/dist/react/headlessRenderers.mjs +1 -1
  50. package/dist/{ref-C8JbwfiS.d.mts → ref-si8ViYun.d.mts} +6 -1
  51. package/dist/{renderer-SOIbJBtk.d.mts → renderer-DI6ZYf7a.d.mts} +1 -1
  52. package/dist/themes/mantine.d.mts +1 -1
  53. package/dist/themes/mui.d.mts +1 -1
  54. package/dist/themes/radix.d.mts +1 -1
  55. package/dist/themes/shadcn.d.mts +1 -1
  56. package/dist/{typeInference-CDoD_LZ_.d.mts → typeInference-Bxw3NOG1.d.mts} +162 -48
  57. package/dist/{types-C9zw9wbX.d.mts → types-BnxPEElk.d.mts} +12 -2
  58. package/package.json +1 -1
@@ -117,6 +117,260 @@ function normaliseOpenApi30Discriminator(node) {
117
117
  return node;
118
118
  }
119
119
  /**
120
+ * Returns the schema name a `$ref` points at when it targets
121
+ * `#/components/schemas/<Name>`, or `undefined` otherwise.
122
+ *
123
+ * The walker only resolves intra-document refs and other allOf-base
124
+ * patterns; refs into `definitions` (Swagger 2.0) are already rewritten
125
+ * before this stage.
126
+ */
127
+ function componentSchemaName(ref) {
128
+ if (typeof ref !== "string") return void 0;
129
+ if (!ref.startsWith("#/components/schemas/")) return void 0;
130
+ const name = ref.slice(21);
131
+ return name.length > 0 ? name : void 0;
132
+ }
133
+ /**
134
+ * Find every immediate `$ref` that an `allOf` array contains pointing
135
+ * back at a `components/schemas/<Name>` entry. Used to discover
136
+ * "Cat extends Pet"-style inheritance — the subtype's `allOf` lists
137
+ * the base by `$ref` alongside its own local fields.
138
+ */
139
+ function listAllOfBaseRefs(schema) {
140
+ const allOf = schema.allOf;
141
+ if (!Array.isArray(allOf)) return [];
142
+ const result = [];
143
+ for (const entry of allOf) {
144
+ if (!isObject(entry)) continue;
145
+ const name = componentSchemaName(entry.$ref);
146
+ if (name !== void 0) result.push(name);
147
+ }
148
+ return result;
149
+ }
150
+ /**
151
+ * Collect discriminator subtypes for a base schema. Entries come from:
152
+ *
153
+ * 1. The base's `discriminator.mapping` (explicit author intent — the
154
+ * mapping key supplies the `const` value, the ref names the subtype).
155
+ * 2. Component schemas whose `allOf` lists this base by `$ref` and
156
+ * were not already named in the mapping. The `const` value defaults
157
+ * to the subtype's component name.
158
+ *
159
+ * Returned in deterministic order: mapping entries first (preserving
160
+ * authored order), then implicit subtypes alphabetically.
161
+ */
162
+ function collectDiscriminatorSubtypes(baseName, discriminator, componentSchemas) {
163
+ const result = [];
164
+ const seen = /* @__PURE__ */ new Set();
165
+ const mapping = isObject(discriminator.mapping) ? discriminator.mapping : void 0;
166
+ if (mapping !== void 0) for (const [constValue, ref] of Object.entries(mapping)) {
167
+ const name = componentSchemaName(ref);
168
+ if (name === void 0) continue;
169
+ if (!isObject(componentSchemas[name])) continue;
170
+ if (seen.has(name)) continue;
171
+ seen.add(name);
172
+ result.push({
173
+ name,
174
+ constValue
175
+ });
176
+ }
177
+ const implicitNames = [];
178
+ for (const [name, schema] of Object.entries(componentSchemas)) {
179
+ if (!isObject(schema)) continue;
180
+ if (seen.has(name)) continue;
181
+ if (!listAllOfBaseRefs(schema).includes(baseName)) continue;
182
+ implicitNames.push(name);
183
+ }
184
+ implicitNames.sort();
185
+ for (const name of implicitNames) {
186
+ result.push({
187
+ name,
188
+ constValue: name
189
+ });
190
+ seen.add(name);
191
+ }
192
+ return result;
193
+ }
194
+ /**
195
+ * Inject the discriminator `const` on a subtype schema in-place.
196
+ *
197
+ * When the subtype already declares a matching const we leave it
198
+ * alone. Otherwise the const is added in whichever location the walker
199
+ * will actually observe:
200
+ *
201
+ * - Subtype declares `allOf`: append a new `allOf` entry carrying just
202
+ * `{ properties: { [propertyName]: { const } } }`. The walker's
203
+ * `mergeAllOf` merges every entry's `properties` into the resolved
204
+ * schema, so the const propagates through to the merged result. A
205
+ * top-level `properties` sibling of `allOf` would be ignored by the
206
+ * merge.
207
+ * - Subtype does not declare `allOf`: extend the top-level `properties`
208
+ * block — the walker reads this directly.
209
+ */
210
+ function injectSubtypeConst(subtype, propertyName, constValue) {
211
+ if (subtypeAlreadyDeclaresConst(subtype, propertyName)) return;
212
+ const constEntry = { properties: { [propertyName]: { const: constValue } } };
213
+ if (Array.isArray(subtype.allOf)) {
214
+ subtype.allOf = [...subtype.allOf, constEntry];
215
+ return;
216
+ }
217
+ const existingProps = isObject(subtype.properties) ? { ...subtype.properties } : {};
218
+ const existingDisc = existingProps[propertyName];
219
+ existingProps[propertyName] = {
220
+ ...isObject(existingDisc) ? existingDisc : {},
221
+ const: constValue
222
+ };
223
+ subtype.properties = existingProps;
224
+ }
225
+ /**
226
+ * Check whether a subtype (or any of its `allOf` entries) already
227
+ * carries a `const` for the discriminator property. Used to avoid
228
+ * overwriting an author-supplied const.
229
+ */
230
+ function subtypeAlreadyDeclaresConst(subtype, propertyName) {
231
+ if (hasConstProp(subtype.properties, propertyName)) return true;
232
+ if (Array.isArray(subtype.allOf)) for (const entry of subtype.allOf) {
233
+ if (!isObject(entry)) continue;
234
+ if (hasConstProp(entry.properties, propertyName)) return true;
235
+ }
236
+ return false;
237
+ }
238
+ function hasConstProp(properties, propertyName) {
239
+ if (!isObject(properties)) return false;
240
+ const prop = properties[propertyName];
241
+ return isObject(prop) && "const" in prop;
242
+ }
243
+ /**
244
+ * Strip every `$ref` entry in a subtype's `allOf` that targets the
245
+ * discriminator base. The base's own schema content (properties,
246
+ * required, type) is replicated into a synthesised `allOf` entry so
247
+ * the subtype remains structurally complete — without this the base's
248
+ * synthesised `oneOf` would cycle through the subtype's `$ref`s on
249
+ * every walk (Dog → Pet.oneOf → Dog → ...).
250
+ */
251
+ function rewriteSubtypeAllOf(subtype, baseName, baseInherited) {
252
+ const allOf = subtype.allOf;
253
+ if (!Array.isArray(allOf)) return;
254
+ const baseRefPrefix = `#/components/schemas/${baseName}`;
255
+ const rewritten = [];
256
+ let removedBaseRef = false;
257
+ for (const entry of allOf) {
258
+ if (isObject(entry) && typeof entry.$ref === "string" && entry.$ref === baseRefPrefix) {
259
+ removedBaseRef = true;
260
+ continue;
261
+ }
262
+ rewritten.push(entry);
263
+ }
264
+ if (!removedBaseRef) return;
265
+ subtype.allOf = [baseInherited, ...rewritten];
266
+ }
267
+ /**
268
+ * Capture the "inheritable" portion of a base schema before rewriting:
269
+ * `properties`, `required`, `type`, and any other constraint that
270
+ * subtypes used to inherit through `$ref`. The discriminator keyword
271
+ * and the synthesised `oneOf` are intentionally excluded — subtypes
272
+ * never inherited those and including them would re-introduce the
273
+ * Pet → Dog → Pet cycle.
274
+ */
275
+ function captureBaseInherited(base) {
276
+ const inherited = {};
277
+ for (const [key, value] of Object.entries(base)) {
278
+ if (key === "discriminator") continue;
279
+ if (key === "oneOf") continue;
280
+ if (key === "anyOf") continue;
281
+ inherited[key] = value;
282
+ }
283
+ return inherited;
284
+ }
285
+ /**
286
+ * Build an inline `oneOf` option that targets a subtype via `$ref` and
287
+ * carries the discriminator property's `const` at the option's top
288
+ * level. The const sibling is what makes `detectDiscriminated` classify
289
+ * the parent `oneOf` as a discriminated union — it inspects each
290
+ * option's literal `properties`, not the resolved schema.
291
+ */
292
+ function buildDiscriminatorOption(subtype, propertyName) {
293
+ return {
294
+ $ref: `#/components/schemas/${subtype.name}`,
295
+ properties: { [propertyName]: { const: subtype.constValue } }
296
+ };
297
+ }
298
+ /**
299
+ * Document-level pre-pass for OpenAPI discriminators that are declared
300
+ * on a base schema and inherited by subtypes via `allOf`.
301
+ *
302
+ * The per-node {@link normaliseOpenApi30Discriminator} only handles
303
+ * discriminators that already sit alongside `oneOf`/`anyOf`. For the
304
+ * canonical "Cat extends Pet" pattern — where `Pet` carries the
305
+ * discriminator and `Cat`/`Dog` reference `Pet` via `allOf` — the
306
+ * discriminator is silently lost. This pre-pass:
307
+ *
308
+ * 1. Injects the discriminator `const` on each subtype's local
309
+ * `properties` (so a direct render of the subtype validates the
310
+ * discriminator value correctly).
311
+ * 2. Synthesises a `oneOf` on the base whenever it lacks one, listing
312
+ * each subtype as `{ $ref, properties: { propertyName: { const } } }`.
313
+ * The per-node discriminator transform then sees `oneOf` and clears
314
+ * the `discriminator` keyword, and the walker's
315
+ * `detectDiscriminated` finds the per-option `const`s.
316
+ *
317
+ * Mutates a shallow clone of `components/schemas` — the input document
318
+ * is never modified.
319
+ */
320
+ function applyDiscriminatorAllOfPrepass(doc) {
321
+ const components = doc.components;
322
+ if (!isObject(components)) return doc;
323
+ const schemas = components.schemas;
324
+ if (!isObject(schemas)) return doc;
325
+ const plans = [];
326
+ for (const [baseName, base] of Object.entries(schemas)) {
327
+ if (!isObject(base)) continue;
328
+ const discriminator = base.discriminator;
329
+ if (!isObject(discriminator)) continue;
330
+ const propertyName = discriminator.propertyName;
331
+ if (typeof propertyName !== "string") continue;
332
+ const subtypes = collectDiscriminatorSubtypes(baseName, discriminator, schemas);
333
+ if (subtypes.length === 0) continue;
334
+ plans.push({
335
+ baseName,
336
+ propertyName,
337
+ subtypes,
338
+ baseHasOneOfOrAnyOf: Array.isArray(base.oneOf) || Array.isArray(base.anyOf),
339
+ baseInherited: captureBaseInherited(base)
340
+ });
341
+ }
342
+ if (plans.length === 0) return doc;
343
+ const newSchemas = { ...schemas };
344
+ const cloneSchema = (name) => {
345
+ const existing = newSchemas[name];
346
+ if (!isObject(existing)) throw new Error(`applyDiscriminatorAllOfPrepass: schema "${name}" disappeared between planning and rewrite`);
347
+ const clone = { ...existing };
348
+ newSchemas[name] = clone;
349
+ return clone;
350
+ };
351
+ for (const plan of plans) {
352
+ for (const subtype of plan.subtypes) {
353
+ const clone = cloneSchema(subtype.name);
354
+ rewriteSubtypeAllOf(clone, plan.baseName, plan.baseInherited);
355
+ injectSubtypeConst(clone, plan.propertyName, subtype.constValue);
356
+ }
357
+ if (!plan.baseHasOneOfOrAnyOf) {
358
+ const baseClone = cloneSchema(plan.baseName);
359
+ baseClone.oneOf = plan.subtypes.map((subtype) => buildDiscriminatorOption(subtype, plan.propertyName));
360
+ delete baseClone.properties;
361
+ delete baseClone.required;
362
+ delete baseClone.type;
363
+ }
364
+ }
365
+ return {
366
+ ...doc,
367
+ components: {
368
+ ...components,
369
+ schemas: newSchemas
370
+ }
371
+ };
372
+ }
373
+ /**
120
374
  * Combined OpenAPI 3.0.x node transform: Draft 04 + nullable + discriminator.
121
375
  * Applied to every schema node in an OpenAPI 3.0 document.
122
376
  *
@@ -779,35 +1033,43 @@ const SINGLE_SUBSCHEMA_KEYS = new Set([
779
1033
  * Normalise each element of an unknown array by applying deepNormalise
780
1034
  * to object elements and passing others through unchanged.
781
1035
  */
782
- function normaliseArray(items, transform) {
1036
+ function normaliseArray(items, transform, visited) {
783
1037
  const result = [];
784
- for (const item of items) result.push(isObject(item) ? deepNormalise(item, transform) : item);
1038
+ for (const item of items) result.push(isObject(item) ? deepNormalise(item, transform, visited) : item);
785
1039
  return result;
786
1040
  }
787
1041
  /**
788
1042
  * Normalise each value of a sub-schema map (e.g. properties, $defs).
789
1043
  */
790
- function normaliseSubSchemaMap(map, transform) {
1044
+ function normaliseSubSchemaMap(map, transform, visited) {
791
1045
  const result = {};
792
- for (const [k, v] of Object.entries(map)) result[k] = isObject(v) ? deepNormalise(v, transform) : v;
1046
+ for (const [k, v] of Object.entries(map)) result[k] = isObject(v) ? deepNormalise(v, transform, visited) : v;
793
1047
  return result;
794
1048
  }
795
1049
  /**
796
1050
  * Deep-normalise a JSON Schema object by applying a per-node transform
797
1051
  * and recursing into every sub-schema location.
1052
+ *
1053
+ * The optional `visited` set guards against shared object references and
1054
+ * cycles introduced upstream (e.g. by the OpenAPI bundler's
1055
+ * `structuredClone`-based inlining of external refs). The walk skips
1056
+ * already-seen nodes by returning the original reference rather than
1057
+ * recursing forever.
798
1058
  */
799
- function deepNormalise(schema, transform) {
1059
+ function deepNormalise(schema, transform, visited = /* @__PURE__ */ new WeakSet()) {
1060
+ if (visited.has(schema)) return schema;
1061
+ visited.add(schema);
800
1062
  const node = transform({ ...schema });
801
1063
  const result = {};
802
- for (const [key, value] of Object.entries(node)) if (isObject(value) && OBJECT_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseSubSchemaMap(value, transform);
803
- else if (Array.isArray(value) && ARRAY_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseArray(value, transform);
804
- else if (isObject(value) && SINGLE_SUBSCHEMA_KEYS.has(key)) result[key] = deepNormalise(value, transform);
805
- else if (key === "items") if (Array.isArray(value)) result[key] = normaliseArray(value, transform);
806
- else if (isObject(value)) result[key] = deepNormalise(value, transform);
1064
+ for (const [key, value] of Object.entries(node)) if (isObject(value) && OBJECT_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseSubSchemaMap(value, transform, visited);
1065
+ else if (Array.isArray(value) && ARRAY_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseArray(value, transform, visited);
1066
+ else if (isObject(value) && SINGLE_SUBSCHEMA_KEYS.has(key)) result[key] = deepNormalise(value, transform, visited);
1067
+ else if (key === "items") if (Array.isArray(value)) result[key] = normaliseArray(value, transform, visited);
1068
+ else if (isObject(value)) result[key] = deepNormalise(value, transform, visited);
807
1069
  else result[key] = value;
808
1070
  else if (key === "dependencies" && isObject(value)) {
809
1071
  const normalised = {};
810
- for (const [dk, dv] of Object.entries(value)) if (isObject(dv)) normalised[dk] = deepNormalise(dv, transform);
1072
+ for (const [dk, dv] of Object.entries(value)) if (isObject(dv)) normalised[dk] = deepNormalise(dv, transform, visited);
811
1073
  else normalised[dk] = dv;
812
1074
  result[key] = normalised;
813
1075
  } else result[key] = value;
@@ -976,6 +1238,26 @@ function validateDependentRequired(node, ctx) {
976
1238
  }
977
1239
  }
978
1240
  /**
1241
+ * Translate the legacy tuple-form `items: [Schema, Schema]` keyword to
1242
+ * Draft 2020-12's `prefixItems`. `additionalItems` becomes the rest-element
1243
+ * schema and is rewritten to `items`.
1244
+ *
1245
+ * Applies to Drafts 04, 06, and 07 — all of which used the tuple form
1246
+ * before Draft 2020-12 split positional schemas into `prefixItems`.
1247
+ *
1248
+ * No-op when `items` is already a single sub-schema, or when `prefixItems`
1249
+ * is already present (do not overwrite an explicit author choice).
1250
+ */
1251
+ function translateTupleItems(node) {
1252
+ if (!Array.isArray(node.items) || "prefixItems" in node) return;
1253
+ node.prefixItems = node.items;
1254
+ delete node.items;
1255
+ if ("additionalItems" in node) {
1256
+ node.items = node.additionalItems;
1257
+ delete node.additionalItems;
1258
+ }
1259
+ }
1260
+ /**
979
1261
  * Apply the version-agnostic Draft 04 keyword translations to a single
980
1262
  * node: boolean exclusive-min/max → number form, bare `id` → `$id`, and
981
1263
  * tuple-form `items` → `prefixItems`.
@@ -1036,14 +1318,7 @@ function applyDraft04Translations(node, ctx) {
1036
1318
  node.$id = node.id;
1037
1319
  delete node.id;
1038
1320
  }
1039
- if (Array.isArray(node.items) && !("prefixItems" in node)) {
1040
- node.prefixItems = node.items;
1041
- delete node.items;
1042
- if ("additionalItems" in node) {
1043
- node.items = node.additionalItems;
1044
- delete node.additionalItems;
1045
- }
1046
- }
1321
+ translateTupleItems(node);
1047
1322
  splitDependencies(node, ctx, false);
1048
1323
  validateDependentRequired(node, ctx);
1049
1324
  }
@@ -1089,8 +1364,14 @@ function normaliseDraft04NodeWithContext(node, ctx) {
1089
1364
  * (already in final form) and `const`/`examples`, but still use the
1090
1365
  * legacy `dependencies` keyword. Split it into `dependentRequired` /
1091
1366
  * `dependentSchemas` so the walker can process them uniformly.
1367
+ *
1368
+ * Drafts 06 and 07 also use the tuple form
1369
+ * `items: [Schema, Schema], additionalItems: Schema` — translate that to
1370
+ * Draft 2020-12's `prefixItems` + rest-`items` so the walker produces a
1371
+ * `TupleField` rather than silently dropping the positional schemas.
1092
1372
  */
1093
1373
  function normaliseDraft06Or07NodeWithContext(node, ctx) {
1374
+ translateTupleItems(node);
1094
1375
  splitDependencies(node, ctx, false);
1095
1376
  validateDependentRequired(node, ctx);
1096
1377
  return node;
@@ -1219,9 +1500,21 @@ function resolveRelativeRefs(schema, diagnostics) {
1219
1500
  const docBaseUrl = parseAbsoluteUri(schema.$id);
1220
1501
  if (docBaseUrl === void 0) return schema;
1221
1502
  const docBase = stripFragment(docBaseUrl);
1222
- return rewriteRelativeRefsNode(schema, docBase, docBase, "", diagnostics);
1503
+ return rewriteRelativeRefsNode(schema, docBase, docBase, "", diagnostics, /* @__PURE__ */ new WeakSet());
1223
1504
  }
1224
- function rewriteRelativeRefsNode(node, currentBase, docBase, pointer, diagnostics) {
1505
+ /**
1506
+ * Rewrite relative `$ref`s in a single node, recursing into sub-schemas.
1507
+ *
1508
+ * The `visited` set guards against shared object references and cycles
1509
+ * introduced upstream (e.g. by the OpenAPI bundler's `structuredClone`
1510
+ * inlining of external refs). When a node is re-encountered the rewrite
1511
+ * is short-circuited — returning the original reference unchanged is
1512
+ * sound because every relative `$ref` in the subtree was already
1513
+ * rewritten on the first visit.
1514
+ */
1515
+ function rewriteRelativeRefsNode(node, currentBase, docBase, pointer, diagnostics, visited) {
1516
+ if (visited.has(node)) return node;
1517
+ visited.add(node);
1225
1518
  let nextBase = currentBase;
1226
1519
  const nodeId = node.$id;
1227
1520
  if (typeof nodeId === "string" && nodeId.length > 0) {
@@ -1234,13 +1527,17 @@ function rewriteRelativeRefsNode(node, currentBase, docBase, pointer, diagnostic
1234
1527
  result[key] = rewriteRef(value, nextBase, docBase, appendPointer(pointer, key), diagnostics);
1235
1528
  continue;
1236
1529
  }
1237
- result[key] = rewriteRelativeRefsValue(value, key, nextBase, docBase, appendPointer(pointer, key), diagnostics);
1530
+ result[key] = rewriteRelativeRefsValue(value, nextBase, docBase, appendPointer(pointer, key), diagnostics, visited);
1238
1531
  }
1239
1532
  return result;
1240
1533
  }
1241
- function rewriteRelativeRefsValue(value, parentKey, currentBase, docBase, pointer, diagnostics) {
1242
- if (Array.isArray(value)) return value.map((item, i) => rewriteRelativeRefsValue(item, parentKey, currentBase, docBase, appendPointer(pointer, String(i)), diagnostics));
1243
- if (isObject(value)) return rewriteRelativeRefsNode(value, currentBase, docBase, pointer, diagnostics);
1534
+ function rewriteRelativeRefsValue(value, currentBase, docBase, pointer, diagnostics, visited) {
1535
+ if (Array.isArray(value)) {
1536
+ if (visited.has(value)) return value;
1537
+ visited.add(value);
1538
+ return value.map((item, i) => rewriteRelativeRefsValue(item, currentBase, docBase, appendPointer(pointer, String(i)), diagnostics, visited));
1539
+ }
1540
+ if (isObject(value)) return rewriteRelativeRefsNode(value, currentBase, docBase, pointer, diagnostics, visited);
1244
1541
  return value;
1245
1542
  }
1246
1543
  /**
@@ -1290,7 +1587,7 @@ function rewriteRef(ref, currentBase, docBase, pointer, diagnostics) {
1290
1587
  */
1291
1588
  function normaliseOpenApiSchemas(doc, version, diagnostics) {
1292
1589
  if (isSwagger2(version)) return normaliseSwagger2Document(doc, deepNormalise, normaliseDraft04Node, diagnostics);
1293
- if (isOpenApi30(version)) return deepNormaliseOpenApi30Doc(doc, deepNormalise);
1590
+ if (isOpenApi30(version)) return deepNormaliseOpenApi30Doc(applyDiscriminatorAllOfPrepass(doc), deepNormalise);
1294
1591
  if (isOpenApi31(version)) {
1295
1592
  const dialect = readJsonSchemaDialect(doc);
1296
1593
  if (dialect.kind === "unknown") emitDiagnostic(diagnostics, {
@@ -1300,7 +1597,7 @@ function normaliseOpenApiSchemas(doc, version, diagnostics) {
1300
1597
  detail: { uri: dialect.uri }
1301
1598
  });
1302
1599
  }
1303
- return deepNormaliseOpenApiDoc(doc, (schema) => deepNormalise(schema, normaliseOpenApi30Discriminator));
1600
+ return deepNormaliseOpenApiDoc(applyDiscriminatorAllOfPrepass(doc), (schema) => resolveRelativeRefs(deepNormalise(schema, normaliseOpenApi30Discriminator), diagnostics));
1304
1601
  }
1305
1602
  //#endregion
1306
- export { normaliseOpenApiSchemas as a, deepNormaliseOpenApiDoc as c, normaliseOpenApi30Node as d, normaliseJsonSchema as i, normaliseOpenApi30Combined as l, deepNormaliseWithContext as n, normaliseSwagger2Document as o, normaliseDraft04Node as r, deepNormaliseOpenApi30Doc as s, deepNormalise as t, normaliseOpenApi30Discriminator as u };
1603
+ export { normaliseOpenApiSchemas as a, deepNormaliseOpenApi30Doc as c, normaliseOpenApi30Discriminator as d, normaliseOpenApi30Node as f, normaliseJsonSchema as i, deepNormaliseOpenApiDoc as l, deepNormaliseWithContext as n, normaliseSwagger2Document as o, normaliseDraft04Node as r, applyDiscriminatorAllOfPrepass as s, deepNormalise as t, normaliseOpenApi30Combined as u };
@@ -1,4 +1,4 @@
1
- import { T as SchemaMeta } from "../types-C9zw9wbX.mjs";
1
+ import { T as SchemaMeta } from "../types-BnxPEElk.mjs";
2
2
  import { CallbackInfo } from "./parser.mjs";
3
3
  import { ReactNode } from "react";
4
4
 
@@ -1,4 +1,4 @@
1
- import { T as SchemaMeta } from "../types-C9zw9wbX.mjs";
1
+ import { T as SchemaMeta } from "../types-BnxPEElk.mjs";
2
2
  import { LinkInfo } from "./parser.mjs";
3
3
  import { ReactNode } from "react";
4
4
 
@@ -1,4 +1,4 @@
1
- import { T as SchemaMeta } from "../types-C9zw9wbX.mjs";
1
+ import { T as SchemaMeta } from "../types-BnxPEElk.mjs";
2
2
  import { HeaderInfo } from "./parser.mjs";
3
3
  import { ReactNode } from "react";
4
4
 
@@ -1,4 +1,4 @@
1
- import { T as SchemaMeta } from "../types-C9zw9wbX.mjs";
1
+ import { T as SchemaMeta } from "../types-BnxPEElk.mjs";
2
2
  import { SecurityRequirement, SecurityScheme } from "./parser.mjs";
3
3
  import { ReactNode } from "react";
4
4
 
@@ -1,5 +1,46 @@
1
+ import { isObject } from "../core/guards.mjs";
1
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
3
  //#region src/openapi/ApiSecurity.tsx
4
+ /**
5
+ * The four OAuth 2 flow keys defined by OpenAPI 3.x. Listed in the
6
+ * canonical specification order so renders are deterministic.
7
+ */
8
+ const OAUTH_FLOW_KEYS = [
9
+ "implicit",
10
+ "password",
11
+ "clientCredentials",
12
+ "authorizationCode"
13
+ ];
14
+ function readString(source, key) {
15
+ const value = source[key];
16
+ return typeof value === "string" ? value : void 0;
17
+ }
18
+ function readScopes(source) {
19
+ const scopes = source.scopes;
20
+ const result = /* @__PURE__ */ new Map();
21
+ if (!isObject(scopes)) return result;
22
+ for (const [name, description] of Object.entries(scopes)) {
23
+ if (typeof description !== "string") continue;
24
+ result.set(name, description);
25
+ }
26
+ return result;
27
+ }
28
+ function extractFlows(flows) {
29
+ if (flows === void 0) return [];
30
+ const result = [];
31
+ for (const name of OAUTH_FLOW_KEYS) {
32
+ const flow = flows[name];
33
+ if (!isObject(flow)) continue;
34
+ result.push({
35
+ name,
36
+ authorizationUrl: readString(flow, "authorizationUrl"),
37
+ tokenUrl: readString(flow, "tokenUrl"),
38
+ refreshUrl: readString(flow, "refreshUrl"),
39
+ scopes: readScopes(flow)
40
+ });
41
+ }
42
+ return result;
43
+ }
3
44
  function ApiSecurity({ requirements, schemes }) {
4
45
  if (requirements.length === 0) return null;
5
46
  return /* @__PURE__ */ jsxs("section", {
@@ -13,13 +54,7 @@ function ApiSecurity({ requirements, schemes }) {
13
54
  "data-security-name": true,
14
55
  children: req.name
15
56
  }),
16
- scheme !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [scheme.type !== void 0 && /* @__PURE__ */ jsx("span", {
17
- "data-security-type": true,
18
- children: scheme.type
19
- }), scheme.description && /* @__PURE__ */ jsx("span", {
20
- "data-security-description": true,
21
- children: scheme.description
22
- })] }),
57
+ scheme !== void 0 && /* @__PURE__ */ jsx(SchemeDetails, { scheme }),
23
58
  req.scopes.length > 0 && /* @__PURE__ */ jsx("span", {
24
59
  "data-security-scopes": true,
25
60
  children: req.scopes.join(", ")
@@ -29,5 +64,76 @@ function ApiSecurity({ requirements, schemes }) {
29
64
  })]
30
65
  });
31
66
  }
67
+ function SchemeDetails({ scheme }) {
68
+ const flows = extractFlows(scheme.flows);
69
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
70
+ scheme.type !== void 0 && /* @__PURE__ */ jsx("span", {
71
+ "data-security-type": true,
72
+ children: scheme.type
73
+ }),
74
+ scheme.description !== void 0 && /* @__PURE__ */ jsx("span", {
75
+ "data-security-description": true,
76
+ children: scheme.description
77
+ }),
78
+ scheme.scheme !== void 0 && /* @__PURE__ */ jsx("span", {
79
+ "data-security-http-scheme": true,
80
+ children: scheme.scheme
81
+ }),
82
+ scheme.bearerFormat !== void 0 && /* @__PURE__ */ jsx("span", {
83
+ "data-security-bearer-format": true,
84
+ children: scheme.bearerFormat
85
+ }),
86
+ scheme.name !== void 0 && /* @__PURE__ */ jsx("span", {
87
+ "data-security-apikey-name": true,
88
+ children: scheme.name
89
+ }),
90
+ scheme.location !== void 0 && /* @__PURE__ */ jsx("span", {
91
+ "data-security-apikey-in": true,
92
+ children: scheme.location
93
+ }),
94
+ scheme.openIdConnectUrl !== void 0 && /* @__PURE__ */ jsx("a", {
95
+ "data-security-openid-url": true,
96
+ href: scheme.openIdConnectUrl,
97
+ children: scheme.openIdConnectUrl
98
+ }),
99
+ flows.length > 0 && /* @__PURE__ */ jsx("section", {
100
+ "data-security-flows": true,
101
+ children: flows.map((flow) => /* @__PURE__ */ jsx(FlowDetails, { flow }, flow.name))
102
+ })
103
+ ] });
104
+ }
105
+ function FlowDetails({ flow }) {
106
+ return /* @__PURE__ */ jsxs("div", {
107
+ "data-security-flow": flow.name,
108
+ children: [
109
+ /* @__PURE__ */ jsx("span", {
110
+ "data-security-flow-name": true,
111
+ children: flow.name
112
+ }),
113
+ flow.authorizationUrl !== void 0 && /* @__PURE__ */ jsx("a", {
114
+ "data-security-flow-authorization-url": true,
115
+ href: flow.authorizationUrl,
116
+ children: flow.authorizationUrl
117
+ }),
118
+ flow.tokenUrl !== void 0 && /* @__PURE__ */ jsx("a", {
119
+ "data-security-flow-token-url": true,
120
+ href: flow.tokenUrl,
121
+ children: flow.tokenUrl
122
+ }),
123
+ flow.refreshUrl !== void 0 && /* @__PURE__ */ jsx("a", {
124
+ "data-security-flow-refresh-url": true,
125
+ href: flow.refreshUrl,
126
+ children: flow.refreshUrl
127
+ }),
128
+ flow.scopes.size > 0 && /* @__PURE__ */ jsx("dl", {
129
+ "data-security-flow-scopes": true,
130
+ children: [...flow.scopes.entries()].map(([name, description]) => /* @__PURE__ */ jsxs("div", {
131
+ "data-security-flow-scope": name,
132
+ children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: description })]
133
+ }, name))
134
+ })
135
+ ]
136
+ });
137
+ }
32
138
  //#endregion
33
139
  export { ApiSecurity };
@@ -1,10 +1,24 @@
1
- import { T as SchemaMeta, u as FieldOverride } from "../types-C9zw9wbX.mjs";
2
- import { a as InferResponseFields, i as InferRequestBodyFields, m as UnsafeFields, r as InferParameterOverrides } from "../typeInference-CDoD_LZ_.mjs";
1
+ import { T as SchemaMeta, u as FieldOverride } from "../types-BnxPEElk.mjs";
2
+ import { r as DiagnosticSink } from "../diagnostics-CbBPsxSt.mjs";
3
+ import { a as InferResponseFields, i as InferRequestBodyFields, m as UnsafeFields, r as InferParameterOverrides } from "../typeInference-Bxw3NOG1.mjs";
3
4
  import { WidgetMap } from "../react/SchemaComponent.mjs";
4
5
  import { ReactNode } from "react";
5
6
 
6
7
  //#region src/openapi/components.d.ts
7
- interface ApiOperationProps<Doc = unknown, Path extends string = string, Method extends string = string> {
8
+ /**
9
+ * Diagnostics props accepted by every top-level OpenAPI component.
10
+ *
11
+ * `onDiagnostic` is the sink invoked for each event surfaced by the
12
+ * normalisation pipeline (duplicate body parameter, dropped Swagger
13
+ * feature, divisible-by conflict, unknown JSON Schema dialect,
14
+ * relative-ref resolved, etc.). `strict` converts every emitted
15
+ * diagnostic into a thrown `SchemaNormalisationError`.
16
+ */
17
+ interface ApiDiagnosticsProps {
18
+ onDiagnostic?: DiagnosticSink;
19
+ strict?: boolean;
20
+ }
21
+ interface ApiOperationProps<Doc = unknown, Path extends string = string, Method extends string = string> extends ApiDiagnosticsProps {
8
22
  schema: Doc;
9
23
  path: Path;
10
24
  method: Method;
@@ -30,9 +44,11 @@ declare function ApiOperation<Doc = unknown, Path extends string = string, Metho
30
44
  responseValue,
31
45
  meta,
32
46
  requestBodyFields,
33
- widgets
47
+ widgets,
48
+ onDiagnostic,
49
+ strict
34
50
  }: ApiOperationProps<Doc, Path, Method>): ReactNode;
35
- interface ApiParametersProps<Doc = unknown, Path extends string = string, Method extends string = string> {
51
+ interface ApiParametersProps<Doc = unknown, Path extends string = string, Method extends string = string> extends ApiDiagnosticsProps {
36
52
  schema: Doc;
37
53
  path: Path;
38
54
  method: Method;
@@ -47,9 +63,11 @@ declare function ApiParameters<Doc = unknown, Path extends string = string, Meth
47
63
  method,
48
64
  meta,
49
65
  overrides,
50
- widgets
66
+ widgets,
67
+ onDiagnostic,
68
+ strict
51
69
  }: ApiParametersProps<Doc, Path, Method>): ReactNode;
52
- interface ApiRequestBodyProps<Doc = unknown, Path extends string = string, Method extends string = string> {
70
+ interface ApiRequestBodyProps<Doc = unknown, Path extends string = string, Method extends string = string> extends ApiDiagnosticsProps {
53
71
  schema: Doc;
54
72
  path: Path;
55
73
  method: Method;
@@ -68,9 +86,11 @@ declare function ApiRequestBody<Doc = unknown, Path extends string = string, Met
68
86
  onChange,
69
87
  meta,
70
88
  fields,
71
- widgets
89
+ widgets,
90
+ onDiagnostic,
91
+ strict
72
92
  }: ApiRequestBodyProps<Doc, Path, Method>): ReactNode;
73
- interface ApiResponseProps<Doc = unknown, Path extends string = string, Method extends string = string, Status extends string = string> {
93
+ interface ApiResponseProps<Doc = unknown, Path extends string = string, Method extends string = string, Status extends string = string> extends ApiDiagnosticsProps {
74
94
  schema: Doc;
75
95
  path: Path;
76
96
  method: Method;
@@ -89,7 +109,9 @@ declare function ApiResponse<Doc = unknown, Path extends string = string, Method
89
109
  value,
90
110
  meta,
91
111
  fields,
92
- widgets
112
+ widgets,
113
+ onDiagnostic,
114
+ strict
93
115
  }: ApiResponseProps<Doc, Path, Method, Status>): ReactNode;
94
116
  //#endregion
95
117
  export { ApiOperation, ApiOperationProps, ApiParameters, ApiParametersProps, ApiRequestBody, ApiRequestBodyProps, ApiResponse, ApiResponseProps };