docusaurus-theme-openapi-docs 0.0.0-1074 → 0.0.0-1076

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.
@@ -71,6 +71,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
71
71
  const react_1 = __importStar(require("react"));
72
72
  const client_1 = require("@docusaurus/plugin-content-docs/client");
73
73
  const Translate_1 = require("@docusaurus/Translate");
74
+ const useDocusaurusContext_1 = __importDefault(
75
+ require("@docusaurus/useDocusaurusContext")
76
+ );
74
77
  const Accept_1 = __importDefault(require("@theme/ApiExplorer/Accept"));
75
78
  const Authorization_1 = __importDefault(
76
79
  require("@theme/ApiExplorer/Authorization")
@@ -91,11 +94,17 @@ const hooks_1 = require("@theme/ApiItem/hooks");
91
94
  const translationIds_1 = require("@theme/translationIds");
92
95
  const sdk = __importStar(require("postman-collection"));
93
96
  const react_hook_form_1 = require("react-hook-form");
94
- const makeRequest_1 = __importDefault(require("./makeRequest"));
97
+ const makeRequest_1 = __importStar(require("./makeRequest"));
95
98
  function Request({ item }) {
96
99
  const postman = new sdk.Request(item.postman);
97
100
  const metadata = (0, client_1.useDoc)();
98
- const { proxy, hide_send_button: hideSendButton } = metadata.frontMatter;
101
+ const { proxy: frontMatterProxy, hide_send_button: hideSendButton } =
102
+ metadata.frontMatter;
103
+ const { siteConfig } = (0, useDocusaurusContext_1.default)();
104
+ const themeConfig = siteConfig.themeConfig;
105
+ const requestTimeout = themeConfig.api?.requestTimeout;
106
+ // Frontmatter proxy (per-spec) takes precedence over theme config proxy (site-wide)
107
+ const proxy = frontMatterProxy ?? themeConfig.api?.proxy;
99
108
  const pathParams = (0, hooks_1.useTypedSelector)(
100
109
  (state) => state.params.path
101
110
  );
@@ -180,6 +189,35 @@ function Request({ item }) {
180
189
  res.headers &&
181
190
  dispatch((0, slice_1.setHeaders)(Object.fromEntries(res.headers)));
182
191
  };
192
+ const getErrorMessage = (errorType) => {
193
+ switch (errorType) {
194
+ case "timeout":
195
+ return (0, Translate_1.translate)({
196
+ id: translationIds_1.OPENAPI_REQUEST.ERROR_TIMEOUT,
197
+ message:
198
+ "The request timed out waiting for the server to respond. Please try again. If the issue persists, try using a different client (e.g., curl) with a longer timeout.",
199
+ });
200
+ case "network":
201
+ return (0, Translate_1.translate)({
202
+ id: translationIds_1.OPENAPI_REQUEST.ERROR_NETWORK,
203
+ message:
204
+ "Unable to reach the server. Please check your network connection and verify the server URL is correct. If the server is running, this may be a CORS issue.",
205
+ });
206
+ case "cors":
207
+ return (0, Translate_1.translate)({
208
+ id: translationIds_1.OPENAPI_REQUEST.ERROR_CORS,
209
+ message:
210
+ "The request was blocked, possibly due to CORS restrictions. Ensure the server allows requests from this origin, or try using a proxy.",
211
+ });
212
+ case "unknown":
213
+ default:
214
+ return (0, Translate_1.translate)({
215
+ id: translationIds_1.OPENAPI_REQUEST.ERROR_UNKNOWN,
216
+ message:
217
+ "An unexpected error occurred while making the request. Please try again.",
218
+ });
219
+ }
220
+ };
183
221
  const onSubmit = async (data) => {
184
222
  dispatch(
185
223
  (0, slice_1.setResponse)(
@@ -191,7 +229,12 @@ function Request({ item }) {
191
229
  );
192
230
  try {
193
231
  await delay(1200);
194
- const res = await (0, makeRequest_1.default)(postmanRequest, proxy, body);
232
+ const res = await (0, makeRequest_1.default)(
233
+ postmanRequest,
234
+ proxy,
235
+ body,
236
+ requestTimeout
237
+ );
195
238
  if (res.headers.get("content-type")?.includes("text/event-stream")) {
196
239
  await handleEventStream(res);
197
240
  } else {
@@ -199,14 +242,16 @@ function Request({ item }) {
199
242
  }
200
243
  } catch (e) {
201
244
  console.log(e);
202
- dispatch(
203
- (0, slice_1.setResponse)(
204
- (0, Translate_1.translate)({
205
- id: translationIds_1.OPENAPI_REQUEST.CONNECTION_FAILED,
206
- message: "Connection failed",
207
- })
208
- )
209
- );
245
+ let errorMessage;
246
+ if (e instanceof makeRequest_1.RequestError) {
247
+ errorMessage = getErrorMessage(e.type);
248
+ } else {
249
+ errorMessage = (0, Translate_1.translate)({
250
+ id: translationIds_1.OPENAPI_REQUEST.CONNECTION_FAILED,
251
+ message: "Connection failed",
252
+ });
253
+ }
254
+ dispatch((0, slice_1.setResponse)(errorMessage));
210
255
  dispatch((0, slice_1.clearCode)());
211
256
  dispatch((0, slice_1.clearHeaders)());
212
257
  }
@@ -1,4 +1,10 @@
1
1
  import { Body } from "@theme/ApiExplorer/Body/slice";
2
2
  import * as sdk from "postman-collection";
3
- declare function makeRequest(request: sdk.Request, proxy: string | undefined, _body: Body): Promise<any>;
3
+ export type RequestErrorType = "timeout" | "network" | "cors" | "abort" | "unknown";
4
+ export declare class RequestError extends Error {
5
+ type: RequestErrorType;
6
+ originalError?: Error;
7
+ constructor(type: RequestErrorType, message: string, originalError?: Error);
8
+ }
9
+ declare function makeRequest(request: sdk.Request, proxy: string | undefined, _body: Body, timeout?: number): Promise<Response>;
4
10
  export default makeRequest;
@@ -6,13 +6,65 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  * ========================================================================== */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- function fetchWithtimeout(url, options, timeout = 5000) {
10
- return Promise.race([
11
- fetch(url, options),
12
- new Promise((_, reject) =>
13
- setTimeout(() => reject(new Error("Request timed out")), timeout)
14
- ),
15
- ]);
9
+ exports.RequestError = void 0;
10
+ class RequestError extends Error {
11
+ type;
12
+ originalError;
13
+ constructor(type, message, originalError) {
14
+ super(message);
15
+ this.name = "RequestError";
16
+ this.type = type;
17
+ this.originalError = originalError;
18
+ }
19
+ }
20
+ exports.RequestError = RequestError;
21
+ const DEFAULT_REQUEST_TIMEOUT = 30000; // 30 seconds
22
+ function fetchWithtimeout(url, options, timeout = DEFAULT_REQUEST_TIMEOUT) {
23
+ const controller = new AbortController();
24
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
25
+ return fetch(url, {
26
+ ...options,
27
+ signal: controller.signal,
28
+ })
29
+ .then((response) => {
30
+ clearTimeout(timeoutId);
31
+ return response;
32
+ })
33
+ .catch((error) => {
34
+ clearTimeout(timeoutId);
35
+ // Check if it was an abort due to timeout
36
+ if (error.name === "AbortError") {
37
+ throw new RequestError(
38
+ "timeout",
39
+ "The request timed out waiting for the server to respond. Please try again. If the issue persists, try using a different client (e.g., curl) with a longer timeout.",
40
+ error
41
+ );
42
+ }
43
+ // Check for network errors (offline, DNS failure, etc.)
44
+ if (error instanceof TypeError && error.message === "Failed to fetch") {
45
+ // This could be CORS, network failure, or the server being unreachable
46
+ throw new RequestError(
47
+ "network",
48
+ "Unable to reach the server. Please check your network connection and verify the server URL is correct. If the server is running, this may be a CORS issue.",
49
+ error
50
+ );
51
+ }
52
+ // Handle other TypeErrors that might indicate CORS issues
53
+ if (error instanceof TypeError) {
54
+ throw new RequestError(
55
+ "cors",
56
+ "The request was blocked, possibly due to CORS restrictions. Ensure the server allows requests from this origin, or try using a proxy.",
57
+ error
58
+ );
59
+ }
60
+ // Generic error fallback
61
+ throw new RequestError(
62
+ "unknown",
63
+ error.message ||
64
+ "An unexpected error occurred while making the request.",
65
+ error
66
+ );
67
+ });
16
68
  }
17
69
  async function loadImage(content) {
18
70
  return new Promise((accept, reject) => {
@@ -33,7 +85,12 @@ async function loadImage(content) {
33
85
  reader.readAsArrayBuffer(content);
34
86
  });
35
87
  }
36
- async function makeRequest(request, proxy, _body) {
88
+ async function makeRequest(
89
+ request,
90
+ proxy,
91
+ _body,
92
+ timeout = DEFAULT_REQUEST_TIMEOUT
93
+ ) {
37
94
  const headers = request.toJSON().header;
38
95
  let myHeaders = new Headers();
39
96
  if (headers) {
@@ -171,7 +228,8 @@ async function makeRequest(request, proxy, _body) {
171
228
  let normalizedProxy = proxy.replace(/\/$/, "") + "/";
172
229
  finalUrl = normalizedProxy + request.url.toString();
173
230
  }
174
- return fetchWithtimeout(finalUrl, requestOptions).then((response) => {
231
+ try {
232
+ const response = await fetchWithtimeout(finalUrl, requestOptions, timeout);
175
233
  const contentType = response.headers.get("content-type");
176
234
  let fileExtension = "";
177
235
  if (contentType) {
@@ -199,25 +257,37 @@ async function makeRequest(request, proxy, _body) {
199
257
  fileExtension = ".zip";
200
258
  }
201
259
  if (fileExtension) {
202
- return response.blob().then((blob) => {
203
- const url = window.URL.createObjectURL(blob);
204
- const link = document.createElement("a");
205
- link.href = url;
206
- // Now the file name includes the extension
207
- link.setAttribute("download", `file${fileExtension}`);
208
- // These two lines are necessary to make the link click in Firefox
209
- link.style.display = "none";
210
- document.body.appendChild(link);
211
- link.click();
212
- // After link is clicked, it's safe to remove it.
213
- setTimeout(() => document.body.removeChild(link), 0);
214
- return response;
215
- });
260
+ const blob = await response.blob();
261
+ const url = window.URL.createObjectURL(blob);
262
+ const link = document.createElement("a");
263
+ link.href = url;
264
+ // Now the file name includes the extension
265
+ link.setAttribute("download", `file${fileExtension}`);
266
+ // These two lines are necessary to make the link click in Firefox
267
+ link.style.display = "none";
268
+ document.body.appendChild(link);
269
+ link.click();
270
+ // After link is clicked, it's safe to remove it.
271
+ setTimeout(() => document.body.removeChild(link), 0);
272
+ return response;
216
273
  } else {
217
274
  return response;
218
275
  }
219
276
  }
220
277
  return response;
221
- });
278
+ } catch (error) {
279
+ // Re-throw RequestError instances as-is
280
+ if (error instanceof RequestError) {
281
+ throw error;
282
+ }
283
+ // Wrap unexpected errors
284
+ throw new RequestError(
285
+ "unknown",
286
+ error instanceof Error
287
+ ? error.message
288
+ : "An unexpected error occurred while processing the response.",
289
+ error instanceof Error ? error : undefined
290
+ );
291
+ }
222
292
  }
223
293
  exports.default = makeRequest;
@@ -20,6 +20,10 @@ export declare const OPENAPI_REQUEST: {
20
20
  PARAMETERS_TITLE: string;
21
21
  FETCHING_MESSAGE: string;
22
22
  CONNECTION_FAILED: string;
23
+ ERROR_TIMEOUT: string;
24
+ ERROR_NETWORK: string;
25
+ ERROR_CORS: string;
26
+ ERROR_UNKNOWN: string;
23
27
  };
24
28
  export declare const OPENAPI_SERVER: {
25
29
  EDIT_BUTTON: string;
@@ -43,6 +43,10 @@ exports.OPENAPI_REQUEST = {
43
43
  PARAMETERS_TITLE: "theme.openapi.request.parameters.title",
44
44
  FETCHING_MESSAGE: "theme.openapi.request.fetchingMessage",
45
45
  CONNECTION_FAILED: "theme.openapi.request.connectionFailed",
46
+ ERROR_TIMEOUT: "theme.openapi.request.error.timeout",
47
+ ERROR_NETWORK: "theme.openapi.request.error.network",
48
+ ERROR_CORS: "theme.openapi.request.error.cors",
49
+ ERROR_UNKNOWN: "theme.openapi.request.error.unknown",
46
50
  };
47
51
  exports.OPENAPI_SERVER = {
48
52
  EDIT_BUTTON: "theme.openapi.server.editButton",
package/lib/types.d.ts CHANGED
@@ -4,6 +4,8 @@ export interface ThemeConfig {
4
4
  api?: {
5
5
  proxy?: string;
6
6
  authPersistance?: false | "localStorage" | "sessionStorage";
7
+ /** Request timeout in milliseconds. Defaults to 30000 (30 seconds). */
8
+ requestTimeout?: number;
7
9
  };
8
10
  }
9
11
  export type JSONSchema = JSONSchema4 | JSONSchema6 | JSONSchema7;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docusaurus-theme-openapi-docs",
3
3
  "description": "OpenAPI theme for Docusaurus.",
4
- "version": "0.0.0-1074",
4
+ "version": "0.0.0-1076",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -38,7 +38,7 @@
38
38
  "@types/postman-collection": "^3.5.11",
39
39
  "@types/react-modal": "^3.16.3",
40
40
  "concurrently": "^9.2.0",
41
- "docusaurus-plugin-openapi-docs": "0.0.0-1074",
41
+ "docusaurus-plugin-openapi-docs": "0.0.0-1076",
42
42
  "docusaurus-plugin-sass": "^0.2.6",
43
43
  "eslint-plugin-prettier": "^5.5.1"
44
44
  },
@@ -81,5 +81,5 @@
81
81
  "engines": {
82
82
  "node": ">=14"
83
83
  },
84
- "gitHead": "3d60239f7cc168258b19fb137135df436ce5ab44"
84
+ "gitHead": "c5c4370633dd11b143393c1bcda342b49816e15a"
85
85
  }
@@ -10,6 +10,8 @@ import React, { useState } from "react";
10
10
 
11
11
  import { useDoc } from "@docusaurus/plugin-content-docs/client";
12
12
  import { translate } from "@docusaurus/Translate";
13
+ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
14
+ import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
13
15
  import Accept from "@theme/ApiExplorer/Accept";
14
16
  import Authorization from "@theme/ApiExplorer/Authorization";
15
17
  import Body from "@theme/ApiExplorer/Body";
@@ -31,12 +33,18 @@ import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
31
33
  import * as sdk from "postman-collection";
32
34
  import { FormProvider, useForm } from "react-hook-form";
33
35
 
34
- import makeRequest from "./makeRequest";
36
+ import makeRequest, { RequestError, RequestErrorType } from "./makeRequest";
35
37
 
36
38
  function Request({ item }: { item: ApiItem }) {
37
39
  const postman = new sdk.Request(item.postman);
38
40
  const metadata = useDoc();
39
- const { proxy, hide_send_button: hideSendButton } = metadata.frontMatter;
41
+ const { proxy: frontMatterProxy, hide_send_button: hideSendButton } =
42
+ metadata.frontMatter;
43
+ const { siteConfig } = useDocusaurusContext();
44
+ const themeConfig = siteConfig.themeConfig as ThemeConfig;
45
+ const requestTimeout = themeConfig.api?.requestTimeout;
46
+ // Frontmatter proxy (per-spec) takes precedence over theme config proxy (site-wide)
47
+ const proxy = frontMatterProxy ?? themeConfig.api?.proxy;
40
48
 
41
49
  const pathParams = useTypedSelector((state: any) => state.params.path);
42
50
  const queryParams = useTypedSelector((state: any) => state.params.query);
@@ -118,6 +126,36 @@ function Request({ item }: { item: ApiItem }) {
118
126
  res.headers && dispatch(setHeaders(Object.fromEntries(res.headers)));
119
127
  };
120
128
 
129
+ const getErrorMessage = (errorType: RequestErrorType): string => {
130
+ switch (errorType) {
131
+ case "timeout":
132
+ return translate({
133
+ id: OPENAPI_REQUEST.ERROR_TIMEOUT,
134
+ message:
135
+ "The request timed out waiting for the server to respond. Please try again. If the issue persists, try using a different client (e.g., curl) with a longer timeout.",
136
+ });
137
+ case "network":
138
+ return translate({
139
+ id: OPENAPI_REQUEST.ERROR_NETWORK,
140
+ message:
141
+ "Unable to reach the server. Please check your network connection and verify the server URL is correct. If the server is running, this may be a CORS issue.",
142
+ });
143
+ case "cors":
144
+ return translate({
145
+ id: OPENAPI_REQUEST.ERROR_CORS,
146
+ message:
147
+ "The request was blocked, possibly due to CORS restrictions. Ensure the server allows requests from this origin, or try using a proxy.",
148
+ });
149
+ case "unknown":
150
+ default:
151
+ return translate({
152
+ id: OPENAPI_REQUEST.ERROR_UNKNOWN,
153
+ message:
154
+ "An unexpected error occurred while making the request. Please try again.",
155
+ });
156
+ }
157
+ };
158
+
121
159
  const onSubmit = async (data) => {
122
160
  dispatch(
123
161
  setResponse(
@@ -129,7 +167,12 @@ function Request({ item }: { item: ApiItem }) {
129
167
  );
130
168
  try {
131
169
  await delay(1200);
132
- const res = await makeRequest(postmanRequest, proxy, body);
170
+ const res = await makeRequest(
171
+ postmanRequest,
172
+ proxy,
173
+ body,
174
+ requestTimeout
175
+ );
133
176
  if (res.headers.get("content-type")?.includes("text/event-stream")) {
134
177
  await handleEventStream(res);
135
178
  } else {
@@ -137,14 +180,18 @@ function Request({ item }: { item: ApiItem }) {
137
180
  }
138
181
  } catch (e) {
139
182
  console.log(e);
140
- dispatch(
141
- setResponse(
142
- translate({
143
- id: OPENAPI_REQUEST.CONNECTION_FAILED,
144
- message: "Connection failed",
145
- })
146
- )
147
- );
183
+
184
+ let errorMessage: string;
185
+ if (e instanceof RequestError) {
186
+ errorMessage = getErrorMessage(e.type);
187
+ } else {
188
+ errorMessage = translate({
189
+ id: OPENAPI_REQUEST.CONNECTION_FAILED,
190
+ message: "Connection failed",
191
+ });
192
+ }
193
+
194
+ dispatch(setResponse(errorMessage));
148
195
  dispatch(clearCode());
149
196
  dispatch(clearHeaders());
150
197
  }
@@ -8,17 +8,83 @@
8
8
  import { Body } from "@theme/ApiExplorer/Body/slice";
9
9
  import * as sdk from "postman-collection";
10
10
 
11
+ // Custom error types for better error handling
12
+ export type RequestErrorType =
13
+ | "timeout"
14
+ | "network"
15
+ | "cors"
16
+ | "abort"
17
+ | "unknown";
18
+
19
+ export class RequestError extends Error {
20
+ type: RequestErrorType;
21
+ originalError?: Error;
22
+
23
+ constructor(type: RequestErrorType, message: string, originalError?: Error) {
24
+ super(message);
25
+ this.name = "RequestError";
26
+ this.type = type;
27
+ this.originalError = originalError;
28
+ }
29
+ }
30
+
31
+ const DEFAULT_REQUEST_TIMEOUT = 30000; // 30 seconds
32
+
11
33
  function fetchWithtimeout(
12
34
  url: string,
13
35
  options: RequestInit,
14
- timeout = 5000
15
- ): any {
16
- return Promise.race([
17
- fetch(url, options),
18
- new Promise((_, reject) =>
19
- setTimeout(() => reject(new Error("Request timed out")), timeout)
20
- ),
21
- ]);
36
+ timeout = DEFAULT_REQUEST_TIMEOUT
37
+ ): Promise<Response> {
38
+ const controller = new AbortController();
39
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
40
+
41
+ return fetch(url, {
42
+ ...options,
43
+ signal: controller.signal,
44
+ })
45
+ .then((response) => {
46
+ clearTimeout(timeoutId);
47
+ return response;
48
+ })
49
+ .catch((error) => {
50
+ clearTimeout(timeoutId);
51
+
52
+ // Check if it was an abort due to timeout
53
+ if (error.name === "AbortError") {
54
+ throw new RequestError(
55
+ "timeout",
56
+ "The request timed out waiting for the server to respond. Please try again. If the issue persists, try using a different client (e.g., curl) with a longer timeout.",
57
+ error
58
+ );
59
+ }
60
+
61
+ // Check for network errors (offline, DNS failure, etc.)
62
+ if (error instanceof TypeError && error.message === "Failed to fetch") {
63
+ // This could be CORS, network failure, or the server being unreachable
64
+ throw new RequestError(
65
+ "network",
66
+ "Unable to reach the server. Please check your network connection and verify the server URL is correct. If the server is running, this may be a CORS issue.",
67
+ error
68
+ );
69
+ }
70
+
71
+ // Handle other TypeErrors that might indicate CORS issues
72
+ if (error instanceof TypeError) {
73
+ throw new RequestError(
74
+ "cors",
75
+ "The request was blocked, possibly due to CORS restrictions. Ensure the server allows requests from this origin, or try using a proxy.",
76
+ error
77
+ );
78
+ }
79
+
80
+ // Generic error fallback
81
+ throw new RequestError(
82
+ "unknown",
83
+ error.message ||
84
+ "An unexpected error occurred while making the request.",
85
+ error
86
+ );
87
+ });
22
88
  }
23
89
 
24
90
  async function loadImage(content: Blob): Promise<string | ArrayBuffer | null> {
@@ -47,7 +113,8 @@ async function loadImage(content: Blob): Promise<string | ArrayBuffer | null> {
47
113
  async function makeRequest(
48
114
  request: sdk.Request,
49
115
  proxy: string | undefined,
50
- _body: Body
116
+ _body: Body,
117
+ timeout: number = DEFAULT_REQUEST_TIMEOUT
51
118
  ) {
52
119
  const headers = request.toJSON().header;
53
120
 
@@ -194,7 +261,8 @@ async function makeRequest(
194
261
  finalUrl = normalizedProxy + request.url.toString();
195
262
  }
196
263
 
197
- return fetchWithtimeout(finalUrl, requestOptions).then((response: any) => {
264
+ try {
265
+ const response = await fetchWithtimeout(finalUrl, requestOptions, timeout);
198
266
  const contentType = response.headers.get("content-type");
199
267
  let fileExtension = "";
200
268
 
@@ -224,32 +292,45 @@ async function makeRequest(
224
292
  }
225
293
 
226
294
  if (fileExtension) {
227
- return response.blob().then((blob: any) => {
228
- const url = window.URL.createObjectURL(blob);
295
+ const blob = await response.blob();
296
+ const url = window.URL.createObjectURL(blob);
229
297
 
230
- const link = document.createElement("a");
231
- link.href = url;
232
- // Now the file name includes the extension
233
- link.setAttribute("download", `file${fileExtension}`);
298
+ const link = document.createElement("a");
299
+ link.href = url;
300
+ // Now the file name includes the extension
301
+ link.setAttribute("download", `file${fileExtension}`);
234
302
 
235
- // These two lines are necessary to make the link click in Firefox
236
- link.style.display = "none";
237
- document.body.appendChild(link);
303
+ // These two lines are necessary to make the link click in Firefox
304
+ link.style.display = "none";
305
+ document.body.appendChild(link);
238
306
 
239
- link.click();
307
+ link.click();
240
308
 
241
- // After link is clicked, it's safe to remove it.
242
- setTimeout(() => document.body.removeChild(link), 0);
309
+ // After link is clicked, it's safe to remove it.
310
+ setTimeout(() => document.body.removeChild(link), 0);
243
311
 
244
- return response;
245
- });
312
+ return response;
246
313
  } else {
247
314
  return response;
248
315
  }
249
316
  }
250
317
 
251
318
  return response;
252
- });
319
+ } catch (error) {
320
+ // Re-throw RequestError instances as-is
321
+ if (error instanceof RequestError) {
322
+ throw error;
323
+ }
324
+
325
+ // Wrap unexpected errors
326
+ throw new RequestError(
327
+ "unknown",
328
+ error instanceof Error
329
+ ? error.message
330
+ : "An unexpected error occurred while processing the response.",
331
+ error instanceof Error ? error : undefined
332
+ );
333
+ }
253
334
  }
254
335
 
255
336
  export default makeRequest;
@@ -29,6 +29,10 @@ export const OPENAPI_REQUEST = {
29
29
  PARAMETERS_TITLE: "theme.openapi.request.parameters.title",
30
30
  FETCHING_MESSAGE: "theme.openapi.request.fetchingMessage",
31
31
  CONNECTION_FAILED: "theme.openapi.request.connectionFailed",
32
+ ERROR_TIMEOUT: "theme.openapi.request.error.timeout",
33
+ ERROR_NETWORK: "theme.openapi.request.error.network",
34
+ ERROR_CORS: "theme.openapi.request.error.cors",
35
+ ERROR_UNKNOWN: "theme.openapi.request.error.unknown",
32
36
  };
33
37
 
34
38
  export const OPENAPI_SERVER = {
package/src/types.ts CHANGED
@@ -12,6 +12,8 @@ export interface ThemeConfig {
12
12
  api?: {
13
13
  proxy?: string;
14
14
  authPersistance?: false | "localStorage" | "sessionStorage";
15
+ /** Request timeout in milliseconds. Defaults to 30000 (30 seconds). */
16
+ requestTimeout?: number;
15
17
  };
16
18
  }
17
19