UNPKG

4.9 kBJavaScriptView Raw
1import copy from 'fast-copy';
2import qs from 'qs';
3import asyncToken from './async-token.js';
4import rateLimitRetry from './rate-limit.js';
5import rateLimitThrottle from './rate-limit-throttle.js';
6// Matches 'sub.host:port' or 'host:port' and extracts hostname and port
7// Also enforces toplevel domain specified, no spaces and no protocol
8const HOST_REGEX = /^(?!\w+:\/\/)([^\s:]+\.?[^\s:]+)(?::(\d+))?(?!:)$/;
9/**
10 * Create pre-configured axios instance
11 * @private
12 * @param {AxiosStatic} axios - Axios library
13 * @param {CreateHttpClientParams} options - Initialization parameters for the HTTP client
14 * @return {AxiosInstance} Initialized axios instance
15 */
16export default function createHttpClient(axios, options) {
17 const defaultConfig = {
18 insecure: false,
19 retryOnError: true,
20 // eslint-disable-next-line @typescript-eslint/no-explicit-any
21 logHandler: (level, data) => {
22 if (level === 'error' && data) {
23 const title = [data.name, data.message].filter((a) => a).join(' - ');
24 console.error(`[error] ${title}`);
25 console.error(data);
26 return;
27 }
28 console.log(`[${level}] ${data}`);
29 },
30 // Passed to axios
31 headers: {},
32 httpAgent: false,
33 httpsAgent: false,
34 timeout: 30000,
35 throttle: 0,
36 basePath: '',
37 adapter: undefined,
38 maxContentLength: 1073741824, // 1GB
39 maxBodyLength: 1073741824, // 1GB
40 };
41 const config = {
42 ...defaultConfig,
43 ...options,
44 };
45 if (!config.accessToken) {
46 const missingAccessTokenError = new TypeError('Expected parameter accessToken');
47 config.logHandler('error', missingAccessTokenError);
48 throw missingAccessTokenError;
49 }
50 // Construct axios baseURL option
51 const protocol = config.insecure ? 'http' : 'https';
52 const space = config.space ? `${config.space}/` : '';
53 let hostname = config.defaultHostname;
54 let port = config.insecure ? 80 : 443;
55 if (config.host && HOST_REGEX.test(config.host)) {
56 const parsed = config.host.split(':');
57 if (parsed.length === 2) {
58 ;
59 [hostname, port] = parsed;
60 }
61 else {
62 hostname = parsed[0];
63 }
64 }
65 // Ensure that basePath does start but not end with a slash
66 if (config.basePath) {
67 config.basePath = `/${config.basePath.split('/').filter(Boolean).join('/')}`;
68 }
69 const baseURL = options.baseURL || `${protocol}://${hostname}:${port}${config.basePath}/spaces/${space}`;
70 if (!config.headers.Authorization && typeof config.accessToken !== 'function') {
71 config.headers.Authorization = 'Bearer ' + config.accessToken;
72 }
73 const axiosOptions = {
74 // Axios
75 baseURL,
76 headers: config.headers,
77 httpAgent: config.httpAgent,
78 httpsAgent: config.httpsAgent,
79 proxy: config.proxy,
80 timeout: config.timeout,
81 adapter: config.adapter,
82 maxContentLength: config.maxContentLength,
83 maxBodyLength: config.maxBodyLength,
84 paramsSerializer: {
85 serialize: (params) => {
86 return qs.stringify(params);
87 },
88 },
89 // Contentful
90 logHandler: config.logHandler,
91 responseLogger: config.responseLogger,
92 requestLogger: config.requestLogger,
93 retryOnError: config.retryOnError,
94 };
95 const instance = axios.create(axiosOptions);
96 instance.httpClientParams = options;
97 /**
98 * Creates a new axios instance with the same default base parameters as the
99 * current one, and with any overrides passed to the newParams object
100 * This is useful as the SDKs use dependency injection to get the axios library
101 * and the version of the library comes from different places depending
102 * on whether it's a browser build or a node.js build.
103 * @private
104 * @param {CreateHttpClientParams} newParams - Initialization parameters for the HTTP client
105 * @return {AxiosInstance} Initialized axios instance
106 */
107 instance.cloneWithNewParams = function (newParams) {
108 return createHttpClient(axios, {
109 ...copy(options),
110 ...newParams,
111 });
112 };
113 /**
114 * Apply interceptors.
115 * Please note that the order of interceptors is important
116 */
117 if (config.onBeforeRequest) {
118 instance.interceptors.request.use(config.onBeforeRequest);
119 }
120 if (typeof config.accessToken === 'function') {
121 asyncToken(instance, config.accessToken);
122 }
123 if (config.throttle) {
124 rateLimitThrottle(instance, config.throttle);
125 }
126 rateLimitRetry(instance, config.retryLimit);
127 if (config.onError) {
128 instance.interceptors.response.use((response) => response, config.onError);
129 }
130 return instance;
131}