@marianmeres/http-utils 2.0.1 → 2.1.0
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/AGENTS.md +197 -0
- package/API.md +459 -0
- package/README.md +42 -159
- package/dist/api.d.ts +57 -17
- package/dist/api.js +60 -29
- package/dist/error.d.ts +58 -3
- package/dist/error.js +76 -22
- package/dist/mod.d.ts +24 -1
- package/dist/mod.js +23 -0
- package/dist/status.d.ts +29 -1
- package/dist/status.js +31 -2
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @marianmeres/http-utils
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@marianmeres/http-utils)
|
|
4
|
+
[](https://jsr.io/@marianmeres/http-utils)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
3
7
|
Opinionated, lightweight HTTP client wrapper for `fetch` with type-safe errors and convenient defaults.
|
|
4
8
|
|
|
5
9
|
## Features
|
|
@@ -64,186 +68,69 @@ try {
|
|
|
64
68
|
}
|
|
65
69
|
```
|
|
66
70
|
|
|
67
|
-
## API
|
|
71
|
+
## API Overview
|
|
68
72
|
|
|
69
73
|
### `createHttpApi(base?, defaults?, errorExtractor?)`
|
|
70
74
|
|
|
71
75
|
Creates an HTTP API client.
|
|
72
76
|
|
|
73
|
-
**Parameters:**
|
|
74
|
-
- `base` - Optional base URL for all requests
|
|
75
|
-
- `defaults` - Optional default params (headers, credentials, etc.) or async function returning defaults
|
|
76
|
-
- `errorExtractor` - Optional global error message extractor function
|
|
77
|
-
|
|
78
|
-
**Returns:** Object with methods: `get`, `post`, `put`, `patch`, `del`, `url`, `base`
|
|
79
|
-
|
|
80
|
-
### HTTP Methods
|
|
81
|
-
|
|
82
|
-
All methods return the parsed response body (JSON if possible) or throw `HttpError` on failure.
|
|
83
|
-
|
|
84
|
-
**New Options API (recommended):**
|
|
85
77
|
```ts
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
params?: { headers?, signal?, credentials?, raw?, assert?, token? },
|
|
89
|
-
respHeaders?: {},
|
|
90
|
-
errorExtractor?: (body, response) => string
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// POST/PUT/PATCH/DELETE with options
|
|
94
|
-
await api.post(path, {
|
|
95
|
-
data?: any, // Request body
|
|
96
|
-
params?: { headers?, signal?, credentials?, raw?, assert?, token? },
|
|
97
|
-
respHeaders?: {},
|
|
98
|
-
errorExtractor?: (body, response) => string
|
|
78
|
+
const api = createHttpApi("https://api.example.com", {
|
|
79
|
+
headers: { "Authorization": "Bearer token" }
|
|
99
80
|
});
|
|
100
81
|
```
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
// GET
|
|
105
|
-
await api.get(path, params?, respHeaders?, errorExtractor?)
|
|
106
|
-
|
|
107
|
-
// POST, PUT, PATCH, DELETE
|
|
108
|
-
await api.post(path, data?, params?, respHeaders?, errorExtractor?)
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
**Common params:**
|
|
112
|
-
- `headers` - Custom headers object
|
|
113
|
-
- `token` - Bearer token (auto-adds `Authorization: Bearer {token}`)
|
|
114
|
-
- `signal` - AbortSignal for cancellation
|
|
115
|
-
- `credentials` - `'omit' | 'same-origin' | 'include'`
|
|
116
|
-
- `raw` - Return raw Response object instead of parsed body
|
|
117
|
-
- `assert` - Set to `false` to disable throwing on errors
|
|
118
|
-
|
|
119
|
-
### Response Headers
|
|
120
|
-
|
|
121
|
-
Access response headers by passing a respHeaders object:
|
|
122
|
-
|
|
123
|
-
```ts
|
|
124
|
-
// New API
|
|
125
|
-
const headers = {};
|
|
126
|
-
const data = await api.get("/users", { respHeaders: headers });
|
|
127
|
-
|
|
128
|
-
console.log(headers.__http_status_code__); // 200
|
|
129
|
-
console.log(headers["content-type"]); // "application/json"
|
|
130
|
-
|
|
131
|
-
// Legacy API
|
|
132
|
-
const legacyHeaders = {};
|
|
133
|
-
const data2 = await api.get("/users", {}, legacyHeaders);
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Error Classes
|
|
137
|
-
|
|
138
|
-
Well-known HTTP errors have specific classes:
|
|
139
|
-
|
|
140
|
-
**Client Errors (4xx):**
|
|
141
|
-
- `BadRequest` (400)
|
|
142
|
-
- `Unauthorized` (401)
|
|
143
|
-
- `Forbidden` (403)
|
|
144
|
-
- `NotFound` (404)
|
|
145
|
-
- `MethodNotAllowed` (405)
|
|
146
|
-
- `RequestTimeout` (408)
|
|
147
|
-
- `Conflict` (409)
|
|
148
|
-
- `Gone` (410)
|
|
149
|
-
- `LengthRequired` (411)
|
|
150
|
-
- `ImATeapot` (418)
|
|
151
|
-
- `UnprocessableContent` (422)
|
|
152
|
-
- `TooManyRequests` (429)
|
|
153
|
-
|
|
154
|
-
**Server Errors (5xx):**
|
|
155
|
-
- `InternalServerError` (500)
|
|
156
|
-
- `NotImplemented` (501)
|
|
157
|
-
- `BadGateway` (502)
|
|
158
|
-
- `ServiceUnavailable` (503)
|
|
159
|
-
|
|
160
|
-
All errors extend `HttpError` with properties:
|
|
161
|
-
- `status` - HTTP status code
|
|
162
|
-
- `statusText` - HTTP status text
|
|
163
|
-
- `body` - Response body (auto-parsed as JSON if possible)
|
|
164
|
-
- `cause` - Error details/context
|
|
165
|
-
|
|
166
|
-
### HTTP Status Codes
|
|
167
|
-
|
|
168
|
-
Access status codes via `HTTP_STATUS`:
|
|
169
|
-
|
|
170
|
-
```ts
|
|
171
|
-
import { HTTP_STATUS } from "@marianmeres/http-utils";
|
|
172
|
-
|
|
173
|
-
// By category
|
|
174
|
-
HTTP_STATUS.SUCCESS.OK.CODE // 200
|
|
175
|
-
HTTP_STATUS.ERROR_CLIENT.NOT_FOUND.CODE // 404
|
|
176
|
-
|
|
177
|
-
// Direct shortcuts
|
|
178
|
-
HTTP_STATUS.OK // 200
|
|
179
|
-
HTTP_STATUS.NOT_FOUND // 404
|
|
180
|
-
HTTP_STATUS.INTERNAL_SERVER_ERROR // 500
|
|
181
|
-
|
|
182
|
-
// Lookup by code
|
|
183
|
-
const info = HTTP_STATUS.findByCode(404);
|
|
184
|
-
// { CODE: 404, TEXT: "Not Found", _TYPE: "ERROR_CLIENT", _KEY: "NOT_FOUND" }
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Advanced Usage
|
|
188
|
-
|
|
189
|
-
### Error Message Extraction
|
|
190
|
-
|
|
191
|
-
Customize how error messages are extracted from failed responses:
|
|
192
|
-
|
|
193
|
-
```ts
|
|
194
|
-
// Global default
|
|
195
|
-
createHttpApi.defaultErrorMessageExtractor = (body, response) => {
|
|
196
|
-
return body.error?.message || response.statusText;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// Per-instance
|
|
200
|
-
const api = createHttpApi(null, null, (body) => body.customError);
|
|
201
|
-
|
|
202
|
-
// Per-request
|
|
203
|
-
await api.get("/path", null, null, (body) => body.message);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Priority: per-request → per-instance → global → built-in fallback
|
|
207
|
-
|
|
208
|
-
### Dynamic Configuration
|
|
83
|
+
### HTTP Methods
|
|
209
84
|
|
|
210
85
|
```ts
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
86
|
+
// GET (new options API)
|
|
87
|
+
const data = await api.get("/users", {
|
|
88
|
+
params: { headers: { "X-Custom": "value" } },
|
|
89
|
+
respHeaders: {}
|
|
214
90
|
});
|
|
215
|
-
```
|
|
216
91
|
|
|
217
|
-
|
|
92
|
+
// POST/PUT/PATCH/DELETE (new options API)
|
|
93
|
+
await api.post("/users", {
|
|
94
|
+
data: { name: "John" },
|
|
95
|
+
params: { token: "bearer-token" }
|
|
96
|
+
});
|
|
218
97
|
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
const data = await response.json();
|
|
98
|
+
// Legacy API still supported
|
|
99
|
+
const data = await api.get("/users", { headers: { "X-Custom": "value" } });
|
|
100
|
+
await api.post("/users", { name: "John" });
|
|
223
101
|
```
|
|
224
102
|
|
|
225
|
-
###
|
|
103
|
+
### Error Handling
|
|
226
104
|
|
|
227
105
|
```ts
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
106
|
+
import { HTTP_ERROR, NotFound } from "@marianmeres/http-utils";
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await api.get("/resource");
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (error instanceof NotFound) {
|
|
112
|
+
console.log("Not found:", error.body);
|
|
113
|
+
}
|
|
114
|
+
// All errors have: status, statusText, body, cause
|
|
231
115
|
}
|
|
232
116
|
```
|
|
233
117
|
|
|
234
|
-
###
|
|
118
|
+
### Key Features
|
|
235
119
|
|
|
236
|
-
|
|
237
|
-
|
|
120
|
+
- **Auto JSON**: Response bodies are automatically parsed as JSON
|
|
121
|
+
- **Bearer tokens**: Use `token` param to auto-add `Authorization: Bearer` header
|
|
122
|
+
- **Response headers**: Pass `respHeaders: {}` to capture response headers
|
|
123
|
+
- **Raw response**: Use `raw: true` to get the raw Response object
|
|
124
|
+
- **Non-throwing**: Use `assert: false` to prevent throwing on errors
|
|
125
|
+
- **AbortController**: Pass `signal` for request cancellation
|
|
238
126
|
|
|
239
|
-
|
|
127
|
+
## Full API Reference
|
|
240
128
|
|
|
241
|
-
|
|
242
|
-
```
|
|
129
|
+
For complete API documentation including all error classes, HTTP status codes, types, and utilities, see **[API.md](API.md)**.
|
|
243
130
|
|
|
244
131
|
## Utilities
|
|
245
132
|
|
|
246
|
-
### `getErrorMessage(error
|
|
133
|
+
### `getErrorMessage(error)`
|
|
247
134
|
|
|
248
135
|
Extracts human-readable messages from any error format:
|
|
249
136
|
|
|
@@ -265,9 +152,5 @@ Manually create HTTP errors:
|
|
|
265
152
|
import { createHttpError } from "@marianmeres/http-utils";
|
|
266
153
|
|
|
267
154
|
const error = createHttpError(404, "User not found", { userId: 123 });
|
|
268
|
-
throw error;
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
## License
|
|
272
|
-
|
|
273
|
-
MIT
|
|
155
|
+
throw error; // instanceof NotFound
|
|
156
|
+
```
|
package/dist/api.d.ts
CHANGED
|
@@ -1,34 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api
|
|
3
|
+
*
|
|
4
|
+
* HTTP API client factory and related types.
|
|
5
|
+
* Provides a convenient wrapper over the native `fetch` API with sensible defaults.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Request body data type.
|
|
9
|
+
* Supports JSON-serializable objects, FormData for file uploads, or raw strings.
|
|
10
|
+
*/
|
|
11
|
+
export type RequestData = Record<string, unknown> | FormData | string | null;
|
|
1
12
|
interface BaseParams {
|
|
2
13
|
method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT';
|
|
3
14
|
path: string;
|
|
4
15
|
}
|
|
5
|
-
|
|
6
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Parameters for fetch requests.
|
|
18
|
+
*/
|
|
19
|
+
export interface FetchParams {
|
|
20
|
+
/** Request body data (automatically JSON stringified unless FormData). */
|
|
21
|
+
data?: RequestData;
|
|
22
|
+
/** Bearer token (auto-adds `Authorization: Bearer {token}` header). */
|
|
7
23
|
token?: string | null;
|
|
24
|
+
/** Custom request headers. */
|
|
8
25
|
headers?: Record<string, string> | null;
|
|
26
|
+
/** AbortSignal for request cancellation. */
|
|
9
27
|
signal?: AbortSignal;
|
|
28
|
+
/** Credentials mode for the request. */
|
|
10
29
|
credentials?: 'omit' | 'same-origin' | 'include' | null;
|
|
30
|
+
/** If true, returns the raw Response object instead of parsed body. */
|
|
11
31
|
raw?: boolean | null;
|
|
32
|
+
/** If false, does not throw on HTTP errors (default: true). */
|
|
12
33
|
assert?: boolean | null;
|
|
13
34
|
}
|
|
14
35
|
type BaseFetchParams = BaseParams & FetchParams;
|
|
15
|
-
type ErrorMessageExtractor = (body: any, response: Response) => string;
|
|
16
|
-
type ResponseHeaders = Record<string, string | number>;
|
|
17
36
|
/**
|
|
18
|
-
*
|
|
37
|
+
* Function to extract error messages from failed HTTP responses.
|
|
38
|
+
* @param body - The parsed response body.
|
|
39
|
+
* @param response - The raw Response object.
|
|
40
|
+
* @returns A human-readable error message string.
|
|
41
|
+
*/
|
|
42
|
+
export type ErrorMessageExtractor = (body: unknown, response: Response) => string;
|
|
43
|
+
/**
|
|
44
|
+
* Object to receive response headers after a request completes.
|
|
45
|
+
* Will be mutated to include all response headers plus special keys:
|
|
46
|
+
* - `__http_status_code__`: The HTTP status code
|
|
47
|
+
* - `__http_status_text__`: The HTTP status text
|
|
48
|
+
*/
|
|
49
|
+
export type ResponseHeaders = Record<string, string | number>;
|
|
50
|
+
/**
|
|
51
|
+
* Options for HTTP GET requests using the new cleaner API.
|
|
19
52
|
*/
|
|
20
53
|
export interface GetOptions {
|
|
54
|
+
/** Fetch parameters (headers, token, signal, credentials, raw, assert). */
|
|
21
55
|
params?: FetchParams;
|
|
56
|
+
/** Object to receive response headers (will be mutated). */
|
|
22
57
|
respHeaders?: ResponseHeaders | null;
|
|
58
|
+
/** Custom error message extractor for this request. */
|
|
23
59
|
errorExtractor?: ErrorMessageExtractor | null;
|
|
24
60
|
}
|
|
25
61
|
/**
|
|
26
|
-
* Options for HTTP POST/PUT/PATCH/DELETE requests
|
|
62
|
+
* Options for HTTP POST/PUT/PATCH/DELETE requests using the new cleaner API.
|
|
27
63
|
*/
|
|
28
64
|
export interface DataOptions {
|
|
29
|
-
data
|
|
65
|
+
/** Request body data. */
|
|
66
|
+
data?: RequestData;
|
|
67
|
+
/** Fetch parameters (headers, token, signal, credentials, raw, assert). */
|
|
30
68
|
params?: FetchParams;
|
|
69
|
+
/** Object to receive response headers (will be mutated). */
|
|
31
70
|
respHeaders?: ResponseHeaders | null;
|
|
71
|
+
/** Custom error message extractor for this request. */
|
|
32
72
|
errorExtractor?: ErrorMessageExtractor | null;
|
|
33
73
|
}
|
|
34
74
|
/**
|
|
@@ -53,7 +93,7 @@ export declare class HttpApi {
|
|
|
53
93
|
* });
|
|
54
94
|
* ```
|
|
55
95
|
*/
|
|
56
|
-
get(path: string, options: GetOptions): Promise<
|
|
96
|
+
get(path: string, options: GetOptions): Promise<unknown>;
|
|
57
97
|
/**
|
|
58
98
|
* Performs a GET request (legacy API).
|
|
59
99
|
*
|
|
@@ -65,7 +105,7 @@ export declare class HttpApi {
|
|
|
65
105
|
* @returns The response body (auto-parsed as JSON if possible), or Response if `raw: true`.
|
|
66
106
|
* @throws {HttpError} When the response is not OK and `assert` is true (default).
|
|
67
107
|
*/
|
|
68
|
-
get(path: string, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<
|
|
108
|
+
get(path: string, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<unknown>;
|
|
69
109
|
/**
|
|
70
110
|
* Performs a POST request (new options API - recommended).
|
|
71
111
|
*
|
|
@@ -83,7 +123,7 @@ export declare class HttpApi {
|
|
|
83
123
|
* });
|
|
84
124
|
* ```
|
|
85
125
|
*/
|
|
86
|
-
post(path: string, options: DataOptions): Promise<
|
|
126
|
+
post(path: string, options: DataOptions): Promise<unknown>;
|
|
87
127
|
/**
|
|
88
128
|
* Performs a POST request (legacy API).
|
|
89
129
|
*
|
|
@@ -96,23 +136,23 @@ export declare class HttpApi {
|
|
|
96
136
|
* @returns The response body (auto-parsed as JSON if possible), or Response if `raw: true`.
|
|
97
137
|
* @throws {HttpError} When the response is not OK and `assert` is true (default).
|
|
98
138
|
*/
|
|
99
|
-
post(path: string, data?:
|
|
139
|
+
post(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<unknown>;
|
|
100
140
|
/** Performs a PUT request (new options API). @see post */
|
|
101
|
-
put(path: string, options: DataOptions): Promise<
|
|
141
|
+
put(path: string, options: DataOptions): Promise<unknown>;
|
|
102
142
|
/** Performs a PUT request (legacy API). @see post */
|
|
103
|
-
put(path: string, data?:
|
|
143
|
+
put(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<unknown>;
|
|
104
144
|
/** Performs a PATCH request (new options API). @see post */
|
|
105
|
-
patch(path: string, options: DataOptions): Promise<
|
|
145
|
+
patch(path: string, options: DataOptions): Promise<unknown>;
|
|
106
146
|
/** Performs a PATCH request (legacy API). @see post */
|
|
107
|
-
patch(path: string, data?:
|
|
147
|
+
patch(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<unknown>;
|
|
108
148
|
/**
|
|
109
149
|
* Performs a DELETE request (new options API).
|
|
110
150
|
* Note: Request body in DELETE is allowed per HTTP spec.
|
|
111
151
|
* @see post
|
|
112
152
|
*/
|
|
113
|
-
del(path: string, options: DataOptions): Promise<
|
|
153
|
+
del(path: string, options: DataOptions): Promise<unknown>;
|
|
114
154
|
/** Performs a DELETE request (legacy API). @see post */
|
|
115
|
-
del(path: string, data?:
|
|
155
|
+
del(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<unknown>;
|
|
116
156
|
/**
|
|
117
157
|
* Helper method to build the full URL from a path.
|
|
118
158
|
*
|
package/dist/api.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api
|
|
3
|
+
*
|
|
4
|
+
* HTTP API client factory and related types.
|
|
5
|
+
* Provides a convenient wrapper over the native `fetch` API with sensible defaults.
|
|
6
|
+
*/
|
|
1
7
|
import { createHttpError } from './error.js';
|
|
2
|
-
// This is an opinionated HTTP client wrapper and may not be suitable for every use case.
|
|
3
|
-
// It provides convenient defaults over plain fetch calls without adding unnecessary abstractions.
|
|
4
8
|
/**
|
|
5
9
|
* Deep merges two objects. Later properties overwrite earlier properties.
|
|
6
10
|
*/
|
|
@@ -8,23 +12,25 @@ function deepMerge(target, source) {
|
|
|
8
12
|
const output = { ...target };
|
|
9
13
|
if (isObject(target) && isObject(source)) {
|
|
10
14
|
Object.keys(source).forEach(key => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const sourceVal = source[key];
|
|
16
|
+
const targetVal = target[key];
|
|
17
|
+
if (isObject(sourceVal)) {
|
|
18
|
+
if (!(key in target) || !isObject(targetVal)) {
|
|
19
|
+
Object.assign(output, { [key]: sourceVal });
|
|
14
20
|
}
|
|
15
21
|
else {
|
|
16
|
-
output[key] = deepMerge(
|
|
22
|
+
output[key] = deepMerge(targetVal, sourceVal);
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
else {
|
|
20
|
-
Object.assign(output, { [key]:
|
|
26
|
+
Object.assign(output, { [key]: sourceVal });
|
|
21
27
|
}
|
|
22
28
|
});
|
|
23
29
|
}
|
|
24
30
|
return output;
|
|
25
31
|
}
|
|
26
32
|
function isObject(item) {
|
|
27
|
-
return item && typeof item === 'object' && !Array.isArray(item);
|
|
33
|
+
return item !== null && typeof item === 'object' && !Array.isArray(item);
|
|
28
34
|
}
|
|
29
35
|
const _fetchRaw = async ({ method, path, data = null, token = null, headers = null, signal, credentials, }) => {
|
|
30
36
|
const normalizedHeaders = Object.entries(headers || {}).reduce((m, [k, v]) => ({ ...m, [k.toLowerCase()]: v }), {});
|
|
@@ -84,13 +90,14 @@ const _fetch = async (params, respHeaders = null, errorMessageExtractor = null,
|
|
|
84
90
|
createHttpApi.defaultErrorMessageExtractor ?? // static default
|
|
85
91
|
// educated guess fallback
|
|
86
92
|
function (_body, _response) {
|
|
87
|
-
|
|
93
|
+
const b = _body;
|
|
94
|
+
let msg = String(
|
|
88
95
|
// try opinionated convention first
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
b?.error?.message ||
|
|
97
|
+
b?.message ||
|
|
98
|
+
b?.error ||
|
|
92
99
|
_response?.statusText ||
|
|
93
|
-
'Unknown error';
|
|
100
|
+
'Unknown error');
|
|
94
101
|
if (msg.length > 255)
|
|
95
102
|
msg = `[Shortened]: ${msg.slice(0, 255)}`;
|
|
96
103
|
return msg;
|
|
@@ -161,10 +168,13 @@ export class HttpApi {
|
|
|
161
168
|
let fetchParams;
|
|
162
169
|
let headers = null;
|
|
163
170
|
let extractor = null;
|
|
164
|
-
if (dataOrOptions &&
|
|
165
|
-
'
|
|
166
|
-
|
|
167
|
-
'
|
|
171
|
+
if (dataOrOptions &&
|
|
172
|
+
typeof dataOrOptions === 'object' &&
|
|
173
|
+
!(dataOrOptions instanceof FormData) &&
|
|
174
|
+
('data' in dataOrOptions ||
|
|
175
|
+
'params' in dataOrOptions ||
|
|
176
|
+
'respHeaders' in dataOrOptions ||
|
|
177
|
+
'errorExtractor' in dataOrOptions)) {
|
|
168
178
|
// New options API
|
|
169
179
|
const opts = dataOrOptions;
|
|
170
180
|
data = opts.data ?? null;
|
|
@@ -187,10 +197,13 @@ export class HttpApi {
|
|
|
187
197
|
let fetchParams;
|
|
188
198
|
let headers = null;
|
|
189
199
|
let extractor = null;
|
|
190
|
-
if (dataOrOptions &&
|
|
191
|
-
'
|
|
192
|
-
|
|
193
|
-
'
|
|
200
|
+
if (dataOrOptions &&
|
|
201
|
+
typeof dataOrOptions === 'object' &&
|
|
202
|
+
!(dataOrOptions instanceof FormData) &&
|
|
203
|
+
('data' in dataOrOptions ||
|
|
204
|
+
'params' in dataOrOptions ||
|
|
205
|
+
'respHeaders' in dataOrOptions ||
|
|
206
|
+
'errorExtractor' in dataOrOptions)) {
|
|
194
207
|
const opts = dataOrOptions;
|
|
195
208
|
data = opts.data ?? null;
|
|
196
209
|
fetchParams = opts.params;
|
|
@@ -211,10 +224,13 @@ export class HttpApi {
|
|
|
211
224
|
let fetchParams;
|
|
212
225
|
let headers = null;
|
|
213
226
|
let extractor = null;
|
|
214
|
-
if (dataOrOptions &&
|
|
215
|
-
'
|
|
216
|
-
|
|
217
|
-
'
|
|
227
|
+
if (dataOrOptions &&
|
|
228
|
+
typeof dataOrOptions === 'object' &&
|
|
229
|
+
!(dataOrOptions instanceof FormData) &&
|
|
230
|
+
('data' in dataOrOptions ||
|
|
231
|
+
'params' in dataOrOptions ||
|
|
232
|
+
'respHeaders' in dataOrOptions ||
|
|
233
|
+
'errorExtractor' in dataOrOptions)) {
|
|
218
234
|
const opts = dataOrOptions;
|
|
219
235
|
data = opts.data ?? null;
|
|
220
236
|
fetchParams = opts.params;
|
|
@@ -235,10 +251,13 @@ export class HttpApi {
|
|
|
235
251
|
let fetchParams;
|
|
236
252
|
let headers = null;
|
|
237
253
|
let extractor = null;
|
|
238
|
-
if (dataOrOptions &&
|
|
239
|
-
'
|
|
240
|
-
|
|
241
|
-
'
|
|
254
|
+
if (dataOrOptions &&
|
|
255
|
+
typeof dataOrOptions === 'object' &&
|
|
256
|
+
!(dataOrOptions instanceof FormData) &&
|
|
257
|
+
('data' in dataOrOptions ||
|
|
258
|
+
'params' in dataOrOptions ||
|
|
259
|
+
'respHeaders' in dataOrOptions ||
|
|
260
|
+
'errorExtractor' in dataOrOptions)) {
|
|
242
261
|
const opts = dataOrOptions;
|
|
243
262
|
data = opts.data ?? null;
|
|
244
263
|
fetchParams = opts.params;
|
|
@@ -295,4 +314,16 @@ export class HttpApi {
|
|
|
295
314
|
export function createHttpApi(base, defaults, factoryErrorMessageExtractor) {
|
|
296
315
|
return new HttpApi(base, defaults, factoryErrorMessageExtractor);
|
|
297
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Global default error message extractor.
|
|
319
|
+
* Applied to all requests unless overridden at instance or request level.
|
|
320
|
+
* Priority: per-request → per-instance → global → built-in fallback.
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```ts
|
|
324
|
+
* createHttpApi.defaultErrorMessageExtractor = (body, response) => {
|
|
325
|
+
* return body?.error?.message || response.statusText;
|
|
326
|
+
* };
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
298
329
|
createHttpApi.defaultErrorMessageExtractor = null;
|