UNPKG

scryfall-sdk

Version:

A Node.js SDK for https://scryfall.com/docs/api written in Typescript.

156 lines (155 loc) 7.44 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.minimumRequestTimeout = exports.defaultRequestTimeout = void 0; const IScry_1 = require("../IScry"); let axios; if (typeof fetch === "undefined") { try { axios = require("axios").default; } catch (_a) { throw new Error("[scryfall-sdk] If the global `fetch` function is undefined (any node.js version older than v18), the axios peerDependency is required."); } } // the api requests 50-100 ms between calls, we go on the generous side and never wait less than 100 ms between calls exports.defaultRequestTimeout = 100; exports.minimumRequestTimeout = 50; let lastQuery = 0; function sleep(ms = 0) { return new Promise(resolve => setTimeout(resolve, ms)); } class MagicQuerier { query(apiPath, query, post) { return __awaiter(this, void 0, void 0, function* () { if (Array.isArray(apiPath)) apiPath = apiPath.join("/"); let lastError; let result; let retries; for (retries = 0; retries < MagicQuerier.retry.attempts; retries++) { ({ result, lastError } = yield this.tryQuery(`${apiPath}`, query, post)); if (result || (!this.canRetry(lastError) && !MagicQuerier.retry.forced)) break; yield sleep(MagicQuerier.retry.timeout); } if (!result) { lastError !== null && lastError !== void 0 ? lastError : (lastError = new Error("No data")); lastError.attempts = retries; throw lastError; } return result; }); } queryPage(emitter, apiPath, query, page) { var _a, _b; if (page === void 0) { page = (_a = query === null || query === void 0 ? void 0 : query.page) !== null && _a !== void 0 ? _a : 1; } return __awaiter(this, void 0, void 0, function* () { let error; const results = yield this.query(apiPath, Object.assign(Object.assign({}, query), { page })) .catch(err => error = err); const data = (_b = results === null || results === void 0 ? void 0 : results.data) !== null && _b !== void 0 ? _b : []; if ((results === null || results === void 0 ? void 0 : results.object) !== "list" && error === undefined) { emitter.emit("error", new Error("Result object is not a list")); return; } for (const card of data) { if (emitter.cancelled) break; emitter.emit("data", card); } if ((results === null || results === void 0 ? void 0 : results.has_more) && data.length !== 0) { // check if there was no data to workaround scryfall being buggy and returning true for invalid pages if (!emitter.cancelled) { if (emitter.willCancelAfterPage) emitter.cancel(); else return this.queryPage(emitter, apiPath, query, page + 1) .catch(err => { emitter.emit("error", err); }); } } if (!emitter.cancelled) emitter.emit("end"); emitter.emit("done"); }); } tryQuery(apiPath, query, post) { return __awaiter(this, void 0, void 0, function* () { const now = Date.now(); const timeSinceLastQuery = now - lastQuery; if (timeSinceLastQuery >= MagicQuerier.timeout) { lastQuery = now; } else { const timeUntilNextQuery = MagicQuerier.timeout - timeSinceLastQuery; lastQuery += timeUntilNextQuery; yield sleep(timeUntilNextQuery); } MagicQuerier.requestCount++; if (axios) return this.queryAxios(apiPath, query, post); else return this.queryFetch(apiPath, query, post); }); } queryFetch(apiPath, query, post) { var _a; return __awaiter(this, void 0, void 0, function* () { const cleanParams = {}; for (const [key, value] of Object.entries(query !== null && query !== void 0 ? query : {})) if (value !== undefined) cleanParams[key] = value; const searchParams = query ? `?${new URLSearchParams(cleanParams).toString()}` : ''; const url = `${IScry_1.ENDPOINT_API}/${apiPath}` + searchParams; let result = yield fetch(url, { body: JSON.stringify(post), headers: Object.assign(Object.assign({ 'Content-Type': 'application/json' }, !MagicQuerier.agent ? undefined : { 'User-Agent': MagicQuerier.agent, }), { Accept: "*/*" }), method: post ? "POST" : "GET", }); let lastError; if (result !== undefined && !result.ok) { const error = yield result.json(); lastError = new Error((_a = error.details) !== null && _a !== void 0 ? _a : error.code); Object.assign(lastError, error); result = undefined; } return { result: yield (result === null || result === void 0 ? void 0 : result.json()), lastError }; }); } queryAxios(apiPath, query, post) { return __awaiter(this, void 0, void 0, function* () { let lastError; const result = (yield axios.request({ data: post, method: post ? "POST" : "GET", params: query, url: `${IScry_1.ENDPOINT_API}/${apiPath}`, }).catch(({ response }) => { var _a; const error = response.data; lastError = new Error((_a = error.details) !== null && _a !== void 0 ? _a : error.code); Object.assign(lastError, response.data); })) || undefined; return { result: result === null || result === void 0 ? void 0 : result.data, lastError }; }); } canRetry(error) { if (error.code === "not_found" || error.code === "bad_request") return false; return !MagicQuerier.retry.canRetry || MagicQuerier.retry.canRetry(error); } } exports.default = MagicQuerier; MagicQuerier.lastQuery = 0; MagicQuerier.retry = { attempts: 1 }; MagicQuerier.timeout = exports.defaultRequestTimeout; MagicQuerier.requestCount = 0;