kubo-rpc-client-esm-cjs
Version:
A client library for the Kubo RPC API
195 lines • 7.58 kB
JavaScript
;
/* eslint-env browser */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTTPError = exports.Client = exports.errorHandler = void 0;
const multiaddr_1 = require("@multiformats/multiaddr");
const env_js_1 = require("ipfs-utils/src/env.js");
const parse_duration_1 = __importDefault(require("parse-duration"));
const logger_1 = require("@libp2p/logger");
const http_js_1 = __importDefault(require("ipfs-utils/src/http.js"));
const merge_options_1 = __importDefault(require("merge-options"));
const to_url_string_1 = require("ipfs-core-utils/to-url-string");
const agent_1 = __importDefault(require("ipfs-core-utils/agent"));
const log = (0, logger_1.logger)('js-kubo-rpc-client:lib:error-handler');
const merge = merge_options_1.default.bind({ ignoreUndefined: true });
const DEFAULT_PROTOCOL = env_js_1.isBrowser || env_js_1.isWebWorker ? location.protocol : 'http';
const DEFAULT_HOST = env_js_1.isBrowser || env_js_1.isWebWorker ? location.hostname : 'localhost';
const DEFAULT_PORT = env_js_1.isBrowser || env_js_1.isWebWorker ? location.port : '5001';
/**
* @typedef {import('../types').Options} Options
*/
/**
* @typedef {import('../types').Multiaddr} Multiaddr
*/
/**
* @param {Options|URL|Multiaddr|string} [options]
* @returns {Options}
*/
const normalizeOptions = (options = {}) => {
let url;
/** @type {Options} */
let opts = {};
let agent;
if (typeof options === 'string' || (0, multiaddr_1.isMultiaddr)(options)) {
url = new URL((0, to_url_string_1.toUrlString)(options));
}
else if (options instanceof URL) {
url = options;
}
else if (typeof options.url === 'string' || (0, multiaddr_1.isMultiaddr)(options.url)) {
url = new URL((0, to_url_string_1.toUrlString)(options.url));
opts = options;
}
else if (options.url instanceof URL) {
url = options.url;
opts = options;
}
else {
opts = options || {};
const protocol = (opts.protocol || DEFAULT_PROTOCOL).replace(':', '');
const host = (opts.host || DEFAULT_HOST).split(':')[0];
const port = (opts.port || DEFAULT_PORT);
url = new URL(`${protocol}://${host}:${port}`);
}
if (opts.apiPath) {
url.pathname = opts.apiPath;
}
else if (url.pathname === '/' || url.pathname === undefined) {
url.pathname = 'api/v0';
}
if (env_js_1.isNode) {
const Agent = (0, agent_1.default)(url);
agent = opts.agent || new Agent({
keepAlive: true,
// Similar to browsers which limit connections to six per host
maxSockets: 6
});
}
return Object.assign(Object.assign({}, opts), { host: url.host, protocol: url.protocol.replace(':', ''), port: Number(url.port), apiPath: url.pathname, url,
agent });
};
/**
* @param {Response} response
*/
const errorHandler = (response) => __awaiter(void 0, void 0, void 0, function* () {
let msg;
try {
if ((response.headers.get('Content-Type') || '').startsWith('application/json')) {
const data = yield response.json();
log(data);
msg = data.Message || data.message;
}
else {
msg = yield response.text();
}
}
catch ( /** @type {any} */err) {
log('Failed to parse error response', err);
// Failed to extract/parse error message from response
msg = err.message;
}
/** @type {Error} */
let error = new http_js_1.default.HTTPError(response);
if (msg) {
// This is what rs-ipfs returns where there's a timeout
if (msg.includes('deadline has elapsed')) {
error = new http_js_1.default.TimeoutError();
}
// This is what go-ipfs returns where there's a timeout
if (msg && msg.includes('context deadline exceeded')) {
error = new http_js_1.default.TimeoutError();
}
}
// This also gets returned
if (msg && msg.includes('request timed out')) {
error = new http_js_1.default.TimeoutError();
}
// If we managed to extract a message from the response, use it
if (msg) {
error.message = msg;
}
throw error;
});
exports.errorHandler = errorHandler;
const KEBAB_REGEX = /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g;
/**
* @param {string} str
*/
const kebabCase = (str) => {
return str.replace(KEBAB_REGEX, function (match) {
return '-' + match.toLowerCase();
});
};
/**
* @param {string | number} value
*/
const parseTimeout = (value) => {
return typeof value === 'string' ? (0, parse_duration_1.default)(value) : value;
};
class Client extends http_js_1.default {
/**
* @param {Options|URL|Multiaddr|string} [options]
*/
constructor(options = {}) {
const opts = normalizeOptions(options);
super({
timeout: parseTimeout(opts.timeout || 0) || undefined,
headers: opts.headers,
base: `${opts.url}`,
handleError: exports.errorHandler,
transformSearchParams: (search) => {
const out = new URLSearchParams();
for (const [key, value] of search) {
if (value !== 'undefined' &&
value !== 'null' &&
key !== 'signal') {
out.append(kebabCase(key), value);
}
// @ts-expect-error server timeouts are strings
if (key === 'timeout' && !isNaN(value)) {
out.append(kebabCase(key), value);
}
}
return out;
},
// @ts-expect-error this can be a https agent or a http agent
agent: opts.agent
});
// @ts-expect-error - cannot delete no-optional fields
delete this.get;
// @ts-expect-error - cannot delete no-optional fields
delete this.put;
// @ts-expect-error - cannot delete no-optional fields
delete this.delete;
// @ts-expect-error - cannot delete no-optional fields
delete this.options;
const fetch = this.fetch;
/**
* @param {string | Request} resource
* @param {import('../types').HTTPOptions} options
*/
this.fetch = (resource, options = {}) => {
if (typeof resource === 'string' && !resource.startsWith('/')) {
resource = `${opts.url}/${resource}`;
}
return fetch.call(this, resource, merge(options, {
method: 'POST'
}));
};
}
}
exports.Client = Client;
exports.HTTPError = http_js_1.default.HTTPError;
//# sourceMappingURL=core.js.map