@marianmeres/http-utils 2.1.0 → 2.3.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 +14 -1
- package/API.md +73 -26
- package/README.md +37 -16
- package/dist/api.d.ts +24 -10
- package/dist/api.js +72 -117
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -103,6 +103,13 @@ HTTP_ERROR.BadGateway // 502
|
|
|
103
103
|
HTTP_ERROR.ServiceUnavailable // 503
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
### Helper Functions
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Marks options object for options-based API (vs legacy positional API)
|
|
110
|
+
function opts<T extends GetOptions | DataOptions>(options: T): T
|
|
111
|
+
```
|
|
112
|
+
|
|
106
113
|
### Utility Functions
|
|
107
114
|
|
|
108
115
|
```typescript
|
|
@@ -168,12 +175,18 @@ deno task publish # Publish to JSR and NPM
|
|
|
168
175
|
### Basic Usage
|
|
169
176
|
|
|
170
177
|
```typescript
|
|
178
|
+
import { createHttpApi, opts } from "@marianmeres/http-utils";
|
|
179
|
+
|
|
171
180
|
const api = createHttpApi("https://api.example.com", {
|
|
172
181
|
headers: { "Authorization": "Bearer token" }
|
|
173
182
|
});
|
|
174
183
|
|
|
184
|
+
// Legacy API (default - object is request body)
|
|
175
185
|
const data = await api.get("/users");
|
|
176
|
-
await api.post("/users", {
|
|
186
|
+
await api.post("/users", { name: "John" });
|
|
187
|
+
|
|
188
|
+
// Options API (requires opts() wrapper)
|
|
189
|
+
await api.post("/users", opts({ data: { name: "John" }, params: { token: "abc" } }));
|
|
177
190
|
```
|
|
178
191
|
|
|
179
192
|
### Error Handling
|
package/API.md
CHANGED
|
@@ -5,6 +5,7 @@ Complete API reference for `@marianmeres/http-utils`.
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
7
|
- [createHttpApi](#createhttpapi)
|
|
8
|
+
- [opts](#opts)
|
|
8
9
|
- [HttpApi Class](#httpapi-class)
|
|
9
10
|
- [Types](#types)
|
|
10
11
|
- [HTTP Errors](#http-errors)
|
|
@@ -78,86 +79,132 @@ Priority order: per-request > per-instance > global > built-in fallback.
|
|
|
78
79
|
|
|
79
80
|
---
|
|
80
81
|
|
|
82
|
+
## opts
|
|
83
|
+
|
|
84
|
+
Marks an options object for the options-based API. Without this wrapper, arguments are treated as legacy positional parameters.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
function opts<T extends GetOptions | DataOptions>(options: T): T
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Why `opts()`?
|
|
91
|
+
|
|
92
|
+
The library supports two API styles:
|
|
93
|
+
- **Legacy API**: Positional parameters (backward compatible)
|
|
94
|
+
- **Options API**: Single options object with named properties
|
|
95
|
+
|
|
96
|
+
The `opts()` wrapper explicitly indicates which style you're using, preventing ambiguity when your request data might look like an options object.
|
|
97
|
+
|
|
98
|
+
### Example
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { createHttpApi, opts } from "@marianmeres/http-utils";
|
|
102
|
+
|
|
103
|
+
const api = createHttpApi("https://api.example.com");
|
|
104
|
+
|
|
105
|
+
// Without opts() - legacy behavior: entire object is sent as request body
|
|
106
|
+
await api.post("/users", { data: { name: "John" } });
|
|
107
|
+
// Sends: { "data": { "name": "John" } }
|
|
108
|
+
|
|
109
|
+
// With opts() - options API: data is extracted and sent as body
|
|
110
|
+
await api.post("/users", opts({ data: { name: "John" } }));
|
|
111
|
+
// Sends: { "name": "John" }
|
|
112
|
+
|
|
113
|
+
// GET with options
|
|
114
|
+
const respHeaders = {};
|
|
115
|
+
await api.get("/users", opts({
|
|
116
|
+
params: { headers: { "X-Custom": "value" } },
|
|
117
|
+
respHeaders
|
|
118
|
+
}));
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
81
123
|
## HttpApi Class
|
|
82
124
|
|
|
83
125
|
HTTP API client class. Usually created via `createHttpApi()`.
|
|
84
126
|
|
|
85
127
|
### Methods
|
|
86
128
|
|
|
87
|
-
#### `get(path, options?)`
|
|
129
|
+
#### `get<T>(path, options?)`
|
|
88
130
|
|
|
89
131
|
Performs a GET request.
|
|
90
132
|
|
|
91
|
-
**
|
|
133
|
+
**Options API (with `opts()` wrapper):**
|
|
92
134
|
```ts
|
|
93
|
-
async get(path: string, options?: GetOptions): Promise<
|
|
135
|
+
async get<T = unknown>(path: string, options?: GetOptions): Promise<T>
|
|
94
136
|
```
|
|
95
137
|
|
|
96
|
-
**Legacy API:**
|
|
138
|
+
**Legacy API (default behavior):**
|
|
97
139
|
```ts
|
|
98
|
-
async get(
|
|
140
|
+
async get<T = unknown>(
|
|
99
141
|
path: string,
|
|
100
142
|
params?: FetchParams,
|
|
101
143
|
respHeaders?: ResponseHeaders | null,
|
|
102
144
|
errorMessageExtractor?: ErrorMessageExtractor | null
|
|
103
|
-
): Promise<
|
|
145
|
+
): Promise<T>
|
|
104
146
|
```
|
|
105
147
|
|
|
106
148
|
**Example:**
|
|
107
149
|
```ts
|
|
108
|
-
//
|
|
109
|
-
|
|
150
|
+
// Options API with type parameter (requires opts() wrapper)
|
|
151
|
+
interface User { id: number; name: string; }
|
|
152
|
+
const user = await api.get<User>("/users/1", opts({
|
|
110
153
|
params: { headers: { "X-Custom": "value" } },
|
|
111
154
|
respHeaders: {}
|
|
112
|
-
});
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
// Without type parameter (returns unknown)
|
|
158
|
+
const data = await api.get("/users");
|
|
113
159
|
|
|
114
|
-
// Legacy API
|
|
160
|
+
// Legacy API (no opts() needed)
|
|
115
161
|
const data = await api.get("/users", { headers: { "X-Custom": "value" } });
|
|
116
162
|
```
|
|
117
163
|
|
|
118
|
-
#### `post(path, options?)`
|
|
164
|
+
#### `post<T>(path, options?)`
|
|
119
165
|
|
|
120
166
|
Performs a POST request.
|
|
121
167
|
|
|
122
|
-
**
|
|
168
|
+
**Options API (with `opts()` wrapper):**
|
|
123
169
|
```ts
|
|
124
|
-
async post(path: string, options?: DataOptions): Promise<
|
|
170
|
+
async post<T = unknown>(path: string, options?: DataOptions): Promise<T>
|
|
125
171
|
```
|
|
126
172
|
|
|
127
|
-
**Legacy API:**
|
|
173
|
+
**Legacy API (default behavior):**
|
|
128
174
|
```ts
|
|
129
|
-
async post(
|
|
175
|
+
async post<T = unknown>(
|
|
130
176
|
path: string,
|
|
131
177
|
data?: RequestData,
|
|
132
178
|
params?: FetchParams,
|
|
133
179
|
respHeaders?: ResponseHeaders | null,
|
|
134
180
|
errorMessageExtractor?: ErrorMessageExtractor | null
|
|
135
|
-
): Promise<
|
|
181
|
+
): Promise<T>
|
|
136
182
|
```
|
|
137
183
|
|
|
138
184
|
**Example:**
|
|
139
185
|
```ts
|
|
140
|
-
//
|
|
141
|
-
|
|
186
|
+
// Options API with type parameter (requires opts() wrapper)
|
|
187
|
+
interface User { id: number; name: string; }
|
|
188
|
+
const user = await api.post<User>("/users", opts({
|
|
142
189
|
data: { name: "John" },
|
|
143
190
|
params: { headers: { "X-Custom": "value" } }
|
|
144
|
-
});
|
|
191
|
+
}));
|
|
145
192
|
|
|
146
|
-
// Legacy API
|
|
193
|
+
// Legacy API (no opts() needed)
|
|
147
194
|
const result = await api.post("/users", { name: "John" });
|
|
148
195
|
```
|
|
149
196
|
|
|
150
|
-
#### `put(path, options?)`
|
|
197
|
+
#### `put<T>(path, options?)`
|
|
151
198
|
|
|
152
|
-
Performs a PUT request. Same signature as `post()`.
|
|
199
|
+
Performs a PUT request. Same signature as `post<T>()`.
|
|
153
200
|
|
|
154
|
-
#### `patch(path, options?)`
|
|
201
|
+
#### `patch<T>(path, options?)`
|
|
155
202
|
|
|
156
|
-
Performs a PATCH request. Same signature as `post()`.
|
|
203
|
+
Performs a PATCH request. Same signature as `post<T>()`.
|
|
157
204
|
|
|
158
|
-
#### `del(path, options?)`
|
|
205
|
+
#### `del<T>(path, options?)`
|
|
159
206
|
|
|
160
|
-
Performs a DELETE request. Same signature as `post()`.
|
|
207
|
+
Performs a DELETE request. Same signature as `post<T>()`.
|
|
161
208
|
|
|
162
209
|
#### `url(path)`
|
|
163
210
|
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Opinionated, lightweight HTTP client wrapper for `fetch` with type-safe errors a
|
|
|
13
13
|
- 🪶 **Lightweight** - Zero dependencies, thin wrapper over native `fetch`
|
|
14
14
|
- 🎨 **Flexible error handling** - Three-tier error message extraction (local → factory → global)
|
|
15
15
|
- 📦 **Deno & Node.js** - Works in both runtimes
|
|
16
|
+
- 🦾 **Generic return types** - Optional type parameters for typed responses
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -25,34 +26,39 @@ npm install @marianmeres/http-utils
|
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
```ts
|
|
28
|
-
import { createHttpApi, HTTP_ERROR } from "@marianmeres/http-utils";
|
|
29
|
+
import { createHttpApi, opts, HTTP_ERROR } from "@marianmeres/http-utils";
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
## Quick Start
|
|
32
33
|
|
|
33
34
|
```ts
|
|
34
|
-
import { createHttpApi, HTTP_ERROR, NotFound } from "@marianmeres/http-utils";
|
|
35
|
+
import { createHttpApi, opts, HTTP_ERROR, NotFound } from "@marianmeres/http-utils";
|
|
35
36
|
|
|
36
37
|
// Create an API client with base URL
|
|
37
38
|
const api = createHttpApi("https://api.example.com", {
|
|
38
39
|
headers: { "Authorization": "Bearer your-token" }
|
|
39
40
|
});
|
|
40
41
|
|
|
41
|
-
// GET request (
|
|
42
|
-
const users = await api.get("/users", {
|
|
42
|
+
// GET request (options API with opts() wrapper)
|
|
43
|
+
const users = await api.get("/users", opts({
|
|
43
44
|
params: { headers: { "X-Custom": "value" } }
|
|
44
|
-
});
|
|
45
|
+
}));
|
|
45
46
|
|
|
46
|
-
// POST request (
|
|
47
|
-
const newUser = await api.post("/users", {
|
|
47
|
+
// POST request (options API with opts() wrapper)
|
|
48
|
+
const newUser = await api.post("/users", opts({
|
|
48
49
|
data: { name: "John Doe" },
|
|
49
50
|
params: { headers: { "X-Custom": "value" } }
|
|
50
|
-
});
|
|
51
|
+
}));
|
|
51
52
|
|
|
52
|
-
// Legacy API
|
|
53
|
+
// Legacy API (default behavior without opts())
|
|
53
54
|
const legacyUsers = await api.get("/users", { headers: { "X-Custom": "value" } });
|
|
54
55
|
const legacyUser = await api.post("/users", { name: "John Doe" });
|
|
55
56
|
|
|
57
|
+
// With type parameters for typed responses
|
|
58
|
+
interface User { id: number; name: string; }
|
|
59
|
+
const user = await api.get<User>("/users/1");
|
|
60
|
+
const created = await api.post<User>("/users", opts({ data: { name: "Jane" } }));
|
|
61
|
+
|
|
56
62
|
// Error handling
|
|
57
63
|
try {
|
|
58
64
|
await api.get("/not-found");
|
|
@@ -83,23 +89,37 @@ const api = createHttpApi("https://api.example.com", {
|
|
|
83
89
|
### HTTP Methods
|
|
84
90
|
|
|
85
91
|
```ts
|
|
86
|
-
// GET (
|
|
87
|
-
const data = await api.get("/users", {
|
|
92
|
+
// GET (options API with opts() wrapper)
|
|
93
|
+
const data = await api.get("/users", opts({
|
|
88
94
|
params: { headers: { "X-Custom": "value" } },
|
|
89
95
|
respHeaders: {}
|
|
90
|
-
});
|
|
96
|
+
}));
|
|
91
97
|
|
|
92
|
-
// POST/PUT/PATCH/DELETE (
|
|
93
|
-
await api.post("/users", {
|
|
98
|
+
// POST/PUT/PATCH/DELETE (options API with opts() wrapper)
|
|
99
|
+
await api.post("/users", opts({
|
|
94
100
|
data: { name: "John" },
|
|
95
101
|
params: { token: "bearer-token" }
|
|
96
|
-
});
|
|
102
|
+
}));
|
|
97
103
|
|
|
98
|
-
// Legacy API
|
|
104
|
+
// Legacy API (default behavior without opts())
|
|
99
105
|
const data = await api.get("/users", { headers: { "X-Custom": "value" } });
|
|
100
106
|
await api.post("/users", { name: "John" });
|
|
101
107
|
```
|
|
102
108
|
|
|
109
|
+
### The `opts()` Helper
|
|
110
|
+
|
|
111
|
+
The `opts()` function explicitly marks an options object for the options-based API. Without it, arguments are treated as legacy positional parameters.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// Without opts() - legacy behavior: object is sent as request body
|
|
115
|
+
await api.post("/users", { data: { name: "John" } }); // Sends: { data: { name: "John" } }
|
|
116
|
+
|
|
117
|
+
// With opts() - options API: data is extracted and sent as body
|
|
118
|
+
await api.post("/users", opts({ data: { name: "John" } })); // Sends: { name: "John" }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This makes the API unambiguous and prevents accidental misinterpretation of request data.
|
|
122
|
+
|
|
103
123
|
### Error Handling
|
|
104
124
|
|
|
105
125
|
```ts
|
|
@@ -123,6 +143,7 @@ try {
|
|
|
123
143
|
- **Raw response**: Use `raw: true` to get the raw Response object
|
|
124
144
|
- **Non-throwing**: Use `assert: false` to prevent throwing on errors
|
|
125
145
|
- **AbortController**: Pass `signal` for request cancellation
|
|
146
|
+
- **Typed responses**: Use generics for type-safe responses: `api.get<User>("/users/1")`
|
|
126
147
|
|
|
127
148
|
## Full API Reference
|
|
128
149
|
|
package/dist/api.d.ts
CHANGED
|
@@ -71,6 +71,20 @@ export interface DataOptions {
|
|
|
71
71
|
/** Custom error message extractor for this request. */
|
|
72
72
|
errorExtractor?: ErrorMessageExtractor | null;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Marks an options object for the new options API.
|
|
76
|
+
* Use this to explicitly indicate you're using the options-based API.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* // GET with options
|
|
81
|
+
* await api.get('/users', opts({ params: { token: 'abc' } }));
|
|
82
|
+
*
|
|
83
|
+
* // POST with options
|
|
84
|
+
* await api.post('/users', opts({ data: { name: 'John' }, params: { token: 'abc' } }));
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function opts<T extends GetOptions | DataOptions>(options: T): T;
|
|
74
88
|
/**
|
|
75
89
|
* HTTP API client with convenient defaults and error handling.
|
|
76
90
|
*/
|
|
@@ -93,7 +107,7 @@ export declare class HttpApi {
|
|
|
93
107
|
* });
|
|
94
108
|
* ```
|
|
95
109
|
*/
|
|
96
|
-
get(path: string, options: GetOptions): Promise<
|
|
110
|
+
get<T = unknown>(path: string, options: GetOptions): Promise<T>;
|
|
97
111
|
/**
|
|
98
112
|
* Performs a GET request (legacy API).
|
|
99
113
|
*
|
|
@@ -105,7 +119,7 @@ export declare class HttpApi {
|
|
|
105
119
|
* @returns The response body (auto-parsed as JSON if possible), or Response if `raw: true`.
|
|
106
120
|
* @throws {HttpError} When the response is not OK and `assert` is true (default).
|
|
107
121
|
*/
|
|
108
|
-
get(path: string, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<
|
|
122
|
+
get<T = unknown>(path: string, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<T>;
|
|
109
123
|
/**
|
|
110
124
|
* Performs a POST request (new options API - recommended).
|
|
111
125
|
*
|
|
@@ -123,7 +137,7 @@ export declare class HttpApi {
|
|
|
123
137
|
* });
|
|
124
138
|
* ```
|
|
125
139
|
*/
|
|
126
|
-
post(path: string, options: DataOptions): Promise<
|
|
140
|
+
post<T = unknown>(path: string, options: DataOptions): Promise<T>;
|
|
127
141
|
/**
|
|
128
142
|
* Performs a POST request (legacy API).
|
|
129
143
|
*
|
|
@@ -136,23 +150,23 @@ export declare class HttpApi {
|
|
|
136
150
|
* @returns The response body (auto-parsed as JSON if possible), or Response if `raw: true`.
|
|
137
151
|
* @throws {HttpError} When the response is not OK and `assert` is true (default).
|
|
138
152
|
*/
|
|
139
|
-
post(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<
|
|
153
|
+
post<T = unknown>(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<T>;
|
|
140
154
|
/** Performs a PUT request (new options API). @see post */
|
|
141
|
-
put(path: string, options: DataOptions): Promise<
|
|
155
|
+
put<T = unknown>(path: string, options: DataOptions): Promise<T>;
|
|
142
156
|
/** Performs a PUT request (legacy API). @see post */
|
|
143
|
-
put(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<
|
|
157
|
+
put<T = unknown>(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<T>;
|
|
144
158
|
/** Performs a PATCH request (new options API). @see post */
|
|
145
|
-
patch(path: string, options: DataOptions): Promise<
|
|
159
|
+
patch<T = unknown>(path: string, options: DataOptions): Promise<T>;
|
|
146
160
|
/** Performs a PATCH request (legacy API). @see post */
|
|
147
|
-
patch(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<
|
|
161
|
+
patch<T = unknown>(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<T>;
|
|
148
162
|
/**
|
|
149
163
|
* Performs a DELETE request (new options API).
|
|
150
164
|
* Note: Request body in DELETE is allowed per HTTP spec.
|
|
151
165
|
* @see post
|
|
152
166
|
*/
|
|
153
|
-
del(path: string, options: DataOptions): Promise<
|
|
167
|
+
del<T = unknown>(path: string, options: DataOptions): Promise<T>;
|
|
154
168
|
/** Performs a DELETE request (legacy API). @see post */
|
|
155
|
-
del(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<
|
|
169
|
+
del<T = unknown>(path: string, data?: RequestData, params?: FetchParams, respHeaders?: ResponseHeaders | null, errorMessageExtractor?: ErrorMessageExtractor | null, _dumpParams?: boolean): Promise<T>;
|
|
156
170
|
/**
|
|
157
171
|
* Helper method to build the full URL from a path.
|
|
158
172
|
*
|
package/dist/api.js
CHANGED
|
@@ -32,6 +32,68 @@ function deepMerge(target, source) {
|
|
|
32
32
|
function isObject(item) {
|
|
33
33
|
return item !== null && typeof item === 'object' && !Array.isArray(item);
|
|
34
34
|
}
|
|
35
|
+
/** Symbol marker for explicit options API detection. */
|
|
36
|
+
const OPTIONS_MARKER = Symbol('options');
|
|
37
|
+
/**
|
|
38
|
+
* Marks an options object for the new options API.
|
|
39
|
+
* Use this to explicitly indicate you're using the options-based API.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* // GET with options
|
|
44
|
+
* await api.get('/users', opts({ params: { token: 'abc' } }));
|
|
45
|
+
*
|
|
46
|
+
* // POST with options
|
|
47
|
+
* await api.post('/users', opts({ data: { name: 'John' }, params: { token: 'abc' } }));
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function opts(options) {
|
|
51
|
+
return Object.assign(options, { [OPTIONS_MARKER]: true });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parses GET method arguments, detecting new options API via OPTIONS_MARKER.
|
|
55
|
+
*/
|
|
56
|
+
function parseGetOptions(paramsOrOptions, legacyRespHeaders, legacyErrorExtractor) {
|
|
57
|
+
if (paramsOrOptions && OPTIONS_MARKER in paramsOrOptions) {
|
|
58
|
+
// New options API (explicit via opts() wrapper)
|
|
59
|
+
const o = paramsOrOptions;
|
|
60
|
+
return {
|
|
61
|
+
params: o.params,
|
|
62
|
+
respHeaders: o.respHeaders ?? null,
|
|
63
|
+
errorExtractor: o.errorExtractor ?? null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Legacy positional API
|
|
67
|
+
return {
|
|
68
|
+
params: paramsOrOptions,
|
|
69
|
+
respHeaders: legacyRespHeaders ?? null,
|
|
70
|
+
errorExtractor: legacyErrorExtractor ?? null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parses body method arguments (POST/PUT/PATCH/DELETE), detecting new options API via OPTIONS_MARKER.
|
|
75
|
+
*/
|
|
76
|
+
function parseDataOptions(dataOrOptions, legacyParams, legacyRespHeaders, legacyErrorExtractor) {
|
|
77
|
+
if (dataOrOptions &&
|
|
78
|
+
typeof dataOrOptions === 'object' &&
|
|
79
|
+
OPTIONS_MARKER in dataOrOptions) {
|
|
80
|
+
// New options API (explicit via opts() wrapper)
|
|
81
|
+
const o = dataOrOptions;
|
|
82
|
+
return {
|
|
83
|
+
data: o.data ?? null,
|
|
84
|
+
params: o.params,
|
|
85
|
+
respHeaders: o.respHeaders ?? null,
|
|
86
|
+
errorExtractor: o.errorExtractor ?? null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Legacy positional API
|
|
90
|
+
return {
|
|
91
|
+
data: dataOrOptions ?? null,
|
|
92
|
+
params: legacyParams,
|
|
93
|
+
respHeaders: legacyRespHeaders ?? null,
|
|
94
|
+
errorExtractor: legacyErrorExtractor ?? null,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
35
97
|
const _fetchRaw = async ({ method, path, data = null, token = null, headers = null, signal, credentials, }) => {
|
|
36
98
|
const normalizedHeaders = Object.entries(headers || {}).reduce((m, [k, v]) => ({ ...m, [k.toLowerCase()]: v }), {});
|
|
37
99
|
const opts = {
|
|
@@ -142,136 +204,29 @@ export class HttpApi {
|
|
|
142
204
|
return /^https?:/.test(path) ? path : base + path;
|
|
143
205
|
}
|
|
144
206
|
async get(path, paramsOrOptions, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
145
|
-
|
|
146
|
-
let params;
|
|
147
|
-
let headers = null;
|
|
148
|
-
let extractor = null;
|
|
149
|
-
if (paramsOrOptions && ('respHeaders' in paramsOrOptions || 'errorExtractor' in paramsOrOptions)) {
|
|
150
|
-
// New options API
|
|
151
|
-
const opts = paramsOrOptions;
|
|
152
|
-
params = opts.params;
|
|
153
|
-
headers = opts.respHeaders ?? null;
|
|
154
|
-
extractor = opts.errorExtractor ?? null;
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
// Legacy positional API
|
|
158
|
-
params = paramsOrOptions;
|
|
159
|
-
headers = respHeaders ?? null;
|
|
160
|
-
extractor = errorMessageExtractor ?? null;
|
|
161
|
-
}
|
|
207
|
+
const { params, respHeaders: headers, errorExtractor } = parseGetOptions(paramsOrOptions, respHeaders, errorMessageExtractor);
|
|
162
208
|
path = this.#buildPath(path, this.#base);
|
|
163
|
-
return _fetch(this.#merge(await this.#getDefs(), { ...params, method: 'GET', path }), headers,
|
|
209
|
+
return _fetch(this.#merge(await this.#getDefs(), { ...params, method: 'GET', path }), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
164
210
|
}
|
|
165
211
|
async post(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
166
|
-
|
|
167
|
-
let data = null;
|
|
168
|
-
let fetchParams;
|
|
169
|
-
let headers = null;
|
|
170
|
-
let extractor = null;
|
|
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)) {
|
|
178
|
-
// New options API
|
|
179
|
-
const opts = dataOrOptions;
|
|
180
|
-
data = opts.data ?? null;
|
|
181
|
-
fetchParams = opts.params;
|
|
182
|
-
headers = opts.respHeaders ?? null;
|
|
183
|
-
extractor = opts.errorExtractor ?? null;
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// Legacy positional API
|
|
187
|
-
data = dataOrOptions ?? null;
|
|
188
|
-
fetchParams = params;
|
|
189
|
-
headers = respHeaders ?? null;
|
|
190
|
-
extractor = errorMessageExtractor ?? null;
|
|
191
|
-
}
|
|
212
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
192
213
|
path = this.#buildPath(path, this.#base);
|
|
193
|
-
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'POST', path }), headers,
|
|
214
|
+
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'POST', path }), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
194
215
|
}
|
|
195
216
|
async put(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
196
|
-
|
|
197
|
-
let fetchParams;
|
|
198
|
-
let headers = null;
|
|
199
|
-
let extractor = null;
|
|
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)) {
|
|
207
|
-
const opts = dataOrOptions;
|
|
208
|
-
data = opts.data ?? null;
|
|
209
|
-
fetchParams = opts.params;
|
|
210
|
-
headers = opts.respHeaders ?? null;
|
|
211
|
-
extractor = opts.errorExtractor ?? null;
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
data = dataOrOptions ?? null;
|
|
215
|
-
fetchParams = params;
|
|
216
|
-
headers = respHeaders ?? null;
|
|
217
|
-
extractor = errorMessageExtractor ?? null;
|
|
218
|
-
}
|
|
217
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
219
218
|
path = this.#buildPath(path, this.#base);
|
|
220
|
-
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'PUT', path }), headers,
|
|
219
|
+
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'PUT', path }), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
221
220
|
}
|
|
222
221
|
async patch(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
223
|
-
|
|
224
|
-
let fetchParams;
|
|
225
|
-
let headers = null;
|
|
226
|
-
let extractor = null;
|
|
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)) {
|
|
234
|
-
const opts = dataOrOptions;
|
|
235
|
-
data = opts.data ?? null;
|
|
236
|
-
fetchParams = opts.params;
|
|
237
|
-
headers = opts.respHeaders ?? null;
|
|
238
|
-
extractor = opts.errorExtractor ?? null;
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
data = dataOrOptions ?? null;
|
|
242
|
-
fetchParams = params;
|
|
243
|
-
headers = respHeaders ?? null;
|
|
244
|
-
extractor = errorMessageExtractor ?? null;
|
|
245
|
-
}
|
|
222
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
246
223
|
path = this.#buildPath(path, this.#base);
|
|
247
|
-
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'PATCH', path }), headers,
|
|
224
|
+
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'PATCH', path }), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
248
225
|
}
|
|
249
226
|
async del(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
250
|
-
|
|
251
|
-
let fetchParams;
|
|
252
|
-
let headers = null;
|
|
253
|
-
let extractor = null;
|
|
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)) {
|
|
261
|
-
const opts = dataOrOptions;
|
|
262
|
-
data = opts.data ?? null;
|
|
263
|
-
fetchParams = opts.params;
|
|
264
|
-
headers = opts.respHeaders ?? null;
|
|
265
|
-
extractor = opts.errorExtractor ?? null;
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
data = dataOrOptions ?? null;
|
|
269
|
-
fetchParams = params;
|
|
270
|
-
headers = respHeaders ?? null;
|
|
271
|
-
extractor = errorMessageExtractor ?? null;
|
|
272
|
-
}
|
|
227
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
273
228
|
path = this.#buildPath(path, this.#base);
|
|
274
|
-
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'DELETE', path }), headers,
|
|
229
|
+
return _fetch(this.#merge(await this.#getDefs(), { ...(fetchParams || {}), data, method: 'DELETE', path }), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
275
230
|
}
|
|
276
231
|
/**
|
|
277
232
|
* Helper method to build the full URL from a path.
|
package/dist/mod.d.ts
CHANGED
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
* }
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
export { HttpApi, createHttpApi, type DataOptions, type GetOptions, type FetchParams, type ErrorMessageExtractor, type ResponseHeaders, type RequestData, } from "./api.js";
|
|
24
|
+
export { HttpApi, createHttpApi, opts, type DataOptions, type GetOptions, type FetchParams, type ErrorMessageExtractor, type ResponseHeaders, type RequestData, } from "./api.js";
|
|
25
25
|
export { HTTP_ERROR, createHttpError, getErrorMessage } from "./error.js";
|
|
26
26
|
export { HTTP_STATUS } from "./status.js";
|
package/dist/mod.js
CHANGED