magicbell
Version:
MagicBell API wrapper
90 lines • 3.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Client = exports.DEFAULT_OPTIONS = void 0;
const tslib_1 = require("tslib");
require("../lib/signal-polyfill.js");
const ky_1 = tslib_1.__importDefault(require("@smeijer/ky"));
const url_join_1 = tslib_1.__importDefault(require("url-join"));
const utils_js_1 = require("../lib/utils.js");
const cache_js_1 = require("./cache.js");
const error_js_1 = require("./error.js");
const headers_js_1 = require("./headers.js");
const log_js_1 = require("./log.js");
const options_js_1 = require("./options.js");
const telemetry_js_1 = require("./telemetry.js");
exports.DEFAULT_OPTIONS = {
host: 'https://api.magicbell.com',
timeout: 30_000,
maxRetries: 3,
maxRetryDelay: 60,
telemetry: true,
cacheTTL: 1000,
};
class Client {
#options;
#cache;
constructor(options) {
(0, options_js_1.assertHasValidOptions)(options);
(0, options_js_1.assertHasSomeOptions)(options, ['token', 'apiKey']);
this.#options = { ...exports.DEFAULT_OPTIONS, ...options };
this.#cache = new cache_js_1.Cache({ ttl: this.#options.cacheTTL });
}
hasFlag(flag) {
return this.#options.features?.[flag] || false;
}
async request({ method, path, data, params, headers: reqHeaders }, options) {
const requestOptions = {
...this.#options,
...options,
headers: { ...this.#options.headers, ...reqHeaders },
};
// don't just use `new URL(path, host)` as that will strip the path from the host
const url = new URL((0, url_join_1.default)(requestOptions.host, path));
for (const [key, value] of Object.entries(params || {}).sort()) {
const val = Array.isArray(value) ? [...value].sort().join(',') : value;
url.searchParams.append(key, val);
}
const hooks = (0, options_js_1.mergeHooks)((0, headers_js_1.withRequestHeaders)(requestOptions), (0, telemetry_js_1.withRequestTelemetry)(requestOptions), (0, log_js_1.withRequestLogging)(), this.#options.hooks);
const requestKey = this.#cache.getRequestKey({ method, url, data, headers: reqHeaders });
// Check if there's already a pending request
let promise = this.#cache.get(requestKey);
if (promise) {
return promise;
}
promise = (0, ky_1.default)(url, {
method,
body: data && JSON.stringify(data),
retry: {
limit: requestOptions.maxRetries,
// all methods, as post get idempotency keys
methods: ['get', 'post', 'put', 'head', 'delete', 'options', 'trace'],
},
timeout: requestOptions.timeout,
hooks,
})
.then((response) => response
.text()
// handle cases where the response is empty
.then((text) => JSON.parse(text))
.catch(() => undefined))
.catch(async (error) => {
const body = (0, utils_js_1.tryParse)(await error?.response?.text());
throw (0, error_js_1.createError)({
code: error.code,
name: error.name,
message: error.message,
type: error['type'],
status: error?.response?.status,
statusText: error?.response?.statusText,
responseBody: body,
...body?.errors?.[0],
stack: error.stack,
});
});
// Cache the promise so we can dedupe parallel requests
this.#cache.set(requestKey, promise);
return promise;
}
}
exports.Client = Client;
//# sourceMappingURL=client.js.map