@hey-api/json-schema-ref-parser 1.0.1 → 1.0.3
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.js +5 -3
- package/dist/lib/resolvers/url.d.ts +3 -1
- package/dist/lib/resolvers/url.js +40 -36
- package/dist/vite.config.js +6 -5
- package/lib/__tests__/index.test.ts +43 -0
- package/lib/index.ts +5 -3
- package/lib/resolvers/url.ts +51 -43
- 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.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;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { FileInfo } from "../types/index.js";
|
|
2
|
-
export declare const sendRequest: ({ init, redirects, url, }: {
|
|
2
|
+
export declare const sendRequest: ({ init, redirects, timeout, url, }: {
|
|
3
3
|
init?: RequestInit;
|
|
4
4
|
redirects?: string[];
|
|
5
|
+
timeout?: number;
|
|
5
6
|
url: URL | string;
|
|
6
7
|
}) => Promise<{
|
|
8
|
+
init?: RequestInit;
|
|
7
9
|
response: Response;
|
|
8
10
|
}>;
|
|
9
11
|
export declare const urlResolver: {
|
|
@@ -4,53 +4,57 @@ 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 ({ init, redirects = [], url, }) => {
|
|
7
|
+
const sendRequest = async ({ init, 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
|
-
throw (0, ono_1.ono)({ status: 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
|
+
...init,
|
|
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 ")}`));
|
|
22
22
|
}
|
|
23
|
-
if (response.
|
|
24
|
-
|
|
25
|
-
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
|
-
}
|
|
27
|
-
if (!("location" in response.headers) || !response.headers.location) {
|
|
28
|
-
throw (0, ono_1.ono)({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
29
|
-
}
|
|
30
|
-
return (0, exports.sendRequest)({
|
|
31
|
-
init,
|
|
32
|
-
redirects,
|
|
33
|
-
url: (0, url_js_1.resolve)(url.href, response.headers.location),
|
|
34
|
-
});
|
|
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`);
|
|
35
25
|
}
|
|
36
|
-
return {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
return (0, exports.sendRequest)({
|
|
27
|
+
init,
|
|
28
|
+
redirects,
|
|
29
|
+
timeout,
|
|
30
|
+
url: (0, url_js_1.resolve)(url.href, response.headers.location),
|
|
31
|
+
});
|
|
40
32
|
}
|
|
33
|
+
return { init, response };
|
|
41
34
|
};
|
|
42
35
|
exports.sendRequest = sendRequest;
|
|
43
36
|
exports.urlResolver = {
|
|
44
37
|
handler: async (file, arrayBuffer) => {
|
|
45
38
|
let data = arrayBuffer;
|
|
46
39
|
if (!data) {
|
|
47
|
-
|
|
48
|
-
init
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
try {
|
|
41
|
+
const { init, response } = await (0, exports.sendRequest)({
|
|
42
|
+
init: {
|
|
43
|
+
method: 'GET',
|
|
44
|
+
},
|
|
45
|
+
url: file.url,
|
|
46
|
+
});
|
|
47
|
+
if (response.status >= 400) {
|
|
48
|
+
// gracefully handle HEAD method not allowed
|
|
49
|
+
if (response.status !== 405 || init?.method !== 'HEAD') {
|
|
50
|
+
throw (0, ono_1.ono)({ status: response.status }, `HTTP ERROR ${response.status}`);
|
|
51
|
+
}
|
|
52
|
+
data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new errors_js_1.ResolverError((0, ono_1.ono)(error, `Error requesting ${file.url}`), file.url);
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
file.data = Buffer.from(data);
|
|
56
60
|
},
|
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
|
}
|
package/lib/resolvers/url.ts
CHANGED
|
@@ -6,57 +6,53 @@ import type { FileInfo } from "../types/index.js";
|
|
|
6
6
|
export const sendRequest = async ({
|
|
7
7
|
init,
|
|
8
8
|
redirects = [],
|
|
9
|
+
timeout = 60_000,
|
|
9
10
|
url,
|
|
10
11
|
}: {
|
|
11
12
|
init?: RequestInit;
|
|
12
13
|
redirects?: string[];
|
|
14
|
+
timeout?: number;
|
|
13
15
|
url: URL | string;
|
|
14
16
|
}): Promise<{
|
|
17
|
+
init?: RequestInit;
|
|
15
18
|
response: Response;
|
|
16
19
|
}> => {
|
|
17
20
|
url = new URL(url);
|
|
18
21
|
redirects.push(url.href);
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
clearTimeout(timeoutId);
|
|
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
|
+
...init,
|
|
30
|
+
});
|
|
31
|
+
clearTimeout(timeoutId);
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
);
|
|
33
41
|
}
|
|
34
|
-
|
|
35
|
-
if (response.
|
|
36
|
-
|
|
37
|
-
throw new ResolverError(
|
|
38
|
-
ono(
|
|
39
|
-
{ status: response.status },
|
|
40
|
-
`Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
|
|
41
|
-
),
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!("location" in response.headers) || !response.headers.location) {
|
|
46
|
-
throw ono({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return sendRequest({
|
|
50
|
-
init,
|
|
51
|
-
redirects,
|
|
52
|
-
url: resolve(url.href, response.headers.location as string),
|
|
53
|
-
});
|
|
42
|
+
|
|
43
|
+
if (!("location" in response.headers) || !response.headers.location) {
|
|
44
|
+
throw ono({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
|
|
54
45
|
}
|
|
55
46
|
|
|
56
|
-
return {
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
return sendRequest({
|
|
48
|
+
init,
|
|
49
|
+
redirects,
|
|
50
|
+
timeout,
|
|
51
|
+
url: resolve(url.href, response.headers.location as string),
|
|
52
|
+
});
|
|
59
53
|
}
|
|
54
|
+
|
|
55
|
+
return { init, response };
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
export const urlResolver = {
|
|
@@ -64,15 +60,27 @@ export const urlResolver = {
|
|
|
64
60
|
let data = arrayBuffer;
|
|
65
61
|
|
|
66
62
|
if (!data) {
|
|
67
|
-
|
|
68
|
-
init
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
try {
|
|
64
|
+
const { init, response } = await sendRequest({
|
|
65
|
+
init: {
|
|
66
|
+
method: 'GET',
|
|
67
|
+
},
|
|
68
|
+
url: file.url,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (response.status >= 400) {
|
|
72
|
+
// gracefully handle HEAD method not allowed
|
|
73
|
+
if (response.status !== 405 || init?.method !== 'HEAD') {
|
|
74
|
+
throw ono({ status: response.status }, `HTTP ERROR ${response.status}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0)
|
|
78
|
+
}
|
|
79
|
+
} catch (error: any) {
|
|
80
|
+
throw new ResolverError(ono(error, `Error requesting ${file.url}`), file.url);
|
|
81
|
+
}
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
file.data = Buffer.from(data);
|
|
84
|
+
file.data = Buffer.from(data!);
|
|
77
85
|
},
|
|
78
86
|
};
|
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.3",
|
|
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": {
|