UNPKG

scryfall-api

Version:

A Javascript library for https://scryfall.com/docs/api written in Typescript.

486 lines (471 loc) 13.7 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/scryfall-api.ts var scryfall_api_exports = {}; __export(scryfall_api_exports, { CardIdentifierBuilder: () => CardIdentifierApi, Cards: () => card_api_default, Catalogs: () => catalog_api_default, InvalidScryfallArgumentError: () => InvalidScryfallArgumentError, MagicPageResult: () => MagicPageResult, Rulings: () => ruling_api_default, ScryfallError: () => ScryfallError, Sets: () => set_api_default, SymbologyApi: () => symbology_api_default, UnknownScryfallError: () => UnknownScryfallError }); module.exports = __toCommonJS(scryfall_api_exports); // src/error/invalid.error.ts var InvalidScryfallArgumentError = class extends Error { }; // src/error/scryfall.error.ts var ScryfallError = class extends Error { constructor(code, details, status, type, warnings) { super(details); this.code = code; this.status = status; this.type = type; this.warnings = warnings; } }; // src/error/unknown.error.ts var UnknownScryfallError = class extends Error { }; // src/fetcher/debounce.fetcher.ts var lastQuery = 100; var rateLimit = 100; async function sleep(ms = 0) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function debounce() { const now = Date.now(); const timeSinceLastQuery = now - lastQuery; if (timeSinceLastQuery >= rateLimit) { lastQuery = now; } else { const timeUntilNextQuery = rateLimit - timeSinceLastQuery; lastQuery += timeUntilNextQuery; await sleep(timeUntilNextQuery); } } function createDebounceFetcher(fetcher2 = fetch) { return async function debounceFetcher(...arguments_) { await debounce(); return fetcher2(...arguments_); }; } // src/fetcher/retry.fetcher.ts var defaultCanRetry = (object) => { const status = "status" in object ? Number(object.status) : void 0; if (status && status >= 500) { return true; } const code = "code" in object && typeof object.code === "string" ? object.code : void 0; return code !== "not_found" && code !== "bad_request"; }; function createRetryFetcher({ canRetry = defaultCanRetry, maxAttempts = 3 } = {}, fetcher2 = fetch) { let retries = 0; return async function retryFetcher(...arguments_) { retries++; const response = await fetcher2(...arguments_); if (response.ok) { return response; } const object = await response.json(); const code = "code" in object && typeof object.code === "string" ? object.code : void 0; const status = "status" in object ? Number(object.status) : void 0; const details = "details" in object && typeof object.details === "string" ? object.details : void 0; const type = "type" in object && typeof object.type === "string" ? object.type : void 0; const warnings = "warnings" in object && Array.isArray(object.warnings) ? object.warnings : void 0; if (retries >= maxAttempts) { if (code && details && status) { throw new ScryfallError(code, details, status, type, warnings); } throw new UnknownScryfallError(`Request failed with status ${response.status.toFixed(0)}`); } if (canRetry(object)) { return retryFetcher(...arguments_); } if (code && details && status) { throw new ScryfallError(code, details, status, type, warnings); } throw new UnknownScryfallError(`Request failed with status ${response.status.toFixed(0)}`); }; } // src/fetcher/fetcher.ts var simpleFetcher = createDebounceFetcher(createRetryFetcher()); var endpoint = "https://api.scryfall.com"; var DATE_REGEXP = /^\d{4}-\d{2}-\d{2}?$/; var dateParser = (_key, value) => { if (typeof value === "string" && DATE_REGEXP.test(value)) { return new Date(value); } return value; }; async function fetcher(apiPath, parameters, body) { let path = ""; if (typeof apiPath === "number" || typeof apiPath === "string") { path = apiPath; } else if (apiPath) { path = apiPath.join("/"); } const url = new URL(`${endpoint}/${String(path)}`); if (parameters) { for (const [key, value] of Object.entries(parameters)) { if (value !== void 0) { url.searchParams.append(key, value.toString()); } } } try { const init = { method: body ? "POST" : "GET" }; if (body) { init.body = JSON.stringify(body); init.headers = { "Content-Type": "application/json" }; } const response = await simpleFetcher(url, init); const content = await response.text(); return JSON.parse(content, dateParser); } catch (error) { if (error instanceof ScryfallError) { if (error.warnings) { console.warn(error.warnings); } if (error.code === "not_found" || error.code === "bad_request") { return void 0; } } throw error; } } // src/fetcher/page.result.ts var MagicPageResult = class { constructor(apiPath, query) { this.apiPath = apiPath; this.query = query; this.#hasMore = true; this.#count = 0; } #count; #hasMore; get count() { return this.#count; } get hasMore() { return this.#hasMore; } async all() { const r = []; while (this.#hasMore) { const result = await this.next(); r.push(...result); } return r; } async get(limit) { const r = []; while (this.#hasMore) { const result = await this.next(); r.push(...result); if (r.length === limit) { return r; } if (r.length > limit) { return r.slice(0, limit); } } return r; } async next() { const results = await fetcher(this.apiPath, this.query); this.#hasMore = results?.has_more ?? false; this.#count = Number.parseInt(results?.total_cards?.toFixed(0) ?? "0", 10); if (this.#hasMore) { this.setNextPage(); } return results?.data ?? []; } async page(page) { this.query.page = page; return this.next(); } setNextPage() { this.query.page += 1; } }; // src/api/card/card.api.ts var CardApi = class { /* * Returns up to 20 full English card names that could be autocompletions of the given string parameter. * @param {string} name * The string to search for * @returns {Promise<string[]>} * A list of up to 20 full English card names. */ async autoCompleteName(name) { const result = await fetcher("cards/autocomplete", { q: name }); return result?.data ?? []; } /* * Returns a single card with the given Magic: The Gathering Arena ID. * @param {number} id * The Magic: The Gathering Arena ID of the card to retrieve. * @returns {Promise<Card | undefined>} * Returns a single card or undefined if no card is found. */ async byArenaId(id) { return fetcher(["cards/arena", id]); } /* * Returns a single card with the given Scryfall ID. * @param {number} id * The Scryfall ID. * @returns {Promise<Card | undefined>} * Returns a single card or undefined if no card is found. */ async byId(id) { return fetcher(["cards", id]); } async byMtgoId(id) { return fetcher(["cards/mtgo", id]); } async byMultiverseId(id) { return fetcher(["cards/multiverse", id]); } async byName(name, set2, fuzzy = false) { let f = fuzzy; let s = set2; if (typeof set2 === "boolean") { f = set2; s = void 0; } return fetcher("cards/named", { [f ? "fuzzy" : "exact"]: name, set: s }); } async bySet(setCode, collectorNumber, lang) { const path = ["cards", setCode, collectorNumber]; if (lang) { path.push(lang); } return fetcher(path); } async byTcgPlayerId(id) { return fetcher(["cards/tcgplayer", id]); } async collection(...identifiers) { const cards2 = []; for (let index = 0; index < identifiers.length; index += 75) { const collectionSection = { identifiers: identifiers.slice(index, index + 75) }; const result = await fetcher( "cards/collection", void 0, collectionSection ); cards2.push(...result?.data ?? []); } return cards2; } async random() { return fetcher("cards/random"); } search(query, options) { return new MagicPageResult("cards/search", { q: query, ...typeof options === "number" ? { page: options } : { ...options, page: options?.page ?? 1 } }); } }; var cards = new CardApi(); var card_api_default = cards; // src/api/card-identifier/card-identifier.api.ts var CardIdentifierApi = class { static byId(id) { return { id }; } static byIllustrationId(id) { return { illustration_id: id }; } static byMtgoId(id) { return { mtgo_id: id }; } static byMultiverseId(id) { return { multiverse_id: id }; } static byName(name, set2) { if (set2) { return { name, set: set2 }; } return { name }; } static byOracleId(id) { return { oracle_id: id }; } static bySet(set2, collectorNumber) { return { collector_number: String(collectorNumber), set: set2 }; } }; // src/api/catalog/catalog.api.ts var CatalogApi = class { async artifactTypes() { const response = await fetcher("catalog/artifact-types"); return response?.data ?? []; } async artistNames() { const response = await fetcher("catalog/artist-names"); return response?.data ?? []; } async cardNames() { const response = await fetcher("catalog/card-names"); return response?.data ?? []; } async creatureTypes() { const response = await fetcher("catalog/creature-types"); return response?.data ?? []; } async enchantmentTypes() { const response = await fetcher("catalog/enchantment-types"); return response?.data ?? []; } async landTypes() { const response = await fetcher("catalog/land-types"); return response?.data ?? []; } async loyalties() { const response = await fetcher("catalog/loyalties"); return response?.data ?? []; } async planeswalkerTypes() { const response = await fetcher("catalog/planeswalker-types"); return response?.data ?? []; } async powers() { const response = await fetcher("catalog/powers"); return response?.data ?? []; } async spellTypes() { const response = await fetcher("catalog/spell-types"); return response?.data ?? []; } async toughnesses() { const response = await fetcher("catalog/toughnesses"); return response?.data ?? []; } async watermarks() { const response = await fetcher("catalog/watermarks"); return response?.data ?? []; } async wordBank() { const response = await fetcher("catalog/word-bank"); return response?.data ?? []; } }; var catalog = new CatalogApi(); var catalog_api_default = catalog; // src/api/ruling/ruling.api.ts var RulingApi = class { async byArenaId(id) { const response = await fetcher(["cards/arena", id, "rulings"]); return response?.data ?? []; } async byId(id) { const response = await fetcher(["cards", id, "rulings"]); return response?.data ?? []; } async byMtgoId(id) { const response = await fetcher(["cards/mtgo", id, "rulings"]); return response?.data ?? []; } async byMultiverseId(id) { const response = await fetcher(["cards/multiverse", id, "rulings"]); return response?.data ?? []; } async bySet(setCode, collectorNumber) { const response = await fetcher(["cards", setCode, String(collectorNumber), "rulings"]); return response?.data ?? []; } }; var rulings = new RulingApi(); var ruling_api_default = rulings; // src/api/set/set.api.ts var SetApi = class { async all() { const result = await fetcher("sets"); return result?.data ?? []; } async byCode(code) { return await fetcher(["sets", code]); } async byId(id) { return fetcher(["sets", id]); } async byTcgPlayerId(id) { return fetcher(["sets/tcgplayer", id]); } }; var set = new SetApi(); var set_api_default = set; // src/api/symbology/symbology.api.ts var SymbologyApi = class { async all() { const list = await fetcher("symbology"); return list?.data ?? []; } async parseMana(shorthand) { if (!shorthand || typeof shorthand !== "string") { throw new InvalidScryfallArgumentError("shorthand must be a string"); } const cost = await fetcher("symbology/parse-mana", { cost: shorthand }); return cost ?? { cmc: 0, colorless: false, colors: [], cost: "", monocolored: false, multicolored: false, object: "mana_cost" }; } }; var symbology = new SymbologyApi(); var symbology_api_default = symbology; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CardIdentifierBuilder, Cards, Catalogs, InvalidScryfallArgumentError, MagicPageResult, Rulings, ScryfallError, Sets, SymbologyApi, UnknownScryfallError });