@marianmeres/http-utils 1.5.0 → 1.6.1
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/README.md +9 -1
- package/dist/api.d.ts +5 -5
- package/dist/error.d.ts +2 -1
- package/dist/index.cjs +37 -14
- package/dist/index.js +37 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,10 @@ try {
|
|
|
30
30
|
assert(e.toString() === 'HttpNotFoundError: Not Found');
|
|
31
31
|
assert(e.status === HTTP_STATUS.ERROR_CLIENT.NOT_FOUND.CODE);
|
|
32
32
|
assert(e.statusText === HTTP_STATUS.ERROR_CLIENT.NOT_FOUND.TEXT);
|
|
33
|
-
|
|
33
|
+
// `body` is a custom prop containing the raw http response body text (JSON.parse-d if available)
|
|
34
|
+
assert(e.body.message === 'hey');
|
|
35
|
+
// `cause` is a standart Error prop, containg here some default debug info
|
|
36
|
+
assert(err.cause.response.headers)
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
// EXAMPLE: assuming `/foo` returns 404 header and json {"message":"hey"}
|
|
@@ -46,6 +49,11 @@ assert(r.message === 'created');
|
|
|
46
49
|
// EXAMPLE: raw Response
|
|
47
50
|
const r = await api.get('/resource', { raw: true });
|
|
48
51
|
assert(r instanceof Response);
|
|
52
|
+
|
|
53
|
+
// EXAMPLE: access to response headers
|
|
54
|
+
let respHeaders = {};
|
|
55
|
+
const r = await api.get('/resource', null, respHeaders);
|
|
56
|
+
assert(Object.keys(respHeaders).length)
|
|
49
57
|
```
|
|
50
58
|
|
|
51
59
|
See [`HTTP_STATUS`](./src/status.ts) and [`HTTP_ERROR`](./src/error.ts) for more.
|
package/dist/api.d.ts
CHANGED
|
@@ -13,10 +13,10 @@ interface FetchParams {
|
|
|
13
13
|
}
|
|
14
14
|
type BaseFetchParams = BaseParams & FetchParams;
|
|
15
15
|
export declare const createHttpApi: (base?: string | null, defaults?: Partial<BaseFetchParams> | (() => Promise<Partial<BaseFetchParams>>)) => {
|
|
16
|
-
get(path: string, params?: FetchParams, respHeaders?:
|
|
17
|
-
post(path: string, data?: null, params?: FetchParams, respHeaders?:
|
|
18
|
-
put(path: string, data?: null, params?: FetchParams, respHeaders?:
|
|
19
|
-
patch(path: string, data?: null, params?: FetchParams, respHeaders?:
|
|
20
|
-
del(path: string, data?: null, params?: FetchParams, respHeaders?:
|
|
16
|
+
get(path: string, params?: FetchParams, respHeaders?: any, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
|
|
17
|
+
post(path: string, data?: null, params?: FetchParams, respHeaders?: any, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
|
|
18
|
+
put(path: string, data?: null, params?: FetchParams, respHeaders?: any, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
|
|
19
|
+
patch(path: string, data?: null, params?: FetchParams, respHeaders?: any, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
|
|
20
|
+
del(path: string, data?: null, params?: FetchParams, respHeaders?: any, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
|
|
21
21
|
};
|
|
22
22
|
export {};
|
package/dist/error.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ declare class HttpError extends Error {
|
|
|
2
2
|
name: string;
|
|
3
3
|
status: number;
|
|
4
4
|
statusText: string;
|
|
5
|
+
body: any;
|
|
5
6
|
}
|
|
6
7
|
declare class BadRequest extends HttpError {
|
|
7
8
|
name: string;
|
|
@@ -82,5 +83,5 @@ export declare const HTTP_ERROR: {
|
|
|
82
83
|
BadGateway: typeof BadGateway;
|
|
83
84
|
ServiceUnavailable: typeof ServiceUnavailable;
|
|
84
85
|
};
|
|
85
|
-
export declare const createHttpError: (code: number | string, message?: string | null, cause?: any) => BadRequest | Unauthorized | Forbidden | NotFound | MethodNotAllowed | RequestTimeout | Conflict | Gone | ImATeapot | InternalServerError | NotImplemented | BadGateway | ServiceUnavailable;
|
|
86
|
+
export declare const createHttpError: (code: number | string, message?: string | null, body?: string | null, cause?: any) => BadRequest | Unauthorized | Forbidden | NotFound | MethodNotAllowed | RequestTimeout | Conflict | Gone | ImATeapot | InternalServerError | NotImplemented | BadGateway | ServiceUnavailable;
|
|
86
87
|
export {};
|
package/dist/index.cjs
CHANGED
|
@@ -106,8 +106,10 @@ class HTTP_STATUS {
|
|
|
106
106
|
// opinionated base for all
|
|
107
107
|
class HttpError extends Error {
|
|
108
108
|
name = 'HttpError';
|
|
109
|
+
// props simulating fetch Response
|
|
109
110
|
status = HTTP_STATUS.ERROR_SERVER.INTERNAL_SERVER_ERROR.CODE;
|
|
110
111
|
statusText = HTTP_STATUS.ERROR_SERVER.INTERNAL_SERVER_ERROR.TEXT;
|
|
112
|
+
body = null;
|
|
111
113
|
}
|
|
112
114
|
// some more specific instances of the well known ones...
|
|
113
115
|
// client
|
|
@@ -212,15 +214,24 @@ const _wellKnownCtorMap = {
|
|
|
212
214
|
'503': ServiceUnavailable,
|
|
213
215
|
};
|
|
214
216
|
const createHttpError = (code, message,
|
|
217
|
+
// arbitrary content, typically http response body which threw this error
|
|
218
|
+
// (will be JSON.parse-d if the content is a valid json string)
|
|
219
|
+
body,
|
|
215
220
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
|
|
216
|
-
// arbitrary details, typically response text (will be JSON.parse-d if
|
|
221
|
+
// arbitrary details, typically response text (will be JSON.parse-d if the content is a valid json string)
|
|
217
222
|
cause) => {
|
|
218
223
|
const fallback = HTTP_STATUS.ERROR_SERVER.INTERNAL_SERVER_ERROR;
|
|
219
224
|
code = Number(code);
|
|
220
225
|
if (isNaN(code) || !(code >= 400 && code < 600))
|
|
221
226
|
code = fallback.CODE;
|
|
222
|
-
|
|
223
|
-
|
|
227
|
+
// opinionated convention
|
|
228
|
+
if (typeof body === 'string') {
|
|
229
|
+
// prettier-ignore
|
|
230
|
+
try {
|
|
231
|
+
body = JSON.parse(body);
|
|
232
|
+
}
|
|
233
|
+
catch (e) { }
|
|
234
|
+
}
|
|
224
235
|
// opinionated convention
|
|
225
236
|
if (typeof cause === 'string') {
|
|
226
237
|
// prettier-ignore
|
|
@@ -231,9 +242,14 @@ cause) => {
|
|
|
231
242
|
}
|
|
232
243
|
// try to find the well known one, otherwise fallback to generic
|
|
233
244
|
const ctor = _wellKnownCtorMap[`${code}`] ?? HttpError;
|
|
245
|
+
//
|
|
246
|
+
const found = HTTP_STATUS.findByCode(code);
|
|
247
|
+
const statusText = found?.TEXT ?? fallback.TEXT;
|
|
248
|
+
//
|
|
234
249
|
let e = new ctor(message || statusText, { cause });
|
|
235
250
|
e.status = found?.CODE ?? fallback.CODE;
|
|
236
251
|
e.statusText = statusText;
|
|
252
|
+
e.body = body;
|
|
237
253
|
return e;
|
|
238
254
|
};
|
|
239
255
|
|
|
@@ -267,14 +283,13 @@ const _fetch = async (params, respHeaders = null, _dumpParams = false) => {
|
|
|
267
283
|
const r = await _fetchRaw(params);
|
|
268
284
|
if (params.raw)
|
|
269
285
|
return r;
|
|
286
|
+
//
|
|
287
|
+
const headers = [...r.headers.entries()].reduce((m, [k, v]) => ({ ...m, [k]: v }), {});
|
|
270
288
|
// quick-n-dirty reference to headers (so it's still accessible over this api wrap)
|
|
271
289
|
if (respHeaders) {
|
|
272
|
-
Object.assign(respHeaders,
|
|
290
|
+
Object.assign(respHeaders, { ...headers },
|
|
273
291
|
// adding status/text under special keys
|
|
274
|
-
{
|
|
275
|
-
__http_status_code__: r.status,
|
|
276
|
-
__http_status_text__: r.statusText,
|
|
277
|
-
});
|
|
292
|
+
{ __http_status_code__: r.status, __http_status_text__: r.statusText });
|
|
278
293
|
}
|
|
279
294
|
let body = await r.text();
|
|
280
295
|
// prettier-ignore
|
|
@@ -284,7 +299,15 @@ const _fetch = async (params, respHeaders = null, _dumpParams = false) => {
|
|
|
284
299
|
catch (e) { }
|
|
285
300
|
params.assert ??= true; // default is true
|
|
286
301
|
if (!r.ok && params.assert) {
|
|
287
|
-
throw createHttpError(r.status, null, body
|
|
302
|
+
throw createHttpError(r.status, null, body, {
|
|
303
|
+
method: params.method,
|
|
304
|
+
path: params.path,
|
|
305
|
+
response: {
|
|
306
|
+
status: r.status,
|
|
307
|
+
statusText: r.statusText,
|
|
308
|
+
headers,
|
|
309
|
+
},
|
|
310
|
+
});
|
|
288
311
|
}
|
|
289
312
|
return body;
|
|
290
313
|
};
|
|
@@ -305,28 +328,28 @@ const createHttpApi = (base, defaults) => {
|
|
|
305
328
|
return {
|
|
306
329
|
// GET
|
|
307
330
|
async get(path, params, respHeaders = null, _dumpParams = false) {
|
|
308
|
-
path = `${base || ''}
|
|
331
|
+
path = `${base || ''}${path || ''}`;
|
|
309
332
|
return _fetch(_merge(await _getDefs(), { ...params, method: 'GET', path }), respHeaders, _dumpParams);
|
|
310
333
|
},
|
|
311
334
|
// POST
|
|
312
335
|
async post(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
313
|
-
path = `${base || ''}
|
|
336
|
+
path = `${base || ''}${path || ''}`;
|
|
314
337
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'POST', path }), respHeaders, _dumpParams);
|
|
315
338
|
},
|
|
316
339
|
// PUT
|
|
317
340
|
async put(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
318
|
-
path = `${base || ''}
|
|
341
|
+
path = `${base || ''}${path || ''}`;
|
|
319
342
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'PUT', path }), respHeaders, _dumpParams);
|
|
320
343
|
},
|
|
321
344
|
// PATCH
|
|
322
345
|
async patch(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
323
|
-
path = `${base || ''}
|
|
346
|
+
path = `${base || ''}${path || ''}`;
|
|
324
347
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'PATCH', path }), respHeaders, _dumpParams);
|
|
325
348
|
},
|
|
326
349
|
// DELETE
|
|
327
350
|
// https://stackoverflow.com/questions/299628/is-an-entity-body-allowed-for-an-http-delete-request
|
|
328
351
|
async del(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
329
|
-
path = `${base || ''}
|
|
352
|
+
path = `${base || ''}${path || ''}`;
|
|
330
353
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'DELETE', path }), respHeaders, _dumpParams);
|
|
331
354
|
},
|
|
332
355
|
};
|
package/dist/index.js
CHANGED
|
@@ -104,8 +104,10 @@ class HTTP_STATUS {
|
|
|
104
104
|
// opinionated base for all
|
|
105
105
|
class HttpError extends Error {
|
|
106
106
|
name = 'HttpError';
|
|
107
|
+
// props simulating fetch Response
|
|
107
108
|
status = HTTP_STATUS.ERROR_SERVER.INTERNAL_SERVER_ERROR.CODE;
|
|
108
109
|
statusText = HTTP_STATUS.ERROR_SERVER.INTERNAL_SERVER_ERROR.TEXT;
|
|
110
|
+
body = null;
|
|
109
111
|
}
|
|
110
112
|
// some more specific instances of the well known ones...
|
|
111
113
|
// client
|
|
@@ -210,15 +212,24 @@ const _wellKnownCtorMap = {
|
|
|
210
212
|
'503': ServiceUnavailable,
|
|
211
213
|
};
|
|
212
214
|
const createHttpError = (code, message,
|
|
215
|
+
// arbitrary content, typically http response body which threw this error
|
|
216
|
+
// (will be JSON.parse-d if the content is a valid json string)
|
|
217
|
+
body,
|
|
213
218
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
|
|
214
|
-
// arbitrary details, typically response text (will be JSON.parse-d if
|
|
219
|
+
// arbitrary details, typically response text (will be JSON.parse-d if the content is a valid json string)
|
|
215
220
|
cause) => {
|
|
216
221
|
const fallback = HTTP_STATUS.ERROR_SERVER.INTERNAL_SERVER_ERROR;
|
|
217
222
|
code = Number(code);
|
|
218
223
|
if (isNaN(code) || !(code >= 400 && code < 600))
|
|
219
224
|
code = fallback.CODE;
|
|
220
|
-
|
|
221
|
-
|
|
225
|
+
// opinionated convention
|
|
226
|
+
if (typeof body === 'string') {
|
|
227
|
+
// prettier-ignore
|
|
228
|
+
try {
|
|
229
|
+
body = JSON.parse(body);
|
|
230
|
+
}
|
|
231
|
+
catch (e) { }
|
|
232
|
+
}
|
|
222
233
|
// opinionated convention
|
|
223
234
|
if (typeof cause === 'string') {
|
|
224
235
|
// prettier-ignore
|
|
@@ -229,9 +240,14 @@ cause) => {
|
|
|
229
240
|
}
|
|
230
241
|
// try to find the well known one, otherwise fallback to generic
|
|
231
242
|
const ctor = _wellKnownCtorMap[`${code}`] ?? HttpError;
|
|
243
|
+
//
|
|
244
|
+
const found = HTTP_STATUS.findByCode(code);
|
|
245
|
+
const statusText = found?.TEXT ?? fallback.TEXT;
|
|
246
|
+
//
|
|
232
247
|
let e = new ctor(message || statusText, { cause });
|
|
233
248
|
e.status = found?.CODE ?? fallback.CODE;
|
|
234
249
|
e.statusText = statusText;
|
|
250
|
+
e.body = body;
|
|
235
251
|
return e;
|
|
236
252
|
};
|
|
237
253
|
|
|
@@ -265,14 +281,13 @@ const _fetch = async (params, respHeaders = null, _dumpParams = false) => {
|
|
|
265
281
|
const r = await _fetchRaw(params);
|
|
266
282
|
if (params.raw)
|
|
267
283
|
return r;
|
|
284
|
+
//
|
|
285
|
+
const headers = [...r.headers.entries()].reduce((m, [k, v]) => ({ ...m, [k]: v }), {});
|
|
268
286
|
// quick-n-dirty reference to headers (so it's still accessible over this api wrap)
|
|
269
287
|
if (respHeaders) {
|
|
270
|
-
Object.assign(respHeaders,
|
|
288
|
+
Object.assign(respHeaders, { ...headers },
|
|
271
289
|
// adding status/text under special keys
|
|
272
|
-
{
|
|
273
|
-
__http_status_code__: r.status,
|
|
274
|
-
__http_status_text__: r.statusText,
|
|
275
|
-
});
|
|
290
|
+
{ __http_status_code__: r.status, __http_status_text__: r.statusText });
|
|
276
291
|
}
|
|
277
292
|
let body = await r.text();
|
|
278
293
|
// prettier-ignore
|
|
@@ -282,7 +297,15 @@ const _fetch = async (params, respHeaders = null, _dumpParams = false) => {
|
|
|
282
297
|
catch (e) { }
|
|
283
298
|
params.assert ??= true; // default is true
|
|
284
299
|
if (!r.ok && params.assert) {
|
|
285
|
-
throw createHttpError(r.status, null, body
|
|
300
|
+
throw createHttpError(r.status, null, body, {
|
|
301
|
+
method: params.method,
|
|
302
|
+
path: params.path,
|
|
303
|
+
response: {
|
|
304
|
+
status: r.status,
|
|
305
|
+
statusText: r.statusText,
|
|
306
|
+
headers,
|
|
307
|
+
},
|
|
308
|
+
});
|
|
286
309
|
}
|
|
287
310
|
return body;
|
|
288
311
|
};
|
|
@@ -303,28 +326,28 @@ const createHttpApi = (base, defaults) => {
|
|
|
303
326
|
return {
|
|
304
327
|
// GET
|
|
305
328
|
async get(path, params, respHeaders = null, _dumpParams = false) {
|
|
306
|
-
path = `${base || ''}
|
|
329
|
+
path = `${base || ''}${path || ''}`;
|
|
307
330
|
return _fetch(_merge(await _getDefs(), { ...params, method: 'GET', path }), respHeaders, _dumpParams);
|
|
308
331
|
},
|
|
309
332
|
// POST
|
|
310
333
|
async post(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
311
|
-
path = `${base || ''}
|
|
334
|
+
path = `${base || ''}${path || ''}`;
|
|
312
335
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'POST', path }), respHeaders, _dumpParams);
|
|
313
336
|
},
|
|
314
337
|
// PUT
|
|
315
338
|
async put(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
316
|
-
path = `${base || ''}
|
|
339
|
+
path = `${base || ''}${path || ''}`;
|
|
317
340
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'PUT', path }), respHeaders, _dumpParams);
|
|
318
341
|
},
|
|
319
342
|
// PATCH
|
|
320
343
|
async patch(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
321
|
-
path = `${base || ''}
|
|
344
|
+
path = `${base || ''}${path || ''}`;
|
|
322
345
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'PATCH', path }), respHeaders, _dumpParams);
|
|
323
346
|
},
|
|
324
347
|
// DELETE
|
|
325
348
|
// https://stackoverflow.com/questions/299628/is-an-entity-body-allowed-for-an-http-delete-request
|
|
326
349
|
async del(path, data = null, params, respHeaders = null, _dumpParams = false) {
|
|
327
|
-
path = `${base || ''}
|
|
350
|
+
path = `${base || ''}${path || ''}`;
|
|
328
351
|
return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'DELETE', path }), respHeaders, _dumpParams);
|
|
329
352
|
},
|
|
330
353
|
};
|