@marianmeres/http-utils 2.3.0 → 2.5.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/dist/api.d.ts +3 -3
- package/dist/api.js +57 -25
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export type RequestData = Record<string, unknown> | FormData | string | null;
|
|
12
12
|
interface BaseParams {
|
|
13
|
-
method:
|
|
13
|
+
method: "GET" | "POST" | "PATCH" | "DELETE" | "PUT";
|
|
14
14
|
path: string;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
@@ -22,11 +22,11 @@ export interface FetchParams {
|
|
|
22
22
|
/** Bearer token (auto-adds `Authorization: Bearer {token}` header). */
|
|
23
23
|
token?: string | null;
|
|
24
24
|
/** Custom request headers. */
|
|
25
|
-
headers?: Record<string, string> | null;
|
|
25
|
+
headers?: HeadersInit | Record<string, string> | null;
|
|
26
26
|
/** AbortSignal for request cancellation. */
|
|
27
27
|
signal?: AbortSignal;
|
|
28
28
|
/** Credentials mode for the request. */
|
|
29
|
-
credentials?:
|
|
29
|
+
credentials?: "omit" | "same-origin" | "include" | null;
|
|
30
30
|
/** If true, returns the raw Response object instead of parsed body. */
|
|
31
31
|
raw?: boolean | null;
|
|
32
32
|
/** If false, does not throw on HTTP errors (default: true). */
|
package/dist/api.js
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* HTTP API client factory and related types.
|
|
5
5
|
* Provides a convenient wrapper over the native `fetch` API with sensible defaults.
|
|
6
6
|
*/
|
|
7
|
-
import { createHttpError } from
|
|
7
|
+
import { createHttpError } from "./error.js";
|
|
8
8
|
/**
|
|
9
9
|
* Deep merges two objects. Later properties overwrite earlier properties.
|
|
10
10
|
*/
|
|
11
11
|
function deepMerge(target, source) {
|
|
12
12
|
const output = { ...target };
|
|
13
13
|
if (isObject(target) && isObject(source)) {
|
|
14
|
-
Object.keys(source).forEach(key => {
|
|
14
|
+
Object.keys(source).forEach((key) => {
|
|
15
15
|
const sourceVal = source[key];
|
|
16
16
|
const targetVal = target[key];
|
|
17
17
|
if (isObject(sourceVal)) {
|
|
@@ -30,10 +30,10 @@ function deepMerge(target, source) {
|
|
|
30
30
|
return output;
|
|
31
31
|
}
|
|
32
32
|
function isObject(item) {
|
|
33
|
-
return item !== null && typeof item ===
|
|
33
|
+
return item !== null && typeof item === "object" && !Array.isArray(item);
|
|
34
34
|
}
|
|
35
35
|
/** Symbol marker for explicit options API detection. */
|
|
36
|
-
const OPTIONS_MARKER = Symbol(
|
|
36
|
+
const OPTIONS_MARKER = Symbol("options");
|
|
37
37
|
/**
|
|
38
38
|
* Marks an options object for the new options API.
|
|
39
39
|
* Use this to explicitly indicate you're using the options-based API.
|
|
@@ -75,7 +75,7 @@ function parseGetOptions(paramsOrOptions, legacyRespHeaders, legacyErrorExtracto
|
|
|
75
75
|
*/
|
|
76
76
|
function parseDataOptions(dataOrOptions, legacyParams, legacyRespHeaders, legacyErrorExtractor) {
|
|
77
77
|
if (dataOrOptions &&
|
|
78
|
-
typeof dataOrOptions ===
|
|
78
|
+
typeof dataOrOptions === "object" &&
|
|
79
79
|
OPTIONS_MARKER in dataOrOptions) {
|
|
80
80
|
// New options API (explicit via opts() wrapper)
|
|
81
81
|
const o = dataOrOptions;
|
|
@@ -95,15 +95,20 @@ function parseDataOptions(dataOrOptions, legacyParams, legacyRespHeaders, legacy
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
const _fetchRaw = async ({ method, path, data = null, token = null, headers = null, signal, credentials, }) => {
|
|
98
|
-
const normalizedHeaders =
|
|
98
|
+
const normalizedHeaders = {};
|
|
99
|
+
if (headers) {
|
|
100
|
+
new Headers(headers).forEach((value, key) => {
|
|
101
|
+
normalizedHeaders[key] = value;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
99
104
|
const opts = {
|
|
100
105
|
method,
|
|
101
106
|
credentials: credentials ?? undefined,
|
|
102
107
|
headers: normalizedHeaders,
|
|
103
|
-
signal
|
|
108
|
+
signal,
|
|
104
109
|
};
|
|
105
110
|
if (data) {
|
|
106
|
-
const isObj = typeof data ===
|
|
111
|
+
const isObj = typeof data === "object";
|
|
107
112
|
// FormData: multipart/form-data -- no explicit Content-Type
|
|
108
113
|
if (data instanceof FormData) {
|
|
109
114
|
opts.body = data;
|
|
@@ -111,15 +116,15 @@ const _fetchRaw = async ({ method, path, data = null, token = null, headers = nu
|
|
|
111
116
|
// Cover 99% of use cases (may not fit all scenarios)
|
|
112
117
|
else {
|
|
113
118
|
// If not explicitly stated, assume JSON
|
|
114
|
-
if (isObj || !normalizedHeaders[
|
|
115
|
-
normalizedHeaders[
|
|
119
|
+
if (isObj || !normalizedHeaders["content-type"]) {
|
|
120
|
+
normalizedHeaders["content-type"] = "application/json";
|
|
116
121
|
}
|
|
117
122
|
opts.body = JSON.stringify(data);
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
// Opinionated convention: auto-add Bearer token
|
|
121
126
|
if (token) {
|
|
122
|
-
normalizedHeaders[
|
|
127
|
+
normalizedHeaders["authorization"] = `Bearer ${token}`;
|
|
123
128
|
}
|
|
124
129
|
opts.headers = normalizedHeaders;
|
|
125
130
|
return await fetch(path, opts);
|
|
@@ -159,7 +164,7 @@ const _fetch = async (params, respHeaders = null, errorMessageExtractor = null,
|
|
|
159
164
|
b?.message ||
|
|
160
165
|
b?.error ||
|
|
161
166
|
_response?.statusText ||
|
|
162
|
-
|
|
167
|
+
"Unknown error");
|
|
163
168
|
if (msg.length > 255)
|
|
164
169
|
msg = `[Shortened]: ${msg.slice(0, 255)}`;
|
|
165
170
|
return msg;
|
|
@@ -188,45 +193,72 @@ export class HttpApi {
|
|
|
188
193
|
this.#base = base;
|
|
189
194
|
this.#defaults = defaults;
|
|
190
195
|
this.#factoryErrorMessageExtractor = factoryErrorMessageExtractor;
|
|
196
|
+
// Bind methods for destructuring support
|
|
197
|
+
this.get = this.get.bind(this);
|
|
198
|
+
this.post = this.post.bind(this);
|
|
199
|
+
this.put = this.put.bind(this);
|
|
200
|
+
this.patch = this.patch.bind(this);
|
|
201
|
+
this.del = this.del.bind(this);
|
|
202
|
+
this.url = this.url.bind(this);
|
|
191
203
|
}
|
|
192
204
|
#merge(a, b) {
|
|
193
205
|
return deepMerge(a, b);
|
|
194
206
|
}
|
|
195
207
|
async #getDefs() {
|
|
196
|
-
if (typeof this.#defaults ===
|
|
208
|
+
if (typeof this.#defaults === "function") {
|
|
197
209
|
return { ...(await this.#defaults()) };
|
|
198
210
|
}
|
|
199
211
|
return { ...(this.#defaults || {}) };
|
|
200
212
|
}
|
|
201
213
|
#buildPath(path, base) {
|
|
202
|
-
base = `${base ||
|
|
203
|
-
path = `${path ||
|
|
214
|
+
base = `${base || ""}`;
|
|
215
|
+
path = `${path || ""}`;
|
|
204
216
|
return /^https?:/.test(path) ? path : base + path;
|
|
205
217
|
}
|
|
206
218
|
async get(path, paramsOrOptions, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
207
|
-
const { params, respHeaders: headers, errorExtractor } = parseGetOptions(paramsOrOptions, respHeaders, errorMessageExtractor);
|
|
219
|
+
const { params, respHeaders: headers, errorExtractor, } = parseGetOptions(paramsOrOptions, respHeaders, errorMessageExtractor);
|
|
208
220
|
path = this.#buildPath(path, this.#base);
|
|
209
|
-
return _fetch(this.#merge(await this.#getDefs(), { ...params, method:
|
|
221
|
+
return _fetch(this.#merge(await this.#getDefs(), { ...params, method: "GET", path }), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
210
222
|
}
|
|
211
223
|
async post(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
212
|
-
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
224
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor, } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
213
225
|
path = this.#buildPath(path, this.#base);
|
|
214
|
-
return _fetch(this.#merge(await this.#getDefs(), {
|
|
226
|
+
return _fetch(this.#merge(await this.#getDefs(), {
|
|
227
|
+
...(fetchParams || {}),
|
|
228
|
+
data,
|
|
229
|
+
method: "POST",
|
|
230
|
+
path,
|
|
231
|
+
}), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
215
232
|
}
|
|
216
233
|
async put(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
217
|
-
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
234
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor, } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
218
235
|
path = this.#buildPath(path, this.#base);
|
|
219
|
-
return _fetch(this.#merge(await this.#getDefs(), {
|
|
236
|
+
return _fetch(this.#merge(await this.#getDefs(), {
|
|
237
|
+
...(fetchParams || {}),
|
|
238
|
+
data,
|
|
239
|
+
method: "PUT",
|
|
240
|
+
path,
|
|
241
|
+
}), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
220
242
|
}
|
|
221
243
|
async patch(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
222
|
-
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
244
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor, } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
223
245
|
path = this.#buildPath(path, this.#base);
|
|
224
|
-
return _fetch(this.#merge(await this.#getDefs(), {
|
|
246
|
+
return _fetch(this.#merge(await this.#getDefs(), {
|
|
247
|
+
...(fetchParams || {}),
|
|
248
|
+
data,
|
|
249
|
+
method: "PATCH",
|
|
250
|
+
path,
|
|
251
|
+
}), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
225
252
|
}
|
|
226
253
|
async del(path, dataOrOptions, params, respHeaders, errorMessageExtractor, _dumpParams = false) {
|
|
227
|
-
const { data, params: fetchParams, respHeaders: headers, errorExtractor } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
254
|
+
const { data, params: fetchParams, respHeaders: headers, errorExtractor, } = parseDataOptions(dataOrOptions, params, respHeaders, errorMessageExtractor);
|
|
228
255
|
path = this.#buildPath(path, this.#base);
|
|
229
|
-
return _fetch(this.#merge(await this.#getDefs(), {
|
|
256
|
+
return _fetch(this.#merge(await this.#getDefs(), {
|
|
257
|
+
...(fetchParams || {}),
|
|
258
|
+
data,
|
|
259
|
+
method: "DELETE",
|
|
260
|
+
path,
|
|
261
|
+
}), headers, errorExtractor ?? this.#factoryErrorMessageExtractor, _dumpParams);
|
|
230
262
|
}
|
|
231
263
|
/**
|
|
232
264
|
* Helper method to build the full URL from a path.
|