@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 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", { data: { name: "John" } });
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
- **New Options API (recommended):**
133
+ **Options API (with `opts()` wrapper):**
92
134
  ```ts
93
- async get(path: string, options?: GetOptions): Promise<unknown>
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<unknown>
145
+ ): Promise<T>
104
146
  ```
105
147
 
106
148
  **Example:**
107
149
  ```ts
108
- // New API
109
- const data = await api.get("/users", {
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
- **New Options API (recommended):**
168
+ **Options API (with `opts()` wrapper):**
123
169
  ```ts
124
- async post(path: string, options?: DataOptions): Promise<unknown>
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<unknown>
181
+ ): Promise<T>
136
182
  ```
137
183
 
138
184
  **Example:**
139
185
  ```ts
140
- // New API
141
- const result = await api.post("/users", {
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 (new options API - recommended)
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 (new options API - recommended)
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 still works
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 (new options API)
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 (new options API)
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 still supported
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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<unknown>;
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
- // Detect which API is being used
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, extractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
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
- // Detect which API is being used
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, extractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
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
- let data = null;
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, extractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
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
- let data = null;
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, extractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
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
- let data = null;
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, extractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
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
@@ -21,6 +21,6 @@
21
21
  * }
22
22
  * ```
23
23
  */
24
- export { HttpApi, createHttpApi, } from "./api.js";
24
+ export { HttpApi, createHttpApi, opts, } from "./api.js";
25
25
  export { HTTP_ERROR, createHttpError, getErrorMessage } from "./error.js";
26
26
  export { HTTP_STATUS } from "./status.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/http-utils",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "main": "dist/mod.js",
6
6
  "types": "dist/mod.d.ts",