arweave-multihost
Version:
JavaScript/TypeScript Arweave SDK with multiple host support
124 lines (123 loc) • 4.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const arweave_1 = __importDefault(require("arweave"));
;
;
const DEFAULT_HOSTS = [
{
host: "arweave.net",
protocol: "https",
port: 443,
},
{
host: "gateway.amplify.host",
protocol: "https",
port: 443,
},
{
host: "arweave.dev",
protocol: "https",
port: 443,
},
];
const defaultArweaveConfig = {
timeout: 10000,
logging: false,
onError: console.error,
};
/**
* Returns overrriden arweave instance
* @param hosts - Array of hosts
* @param config - Other configuration options
* (timeout, logging, logger, onError callback)
* @returns arweave instance
*/
function init(hosts, config = defaultArweaveConfig) {
if (hosts.length === 0) {
throw new Error("Multihost config should have at least one host");
}
const arweave = arweave_1.default.init(Object.assign(Object.assign({}, hosts[0]), config));
let currentHostIndex = 0, lastFailedHost = 0;
function updateHostParams(hostConfig) {
arweave.api.config.host = hostConfig.host;
arweave.api.config.port = hostConfig.port;
arweave.api.config.protocol = hostConfig.protocol;
}
const originalApiRequestMethods = {
get: arweave.api.get.bind(arweave.api),
post: arweave.api.post.bind(arweave.api),
};
function switchHost() {
const oldHost = getCurrentHost();
currentHostIndex = (currentHostIndex + 1) % hosts.length;
const newHost = hosts[currentHostIndex];
if (config.logging && config.logger !== undefined) {
config.logger("Request failed. Switching host and retrying. "
+ `Old host: ${JSON.stringify(oldHost)}. `
+ `New host: ${JSON.stringify(newHost)}.`);
}
updateHostParams(newHost);
}
function getCurrentHost() {
return hosts[currentHostIndex];
}
function isHostActive(host) {
const currentHost = getCurrentHost();
return JSON.stringify(host) === JSON.stringify(currentHost);
}
async function overridenRequestMethod(sendOriginalRequest) {
for (let i = 0; i < hosts.length; i++) {
const hostUsedForRequest = getCurrentHost();
try {
const response = await sendOriginalRequest();
if (response.status >= 500 && response.status < 600) {
throw new Error("Error while sending http request: "
+ `Status: ${response.status}. `
+ `Data: ${JSON.stringify(response.data)}`);
}
return response;
}
catch (err) {
// config.onError callback may be used for error logging
if (config.onError) {
config.onError(err);
}
if (i === hosts.length - 1) {
throw err;
}
else {
// We switch host only if it's still active.
// If it's not active it means that it was switched by
// another concurrent request
if (isHostActive(hostUsedForRequest)) {
switchHost();
}
}
}
}
throw new Error("Should never reach this");
}
arweave.api.get = async (endpoint, axiosConfig) => {
return await overridenRequestMethod(() => originalApiRequestMethods.get(endpoint, axiosConfig));
};
arweave.api.post = async (endpoint, body, axiosConfig) => {
return await overridenRequestMethod(() => originalApiRequestMethods.post(endpoint, body, axiosConfig));
};
return arweave;
}
/**
* Returns overrriden arweave instance with default hosts
* (arweave.net, gateway.amplify.host, arweave.dev)
* @param config - Other configuration options
* (timeout, logging, logger, onError callback)
* @returns arweave instance
*/
function initWithDefaultHosts(config) {
return init(DEFAULT_HOSTS, config);
}
module.exports = {
init,
initWithDefaultHosts,
};