@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.
@@ -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
+ });
@@ -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
- // 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;
@@ -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({ arrayBuffer, pathOrUrlOrSchema, resolvedInput });
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({ pathOrUrlOrSchema });
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(file, arrayBuffer);
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);
@@ -1,4 +1,6 @@
1
1
  import type { FileInfo } from "../types/index.js";
2
2
  export declare const fileResolver: {
3
- handler: (file: FileInfo) => Promise<void>;
3
+ handler: ({ file, }: {
4
+ file: FileInfo;
5
+ }) => Promise<void>;
4
6
  };
@@ -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: ({ init, redirects, timeout, url, }: {
3
- init?: RequestInit;
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: (file: FileInfo, arrayBuffer?: ArrayBuffer) => Promise<void>;
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 ({ init, redirects = [], timeout = 60000, url, }) => {
7
+ const sendRequest = async ({ fetchOptions, 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
- }, timeout);
15
- const response = await fetch(url, {
16
- signal: controller.signal,
17
- ...init,
18
- });
19
- clearTimeout(timeoutId);
20
- if (response.status >= 400) {
21
- // gracefully handle HEAD method not allowed
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.status >= 300) {
28
- if (redirects.length > 5) {
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 { response };
42
- }
43
- catch (error) {
44
- throw new errors_js_1.ResolverError((0, ono_1.ono)(error, `Error requesting ${url.href}`), url.href);
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, arrayBuffer) => {
37
+ handler: async ({ arrayBuffer, fetch: _fetch, file, }) => {
50
38
  let data = arrayBuffer;
51
39
  if (!data) {
52
- const { response } = await (0, exports.sendRequest)({
53
- init: {
54
- method: 'GET',
55
- },
56
- url: file.url,
57
- });
58
- data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0);
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
  },
@@ -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
  }
@@ -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({ arrayBuffer, pathOrUrlOrSchema, resolvedInput });
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({ pathOrUrlOrSchema });
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(file, arrayBuffer);
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;
@@ -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);
@@ -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 (file: FileInfo): Promise<void> => {
8
+ handler: async ({
9
+ file,
10
+ }: {
11
+ file: FileInfo;
12
+ }): Promise<void> => {
9
13
  let path: string | undefined;
10
14
 
11
15
  try {
@@ -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
- init,
7
+ fetchOptions,
8
8
  redirects = [],
9
9
  timeout = 60_000,
10
10
  url,
11
11
  }: {
12
- init?: RequestInit;
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
- try {
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);
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
- 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
+ );
40
41
  }
41
-
42
- if (response.status >= 300) {
43
- if (redirects.length > 5) {
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 { response };
65
- } catch (error: any) {
66
- throw new ResolverError(ono(error, `Error requesting ${url.href}`), url.href);
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 (file: FileInfo, arrayBuffer?: ArrayBuffer): Promise<void> => {
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
- const { response } = await sendRequest({
76
- init: {
77
- method: 'GET',
78
- },
79
- url: file.url,
80
- });
81
- data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0)
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.2",
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 -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": {