@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.
@@ -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
- // resolve the absolute path of the schema
84
- resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
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
- try {
11
- const controller = new AbortController();
12
- const timeoutId = setTimeout(() => {
13
- controller.abort();
14
- }, 60000);
15
- const response = await fetch(url, {
16
- signal: controller.signal,
17
- ...init,
18
- });
19
- clearTimeout(timeoutId);
20
- if (response.status >= 400) {
21
- 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
+ ...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.status >= 300) {
24
- if (redirects.length > 5) {
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 { response };
37
- }
38
- catch (error) {
39
- throw new errors_js_1.ResolverError((0, ono_1.ono)(error, `Error requesting ${url.href}`), url.href);
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
- const { response } = await (0, exports.sendRequest)({
48
- init: {
49
- method: 'GET',
50
- },
51
- url: file.url,
52
- });
53
- data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0);
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
  },
@@ -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
- environment: isBrowser ? "jsdom" : "node",
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
- coverage: { reporter: ["lcov", "html", "text"] },
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
- // resolve the absolute path of the schema
58
- resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
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
  }
@@ -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
- try {
21
- const controller = new AbortController();
22
- const timeoutId = setTimeout(() => {
23
- controller.abort();
24
- }, 60_000);
25
- const response = await fetch(url, {
26
- signal: controller.signal,
27
- ...init,
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
- if (response.status >= 400) {
32
- throw ono({ status: response.status }, `HTTP ERROR ${response.status}`);
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.status >= 300) {
36
- if (redirects.length > 5) {
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 { response };
57
- } catch (error: any) {
58
- throw new ResolverError(ono(error, `Error requesting ${url.href}`), url.href);
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
- const { response } = await sendRequest({
68
- init: {
69
- method: 'GET',
70
- },
71
- url: file.url,
72
- });
73
- data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0)
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.1",
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 -w",
52
- "test": "vitest --coverage",
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": {