feat: add types for ajax
This commit is contained in:
parent
acbd1aa01f
commit
6cc8b95c2d
12 changed files with 176 additions and 66 deletions
5
.changeset/heavy-actors-retire.md
Normal file
5
.changeset/heavy-actors-retire.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/ajax': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"debug": "cd ../../ && npm run -- --debug ajax",
|
"debug": "cd ../../ && npm run debug -- --group ajax",
|
||||||
"debug:firefox": "cd ../../ && npm run debug:firefox -- --group ajax",
|
"debug:firefox": "cd ../../ && npm run debug:firefox -- --group ajax",
|
||||||
"debug:webkit": "cd ../../ && npm run debug:webkit -- --group ajax",
|
"debug:webkit": "cd ../../ && npm run debug:webkit -- --group ajax",
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-ignore no types for bundled-es-modules/axios
|
||||||
import { axios } from '@bundled-es-modules/axios';
|
import { axios } from '@bundled-es-modules/axios';
|
||||||
import {
|
import {
|
||||||
cancelInterceptorFactory,
|
cancelInterceptorFactory,
|
||||||
|
|
@ -6,6 +7,21 @@ import {
|
||||||
} from './interceptors.js';
|
} from './interceptors.js';
|
||||||
import { jsonPrefixTransformerFactory } from './transformers.js';
|
import { jsonPrefixTransformerFactory } from './transformers.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
* `AjaxClass` creates the singleton instance {@link:ajax}. It is a promise based system for
|
||||||
* fetching data, based on [axios](https://github.com/axios/axios).
|
* fetching data, based on [axios](https://github.com/axios/axios).
|
||||||
|
|
@ -16,15 +32,7 @@ export class AjaxClass {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} config configuration for the AjaxClass instance
|
* @param {AjaxConfig} [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
|
|
||||||
*/
|
*/
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
this.__config = {
|
this.__config = {
|
||||||
|
|
@ -34,33 +42,41 @@ export class AjaxClass {
|
||||||
cancelPreviousOnNewRequest: false,
|
cancelPreviousOnNewRequest: false,
|
||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.proxy = axios.create(this.__config);
|
this.proxy = axios.create(this.__config);
|
||||||
this.__setupInterceptors();
|
this.__setupInterceptors();
|
||||||
|
|
||||||
|
/** @type {Array.<RequestInterceptor>} */
|
||||||
this.requestInterceptors = [];
|
this.requestInterceptors = [];
|
||||||
|
/** @type {Array.<RequestInterceptor>} */
|
||||||
this.requestErrorInterceptors = [];
|
this.requestErrorInterceptors = [];
|
||||||
|
/** @type {Array.<RequestInterceptor>} */
|
||||||
this.requestDataTransformers = [];
|
|
||||||
this.requestDataErrorTransformers = [];
|
|
||||||
|
|
||||||
this.responseDataTransformers = [];
|
|
||||||
this.responseDataErrorTransformers = [];
|
|
||||||
|
|
||||||
this.responseInterceptors = [];
|
|
||||||
this.responseErrorInterceptors = [];
|
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.__isInterceptorsSetup = false;
|
this.__isInterceptorsSetup = false;
|
||||||
|
|
||||||
if (this.__config.languageHeader) {
|
if (this.__config.languageHeader) {
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
this.requestInterceptors.push(addAcceptLanguageHeaderInterceptorFactory(this.__config.lang));
|
this.requestInterceptors.push(addAcceptLanguageHeaderInterceptorFactory(this.__config.lang));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.__config.cancelable) {
|
if (this.__config.cancelable) {
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
this.requestInterceptors.push(cancelInterceptorFactory(this));
|
this.requestInterceptors.push(cancelInterceptorFactory(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.__config.cancelPreviousOnNewRequest) {
|
if (this.__config.cancelPreviousOnNewRequest) {
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
this.requestInterceptors.push(cancelPreviousOnNewRequestInterceptorFactory());
|
this.requestInterceptors.push(cancelPreviousOnNewRequestInterceptorFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,30 +88,38 @@ export class AjaxClass {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the config for the instance
|
* Sets the config for the instance
|
||||||
|
* @param {AjaxConfig} config configuration for the AjaxClass instance
|
||||||
*/
|
*/
|
||||||
set options(config) {
|
set options(config) {
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
this.__config = config;
|
this.__config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
get options() {
|
get options() {
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
return this.__config;
|
return this.__config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches a request
|
* Dispatches a request
|
||||||
* @see https://github.com/axios/axios
|
* @see https://github.com/axios/axios
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {string} url
|
||||||
* @returns {AxiosResponseSchema}
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
request(url, config) {
|
request(url, config) {
|
||||||
return this.proxy.request.apply(this, [url, { ...this.__config, ...config }]);
|
return this.proxy.request.apply(this, [url, { ...this.__config, ...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
|
* Dispatches a {@link AxiosRequestConfig} with method 'get' predefined
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
get(url, config) {
|
get(url, config) {
|
||||||
return this.proxy.get.apply(this, [url, { ...this.__config, ...config }]);
|
return this.proxy.get.apply(this, [url, { ...this.__config, ...config }]);
|
||||||
|
|
@ -104,8 +128,8 @@ export class AjaxClass {
|
||||||
/**
|
/**
|
||||||
* Dispatches a {@link AxiosRequestConfig} with method 'delete' predefined
|
* Dispatches a {@link AxiosRequestConfig} with method 'delete' predefined
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
delete(url, config) {
|
delete(url, config) {
|
||||||
return this.proxy.delete.apply(this, [url, { ...this.__config, ...config }]);
|
return this.proxy.delete.apply(this, [url, { ...this.__config, ...config }]);
|
||||||
|
|
@ -114,8 +138,8 @@ export class AjaxClass {
|
||||||
/**
|
/**
|
||||||
* Dispatches a {@link AxiosRequestConfig} with method 'head' predefined
|
* Dispatches a {@link AxiosRequestConfig} with method 'head' predefined
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
head(url, config) {
|
head(url, config) {
|
||||||
return this.proxy.head.apply(this, [url, { ...this.__config, ...config }]);
|
return this.proxy.head.apply(this, [url, { ...this.__config, ...config }]);
|
||||||
|
|
@ -124,8 +148,8 @@ export class AjaxClass {
|
||||||
/**
|
/**
|
||||||
* Dispatches a {@link AxiosRequestConfig} with method 'options' predefined
|
* Dispatches a {@link AxiosRequestConfig} with method 'options' predefined
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
// options(url, config) {
|
// options(url, config) {
|
||||||
// return this.proxy.options.apply(this, [url, { ...this.__config, ...config }]);
|
// return this.proxy.options.apply(this, [url, { ...this.__config, ...config }]);
|
||||||
|
|
@ -134,9 +158,9 @@ export class AjaxClass {
|
||||||
/**
|
/**
|
||||||
* Dispatches a {@link AxiosRequestConfig} with method 'post' predefined
|
* Dispatches a {@link AxiosRequestConfig} with method 'post' predefined
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {Object} data the data to be sent to the endpoint
|
* @param {Object} [data] the data to be sent to the endpoint
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
post(url, data, config) {
|
post(url, data, config) {
|
||||||
return this.proxy.post.apply(this, [url, data, { ...this.__config, ...config }]);
|
return this.proxy.post.apply(this, [url, data, { ...this.__config, ...config }]);
|
||||||
|
|
@ -145,9 +169,9 @@ export class AjaxClass {
|
||||||
/**
|
/**
|
||||||
* Dispatches a {@link AxiosRequestConfig} with method 'put' predefined
|
* Dispatches a {@link AxiosRequestConfig} with method 'put' predefined
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {Object} data the data to be sent to the endpoint
|
* @param {Object} [data] the data to be sent to the endpoint
|
||||||
* @param {AxiosRequestConfig} config the config specific for this request
|
* @param {{[key:string]: ?}} [config] the config specific for this request
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
put(url, data, config) {
|
put(url, data, config) {
|
||||||
return this.proxy.put.apply(this, [url, data, { ...this.__config, ...config }]);
|
return this.proxy.put.apply(this, [url, data, { ...this.__config, ...config }]);
|
||||||
|
|
@ -157,9 +181,9 @@ export class AjaxClass {
|
||||||
* Dispatches a {@link AxiosRequestConfig} with method 'patch' predefined
|
* Dispatches a {@link AxiosRequestConfig} with method 'patch' predefined
|
||||||
* @see https://github.com/axios/axios (Request Config)
|
* @see https://github.com/axios/axios (Request Config)
|
||||||
* @param {string} url the endpoint location
|
* @param {string} url the endpoint location
|
||||||
* @param {Object} data the data to be sent to the endpoint
|
* @param {Object} [data] the data to be sent to the endpoint
|
||||||
* @param {Object} config the config specific for this request.
|
* @param {Object} [config] the config specific for this request.
|
||||||
* @returns {AxiosResponseSchema}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
patch(url, data, config) {
|
patch(url, data, config) {
|
||||||
return this.proxy.patch.apply(this, [url, data, { ...this.__config, ...config }]);
|
return this.proxy.patch.apply(this, [url, data, { ...this.__config, ...config }]);
|
||||||
|
|
@ -167,30 +191,39 @@ export class AjaxClass {
|
||||||
|
|
||||||
__setupInterceptors() {
|
__setupInterceptors() {
|
||||||
this.proxy.interceptors.request.use(
|
this.proxy.interceptors.request.use(
|
||||||
config => {
|
/** @param {{[key:string]: unknown}} config */ config => {
|
||||||
const configWithTransformers = this.__setupTransformers(config);
|
const configWithTransformers = this.__setupTransformers(config);
|
||||||
|
// @ts-ignore I dont know....
|
||||||
return this.requestInterceptors.reduce((c, i) => i(c), configWithTransformers);
|
return this.requestInterceptors.reduce((c, i) => i(c), configWithTransformers);
|
||||||
},
|
},
|
||||||
error => {
|
/** @param {Error} error */ error => {
|
||||||
this.requestErrorInterceptors.forEach(i => i(error));
|
this.requestErrorInterceptors.forEach(i => i(error));
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.proxy.interceptors.response.use(
|
this.proxy.interceptors.response.use(
|
||||||
|
/**
|
||||||
|
* @param {Response} response
|
||||||
|
*/
|
||||||
response => this.responseInterceptors.reduce((r, i) => i(r), response),
|
response => this.responseInterceptors.reduce((r, i) => i(r), response),
|
||||||
error => {
|
/** @param {Error} error */ error => {
|
||||||
this.responseErrorInterceptors.forEach(i => i(error));
|
this.responseErrorInterceptors.forEach(i => i(error));
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {{[key:string]: ?}} config */
|
||||||
__setupTransformers(config) {
|
__setupTransformers(config) {
|
||||||
const axiosTransformRequest = config.transformRequest[0];
|
const axiosTransformRequest = config.transformRequest[0];
|
||||||
const axiosTransformResponse = config.transformResponse[0];
|
const axiosTransformResponse = config.transformResponse[0];
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
|
/**
|
||||||
|
* @param {string} data
|
||||||
|
* @param {{[key:string]: ?}} headers
|
||||||
|
*/
|
||||||
transformRequest: (data, headers) => {
|
transformRequest: (data, headers) => {
|
||||||
try {
|
try {
|
||||||
const ourData = this.requestDataTransformers.reduce((d, t) => t(d, headers), data);
|
const ourData = this.requestDataTransformers.reduce((d, t) => t(d, headers), data);
|
||||||
|
|
@ -202,6 +235,9 @@ export class AjaxClass {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @param {string} data
|
||||||
|
*/
|
||||||
transformResponse: data => {
|
transformResponse: data => {
|
||||||
try {
|
try {
|
||||||
// axios does a lot of smart things with the response that people rely on
|
// axios does a lot of smart things with the response that people rely on
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { singletonManager } from 'singleton-manager';
|
||||||
import { AjaxClass } from './AjaxClass.js';
|
import { AjaxClass } from './AjaxClass.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {ajax} ajax the global instance for handling all ajax requests
|
*
|
||||||
*/
|
*/
|
||||||
export let ajax = singletonManager.get('@lion/ajax::ajax::0.3.x') || new AjaxClass(); // eslint-disable-line import/no-mutable-exports
|
export let ajax = singletonManager.get('@lion/ajax::ajax::0.3.x') || new AjaxClass(); // eslint-disable-line import/no-mutable-exports
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
|
// @ts-ignore no types for bundled-es-modules/axios
|
||||||
import { axios } from '@bundled-es-modules/axios';
|
import { axios } from '@bundled-es-modules/axios';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} [lang]
|
||||||
|
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
|
||||||
|
*/
|
||||||
export function addAcceptLanguageHeaderInterceptorFactory(lang) {
|
export function addAcceptLanguageHeaderInterceptorFactory(lang) {
|
||||||
return config => {
|
console.log('add language header');
|
||||||
|
console.log(lang);
|
||||||
|
return /** @param {{[key:string]: ?}} config */ config => {
|
||||||
const result = config;
|
const result = config;
|
||||||
if (typeof lang === 'string' && lang !== '') {
|
if (typeof lang === 'string' && lang !== '') {
|
||||||
if (typeof result.headers !== 'object') {
|
if (typeof result.headers !== 'object') {
|
||||||
|
|
@ -14,23 +21,35 @@ export function addAcceptLanguageHeaderInterceptorFactory(lang) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./AjaxClass').AjaxClass} ajaxInstance
|
||||||
|
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
|
||||||
|
*/
|
||||||
export function cancelInterceptorFactory(ajaxInstance) {
|
export function cancelInterceptorFactory(ajaxInstance) {
|
||||||
|
/** @type {unknown[]} */
|
||||||
const cancelSources = [];
|
const cancelSources = [];
|
||||||
return config => {
|
return /** @param {{[key:string]: ?}} config */ config => {
|
||||||
const source = axios.CancelToken.source();
|
const source = axios.CancelToken.source();
|
||||||
cancelSources.push(source);
|
cancelSources.push(source);
|
||||||
/* eslint-disable-next-line no-param-reassign */
|
/* eslint-disable-next-line no-param-reassign */
|
||||||
ajaxInstance.cancel = (message = 'Operation canceled by the user.') => {
|
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));
|
cancelSources.forEach(s => s.cancel(message));
|
||||||
};
|
};
|
||||||
return { ...config, cancelToken: source.token };
|
return { ...config, cancelToken: source.token };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
|
||||||
|
*/
|
||||||
export function cancelPreviousOnNewRequestInterceptorFactory() {
|
export function cancelPreviousOnNewRequestInterceptorFactory() {
|
||||||
|
// @ts-ignore axios is untyped so we don't know the type for the source
|
||||||
let prevCancelSource;
|
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) {
|
if (prevCancelSource) {
|
||||||
|
// @ts-ignore
|
||||||
prevCancelSource.cancel('Concurrent requests not allowed.');
|
prevCancelSource.cancel('Concurrent requests not allowed.');
|
||||||
}
|
}
|
||||||
const source = axios.CancelToken.source();
|
const source = axios.CancelToken.source();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
/**
|
||||||
|
* @param {string} prefix
|
||||||
|
*/
|
||||||
export function jsonPrefixTransformerFactory(prefix) {
|
export function jsonPrefixTransformerFactory(prefix) {
|
||||||
return data => {
|
return /** @param {string} data */ data => {
|
||||||
let result = data;
|
let result = data;
|
||||||
if (typeof result === 'string') {
|
if (typeof result === 'string') {
|
||||||
if (prefix.length > 0 && result.indexOf(prefix) === 0) {
|
if (prefix.length > 0 && result.indexOf(prefix) === 0) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,20 @@ import sinon from 'sinon';
|
||||||
import { AjaxClass } from '../src/AjaxClass.js';
|
import { AjaxClass } from '../src/AjaxClass.js';
|
||||||
|
|
||||||
describe('AjaxClass interceptors', () => {
|
describe('AjaxClass interceptors', () => {
|
||||||
|
/** @type {import('sinon').SinonFakeServer} */
|
||||||
let server;
|
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) {
|
function getInstance(cfg) {
|
||||||
return new AjaxClass(cfg);
|
return new AjaxClass(cfg);
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +74,7 @@ describe('AjaxClass interceptors', () => {
|
||||||
ajax[type].push(myInterceptor);
|
ajax[type].push(myInterceptor);
|
||||||
await ajax.get('data.json');
|
await ajax.get('data.json');
|
||||||
|
|
||||||
ajax[type] = ajax[type].filter(item => item !== myInterceptor);
|
ajax[type] = ajax[type].filter(/** @param {?} item */ item => item !== myInterceptor);
|
||||||
await ajax.get('data.json');
|
await ajax.get('data.json');
|
||||||
|
|
||||||
expect(myInterceptor.callCount).to.eql(1);
|
expect(myInterceptor.callCount).to.eql(1);
|
||||||
|
|
@ -73,12 +85,14 @@ describe('AjaxClass interceptors', () => {
|
||||||
it('has access to provided instance config(options) on requestInterceptors', async () => {
|
it('has access to provided instance config(options) on requestInterceptors', async () => {
|
||||||
server.respondWith('GET', 'data.json', [200, { 'Content-Type': 'application/json' }, '{}']);
|
server.respondWith('GET', 'data.json', [200, { 'Content-Type': 'application/json' }, '{}']);
|
||||||
const ajax = getInstance();
|
const ajax = getInstance();
|
||||||
|
// @ts-ignore setting a prop that isn't existing on options
|
||||||
ajax.options.myCustomValue = 'foo';
|
ajax.options.myCustomValue = 'foo';
|
||||||
let customValueAccess = false;
|
let customValueAccess = false;
|
||||||
const myInterceptor = config => {
|
const myInterceptor = /** @param {{[key: string]: ?}} config */ config => {
|
||||||
customValueAccess = config.myCustomValue === 'foo';
|
customValueAccess = config.myCustomValue === 'foo';
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
ajax.requestInterceptors.push(myInterceptor);
|
ajax.requestInterceptors.push(myInterceptor);
|
||||||
await ajax.get('data.json');
|
await ajax.get('data.json');
|
||||||
expect(customValueAccess).to.eql(true);
|
expect(customValueAccess).to.eql(true);
|
||||||
|
|
@ -97,8 +111,12 @@ describe('AjaxClass interceptors', () => {
|
||||||
{ 'Content-Type': 'application/json' },
|
{ 'Content-Type': 'application/json' },
|
||||||
'{ "method": "put" }',
|
'{ "method": "put" }',
|
||||||
]);
|
]);
|
||||||
const enforcePutInterceptor = config => ({ ...config, method: 'PUT' });
|
const enforcePutInterceptor = /** @param {{[key: string]: ?}} config */ config => ({
|
||||||
|
...config,
|
||||||
|
method: 'PUT',
|
||||||
|
});
|
||||||
const myAjax = getInstance();
|
const myAjax = getInstance();
|
||||||
|
// @ts-ignore butchered something here..
|
||||||
myAjax.requestInterceptors.push(enforcePutInterceptor);
|
myAjax.requestInterceptors.push(enforcePutInterceptor);
|
||||||
const response = await myAjax.post('data.json');
|
const response = await myAjax.post('data.json');
|
||||||
expect(response.data).to.deep.equal({ method: 'put' });
|
expect(response.data).to.deep.equal({ method: 'put' });
|
||||||
|
|
@ -112,11 +130,12 @@ describe('AjaxClass interceptors', () => {
|
||||||
{ 'Content-Type': 'application/json' },
|
{ 'Content-Type': 'application/json' },
|
||||||
'{ "method": "get" }',
|
'{ "method": "get" }',
|
||||||
]);
|
]);
|
||||||
const addDataInterceptor = response => ({
|
const addDataInterceptor = /** @param {{[key: string]: ?}} config */ config => ({
|
||||||
...response,
|
...config,
|
||||||
data: { ...response.data, foo: 'bar' },
|
data: { ...config.data, foo: 'bar' },
|
||||||
});
|
});
|
||||||
const myAjax = getInstance();
|
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);
|
myAjax.responseInterceptors.push(addDataInterceptor);
|
||||||
const response = await myAjax.get('data.json');
|
const response = await myAjax.get('data.json');
|
||||||
expect(response.data).to.deep.equal({ method: 'get', foo: 'bar' });
|
expect(response.data).to.deep.equal({ method: 'get', foo: 'bar' });
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import sinon from 'sinon';
|
||||||
import { AjaxClass } from '../src/AjaxClass.js';
|
import { AjaxClass } from '../src/AjaxClass.js';
|
||||||
|
|
||||||
describe('AjaxClass languages', () => {
|
describe('AjaxClass languages', () => {
|
||||||
|
/** @type {import('sinon').SinonFakeXMLHttpRequestStatic} */
|
||||||
let fakeXhr;
|
let fakeXhr;
|
||||||
|
/** @type {import('sinon').SinonFakeXMLHttpRequest[]} */
|
||||||
let requests;
|
let requests;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -21,10 +23,12 @@ describe('AjaxClass languages', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets "Accept-Language" header to "en-GB" for one request if <html lang="en-GB">', async () => {
|
it('sets "Accept-Language" header to "en-GB" for one request if <html lang="en-GB">', async () => {
|
||||||
|
console.log('setting lang');
|
||||||
document.documentElement.lang = 'en-GB';
|
document.documentElement.lang = 'en-GB';
|
||||||
|
console.log('after setting lang');
|
||||||
const req = new AjaxClass();
|
const req = new AjaxClass();
|
||||||
req.get('data.json');
|
req.get('data.json');
|
||||||
await aTimeout();
|
await aTimeout(0);
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
expect(requests[0].requestHeaders['Accept-Language']).to.equal('en-GB');
|
expect(requests[0].requestHeaders['Accept-Language']).to.equal('en-GB');
|
||||||
});
|
});
|
||||||
|
|
@ -36,7 +40,7 @@ describe('AjaxClass languages', () => {
|
||||||
req.post('data2.json');
|
req.post('data2.json');
|
||||||
req.put('data3.json');
|
req.put('data3.json');
|
||||||
req.delete('data4.json');
|
req.delete('data4.json');
|
||||||
await aTimeout();
|
await aTimeout(0);
|
||||||
expect(requests.length).to.equal(4);
|
expect(requests.length).to.equal(4);
|
||||||
requests.forEach(request => {
|
requests.forEach(request => {
|
||||||
expect(request.requestHeaders['Accept-Language']).to.equal('en-GB');
|
expect(request.requestHeaders['Accept-Language']).to.equal('en-GB');
|
||||||
|
|
@ -47,7 +51,7 @@ describe('AjaxClass languages', () => {
|
||||||
document.documentElement.lang = 'nl-NL';
|
document.documentElement.lang = 'nl-NL';
|
||||||
const req = new AjaxClass();
|
const req = new AjaxClass();
|
||||||
req.get('data.json');
|
req.get('data.json');
|
||||||
await aTimeout();
|
await aTimeout(0);
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
expect(requests[0].requestHeaders['Accept-Language']).to.equal('nl-NL');
|
expect(requests[0].requestHeaders['Accept-Language']).to.equal('nl-NL');
|
||||||
});
|
});
|
||||||
|
|
@ -59,7 +63,7 @@ describe('AjaxClass languages', () => {
|
||||||
req.post('data2.json');
|
req.post('data2.json');
|
||||||
req.put('data3.json');
|
req.put('data3.json');
|
||||||
req.delete('data4.json');
|
req.delete('data4.json');
|
||||||
await aTimeout();
|
await aTimeout(0);
|
||||||
expect(requests.length).to.equal(4);
|
expect(requests.length).to.equal(4);
|
||||||
requests.forEach(request => {
|
requests.forEach(request => {
|
||||||
expect(request.requestHeaders['Accept-Language']).to.equal('nl-NL');
|
expect(request.requestHeaders['Accept-Language']).to.equal('nl-NL');
|
||||||
|
|
@ -70,7 +74,7 @@ describe('AjaxClass languages', () => {
|
||||||
document.documentElement.lang = '';
|
document.documentElement.lang = '';
|
||||||
const req = new AjaxClass();
|
const req = new AjaxClass();
|
||||||
req.get('data.json');
|
req.get('data.json');
|
||||||
await aTimeout();
|
await aTimeout(0);
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
expect(requests[0].requestHeaders['Accept-Language']).to.equal(undefined);
|
expect(requests[0].requestHeaders['Accept-Language']).to.equal(undefined);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,20 @@ import { AjaxClass } from '../src/AjaxClass.js';
|
||||||
import { ajax } from '../src/ajax.js';
|
import { ajax } from '../src/ajax.js';
|
||||||
|
|
||||||
describe('AjaxClass', () => {
|
describe('AjaxClass', () => {
|
||||||
|
/** @type {import('sinon').SinonFakeServer} */
|
||||||
let server;
|
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) {
|
function getInstance(cfg) {
|
||||||
return new AjaxClass(cfg);
|
return new AjaxClass(cfg);
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +212,7 @@ describe('AjaxClass', () => {
|
||||||
'{ "method": "get" }',
|
'{ "method": "get" }',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const makeRequest = async url => {
|
const makeRequest = /** @param {string} url */ async url => {
|
||||||
try {
|
try {
|
||||||
await myAjax.get(url);
|
await myAjax.get(url);
|
||||||
throw new Error('is resolved');
|
throw new Error('is resolved');
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,20 @@ import sinon from 'sinon';
|
||||||
import { AjaxClass } from '../src/AjaxClass.js';
|
import { AjaxClass } from '../src/AjaxClass.js';
|
||||||
|
|
||||||
describe('AjaxClass transformers', () => {
|
describe('AjaxClass transformers', () => {
|
||||||
|
/** @type {import('sinon').SinonFakeServer} */
|
||||||
let server;
|
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) {
|
function getInstance(cfg) {
|
||||||
return new AjaxClass(cfg);
|
return new AjaxClass(cfg);
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +74,7 @@ describe('AjaxClass transformers', () => {
|
||||||
ajax[type].push(myTransformer);
|
ajax[type].push(myTransformer);
|
||||||
await ajax.get('data.json');
|
await ajax.get('data.json');
|
||||||
|
|
||||||
ajax[type] = ajax[type].filter(item => item !== myTransformer);
|
ajax[type] = ajax[type].filter(/** @param {?} item */ item => item !== myTransformer);
|
||||||
await ajax.get('data.json');
|
await ajax.get('data.json');
|
||||||
|
|
||||||
expect(myTransformer.callCount).to.eql(1);
|
expect(myTransformer.callCount).to.eql(1);
|
||||||
|
|
@ -78,7 +90,7 @@ describe('AjaxClass transformers', () => {
|
||||||
{ 'Content-Type': 'application/json' },
|
{ 'Content-Type': 'application/json' },
|
||||||
'{ "method": "post" }',
|
'{ "method": "post" }',
|
||||||
]);
|
]);
|
||||||
const addBarTransformer = data => ({ ...data, bar: 'bar' });
|
const addBarTransformer = /** @param {?} data */ data => ({ ...data, bar: 'bar' });
|
||||||
const myAjax = getInstance();
|
const myAjax = getInstance();
|
||||||
myAjax.requestDataTransformers.push(addBarTransformer);
|
myAjax.requestDataTransformers.push(addBarTransformer);
|
||||||
const response = await myAjax.post('data.json', { foo: 'foo' });
|
const response = await myAjax.post('data.json', { foo: 'foo' });
|
||||||
|
|
@ -96,7 +108,7 @@ describe('AjaxClass transformers', () => {
|
||||||
{ 'Content-Type': 'application/json' },
|
{ 'Content-Type': 'application/json' },
|
||||||
'{ "method": "get" }',
|
'{ "method": "get" }',
|
||||||
]);
|
]);
|
||||||
const addBarTransformer = data => ({ ...data, bar: 'bar' });
|
const addBarTransformer = /** @param {?} data */ data => ({ ...data, bar: 'bar' });
|
||||||
const myAjax = getInstance();
|
const myAjax = getInstance();
|
||||||
myAjax.responseDataTransformers.push(addBarTransformer);
|
myAjax.responseDataTransformers.push(addBarTransformer);
|
||||||
const response = await myAjax.get('data.json');
|
const response = await myAjax.get('data.json');
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import sinon from 'sinon';
|
||||||
import { ajax } from '../src/ajax.js';
|
import { ajax } from '../src/ajax.js';
|
||||||
|
|
||||||
describe('ajax', () => {
|
describe('ajax', () => {
|
||||||
|
/** @type {import('sinon').SinonFakeServer} */
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -32,7 +33,7 @@ describe('ajax', () => {
|
||||||
{ 'Content-Type': 'application/json' },
|
{ 'Content-Type': 'application/json' },
|
||||||
'{"success": true}',
|
'{"success": true}',
|
||||||
]);
|
]);
|
||||||
const makeRequest = async method => {
|
const makeRequest = /** @param {string} method */ async method => {
|
||||||
const response = await ajax[method]('data.json', { foo: 'bar' });
|
const response = await ajax[method]('data.json', { foo: 'bar' });
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
expect(response.data).to.deep.equal({ success: true });
|
expect(response.data).to.deep.equal({ success: true });
|
||||||
|
|
@ -46,7 +47,7 @@ describe('ajax', () => {
|
||||||
{ 'Content-Type': 'application/json' },
|
{ 'Content-Type': 'application/json' },
|
||||||
'{"success": true}',
|
'{"success": true}',
|
||||||
]);
|
]);
|
||||||
const makeRequest = async method => {
|
const makeRequest = /** @param {string} method */ async method => {
|
||||||
const response = await ajax[method]('data.json', { data: 'foobar' }, { foo: 'bar' });
|
const response = await ajax[method]('data.json', { data: 'foobar' }, { foo: 'bar' });
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
expect(response.data).to.deep.equal({ success: true });
|
expect(response.data).to.deep.equal({ success: true });
|
||||||
|
|
@ -62,7 +63,7 @@ describe('ajax', () => {
|
||||||
'{"success": true}',
|
'{"success": true}',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const makeRequest = async method => {
|
const makeRequest = /** @param {string} method */ async method => {
|
||||||
const response = await ajax[method]('data.json');
|
const response = await ajax[method]('data.json');
|
||||||
expect(response.config.headers['X-XSRF-TOKEN']).to.equal('test');
|
expect(response.config.headers['X-XSRF-TOKEN']).to.equal('test');
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
|
|
@ -82,7 +83,7 @@ describe('ajax', () => {
|
||||||
'{"success": true}',
|
'{"success": true}',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const makeRequest = async method => {
|
const makeRequest = /** @param {string} method */ async method => {
|
||||||
const response = await ajax[method]('data.json');
|
const response = await ajax[method]('data.json');
|
||||||
expect(response.config.headers['X-XSRF-TOKEN']).to.equal(undefined);
|
expect(response.config.headers['X-XSRF-TOKEN']).to.equal(undefined);
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
"packages/overlays/test/utils-tests/**/*.js", // TODO: Needs to get typed!
|
"packages/overlays/test/utils-tests/**/*.js", // TODO: Needs to get typed!
|
||||||
"packages/form-integrations/test/**/*.js", // TODO: Needs to get typed!
|
"packages/form-integrations/test/**/*.js", // TODO: Needs to get typed!
|
||||||
"packages/combobox/test/**/*.js", // TODO: Needs to get typed!
|
"packages/combobox/test/**/*.js", // TODO: Needs to get typed!
|
||||||
"packages/ajax/**/*.js", // Deprecated because we will move to redaxios soon.
|
|
||||||
// ignore test/demos for singleton manager until overlays are typed as it's used in there
|
// ignore test/demos for singleton manager until overlays are typed as it's used in there
|
||||||
"packages/singleton-manager/demo/",
|
"packages/singleton-manager/demo/",
|
||||||
"packages/singleton-manager/test/"
|
"packages/singleton-manager/test/"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue