@scalar/openapi-to-markdown 1.5.2 → 1.5.4
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 +13 -0
- package/README.md +3 -0
- 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/load-document.d.ts.map +1 -1
- package/dist/load-document.js +16 -10
- 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 -25
- 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/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 +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @scalar/openapi-to-markdown
|
|
2
2
|
|
|
3
|
+
## 1.5.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#10559](https://github.com/scalar/scalar/pull/10559): Clean up Biome warnings in package code and tests. Handle rejected debounced callbacks and await streaming writes.
|
|
8
|
+
- [#10505](https://github.com/scalar/scalar/pull/10505): Apply formatting and lint compatibility changes for Biome 2.5.15.
|
|
9
|
+
|
|
10
|
+
## 1.5.3
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [#10484](https://github.com/scalar/scalar/pull/10484): Use OpenAPI 3.2 tag summaries as Markdown headings, falling back to tag names.
|
|
15
|
+
|
|
3
16
|
## 1.5.2
|
|
4
17
|
|
|
5
18
|
## 1.5.1
|
package/README.md
CHANGED
|
@@ -143,6 +143,9 @@ Schema normalization and description parsing are cached within each renderer. Re
|
|
|
143
143
|
schema expansion still tracks ancestors and stops at a depth of ten. Output may use tighter
|
|
144
144
|
list spacing and normalized Markdown escaping compared with earlier versions.
|
|
145
145
|
|
|
146
|
+
For OpenAPI 3.2 descriptions, tag headings use `summary` when present and otherwise
|
|
147
|
+
use `name`. Tag selectors and operation tag metadata continue to use `name`.
|
|
148
|
+
|
|
146
149
|
The package only generates Markdown. To produce HTML, pass the Markdown output to a
|
|
147
150
|
Markdown renderer in your application.
|
|
148
151
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-anchors.d.ts","sourceRoot":"","sources":["../src/document-anchors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,kGAAkG;AAClG,eAAO,MAAM,qBAAqB,QAAO;IACvC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"document-anchors.d.ts","sourceRoot":"","sources":["../src/document-anchors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,kGAAkG;AAClG,eAAO,MAAM,qBAAqB,QAAO;IACvC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAuBhE,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,KAAG,IAAuD,CAAA"}
|
package/dist/document-anchors.js
CHANGED
|
@@ -8,13 +8,15 @@ export const createDocumentAnchors = () => {
|
|
|
8
8
|
get: (kind, identity, label = identity) => {
|
|
9
9
|
const key = JSON.stringify([kind, identity]);
|
|
10
10
|
const previous = ids.get(key);
|
|
11
|
-
if (previous !== undefined)
|
|
11
|
+
if (previous !== undefined) {
|
|
12
12
|
return previous;
|
|
13
|
+
}
|
|
13
14
|
const base = `scalar-${kind}-${label}`;
|
|
14
15
|
let id = slugs.slug(base);
|
|
15
16
|
// A literal suffix in a later name can collide with the slugger's numeric suffix.
|
|
16
|
-
while (used.has(id))
|
|
17
|
+
while (used.has(id)) {
|
|
17
18
|
id = slugs.slug(base);
|
|
19
|
+
}
|
|
18
20
|
used.add(id);
|
|
19
21
|
ids.set(key, id);
|
|
20
22
|
return id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-examples.d.ts","sourceRoot":"","sources":["../src/document-examples.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,KAAK,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAEjF,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,OAAO,mBAAmB,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;CAC9F,CAAA;
|
|
1
|
+
{"version":3,"file":"document-examples.d.ts","sourceRoot":"","sources":["../src/document-examples.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,KAAK,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAEjF,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,OAAO,mBAAmB,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;CAC9F,CAAA;AAeD,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,CAAC,OAAO,qBAAqB,CAAC,KAAG,gBA4C1F,CAAA"}
|
|
@@ -4,8 +4,9 @@ import { getMarkdownExamples } from './get-markdown-examples.js';
|
|
|
4
4
|
const bookkeeping = new Set(['$ref', '$ref-value', '$global', '$status', '__scalar_']);
|
|
5
5
|
/** Sibling constraints make a reference a different example source from its target. */
|
|
6
6
|
const identity = (source) => {
|
|
7
|
-
if (!isObject(source.schema))
|
|
7
|
+
if (!isObject(source.schema)) {
|
|
8
8
|
return undefined;
|
|
9
|
+
}
|
|
9
10
|
const schema = source.schema;
|
|
10
11
|
const target = '$ref' in schema && Object.keys(schema).every((key) => bookkeeping.has(key)) ? getResolvedRef(schema) : schema;
|
|
11
12
|
return isObject(target) ? target : schema;
|
|
@@ -20,12 +21,14 @@ export const createDocumentExamples = (anchors) => {
|
|
|
20
21
|
get: (...args) => {
|
|
21
22
|
const [source, ...settings] = args;
|
|
22
23
|
const schema = identity(source);
|
|
23
|
-
if (!schema || source.example !== undefined || Object.keys(source.examples ?? {}).length)
|
|
24
|
+
if (!schema || source.example !== undefined || Object.keys(source.examples ?? {}).length) {
|
|
24
25
|
return getMarkdownExamples(...args);
|
|
26
|
+
}
|
|
25
27
|
const scope = JSON.stringify(settings);
|
|
26
28
|
const previous = cache.get(schema)?.get(scope);
|
|
27
|
-
if (previous)
|
|
29
|
+
if (previous) {
|
|
28
30
|
return previous;
|
|
31
|
+
}
|
|
29
32
|
// Large APIs must not retain every generated object until the export finishes.
|
|
30
33
|
if (cachedCount >= 256) {
|
|
31
34
|
cache.clear();
|
|
@@ -42,12 +45,14 @@ export const createDocumentExamples = (anchors) => {
|
|
|
42
45
|
const schema = identity(source);
|
|
43
46
|
const entries = schema ? (shown.get(schema) ?? new Map()) : new Map();
|
|
44
47
|
const previous = entries.get(scope);
|
|
45
|
-
if (previous?.value === code.value)
|
|
48
|
+
if (previous?.value === code.value) {
|
|
46
49
|
return { id: previous.id, previous: true };
|
|
50
|
+
}
|
|
47
51
|
const id = anchors.get('example', String(++nextId));
|
|
48
52
|
entries.set(scope, { id, value: code.value });
|
|
49
|
-
if (schema)
|
|
53
|
+
if (schema) {
|
|
50
54
|
shown.set(schema, entries);
|
|
55
|
+
}
|
|
51
56
|
return { id, previous: false };
|
|
52
57
|
},
|
|
53
58
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-markdown-examples.d.ts","sourceRoot":"","sources":["../src/get-markdown-examples.ts"],"names":[],"mappings":"AAMA,4FAA4F;AAC5F,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,gFAAgF;AAChF,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,CACA;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAClB;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CACpB,CAAA;AAYD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,OAAO,EAAE,cAAoC,KAAG,
|
|
1
|
+
{"version":3,"file":"get-markdown-examples.d.ts","sourceRoot":"","sources":["../src/get-markdown-examples.ts"],"names":[],"mappings":"AAMA,4FAA4F;AAC5F,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,gFAAgF;AAChF,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,CACA;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAClB;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CACpB,CAAA;AAYD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,OAAO,EAAE,cAAoC,KAAG,MAmEjG,CAAA;AAED,yFAAyF;AACzF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,aAAa,EACrB,WAAW,MAAM,EACjB,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,gBAAc,KACb,eAAe,EA6DjB,CAAA"}
|
|
@@ -17,35 +17,47 @@ const MAX_GENERATED_EXAMPLE_VALUES = 10_000;
|
|
|
17
17
|
export const countGeneratedExampleValues = (root, limit = MAX_GENERATED_EXAMPLE_VALUES) => {
|
|
18
18
|
const counts = new WeakMap();
|
|
19
19
|
const count = (input, level) => {
|
|
20
|
-
if (level > EXAMPLE_DEPTH || !isObject(input))
|
|
20
|
+
if (level > EXAMPLE_DEPTH || !isObject(input)) {
|
|
21
21
|
return 1;
|
|
22
|
+
}
|
|
22
23
|
const cached = counts.get(input)?.get(level);
|
|
23
|
-
if (cached !== undefined)
|
|
24
|
+
if (cached !== undefined) {
|
|
24
25
|
return cached;
|
|
26
|
+
}
|
|
25
27
|
const schema = getResolvedRef(input, mergeSiblingReferences);
|
|
26
|
-
if (!isObject(schema))
|
|
28
|
+
if (!isObject(schema)) {
|
|
27
29
|
return 1;
|
|
30
|
+
}
|
|
28
31
|
// Supplied examples bypass schema expansion in the generator too.
|
|
29
|
-
if (schema.example !== undefined || (Array.isArray(schema.examples) && schema.examples.length > 0))
|
|
32
|
+
if (schema.example !== undefined || (Array.isArray(schema.examples) && schema.examples.length > 0)) {
|
|
30
33
|
return 1;
|
|
34
|
+
}
|
|
31
35
|
let total = 1;
|
|
32
36
|
const add = (child) => {
|
|
33
|
-
if (total <= limit)
|
|
37
|
+
if (total <= limit) {
|
|
34
38
|
total += count(child, level + 1);
|
|
39
|
+
}
|
|
35
40
|
};
|
|
36
|
-
if (isObject(schema.properties))
|
|
41
|
+
if (isObject(schema.properties)) {
|
|
37
42
|
Object.values(schema.properties).forEach(add);
|
|
38
|
-
|
|
43
|
+
}
|
|
44
|
+
if (isObject(schema.patternProperties)) {
|
|
39
45
|
Object.values(schema.patternProperties).forEach(add);
|
|
40
|
-
|
|
46
|
+
}
|
|
47
|
+
if (isObject(schema.additionalProperties)) {
|
|
41
48
|
add(schema.additionalProperties);
|
|
42
|
-
|
|
49
|
+
}
|
|
50
|
+
if (schema.items !== undefined) {
|
|
43
51
|
add(schema.items);
|
|
44
|
-
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(schema.prefixItems)) {
|
|
45
54
|
schema.prefixItems.forEach(add);
|
|
46
|
-
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(schema.allOf)) {
|
|
47
57
|
schema.allOf.forEach(add);
|
|
48
|
-
|
|
58
|
+
}
|
|
59
|
+
const anyOf = Array.isArray(schema.anyOf) ? schema.anyOf : [];
|
|
60
|
+
const variants = Array.isArray(schema.oneOf) ? schema.oneOf : anyOf;
|
|
49
61
|
// Object and array generation can use the first variant, while other unions skip null.
|
|
50
62
|
// A discriminator can choose any variant, so bound the largest candidate in that case.
|
|
51
63
|
const nonNull = variants.find((variant) => {
|
|
@@ -57,8 +69,9 @@ export const countGeneratedExampleValues = (root, limit = MAX_GENERATED_EXAMPLE_
|
|
|
57
69
|
: [variants[0], nonNull];
|
|
58
70
|
let variantCount = 0;
|
|
59
71
|
for (const candidate of candidates) {
|
|
60
|
-
if (candidate !== undefined && total <= limit && variantCount <= limit)
|
|
72
|
+
if (candidate !== undefined && total <= limit && variantCount <= limit) {
|
|
61
73
|
variantCount = Math.max(variantCount, count(candidate, level + 1));
|
|
74
|
+
}
|
|
62
75
|
}
|
|
63
76
|
total += variantCount;
|
|
64
77
|
const levels = counts.get(input) ?? new Map();
|
|
@@ -72,40 +85,51 @@ export const countGeneratedExampleValues = (root, limit = MAX_GENERATED_EXAMPLE_
|
|
|
72
85
|
export const getMarkdownExamples = (source, mediaType, mode, openapiVersion = '3.2.0',
|
|
73
86
|
// Schema metadata can be upgraded while example fields still follow the original version.
|
|
74
87
|
schemaOpenapiVersion = openapiVersion, linked = false) => {
|
|
75
|
-
if (source.example !== undefined)
|
|
88
|
+
if (source.example !== undefined) {
|
|
76
89
|
return [{ value: source.example }];
|
|
90
|
+
}
|
|
77
91
|
if (source.examples && Object.keys(source.examples).length) {
|
|
78
92
|
return Object.entries(source.examples).flatMap(([name, reference]) => {
|
|
79
93
|
const example = getResolvedRef(reference);
|
|
80
|
-
if (!isObject(example))
|
|
94
|
+
if (!isObject(example)) {
|
|
81
95
|
return [];
|
|
96
|
+
}
|
|
82
97
|
const metadata = {
|
|
83
98
|
name,
|
|
84
99
|
summary: typeof example.summary === 'string' ? example.summary : undefined,
|
|
85
100
|
description: typeof example.description === 'string' ? example.description : undefined,
|
|
86
101
|
};
|
|
87
|
-
if (example.value !== undefined)
|
|
102
|
+
if (example.value !== undefined) {
|
|
88
103
|
return [{ ...metadata, value: example.value }];
|
|
89
|
-
|
|
104
|
+
}
|
|
105
|
+
if (/^3\.2\./.test(openapiVersion) && typeof example.serializedValue === 'string') {
|
|
90
106
|
return [{ ...metadata, serializedValue: example.serializedValue }];
|
|
91
|
-
|
|
107
|
+
}
|
|
108
|
+
if (typeof example.externalValue === 'string') {
|
|
92
109
|
return [{ ...metadata, externalValue: example.externalValue }];
|
|
93
|
-
|
|
110
|
+
}
|
|
111
|
+
if (/^3\.2\./.test(openapiVersion) && example.dataValue !== undefined) {
|
|
94
112
|
return [{ ...metadata, dataValue: example.dataValue }];
|
|
113
|
+
}
|
|
95
114
|
return [];
|
|
96
115
|
});
|
|
97
116
|
}
|
|
98
117
|
const schema = getResolvedRef(source.schema, mergeSiblingReferences);
|
|
99
|
-
if (!isObject(schema))
|
|
118
|
+
if (!isObject(schema)) {
|
|
100
119
|
return [];
|
|
101
|
-
|
|
120
|
+
}
|
|
121
|
+
if (schema.example !== undefined) {
|
|
102
122
|
return [{ value: schema.example }];
|
|
103
|
-
|
|
123
|
+
}
|
|
124
|
+
if (Array.isArray(schema.examples) && schema.examples.length) {
|
|
104
125
|
return schema.examples.map((value) => ({ value }));
|
|
105
|
-
|
|
126
|
+
}
|
|
127
|
+
if (linked) {
|
|
106
128
|
return [];
|
|
107
|
-
|
|
129
|
+
}
|
|
130
|
+
if (countGeneratedExampleValues(source.schema) > MAX_GENERATED_EXAMPLE_VALUES) {
|
|
108
131
|
return [{ omitted: true }];
|
|
132
|
+
}
|
|
109
133
|
if (isXmlMediaType(mediaType)) {
|
|
110
134
|
const result = getXmlBodyExample(source.schema, undefined, {
|
|
111
135
|
mode,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-document.d.ts","sourceRoot":"","sources":["../src/load-document.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,8DAA8D,CAAA;
|
|
1
|
+
{"version":3,"file":"load-document.d.ts","sourceRoot":"","sources":["../src/load-document.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,8DAA8D,CAAA;AAiMrE,8EAA8E;AAC9E,eAAO,MAAM,YAAY,GACvB,OAAO,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,KACxD,OAAO,CAAC,eAAe,CAgGzB,CAAA"}
|
package/dist/load-document.js
CHANGED
|
@@ -40,8 +40,9 @@ const attachRefValues = (document, schemas = getSchemas(document), { detectOnly
|
|
|
40
40
|
kind = kind && getChildKind(kind, key);
|
|
41
41
|
position = Array.isArray(parent) ? position : position && getChildPosition(position, key);
|
|
42
42
|
// Arbitrary extension and example data has not passed an OpenAPI object schema.
|
|
43
|
-
if (position === 'document' && kind === undefined)
|
|
43
|
+
if (position === 'document' && kind === undefined) {
|
|
44
44
|
position = undefined;
|
|
45
|
+
}
|
|
45
46
|
parent = getValueByPath(parent, [key]).value;
|
|
46
47
|
}
|
|
47
48
|
const schema = getTargetSchema(position, kind);
|
|
@@ -78,15 +79,20 @@ const attachRefValues = (document, schemas = getSchemas(document), { detectOnly
|
|
|
78
79
|
// support summary and description; casting them as their targets would add defaults.
|
|
79
80
|
// Path Item Objects declare their own $ref field, so their siblings are cast normally.
|
|
80
81
|
const reference = typeof value.$ref === 'string' ? value.$ref : undefined;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
let cast;
|
|
83
|
+
if (reference === undefined || schema === referenceTargetSchemas.pathItem) {
|
|
84
|
+
cast = coerceValue(schema, value);
|
|
85
|
+
}
|
|
86
|
+
else if (position === 'schema') {
|
|
87
|
+
cast = { ...coerceValue(SchemaObjectSchema, value), $ref: reference };
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
cast = {
|
|
91
|
+
$ref: reference,
|
|
92
|
+
...(typeof value.summary === 'string' ? { summary: value.summary } : {}),
|
|
93
|
+
...(typeof value.description === 'string' ? { description: value.description } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
90
96
|
if (fallback?.booleanSchemas) {
|
|
91
97
|
restoreBooleanSchemas(value, cast, position);
|
|
92
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-description.d.ts","sourceRoot":"","sources":["../src/parse-description.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,WAAW,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"parse-description.d.ts","sourceRoot":"","sources":["../src/parse-description.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,WAAW,EAAE,MAAM,OAAO,CAAA;AAsG7D,0FAA0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;AAE1E,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,QAAO,CAAC,MAAM,iBAAiB,CAuDlE,CAAA;AAED,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,GAAU,OAAO,WAAW,EAAE,EAAE,aAAa,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAkB3G,CAAA"}
|
|
@@ -10,8 +10,9 @@ const clean = (node) => {
|
|
|
10
10
|
node.url = safeUrl(node.url);
|
|
11
11
|
}
|
|
12
12
|
if ('children' in node) {
|
|
13
|
-
for (const child of node.children)
|
|
13
|
+
for (const child of node.children) {
|
|
14
14
|
clean(child);
|
|
15
|
+
}
|
|
15
16
|
// The filter preserves the parent's existing child types.
|
|
16
17
|
const previousLength = node.children.length;
|
|
17
18
|
node.children = node.children.filter((child) => child.type !== 'image' &&
|
|
@@ -33,10 +34,12 @@ const clean = (node) => {
|
|
|
33
34
|
if (node.children.length !== previousLength) {
|
|
34
35
|
const first = node.children.at(0);
|
|
35
36
|
const last = node.children.at(-1);
|
|
36
|
-
if (first?.type === 'text')
|
|
37
|
+
if (first?.type === 'text') {
|
|
37
38
|
first.value = first.value.trimStart();
|
|
38
|
-
|
|
39
|
+
}
|
|
40
|
+
if (last?.type === 'text') {
|
|
39
41
|
last.value = last.value.trimEnd();
|
|
42
|
+
}
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
delete node.position;
|
|
@@ -47,24 +50,31 @@ const pruneDefinitions = (tree) => {
|
|
|
47
50
|
const footnotes = new Set();
|
|
48
51
|
const definitions = new Map(tree.children.filter((node) => node.type === 'footnoteDefinition').map((node) => [node.identifier, node]));
|
|
49
52
|
const collect = (node) => {
|
|
50
|
-
if (node.type === 'linkReference')
|
|
53
|
+
if (node.type === 'linkReference') {
|
|
51
54
|
links.add(node.identifier);
|
|
52
|
-
|
|
55
|
+
}
|
|
56
|
+
if (node.type === 'footnoteReference') {
|
|
53
57
|
footnotes.add(node.identifier);
|
|
54
|
-
|
|
58
|
+
}
|
|
59
|
+
if ('children' in node) {
|
|
55
60
|
node.children.forEach(collect);
|
|
61
|
+
}
|
|
56
62
|
};
|
|
57
63
|
tree.children.filter((node) => node.type !== 'footnoteDefinition').forEach(collect);
|
|
58
64
|
// Set iteration also visits footnotes discovered inside another footnote.
|
|
59
|
-
for (const identifier of footnotes)
|
|
65
|
+
for (const identifier of footnotes) {
|
|
60
66
|
definitions.get(identifier)?.children.forEach(collect);
|
|
67
|
+
}
|
|
61
68
|
tree.children = tree.children.filter((node) => {
|
|
62
|
-
if (node.type === 'definition')
|
|
69
|
+
if (node.type === 'definition') {
|
|
63
70
|
return links.has(node.identifier);
|
|
64
|
-
|
|
71
|
+
}
|
|
72
|
+
if (node.type === 'footnoteDefinition') {
|
|
65
73
|
return footnotes.has(node.identifier);
|
|
66
|
-
|
|
74
|
+
}
|
|
75
|
+
if (node.type === 'paragraph') {
|
|
67
76
|
return node.children.some((child) => child.type !== 'text' || child.value.trim());
|
|
77
|
+
}
|
|
68
78
|
return true;
|
|
69
79
|
});
|
|
70
80
|
};
|
|
@@ -78,8 +88,9 @@ export const createDescriptionParser = () => {
|
|
|
78
88
|
const cache = new Map();
|
|
79
89
|
const parse = async (value) => {
|
|
80
90
|
// Most schema descriptions are one plain sentence. Parsing them costs more than the page's other text.
|
|
81
|
-
if (isPlainText(value))
|
|
91
|
+
if (isPlainText(value)) {
|
|
82
92
|
return [{ type: 'paragraph', children: [{ type: 'text', value: value.trim() }] }];
|
|
93
|
+
}
|
|
83
94
|
const parsed = parser.parse(value);
|
|
84
95
|
// Raw HTML and GitHub alerts retain Scalar's existing conversion behavior.
|
|
85
96
|
const tree = containsHtml(parsed) || /^\s*>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|SUCCESS)\]/im.test(value)
|
|
@@ -93,26 +104,31 @@ export const createDescriptionParser = () => {
|
|
|
93
104
|
const copy = { ...node };
|
|
94
105
|
if ('identifier' in copy) {
|
|
95
106
|
copy.identifier = `${prefix}${copy.identifier}`;
|
|
96
|
-
if ('label' in copy)
|
|
107
|
+
if ('label' in copy) {
|
|
97
108
|
copy.label = copy.identifier;
|
|
98
|
-
|
|
109
|
+
}
|
|
110
|
+
if ('referenceType' in copy) {
|
|
99
111
|
copy.referenceType = 'full';
|
|
112
|
+
}
|
|
100
113
|
}
|
|
101
|
-
if ('children' in copy)
|
|
114
|
+
if ('children' in copy) {
|
|
102
115
|
copy.children = copy.children.map((child) => namespace(child, prefix));
|
|
116
|
+
}
|
|
103
117
|
return copy;
|
|
104
118
|
};
|
|
105
119
|
const hasReference = (node) => 'identifier' in node || ('children' in node && node.children.some(hasReference));
|
|
106
120
|
return () => {
|
|
107
121
|
const state = { nextId: 0 };
|
|
108
122
|
return async (value) => {
|
|
109
|
-
if (!value)
|
|
123
|
+
if (!value) {
|
|
110
124
|
return [];
|
|
125
|
+
}
|
|
111
126
|
const prefix = `description-${state.nextId++}-`;
|
|
112
127
|
const cached = cache.get(value) ?? parse(value);
|
|
113
128
|
if (!cache.has(value)) {
|
|
114
|
-
if (cache.size >= 8192)
|
|
129
|
+
if (cache.size >= 8192) {
|
|
115
130
|
cache.clear();
|
|
131
|
+
}
|
|
116
132
|
cache.set(value, cached);
|
|
117
133
|
}
|
|
118
134
|
const nodes = await cached;
|
|
@@ -127,10 +143,12 @@ export const expandDescriptions = async (nodes, description) => {
|
|
|
127
143
|
const found = [];
|
|
128
144
|
const collect = (siblings) => {
|
|
129
145
|
for (const node of siblings) {
|
|
130
|
-
if (node.type === 'descriptionPlaceholder')
|
|
146
|
+
if (node.type === 'descriptionPlaceholder') {
|
|
131
147
|
found.push({ siblings, node });
|
|
132
|
-
|
|
148
|
+
}
|
|
149
|
+
else if ('children' in node) {
|
|
133
150
|
collect(node.children);
|
|
151
|
+
}
|
|
134
152
|
}
|
|
135
153
|
};
|
|
136
154
|
collect(nodes);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-document.d.ts","sourceRoot":"","sources":["../src/render-document.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,8DAA8D,CAAA;AAcrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"render-document.d.ts","sourceRoot":"","sources":["../src/render-document.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,8DAA8D,CAAA;AAcrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAwD7D,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,QAAO,CAAC,CACzC,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,MAAM,CAAC,CA0RnB,CAAA"}
|
package/dist/render-document.js
CHANGED
|
@@ -18,14 +18,17 @@ const serializer = unified()
|
|
|
18
18
|
bullet: '-',
|
|
19
19
|
join: [
|
|
20
20
|
(left, right, parent) => {
|
|
21
|
-
if (!('spread' in parent))
|
|
21
|
+
if (!('spread' in parent)) {
|
|
22
22
|
return undefined;
|
|
23
|
+
}
|
|
23
24
|
// Inside a tight list item, content after a nested list would otherwise continue its last item.
|
|
24
|
-
if (left.type === 'list' && right.type !== 'list')
|
|
25
|
+
if (left.type === 'list' && right.type !== 'list') {
|
|
25
26
|
return 1;
|
|
27
|
+
}
|
|
26
28
|
// Code blocks in descriptions read better apart from the prose around them.
|
|
27
|
-
if (left.type === 'code' || right.type === 'code')
|
|
29
|
+
if (left.type === 'code' || right.type === 'code') {
|
|
28
30
|
return 1;
|
|
31
|
+
}
|
|
29
32
|
return undefined;
|
|
30
33
|
},
|
|
31
34
|
],
|
|
@@ -43,12 +46,14 @@ const renderShownModel = async (name, view, previous, description) => {
|
|
|
43
46
|
? [strong(text(view.title)), text(' ('), inlineCode(name), text(')')]
|
|
44
47
|
: [inlineCode(name)];
|
|
45
48
|
label.push(text(' — shown above'));
|
|
46
|
-
if (previous.name !== name)
|
|
49
|
+
if (previous.name !== name) {
|
|
47
50
|
label.push(text(' as '), inlineCode(previous.name));
|
|
51
|
+
}
|
|
48
52
|
label.push(text('.'));
|
|
49
53
|
const blocks = [paragraph(...label)];
|
|
50
|
-
if (view.description && view.description !== previous.description)
|
|
54
|
+
if (view.description && view.description !== previous.description) {
|
|
51
55
|
blocks.push(...(await description(view.description)));
|
|
56
|
+
}
|
|
52
57
|
return item(...blocks);
|
|
53
58
|
};
|
|
54
59
|
/** Build Markdown directly, retaining caches only for this immutable document snapshot. */
|
|
@@ -81,8 +86,9 @@ export const createDocumentRenderer = () => {
|
|
|
81
86
|
const entries = [];
|
|
82
87
|
for (const [path, reference] of Object.entries(group.paths ?? {})) {
|
|
83
88
|
const pathItem = getResolvedPathItem(reference);
|
|
84
|
-
if (!pathItem)
|
|
89
|
+
if (!pathItem) {
|
|
85
90
|
continue;
|
|
91
|
+
}
|
|
86
92
|
forEachPathItemOperation(reference, (method, operation) => {
|
|
87
93
|
const label = `${formatOperationMethod(method)} ${path}`;
|
|
88
94
|
entries.push({
|
|
@@ -107,8 +113,9 @@ export const createDocumentRenderer = () => {
|
|
|
107
113
|
const nodes = [];
|
|
108
114
|
const sections = [];
|
|
109
115
|
const flush = async () => {
|
|
110
|
-
if (!nodes.length)
|
|
116
|
+
if (!nodes.length) {
|
|
111
117
|
return;
|
|
118
|
+
}
|
|
112
119
|
const children = nodes.splice(0);
|
|
113
120
|
await expandDescriptions(children, description);
|
|
114
121
|
sections.push(serializer.stringify({ type: 'root', children }).trimEnd());
|
|
@@ -116,62 +123,74 @@ export const createDocumentRenderer = () => {
|
|
|
116
123
|
if (!single) {
|
|
117
124
|
const { info } = document;
|
|
118
125
|
const metadata = [field('OpenAPI Version', inlineCode(document.openapi))];
|
|
119
|
-
if (info.version)
|
|
126
|
+
if (info.version) {
|
|
120
127
|
metadata.push(field('API Version', inlineCode(info.version)));
|
|
121
|
-
|
|
128
|
+
}
|
|
129
|
+
if (info.termsOfService) {
|
|
122
130
|
metadata.push(field('Terms of service', link(info.termsOfService, info.termsOfService)));
|
|
131
|
+
}
|
|
123
132
|
if (info.contact) {
|
|
124
133
|
const contact = [text(info.contact.name ?? '')];
|
|
125
134
|
metadata.push(item(paragraph(strong(text('Contact:')), text(' '), ...contact, ...(info.contact.url ? [text(' '), link(info.contact.url, info.contact.url)] : []), ...(info.contact.email ? [text(' '), link(`mailto:${info.contact.email}`, info.contact.email)] : []))));
|
|
126
135
|
}
|
|
127
|
-
if (info.license)
|
|
136
|
+
if (info.license) {
|
|
128
137
|
metadata.push(field('License', info.license.url ? link(info.license.url, info.license.name ?? '') : text(info.license.name)));
|
|
138
|
+
}
|
|
129
139
|
nodes.push(heading(1, text(info.title)), list(metadata), ...(await description(info.description)));
|
|
130
140
|
if (whole) {
|
|
131
141
|
const contents = [];
|
|
132
142
|
for (const group of groups) {
|
|
133
|
-
if (!group.entries.length)
|
|
143
|
+
if (!group.entries.length) {
|
|
134
144
|
continue;
|
|
145
|
+
}
|
|
135
146
|
contents.push(paragraph(strong(text(group.title))), list(group.entries.map(({ id, label }) => item(paragraph(link(`#${encodeURIComponent(id)}`, label))))));
|
|
136
147
|
}
|
|
137
148
|
if (destinations?.size) {
|
|
138
149
|
contents.push(paragraph(strong(text('Schemas'))), list([...destinations].map(([name, url]) => item(paragraph(link(url, name))))));
|
|
139
150
|
}
|
|
140
|
-
if (contents.length)
|
|
151
|
+
if (contents.length) {
|
|
141
152
|
nodes.push(heading(2, text('Contents')), ...contents);
|
|
153
|
+
}
|
|
142
154
|
}
|
|
143
155
|
if (document.servers?.length || (whole && document.servers !== undefined)) {
|
|
144
|
-
if (documentContext?.servers)
|
|
156
|
+
if (documentContext?.servers) {
|
|
145
157
|
nodes.push(anchor(documentContext.servers));
|
|
158
|
+
}
|
|
146
159
|
nodes.push(heading(2, text('Servers')));
|
|
147
160
|
const effectiveServers = document.servers?.length
|
|
148
161
|
? document.servers
|
|
149
162
|
: [{ url: '/' }];
|
|
150
163
|
const servers = effectiveServers.map((server) => {
|
|
151
164
|
const nested = [];
|
|
152
|
-
if (server.description)
|
|
165
|
+
if (server.description) {
|
|
153
166
|
nested.push(field('Description', text(server.description)));
|
|
167
|
+
}
|
|
154
168
|
const variables = Object.entries(server.variables ?? {});
|
|
155
|
-
if (variables.length)
|
|
169
|
+
if (variables.length) {
|
|
156
170
|
nested.push(item(paragraph(strong(text('Variables:'))), list(variables.map(([name, variable]) => item(paragraph(inlineCode(name), text(' (default: '), inlineCode(variable.default), text(')'), ...(whole && variable.enum?.length
|
|
157
171
|
? [text(', possible values: '), inlineCode(variable.enum.join(', '))]
|
|
158
172
|
: []), text(variable.description ? `: ${variable.description}` : '')))))));
|
|
173
|
+
}
|
|
159
174
|
const entry = field('URL', inlineCode(server.url));
|
|
160
|
-
if (nested.length)
|
|
175
|
+
if (nested.length) {
|
|
161
176
|
entry.children.push(list(nested));
|
|
177
|
+
}
|
|
162
178
|
return entry;
|
|
163
179
|
});
|
|
164
180
|
nodes.push(list(servers));
|
|
165
181
|
}
|
|
166
|
-
if (documentContext?.authentication)
|
|
182
|
+
if (documentContext?.authentication) {
|
|
167
183
|
nodes.push(anchor(documentContext.authentication));
|
|
184
|
+
}
|
|
168
185
|
nodes.push(...(await renderSecurity(document.security, document.components?.securitySchemes, description, 2)));
|
|
169
186
|
if (document.tags?.length) {
|
|
170
187
|
nodes.push(heading(2, text('Tags')));
|
|
171
188
|
for (const tag of document.tags) {
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
const title = /^3\.2\./.test(openapiVersion) ? (tag.summary ?? tag.name) : tag.name;
|
|
190
|
+
nodes.push(heading(3, text(title)), ...(await description(tag.description)));
|
|
191
|
+
if (tag.externalDocs) {
|
|
174
192
|
nodes.push(paragraph(link(tag.externalDocs.url, tag.externalDocs.description ?? tag.externalDocs.url)));
|
|
193
|
+
}
|
|
175
194
|
}
|
|
176
195
|
}
|
|
177
196
|
await flush();
|
|
@@ -179,12 +198,14 @@ export const createDocumentRenderer = () => {
|
|
|
179
198
|
for (const group of groups) {
|
|
180
199
|
let hasOperations = false;
|
|
181
200
|
for (const { path, pathItem, method, operation, id } of group.entries) {
|
|
182
|
-
if (!hasOperations && !single)
|
|
201
|
+
if (!hasOperations && !single) {
|
|
183
202
|
nodes.push(heading(2, text(group.title)));
|
|
203
|
+
}
|
|
184
204
|
hasOperations = true;
|
|
185
205
|
schemas.beginSection();
|
|
186
|
-
if (id)
|
|
206
|
+
if (id) {
|
|
187
207
|
nodes.push(anchor(id));
|
|
208
|
+
}
|
|
188
209
|
nodes.push(...(await renderOperation(document, path, method, pathItem, operation, group.webhook, {
|
|
189
210
|
description,
|
|
190
211
|
schemas,
|
|
@@ -200,11 +221,13 @@ export const createDocumentRenderer = () => {
|
|
|
200
221
|
const renderModel = async (name, schema, level) => {
|
|
201
222
|
const view = schemas.view(schema);
|
|
202
223
|
const summary = schemas.summarize(schema);
|
|
203
|
-
if (whole)
|
|
224
|
+
if (whole) {
|
|
204
225
|
nodes.push(anchor(anchors.get('schema', name)));
|
|
226
|
+
}
|
|
205
227
|
nodes.push(heading(level, text(view.title ?? name)));
|
|
206
|
-
if (summary.length)
|
|
228
|
+
if (summary.length) {
|
|
207
229
|
nodes.push(paragraph(strong(text('Type:')), text('\u00a0'), ...summary));
|
|
230
|
+
}
|
|
208
231
|
nodes.push(...(await description(view.description)), ...schemas.render(schema, 0, [], { hideDetails: true, name }));
|
|
209
232
|
if (view.type === 'object' || hasAuthoredExamples(view)) {
|
|
210
233
|
// A model page's own schema is bounded, so it gets a generated example even in linked mode.
|
|
@@ -223,13 +246,15 @@ export const createDocumentRenderer = () => {
|
|
|
223
246
|
await renderModel(selected[0], selected[1], 1);
|
|
224
247
|
}
|
|
225
248
|
const dependencies = models.filter(([name]) => name !== options.model);
|
|
226
|
-
if (dependencies.length)
|
|
249
|
+
if (dependencies.length) {
|
|
227
250
|
nodes.push(heading(2, text('Schemas')));
|
|
251
|
+
}
|
|
228
252
|
// Models the page already expanded cost one line each, grouped into a single list.
|
|
229
253
|
const shownAbove = [];
|
|
230
254
|
const flushShownAbove = () => {
|
|
231
|
-
if (shownAbove.length)
|
|
255
|
+
if (shownAbove.length) {
|
|
232
256
|
nodes.push(list(shownAbove.splice(0)));
|
|
257
|
+
}
|
|
233
258
|
};
|
|
234
259
|
for (const [name, schema] of dependencies) {
|
|
235
260
|
schemas.beginSection();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-examples.d.ts","sourceRoot":"","sources":["../src/render-examples.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,OAAO,CAAA;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,yBAAyB,CAAA;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,+EAA+E;AAC/E,eAAO,MAAM,cAAc,GACzB,QAAQ,aAAa,EACrB,aAAa,iBAAiB,EAC9B,kBAA8B,EAC9B,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,+BAIG;IACD,yFAAyF;IACzF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oFAAoF;IACpF,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CACvB,KACL,OAAO,CAAC,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"render-examples.d.ts","sourceRoot":"","sources":["../src/render-examples.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,OAAO,CAAA;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,yBAAyB,CAAA;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,+EAA+E;AAC/E,eAAO,MAAM,cAAc,GACzB,QAAQ,aAAa,EACrB,aAAa,iBAAiB,EAC9B,kBAA8B,EAC9B,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,+BAIG;IACD,yFAAyF;IACzF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oFAAoF;IACpF,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CACvB,KACL,OAAO,CAAC,WAAW,EAAE,CAgFvB,CAAA"}
|