@digitalcredentials/http-client
Advanced tools
| 'use strict'; | ||
| var undici = require('undici'); | ||
| var node_process = require('node:process'); | ||
| /*! | ||
| * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| // as long as an agent has a reference to it, its associated dispatcher will | ||
| // be kept in this cache for reuse | ||
| const AGENT_CACHE = new WeakMap(); | ||
| // can only convert agent to dispatcher option on node 18.2+ | ||
| const [major, minor] = node_process.versions.node.split('.').map(v => parseInt(v, 10)); | ||
| const canConvert = (major > 18) || (major === 18 && minor >= 2); | ||
| // converts `agent`/`httpsAgent` option to a dispatcher option | ||
| function convertAgent(options) { | ||
| if(!canConvert) { | ||
| return options; | ||
| } | ||
| // do not override custom fetch function from another lib | ||
| if(options?.fetch && !options.fetch._httpClientCustomFetch) { | ||
| return options; | ||
| } | ||
| // only override if an agent option is present | ||
| const agent = options?.agent || options?.httpsAgent; | ||
| if(!agent) { | ||
| return options; | ||
| } | ||
| // use custom fetch if agent has already been converted | ||
| let fetch = AGENT_CACHE.get(agent); | ||
| if(!fetch) { | ||
| const dispatcher = new undici.Agent({connect: agent.options}); | ||
| fetch = createFetch(dispatcher); | ||
| fetch._httpClientCustomFetch = true; | ||
| AGENT_CACHE.set(agent, fetch); | ||
| } | ||
| return {...options, fetch}; | ||
| } | ||
| // create fetch override uses custom `dispatcher`; since `ky` does not pass | ||
| // the dispatcher option through to `fetch`, we must use this override | ||
| function createFetch(dispatcher) { | ||
| return function fetch(...args) { | ||
| dispatcher = (args[1] && args[1].dispatcher) || dispatcher; | ||
| args[1] = {...args[1], dispatcher}; | ||
| // eslint-disable-next-line no-undef | ||
| return globalThis.fetch(...args); | ||
| }; | ||
| } | ||
| exports.convertAgent = convertAgent; |
| 'use strict'; | ||
| function deferred(f) { | ||
| let promise; | ||
| return { | ||
| then( | ||
| onfulfilled, | ||
| onrejected | ||
| ) { | ||
| // Use logical OR assignment when Node.js 14.x support is dropped | ||
| //promise ||= new Promise(resolve => resolve(f())); | ||
| promise || (promise = new Promise(resolve => resolve(f()))); | ||
| return promise.then( | ||
| onfulfilled, | ||
| onrejected | ||
| ); | ||
| }, | ||
| }; | ||
| } | ||
| exports.deferred = deferred; |
| 'use strict'; | ||
| var agentCompatibility = require('./agentCompatibility.js'); | ||
| var deferred = require('./deferred.js'); | ||
| /*! | ||
| * Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| const kyOriginalPromise = deferred.deferred(() => import('ky') | ||
| .then(({default: ky}) => ky)); | ||
| const DEFAULT_HEADERS = { | ||
| Accept: 'application/ld+json, application/json' | ||
| }; | ||
| // methods to proxy from ky | ||
| const PROXY_METHODS = new Set([ | ||
| 'get', 'post', 'put', 'push', 'patch', 'head', 'delete' | ||
| ]); | ||
| /** | ||
| * Returns a custom httpClient instance. Used to specify default headers and | ||
| * other default overrides. | ||
| * | ||
| * @param {object} [options={}] - Options hashmap. | ||
| * @param {object} [options.parent] - The ky promise to inherit from. | ||
| * @param {object} [options.headers={}] - Default header overrides. | ||
| * @param {object} [options.params] - Other default overrides. | ||
| * | ||
| * @returns {Function} Custom httpClient instance. | ||
| */ | ||
| function createInstance({ | ||
| parent = kyOriginalPromise, headers = {}, ...params | ||
| } = {}) { | ||
| // convert legacy agent options | ||
| params = agentCompatibility.convertAgent(params); | ||
| // create new ky instance that will asynchronously resolve | ||
| const kyPromise = deferred.deferred(() => parent.then(kyBase => { | ||
| let ky; | ||
| if(parent === kyOriginalPromise) { | ||
| // ensure default headers, allow overrides | ||
| ky = kyBase.create({ | ||
| headers: {...DEFAULT_HEADERS, ...headers}, | ||
| ...params | ||
| }); | ||
| } else { | ||
| // extend parent | ||
| ky = kyBase.extend({headers, ...params}); | ||
| } | ||
| return ky; | ||
| })); | ||
| return _createHttpClient(kyPromise); | ||
| } | ||
| function _createHttpClient(kyPromise) { | ||
| async function httpClient(...args) { | ||
| const ky = await kyPromise; | ||
| const method = ((args[1] && args[1].method) || 'get').toLowerCase(); | ||
| if(PROXY_METHODS.has(method)) { | ||
| return httpClient[method].apply(ky[method], args); | ||
| } | ||
| // convert legacy agent options | ||
| args[1] = agentCompatibility.convertAgent(args[1]); | ||
| return ky.apply(ky, args); | ||
| } | ||
| for(const method of PROXY_METHODS) { | ||
| httpClient[method] = async function(...args) { | ||
| const ky = await kyPromise; | ||
| return _handleResponse(ky[method], ky, args); | ||
| }; | ||
| } | ||
| httpClient.create = function({headers = {}, ...params}) { | ||
| return createInstance({headers, ...params}); | ||
| }; | ||
| httpClient.extend = function({headers = {}, ...params}) { | ||
| return createInstance({parent: kyPromise, headers, ...params}); | ||
| }; | ||
| // default async `stop` signal getter | ||
| Object.defineProperty(httpClient, 'stop', { | ||
| async get() { | ||
| const ky = await kyPromise; | ||
| return ky.stop; | ||
| } | ||
| }); | ||
| return httpClient; | ||
| } | ||
| async function _handleResponse(target, thisArg, args) { | ||
| // convert legacy agent options | ||
| args[1] = agentCompatibility.convertAgent(args[1]); | ||
| let response; | ||
| const [url] = args; | ||
| try { | ||
| response = await target.apply(thisArg, args); | ||
| } catch(error) { | ||
| return _handleError({error, url}); | ||
| } | ||
| const {parseBody = true} = args[1] || {}; | ||
| // always set 'data', default to undefined | ||
| let data; | ||
| if(parseBody) { | ||
| // a 204 will not include a content-type header | ||
| const contentType = response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| data = await response.json(); | ||
| } | ||
| } | ||
| Object.defineProperty(response, 'data', {value: data}); | ||
| return response; | ||
| } | ||
| /** | ||
| * @param {object} options - Options hashmap. | ||
| * @param {Error} options.error - Error thrown during http operation. | ||
| * @param {string} options.url - Target URL of the request. | ||
| * | ||
| * @returns {Promise} Rejects with a thrown error. | ||
| */ | ||
| async function _handleError({error, url}) { | ||
| error.requestUrl = url; | ||
| // handle network errors and system errors that do not have a response | ||
| if(!error.response) { | ||
| if(error.message === 'Failed to fetch') { | ||
| error.message = `Failed to fetch "${url}". Possible CORS error.`; | ||
| } | ||
| // ky's TimeoutError class | ||
| if(error.name === 'TimeoutError') { | ||
| error.message = `Request to "${url}" timed out.`; | ||
| } | ||
| throw error; | ||
| } | ||
| // always move status up to the root of error | ||
| error.status = error.response.status; | ||
| const contentType = error.response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| const errorBody = await error.response.json(); | ||
| // the HTTPError received from ky has a generic message based on status | ||
| // use that if the JSON body does not include a message | ||
| error.message = errorBody.message || error.message; | ||
| error.data = errorBody; | ||
| } | ||
| throw error; | ||
| } | ||
| exports.DEFAULT_HEADERS = DEFAULT_HEADERS; | ||
| exports.createInstance = createInstance; | ||
| exports.kyOriginalPromise = kyOriginalPromise; |
| /*! | ||
| * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| // no-op for browsers | ||
| export function convertAgent(options) { | ||
| return options; | ||
| } |
| /*! | ||
| * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| import {Agent} from 'undici'; | ||
| import {versions} from 'node:process'; | ||
| // as long as an agent has a reference to it, its associated dispatcher will | ||
| // be kept in this cache for reuse | ||
| const AGENT_CACHE = new WeakMap(); | ||
| // can only convert agent to dispatcher option on node 18.2+ | ||
| const [major, minor] = versions.node.split('.').map(v => parseInt(v, 10)); | ||
| const canConvert = (major > 18) || (major === 18 && minor >= 2); | ||
| // converts `agent`/`httpsAgent` option to a dispatcher option | ||
| export function convertAgent(options) { | ||
| if(!canConvert) { | ||
| return options; | ||
| } | ||
| // do not override custom fetch function from another lib | ||
| if(options?.fetch && !options.fetch._httpClientCustomFetch) { | ||
| return options; | ||
| } | ||
| // only override if an agent option is present | ||
| const agent = options?.agent || options?.httpsAgent; | ||
| if(!agent) { | ||
| return options; | ||
| } | ||
| // use custom fetch if agent has already been converted | ||
| let fetch = AGENT_CACHE.get(agent); | ||
| if(!fetch) { | ||
| const dispatcher = new Agent({connect: agent.options}); | ||
| fetch = createFetch(dispatcher); | ||
| fetch._httpClientCustomFetch = true; | ||
| AGENT_CACHE.set(agent, fetch); | ||
| } | ||
| return {...options, fetch}; | ||
| } | ||
| // create fetch override uses custom `dispatcher`; since `ky` does not pass | ||
| // the dispatcher option through to `fetch`, we must use this override | ||
| function createFetch(dispatcher) { | ||
| return function fetch(...args) { | ||
| dispatcher = (args[1] && args[1].dispatcher) || dispatcher; | ||
| args[1] = {...args[1], dispatcher}; | ||
| // eslint-disable-next-line no-undef | ||
| return globalThis.fetch(...args); | ||
| }; | ||
| } |
| export function deferred(f) { | ||
| let promise; | ||
| return { | ||
| then( | ||
| onfulfilled, | ||
| onrejected | ||
| ) { | ||
| // Use logical OR assignment when Node.js 14.x support is dropped | ||
| //promise ||= new Promise(resolve => resolve(f())); | ||
| promise || (promise = new Promise(resolve => resolve(f()))); | ||
| return promise.then( | ||
| onfulfilled, | ||
| onrejected | ||
| ); | ||
| }, | ||
| }; | ||
| } |
| /*! | ||
| * Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| import {convertAgent} from './agentCompatibility.js'; | ||
| import {deferred} from './deferred.js'; | ||
| export const kyOriginalPromise = deferred(() => import('ky') | ||
| .then(({default: ky}) => ky)); | ||
| export const DEFAULT_HEADERS = { | ||
| Accept: 'application/ld+json, application/json' | ||
| }; | ||
| // methods to proxy from ky | ||
| const PROXY_METHODS = new Set([ | ||
| 'get', 'post', 'put', 'push', 'patch', 'head', 'delete' | ||
| ]); | ||
| /** | ||
| * Returns a custom httpClient instance. Used to specify default headers and | ||
| * other default overrides. | ||
| * | ||
| * @param {object} [options={}] - Options hashmap. | ||
| * @param {object} [options.parent] - The ky promise to inherit from. | ||
| * @param {object} [options.headers={}] - Default header overrides. | ||
| * @param {object} [options.params] - Other default overrides. | ||
| * | ||
| * @returns {Function} Custom httpClient instance. | ||
| */ | ||
| export function createInstance({ | ||
| parent = kyOriginalPromise, headers = {}, ...params | ||
| } = {}) { | ||
| // convert legacy agent options | ||
| params = convertAgent(params); | ||
| // create new ky instance that will asynchronously resolve | ||
| const kyPromise = deferred(() => parent.then(kyBase => { | ||
| let ky; | ||
| if(parent === kyOriginalPromise) { | ||
| // ensure default headers, allow overrides | ||
| ky = kyBase.create({ | ||
| headers: {...DEFAULT_HEADERS, ...headers}, | ||
| ...params | ||
| }); | ||
| } else { | ||
| // extend parent | ||
| ky = kyBase.extend({headers, ...params}); | ||
| } | ||
| return ky; | ||
| })); | ||
| return _createHttpClient(kyPromise); | ||
| } | ||
| function _createHttpClient(kyPromise) { | ||
| async function httpClient(...args) { | ||
| const ky = await kyPromise; | ||
| const method = ((args[1] && args[1].method) || 'get').toLowerCase(); | ||
| if(PROXY_METHODS.has(method)) { | ||
| return httpClient[method].apply(ky[method], args); | ||
| } | ||
| // convert legacy agent options | ||
| args[1] = convertAgent(args[1]); | ||
| return ky.apply(ky, args); | ||
| } | ||
| for(const method of PROXY_METHODS) { | ||
| httpClient[method] = async function(...args) { | ||
| const ky = await kyPromise; | ||
| return _handleResponse(ky[method], ky, args); | ||
| }; | ||
| } | ||
| httpClient.create = function({headers = {}, ...params}) { | ||
| return createInstance({headers, ...params}); | ||
| }; | ||
| httpClient.extend = function({headers = {}, ...params}) { | ||
| return createInstance({parent: kyPromise, headers, ...params}); | ||
| }; | ||
| // default async `stop` signal getter | ||
| Object.defineProperty(httpClient, 'stop', { | ||
| async get() { | ||
| const ky = await kyPromise; | ||
| return ky.stop; | ||
| } | ||
| }); | ||
| return httpClient; | ||
| } | ||
| async function _handleResponse(target, thisArg, args) { | ||
| // convert legacy agent options | ||
| args[1] = convertAgent(args[1]); | ||
| let response; | ||
| const [url] = args; | ||
| try { | ||
| response = await target.apply(thisArg, args); | ||
| } catch(error) { | ||
| return _handleError({error, url}); | ||
| } | ||
| const {parseBody = true} = args[1] || {}; | ||
| // always set 'data', default to undefined | ||
| let data; | ||
| if(parseBody) { | ||
| // a 204 will not include a content-type header | ||
| const contentType = response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| data = await response.json(); | ||
| } | ||
| } | ||
| Object.defineProperty(response, 'data', {value: data}); | ||
| return response; | ||
| } | ||
| /** | ||
| * @param {object} options - Options hashmap. | ||
| * @param {Error} options.error - Error thrown during http operation. | ||
| * @param {string} options.url - Target URL of the request. | ||
| * | ||
| * @returns {Promise} Rejects with a thrown error. | ||
| */ | ||
| async function _handleError({error, url}) { | ||
| error.requestUrl = url; | ||
| // handle network errors and system errors that do not have a response | ||
| if(!error.response) { | ||
| if(error.message === 'Failed to fetch') { | ||
| error.message = `Failed to fetch "${url}". Possible CORS error.`; | ||
| } | ||
| // ky's TimeoutError class | ||
| if(error.name === 'TimeoutError') { | ||
| error.message = `Request to "${url}" timed out.`; | ||
| } | ||
| throw error; | ||
| } | ||
| // always move status up to the root of error | ||
| error.status = error.response.status; | ||
| const contentType = error.response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| const errorBody = await error.response.json(); | ||
| // the HTTPError received from ky has a generic message based on status | ||
| // use that if the JSON body does not include a message | ||
| error.message = errorBody.message || error.message; | ||
| error.data = errorBody; | ||
| } | ||
| throw error; | ||
| } |
+1
-1
@@ -6,3 +6,3 @@ mkdir ./dist/esm | ||
| export const httpClient = cjsModule.httpClient; | ||
| export const ky = cjsModule.ky; | ||
| export const kyPromise = cjsModule.kyPromise; | ||
| !EOF | ||
@@ -9,0 +9,0 @@ |
| import cjsModule from '../index.js'; | ||
| export const DEFAULT_HEADERS = cjsModule.DEFAULT_HEADERS; | ||
| export const httpClient = cjsModule.httpClient; | ||
| export const ky = cjsModule.ky; | ||
| export const kyPromise = cjsModule.kyPromise; |
+5
-81
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var httpClient$1 = require('./httpClient.js'); | ||
| var kyOriginal = require('ky-universal'); | ||
| function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
| var kyOriginal__default = /*#__PURE__*/_interopDefaultLegacy(kyOriginal); | ||
| /*! | ||
| * Copyright (c) 2020 Digital Bazaar, Inc. All rights reserved. | ||
| * Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| const DEFAULT_HEADERS = { | ||
| Accept: 'application/ld+json, application/json' | ||
| }; | ||
| const httpClient = httpClient$1.createInstance(); | ||
| const ky = kyOriginal__default["default"].create({headers: DEFAULT_HEADERS}); | ||
| const proxyMethods = new Set([ | ||
| 'get', 'post', 'push', 'patch', 'head', 'delete' | ||
| ]); | ||
| const httpClient = new Proxy(ky, { | ||
| apply: _handleResponse, | ||
| get(target, propKey) { | ||
| const propValue = target[propKey]; | ||
| // only intercept particular methods | ||
| if(!proxyMethods.has(propKey)) { | ||
| return propValue; | ||
| } | ||
| return async function() { | ||
| return _handleResponse(propValue, this, arguments); | ||
| }; | ||
| } | ||
| }); | ||
| async function _handleResponse(target, thisArg, args) { | ||
| let response; | ||
| try { | ||
| response = await target.apply(thisArg, args); | ||
| } catch(e) { | ||
| return _handleError(e); | ||
| } | ||
| const {parseBody = true} = args[1] || {}; | ||
| if(parseBody) { | ||
| // a 204 will not include a content-type header | ||
| const contentType = response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| response.data = await response.json(); | ||
| } | ||
| } | ||
| return response; | ||
| } | ||
| async function _handleError(e) { | ||
| // handle network errors that do not have a response | ||
| if(!e.response) { | ||
| if(e.message === 'Failed to fetch') { | ||
| e.message = `${e.message}. Possible CORS error.`; | ||
| } | ||
| throw e; | ||
| } | ||
| // always move status up to the root of e | ||
| e.status = e.response.status; | ||
| const contentType = e.response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| const errorBody = await e.response.json(); | ||
| // the HTTPError received from ky has a generic message based on status | ||
| // use that if the JSON body does not include a message | ||
| e.message = errorBody.message || e.message; | ||
| e.data = errorBody; | ||
| } | ||
| throw e; | ||
| } | ||
| var index = { | ||
| httpClient, | ||
| ky: kyOriginal__default["default"], | ||
| DEFAULT_HEADERS, | ||
| }; | ||
| exports.DEFAULT_HEADERS = DEFAULT_HEADERS; | ||
| exports["default"] = index; | ||
| exports.DEFAULT_HEADERS = httpClient$1.DEFAULT_HEADERS; | ||
| exports.kyPromise = httpClient$1.kyOriginalPromise; | ||
| exports.httpClient = httpClient; |
+8
-72
| /*! | ||
| * Copyright (c) 2020 Digital Bazaar, Inc. All rights reserved. | ||
| * Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved. | ||
| */ | ||
| import kyOriginal from 'ky-universal'; | ||
| import { | ||
| createInstance, | ||
| DEFAULT_HEADERS, | ||
| kyOriginalPromise | ||
| } from './httpClient.js'; | ||
| export const DEFAULT_HEADERS = { | ||
| Accept: 'application/ld+json, application/json' | ||
| }; | ||
| export {kyOriginalPromise as kyPromise, DEFAULT_HEADERS}; | ||
| const ky = kyOriginal.create({headers: DEFAULT_HEADERS}); | ||
| const proxyMethods = new Set([ | ||
| 'get', 'post', 'push', 'patch', 'head', 'delete' | ||
| ]); | ||
| export const httpClient = new Proxy(ky, { | ||
| apply: _handleResponse, | ||
| get(target, propKey) { | ||
| const propValue = target[propKey]; | ||
| // only intercept particular methods | ||
| if(!proxyMethods.has(propKey)) { | ||
| return propValue; | ||
| } | ||
| return async function() { | ||
| return _handleResponse(propValue, this, arguments); | ||
| }; | ||
| } | ||
| }); | ||
| async function _handleResponse(target, thisArg, args) { | ||
| let response; | ||
| try { | ||
| response = await target.apply(thisArg, args); | ||
| } catch(e) { | ||
| return _handleError(e); | ||
| } | ||
| const {parseBody = true} = args[1] || {}; | ||
| if(parseBody) { | ||
| // a 204 will not include a content-type header | ||
| const contentType = response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| response.data = await response.json(); | ||
| } | ||
| } | ||
| return response; | ||
| } | ||
| async function _handleError(e) { | ||
| // handle network errors that do not have a response | ||
| if(!e.response) { | ||
| if(e.message === 'Failed to fetch') { | ||
| e.message = `${e.message}. Possible CORS error.`; | ||
| } | ||
| throw e; | ||
| } | ||
| // always move status up to the root of e | ||
| e.status = e.response.status; | ||
| const contentType = e.response.headers.get('content-type'); | ||
| if(contentType && contentType.includes('json')) { | ||
| const errorBody = await e.response.json(); | ||
| // the HTTPError received from ky has a generic message based on status | ||
| // use that if the JSON body does not include a message | ||
| e.message = errorBody.message || e.message; | ||
| e.data = errorBody; | ||
| } | ||
| throw e; | ||
| } | ||
| export default { | ||
| httpClient, | ||
| ky: kyOriginal, | ||
| DEFAULT_HEADERS, | ||
| }; | ||
| export const httpClient = createInstance(); |
+56
-40
| { | ||
| "name": "@digitalcredentials/http-client", | ||
| "version": "1.2.2", | ||
| "version": "5.0.0", | ||
| "description": "An opinionated, isomorphic HTTP client.", | ||
| "license": "BSD-3-Clause", | ||
| "files": [ | ||
| "dist", | ||
| "lib", | ||
| "rollup.config.js", | ||
| "build-dist.sh", | ||
| "README.md", | ||
| "LICENSE" | ||
| ], | ||
| "type": "module", | ||
| "main": "dist/index.js", | ||
@@ -23,43 +16,59 @@ "module": "dist/esm/index.js", | ||
| }, | ||
| "browser": { | ||
| "./lib/agentCompatibility.js": "./lib/agentCompatibility-browser.js", | ||
| "./tests/utils.cjs": "./tests/utils-browser.cjs" | ||
| }, | ||
| "react-native": { | ||
| "./lib/agentCompatibility.js": "./lib/agentCompatibility-browser.js" | ||
| }, | ||
| "scripts": { | ||
| "rollup": "rollup -c rollup.config.js", | ||
| "build": "npm run clear && npm run rollup && ./build-dist.sh", | ||
| "rollup": "rollup -c rollup.config.js && ./build-dist.sh", | ||
| "build": "npm run clear && npm run rollup", | ||
| "clear": "rimraf dist/ && mkdir dist", | ||
| "prepare": "npm run build", | ||
| "rebuild": "npm run clear && npm run build", | ||
| "test": "npm run lint && npm run test-node && npm run test-karma", | ||
| "test-node": "cross-env NODE_ENV=test mocha -r esm --preserve-symlinks -t 30000 -A -R ${REPORTER:-spec} --require test/test-mocha.js test/*.spec.js", | ||
| "test-karma": "karma start karma.conf.js", | ||
| "test-watch": "cross-env NODE_ENV=test mocha -r esm --watch --preserve-symlinks -t 30000 -A -R ${REPORTER:-spec} --require test/test-mocha.js test/*.spec.js", | ||
| "coverage-report": "nyc report", | ||
| "lint": "eslint ." | ||
| "test": "npm run test-node && npm run test-karma", | ||
| "test-node": "cross-env NODE_ENV=test mocha --preserve-symlinks -t 30000 -A -R ${REPORTER:-spec} --require tests/test-mocha.js tests/*.spec.js", | ||
| "test-karma": "karma start karma.conf.cjs", | ||
| "test-watch": "cross-env NODE_ENV=test mocha --watch --parallel --preserve-symlinks -t 30000 -A -R ${REPORTER:-spec} --require tests/test-mocha.js tests/*.spec.js", | ||
| "coverage": "cross-env NODE_ENV=test c8 npm run test-node", | ||
| "coverage-ci": "cross-env NODE_ENV=test c8 --reporter=lcovonly --reporter=text-summary --reporter=text npm run test-node", | ||
| "coverage-report": "c8 report", | ||
| "lint": "eslint --ignore-pattern rollup.config.js ." | ||
| }, | ||
| "files": [ | ||
| "lib/*", | ||
| "dist/*", | ||
| "rollup.config.js", | ||
| "build-dist.sh", | ||
| "README.md", | ||
| "LICENSE" | ||
| ], | ||
| "dependencies": { | ||
| "ky": "^0.25.1", | ||
| "ky-universal": "^0.8.2" | ||
| "ky": "^1.0.1", | ||
| "undici": "^6.6.2" | ||
| }, | ||
| "devDependencies": { | ||
| "chai": "^4.3.4", | ||
| "c8": "^7.13.0", | ||
| "chai": "^4.3.7", | ||
| "cors": "^2.8.5", | ||
| "cross-env": "^7.0.3", | ||
| "delay": "^5.0.0", | ||
| "detect-node": "^2.1.0", | ||
| "eslint": "^7.25.0", | ||
| "eslint-config-digitalbazaar": "^2.8.0", | ||
| "eslint-plugin-jsdoc": "^33.1.0", | ||
| "esm": "^3.2.25", | ||
| "karma": "^6.3.2", | ||
| "karma-babel-preprocessor": "^8.0.1", | ||
| "eslint": "^8.38.0", | ||
| "eslint-config-digitalbazaar": "^4.2.0", | ||
| "eslint-plugin-jsdoc": "^41.1.1", | ||
| "eslint-plugin-unicorn": "^46.0.0", | ||
| "express": "^4.18.2", | ||
| "karma": "^6.4.1", | ||
| "karma-chai": "^0.1.0", | ||
| "karma-chrome-launcher": "^3.1.0", | ||
| "karma-chrome-launcher": "^3.1.1", | ||
| "karma-mocha": "^2.0.1", | ||
| "karma-mocha-reporter": "^2.2.5", | ||
| "karma-sourcemap-loader": "^0.3.8", | ||
| "karma-sourcemap-loader": "^0.4.0", | ||
| "karma-webpack": "^5.0.0", | ||
| "mocha": "^8.3.2", | ||
| "mocha-lcov-reporter": "^1.3.0", | ||
| "multibase": "^4.0.4", | ||
| "multicodec": "^3.0.1", | ||
| "nyc": "^15.1.0", | ||
| "rimraf": "^3.0.2", | ||
| "rollup": "^2.47.0", | ||
| "webpack": "^5.36.2" | ||
| "mocha": "^10.2.0", | ||
| "rimraf": "^6.0.1", | ||
| "rollup": "^3.20.2", | ||
| "webpack": "^5.79.0" | ||
| }, | ||
@@ -75,2 +84,7 @@ "repository": { | ||
| ], | ||
| "author": { | ||
| "name": "Digital Bazaar, Inc.", | ||
| "email": "support@digitalbazaar.com", | ||
| "url": "https://digitalbazaar.com/" | ||
| }, | ||
| "bugs": { | ||
@@ -81,9 +95,11 @@ "url": "https://github.com/digitalcredentials/http-client/issues" | ||
| "engines": { | ||
| "node": ">=12.0.0" | ||
| "node": ">=18.0" | ||
| }, | ||
| "nyc": { | ||
| "exclude": [ | ||
| "tests" | ||
| "c8": { | ||
| "reporter": [ | ||
| "lcov", | ||
| "text-summary", | ||
| "text" | ||
| ] | ||
| } | ||
| } |
+37
-17
| # http-client | ||
| An opinionated, isomorphic HTTP client. | ||
| [](https://github.com/digitalcredentials/http-client/actions?query=workflow%3A%22Node.js+CI%22) | ||
| [](https://npm.im/@digitalcredentials/http-client) | ||
| > An opinionated, isomorphic HTTP client for Node.js, browsers, and React Native. | ||
| ### Usage | ||
| #### Import httpClient | ||
| #### Import httpClient (Node.js) | ||
| ```js | ||
| import https from 'https'; | ||
| import {httpClient} from '@digitalcredentials/http-client'; | ||
| ``` | ||
| #### Import httpClient (browsers or React Native) | ||
| ```js | ||
| import {httpClient} from '@digitalcredentials/http-client'; | ||
| ``` | ||
| #### Import and initialize a custom Bearer Token client | ||
| ```js | ||
| import {httpClient} from '@digitalcredentials/http-client'; | ||
| const httpsAgent = new https.Agent({rejectUnauthorized: false}); | ||
| const accessToken = '12345'; | ||
| const headers = {Authorization: `Bearer ${accessToken}`}; | ||
| const client = httpClient.extend({headers, httpsAgent}); | ||
| // subsequent http calls will include an 'Authorization: Bearer 12345' header, | ||
| // and use the provided httpsAgent | ||
| ``` | ||
| #### GET a JSON response in the browser | ||
| ```js | ||
| try { | ||
| result = await httpClient.get('http://httpbin.org/json'); | ||
| return result.data; | ||
| const response = await httpClient.get('http://httpbin.org/json'); | ||
| return response.data; | ||
| } catch(e) { | ||
@@ -27,3 +47,3 @@ // status is HTTP status code | ||
| #### GET a JSON response in Node with a HTTP Agent | ||
| #### GET a JSON response in Node with an HTTP Agent | ||
| ```js | ||
@@ -34,4 +54,4 @@ import https from 'https'; | ||
| try { | ||
| result = await httpClient.get('http://httpbin.org/json', {agent}); | ||
| return result.data; | ||
| const response = await httpClient.get('http://httpbin.org/json', {agent}); | ||
| return response.data; | ||
| } catch(e) { | ||
@@ -49,5 +69,5 @@ // status is HTTP status code | ||
| try { | ||
| result = await httpClient.get('http://httpbin.org/json', {headers}); | ||
| // see: https://developer.mozilla.org/en-US/docs/Web/API/Body#Methods | ||
| return result.response.text(); | ||
| const response = await httpClient.get('http://httpbin.org/html', {headers}); | ||
| // see: https://developer.mozilla.org/en-US/docs/Web/API/Response#methods | ||
| return response.text(); | ||
| } catch(e) { | ||
@@ -64,7 +84,7 @@ // status is HTTP status code | ||
| try { | ||
| result = await httpClient.post('http://httpbin.org/json', { | ||
| const response = await httpClient.post('http://httpbin.org/json', { | ||
| // `json` is the payload or body of the POST request | ||
| json: {some: 'data'} | ||
| }); | ||
| return result.data; | ||
| return response.data; | ||
| } catch(e) { | ||
@@ -78,3 +98,3 @@ // status is HTTP status code | ||
| #### POST a JSON payload in Node with a HTTP Agent | ||
| #### POST a JSON payload in Node with an HTTP Agent | ||
| ```js | ||
@@ -85,3 +105,3 @@ import https from 'https'; | ||
| try { | ||
| result = await httpClient.post('http://httpbin.org/json', { | ||
| const response = await httpClient.post('http://httpbin.org/json', { | ||
| agent, | ||
@@ -91,3 +111,3 @@ // `json` is the payload or body of the POST request | ||
| }); | ||
| return result.data; | ||
| return response.data; | ||
| } catch(e) { | ||
@@ -94,0 +114,0 @@ // status is HTTP status code |
+2
-1
@@ -1,2 +0,2 @@ | ||
| import pkg from './package.json'; | ||
| import pkg from './package.json' assert {type: 'json'}; | ||
@@ -9,2 +9,3 @@ export default [ | ||
| dir: 'dist', | ||
| format: 'cjs', | ||
@@ -11,0 +12,0 @@ preserveModules: true |
-27
| # @digitalcredentials/http-client ChangeLog | ||
| ## 1.2.1 - 2021-09-39 | ||
| ### Changed | ||
| - Remove `esm` runtime transpiler usage. | ||
| ## 1.2.0 - 2021-07-19 | ||
| ### Added | ||
| - Ensure that body parsing will occur for JSON content types | ||
| when individual method functions (e.g., `get`, `post`) are | ||
| not used (e.g., `httpClient(url, {method: 'get'}`). Body | ||
| parsing can be disabled by passing the `parseBody` option | ||
| set to `false`. | ||
| ## 1.1.0 - 2021-04-06 | ||
| ### Changed | ||
| - Updated `ky`, `ky-universal`, and `mocha` dependencies. | ||
| ## 1.0.0 - 2020-06-18 | ||
| ### Added | ||
| - Add core files. | ||
| - See git history for changes previous to this release. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
22471
85.68%22
-4.35%16
60%444
182.8%0
-100%113
21.51%Yes
NaN33
1550%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated