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.
- package/dist/core/adapter.d.mts +9 -2
- package/dist/core/adapter.mjs +220 -64
- package/dist/core/constraints.d.mts +1 -1
- package/dist/core/constraints.mjs +0 -2
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/errors.mjs +9 -15
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/merge.d.mts +10 -1
- package/dist/core/merge.mjs +11 -0
- package/dist/core/normalise.d.mts +7 -1
- package/dist/core/normalise.mjs +1 -1
- package/dist/core/openapi30.d.mts +24 -1
- package/dist/core/openapi30.mjs +2 -2
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/ref.mjs +34 -9
- package/dist/core/renderer.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +1 -1
- package/dist/core/types.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +12 -4
- package/dist/core/walkBuilders.mjs +11 -3
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +32 -25
- package/dist/{errors-C2iABcn9.d.mts → errors-QEwOtQAA.d.mts} +7 -11
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/renderToHtml.d.mts +2 -2
- package/dist/html/renderToHtmlStream.d.mts +2 -2
- package/dist/html/renderers.d.mts +2 -2
- package/dist/html/renderers.mjs +1 -1
- package/dist/html/streamRenderers.d.mts +2 -2
- package/dist/{normalise-CMMEl4cd.mjs → normalise-DaSrnr8g.mjs} +325 -28
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/ApiSecurity.mjs +113 -7
- package/dist/openapi/components.d.mts +32 -10
- package/dist/openapi/components.mjs +22 -12
- package/dist/openapi/parser.d.mts +1 -1
- package/dist/openapi/parser.mjs +39 -4
- package/dist/openapi/resolve.d.mts +60 -9
- package/dist/openapi/resolve.mjs +86 -23
- package/dist/react/SchemaComponent.d.mts +4 -4
- package/dist/react/SchemaComponent.mjs +32 -4
- package/dist/react/SchemaView.d.mts +2 -2
- package/dist/react/fieldPath.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/react/headlessRenderers.mjs +1 -1
- package/dist/{ref-C8JbwfiS.d.mts → ref-si8ViYun.d.mts} +6 -1
- package/dist/{renderer-SOIbJBtk.d.mts → renderer-DI6ZYf7a.d.mts} +1 -1
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/{typeInference-CDoD_LZ_.d.mts → typeInference-Bxw3NOG1.d.mts} +162 -48
- package/dist/{types-C9zw9wbX.d.mts → types-BnxPEElk.d.mts} +12 -2
- 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
|
-
|
|
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
|
-
|
|
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,
|
|
1530
|
+
result[key] = rewriteRelativeRefsValue(value, nextBase, docBase, appendPointer(pointer, key), diagnostics, visited);
|
|
1238
1531
|
}
|
|
1239
1532
|
return result;
|
|
1240
1533
|
}
|
|
1241
|
-
function rewriteRelativeRefsValue(value,
|
|
1242
|
-
if (Array.isArray(value))
|
|
1243
|
-
|
|
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,
|
|
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,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__ */
|
|
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-
|
|
2
|
-
import {
|
|
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
|
-
|
|
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 };
|