@merkur/plugin-http-client
Version:
Merkur event emitter plugin.
222 lines (220 loc) • 6.71 kB
JavaScript
;
var core = require('@merkur/core');
function setDefaultConfig(widget, newDefaultConfig) {
widget.$in.httpClient.defaultConfig = {
...widget.$in.httpClient.defaultConfig,
...newDefaultConfig
};
}
function getDefaultTransformers(widget) {
return [transformBody(), transformQuery(), transformTimeout()];
}
function httpClientPlugin() {
return {
async setup(widget) {
core.assignMissingKeys(widget, httpClientAPI());
widget.$in.httpClient = {
defaultConfig: {
method: 'GET',
transformers: getDefaultTransformers(),
headers: {},
query: {},
timeout: 15000
}
};
widget.$dependencies.fetch = getFetchAPI();
widget.$dependencies.AbortController = AbortController;
return widget;
},
async create(widget) {
core.bindWidgetToFunctions(widget, widget.http);
return widget;
}
};
}
function httpClientAPI() {
return {
http: {
async request(widget, requestConfig) {
let response = null;
let request = {
...widget.$in.httpClient.defaultConfig,
...requestConfig
};
const transformers = request.transformers;
[request, response] = await runTransformers(widget, transformers, 'transformRequest', request, response);
response = !response ? await widget.$dependencies.fetch(request.url, request) : response;
[request, response] = await runTransformers(widget, transformers, 'transformResponse', request, response);
if (!response.ok) {
const error = new Error(`Received ${response.status} status code when requesting ${request.url}`, {
cause: {
request,
response
}
});
// keep compatablity
error.request = request;
error.response = response;
return Promise.reject(error);
}
return {
request,
response
};
}
}
};
}
async function runTransformers(widget, transformers, method, ...rest) {
for (const transformer of transformers) {
if (typeof transformer[method] === 'function') {
rest = await transformer[method](widget, ...rest);
}
}
return rest;
}
function getFetchAPI() {
var _window, _window$bind;
if (typeof window === 'undefined') {
var _global;
return (_global = global) === null || _global === void 0 ? void 0 : _global.fetch;
}
return (_window = window) === null || _window === void 0 || (_window = _window.fetch) === null || _window === void 0 || (_window$bind = _window.bind) === null || _window$bind === void 0 ? void 0 : _window$bind.call(_window, window);
}
function transformQuery() {
return {
async transformRequest(widget, request, response) {
let newRequest = {
...request
};
let {
baseUrl = '',
path = '/'
} = request;
if (!request.url) {
newRequest.url = `${baseUrl.endsWith('/') ? baseUrl.substring(0, baseUrl.length - 1) : baseUrl}/${path.startsWith('/') ? path.substring(1) : path}`;
} else {
newRequest.url = request.url;
}
const queryString = Object.keys(request.query).map(key => [key, request.query[key]].map(encodeURIComponent).join('=')).join('&');
const hasQuestionMark = newRequest.url.indexOf('?') !== -1;
if (hasQuestionMark) {
newRequest.url += queryString ? `&${queryString}` : '';
} else {
newRequest.url += queryString ? `?${queryString}` : '';
}
return [newRequest, response];
}
};
}
function transformBody() {
return {
async transformResponse(widget, request, response) {
if (response.status !== 204 && typeof response.json === 'function') {
const contentType = response.headers.get('content-type');
let body = null;
if (contentType && contentType.includes('application/json')) {
body = await response.json();
} else {
body = await response.text();
}
let newResponse = copyResponse(response);
newResponse.body = body;
return [request, newResponse];
}
return [request, response];
},
async transformRequest(widget, request, response) {
const {
body,
headers,
method
} = request;
if (body && (headers['Content-Type'] || headers['content-type']) === 'application/json' && !['GET', 'HEAD'].includes(method)) {
let newRequest = {
...request,
body: JSON.stringify(body)
};
return [newRequest, response];
}
return [request, response];
}
};
}
function transformTimeout() {
return {
async transformRequest(widget, request, response) {
let newRequest = {
...request
};
if ('timeout' in request) {
const controller = new widget.$dependencies.AbortController();
newRequest.signal = controller.signal;
newRequest.timeoutTimer = setTimeout(() => {
controller.abort();
}, request.timeout);
}
return [newRequest, response];
},
async transformResponse(widget, request, response) {
if ('timeoutTimer' in request) {
clearTimeout(request.timeoutTimer);
}
return [request, response];
}
};
}
function copyResponse(response) {
const {
body,
headers,
ok,
redirected,
status,
statusText,
trailers,
type,
url,
useFinalURL
} = response;
return {
body: assign({}, body),
headers,
ok,
redirected,
status,
statusText,
trailers,
type,
url,
useFinalURL
};
}
const PROTECTED_FIELDS = ['__proto__', 'prototype', 'constructor'];
function assign(target, source = {}, parentField = null) {
for (const field of Object.keys(source)) {
if (PROTECTED_FIELDS.includes(field)) {
return;
}
const value = source[field];
const fieldPath = parentField ? parentField + '.' + field : field;
if (value instanceof Array) {
target[field] = value.slice();
} else if (value instanceof Object && !(value instanceof Function) && !(value instanceof RegExp)) {
if (!(target[field] instanceof Object)) {
target[field] = {};
}
assign(target[field], value, fieldPath);
} else {
target[field] = value;
}
}
return target;
}
exports.copyResponse = copyResponse;
exports.getDefaultTransformers = getDefaultTransformers;
exports.httpClientPlugin = httpClientPlugin;
exports.setDefaultConfig = setDefaultConfig;
exports.transformBody = transformBody;
exports.transformQuery = transformQuery;
exports.transformTimeout = transformTimeout;