@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 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
- assert(e.detail.message === 'hey');
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?: null, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
17
- post(path: string, data?: null, params?: FetchParams, respHeaders?: null, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
18
- put(path: string, data?: null, params?: FetchParams, respHeaders?: null, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
19
- patch(path: string, data?: null, params?: FetchParams, respHeaders?: null, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
20
- del(path: string, data?: null, params?: FetchParams, respHeaders?: null, _dumpParams?: boolean): Promise<string | BaseFetchParams | Response>;
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 text is valid json string)
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
- const found = HTTP_STATUS.findByCode(code);
223
- const statusText = found?.TEXT ?? fallback.TEXT;
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, [...r.headers.entries()].reduce((m, [k, v]) => ({ ...m, [k]: v }), {}),
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 || ''}` + path;
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 || ''}` + path;
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 || ''}` + path;
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 || ''}` + path;
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 || ''}` + path;
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 text is valid json string)
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
- const found = HTTP_STATUS.findByCode(code);
221
- const statusText = found?.TEXT ?? fallback.TEXT;
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, [...r.headers.entries()].reduce((m, [k, v]) => ({ ...m, [k]: v }), {}),
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 || ''}` + path;
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 || ''}` + path;
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 || ''}` + path;
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 || ''}` + path;
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 || ''}` + path;
350
+ path = `${base || ''}${path || ''}`;
328
351
  return _fetch(_merge(await _getDefs(), { ...(params || {}), data, method: 'DELETE', path }), respHeaders, _dumpParams);
329
352
  },
330
353
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/http-utils",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "Misc DRY http fetch related helpers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",