@scalar/openapi-to-markdown 1.5.3 → 1.6.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/CHANGELOG.md +29 -0
- package/README.md +27 -1
- package/dist/create-markdown-from-openapi.d.ts +5 -2
- package/dist/create-markdown-from-openapi.d.ts.map +1 -1
- package/dist/create-markdown-from-openapi.js +7 -3
- package/dist/document-anchors.d.ts.map +1 -1
- package/dist/document-anchors.js +4 -2
- package/dist/document-examples.d.ts.map +1 -1
- package/dist/document-examples.js +10 -5
- package/dist/get-markdown-examples.d.ts.map +1 -1
- package/dist/get-markdown-examples.js +48 -24
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/load-document.d.ts +8 -1
- package/dist/load-document.d.ts.map +1 -1
- package/dist/load-document.js +34 -23
- package/dist/markdown/index.d.ts +5 -0
- package/dist/markdown/index.d.ts.map +1 -0
- package/dist/markdown/index.js +4 -0
- package/dist/markdown-nodes.d.ts.map +1 -1
- package/dist/markdown-nodes.js +1 -0
- package/dist/parse-description.d.ts.map +1 -1
- package/dist/parse-description.js +36 -18
- package/dist/render-document.d.ts.map +1 -1
- package/dist/render-document.js +50 -26
- package/dist/render-examples.d.ts.map +1 -1
- package/dist/render-examples.js +8 -4
- package/dist/render-operation-details.d.ts.map +1 -1
- package/dist/render-operation-details.js +28 -14
- package/dist/render-operation.d.ts.map +1 -1
- package/dist/render-operation.js +60 -31
- package/dist/render-schema.d.ts.map +1 -1
- package/dist/render-schema.js +145 -77
- package/dist/render-security.d.ts.map +1 -1
- package/dist/render-security.js +12 -6
- package/dist/render-tags.d.ts +7 -0
- package/dist/render-tags.d.ts.map +1 -0
- package/dist/render-tags.js +73 -0
- package/dist/restore-boolean-schemas.d.ts.map +1 -1
- package/dist/restore-boolean-schemas.js +22 -11
- package/dist/select-document.d.ts.map +1 -1
- package/dist/select-document.js +5 -1
- package/package.json +14 -9
package/dist/render-schema.js
CHANGED
|
@@ -41,10 +41,12 @@ const getReferenceName = (ref) => {
|
|
|
41
41
|
/** Only references that add nothing structural can be replaced by the schema they point to. */
|
|
42
42
|
const getSharedName = (input, view) => {
|
|
43
43
|
const ref = isObject(input) ? input.$ref : undefined;
|
|
44
|
-
if (typeof ref !== 'string' || typeof view.schema !== 'object')
|
|
44
|
+
if (typeof ref !== 'string' || typeof view.schema !== 'object') {
|
|
45
45
|
return undefined;
|
|
46
|
-
|
|
46
|
+
}
|
|
47
|
+
if (Object.keys(input).some((key) => structuralKeywords.has(key))) {
|
|
47
48
|
return undefined;
|
|
49
|
+
}
|
|
48
50
|
const structured = Boolean(view.allOf?.length || view.anyOf?.length || view.oneOf?.length || view.properties.length) ||
|
|
49
51
|
view.not !== undefined ||
|
|
50
52
|
view.items !== undefined ||
|
|
@@ -67,13 +69,16 @@ const getSharedIdentity = (input) => {
|
|
|
67
69
|
const resolveMarkdownSchema = (input) => {
|
|
68
70
|
const target = getResolvedRef(input);
|
|
69
71
|
const merged = getResolvedRef(input, mergeSiblingReferences);
|
|
70
|
-
if (typeof target !== 'boolean')
|
|
72
|
+
if (typeof target !== 'boolean') {
|
|
71
73
|
return merged;
|
|
74
|
+
}
|
|
72
75
|
if (!isObject(merged) ||
|
|
73
|
-
!Object.keys(merged).some((key) => !['$ref', '$ref-value', '$global', '$status'].includes(key)))
|
|
76
|
+
!Object.keys(merged).some((key) => !['$ref', '$ref-value', '$global', '$status'].includes(key))) {
|
|
74
77
|
return target;
|
|
75
|
-
|
|
78
|
+
}
|
|
79
|
+
if (target) {
|
|
76
80
|
return merged;
|
|
81
|
+
}
|
|
77
82
|
// A false target remains impossible, even when siblings describe a type or annotations.
|
|
78
83
|
return { ...merged, allOf: [false, ...(Array.isArray(merged.allOf) ? merged.allOf : [])] };
|
|
79
84
|
};
|
|
@@ -85,19 +90,23 @@ const wrapperKeys = new Set([...annotationKeys, ...referenceKeys, '__scalar_', '
|
|
|
85
90
|
const primitiveTypes = new Set(['string', 'number', 'integer', 'boolean', 'null']);
|
|
86
91
|
/** Infer the JSON type of a literal, for `const` and `enum` schemas that do not declare one. */
|
|
87
92
|
const getJsonType = (value) => {
|
|
88
|
-
if (value === null)
|
|
93
|
+
if (value === null) {
|
|
89
94
|
return 'null';
|
|
90
|
-
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(value)) {
|
|
91
97
|
return 'array';
|
|
92
|
-
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === 'number') {
|
|
93
100
|
return Number.isInteger(value) ? 'integer' : 'number';
|
|
101
|
+
}
|
|
94
102
|
return typeof value;
|
|
95
103
|
};
|
|
96
104
|
/** A schema that only allows `null`, as in `anyOf: [X, { type: 'null' }]`. */
|
|
97
105
|
const isNullSchema = (input) => {
|
|
98
106
|
const schema = getResolvedRef(input);
|
|
99
|
-
if (!isObject(schema))
|
|
107
|
+
if (!isObject(schema)) {
|
|
100
108
|
return false;
|
|
109
|
+
}
|
|
101
110
|
const type = Array.isArray(schema.type) && schema.type.length === 1 ? schema.type[0] : schema.type;
|
|
102
111
|
return (type === 'null' &&
|
|
103
112
|
Object.keys(schema).every((key) => key === 'type' || key === 'title' || key === 'description' || referenceKeys.has(key)));
|
|
@@ -114,37 +123,40 @@ const getWrapped = (value) => {
|
|
|
114
123
|
value.not !== undefined ||
|
|
115
124
|
value.discriminator ||
|
|
116
125
|
value.enum ||
|
|
117
|
-
value.const !== undefined)
|
|
126
|
+
value.const !== undefined) {
|
|
118
127
|
return undefined;
|
|
128
|
+
}
|
|
119
129
|
const allOf = value.allOf ?? [];
|
|
120
130
|
const anyOf = value.anyOf ?? [];
|
|
121
131
|
const oneOf = value.oneOf ?? [];
|
|
122
|
-
if (allOf.length === 1 && !anyOf.length && !oneOf.length)
|
|
132
|
+
if (allOf.length === 1 && !anyOf.length && !oneOf.length) {
|
|
123
133
|
return { core: allOf[0], nullable: false };
|
|
134
|
+
}
|
|
124
135
|
const union = anyOf.length ? anyOf : oneOf;
|
|
125
|
-
if (allOf.length || (anyOf.length && oneOf.length) || union.length !== 2)
|
|
136
|
+
if (allOf.length || (anyOf.length && oneOf.length) || union.length !== 2) {
|
|
126
137
|
return undefined;
|
|
138
|
+
}
|
|
127
139
|
const core = union.filter((branch) => !isNullSchema(branch));
|
|
128
140
|
return core.length === 1 ? { core: core[0], nullable: true } : undefined;
|
|
129
141
|
};
|
|
130
142
|
/** Join alternatives with `|`, keeping neighbouring plain types in one code span. */
|
|
131
143
|
const joinAlternatives = (alternatives) => {
|
|
132
144
|
// `array of string | null` would read as an array of nullable strings.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
});
|
|
145
|
+
const groupedAlternatives = alternatives.map((alternative) => {
|
|
146
|
+
const only = alternative.length === 1 ? alternative[0] : undefined;
|
|
147
|
+
return alternatives.length > 1 && only?.type === 'inlineCode' && only.value.startsWith('array of ')
|
|
148
|
+
? [inlineCode(`(${only.value})`)]
|
|
149
|
+
: alternative;
|
|
150
|
+
});
|
|
140
151
|
const nodes = [];
|
|
141
152
|
let plain = [];
|
|
142
153
|
const flushPlain = () => {
|
|
143
|
-
if (plain.length)
|
|
154
|
+
if (plain.length) {
|
|
144
155
|
nodes.push(...(nodes.length ? [text(' | ')] : []), inlineCode(plain.join(' | ')));
|
|
156
|
+
}
|
|
145
157
|
plain = [];
|
|
146
158
|
};
|
|
147
|
-
for (const alternative of
|
|
159
|
+
for (const alternative of groupedAlternatives) {
|
|
148
160
|
const only = alternative.length === 1 ? alternative[0] : undefined;
|
|
149
161
|
if (only?.type === 'inlineCode') {
|
|
150
162
|
plain.push(only.value);
|
|
@@ -163,8 +175,9 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
163
175
|
const unwrapping = new Set();
|
|
164
176
|
const view = (input) => {
|
|
165
177
|
const cached = typeof input === 'object' ? views.get(input) : undefined;
|
|
166
|
-
if (cached)
|
|
178
|
+
if (cached) {
|
|
167
179
|
return cached;
|
|
180
|
+
}
|
|
168
181
|
// Keep the linked document's nested identities and boolean schemas. Coercing a
|
|
169
182
|
// second time here would discard boolean children and copy recursive targets.
|
|
170
183
|
const schema = resolveMarkdownSchema(input);
|
|
@@ -173,9 +186,10 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
173
186
|
const properties = Object.entries(value.properties ?? {})
|
|
174
187
|
.filter(([, child]) => typeof child === 'boolean' || (child !== null && typeof child === 'object'))
|
|
175
188
|
.sort(([a], [b]) => Number(required.has(b)) - Number(required.has(a)) || a.localeCompare(b));
|
|
189
|
+
const booleanType = schema ? 'any' : 'never';
|
|
176
190
|
let result = {
|
|
177
191
|
...value,
|
|
178
|
-
type: typeof schema === 'boolean' ?
|
|
192
|
+
type: typeof schema === 'boolean' ? booleanType : value.type,
|
|
179
193
|
schema: schema,
|
|
180
194
|
required,
|
|
181
195
|
properties,
|
|
@@ -189,18 +203,21 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
189
203
|
const annotations = Object.fromEntries(annotationKeys.flatMap((key) => (result[key] === undefined ? [] : [[key, result[key]]])));
|
|
190
204
|
result = { ...inner, ...annotations, core: wrapped.core, nullable: wrapped.nullable || inner.nullable };
|
|
191
205
|
}
|
|
192
|
-
if (typeof input === 'object')
|
|
206
|
+
if (typeof input === 'object') {
|
|
193
207
|
views.set(input, result);
|
|
208
|
+
}
|
|
194
209
|
return result;
|
|
195
210
|
};
|
|
196
211
|
const primitives = new WeakMap();
|
|
197
212
|
/** Primitives, enums, consts and aliases of them, whose whole definition fits on one line. */
|
|
198
213
|
const isPrimitive = (input) => {
|
|
199
|
-
if (typeof input !== 'object')
|
|
214
|
+
if (typeof input !== 'object') {
|
|
200
215
|
return true;
|
|
216
|
+
}
|
|
201
217
|
const cached = primitives.get(input);
|
|
202
|
-
if (cached !== undefined)
|
|
218
|
+
if (cached !== undefined) {
|
|
203
219
|
return cached;
|
|
220
|
+
}
|
|
204
221
|
// Assume a recursive alias is not primitive while it is being checked.
|
|
205
222
|
primitives.set(input, false);
|
|
206
223
|
const value = view(input);
|
|
@@ -248,16 +265,21 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
248
265
|
];
|
|
249
266
|
/** The declared types, or the types implied by `const`, `enum`, properties or items. */
|
|
250
267
|
const getTypes = (value) => {
|
|
251
|
-
if (value.type !== undefined)
|
|
268
|
+
if (value.type !== undefined) {
|
|
252
269
|
return [value.type].flat();
|
|
253
|
-
|
|
270
|
+
}
|
|
271
|
+
if (value.const !== undefined) {
|
|
254
272
|
return [getJsonType(value.const)];
|
|
255
|
-
|
|
273
|
+
}
|
|
274
|
+
if (value.enum?.length) {
|
|
256
275
|
return [...new Set(value.enum.map(getJsonType))];
|
|
257
|
-
|
|
276
|
+
}
|
|
277
|
+
if (value.properties.length || isObject(value.additionalProperties)) {
|
|
258
278
|
return ['object'];
|
|
259
|
-
|
|
279
|
+
}
|
|
280
|
+
if (value.items !== undefined) {
|
|
260
281
|
return ['array'];
|
|
282
|
+
}
|
|
261
283
|
return [];
|
|
262
284
|
};
|
|
263
285
|
const forDocument = (models = {}, settings = {}, destinations) => {
|
|
@@ -270,14 +292,16 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
270
292
|
let nodeCount = 0;
|
|
271
293
|
/** The reference a nested schema links to in linked mode, instead of expanding its target. */
|
|
272
294
|
const getLink = (input) => {
|
|
273
|
-
if ((!linked && !destinations) || !isObject(input))
|
|
295
|
+
if ((!linked && !destinations) || !isObject(input)) {
|
|
274
296
|
return undefined;
|
|
297
|
+
}
|
|
275
298
|
const ref = getRef(input);
|
|
276
299
|
if (ref !== undefined) {
|
|
277
300
|
// A primitive alias is shorter than a link to it, and saves the reader a page.
|
|
278
301
|
const resolved = getResolvedRef(input) !== undefined;
|
|
279
|
-
if (!linked && !destinations?.has(getReferenceName(ref)))
|
|
302
|
+
if (!linked && !destinations?.has(getReferenceName(ref))) {
|
|
280
303
|
return undefined;
|
|
304
|
+
}
|
|
281
305
|
return resolved && inlinePrimitives && isPrimitive(input) ? undefined : ref;
|
|
282
306
|
}
|
|
283
307
|
const core = view(input).core;
|
|
@@ -300,11 +324,13 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
300
324
|
(!structuralKeywords.has(key) ||
|
|
301
325
|
numericAnnotations.some((annotation) => annotation === key) ||
|
|
302
326
|
(key === 'additionalProperties' && entry === false)) &&
|
|
303
|
-
!(key in own))
|
|
327
|
+
!(key in own)) {
|
|
304
328
|
own[key] = entry;
|
|
329
|
+
}
|
|
305
330
|
}
|
|
306
|
-
if (getRef(current) !== undefined)
|
|
331
|
+
if (getRef(current) !== undefined) {
|
|
307
332
|
break;
|
|
333
|
+
}
|
|
308
334
|
current = view(current).core;
|
|
309
335
|
}
|
|
310
336
|
return view(own);
|
|
@@ -323,8 +349,9 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
323
349
|
complete: !structural,
|
|
324
350
|
};
|
|
325
351
|
}
|
|
326
|
-
if (typeof value.schema === 'boolean')
|
|
352
|
+
if (typeof value.schema === 'boolean') {
|
|
327
353
|
return { nodes: [], complete: true };
|
|
354
|
+
}
|
|
328
355
|
const types = getTypes(value);
|
|
329
356
|
const alternatives = [];
|
|
330
357
|
let complete = !value.properties.length &&
|
|
@@ -340,10 +367,12 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
340
367
|
if (complete &&
|
|
341
368
|
!types.length &&
|
|
342
369
|
branches.length === union.length &&
|
|
343
|
-
branches.every((branch, index) => branch.complete && branch.nodes.length && !hasAnnotations(getOwnView(union[index]))))
|
|
370
|
+
branches.every((branch, index) => branch.complete && branch.nodes.length && !hasAnnotations(getOwnView(union[index])))) {
|
|
344
371
|
alternatives.push(...branches.map((branch) => branch.nodes));
|
|
345
|
-
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
346
374
|
complete = false;
|
|
375
|
+
}
|
|
347
376
|
}
|
|
348
377
|
for (const type of types) {
|
|
349
378
|
if (type !== 'array' || value.items === undefined) {
|
|
@@ -361,53 +390,67 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
361
390
|
? [inlineCode(only.value.includes(' | ') ? `array of (${only.value})` : `array of ${only.value}`)]
|
|
362
391
|
: [text('array of '), ...(items.nodes.length > 1 ? [text('('), ...items.nodes, text(')')] : items.nodes)]);
|
|
363
392
|
}
|
|
364
|
-
if (!types.length && value.items !== undefined)
|
|
393
|
+
if (!types.length && value.items !== undefined) {
|
|
365
394
|
complete = false;
|
|
395
|
+
}
|
|
366
396
|
// Without a type to name, a nullable schema says so and leaves its structure to the lines below.
|
|
367
|
-
if (value.nullable && !types.includes('null'))
|
|
397
|
+
if (value.nullable && !types.includes('null')) {
|
|
368
398
|
alternatives.push(alternatives.length || complete ? [inlineCode('null')] : [text('nullable')]);
|
|
399
|
+
}
|
|
369
400
|
// A schema without types or structure accepts any value.
|
|
370
|
-
if (!alternatives.length && complete)
|
|
401
|
+
if (!alternatives.length && complete) {
|
|
371
402
|
alternatives.push([inlineCode('any')]);
|
|
403
|
+
}
|
|
372
404
|
return { nodes: joinAlternatives(alternatives), complete };
|
|
373
405
|
};
|
|
374
406
|
/** The one-line summary of a schema: its type, then its annotations. */
|
|
375
407
|
const summarize = (input, { nested, label = getLabel(input, nested), showType = true, }) => {
|
|
376
408
|
const linkedLabel = (nested || destinations !== undefined) && getLink(input) !== undefined;
|
|
377
409
|
const value = linkedLabel ? getLinkAnnotations(input) : view(input);
|
|
378
|
-
if (typeof value.schema === 'boolean')
|
|
410
|
+
if (typeof value.schema === 'boolean') {
|
|
379
411
|
return { line: [text(value.schema ? 'any (true schema)' : 'never (false schema)')] };
|
|
412
|
+
}
|
|
380
413
|
const nodes = showType ? [...label.nodes] : [];
|
|
381
414
|
const add = (name, entry) => {
|
|
382
|
-
if (entry !== undefined)
|
|
415
|
+
if (entry !== undefined) {
|
|
383
416
|
nodes.push(text(`${nodes.length ? ', ' : ''}${name}: `), inlineCode(entry));
|
|
417
|
+
}
|
|
384
418
|
};
|
|
385
419
|
const flag = (name, entry) => {
|
|
386
|
-
if (entry)
|
|
420
|
+
if (entry) {
|
|
387
421
|
nodes.push(text(`${nodes.length ? ', ' : ''}${name}`));
|
|
422
|
+
}
|
|
388
423
|
};
|
|
389
424
|
// Later references to a shared schema point back to this name.
|
|
390
425
|
const ref = getRef(input);
|
|
391
426
|
if (!linkedLabel && value.name !== undefined) {
|
|
392
427
|
// In linked mode the expanded schema also has its own page.
|
|
393
|
-
if (linked && ref !== undefined)
|
|
428
|
+
if (linked && ref !== undefined) {
|
|
394
429
|
nodes.push(text(`${nodes.length ? ', ' : ''}schema: `), referenceNode(ref));
|
|
395
|
-
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
396
432
|
add('schema', value.name);
|
|
433
|
+
}
|
|
397
434
|
}
|
|
398
|
-
if (linkedLabel && value.type !== undefined)
|
|
435
|
+
if (linkedLabel && value.type !== undefined) {
|
|
399
436
|
add('type', [value.type].flat().join(' | '));
|
|
437
|
+
}
|
|
400
438
|
add('format', value.format);
|
|
401
|
-
if (value.enum)
|
|
439
|
+
if (value.enum) {
|
|
402
440
|
add('possible values', value.enum.map((entry) => JSON.stringify(entry)).join(', '));
|
|
403
|
-
|
|
441
|
+
}
|
|
442
|
+
if (value.const !== undefined) {
|
|
404
443
|
add('const', JSON.stringify(value.const));
|
|
405
|
-
|
|
444
|
+
}
|
|
445
|
+
if (value.default !== undefined) {
|
|
406
446
|
add('default', JSON.stringify(value.default));
|
|
407
|
-
|
|
447
|
+
}
|
|
448
|
+
for (const key of numericAnnotations) {
|
|
408
449
|
add(key, value[key]);
|
|
409
|
-
|
|
450
|
+
}
|
|
451
|
+
for (const key of ['readOnly', 'writeOnly', 'deprecated']) {
|
|
410
452
|
flag(key, value[key]);
|
|
453
|
+
}
|
|
411
454
|
flag('no additional properties', value.additionalProperties === false);
|
|
412
455
|
// A reference without its own description still says what it is, from the schema it links to.
|
|
413
456
|
return { line: nodes, description: value.description ?? (linkedLabel ? view(input).description : undefined) };
|
|
@@ -420,18 +463,21 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
420
463
|
: {};
|
|
421
464
|
if (Object.keys(siblings).length && !options.hideDetails) {
|
|
422
465
|
const { line, description } = summarize(siblings, { nested: false, showType: false });
|
|
423
|
-
if (line.length)
|
|
466
|
+
if (line.length) {
|
|
424
467
|
nodes.push(paragraph(...line));
|
|
425
|
-
|
|
468
|
+
}
|
|
469
|
+
if (!options.hideDescription) {
|
|
426
470
|
nodes.push(...describe(description));
|
|
471
|
+
}
|
|
427
472
|
}
|
|
428
473
|
nodes.push(paragraph(emphasis(text('Schema '), inlineCode(name), text(` is shown ${location}.`))));
|
|
429
474
|
return nodes;
|
|
430
475
|
};
|
|
431
476
|
/** Print a schema's summary line and description, unless the caller already did. */
|
|
432
477
|
const header = (input, depth, options, showType = true) => {
|
|
433
|
-
if (options.hideDetails)
|
|
478
|
+
if (options.hideDetails) {
|
|
434
479
|
return [];
|
|
480
|
+
}
|
|
435
481
|
const { line, description } = summarize(input, { nested: depth > 0, showType });
|
|
436
482
|
return [...(line.length ? [paragraph(...line)] : []), ...(options.hideDescription ? [] : describe(description))];
|
|
437
483
|
};
|
|
@@ -471,8 +517,9 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
471
517
|
// A wrapper prints its merged summary, then its core schema's structure.
|
|
472
518
|
if (value.core !== undefined) {
|
|
473
519
|
const nodes = header(input, depth, options);
|
|
474
|
-
if (label.complete)
|
|
520
|
+
if (label.complete) {
|
|
475
521
|
return nodes;
|
|
522
|
+
}
|
|
476
523
|
return [
|
|
477
524
|
...nodes,
|
|
478
525
|
...render(value.core, depth, ancestors, {
|
|
@@ -492,18 +539,23 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
492
539
|
const referenceOptions = options.name === undefined ? options : { ...options, hideDetails: true };
|
|
493
540
|
return reference(input, referenceOptions, previous.name, 'above');
|
|
494
541
|
}
|
|
495
|
-
if (value.name !== undefined && options.name === undefined && depth >= MAX_DEPTH && sections.has(shared))
|
|
542
|
+
if (value.name !== undefined && options.name === undefined && depth >= MAX_DEPTH && sections.has(shared)) {
|
|
496
543
|
return reference(input, options, value.name, 'below under Schemas');
|
|
544
|
+
}
|
|
497
545
|
}
|
|
498
|
-
if (depth >= MAX_DEPTH)
|
|
546
|
+
if (depth >= MAX_DEPTH) {
|
|
499
547
|
return [paragraph(text('[Maximum schema depth reached]'))];
|
|
500
|
-
|
|
548
|
+
}
|
|
549
|
+
if (nodeCount >= maxNodes) {
|
|
501
550
|
return [paragraph(emphasis(text('[Schema output truncated]')))];
|
|
551
|
+
}
|
|
502
552
|
nodeCount++;
|
|
503
|
-
if (shared && name !== undefined && !shown.has(shared))
|
|
553
|
+
if (shared && name !== undefined && !shown.has(shared)) {
|
|
504
554
|
shown.set(shared, { name, description: value.description });
|
|
505
|
-
|
|
555
|
+
}
|
|
556
|
+
if (typeof value.schema === 'boolean') {
|
|
506
557
|
return header(input, depth, options);
|
|
558
|
+
}
|
|
507
559
|
const childAncestors = [...ancestors, identity];
|
|
508
560
|
const nodes = [];
|
|
509
561
|
// Discriminator values that name a branch are printed beside it instead of in a separate list.
|
|
@@ -516,8 +568,9 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
516
568
|
['anyOf', 'Any of:'],
|
|
517
569
|
['oneOf', 'One of:'],
|
|
518
570
|
]) {
|
|
519
|
-
if (!value[key]?.length)
|
|
571
|
+
if (!value[key]?.length) {
|
|
520
572
|
continue;
|
|
573
|
+
}
|
|
521
574
|
// One list item per branch keeps the boundary between branches visible.
|
|
522
575
|
const branches = value[key].map((child) => {
|
|
523
576
|
const blocks = render(child, depth + 1, childAncestors, { showType: true });
|
|
@@ -525,19 +578,22 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
525
578
|
const values = key === 'allOf' || ref === undefined ? [] : mappings.filter(([, target]) => target === ref);
|
|
526
579
|
const first = blocks[0];
|
|
527
580
|
if (values.length && first?.type === 'paragraph') {
|
|
528
|
-
for (const [name] of values)
|
|
581
|
+
for (const [name] of values) {
|
|
529
582
|
mapped.add(name);
|
|
583
|
+
}
|
|
530
584
|
first.children.push(text(`${first.children.length ? ', ' : ''}${value.discriminator.propertyName}: `), inlineCode(values.map(([name]) => name).join(', ')));
|
|
531
585
|
}
|
|
532
586
|
return item(...blocks);
|
|
533
587
|
});
|
|
534
588
|
const discriminated = key !== 'allOf' && value.discriminator;
|
|
535
589
|
nodes.push(paragraph(strong(text(title)), ...(discriminated ? [text(' discriminated by '), inlineCode(value.discriminator.propertyName)] : [])), list(branches));
|
|
536
|
-
if (discriminated)
|
|
590
|
+
if (discriminated) {
|
|
537
591
|
discriminatorShown = true;
|
|
592
|
+
}
|
|
538
593
|
}
|
|
539
|
-
if (value.not !== undefined)
|
|
594
|
+
if (value.not !== undefined) {
|
|
540
595
|
nodes.push(paragraph(strong(text('Not:'))), ...render(value.not, depth + 1, childAncestors));
|
|
596
|
+
}
|
|
541
597
|
}
|
|
542
598
|
// Child sections imply a single container type, but never its nullable alternatives.
|
|
543
599
|
const impliedType = !options.showType &&
|
|
@@ -549,8 +605,9 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
549
605
|
// A reference sibling may require a field declared only in the target schema.
|
|
550
606
|
const declared = new Set(value.properties.map(([name]) => name));
|
|
551
607
|
const required = [...value.required].filter((name) => !declared.has(name));
|
|
552
|
-
if (required.length)
|
|
608
|
+
if (required.length) {
|
|
553
609
|
nodes.push(paragraph(strong(text('Required fields:')), text(' '), inlineCode(required.join(', '))));
|
|
610
|
+
}
|
|
554
611
|
}
|
|
555
612
|
if (value.properties.length) {
|
|
556
613
|
const properties = value.properties.map(([name, schema]) => {
|
|
@@ -559,15 +616,17 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
559
616
|
const title = [
|
|
560
617
|
strong(inlineCode(name), ...(value.required.has(name) ? [text(' (required)')] : [])),
|
|
561
618
|
];
|
|
562
|
-
if (line.length)
|
|
619
|
+
if (line.length) {
|
|
563
620
|
title.push(text(': '), ...line);
|
|
621
|
+
}
|
|
564
622
|
const blocks = [paragraph(...title), ...describe(description)];
|
|
565
|
-
if (!childLabel.complete)
|
|
623
|
+
if (!childLabel.complete) {
|
|
566
624
|
blocks.push(...render(schema, depth + 1, childAncestors, {
|
|
567
625
|
hideDetails: true,
|
|
568
626
|
hideDescription: true,
|
|
569
627
|
property: true,
|
|
570
628
|
}));
|
|
629
|
+
}
|
|
571
630
|
return item(...blocks);
|
|
572
631
|
});
|
|
573
632
|
nodes.push(list(properties));
|
|
@@ -577,13 +636,15 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
577
636
|
(value.type === 'array' || value.type === undefined || [value.type].flat().includes('array'))) {
|
|
578
637
|
nodes.push(paragraph(strong(text(options.property ? 'Items:' : 'Array of:'))), ...render(value.items, depth + 1, childAncestors));
|
|
579
638
|
}
|
|
580
|
-
if (isObject(value.additionalProperties))
|
|
639
|
+
if (isObject(value.additionalProperties)) {
|
|
581
640
|
nodes.push(paragraph(strong(text('Additional properties:'))), ...render(value.additionalProperties, depth + 1, childAncestors));
|
|
641
|
+
}
|
|
582
642
|
const unmapped = mappings.filter(([name]) => !mapped.has(name));
|
|
583
643
|
if (value.discriminator && (!discriminatorShown || unmapped.length)) {
|
|
584
644
|
nodes.push(paragraph(strong(text('Discriminator:')), text(' '), inlineCode(value.discriminator.propertyName)));
|
|
585
|
-
if (unmapped.length)
|
|
645
|
+
if (unmapped.length) {
|
|
586
646
|
nodes.push(list(unmapped.map(([name, target]) => item(paragraph(inlineCode(name), text(': '), linked || destinations ? referenceNode(target) : inlineCode(target))))));
|
|
647
|
+
}
|
|
587
648
|
}
|
|
588
649
|
return nodes;
|
|
589
650
|
};
|
|
@@ -600,34 +661,41 @@ export const createSchemaRenderer = ({ maxNodes = MAX_NODES } = {}) => {
|
|
|
600
661
|
patternProperties: 'map',
|
|
601
662
|
};
|
|
602
663
|
const getExampleSchema = (input, root) => {
|
|
603
|
-
if (!isObject(input))
|
|
664
|
+
if (!isObject(input)) {
|
|
604
665
|
return input;
|
|
666
|
+
}
|
|
605
667
|
if (!root && getLink(input) !== undefined) {
|
|
606
668
|
const types = getTypes(view(input));
|
|
607
669
|
return types.includes('array') && !types.includes('object') ? { type: 'array', items: {} } : { type: 'object' };
|
|
608
670
|
}
|
|
609
671
|
// Primitive references stay linked; a spread copy would lose the non-enumerable target.
|
|
610
672
|
if (getRef(input) !== undefined) {
|
|
611
|
-
if (!root)
|
|
673
|
+
if (!root) {
|
|
612
674
|
return input;
|
|
675
|
+
}
|
|
613
676
|
// Merging keeps the `$ref` key, which must not be followed again.
|
|
614
677
|
const merged = getResolvedRef(input, mergeSiblingReferences);
|
|
615
|
-
if (!isObject(merged))
|
|
678
|
+
if (!isObject(merged)) {
|
|
616
679
|
return merged;
|
|
680
|
+
}
|
|
617
681
|
const { $ref: _ref, ...target } = merged;
|
|
618
682
|
return getExampleSchema(target, true);
|
|
619
683
|
}
|
|
620
684
|
const copy = { ...input };
|
|
621
685
|
for (const [key, kind] of Object.entries(schemaKeywords)) {
|
|
622
686
|
const value = input[key];
|
|
623
|
-
if (kind === 'one')
|
|
687
|
+
if (kind === 'one') {
|
|
624
688
|
copy[key] = getExampleSchema(value, false);
|
|
625
|
-
|
|
689
|
+
}
|
|
690
|
+
else if (kind === 'list' && Array.isArray(value)) {
|
|
626
691
|
copy[key] = value.map((entry) => getExampleSchema(entry, false));
|
|
627
|
-
|
|
692
|
+
}
|
|
693
|
+
else if (kind === 'map' && isObject(value)) {
|
|
628
694
|
copy[key] = Object.fromEntries(Object.entries(value).map(([name, entry]) => [name, getExampleSchema(entry, false)]));
|
|
629
|
-
|
|
695
|
+
}
|
|
696
|
+
if (copy[key] === undefined) {
|
|
630
697
|
delete copy[key];
|
|
698
|
+
}
|
|
631
699
|
}
|
|
632
700
|
return copy;
|
|
633
701
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-security.d.ts","sourceRoot":"","sources":["../src/render-security.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AACnG,OAAO,KAAK,EAAE,OAAO,EAA6B,WAAW,EAAE,MAAM,OAAO,CAAA;AAG5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"render-security.d.ts","sourceRoot":"","sources":["../src/render-security.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AACnG,OAAO,KAAK,EAAE,OAAO,EAA6B,WAAW,EAAE,MAAM,OAAO,CAAA;AAG5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAwD5D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,cAAc,eAAe,CAAC,UAAU,CAAC,EACzC,SAAS,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,EACtE,aAAa,iBAAiB,EAC9B,QAAO,OAAO,CAAC,OAAO,CAAK,KAC1B,OAAO,CAAC,WAAW,EAAE,CAgCvB,CAAA"}
|
package/dist/render-security.js
CHANGED
|
@@ -33,8 +33,9 @@ const summarizeScheme = (scheme) => {
|
|
|
33
33
|
['refreshUrl', 'refresh'],
|
|
34
34
|
]) {
|
|
35
35
|
// Coercion fills in optional URLs as empty strings.
|
|
36
|
-
if (typeof urls[key] === 'string' && urls[key])
|
|
36
|
+
if (typeof urls[key] === 'string' && urls[key]) {
|
|
37
37
|
nodes.push(text(`, ${label} `), inlineCode(urls[key]));
|
|
38
|
+
}
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
return nodes;
|
|
@@ -53,14 +54,17 @@ const summarizeScheme = (scheme) => {
|
|
|
53
54
|
* no authentication is required.
|
|
54
55
|
*/
|
|
55
56
|
export const renderSecurity = async (requirements, schemes, description, level = 4) => {
|
|
56
|
-
if (!requirements)
|
|
57
|
+
if (!requirements) {
|
|
57
58
|
return [];
|
|
59
|
+
}
|
|
58
60
|
const nodes = [heading(level, text('Authentication'))];
|
|
59
|
-
if (!requirements.length)
|
|
61
|
+
if (!requirements.length) {
|
|
60
62
|
nodes.push(paragraph(text('No authentication required.')));
|
|
63
|
+
}
|
|
61
64
|
for (const [index, requirement] of requirements.entries()) {
|
|
62
|
-
if (index)
|
|
65
|
+
if (index) {
|
|
63
66
|
nodes.push(paragraph(text('Or:')));
|
|
67
|
+
}
|
|
64
68
|
const entries = Object.entries(requirement);
|
|
65
69
|
if (!entries.length) {
|
|
66
70
|
nodes.push(paragraph(text('No authentication required.')));
|
|
@@ -70,10 +74,12 @@ export const renderSecurity = async (requirements, schemes, description, level =
|
|
|
70
74
|
for (const [name, scopes] of entries) {
|
|
71
75
|
const scheme = getResolvedRef(schemes?.[name]);
|
|
72
76
|
const line = [strong(text(name))];
|
|
73
|
-
if (scheme)
|
|
77
|
+
if (scheme) {
|
|
74
78
|
line.push(text(': '), ...summarizeScheme(scheme));
|
|
75
|
-
|
|
79
|
+
}
|
|
80
|
+
if (scopes?.length) {
|
|
76
81
|
line.push(text(', scopes: '), inlineCode(scopes.join(', ')));
|
|
82
|
+
}
|
|
77
83
|
entriesNodes.push(item(paragraph(...line), ...(await description(scheme?.description))));
|
|
78
84
|
}
|
|
79
85
|
nodes.push(list(entriesNodes));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TagObject } from '@scalar/workspace-store/schemas/v3.2/strict/tag';
|
|
2
|
+
/** Keep tag ancestry visible without exceeding Markdown's six heading levels. */
|
|
3
|
+
export declare const getTagSections: (tags: TagObject[], openapiVersion: string) => {
|
|
4
|
+
tag: TagObject;
|
|
5
|
+
title: string;
|
|
6
|
+
}[];
|
|
7
|
+
//# sourceMappingURL=render-tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-tags.d.ts","sourceRoot":"","sources":["../src/render-tags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iDAAiD,CAAA;AAEhF,iFAAiF;AACjF,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,EAAE,EAAE,gBAAgB,MAAM,KAAG;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAwE3G,CAAA"}
|