@lion/ajax
Advanced tools
Comparing version 0.5.9 to 0.5.10
# Change Log | ||
## 0.5.10 | ||
### Patch Changes | ||
- 6cc8b95c: Added types for ajax package, although they are mostly quite butchered. This is due to the complexity of interceptor factories and bundled-es-modules/axios not exporting types, which makes it really difficult to type it properly. | ||
- 1cb604c6: enable types for ajax | ||
## 0.5.9 | ||
@@ -4,0 +11,0 @@ |
{ | ||
"name": "@lion/ajax", | ||
"version": "0.5.9", | ||
"version": "0.5.10", | ||
"description": "Thin wrapper around axios to allow for custom interceptors", | ||
@@ -26,3 +26,3 @@ "license": "MIT", | ||
"scripts": { | ||
"debug": "cd ../../ && npm run -- --debug ajax", | ||
"debug": "cd ../../ && npm run debug -- --group ajax", | ||
"debug:firefox": "cd ../../ && npm run debug:firefox -- --group ajax", | ||
@@ -29,0 +29,0 @@ "debug:webkit": "cd ../../ && npm run debug:webkit -- --group ajax", |
@@ -5,3 +5,3 @@ import { singletonManager } from 'singleton-manager'; | ||
/** | ||
* @typedef {ajax} ajax the global instance for handling all ajax requests | ||
* | ||
*/ | ||
@@ -8,0 +8,0 @@ export let ajax = singletonManager.get('@lion/ajax::ajax::0.3.x') || new AjaxClass(); // eslint-disable-line import/no-mutable-exports |
@@ -0,1 +1,2 @@ | ||
// @ts-ignore no types for bundled-es-modules/axios | ||
import { axios } from '@bundled-es-modules/axios'; | ||
@@ -10,2 +11,17 @@ import { | ||
/** | ||
* @typedef {(config: {[key:string]: ?}) => { transformRequest: (data: string, headers: { [key: string]: any; }) => any;}} RequestInterceptor | ||
* @typedef {(config: {[key:string]: ?}) => Response} ResponseInterceptor | ||
* | ||
* @typedef {Object} AjaxConfig | ||
* @property {string} [jsonPrefix] prefixing the JSON string in this manner is used to help | ||
* prevent JSON Hijacking. The prefix renders the string syntactically invalid as a script so | ||
* that it cannot be hijacked. This prefix should be stripped before parsing the string as JSON. | ||
* @property {string} [lang] language | ||
* @property {boolean} [languageHeader] the Accept-Language request HTTP header advertises | ||
* which languages the client is able to understand, and which locale variant is preferred. | ||
* @property {boolean} [cancelable] if request can be canceled | ||
* @property {boolean} [cancelPreviousOnNewRequest] prevents concurrent requests | ||
*/ | ||
/** | ||
* `AjaxClass` creates the singleton instance {@link:ajax}. It is a promise based system for | ||
@@ -20,11 +36,3 @@ * fetching data, based on [axios](https://github.com/axios/axios). | ||
/** | ||
* @param {Object} config configuration for the AjaxClass instance | ||
* @param {string} config.jsonPrefix prefixing the JSON string in this manner is used to help | ||
* prevent JSON Hijacking. The prefix renders the string syntactically invalid as a script so | ||
* that it cannot be hijacked. This prefix should be stripped before parsing the string as JSON. | ||
* @param {string} config.lang language | ||
* @param {string} config.languageHeader the Accept-Language request HTTP header advertises | ||
* which languages the client is able to understand, and which locale variant is preferred. | ||
* @param {string} config.cancelable if request can be canceled | ||
* @param {string} config.cancelPreviousOnNewRequest prevents concurrent requests | ||
* @param {AjaxConfig} [config] configuration for the AjaxClass instance | ||
*/ | ||
@@ -39,21 +47,27 @@ constructor(config) { | ||
}; | ||
this.proxy = axios.create(this.__config); | ||
this.__setupInterceptors(); | ||
/** @type {Array.<RequestInterceptor>} */ | ||
this.requestInterceptors = []; | ||
/** @type {Array.<RequestInterceptor>} */ | ||
this.requestErrorInterceptors = []; | ||
/** @type {Array.<RequestInterceptor>} */ | ||
this.responseErrorInterceptors = []; | ||
/** @type {Array.<ResponseInterceptor>} */ | ||
this.responseInterceptors = []; | ||
/** @type {Array.<(data: string, headers?: {[key:string]: ?}) => string>} */ | ||
this.requestDataTransformers = []; | ||
/** @type {Array.<(data: string, headers?: {[key:string]: ?}) => string>} */ | ||
this.requestDataErrorTransformers = []; | ||
/** @type {Array.<(data: string, headers?: {[key:string]: ?}) => string>} */ | ||
this.responseDataErrorTransformers = []; | ||
/** @type {Array.<(data: string, headers?: {[key:string]: ?}) => string>} */ | ||
this.responseDataTransformers = []; | ||
this.responseDataErrorTransformers = []; | ||
this.responseInterceptors = []; | ||
this.responseErrorInterceptors = []; | ||
this.__isInterceptorsSetup = false; | ||
if (this.__config.languageHeader) { | ||
// @ts-ignore butchered something here.. | ||
this.requestInterceptors.push(addAcceptLanguageHeaderInterceptorFactory(this.__config.lang)); | ||
@@ -63,2 +77,3 @@ } | ||
if (this.__config.cancelable) { | ||
// @ts-ignore butchered something here.. | ||
this.requestInterceptors.push(cancelInterceptorFactory(this)); | ||
@@ -68,2 +83,3 @@ } | ||
if (this.__config.cancelPreviousOnNewRequest) { | ||
// @ts-ignore butchered something here.. | ||
this.requestInterceptors.push(cancelPreviousOnNewRequestInterceptorFactory()); | ||
@@ -80,4 +96,6 @@ } | ||
* Sets the config for the instance | ||
* @param {AjaxConfig} config configuration for the AjaxClass instance | ||
*/ | ||
set options(config) { | ||
// @ts-ignore butchered something here.. | ||
this.__config = config; | ||
@@ -87,2 +105,3 @@ } | ||
get options() { | ||
// @ts-ignore butchered something here.. | ||
return this.__config; | ||
@@ -94,4 +113,5 @@ } | ||
* @see https://github.com/axios/axios | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {string} url | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -102,7 +122,11 @@ request(url, config) { | ||
/** @param {string} msg */ | ||
// eslint-disable-next-line class-methods-use-this, no-unused-vars | ||
cancel(msg) {} | ||
/** | ||
* Dispatches a {@link AxiosRequestConfig} with method 'get' predefined | ||
* @param {string} url the endpoint location | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -116,4 +140,4 @@ get(url, config) { | ||
* @param {string} url the endpoint location | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -127,4 +151,4 @@ delete(url, config) { | ||
* @param {string} url the endpoint location | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -138,4 +162,4 @@ head(url, config) { | ||
* @param {string} url the endpoint location | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -149,5 +173,5 @@ // options(url, config) { | ||
* @param {string} url the endpoint location | ||
* @param {Object} data the data to be sent to the endpoint | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {Object} [data] the data to be sent to the endpoint | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -161,5 +185,5 @@ post(url, data, config) { | ||
* @param {string} url the endpoint location | ||
* @param {Object} data the data to be sent to the endpoint | ||
* @param {AxiosRequestConfig} config the config specific for this request | ||
* @returns {AxiosResponseSchema} | ||
* @param {Object} [data] the data to be sent to the endpoint | ||
* @param {{[key:string]: ?}} [config] the config specific for this request | ||
* @returns {?} | ||
*/ | ||
@@ -174,5 +198,5 @@ put(url, data, config) { | ||
* @param {string} url the endpoint location | ||
* @param {Object} data the data to be sent to the endpoint | ||
* @param {Object} config the config specific for this request. | ||
* @returns {AxiosResponseSchema} | ||
* @param {Object} [data] the data to be sent to the endpoint | ||
* @param {Object} [config] the config specific for this request. | ||
* @returns {?} | ||
*/ | ||
@@ -185,7 +209,8 @@ patch(url, data, config) { | ||
this.proxy.interceptors.request.use( | ||
config => { | ||
/** @param {{[key:string]: unknown}} config */ config => { | ||
const configWithTransformers = this.__setupTransformers(config); | ||
// @ts-ignore I dont know.... | ||
return this.requestInterceptors.reduce((c, i) => i(c), configWithTransformers); | ||
}, | ||
error => { | ||
/** @param {Error} error */ error => { | ||
this.requestErrorInterceptors.forEach(i => i(error)); | ||
@@ -197,4 +222,7 @@ return Promise.reject(error); | ||
this.proxy.interceptors.response.use( | ||
/** | ||
* @param {Response} response | ||
*/ | ||
response => this.responseInterceptors.reduce((r, i) => i(r), response), | ||
error => { | ||
/** @param {Error} error */ error => { | ||
this.responseErrorInterceptors.forEach(i => i(error)); | ||
@@ -206,2 +234,3 @@ return Promise.reject(error); | ||
/** @param {{[key:string]: ?}} config */ | ||
__setupTransformers(config) { | ||
@@ -212,2 +241,6 @@ const axiosTransformRequest = config.transformRequest[0]; | ||
...config, | ||
/** | ||
* @param {string} data | ||
* @param {{[key:string]: ?}} headers | ||
*/ | ||
transformRequest: (data, headers) => { | ||
@@ -224,2 +257,5 @@ try { | ||
}, | ||
/** | ||
* @param {string} data | ||
*/ | ||
transformResponse: data => { | ||
@@ -226,0 +262,0 @@ try { |
@@ -0,5 +1,12 @@ | ||
// @ts-ignore no types for bundled-es-modules/axios | ||
import { axios } from '@bundled-es-modules/axios'; | ||
/** | ||
* @param {string} [lang] | ||
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}} | ||
*/ | ||
export function addAcceptLanguageHeaderInterceptorFactory(lang) { | ||
return config => { | ||
console.log('add language header'); | ||
console.log(lang); | ||
return /** @param {{[key:string]: ?}} config */ config => { | ||
const result = config; | ||
@@ -17,5 +24,10 @@ if (typeof lang === 'string' && lang !== '') { | ||
/** | ||
* @param {import('./AjaxClass').AjaxClass} ajaxInstance | ||
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}} | ||
*/ | ||
export function cancelInterceptorFactory(ajaxInstance) { | ||
/** @type {unknown[]} */ | ||
const cancelSources = []; | ||
return config => { | ||
return /** @param {{[key:string]: ?}} config */ config => { | ||
const source = axios.CancelToken.source(); | ||
@@ -25,2 +37,3 @@ cancelSources.push(source); | ||
ajaxInstance.cancel = (message = 'Operation canceled by the user.') => { | ||
// @ts-ignore axios is untyped so we don't know the type for the source | ||
cancelSources.forEach(s => s.cancel(message)); | ||
@@ -32,6 +45,12 @@ }; | ||
/** | ||
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}} | ||
*/ | ||
export function cancelPreviousOnNewRequestInterceptorFactory() { | ||
// @ts-ignore axios is untyped so we don't know the type for the source | ||
let prevCancelSource; | ||
return config => { | ||
return /** @param {{[key:string]: ?}} config */ config => { | ||
// @ts-ignore axios is untyped so we don't know the type for the source | ||
if (prevCancelSource) { | ||
// @ts-ignore | ||
prevCancelSource.cancel('Concurrent requests not allowed.'); | ||
@@ -38,0 +57,0 @@ } |
@@ -0,3 +1,6 @@ | ||
/** | ||
* @param {string} prefix | ||
*/ | ||
export function jsonPrefixTransformerFactory(prefix) { | ||
return data => { | ||
return /** @param {string} data */ data => { | ||
let result = data; | ||
@@ -4,0 +7,0 @@ if (typeof result === 'string') { |
@@ -7,2 +7,3 @@ import { expect } from '@open-wc/testing'; | ||
describe('ajax', () => { | ||
/** @type {import('sinon').SinonFakeServer} */ | ||
let server; | ||
@@ -36,3 +37,3 @@ | ||
]); | ||
const makeRequest = async method => { | ||
const makeRequest = /** @param {string} method */ async method => { | ||
const response = await ajax[method]('data.json', { foo: 'bar' }); | ||
@@ -51,3 +52,3 @@ expect(response.status).to.equal(200); | ||
]); | ||
const makeRequest = async method => { | ||
const makeRequest = /** @param {string} method */ async method => { | ||
const response = await ajax[method]('data.json', { data: 'foobar' }, { foo: 'bar' }); | ||
@@ -68,3 +69,3 @@ expect(response.status).to.equal(200); | ||
const makeRequest = async method => { | ||
const makeRequest = /** @param {string} method */ async method => { | ||
const response = await ajax[method]('data.json'); | ||
@@ -89,3 +90,3 @@ expect(response.config.headers['X-XSRF-TOKEN']).to.equal('test'); | ||
const makeRequest = async method => { | ||
const makeRequest = /** @param {string} method */ async method => { | ||
const response = await ajax[method]('data.json'); | ||
@@ -92,0 +93,0 @@ expect(response.config.headers['X-XSRF-TOKEN']).to.equal(undefined); |
@@ -7,4 +7,16 @@ import { expect } from '@open-wc/testing'; | ||
describe('AjaxClass interceptors', () => { | ||
/** @type {import('sinon').SinonFakeServer} */ | ||
let server; | ||
/** | ||
* @param {Object} [cfg] configuration for the AjaxClass instance | ||
* @param {string} cfg.jsonPrefix prefixing the JSON string in this manner is used to help | ||
* prevent JSON Hijacking. The prefix renders the string syntactically invalid as a script so | ||
* that it cannot be hijacked. This prefix should be stripped before parsing the string as JSON. | ||
* @param {string} cfg.lang language | ||
* @param {boolean} cfg.languageHeader the Accept-Language request HTTP header advertises | ||
* which languages the client is able to understand, and which locale variant is preferred. | ||
* @param {boolean} cfg.cancelable if request can be canceled | ||
* @param {boolean} cfg.cancelPreviousOnNewRequest prevents concurrent requests | ||
*/ | ||
function getInstance(cfg) { | ||
@@ -66,3 +78,3 @@ return new AjaxClass(cfg); | ||
ajax[type] = ajax[type].filter(item => item !== myInterceptor); | ||
ajax[type] = ajax[type].filter(/** @param {?} item */ item => item !== myInterceptor); | ||
await ajax.get('data.json'); | ||
@@ -78,8 +90,10 @@ | ||
const ajax = getInstance(); | ||
// @ts-ignore setting a prop that isn't existing on options | ||
ajax.options.myCustomValue = 'foo'; | ||
let customValueAccess = false; | ||
const myInterceptor = config => { | ||
const myInterceptor = /** @param {{[key: string]: ?}} config */ config => { | ||
customValueAccess = config.myCustomValue === 'foo'; | ||
return config; | ||
}; | ||
// @ts-ignore butchered something here.. | ||
ajax.requestInterceptors.push(myInterceptor); | ||
@@ -103,4 +117,8 @@ await ajax.get('data.json'); | ||
]); | ||
const enforcePutInterceptor = config => ({ ...config, method: 'PUT' }); | ||
const enforcePutInterceptor = /** @param {{[key: string]: ?}} config */ config => ({ | ||
...config, | ||
method: 'PUT', | ||
}); | ||
const myAjax = getInstance(); | ||
// @ts-ignore butchered something here.. | ||
myAjax.requestInterceptors.push(enforcePutInterceptor); | ||
@@ -119,7 +137,8 @@ const response = await myAjax.post('data.json'); | ||
]); | ||
const addDataInterceptor = response => ({ | ||
...response, | ||
data: { ...response.data, foo: 'bar' }, | ||
const addDataInterceptor = /** @param {{[key: string]: ?}} config */ config => ({ | ||
...config, | ||
data: { ...config.data, foo: 'bar' }, | ||
}); | ||
const myAjax = getInstance(); | ||
// @ts-ignore I probably butchered the types here or adding data like above is simply not allowed in Response objects | ||
myAjax.responseInterceptors.push(addDataInterceptor); | ||
@@ -126,0 +145,0 @@ const response = await myAjax.get('data.json'); |
@@ -7,3 +7,5 @@ import { expect, aTimeout } from '@open-wc/testing'; | ||
describe('AjaxClass languages', () => { | ||
/** @type {import('sinon').SinonFakeXMLHttpRequestStatic} */ | ||
let fakeXhr; | ||
/** @type {import('sinon').SinonFakeXMLHttpRequest[]} */ | ||
let requests; | ||
@@ -28,3 +30,3 @@ | ||
req.get('data.json'); | ||
await aTimeout(); | ||
await aTimeout(0); | ||
expect(requests.length).to.equal(1); | ||
@@ -41,3 +43,3 @@ expect(requests[0].requestHeaders['Accept-Language']).to.equal('en-GB'); | ||
req.delete('data4.json'); | ||
await aTimeout(); | ||
await aTimeout(0); | ||
expect(requests.length).to.equal(4); | ||
@@ -53,3 +55,3 @@ requests.forEach(request => { | ||
req.get('data.json'); | ||
await aTimeout(); | ||
await aTimeout(0); | ||
expect(requests.length).to.equal(1); | ||
@@ -66,3 +68,3 @@ expect(requests[0].requestHeaders['Accept-Language']).to.equal('nl-NL'); | ||
req.delete('data4.json'); | ||
await aTimeout(); | ||
await aTimeout(0); | ||
expect(requests.length).to.equal(4); | ||
@@ -78,3 +80,3 @@ requests.forEach(request => { | ||
req.get('data.json'); | ||
await aTimeout(); | ||
await aTimeout(0); | ||
expect(requests.length).to.equal(1); | ||
@@ -81,0 +83,0 @@ expect(requests[0].requestHeaders['Accept-Language']).to.equal(undefined); |
@@ -8,4 +8,16 @@ import { expect } from '@open-wc/testing'; | ||
describe('AjaxClass', () => { | ||
/** @type {import('sinon').SinonFakeServer} */ | ||
let server; | ||
/** | ||
* @param {Object} [cfg] configuration for the AjaxClass instance | ||
* @param {string} [cfg.jsonPrefix] prefixing the JSON string in this manner is used to help | ||
* prevent JSON Hijacking. The prefix renders the string syntactically invalid as a script so | ||
* that it cannot be hijacked. This prefix should be stripped before parsing the string as JSON. | ||
* @param {string} [cfg.lang] language | ||
* @param {boolean} [cfg.languageHeader] the Accept-Language request HTTP header advertises | ||
* which languages the client is able to understand, and which locale variant is preferred. | ||
* @param {boolean} [cfg.cancelable] if request can be canceled | ||
* @param {boolean} [cfg.cancelPreviousOnNewRequest] prevents concurrent requests | ||
*/ | ||
function getInstance(cfg) { | ||
@@ -204,3 +216,3 @@ return new AjaxClass(cfg); | ||
const makeRequest = async url => { | ||
const makeRequest = /** @param {string} url */ async url => { | ||
try { | ||
@@ -207,0 +219,0 @@ await myAjax.get(url); |
@@ -7,4 +7,16 @@ import { expect } from '@open-wc/testing'; | ||
describe('AjaxClass transformers', () => { | ||
/** @type {import('sinon').SinonFakeServer} */ | ||
let server; | ||
/** | ||
* @param {Object} [cfg] configuration for the AjaxClass instance | ||
* @param {string} [cfg.jsonPrefix] prefixing the JSON string in this manner is used to help | ||
* prevent JSON Hijacking. The prefix renders the string syntactically invalid as a script so | ||
* that it cannot be hijacked. This prefix should be stripped before parsing the string as JSON. | ||
* @param {string} [cfg.lang] language | ||
* @param {boolean} [cfg.languageHeader] the Accept-Language request HTTP header advertises | ||
* which languages the client is able to understand, and which locale variant is preferred. | ||
* @param {boolean} [cfg.cancelable] if request can be canceled | ||
* @param {boolean} [cfg.cancelPreviousOnNewRequest] prevents concurrent requests | ||
*/ | ||
function getInstance(cfg) { | ||
@@ -66,3 +78,3 @@ return new AjaxClass(cfg); | ||
ajax[type] = ajax[type].filter(item => item !== myTransformer); | ||
ajax[type] = ajax[type].filter(/** @param {?} item */ item => item !== myTransformer); | ||
await ajax.get('data.json'); | ||
@@ -83,3 +95,3 @@ | ||
]); | ||
const addBarTransformer = data => ({ ...data, bar: 'bar' }); | ||
const addBarTransformer = /** @param {?} data */ data => ({ ...data, bar: 'bar' }); | ||
const myAjax = getInstance(); | ||
@@ -102,3 +114,3 @@ myAjax.requestDataTransformers.push(addBarTransformer); | ||
]); | ||
const addBarTransformer = data => ({ ...data, bar: 'bar' }); | ||
const addBarTransformer = /** @param {?} data */ data => ({ ...data, bar: 'bar' }); | ||
const myAjax = getInstance(); | ||
@@ -105,0 +117,0 @@ myAjax.responseDataTransformers.push(addBarTransformer); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
64299
24
1290