@scalar/openapi-parser 0.29.13 → 0.29.15
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 +10 -0
- package/dist/utils/get-list-of-references.d.ts +2 -6
- package/dist/utils/get-list-of-references.d.ts.map +1 -1
- package/dist/utils/get-list-of-references.js +5 -22
- package/dist/utils/get-reference-targets.d.ts +14 -0
- package/dist/utils/get-reference-targets.d.ts.map +1 -0
- package/dist/utils/get-reference-targets.js +45 -0
- package/dist/utils/load/load.d.ts +1 -1
- package/dist/utils/load/load.d.ts.map +1 -1
- package/dist/utils/load/load.js +38 -22
- package/dist/utils/openapi/openapi.d.ts +172 -172
- package/dist/utils/reference-context.d.ts +7 -0
- package/dist/utils/reference-context.d.ts.map +1 -0
- package/dist/utils/reference-context.js +111 -0
- package/dist/utils/resolve-references.d.ts.map +1 -1
- package/dist/utils/resolve-references.js +16 -17
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @scalar/openapi-parser
|
|
2
2
|
|
|
3
|
+
## 0.29.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#10643](https://github.com/scalar/scalar/pull/10643): Distinguish reference locations from schema name maps and literal data when validating, dereferencing, and collecting external references. Preserve `$ref` properties with object or boolean schemas, leave examples and default/enum/const values unchanged, and retain errors for malformed references.
|
|
8
|
+
|
|
9
|
+
Retain reference context across external files and fragments. Resolve custom operations while preserving OAuth scope names, discriminator mappings, Link payloads, and specification extension data.
|
|
10
|
+
|
|
11
|
+
## 0.29.14
|
|
12
|
+
|
|
3
13
|
## 0.29.13
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { AnyObject } from '@scalar/types/utils';
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Warning: Doesn't return internal references.
|
|
6
|
-
*/
|
|
7
|
-
export declare function getListOfReferences(specification: AnyObject): string[];
|
|
2
|
+
/** Collect external reference filenames without interpreting instance data as references. */
|
|
3
|
+
export declare const getListOfReferences: (specification: AnyObject) => string[];
|
|
8
4
|
//# sourceMappingURL=get-list-of-references.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-list-of-references.d.ts","sourceRoot":"","sources":["../../src/utils/get-list-of-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAIpD
|
|
1
|
+
{"version":3,"file":"get-list-of-references.d.ts","sourceRoot":"","sources":["../../src/utils/get-list-of-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAIpD,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,GAAI,eAAe,SAAS,KAAG,MAAM,EAEpE,CAAA"}
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
export function getListOfReferences(specification) {
|
|
8
|
-
const references = [];
|
|
9
|
-
// Make sure we're dealing with an object
|
|
10
|
-
if (!specification || typeof specification !== 'object') {
|
|
11
|
-
return references;
|
|
12
|
-
}
|
|
13
|
-
// Traverse the specification and collect all references
|
|
14
|
-
traverse(specification, (value) => {
|
|
15
|
-
if (value.$ref && typeof value.$ref === 'string' && !value.$ref.startsWith('#')) {
|
|
16
|
-
references.push(value.$ref.split('#')[0]);
|
|
17
|
-
}
|
|
18
|
-
return value;
|
|
19
|
-
});
|
|
20
|
-
// Remove duplicates
|
|
21
|
-
return [...new Set(references)];
|
|
22
|
-
}
|
|
1
|
+
import { getReferenceTargets } from './get-reference-targets.js';
|
|
2
|
+
/** Collect external reference filenames without interpreting instance data as references. */
|
|
3
|
+
export const getListOfReferences = (specification) => [
|
|
4
|
+
...new Set(getReferenceTargets(specification).map(({ filename }) => filename)),
|
|
5
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReferenceContext } from './reference-context.js';
|
|
2
|
+
/** A selected document fragment and the kind of object expected by its caller. */
|
|
3
|
+
export type ReferenceTarget = {
|
|
4
|
+
fragment: string;
|
|
5
|
+
context: ReferenceContext;
|
|
6
|
+
};
|
|
7
|
+
/** External dependencies retain their fragment and context until the file is loaded. */
|
|
8
|
+
type ExternalReferenceTarget = ReferenceTarget & {
|
|
9
|
+
filename: string;
|
|
10
|
+
};
|
|
11
|
+
/** Collect dependencies from selected fragments, following local aliases in the same context. */
|
|
12
|
+
export declare const getReferenceTargets: (specification: unknown, targets?: ReferenceTarget[]) => ExternalReferenceTarget[];
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=get-reference-targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-reference-targets.d.ts","sourceRoot":"","sources":["../../src/utils/get-reference-targets.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,gBAAgB,EAAiD,MAAM,qBAAqB,CAAA;AAE1G,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAE7E,wFAAwF;AACxF,KAAK,uBAAuB,GAAG,eAAe,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,iGAAiG;AACjG,eAAO,MAAM,mBAAmB,GAC9B,eAAe,OAAO,EACtB,UAAS,eAAe,EAA4C,KACnE,uBAAuB,EA6CzB,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isObjectLike } from '@scalar/helpers/object/is-object';
|
|
2
|
+
import { getSegmentsFromPath } from './get-segments-from-path.js';
|
|
3
|
+
import { canResolveReference, getChildReferenceContext } from './reference-context.js';
|
|
4
|
+
/** Collect dependencies from selected fragments, following local aliases in the same context. */
|
|
5
|
+
export const getReferenceTargets = (specification, targets = [{ fragment: '', context: 'document' }]) => {
|
|
6
|
+
const references = [];
|
|
7
|
+
const localTargets = [];
|
|
8
|
+
const visited = new WeakMap();
|
|
9
|
+
const visitTarget = ({ fragment, context }) => {
|
|
10
|
+
try {
|
|
11
|
+
const target = getSegmentsFromPath(fragment).reduce((value, key) => (isObjectLike(value) && Object.hasOwn(value, key) ? value[key] : undefined), specification);
|
|
12
|
+
visit(target, context);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Reference resolution reports invalid pointers; dependency collection does not validate them.
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const visit = (value, context) => {
|
|
19
|
+
if (context === 'literal' || !isObjectLike(value) || visited.get(value)?.has(context)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const contexts = visited.get(value) ?? new Set();
|
|
23
|
+
contexts.add(context);
|
|
24
|
+
visited.set(value, contexts);
|
|
25
|
+
if (canResolveReference(context) && typeof value.$ref === 'string' && value.$ref) {
|
|
26
|
+
const [filename, fragment = ''] = value.$ref.split('#', 2);
|
|
27
|
+
if (filename) {
|
|
28
|
+
references.push({ filename, fragment, context });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
localTargets.push({ fragment, context });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const [key, child] of Object.entries(value)) {
|
|
35
|
+
visit(child, getChildReferenceContext(context, key, child));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
for (const target of targets) {
|
|
39
|
+
visitTarget(target);
|
|
40
|
+
}
|
|
41
|
+
for (const target of localTargets) {
|
|
42
|
+
visitTarget(target);
|
|
43
|
+
}
|
|
44
|
+
return references;
|
|
45
|
+
};
|
|
@@ -26,5 +26,5 @@ export type LoadOptions = {
|
|
|
26
26
|
* It builds a filesystem representation of all loaded content and collects any errors
|
|
27
27
|
* encountered during the process.
|
|
28
28
|
*/
|
|
29
|
-
export declare
|
|
29
|
+
export declare const load: (value: AnyApiDefinitionFormat, options?: LoadOptions) => Promise<LoadResult>;
|
|
30
30
|
//# sourceMappingURL=load.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../../src/utils/load/load.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAe,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAMpH,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAA;IAC/B,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IACxB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IACvD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAA;IAC/B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB,GAAG,kBAAkB,CAAA;AAEtB;;;;;;;;;;;;;;GAcG;AACH,
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../../src/utils/load/load.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAe,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAMpH,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAA;IAC/B,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IACxB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IACvD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAA;IAC/B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB,GAAG,kBAAkB,CAAA;AAEtB;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,sBAAsB,EAAE,UAAU,WAAW,KAAG,OAAO,CAAC,UAAU,CAChE,CAAA"}
|
package/dist/utils/load/load.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ERRORS } from '../../configuration/index.js';
|
|
2
2
|
import { getEntrypoint } from '../../utils/get-entrypoint.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getReferenceTargets } from '../../utils/get-reference-targets.js';
|
|
4
4
|
import { makeFilesystem } from '../../utils/make-filesystem.js';
|
|
5
5
|
import { normalize } from '../../utils/normalize.js';
|
|
6
6
|
/**
|
|
@@ -18,7 +18,8 @@ import { normalize } from '../../utils/normalize.js';
|
|
|
18
18
|
* It builds a filesystem representation of all loaded content and collects any errors
|
|
19
19
|
* encountered during the process.
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
21
|
+
export const load = (value, options) => loadDocument(value, options);
|
|
22
|
+
const loadDocument = async (value, options, targets, documents = new Map()) => {
|
|
22
23
|
const errors = [];
|
|
23
24
|
// Don't load a reference twice, check the filesystem before fetching something
|
|
24
25
|
if (options?.filesystem?.find((entry) => entry.filename === value)) {
|
|
@@ -30,8 +31,10 @@ export async function load(value, options) {
|
|
|
30
31
|
}
|
|
31
32
|
// Check whether the value is an URL or file path
|
|
32
33
|
const plugin = options?.plugins?.find((thisPlugin) => thisPlugin.check(value));
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const documentKey = typeof value === 'string' ? value : options?.filename;
|
|
35
|
+
const cached = documentKey === undefined ? undefined : documents.get(documentKey);
|
|
36
|
+
let content = cached?.content ?? normalize(value);
|
|
37
|
+
if (plugin && !cached) {
|
|
35
38
|
try {
|
|
36
39
|
content = normalize(await plugin.get(value));
|
|
37
40
|
}
|
|
@@ -65,14 +68,29 @@ export async function load(value, options) {
|
|
|
65
68
|
errors,
|
|
66
69
|
};
|
|
67
70
|
}
|
|
68
|
-
|
|
71
|
+
const filesystem = makeFilesystem(content, {
|
|
69
72
|
filename: options?.filename ?? null,
|
|
70
73
|
});
|
|
71
74
|
// Get references from file system entry, or from the content
|
|
72
75
|
const newEntry = options?.filename
|
|
73
76
|
? filesystem.find((entry) => entry.filename === options?.filename)
|
|
74
77
|
: getEntrypoint(filesystem);
|
|
75
|
-
|
|
78
|
+
// A file may be reached later through another fragment or another object type.
|
|
79
|
+
const visitedTargets = cached?.targets ?? new Set();
|
|
80
|
+
const pendingTargets = (targets ?? [{ fragment: '', context: 'document' }]).filter((target) => {
|
|
81
|
+
const key = JSON.stringify(target);
|
|
82
|
+
if (visitedTargets.has(key)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
visitedTargets.add(key);
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
if (documentKey !== undefined) {
|
|
89
|
+
documents.set(documentKey, { content, targets: visitedTargets });
|
|
90
|
+
}
|
|
91
|
+
const referenceTargets = getReferenceTargets(newEntry.specification, pendingTargets);
|
|
92
|
+
const listOfReferences = [...new Set(referenceTargets.map(({ filename }) => filename))];
|
|
93
|
+
newEntry.references = listOfReferences;
|
|
76
94
|
// No other references
|
|
77
95
|
if (listOfReferences.length === 0) {
|
|
78
96
|
return {
|
|
@@ -90,30 +108,28 @@ export async function load(value, options) {
|
|
|
90
108
|
continue;
|
|
91
109
|
}
|
|
92
110
|
const target = otherPlugin.check(reference) && otherPlugin.resolvePath ? otherPlugin.resolvePath(value, reference) : reference;
|
|
93
|
-
|
|
94
|
-
if (filesystem.find((entry) => entry.filename === reference)) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const { filesystem: referencedFiles, errors: newErrors } = await load(target, {
|
|
111
|
+
const { filesystem: referencedFiles, errors: newErrors } = await loadDocument(target, {
|
|
98
112
|
...options,
|
|
99
113
|
// Make the filename the exact same value as the $ref
|
|
100
114
|
// TODO: This leads to problems, if there are multiple references with the same file name but in different folders
|
|
101
115
|
filename: reference,
|
|
102
|
-
}
|
|
116
|
+
}, referenceTargets
|
|
117
|
+
.filter(({ filename }) => filename === reference)
|
|
118
|
+
.map(({ fragment, context }) => ({ fragment, context })), documents);
|
|
103
119
|
errors.push(...newErrors);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
120
|
+
for (const file of referencedFiles) {
|
|
121
|
+
const existing = filesystem.find((entry) => entry.filename === file.filename);
|
|
122
|
+
if (existing) {
|
|
123
|
+
existing.references = [...new Set([...existing.references, ...file.references])];
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
filesystem.push({ ...file, isEntrypoint: false });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
113
129
|
}
|
|
114
130
|
return {
|
|
115
131
|
specification: getEntrypoint(filesystem)?.specification,
|
|
116
132
|
filesystem,
|
|
117
133
|
errors,
|
|
118
134
|
};
|
|
119
|
-
}
|
|
135
|
+
};
|