@humanwhocodes/safe-fetch
Advanced tools
+39
-3
@@ -19,7 +19,43 @@ /** | ||
| return fetch(url, init).catch(error => { | ||
| // Serialize error to JSON | ||
| /** @type {Record<string, any>} */ | ||
| let errorObject; | ||
| const errorMessage = typeof error === "string" ? error : error.message || "Unknown error"; | ||
| if (typeof error === "string") { | ||
| errorObject = { message: error }; | ||
| } | ||
| else { | ||
| // Extract all properties from the error object | ||
| errorObject = {}; | ||
| const propertyNames = Object.getOwnPropertyNames(error); | ||
| for (const name of propertyNames) { | ||
| try { | ||
| const value = error[name]; | ||
| // Skip functions and symbols as they can't be serialized | ||
| if (typeof value !== "function" && | ||
| typeof value !== "symbol") { | ||
| errorObject[name] = value; | ||
| } | ||
| } | ||
| catch { | ||
| // Skip properties that throw on access | ||
| } | ||
| } | ||
| } | ||
| // Safely stringify with circular reference handling | ||
| let body; | ||
| try { | ||
| body = JSON.stringify(errorObject); | ||
| } | ||
| catch { | ||
| // Fallback if serialization fails (e.g., circular references) | ||
| body = JSON.stringify({ message: errorMessage }); | ||
| } | ||
| // Create a custom Response-like object since ERROR_STATUS is out of valid range | ||
| const statusText = typeof error === "string" ? error : error.message; | ||
| const response = new Response(null, { | ||
| const response = new Response(body, { | ||
| status: 599, | ||
| statusText, | ||
| statusText: errorMessage, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| }); | ||
@@ -26,0 +62,0 @@ // Override the status property with a custom value |
+1
-1
| { | ||
| "name": "@humanwhocodes/safe-fetch", | ||
| "version": "1.0.1", | ||
| "version": "1.1.0", | ||
| "description": "A fetch wrapper that returns Response objects for errors instead of rejecting", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+82
-7
@@ -36,9 +36,17 @@ # Safe Fetch | ||
| // the ERROR_STATUS indicates it's a caught error | ||
| if (response.status === ERROR_STATUS) { | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| console.log(data); | ||
| } else if (response.status === ERROR_STATUS) { | ||
| // the ERROR_STATUS indicates it's a caught error | ||
| console.error("Error:", response.statusText); | ||
| // "This operation was aborted" | ||
| // You can also access the error details from the response body | ||
| const errorDetails = await response.json(); | ||
| console.error("Error details:", errorDetails); | ||
| // { message: "This operation was aborted", stack: "..." } | ||
| } else { | ||
| const data = await response.json(); | ||
| console.log(data); | ||
| // Handle HTTP errors (non-2xx status codes) | ||
| console.error(`HTTP Error: ${response.status} ${response.statusText}`); | ||
| } | ||
@@ -63,6 +71,13 @@ ``` | ||
| if (response.status === ERROR_STATUS) { | ||
| if (response.ok) { | ||
| console.log("Success!"); | ||
| } else if (response.status === ERROR_STATUS) { | ||
| console.error("Error:", response.statusText); | ||
| // Access detailed error information from the response body | ||
| const errorDetails = await response.json(); | ||
| console.error("Error details:", errorDetails); | ||
| } else { | ||
| console.log("Success!"); | ||
| // Handle HTTP errors (non-2xx status codes) | ||
| console.error(`HTTP Error: ${response.status} ${response.statusText}`); | ||
| } | ||
@@ -80,4 +95,15 @@ ``` | ||
| if (response.status === ERROR_STATUS) { | ||
| if (response.ok) { | ||
| // Handle successful response | ||
| const data = await response.json(); | ||
| console.log(data); | ||
| } else if (response.status === ERROR_STATUS) { | ||
| console.error("Error:", response.statusText); | ||
| // Get detailed error information from the response body | ||
| const errorDetails = await response.json(); | ||
| console.error("Error details:", errorDetails); | ||
| } else { | ||
| // Handle HTTP errors (non-2xx status codes) | ||
| console.error(`HTTP Error: ${response.status} ${response.statusText}`); | ||
| } | ||
@@ -96,3 +122,52 @@ ``` | ||
| - `statusText`: The error message | ||
| - `body`: JSON-serialized error details | ||
| ### Error Body Serialization | ||
| The error details are serialized as JSON in the response body, making it easy to access structured error information: | ||
| - **String errors**: Serialized as `{ message: "error string" }` | ||
| - **Error objects**: All properties (including `message`, `stack`, and custom properties) are extracted and serialized | ||
| **Example with Error object:** | ||
| ```javascript | ||
| import { safeFetch, ERROR_STATUS } from "@humanwhocodes/safe-fetch"; | ||
| const response = await safeFetch("https://invalid-domain.example"); | ||
| if (response.status === ERROR_STATUS) { | ||
| const error = await response.json(); | ||
| console.log(error.message); // "Failed to fetch" | ||
| console.log(error.stack); // Stack trace | ||
| } | ||
| ``` | ||
| **Example with custom error properties:** | ||
| ```javascript | ||
| const mockFetch = async () => { | ||
| const error = new Error("Database connection failed"); | ||
| error.code = "DB_CONN_ERROR"; | ||
| error.retryAfter = 5000; | ||
| throw error; | ||
| }; | ||
| const safeMockFetch = createSafeFetch(mockFetch); | ||
| const response = await safeMockFetch("https://api.example.com/data"); | ||
| if (response.status === ERROR_STATUS) { | ||
| const error = await response.json(); | ||
| console.log(error.message); // "Database connection failed" | ||
| console.log(error.code); // "DB_CONN_ERROR" | ||
| console.log(error.retryAfter); // 5000 | ||
| } | ||
| ``` | ||
| **Safety features:** | ||
| - Circular references are handled gracefully with a fallback to a simple message format | ||
| - Non-serializable properties (functions, symbols) are automatically filtered out | ||
| - Property access errors are caught and handled | ||
| ## License | ||
@@ -99,0 +174,0 @@ |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
23009
21.66%96
60%184
68.81%