@hey-api/json-schema-ref-parser 1.0.2 → 1.0.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/dist/lib/__tests__/index.test.d.ts +1 -0
- package/dist/lib/__tests__/index.test.js +43 -0
- package/dist/lib/index.d.ts +6 -3
- package/dist/lib/index.js +23 -9
- package/dist/lib/resolve-external.js +1 -1
- package/dist/lib/resolvers/file.d.ts +3 -1
- package/dist/lib/resolvers/file.js +1 -1
- package/dist/lib/resolvers/url.d.ts +8 -3
- package/dist/lib/resolvers/url.js +42 -42
- package/dist/vite.config.js +6 -5
- package/lib/__tests__/index.test.ts +43 -0
- package/lib/index.ts +26 -6
- package/lib/resolve-external.ts +1 -1
- package/lib/resolvers/file.ts +5 -1
- package/lib/resolvers/url.ts +61 -52
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
7
|
+
const vitest_1 = require("vitest");
|
|
8
|
+
const index_1 = require("../index");
|
|
9
|
+
(0, vitest_1.describe)('getResolvedInput', () => {
|
|
10
|
+
(0, vitest_1.it)('handles url', async () => {
|
|
11
|
+
const pathOrUrlOrSchema = 'https://foo.com';
|
|
12
|
+
const resolvedInput = await (0, index_1.getResolvedInput)({ pathOrUrlOrSchema });
|
|
13
|
+
(0, vitest_1.expect)(resolvedInput.type).toBe('url');
|
|
14
|
+
(0, vitest_1.expect)(resolvedInput.schema).toBeUndefined();
|
|
15
|
+
(0, vitest_1.expect)(resolvedInput.path).toBe('https://foo.com/');
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('handles file', async () => {
|
|
18
|
+
const pathOrUrlOrSchema = './path/to/openapi.json';
|
|
19
|
+
const resolvedInput = await (0, index_1.getResolvedInput)({ pathOrUrlOrSchema });
|
|
20
|
+
(0, vitest_1.expect)(resolvedInput.type).toBe('file');
|
|
21
|
+
(0, vitest_1.expect)(resolvedInput.schema).toBeUndefined();
|
|
22
|
+
(0, vitest_1.expect)(resolvedInput.path).toBe(node_path_1.default.resolve('./path/to/openapi.json'));
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)('handles raw spec', async () => {
|
|
25
|
+
const pathOrUrlOrSchema = {
|
|
26
|
+
info: {
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
},
|
|
29
|
+
openapi: '3.1.0',
|
|
30
|
+
paths: {},
|
|
31
|
+
};
|
|
32
|
+
const resolvedInput = await (0, index_1.getResolvedInput)({ pathOrUrlOrSchema });
|
|
33
|
+
(0, vitest_1.expect)(resolvedInput.type).toBe('json');
|
|
34
|
+
(0, vitest_1.expect)(resolvedInput.schema).toEqual({
|
|
35
|
+
info: {
|
|
36
|
+
version: '1.0.0',
|
|
37
|
+
},
|
|
38
|
+
openapi: '3.1.0',
|
|
39
|
+
paths: {},
|
|
40
|
+
});
|
|
41
|
+
(0, vitest_1.expect)(resolvedInput.path).toBe('');
|
|
42
|
+
});
|
|
43
|
+
});
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -37,8 +37,9 @@ export declare class $RefParser {
|
|
|
37
37
|
*
|
|
38
38
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
39
39
|
*/
|
|
40
|
-
bundle({ arrayBuffer, pathOrUrlOrSchema, resolvedInput, }: {
|
|
40
|
+
bundle({ arrayBuffer, fetch, pathOrUrlOrSchema, resolvedInput, }: {
|
|
41
41
|
arrayBuffer?: ArrayBuffer;
|
|
42
|
+
fetch?: RequestInit;
|
|
42
43
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
43
44
|
resolvedInput?: ResolvedInput;
|
|
44
45
|
}): Promise<JSONSchema>;
|
|
@@ -51,7 +52,8 @@ export declare class $RefParser {
|
|
|
51
52
|
*
|
|
52
53
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
53
54
|
*/
|
|
54
|
-
dereference({ pathOrUrlOrSchema, }: {
|
|
55
|
+
dereference({ fetch, pathOrUrlOrSchema, }: {
|
|
56
|
+
fetch?: RequestInit;
|
|
55
57
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
56
58
|
}): Promise<JSONSchema>;
|
|
57
59
|
/**
|
|
@@ -62,8 +64,9 @@ export declare class $RefParser {
|
|
|
62
64
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
63
65
|
* @returns - The returned promise resolves with the parsed JSON schema object.
|
|
64
66
|
*/
|
|
65
|
-
parse({ arrayBuffer, pathOrUrlOrSchema, resolvedInput: _resolvedInput, }: {
|
|
67
|
+
parse({ arrayBuffer, fetch, pathOrUrlOrSchema, resolvedInput: _resolvedInput, }: {
|
|
66
68
|
arrayBuffer?: ArrayBuffer;
|
|
69
|
+
fetch?: RequestInit;
|
|
67
70
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
68
71
|
resolvedInput?: ResolvedInput;
|
|
69
72
|
}): Promise<{
|
package/dist/lib/index.js
CHANGED
|
@@ -63,7 +63,7 @@ const getResolvedInput = ({ pathOrUrlOrSchema, }) => {
|
|
|
63
63
|
// So we're being generous here and doing the conversion automatically.
|
|
64
64
|
// This is not intended to be a 100% bulletproof solution.
|
|
65
65
|
// If it doesn't work for your use-case, then use a URL instead.
|
|
66
|
-
if (url.isFileSystemPath(resolvedInput.path)) {
|
|
66
|
+
if (resolvedInput.path && url.isFileSystemPath(resolvedInput.path)) {
|
|
67
67
|
resolvedInput.path = url.fromFileSystemPath(resolvedInput.path);
|
|
68
68
|
resolvedInput.type = 'file';
|
|
69
69
|
}
|
|
@@ -80,8 +80,10 @@ const getResolvedInput = ({ pathOrUrlOrSchema, }) => {
|
|
|
80
80
|
resolvedInput.type = 'json';
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
if (resolvedInput.type !== 'json') {
|
|
84
|
+
// resolve the absolute path of the schema
|
|
85
|
+
resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
|
|
86
|
+
}
|
|
85
87
|
return resolvedInput;
|
|
86
88
|
};
|
|
87
89
|
exports.getResolvedInput = getResolvedInput;
|
|
@@ -116,8 +118,13 @@ class $RefParser {
|
|
|
116
118
|
*
|
|
117
119
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
118
120
|
*/
|
|
119
|
-
async bundle({ arrayBuffer, pathOrUrlOrSchema, resolvedInput, }) {
|
|
120
|
-
await this.parse({
|
|
121
|
+
async bundle({ arrayBuffer, fetch, pathOrUrlOrSchema, resolvedInput, }) {
|
|
122
|
+
await this.parse({
|
|
123
|
+
arrayBuffer,
|
|
124
|
+
fetch,
|
|
125
|
+
pathOrUrlOrSchema,
|
|
126
|
+
resolvedInput,
|
|
127
|
+
});
|
|
121
128
|
await (0, resolve_external_js_1.resolveExternal)(this, this.options);
|
|
122
129
|
const errors = errors_js_1.JSONParserErrorGroup.getParserErrors(this);
|
|
123
130
|
if (errors.length > 0) {
|
|
@@ -139,8 +146,11 @@ class $RefParser {
|
|
|
139
146
|
*
|
|
140
147
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
141
148
|
*/
|
|
142
|
-
async dereference({ pathOrUrlOrSchema, }) {
|
|
143
|
-
await this.parse({
|
|
149
|
+
async dereference({ fetch, pathOrUrlOrSchema, }) {
|
|
150
|
+
await this.parse({
|
|
151
|
+
fetch,
|
|
152
|
+
pathOrUrlOrSchema,
|
|
153
|
+
});
|
|
144
154
|
await (0, resolve_external_js_1.resolveExternal)(this, this.options);
|
|
145
155
|
const errors = errors_js_1.JSONParserErrorGroup.getParserErrors(this);
|
|
146
156
|
if (errors.length > 0) {
|
|
@@ -161,7 +171,7 @@ class $RefParser {
|
|
|
161
171
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
162
172
|
* @returns - The returned promise resolves with the parsed JSON schema object.
|
|
163
173
|
*/
|
|
164
|
-
async parse({ arrayBuffer, pathOrUrlOrSchema, resolvedInput: _resolvedInput, }) {
|
|
174
|
+
async parse({ arrayBuffer, fetch, pathOrUrlOrSchema, resolvedInput: _resolvedInput, }) {
|
|
165
175
|
const resolvedInput = _resolvedInput || (0, exports.getResolvedInput)({ pathOrUrlOrSchema });
|
|
166
176
|
const { path, type } = resolvedInput;
|
|
167
177
|
let { schema } = resolvedInput;
|
|
@@ -182,7 +192,11 @@ class $RefParser {
|
|
|
182
192
|
$refAdded.pathType = type;
|
|
183
193
|
try {
|
|
184
194
|
const resolver = type === 'file' ? file_js_1.fileResolver : url_js_1.urlResolver;
|
|
185
|
-
await resolver.handler(
|
|
195
|
+
await resolver.handler({
|
|
196
|
+
arrayBuffer,
|
|
197
|
+
fetch,
|
|
198
|
+
file,
|
|
199
|
+
});
|
|
186
200
|
const parseResult = await (0, parse_js_1.parseFile)(file, this.options);
|
|
187
201
|
$refAdded.value = parseResult.result;
|
|
188
202
|
schema = parseResult.result;
|
|
@@ -131,7 +131,7 @@ async function resolve$Ref($ref, path, $refs, options) {
|
|
|
131
131
|
let promises = [];
|
|
132
132
|
if (resolvedInput.type !== 'json') {
|
|
133
133
|
const resolver = resolvedInput.type === 'file' ? file_js_1.fileResolver : url_js_1.urlResolver;
|
|
134
|
-
await resolver.handler(file);
|
|
134
|
+
await resolver.handler({ file });
|
|
135
135
|
const parseResult = await (0, parse_js_1.parseFile)(file, options);
|
|
136
136
|
$refAdded.value = parseResult.result;
|
|
137
137
|
promises = crawl(parseResult.result, `${withoutHash}#`, $refs, options, new Set(), true);
|
|
@@ -42,7 +42,7 @@ const ono_1 = require("@jsdevtools/ono");
|
|
|
42
42
|
const url = __importStar(require("../util/url.js"));
|
|
43
43
|
const errors_js_1 = require("../util/errors.js");
|
|
44
44
|
exports.fileResolver = {
|
|
45
|
-
handler: async (file) => {
|
|
45
|
+
handler: async ({ file, }) => {
|
|
46
46
|
let path;
|
|
47
47
|
try {
|
|
48
48
|
path = url.toFileSystemPath(file.url);
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { FileInfo } from "../types/index.js";
|
|
2
|
-
export declare const sendRequest: ({
|
|
3
|
-
|
|
2
|
+
export declare const sendRequest: ({ fetchOptions, redirects, timeout, url, }: {
|
|
3
|
+
fetchOptions?: RequestInit;
|
|
4
4
|
redirects?: string[];
|
|
5
5
|
timeout?: number;
|
|
6
6
|
url: URL | string;
|
|
7
7
|
}) => Promise<{
|
|
8
|
+
fetchOptions?: RequestInit;
|
|
8
9
|
response: Response;
|
|
9
10
|
}>;
|
|
10
11
|
export declare const urlResolver: {
|
|
11
|
-
handler: (
|
|
12
|
+
handler: ({ arrayBuffer, fetch: _fetch, file, }: {
|
|
13
|
+
arrayBuffer?: ArrayBuffer;
|
|
14
|
+
fetch?: RequestInit;
|
|
15
|
+
file: FileInfo;
|
|
16
|
+
}) => Promise<void>;
|
|
12
17
|
};
|
|
@@ -4,58 +4,58 @@ exports.urlResolver = exports.sendRequest = void 0;
|
|
|
4
4
|
const ono_1 = require("@jsdevtools/ono");
|
|
5
5
|
const url_js_1 = require("../util/url.js");
|
|
6
6
|
const errors_js_1 = require("../util/errors.js");
|
|
7
|
-
const sendRequest = async ({
|
|
7
|
+
const sendRequest = async ({ fetchOptions, redirects = [], timeout = 60000, url, }) => {
|
|
8
8
|
url = new URL(url);
|
|
9
9
|
redirects.push(url.href);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
if (response.status === 405 && init?.method === 'HEAD') {
|
|
23
|
-
return { response };
|
|
24
|
-
}
|
|
25
|
-
throw (0, ono_1.ono)({ status: response.status }, `HTTP ERROR ${response.status}`);
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const timeoutId = setTimeout(() => {
|
|
12
|
+
controller.abort();
|
|
13
|
+
}, timeout);
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
signal: controller.signal,
|
|
16
|
+
...fetchOptions,
|
|
17
|
+
});
|
|
18
|
+
clearTimeout(timeoutId);
|
|
19
|
+
if (response.status >= 300 && response.status <= 399) {
|
|
20
|
+
if (redirects.length > 5) {
|
|
21
|
+
throw new errors_js_1.ResolverError((0, ono_1.ono)({ status: response.status }, `Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`));
|
|
26
22
|
}
|
|
27
|
-
if (response.
|
|
28
|
-
|
|
29
|
-
throw new errors_js_1.ResolverError((0, ono_1.ono)({ status: response.status }, `Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`));
|
|
30
|
-
}
|
|
31
|
-
if (!("location" in response.headers) || !response.headers.location) {
|
|
32
|
-
throw (0, ono_1.ono)({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
33
|
-
}
|
|
34
|
-
return (0, exports.sendRequest)({
|
|
35
|
-
init,
|
|
36
|
-
redirects,
|
|
37
|
-
timeout,
|
|
38
|
-
url: (0, url_js_1.resolve)(url.href, response.headers.location),
|
|
39
|
-
});
|
|
23
|
+
if (!("location" in response.headers) || !response.headers.location) {
|
|
24
|
+
throw (0, ono_1.ono)({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
40
25
|
}
|
|
41
|
-
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
26
|
+
return (0, exports.sendRequest)({
|
|
27
|
+
fetchOptions,
|
|
28
|
+
redirects,
|
|
29
|
+
timeout,
|
|
30
|
+
url: (0, url_js_1.resolve)(url.href, response.headers.location),
|
|
31
|
+
});
|
|
45
32
|
}
|
|
33
|
+
return { fetchOptions, response };
|
|
46
34
|
};
|
|
47
35
|
exports.sendRequest = sendRequest;
|
|
48
36
|
exports.urlResolver = {
|
|
49
|
-
handler: async (file,
|
|
37
|
+
handler: async ({ arrayBuffer, fetch: _fetch, file, }) => {
|
|
50
38
|
let data = arrayBuffer;
|
|
51
39
|
if (!data) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
try {
|
|
41
|
+
const { fetchOptions, response } = await (0, exports.sendRequest)({
|
|
42
|
+
fetchOptions: {
|
|
43
|
+
method: 'GET',
|
|
44
|
+
..._fetch,
|
|
45
|
+
},
|
|
46
|
+
url: file.url,
|
|
47
|
+
});
|
|
48
|
+
if (response.status >= 400) {
|
|
49
|
+
// gracefully handle HEAD method not allowed
|
|
50
|
+
if (response.status !== 405 || fetchOptions?.method !== 'HEAD') {
|
|
51
|
+
throw (0, ono_1.ono)({ status: response.status }, `HTTP ERROR ${response.status}`);
|
|
52
|
+
}
|
|
53
|
+
data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new errors_js_1.ResolverError((0, ono_1.ono)(error, `Error requesting ${file.url}`), file.url);
|
|
58
|
+
}
|
|
59
59
|
}
|
|
60
60
|
file.data = Buffer.from(data);
|
|
61
61
|
},
|
package/dist/vite.config.js
CHANGED
|
@@ -4,15 +4,16 @@ const config_1 = require("vitest/config");
|
|
|
4
4
|
const isBrowser = process.env.BROWSER === "true";
|
|
5
5
|
exports.default = (0, config_1.defineConfig)({
|
|
6
6
|
test: {
|
|
7
|
-
|
|
7
|
+
coverage: { reporter: ["lcov", "html", "text"] },
|
|
8
8
|
dir: "test",
|
|
9
|
+
environment: isBrowser ? "jsdom" : "node",
|
|
9
10
|
exclude: ["**/__IGNORED__/**"],
|
|
10
|
-
watch: false,
|
|
11
|
-
globalSetup: isBrowser ? ["./test/fixtures/server.ts"] : undefined,
|
|
12
|
-
testTimeout: 5000,
|
|
13
11
|
globals: true,
|
|
12
|
+
globalSetup: isBrowser ? ["./test/fixtures/server.ts"] : undefined,
|
|
13
|
+
include: ['./lib/**/*.test.ts'],
|
|
14
14
|
passWithNoTests: true,
|
|
15
15
|
reporters: ["verbose"],
|
|
16
|
-
|
|
16
|
+
testTimeout: 5000,
|
|
17
|
+
watch: false,
|
|
17
18
|
},
|
|
18
19
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { getResolvedInput } from '../index';
|
|
6
|
+
|
|
7
|
+
describe('getResolvedInput', () => {
|
|
8
|
+
it('handles url', async () => {
|
|
9
|
+
const pathOrUrlOrSchema = 'https://foo.com';
|
|
10
|
+
const resolvedInput = await getResolvedInput({ pathOrUrlOrSchema });
|
|
11
|
+
expect(resolvedInput.type).toBe('url');
|
|
12
|
+
expect(resolvedInput.schema).toBeUndefined();
|
|
13
|
+
expect(resolvedInput.path).toBe('https://foo.com/');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('handles file', async () => {
|
|
17
|
+
const pathOrUrlOrSchema = './path/to/openapi.json';
|
|
18
|
+
const resolvedInput = await getResolvedInput({ pathOrUrlOrSchema });
|
|
19
|
+
expect(resolvedInput.type).toBe('file');
|
|
20
|
+
expect(resolvedInput.schema).toBeUndefined();
|
|
21
|
+
expect(resolvedInput.path).toBe(path.resolve('./path/to/openapi.json'));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('handles raw spec', async () => {
|
|
25
|
+
const pathOrUrlOrSchema = {
|
|
26
|
+
info: {
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
},
|
|
29
|
+
openapi: '3.1.0',
|
|
30
|
+
paths: {},
|
|
31
|
+
};
|
|
32
|
+
const resolvedInput = await getResolvedInput({ pathOrUrlOrSchema });
|
|
33
|
+
expect(resolvedInput.type).toBe('json');
|
|
34
|
+
expect(resolvedInput.schema).toEqual({
|
|
35
|
+
info: {
|
|
36
|
+
version: '1.0.0',
|
|
37
|
+
},
|
|
38
|
+
openapi: '3.1.0',
|
|
39
|
+
paths: {},
|
|
40
|
+
});
|
|
41
|
+
expect(resolvedInput.path).toBe('');
|
|
42
|
+
});
|
|
43
|
+
});
|
package/lib/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const getResolvedInput = ({
|
|
|
38
38
|
// So we're being generous here and doing the conversion automatically.
|
|
39
39
|
// This is not intended to be a 100% bulletproof solution.
|
|
40
40
|
// If it doesn't work for your use-case, then use a URL instead.
|
|
41
|
-
if (url.isFileSystemPath(resolvedInput.path)) {
|
|
41
|
+
if (resolvedInput.path && url.isFileSystemPath(resolvedInput.path)) {
|
|
42
42
|
resolvedInput.path = url.fromFileSystemPath(resolvedInput.path);
|
|
43
43
|
resolvedInput.type = 'file';
|
|
44
44
|
} else if (!resolvedInput.path && pathOrUrlOrSchema && typeof pathOrUrlOrSchema === 'object') {
|
|
@@ -54,8 +54,10 @@ export const getResolvedInput = ({
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
if (resolvedInput.type !== 'json') {
|
|
58
|
+
// resolve the absolute path of the schema
|
|
59
|
+
resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
|
|
60
|
+
}
|
|
59
61
|
|
|
60
62
|
return resolvedInput;
|
|
61
63
|
}
|
|
@@ -92,14 +94,21 @@ export class $RefParser {
|
|
|
92
94
|
*/
|
|
93
95
|
public async bundle({
|
|
94
96
|
arrayBuffer,
|
|
97
|
+
fetch,
|
|
95
98
|
pathOrUrlOrSchema,
|
|
96
99
|
resolvedInput,
|
|
97
100
|
}: {
|
|
98
101
|
arrayBuffer?: ArrayBuffer;
|
|
102
|
+
fetch?: RequestInit;
|
|
99
103
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
100
104
|
resolvedInput?: ResolvedInput;
|
|
101
105
|
}): Promise<JSONSchema> {
|
|
102
|
-
await this.parse({
|
|
106
|
+
await this.parse({
|
|
107
|
+
arrayBuffer,
|
|
108
|
+
fetch,
|
|
109
|
+
pathOrUrlOrSchema,
|
|
110
|
+
resolvedInput,
|
|
111
|
+
});
|
|
103
112
|
await resolveExternal(this, this.options);
|
|
104
113
|
const errors = JSONParserErrorGroup.getParserErrors(this);
|
|
105
114
|
if (errors.length > 0) {
|
|
@@ -123,11 +132,16 @@ export class $RefParser {
|
|
|
123
132
|
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
124
133
|
*/
|
|
125
134
|
public async dereference({
|
|
135
|
+
fetch,
|
|
126
136
|
pathOrUrlOrSchema,
|
|
127
137
|
}: {
|
|
138
|
+
fetch?: RequestInit;
|
|
128
139
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
129
140
|
}): Promise<JSONSchema> {
|
|
130
|
-
await this.parse({
|
|
141
|
+
await this.parse({
|
|
142
|
+
fetch,
|
|
143
|
+
pathOrUrlOrSchema,
|
|
144
|
+
});
|
|
131
145
|
await resolveExternal(this, this.options);
|
|
132
146
|
const errors = JSONParserErrorGroup.getParserErrors(this);
|
|
133
147
|
if (errors.length > 0) {
|
|
@@ -151,10 +165,12 @@ export class $RefParser {
|
|
|
151
165
|
*/
|
|
152
166
|
public async parse({
|
|
153
167
|
arrayBuffer,
|
|
168
|
+
fetch,
|
|
154
169
|
pathOrUrlOrSchema,
|
|
155
170
|
resolvedInput: _resolvedInput,
|
|
156
171
|
}: {
|
|
157
172
|
arrayBuffer?: ArrayBuffer;
|
|
173
|
+
fetch?: RequestInit;
|
|
158
174
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
159
175
|
resolvedInput?: ResolvedInput;
|
|
160
176
|
}): Promise<{ schema: JSONSchema }> {
|
|
@@ -180,7 +196,11 @@ export class $RefParser {
|
|
|
180
196
|
$refAdded.pathType = type;
|
|
181
197
|
try {
|
|
182
198
|
const resolver = type === 'file' ? fileResolver : urlResolver;
|
|
183
|
-
await resolver.handler(
|
|
199
|
+
await resolver.handler({
|
|
200
|
+
arrayBuffer,
|
|
201
|
+
fetch,
|
|
202
|
+
file,
|
|
203
|
+
});
|
|
184
204
|
const parseResult = await parseFile(file, this.options);
|
|
185
205
|
$refAdded.value = parseResult.result;
|
|
186
206
|
schema = parseResult.result;
|
package/lib/resolve-external.ts
CHANGED
|
@@ -124,7 +124,7 @@ async function resolve$Ref<S extends object = JSONSchema>(
|
|
|
124
124
|
|
|
125
125
|
if (resolvedInput.type !== 'json') {
|
|
126
126
|
const resolver = resolvedInput.type === 'file' ? fileResolver : urlResolver;
|
|
127
|
-
await resolver.handler(file);
|
|
127
|
+
await resolver.handler({ file });
|
|
128
128
|
const parseResult = await parseFile(file, options);
|
|
129
129
|
$refAdded.value = parseResult.result;
|
|
130
130
|
promises = crawl(parseResult.result, `${withoutHash}#`, $refs, options, new Set(), true);
|
package/lib/resolvers/file.ts
CHANGED
|
@@ -5,7 +5,11 @@ import { ResolverError } from "../util/errors.js";
|
|
|
5
5
|
import type { FileInfo } from "../types/index.js";
|
|
6
6
|
|
|
7
7
|
export const fileResolver = {
|
|
8
|
-
handler: async (
|
|
8
|
+
handler: async ({
|
|
9
|
+
file,
|
|
10
|
+
}: {
|
|
11
|
+
file: FileInfo;
|
|
12
|
+
}): Promise<void> => {
|
|
9
13
|
let path: string | undefined;
|
|
10
14
|
|
|
11
15
|
try {
|
package/lib/resolvers/url.ts
CHANGED
|
@@ -4,83 +4,92 @@ import { ResolverError } from "../util/errors.js";
|
|
|
4
4
|
import type { FileInfo } from "../types/index.js";
|
|
5
5
|
|
|
6
6
|
export const sendRequest = async ({
|
|
7
|
-
|
|
7
|
+
fetchOptions,
|
|
8
8
|
redirects = [],
|
|
9
9
|
timeout = 60_000,
|
|
10
10
|
url,
|
|
11
11
|
}: {
|
|
12
|
-
|
|
12
|
+
fetchOptions?: RequestInit;
|
|
13
13
|
redirects?: string[];
|
|
14
14
|
timeout?: number;
|
|
15
15
|
url: URL | string;
|
|
16
16
|
}): Promise<{
|
|
17
|
+
fetchOptions?: RequestInit;
|
|
17
18
|
response: Response;
|
|
18
19
|
}> => {
|
|
19
20
|
url = new URL(url);
|
|
20
21
|
redirects.push(url.href);
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
clearTimeout(timeoutId);
|
|
32
|
-
|
|
33
|
-
if (response.status >= 400) {
|
|
34
|
-
// gracefully handle HEAD method not allowed
|
|
35
|
-
if (response.status === 405 && init?.method === 'HEAD') {
|
|
36
|
-
return { response };
|
|
37
|
-
}
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timeoutId = setTimeout(() => {
|
|
25
|
+
controller.abort();
|
|
26
|
+
}, timeout);
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
signal: controller.signal,
|
|
29
|
+
...fetchOptions,
|
|
30
|
+
});
|
|
31
|
+
clearTimeout(timeoutId);
|
|
38
32
|
|
|
39
|
-
|
|
33
|
+
if (response.status >= 300 && response.status <= 399) {
|
|
34
|
+
if (redirects.length > 5) {
|
|
35
|
+
throw new ResolverError(
|
|
36
|
+
ono(
|
|
37
|
+
{ status: response.status },
|
|
38
|
+
`Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
|
|
39
|
+
),
|
|
40
|
+
);
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
if (response.
|
|
43
|
-
|
|
44
|
-
throw new ResolverError(
|
|
45
|
-
ono(
|
|
46
|
-
{ status: response.status },
|
|
47
|
-
`Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
|
|
48
|
-
),
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!("location" in response.headers) || !response.headers.location) {
|
|
53
|
-
throw ono({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return sendRequest({
|
|
57
|
-
init,
|
|
58
|
-
redirects,
|
|
59
|
-
timeout,
|
|
60
|
-
url: resolve(url.href, response.headers.location as string),
|
|
61
|
-
});
|
|
42
|
+
|
|
43
|
+
if (!("location" in response.headers) || !response.headers.location) {
|
|
44
|
+
throw ono({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
62
45
|
}
|
|
63
46
|
|
|
64
|
-
return {
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
return sendRequest({
|
|
48
|
+
fetchOptions,
|
|
49
|
+
redirects,
|
|
50
|
+
timeout,
|
|
51
|
+
url: resolve(url.href, response.headers.location as string),
|
|
52
|
+
});
|
|
67
53
|
}
|
|
54
|
+
|
|
55
|
+
return { fetchOptions, response };
|
|
68
56
|
}
|
|
69
57
|
|
|
70
58
|
export const urlResolver = {
|
|
71
|
-
handler: async (
|
|
59
|
+
handler: async ({
|
|
60
|
+
arrayBuffer,
|
|
61
|
+
fetch: _fetch,
|
|
62
|
+
file,
|
|
63
|
+
}: {
|
|
64
|
+
arrayBuffer?: ArrayBuffer;
|
|
65
|
+
fetch?: RequestInit;
|
|
66
|
+
file: FileInfo;
|
|
67
|
+
}): Promise<void> => {
|
|
72
68
|
let data = arrayBuffer;
|
|
73
69
|
|
|
74
70
|
if (!data) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
try {
|
|
72
|
+
const { fetchOptions, response } = await sendRequest({
|
|
73
|
+
fetchOptions: {
|
|
74
|
+
method: 'GET',
|
|
75
|
+
..._fetch,
|
|
76
|
+
},
|
|
77
|
+
url: file.url,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (response.status >= 400) {
|
|
81
|
+
// gracefully handle HEAD method not allowed
|
|
82
|
+
if (response.status !== 405 || fetchOptions?.method !== 'HEAD') {
|
|
83
|
+
throw ono({ status: response.status }, `HTTP ERROR ${response.status}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0)
|
|
87
|
+
}
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
throw new ResolverError(ono(error, `Error requesting ${file.url}`), file.url);
|
|
90
|
+
}
|
|
82
91
|
}
|
|
83
92
|
|
|
84
|
-
file.data = Buffer.from(data);
|
|
93
|
+
file.data = Buffer.from(data!);
|
|
85
94
|
},
|
|
86
95
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hey-api/json-schema-ref-parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Parse, Resolve, and Dereference JSON Schema $ref pointers",
|
|
5
5
|
"homepage": "https://heyapi.dev/",
|
|
6
6
|
"repository": {
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"test:browser": "cross-env BROWSER=\"true\" yarn test",
|
|
49
49
|
"test:node": "yarn test",
|
|
50
50
|
"test:update": "vitest -u",
|
|
51
|
-
"test:watch": "vitest
|
|
52
|
-
"test": "vitest --
|
|
51
|
+
"test:watch": "vitest watch --config vitest.config.unit.ts",
|
|
52
|
+
"test": "vitest run --config vitest.config.unit.ts",
|
|
53
53
|
"typecheck": "tsc --noEmit"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|