scryfall-api
Version:
A Javascript library for https://scryfall.com/docs/api written in Typescript.
486 lines (471 loc) • 13.7 kB
JavaScript
;
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
});