ani-client 1.4.2 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -547
- package/dist/index.d.mts +458 -448
- package/dist/index.d.ts +458 -448
- package/dist/index.js +76 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -60
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -4
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/index.ts","../src/cache/index.ts","../src/errors/index.ts","../src/rate-limiter/index.ts","../src/types/index.ts","../src/client/index.ts","../src/cache/redis.ts"],"names":["MediaType","MediaFormat","MediaStatus","MediaSeason","MediaSort","AiringSort","CharacterSort","CharacterRole","MediaRelationType","RecommendationSort","MediaListStatus","MediaListSort","data"],"mappings":"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","file":"index.mjs","sourcesContent":["/** Core media fields — always returned. Does NOT include relations (opt-in via include). */\nconst MEDIA_FIELDS_BASE = `\n id\n idMal\n title { romaji english native userPreferred }\n type\n format\n status\n description(asHtml: false)\n startDate { year month day }\n endDate { year month day }\n season\n seasonYear\n episodes\n duration\n chapters\n volumes\n countryOfOrigin\n isLicensed\n source\n hashtag\n trailer { id site thumbnail }\n coverImage { extraLarge large medium color }\n bannerImage\n genres\n synonyms\n averageScore\n meanScore\n popularity\n favourites\n trending\n tags { id name description category rank isMediaSpoiler }\n studios { nodes { id name isAnimationStudio siteUrl } }\n isAdult\n siteUrl\n`;\n\nconst RELATIONS_FIELDS = `\n relations {\n edges {\n relationType(version: 2)\n node {\n id\n title { romaji english native userPreferred }\n type\n format\n status\n coverImage { large medium }\n siteUrl\n }\n }\n }\n`;\n\n/** Full media fields with relations — used by existing queries for backward compat. */\nconst MEDIA_FIELDS = `\n ${MEDIA_FIELDS_BASE}\n ${RELATIONS_FIELDS}\n`;\n\n/** Character fields without back-reference to media (used when embedding characters inside a Media query). */\nconst CHARACTER_FIELDS_COMPACT = `\n id\n name { first middle last full native alternative }\n image { large medium }\n description(asHtml: false)\n gender\n dateOfBirth { year month day }\n age\n bloodType\n favourites\n siteUrl\n`;\n\nconst CHARACTER_MEDIA_NODES = `\n media(perPage: 10) {\n nodes {\n id\n title { romaji english native userPreferred }\n type\n coverImage { large medium }\n siteUrl\n }\n }\n`;\n\n/** Compact voice actor fields — lightweight subset for embedding inside character edges. */\nconst VOICE_ACTOR_FIELDS_COMPACT = `\n id\n name { first middle last full native userPreferred }\n languageV2\n image { large medium }\n gender\n primaryOccupations\n siteUrl\n`;\n\nconst CHARACTER_MEDIA_EDGES_WITH_VA = `\n media(perPage: 10) {\n edges {\n voiceActors {\n ${VOICE_ACTOR_FIELDS_COMPACT}\n }\n node {\n id\n title { romaji english native userPreferred }\n type\n coverImage { large medium }\n siteUrl\n }\n }\n }\n`;\n\nconst CHARACTER_FIELDS = `\n ${CHARACTER_FIELDS_COMPACT}\n ${CHARACTER_MEDIA_NODES}\n`;\n\nconst CHARACTER_FIELDS_WITH_VA = `\n ${CHARACTER_FIELDS_COMPACT}\n ${CHARACTER_MEDIA_EDGES_WITH_VA}\n`;\n\nconst STAFF_FIELDS = `\n id\n name { first middle last full native }\n language\n image { large medium }\n description(asHtml: false)\n primaryOccupations\n gender\n dateOfBirth { year month day }\n dateOfDeath { year month day }\n age\n yearsActive\n homeTown\n bloodType\n favourites\n siteUrl\n`;\n\nconst STAFF_MEDIA_FIELDS = `\n staffMedia(perPage: $perPage, sort: [POPULARITY_DESC]) {\n nodes {\n id\n title { romaji english native userPreferred }\n type\n format\n status\n coverImage { extraLarge large medium color }\n bannerImage\n genres\n averageScore\n meanScore\n popularity\n favourites\n episodes\n trending\n hashtag\n season\n seasonYear\n startDate { year month day }\n endDate { year month day }\n nextAiringEpisode {\n id\n airingAt\n episode\n mediaId\n timeUntilAiring\n }\n studios {\n edges {\n node {\n name\n }\n }\n }\n siteUrl\n }\n }\n`;\n\nconst USER_FIELDS = `\n id\n name\n about(asHtml: false)\n avatar { large medium }\n bannerImage\n isFollowing\n isFollower\n donatorTier\n donatorBadge\n createdAt\n siteUrl\n statistics {\n anime { count meanScore minutesWatched episodesWatched chaptersRead volumesRead }\n manga { count meanScore minutesWatched episodesWatched chaptersRead volumesRead }\n }\n`;\n\nexport const QUERY_MEDIA_BY_ID = `\nquery ($id: Int!) {\n Media(id: $id) {\n ${MEDIA_FIELDS}\n }\n}`;\n\nexport const QUERY_MEDIA_SEARCH = `\nquery (\n $search: String,\n $type: MediaType,\n $format: MediaFormat,\n $status: MediaStatus,\n $season: MediaSeason,\n $seasonYear: Int,\n $genre: String,\n $tag: String,\n $isAdult: Boolean,\n $sort: [MediaSort],\n $page: Int,\n $perPage: Int\n) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(\n search: $search,\n type: $type,\n format: $format,\n status: $status,\n season: $season,\n seasonYear: $seasonYear,\n genre: $genre,\n tag: $tag,\n isAdult: $isAdult,\n sort: $sort\n ) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_TRENDING = `\nquery ($type: MediaType, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(type: $type, sort: TRENDING_DESC) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_CHARACTER_BY_ID = `\nquery ($id: Int!) {\n Character(id: $id) {\n ${CHARACTER_FIELDS}\n }\n}`;\n\nexport const QUERY_CHARACTER_BY_ID_WITH_VA = `\nquery ($id: Int!) {\n Character(id: $id) {\n ${CHARACTER_FIELDS_WITH_VA}\n }\n}`;\n\nexport const QUERY_CHARACTER_SEARCH = `\nquery ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n characters(search: $search, sort: $sort) {\n ${CHARACTER_FIELDS}\n }\n }\n}`;\n\nexport const QUERY_CHARACTER_SEARCH_WITH_VA = `\nquery ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n characters(search: $search, sort: $sort) {\n ${CHARACTER_FIELDS_WITH_VA}\n }\n }\n}`;\n\nexport const QUERY_STAFF_BY_ID = `\nquery ($id: Int!) {\n Staff(id: $id) {\n ${STAFF_FIELDS}\n }\n}`;\n\nexport const QUERY_STAFF_BY_ID_WITH_MEDIA = `\nquery ($id: Int!, $perPage: Int) {\n Staff(id: $id) {\n ${STAFF_FIELDS}\n ${STAFF_MEDIA_FIELDS}\n }\n}`;\n\nexport const QUERY_STAFF_SEARCH = `\nquery ($search: String, $sort: [StaffSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n staff(search: $search, sort: $sort) {\n ${STAFF_FIELDS}\n }\n }\n}`;\n\nexport const QUERY_USER_BY_ID = `\nquery ($id: Int!) {\n User(id: $id) {\n ${USER_FIELDS}\n }\n}`;\n\nexport const QUERY_USER_BY_NAME = `\nquery ($name: String!) {\n User(name: $name) {\n ${USER_FIELDS}\n }\n}`;\n\nexport const QUERY_AIRING_SCHEDULE = `\nquery ($airingAt_greater: Int, $airingAt_lesser: Int, $sort: [AiringSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n airingSchedules(airingAt_greater: $airingAt_greater, airingAt_lesser: $airingAt_lesser, sort: $sort) {\n id\n airingAt\n timeUntilAiring\n episode\n mediaId\n media {\n ${MEDIA_FIELDS_BASE}\n }\n }\n }\n}`;\n\nexport const QUERY_RECENT_CHAPTERS = `\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(type: MANGA, status: RELEASING, sort: UPDATED_AT_DESC) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_PLANNING = `\nquery ($type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(type: $type, status: NOT_YET_RELEASED, sort: $sort) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_MEDIA_BY_SEASON = `\nquery ($season: MediaSeason!, $seasonYear: Int!, $type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(season: $season, seasonYear: $seasonYear, type: $type, sort: $sort) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nconst MEDIA_LIST_FIELDS = `\n id\n mediaId\n status\n score(format: POINT_100)\n progress\n progressVolumes\n repeat\n priority\n private\n notes\n startedAt { year month day }\n completedAt { year month day }\n updatedAt\n createdAt\n media {\n ${MEDIA_FIELDS_BASE}\n }\n`;\n\nexport const QUERY_RECOMMENDATIONS = `\nquery ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {\n Media(id: $mediaId) {\n recommendations(page: $page, perPage: $perPage, sort: $sort) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n nodes {\n id\n rating\n userRating\n mediaRecommendation {\n id\n idMal\n title { romaji english native userPreferred }\n type\n format\n status\n coverImage { extraLarge large medium color }\n bannerImage\n genres\n averageScore\n meanScore\n popularity\n favourites\n siteUrl\n }\n user {\n id\n name\n avatar { large medium }\n }\n }\n }\n }\n}`;\n\nexport const QUERY_USER_MEDIA_LIST = `\nquery ($userId: Int, $userName: String, $type: MediaType!, $status: MediaListStatus, $sort: [MediaListSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n mediaList(userId: $userId, userName: $userName, type: $type, status: $status, sort: $sort) {\n ${MEDIA_LIST_FIELDS}\n }\n }\n}`;\n\nconst STUDIO_FIELDS = `\n id\n name\n isAnimationStudio\n siteUrl\n favourites\n media(page: 1, perPage: 25, sort: POPULARITY_DESC) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n nodes {\n id\n title { romaji english native userPreferred }\n type\n format\n coverImage { large medium }\n siteUrl\n }\n }\n`;\n\nexport const QUERY_STUDIO_BY_ID = `\nquery ($id: Int!) {\n Studio(id: $id) {\n ${STUDIO_FIELDS}\n }\n}`;\n\nexport const QUERY_STUDIO_SEARCH = `\nquery ($search: String, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n studios(search: $search) {\n ${STUDIO_FIELDS}\n }\n }\n}`;\n\nexport const QUERY_GENRES = `\nquery {\n GenreCollection\n}`;\n\nexport const QUERY_TAGS = `\nquery {\n MediaTagCollection {\n id\n name\n description\n category\n isAdult\n }\n}`;\n\nimport type { MediaIncludeOptions } from \"../types\";\n\n// ── Dynamic media query builder ──\n\n/**\n * Build a `Media(id: $id)` query that optionally includes characters, staff,\n * relations, streaming episodes, external links, stats, and recommendations.\n *\n * When no include options are given, the query is identical to QUERY_MEDIA_BY_ID.\n *\n * @internal\n */\nexport function buildMediaByIdQuery(include?: MediaIncludeOptions): string {\n if (!include) return QUERY_MEDIA_BY_ID;\n\n const extra: string[] = [];\n\n // Relations — included by default (backward compat), opt-out with `relations: false`\n if (include.relations !== false) {\n extra.push(RELATIONS_FIELDS);\n }\n\n // Characters\n if (include.characters) {\n const opts = typeof include.characters === \"object\" ? include.characters : {};\n const perPage = opts.perPage ?? 25;\n const sortClause = opts.sort !== false ? \", sort: [ROLE, RELEVANCE, ID]\" : \"\";\n const voiceActorBlock = opts.voiceActors\n ? `\\n voiceActors {\n ${VOICE_ACTOR_FIELDS_COMPACT}\n }`\n : \"\";\n extra.push(`\n characters(perPage: ${perPage}${sortClause}) {\n edges {\n role\n node {\n ${CHARACTER_FIELDS_COMPACT}\n }${voiceActorBlock}\n }\n }`);\n }\n\n // Staff\n if (include.staff) {\n const opts = typeof include.staff === \"object\" ? include.staff : {};\n const perPage = opts.perPage ?? 25;\n const sortClause = opts.sort !== false ? \", sort: [RELEVANCE, ID]\" : \"\";\n extra.push(`\n staff(perPage: ${perPage}${sortClause}) {\n edges {\n role\n node {\n ${STAFF_FIELDS}\n }\n }\n }`);\n }\n\n // Recommendations\n if (include.recommendations) {\n const perPage = typeof include.recommendations === \"object\" ? (include.recommendations.perPage ?? 10) : 10;\n extra.push(`\n recommendations(perPage: ${perPage}, sort: [RATING_DESC]) {\n nodes {\n id\n rating\n mediaRecommendation {\n id\n title { romaji english native userPreferred }\n type\n format\n coverImage { large medium }\n averageScore\n siteUrl\n }\n }\n }`);\n }\n\n // Streaming episodes\n if (include.streamingEpisodes) {\n extra.push(`\n streamingEpisodes {\n title\n thumbnail\n url\n site\n }`);\n }\n\n // External links\n if (include.externalLinks) {\n extra.push(`\n externalLinks {\n id\n url\n site\n type\n icon\n color\n }`);\n }\n\n // Stats (score & status distribution)\n if (include.stats) {\n extra.push(`\n stats {\n scoreDistribution { score amount }\n statusDistribution { status amount }\n }`);\n }\n\n return `\nquery ($id: Int!) {\n Media(id: $id) {\n ${MEDIA_FIELDS_BASE}\n ${extra.join(\"\\n\")}\n }\n}`;\n}\n\n// ── Batch query builders ──\n\n/** @internal Build a batched GraphQL query using aliases. */\nfunction buildBatchQuery(ids: number[], typeName: string, fields: string, prefix: string): string {\n const aliases = ids.map((id, i) => `${prefix}${i}: ${typeName}(id: ${id}) { ${fields} }`).join(\"\\n \");\n return `query {\\n ${aliases}\\n}`;\n}\n\nexport const buildBatchMediaQuery = (ids: number[]): string => buildBatchQuery(ids, \"Media\", MEDIA_FIELDS_BASE, \"m\");\n\nexport const buildBatchCharacterQuery = (ids: number[]): string =>\n buildBatchQuery(ids, \"Character\", CHARACTER_FIELDS, \"c\");\n\nexport const buildBatchStaffQuery = (ids: number[]): string => buildBatchQuery(ids, \"Staff\", STAFF_FIELDS, \"s\");\n","import type { CacheAdapter, CacheOptions } from \"../types\";\n\n/**\n * Simple in-memory cache with configurable TTL.\n * Used internally by AniListClient to avoid redundant API calls.\n */\nexport type { CacheOptions };\n\ninterface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\nexport class MemoryCache implements CacheAdapter {\n private readonly ttl: number;\n private readonly maxSize: number;\n private readonly enabled: boolean;\n private readonly store = new Map<string, CacheEntry<unknown>>();\n\n constructor(options: CacheOptions = {}) {\n this.ttl = options.ttl ?? ONE_DAY_MS;\n this.maxSize = options.maxSize ?? 500;\n this.enabled = options.enabled ?? true;\n }\n\n /** Build a deterministic cache key from a query + variables pair. */\n static key(query: string, variables: Record<string, unknown>): string {\n const normalized = query.replace(/\\s+/g, \" \").trim();\n return `${normalized}|${JSON.stringify(variables, Object.keys(variables).sort())}`;\n }\n\n /** Retrieve a cached value, or `undefined` if missing / expired. */\n get<T>(key: string): T | undefined {\n if (!this.enabled) return undefined;\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n // LRU: promote to most-recently-used by re-inserting at the end\n this.store.delete(key);\n this.store.set(key, entry);\n return entry.data as T;\n }\n\n /** Store a value in the cache. */\n set<T>(key: string, data: T): void {\n if (!this.enabled) return;\n\n // Remove first so re-insert places it at the end (MRU position)\n this.store.delete(key);\n\n // Evict least-recently-used entry if at capacity\n if (this.maxSize > 0 && this.store.size >= this.maxSize) {\n const firstKey = this.store.keys().next().value;\n if (firstKey !== undefined) this.store.delete(firstKey);\n }\n\n this.store.set(key, { data, expiresAt: Date.now() + this.ttl });\n }\n\n /** Remove a specific entry. */\n delete(key: string): boolean {\n return this.store.delete(key);\n }\n\n /** Clear the entire cache. */\n clear(): void {\n this.store.clear();\n }\n\n /** Number of entries currently stored. */\n get size(): number {\n return this.store.size;\n }\n\n /** Return all cache keys. */\n keys(): string[] {\n return [...this.store.keys()];\n }\n\n /**\n * Remove all entries whose key matches the given pattern.\n *\n * @param pattern — A string (converted to RegExp) or RegExp.\n * @returns Number of entries removed.\n */\n invalidate(pattern: string | RegExp): number {\n const regex = typeof pattern === \"string\" ? new RegExp(pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")) : pattern;\n const toDelete: string[] = [];\n for (const key of this.store.keys()) {\n if (regex.test(key)) toDelete.push(key);\n }\n for (const key of toDelete) this.store.delete(key);\n return toDelete.length;\n }\n}\n","/**\n * Custom error class for AniList API errors.\n */\nexport class AniListError extends Error {\n /** HTTP status code returned by the API */\n public readonly status: number;\n /** Raw error body from the API response */\n public readonly errors: unknown[];\n\n constructor(message: string, status: number, errors: unknown[] = []) {\n super(message);\n this.name = \"AniListError\";\n this.status = status;\n this.errors = errors;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AniListError);\n }\n }\n}\n","/**\n * Rate limiter with automatic retry for AniList API.\n *\n * AniList allows 90 requests per minute.\n * When a 429 (Too Many Requests) is received, the client\n * waits for the Retry-After header and retries automatically.\n */\n\nimport type { RateLimitOptions } from \"../types\";\nexport type { RateLimitOptions };\n\nexport class RateLimiter {\n private readonly maxRequests: number;\n private readonly windowMs: number;\n private readonly maxRetries: number;\n private readonly retryDelayMs: number;\n private readonly enabled: boolean;\n private readonly timeoutMs: number;\n private readonly retryOnNetworkError: boolean;\n\n /** @internal */\n private timestamps: number[] = [];\n\n constructor(options: RateLimitOptions = {}) {\n this.maxRequests = options.maxRequests ?? 85;\n this.windowMs = options.windowMs ?? 60_000;\n this.maxRetries = options.maxRetries ?? 3;\n this.retryDelayMs = options.retryDelayMs ?? 2_000;\n this.enabled = options.enabled ?? true;\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.retryOnNetworkError = options.retryOnNetworkError ?? true;\n }\n\n /**\n * Wait until it's safe to make a request (respects rate limit window).\n */\n async acquire(): Promise<void> {\n if (!this.enabled) return;\n\n const now = Date.now();\n this.timestamps = this.timestamps.filter((t) => now - t < this.windowMs);\n\n if (this.timestamps.length >= this.maxRequests) {\n // biome-ignore lint/style/noNonNullAssertion: length check guarantees [0] exists\n const oldest = this.timestamps[0]!;\n const waitMs = this.windowMs - (now - oldest) + 50;\n await this.sleep(waitMs);\n return this.acquire(); // Re-check after waiting\n }\n\n this.timestamps.push(Date.now());\n }\n\n /**\n * Execute a fetch with automatic retry on 429 responses and network errors.\n */\n async fetchWithRetry(\n url: string,\n init: RequestInit,\n hooks?: {\n onRetry?: (attempt: number, reason: string, delayMs: number) => void;\n onRateLimit?: (retryAfterMs: number) => void;\n },\n ): Promise<Response> {\n await this.acquire();\n\n let lastResponse: Response | undefined;\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const res = await this.fetchWithTimeout(url, init);\n\n if (res.status !== 429) return res;\n\n lastResponse = res;\n if (attempt === this.maxRetries) break;\n\n const retryAfter = res.headers.get(\"Retry-After\");\n const delayMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1000 : this.retryDelayMs * (attempt + 1);\n\n hooks?.onRateLimit?.(delayMs);\n hooks?.onRetry?.(attempt + 1, \"HTTP 429\", delayMs);\n\n await this.sleep(delayMs);\n await this.acquire();\n } catch (err) {\n lastError = err;\n\n if (this.retryOnNetworkError && isNetworkError(err) && attempt < this.maxRetries) {\n const delayMs = this.retryDelayMs * (attempt + 1);\n hooks?.onRetry?.(attempt + 1, `Network error: ${(err as Error).message}`, delayMs);\n await this.sleep(delayMs);\n await this.acquire();\n continue;\n }\n\n throw err;\n }\n }\n\n if (lastResponse) return lastResponse;\n throw lastError;\n }\n\n /** @internal */\n private async fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n if (this.timeoutMs <= 0) return fetch(url, init);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/** Set of Node.js error codes that indicate a transient network failure. */\nconst RETRYABLE_NETWORK_CODES = new Set([\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n \"UND_ERR_SOCKET\",\n]);\n\n/** @internal */\nfunction isNetworkError(err: unknown): boolean {\n if (err instanceof TypeError && err.message === \"fetch failed\") return true;\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code && RETRYABLE_NETWORK_CODES.has(code)) return true;\n const cause = (err as { cause?: { code?: string } })?.cause?.code;\n if (cause && RETRYABLE_NETWORK_CODES.has(cause)) return true;\n return false;\n}\n","export enum MediaType {\n ANIME = \"ANIME\",\n MANGA = \"MANGA\",\n}\n\nexport enum MediaFormat {\n TV = \"TV\",\n TV_SHORT = \"TV_SHORT\",\n MOVIE = \"MOVIE\",\n SPECIAL = \"SPECIAL\",\n OVA = \"OVA\",\n ONA = \"ONA\",\n MUSIC = \"MUSIC\",\n MANGA = \"MANGA\",\n NOVEL = \"NOVEL\",\n ONE_SHOT = \"ONE_SHOT\",\n}\n\nexport enum MediaStatus {\n FINISHED = \"FINISHED\",\n RELEASING = \"RELEASING\",\n NOT_YET_RELEASED = \"NOT_YET_RELEASED\",\n CANCELLED = \"CANCELLED\",\n HIATUS = \"HIATUS\",\n}\n\nexport enum MediaSeason {\n WINTER = \"WINTER\",\n SPRING = \"SPRING\",\n SUMMER = \"SUMMER\",\n FALL = \"FALL\",\n}\n\nexport enum MediaSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n TITLE_ROMAJI = \"TITLE_ROMAJI\",\n TITLE_ROMAJI_DESC = \"TITLE_ROMAJI_DESC\",\n TITLE_ENGLISH = \"TITLE_ENGLISH\",\n TITLE_ENGLISH_DESC = \"TITLE_ENGLISH_DESC\",\n TITLE_NATIVE = \"TITLE_NATIVE\",\n TITLE_NATIVE_DESC = \"TITLE_NATIVE_DESC\",\n TYPE = \"TYPE\",\n TYPE_DESC = \"TYPE_DESC\",\n FORMAT = \"FORMAT\",\n FORMAT_DESC = \"FORMAT_DESC\",\n START_DATE = \"START_DATE\",\n START_DATE_DESC = \"START_DATE_DESC\",\n END_DATE = \"END_DATE\",\n END_DATE_DESC = \"END_DATE_DESC\",\n SCORE = \"SCORE\",\n SCORE_DESC = \"SCORE_DESC\",\n POPULARITY = \"POPULARITY\",\n POPULARITY_DESC = \"POPULARITY_DESC\",\n TRENDING = \"TRENDING\",\n TRENDING_DESC = \"TRENDING_DESC\",\n EPISODES = \"EPISODES\",\n EPISODES_DESC = \"EPISODES_DESC\",\n DURATION = \"DURATION\",\n DURATION_DESC = \"DURATION_DESC\",\n STATUS = \"STATUS\",\n STATUS_DESC = \"STATUS_DESC\",\n FAVOURITES = \"FAVOURITES\",\n FAVOURITES_DESC = \"FAVOURITES_DESC\",\n UPDATED_AT = \"UPDATED_AT\",\n UPDATED_AT_DESC = \"UPDATED_AT_DESC\",\n SEARCH_MATCH = \"SEARCH_MATCH\",\n}\n\nexport enum AiringSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n MEDIA_ID = \"MEDIA_ID\",\n MEDIA_ID_DESC = \"MEDIA_ID_DESC\",\n TIME = \"TIME\",\n TIME_DESC = \"TIME_DESC\",\n EPISODE = \"EPISODE\",\n EPISODE_DESC = \"EPISODE_DESC\",\n}\n\nexport enum CharacterSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n ROLE = \"ROLE\",\n ROLE_DESC = \"ROLE_DESC\",\n SEARCH_MATCH = \"SEARCH_MATCH\",\n FAVOURITES = \"FAVOURITES\",\n FAVOURITES_DESC = \"FAVOURITES_DESC\",\n}\n\nexport enum CharacterRole {\n MAIN = \"MAIN\",\n SUPPORTING = \"SUPPORTING\",\n BACKGROUND = \"BACKGROUND\",\n}\n\nexport interface MediaTitle {\n romaji: string | null;\n english: string | null;\n native: string | null;\n userPreferred: string | null;\n}\n\nexport interface MediaCoverImage {\n extraLarge: string | null;\n large: string | null;\n medium: string | null;\n color: string | null;\n}\n\nexport interface FuzzyDate {\n year: number | null;\n month: number | null;\n day: number | null;\n}\n\nexport interface MediaTrailer {\n id: string | null;\n site: string | null;\n thumbnail: string | null;\n}\n\nexport interface MediaTag {\n id: number;\n name: string;\n description: string | null;\n category: string | null;\n rank: number | null;\n isMediaSpoiler: boolean | null;\n}\n\nexport interface Studio {\n id: number;\n name: string;\n isAnimationStudio: boolean;\n siteUrl: string | null;\n}\n\nexport interface StudioConnection {\n nodes: Studio[];\n}\n\nexport enum MediaRelationType {\n ADAPTATION = \"ADAPTATION\",\n PREQUEL = \"PREQUEL\",\n SEQUEL = \"SEQUEL\",\n PARENT = \"PARENT\",\n SIDE_STORY = \"SIDE_STORY\",\n CHARACTER = \"CHARACTER\",\n SUMMARY = \"SUMMARY\",\n ALTERNATIVE = \"ALTERNATIVE\",\n SPIN_OFF = \"SPIN_OFF\",\n OTHER = \"OTHER\",\n SOURCE = \"SOURCE\",\n COMPILATION = \"COMPILATION\",\n CONTAINS = \"CONTAINS\",\n}\n\nexport interface MediaEdge {\n relationType: MediaRelationType;\n node: Pick<Media, \"id\" | \"title\" | \"type\" | \"format\" | \"status\" | \"coverImage\" | \"siteUrl\">;\n}\n\nexport interface MediaConnection {\n edges: MediaEdge[];\n}\n\nexport interface CharacterName {\n first: string | null;\n middle: string | null;\n last: string | null;\n full: string | null;\n native: string | null;\n alternative: string[];\n}\n\nexport interface CharacterImage {\n large: string | null;\n medium: string | null;\n}\n\n/** Compact voice actor data returned inside character edges. */\nexport interface VoiceActor {\n id: number;\n name: {\n first: string | null;\n middle: string | null;\n last: string | null;\n full: string | null;\n native: string | null;\n userPreferred: string | null;\n };\n languageV2: string | null;\n image: StaffImage;\n gender: string | null;\n primaryOccupations: string[];\n siteUrl: string | null;\n}\n\nexport interface MediaCharacterEdge {\n role: CharacterRole;\n node: Omit<Character, \"media\">;\n voiceActors?: VoiceActor[];\n}\n\nexport interface MediaCharacterConnection {\n edges: MediaCharacterEdge[];\n}\n\nexport interface MediaStaffEdge {\n role: string;\n node: Staff;\n}\n\nexport interface MediaStaffConnection {\n edges: MediaStaffEdge[];\n}\n\nexport interface StreamingEpisode {\n title: string | null;\n thumbnail: string | null;\n url: string | null;\n site: string | null;\n}\n\nexport interface ExternalLink {\n id: number;\n url: string | null;\n site: string;\n type: string | null;\n icon: string | null;\n color: string | null;\n}\n\nexport interface ScoreDistribution {\n score: number;\n amount: number;\n}\n\nexport interface StatusDistribution {\n status: MediaListStatus | string;\n amount: number;\n}\n\nexport interface MediaStats {\n scoreDistribution: ScoreDistribution[];\n statusDistribution: StatusDistribution[];\n}\n\nexport interface MediaRecommendationNode {\n id: number;\n rating: number | null;\n mediaRecommendation: Pick<Media, \"id\" | \"title\" | \"type\" | \"format\" | \"coverImage\" | \"averageScore\" | \"siteUrl\">;\n}\n\nexport interface Media {\n id: number;\n idMal: number | null;\n title: MediaTitle;\n type: MediaType;\n format: MediaFormat | null;\n status: MediaStatus | null;\n description: string | null;\n startDate: FuzzyDate | null;\n endDate: FuzzyDate | null;\n season: MediaSeason | null;\n seasonYear: number | null;\n episodes: number | null;\n duration: number | null;\n chapters: number | null;\n volumes: number | null;\n countryOfOrigin: string | null;\n isLicensed: boolean | null;\n source: string | null;\n hashtag: string | null;\n trailer: MediaTrailer | null;\n coverImage: MediaCoverImage;\n bannerImage: string | null;\n genres: string[];\n synonyms: string[];\n averageScore: number | null;\n meanScore: number | null;\n popularity: number | null;\n favourites: number | null;\n trending: number | null;\n tags: MediaTag[];\n studios: StudioConnection;\n relations: MediaConnection | null;\n characters?: MediaCharacterConnection;\n staff?: MediaStaffConnection;\n streamingEpisodes?: StreamingEpisode[];\n externalLinks?: ExternalLink[];\n stats?: MediaStats;\n recommendations?: { nodes: MediaRecommendationNode[] };\n isAdult: boolean | null;\n siteUrl: string | null;\n}\n\ntype CharacterMediaNode = Pick<Media, \"id\" | \"title\" | \"type\" | \"coverImage\" | \"siteUrl\">;\n\nexport interface CharacterMediaEdge {\n node: CharacterMediaNode;\n voiceActors?: VoiceActor[];\n}\n\nexport interface Character {\n id: number;\n name: CharacterName;\n image: CharacterImage;\n description: string | null;\n gender: string | null;\n dateOfBirth: FuzzyDate | null;\n age: string | null;\n bloodType: string | null;\n favourites: number | null;\n siteUrl: string | null;\n media: {\n nodes?: CharacterMediaNode[];\n edges?: CharacterMediaEdge[];\n } | null;\n}\n\n/** Options for including extra data when fetching a character. */\nexport interface CharacterIncludeOptions {\n /** Include voice actors for each media the character appears in. */\n voiceActors?: boolean;\n}\n\nexport interface StaffName {\n first: string | null;\n middle: string | null;\n last: string | null;\n full: string | null;\n native: string | null;\n}\n\nexport interface StaffImage {\n large: string | null;\n medium: string | null;\n}\n\n/** A media node returned inside `Staff.staffMedia`. */\nexport interface StaffMediaNode {\n id: number;\n title: MediaTitle;\n type: MediaType;\n format: MediaFormat | null;\n status: MediaStatus | null;\n coverImage: MediaCoverImage;\n bannerImage: string | null;\n genres: string[];\n averageScore: number | null;\n meanScore: number | null;\n popularity: number | null;\n favourites: number | null;\n episodes: number | null;\n trending: number | null;\n hashtag: string | null;\n season: MediaSeason | null;\n seasonYear: number | null;\n startDate: FuzzyDate | null;\n endDate: FuzzyDate | null;\n nextAiringEpisode: {\n id: number;\n airingAt: number;\n episode: number;\n mediaId: number;\n timeUntilAiring: number;\n } | null;\n studios: { edges: { node: { name: string } }[] } | null;\n siteUrl: string | null;\n}\n\nexport interface Staff {\n id: number;\n name: StaffName;\n language: string | null;\n image: StaffImage;\n description: string | null;\n primaryOccupations: string[];\n gender: string | null;\n dateOfBirth: FuzzyDate | null;\n dateOfDeath: FuzzyDate | null;\n age: string | null;\n yearsActive: number[];\n homeTown: string | null;\n bloodType: string | null;\n favourites: number | null;\n siteUrl: string | null;\n /** Media the staff member has worked on — only present when requested via include options. */\n staffMedia?: {\n nodes: StaffMediaNode[];\n } | null;\n}\n\n/** Options to include additional related data when fetching a staff member by ID. */\nexport interface StaffIncludeOptions {\n /** Include media the staff member has worked on.\n * `true` = 25 results sorted by popularity. Object form to customize. */\n media?: boolean | { perPage?: number; sort?: boolean };\n}\n\nexport interface UserAvatar {\n large: string | null;\n medium: string | null;\n}\n\nexport interface UserStatistics {\n count: number;\n meanScore: number;\n minutesWatched: number;\n episodesWatched: number;\n chaptersRead: number;\n volumesRead: number;\n}\n\nexport interface User {\n id: number;\n name: string;\n about: string | null;\n avatar: UserAvatar;\n bannerImage: string | null;\n isFollowing: boolean | null;\n isFollower: boolean | null;\n donatorTier: number | null;\n donatorBadge: string | null;\n createdAt: number | null;\n siteUrl: string | null;\n statistics: {\n anime: UserStatistics;\n manga: UserStatistics;\n } | null;\n}\n\nexport interface AiringSchedule {\n id: number;\n airingAt: number;\n timeUntilAiring: number;\n episode: number;\n mediaId: number;\n media: Media;\n}\n\nexport interface PageInfo {\n total: number | null;\n perPage: number | null;\n currentPage: number | null;\n lastPage: number | null;\n hasNextPage: boolean | null;\n}\n\nexport interface SearchMediaOptions {\n query?: string;\n type?: MediaType;\n format?: MediaFormat;\n status?: MediaStatus;\n season?: MediaSeason;\n seasonYear?: number;\n genre?: string;\n tag?: string;\n isAdult?: boolean;\n sort?: MediaSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface SearchCharacterOptions {\n query?: string;\n sort?: CharacterSort[];\n page?: number;\n perPage?: number;\n /** Include voice actors for each media the character appears in. */\n voiceActors?: boolean;\n}\n\nexport interface SearchStaffOptions {\n query?: string;\n sort?: CharacterSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface PagedResult<T> {\n pageInfo: PageInfo;\n results: T[];\n}\n\nexport interface GetAiringOptions {\n /** Only show episodes that aired after this UNIX timestamp */\n airingAtGreater?: number;\n /** Only show episodes that aired before this UNIX timestamp */\n airingAtLesser?: number;\n /** Sort order (default: TIME_DESC) */\n sort?: AiringSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface GetRecentChaptersOptions {\n /** Page number (default: 1) */\n page?: number;\n /** Results per page (default: 20, max 50) */\n perPage?: number;\n}\n\nexport interface GetPlanningOptions {\n /** Filter by ANIME or MANGA (returns both if omitted) */\n type?: MediaType;\n /** Sort order (default: POPULARITY_DESC) */\n sort?: MediaSort[];\n page?: number;\n perPage?: number;\n}\n\nexport enum RecommendationSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n RATING = \"RATING\",\n RATING_DESC = \"RATING_DESC\",\n}\n\nexport interface Recommendation {\n id: number;\n rating: number | null;\n userRating: string | null;\n mediaRecommendation: Media;\n user: {\n id: number;\n name: string;\n avatar: UserAvatar;\n } | null;\n}\n\nexport interface GetRecommendationsOptions {\n /** The AniList media ID to get recommendations for */\n mediaId: number;\n /** Sort order (default: RATING_DESC) */\n sort?: RecommendationSort[];\n page?: number;\n perPage?: number;\n}\n\nexport enum MediaListStatus {\n CURRENT = \"CURRENT\",\n PLANNING = \"PLANNING\",\n COMPLETED = \"COMPLETED\",\n DROPPED = \"DROPPED\",\n PAUSED = \"PAUSED\",\n REPEATING = \"REPEATING\",\n}\n\nexport enum MediaListSort {\n MEDIA_ID = \"MEDIA_ID\",\n MEDIA_ID_DESC = \"MEDIA_ID_DESC\",\n SCORE = \"SCORE\",\n SCORE_DESC = \"SCORE_DESC\",\n STATUS = \"STATUS\",\n STATUS_DESC = \"STATUS_DESC\",\n PROGRESS = \"PROGRESS\",\n PROGRESS_DESC = \"PROGRESS_DESC\",\n PROGRESS_VOLUMES = \"PROGRESS_VOLUMES\",\n PROGRESS_VOLUMES_DESC = \"PROGRESS_VOLUMES_DESC\",\n REPEAT = \"REPEAT\",\n REPEAT_DESC = \"REPEAT_DESC\",\n PRIORITY = \"PRIORITY\",\n PRIORITY_DESC = \"PRIORITY_DESC\",\n STARTED_ON = \"STARTED_ON\",\n STARTED_ON_DESC = \"STARTED_ON_DESC\",\n FINISHED_ON = \"FINISHED_ON\",\n FINISHED_ON_DESC = \"FINISHED_ON_DESC\",\n ADDED_TIME = \"ADDED_TIME\",\n ADDED_TIME_DESC = \"ADDED_TIME_DESC\",\n UPDATED_TIME = \"UPDATED_TIME\",\n UPDATED_TIME_DESC = \"UPDATED_TIME_DESC\",\n MEDIA_TITLE_ROMAJI = \"MEDIA_TITLE_ROMAJI\",\n MEDIA_TITLE_ROMAJI_DESC = \"MEDIA_TITLE_ROMAJI_DESC\",\n MEDIA_TITLE_ENGLISH = \"MEDIA_TITLE_ENGLISH\",\n MEDIA_TITLE_ENGLISH_DESC = \"MEDIA_TITLE_ENGLISH_DESC\",\n MEDIA_TITLE_NATIVE = \"MEDIA_TITLE_NATIVE\",\n MEDIA_TITLE_NATIVE_DESC = \"MEDIA_TITLE_NATIVE_DESC\",\n MEDIA_POPULARITY = \"MEDIA_POPULARITY\",\n MEDIA_POPULARITY_DESC = \"MEDIA_POPULARITY_DESC\",\n}\n\nexport interface MediaListEntry {\n id: number;\n mediaId: number;\n status: MediaListStatus;\n score: number | null;\n progress: number | null;\n progressVolumes: number | null;\n repeat: number | null;\n priority: number | null;\n private: boolean | null;\n notes: string | null;\n startedAt: FuzzyDate | null;\n completedAt: FuzzyDate | null;\n updatedAt: number | null;\n createdAt: number | null;\n media: Media;\n}\n\nexport interface GetSeasonOptions {\n /** The season (WINTER, SPRING, SUMMER, FALL) */\n season: MediaSeason;\n /** The year */\n seasonYear: number;\n /** Filter by ANIME or MANGA (defaults to ANIME) */\n type?: MediaType;\n /** Sort order (default: POPULARITY_DESC) */\n sort?: MediaSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface GetUserMediaListOptions {\n /** User ID (provide either userId or userName) */\n userId?: number;\n /** Username (provide either userId or userName) */\n userName?: string;\n /** ANIME or MANGA */\n type: MediaType;\n /** Filter by list status (CURRENT, COMPLETED, etc.) */\n status?: MediaListStatus;\n /** Sort order */\n sort?: MediaListSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface StudioDetail {\n id: number;\n name: string;\n isAnimationStudio: boolean;\n siteUrl: string | null;\n favourites: number | null;\n media: {\n pageInfo: PageInfo;\n nodes: Pick<Media, \"id\" | \"title\" | \"type\" | \"format\" | \"coverImage\" | \"siteUrl\">[];\n } | null;\n}\n\nexport interface SearchStudioOptions {\n query?: string;\n page?: number;\n perPage?: number;\n}\n\n/**\n * Options to include additional related data when fetching a media entry.\n * Pass `true` to include with defaults, or an object to customize.\n */\nexport interface MediaIncludeOptions {\n /** Include characters with their roles (MAIN, SUPPORTING, BACKGROUND).\n * `true` = 25 results sorted by role. Object form to customize. */\n characters?: boolean | { perPage?: number; sort?: boolean; voiceActors?: boolean };\n /** Include staff members with their roles.\n * `true` = 25 results sorted by relevance. Object form to customize. */\n staff?: boolean | { perPage?: number; sort?: boolean };\n /** Include relations (default: `true` for backward compat). Set to `false` to exclude. */\n relations?: boolean;\n /** Include streaming episode links (Crunchyroll, Funimation, etc.) */\n streamingEpisodes?: boolean;\n /** Include external links (MAL, official site, etc.) */\n externalLinks?: boolean;\n /** Include score & status distribution stats */\n stats?: boolean;\n /** Include user recommendations. `true` = 10 results, or customize with `{ perPage }`. */\n recommendations?: boolean | { perPage?: number };\n}\n\n/**\n * Interface that all cache adapters must implement.\n * Methods may return sync values or Promises — the client awaits all calls.\n */\nexport interface CacheAdapter {\n /** Retrieve a cached value, or `undefined` if missing / expired. */\n get<T>(key: string): T | undefined | Promise<T | undefined>;\n /** Store a value in the cache. */\n set<T>(key: string, data: T): void | Promise<void>;\n /** Remove a specific entry. Returns `true` if the key existed. */\n delete(key: string): boolean | Promise<boolean>;\n /** Clear the entire cache. */\n clear(): void | Promise<void>;\n /** Number of entries currently stored (sync). Returns -1 if unknown. */\n readonly size: number;\n /** Return all cache keys. */\n keys(): string[] | Promise<string[]>;\n /** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */\n invalidate?(pattern: string | RegExp): number | Promise<number>;\n}\n\n/** Cache configuration options. */\nexport interface CacheOptions {\n /** Time-to-live in milliseconds (default: 86 400 000 = 24h) */\n ttl?: number;\n /** Maximum number of cached entries (default: 500, 0 = unlimited) */\n maxSize?: number;\n /** Set to false to disable caching entirely */\n enabled?: boolean;\n}\n\n/** Rate limiter configuration options. */\nexport interface RateLimitOptions {\n /** Max requests per window (default: 85) */\n maxRequests?: number;\n /** Window size in ms (default: 60 000) */\n windowMs?: number;\n /** Max retries on 429 (default: 3) */\n maxRetries?: number;\n /** Retry delay in ms when Retry-After header is absent (default: 2000) */\n retryDelayMs?: number;\n /** Set to false to disable rate limiting entirely */\n enabled?: boolean;\n /** Timeout per request in ms (default: 30 000). 0 = no timeout. */\n timeoutMs?: number;\n /** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */\n retryOnNetworkError?: boolean;\n}\n\n/** Event hooks for logging, debugging, and monitoring. */\nexport interface AniListHooks {\n /** Called before every API request. */\n onRequest?: (query: string, variables: Record<string, unknown>) => void;\n /** Called when a response is served from cache. */\n onCacheHit?: (key: string) => void;\n /** Called when the rate limiter enforces a wait (429 received). */\n onRateLimit?: (retryAfterMs: number) => void;\n /** Called when a request is retried (429 or network error). */\n onRetry?: (attempt: number, reason: string, delayMs: number) => void;\n /** Called when a request completes. */\n onResponse?: (query: string, durationMs: number, fromCache: boolean) => void;\n}\n\nexport interface AniListClientOptions {\n /** Optional AniList OAuth token for authenticated requests */\n token?: string;\n /** Custom API endpoint (defaults to https://graphql.anilist.co) */\n apiUrl?: string;\n /** Cache configuration (enabled by default, 24h TTL) */\n cache?: CacheOptions;\n /** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */\n cacheAdapter?: CacheAdapter;\n /** Rate limiter configuration (enabled by default, 85 req/min) */\n rateLimit?: RateLimitOptions;\n /** Event hooks for logging, debugging, and monitoring */\n hooks?: AniListHooks;\n}\n","import {\n QUERY_AIRING_SCHEDULE,\n QUERY_CHARACTER_BY_ID,\n QUERY_CHARACTER_BY_ID_WITH_VA,\n QUERY_CHARACTER_SEARCH,\n QUERY_CHARACTER_SEARCH_WITH_VA,\n QUERY_GENRES,\n QUERY_MEDIA_BY_ID,\n QUERY_MEDIA_BY_SEASON,\n QUERY_MEDIA_SEARCH,\n QUERY_PLANNING,\n QUERY_RECENT_CHAPTERS,\n QUERY_RECOMMENDATIONS,\n QUERY_STAFF_BY_ID,\n QUERY_STAFF_BY_ID_WITH_MEDIA,\n QUERY_STAFF_SEARCH,\n QUERY_STUDIO_BY_ID,\n QUERY_STUDIO_SEARCH,\n QUERY_TAGS,\n QUERY_TRENDING,\n QUERY_USER_BY_ID,\n QUERY_USER_BY_NAME,\n QUERY_USER_MEDIA_LIST,\n buildBatchCharacterQuery,\n buildBatchMediaQuery,\n buildBatchStaffQuery,\n buildMediaByIdQuery,\n} from \"../queries\";\n\nimport { MemoryCache } from \"../cache\";\nimport { AniListError } from \"../errors\";\nimport { RateLimiter } from \"../rate-limiter\";\n\nimport { MediaType } from \"../types\";\nimport type {\n AiringSchedule,\n AniListClientOptions,\n AniListHooks,\n CacheAdapter,\n Character,\n CharacterIncludeOptions,\n GetAiringOptions,\n GetPlanningOptions,\n GetRecentChaptersOptions,\n GetRecommendationsOptions,\n GetSeasonOptions,\n GetUserMediaListOptions,\n Media,\n MediaIncludeOptions,\n MediaListEntry,\n MediaTag,\n PageInfo,\n PagedResult,\n Recommendation,\n SearchCharacterOptions,\n SearchMediaOptions,\n SearchStaffOptions,\n SearchStudioOptions,\n Staff,\n StaffIncludeOptions,\n StudioDetail,\n User,\n} from \"../types\";\n\nconst DEFAULT_API_URL = \"https://graphql.anilist.co\";\n\n/**\n * Lightweight AniList GraphQL client with built-in caching and rate limiting.\n *\n * @example\n * ```ts\n * import { AniListClient } from \"ani-client\";\n *\n * const client = new AniListClient();\n * const anime = await client.getMedia(1); // Cowboy Bebop\n * console.log(anime.title.romaji);\n *\n * // Custom cache & rate limit options\n * const client2 = new AniListClient({\n * cache: { ttl: 1000 * 60 * 60 }, // 1 hour cache\n * rateLimit: { maxRequests: 60 },\n * });\n * ```\n */\nexport class AniListClient {\n private readonly apiUrl: string;\n private readonly headers: Record<string, string>;\n private readonly cacheAdapter: CacheAdapter;\n private readonly rateLimiter: RateLimiter;\n private readonly hooks: AniListHooks;\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n constructor(options: AniListClientOptions = {}) {\n this.apiUrl = options.apiUrl ?? DEFAULT_API_URL;\n this.headers = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (options.token) {\n this.headers.Authorization = `Bearer ${options.token}`;\n }\n this.cacheAdapter = options.cacheAdapter ?? new MemoryCache(options.cache);\n this.rateLimiter = new RateLimiter(options.rateLimit);\n this.hooks = options.hooks ?? {};\n }\n\n /**\n * @internal\n */\n private async request<T>(query: string, variables: Record<string, unknown> = {}): Promise<T> {\n const cacheKey = MemoryCache.key(query, variables);\n\n // Check cache (await handles both sync and async adapters)\n const cached = await this.cacheAdapter.get<T>(cacheKey);\n if (cached !== undefined) {\n this.hooks.onCacheHit?.(cacheKey);\n this.hooks.onResponse?.(query, 0, true);\n return cached;\n }\n\n // Request deduplication — reuse in-flight request for the same key\n const existing = this.inFlight.get(cacheKey);\n if (existing) return existing as Promise<T>;\n\n const promise = this.executeRequest<T>(query, variables, cacheKey);\n this.inFlight.set(cacheKey, promise);\n\n try {\n return await promise;\n } finally {\n this.inFlight.delete(cacheKey);\n }\n }\n\n /** @internal */\n private async executeRequest<T>(query: string, variables: Record<string, unknown>, cacheKey: string): Promise<T> {\n const start = Date.now();\n this.hooks.onRequest?.(query, variables);\n\n const res = await this.rateLimiter.fetchWithRetry(\n this.apiUrl,\n {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify({ query, variables }),\n },\n { onRetry: this.hooks.onRetry, onRateLimit: this.hooks.onRateLimit },\n );\n\n const json = (await res.json()) as { data?: T; errors?: unknown[] };\n\n if (!res.ok || json.errors) {\n const message =\n (json.errors as Array<{ message?: string }>)?.[0]?.message ?? `AniList API error (HTTP ${res.status})`;\n throw new AniListError(message, res.status, json.errors ?? []);\n }\n\n const data = json.data as T;\n await this.cacheAdapter.set(cacheKey, data);\n this.hooks.onResponse?.(query, Date.now() - start, false);\n return data;\n }\n\n /**\n * @internal\n * Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.\n */\n private async pagedRequest<T>(\n query: string,\n variables: Record<string, unknown>,\n field: string,\n ): Promise<PagedResult<T>> {\n const data = await this.request<{ Page: Record<string, unknown> & { pageInfo: PageInfo } }>(query, variables);\n const results = data.Page[field];\n if (!Array.isArray(results)) {\n throw new AniListError(`Unexpected response: missing field \"${field}\" in Page`, 0, []);\n }\n return { pageInfo: data.Page.pageInfo, results: results as T[] };\n }\n\n /**\n * @internal\n * Clamp perPage to AniList's maximum of 50.\n */\n private clampPerPage(value: number): number {\n return Math.min(Math.max(value, 1), 50);\n }\n\n /**\n * Fetch a single media entry by its AniList ID.\n *\n * Optionally include related data (characters, staff, relations, etc.) via the `include` parameter.\n *\n * @param id - The AniList media ID\n * @param include - Optional related data to include\n * @returns The media object\n *\n * @example\n * ```ts\n * // Basic usage — same as before (includes relations by default)\n * const anime = await client.getMedia(1);\n *\n * // Include characters sorted by role, 25 results\n * const anime = await client.getMedia(1, { characters: true });\n *\n * // Include characters with voice actors\n * const anime = await client.getMedia(1, { characters: { voiceActors: true } });\n *\n * // Full control\n * const anime = await client.getMedia(1, {\n * characters: { perPage: 50, sort: true },\n * staff: true,\n * relations: true,\n * streamingEpisodes: true,\n * externalLinks: true,\n * stats: true,\n * recommendations: { perPage: 5 },\n * });\n *\n * // Exclude relations for a lighter response\n * const anime = await client.getMedia(1, { characters: true, relations: false });\n * ```\n */\n async getMedia(id: number, include?: MediaIncludeOptions): Promise<Media> {\n const query = include ? buildMediaByIdQuery(include) : QUERY_MEDIA_BY_ID;\n const data = await this.request<{ Media: Media }>(query, { id });\n return data.Media;\n }\n\n /**\n * Search for anime or manga.\n *\n * @param options - Search / filter parameters\n * @returns Paginated results with matching media\n *\n * @example\n * ```ts\n * const results = await client.searchMedia({\n * query: \"Naruto\",\n * type: MediaType.ANIME,\n * perPage: 5,\n * });\n * ```\n */\n async searchMedia(options: SearchMediaOptions = {}): Promise<PagedResult<Media>> {\n const { query: search, page = 1, perPage = 20, ...filters } = options;\n return this.pagedRequest<Media>(\n QUERY_MEDIA_SEARCH,\n { search, ...filters, page, perPage: this.clampPerPage(perPage) },\n \"media\",\n );\n }\n\n /**\n * Get currently trending anime or manga.\n *\n * @param type - `MediaType.ANIME` or `MediaType.MANGA` (defaults to ANIME)\n * @param page - Page number (default 1)\n * @param perPage - Results per page (default 20, max 50)\n */\n async getTrending(type: MediaType = MediaType.ANIME, page = 1, perPage = 20): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(QUERY_TRENDING, { type, page, perPage: this.clampPerPage(perPage) }, \"media\");\n }\n\n /**\n * Fetch a character by AniList ID.\n *\n * @param id - The AniList character ID\n * @param include - Optional include options (e.g. voice actors)\n * @returns The character object\n *\n * @example\n * ```ts\n * const spike = await client.getCharacter(1);\n * console.log(spike.name.full); // \"Spike Spiegel\"\n *\n * // With voice actors\n * const spike = await client.getCharacter(1, { voiceActors: true });\n * spike.media?.edges?.forEach((e) => {\n * console.log(e.node.title.romaji);\n * e.voiceActors?.forEach((va) => console.log(` VA: ${va.name.full}`));\n * });\n * ```\n */\n async getCharacter(id: number, include?: CharacterIncludeOptions): Promise<Character> {\n const query = include?.voiceActors ? QUERY_CHARACTER_BY_ID_WITH_VA : QUERY_CHARACTER_BY_ID;\n const data = await this.request<{ Character: Character }>(query, { id });\n return data.Character;\n }\n\n /**\n * Search for characters by name.\n *\n * @param options - Search / pagination parameters (includes optional `voiceActors`)\n * @returns Paginated results with matching characters\n *\n * @example\n * ```ts\n * const result = await client.searchCharacters({ query: \"Luffy\", perPage: 5 });\n *\n * // With voice actors\n * const result = await client.searchCharacters({ query: \"Luffy\", voiceActors: true });\n * ```\n */\n async searchCharacters(options: SearchCharacterOptions = {}): Promise<PagedResult<Character>> {\n const { query: search, page = 1, perPage = 20, voiceActors, ...rest } = options;\n const gqlQuery = voiceActors ? QUERY_CHARACTER_SEARCH_WITH_VA : QUERY_CHARACTER_SEARCH;\n return this.pagedRequest<Character>(\n gqlQuery,\n { search, ...rest, page, perPage: this.clampPerPage(perPage) },\n \"characters\",\n );\n }\n\n /**\n * Fetch a staff member by AniList ID.\n *\n * @param id - The AniList staff ID\n * @param include - Optional include options to fetch related data (e.g. media)\n * @returns The staff object\n *\n * @example\n * ```ts\n * const staff = await client.getStaff(95001);\n * console.log(staff.name.full);\n *\n * // With media the staff worked on\n * const staff = await client.getStaff(95001, { media: true });\n * staff.staffMedia?.nodes.forEach((m) => console.log(m.title.romaji));\n * ```\n */\n async getStaff(id: number, include?: StaffIncludeOptions): Promise<Staff> {\n if (include?.media) {\n const opts = typeof include.media === \"object\" ? include.media : {};\n const perPage = opts.perPage ?? 25;\n const data = await this.request<{ Staff: Staff }>(QUERY_STAFF_BY_ID_WITH_MEDIA, { id, perPage });\n return data.Staff;\n }\n const data = await this.request<{ Staff: Staff }>(QUERY_STAFF_BY_ID, { id });\n return data.Staff;\n }\n\n /**\n * Search for staff (voice actors, directors, etc.).\n *\n * @param options - Search / pagination parameters\n * @returns Paginated results with matching staff\n *\n * @example\n * ```ts\n * const result = await client.searchStaff({ query: \"Miyazaki\", perPage: 5 });\n * ```\n */\n async searchStaff(options: SearchStaffOptions = {}): Promise<PagedResult<Staff>> {\n const { query: search, page = 1, perPage = 20, sort } = options;\n return this.pagedRequest<Staff>(\n QUERY_STAFF_SEARCH,\n { search, sort, page, perPage: this.clampPerPage(perPage) },\n \"staff\",\n );\n }\n\n /**\n * Fetch a user by AniList ID.\n *\n * @param id - The AniList user ID\n * @returns The user object\n *\n * @example\n * ```ts\n * const user = await client.getUser(1);\n * console.log(user.name);\n * ```\n */\n async getUser(id: number): Promise<User> {\n const data = await this.request<{ User: User }>(QUERY_USER_BY_ID, { id });\n return data.User;\n }\n\n /**\n * Fetch a user by username.\n *\n * @param name - The AniList username\n * @returns The user object\n *\n * @example\n * ```ts\n * const user = await client.getUserByName(\"AniList\");\n * console.log(user.statistics);\n * ```\n */\n async getUserByName(name: string): Promise<User> {\n const data = await this.request<{ User: User }>(QUERY_USER_BY_NAME, { name });\n return data.User;\n }\n\n /**\n * Execute an arbitrary GraphQL query against the AniList API.\n * Useful for advanced use-cases not covered by the built-in methods.\n *\n * @param query - A valid GraphQL query string\n * @param variables - Optional variables object\n */\n async raw<T = unknown>(query: string, variables?: Record<string, unknown>): Promise<T> {\n return this.request<T>(query, variables);\n }\n\n /**\n * Get recently aired anime episodes.\n *\n * By default returns episodes that aired in the last 24 hours.\n *\n * @param options - Filter / pagination parameters\n * @returns Paginated list of airing schedule entries\n *\n * @example\n * ```ts\n * // Episodes that aired in the last 48h\n * const recent = await client.getAiredEpisodes({\n * airingAtGreater: Math.floor(Date.now() / 1000) - 48 * 3600,\n * });\n * ```\n */\n async getAiredEpisodes(options: GetAiringOptions = {}): Promise<PagedResult<AiringSchedule>> {\n const now = Math.floor(Date.now() / 1000);\n const variables: Record<string, unknown> = {\n airingAt_greater: options.airingAtGreater ?? now - 24 * 3600,\n airingAt_lesser: options.airingAtLesser ?? now,\n sort: options.sort ?? [\"TIME_DESC\"],\n page: options.page ?? 1,\n perPage: this.clampPerPage(options.perPage ?? 20),\n };\n\n return this.pagedRequest<AiringSchedule>(QUERY_AIRING_SCHEDULE, variables, \"airingSchedules\");\n }\n\n /**\n * Get manga that are currently releasing, sorted by most recently updated.\n *\n * This is the closest equivalent to \"recently released chapters\" on AniList,\n * since the API does not expose per-chapter airing schedules for manga.\n *\n * @param options - Pagination parameters\n * @returns Paginated list of currently releasing manga\n *\n * @example\n * ```ts\n * const chapters = await client.getAiredChapters({ perPage: 10 });\n * ```\n */\n async getAiredChapters(options: GetRecentChaptersOptions = {}): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(\n QUERY_RECENT_CHAPTERS,\n {\n page: options.page ?? 1,\n perPage: this.clampPerPage(options.perPage ?? 20),\n },\n \"media\",\n );\n }\n\n /**\n * Get upcoming (not yet released) anime and/or manga, sorted by popularity.\n *\n * @param options - Filter / pagination parameters\n * @returns Paginated list of planned media\n *\n * @example\n * ```ts\n * import { MediaType } from \"ani-client\";\n *\n * // Most anticipated upcoming anime\n * const planning = await client.getPlanning({ type: MediaType.ANIME, perPage: 10 });\n * ```\n */\n async getPlanning(options: GetPlanningOptions = {}): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(\n QUERY_PLANNING,\n {\n type: options.type,\n sort: options.sort ?? [\"POPULARITY_DESC\"],\n page: options.page ?? 1,\n perPage: this.clampPerPage(options.perPage ?? 20),\n },\n \"media\",\n );\n }\n\n /**\n * Get recommendations for a specific media.\n *\n * Returns other anime/manga that users have recommended based on the given media.\n *\n * @param mediaId - The AniList media ID\n * @param options - Optional sort / pagination parameters\n * @returns Paginated list of recommendations\n *\n * @example\n * ```ts\n * // Get recommendations for Cowboy Bebop\n * const recs = await client.getRecommendations(1);\n * recs.results.forEach((r) =>\n * console.log(`${r.mediaRecommendation.title.romaji} (rating: ${r.rating})`)\n * );\n * ```\n */\n async getRecommendations(\n mediaId: number,\n options: Omit<GetRecommendationsOptions, \"mediaId\"> = {},\n ): Promise<PagedResult<Recommendation>> {\n const variables: Record<string, unknown> = {\n mediaId,\n sort: options.sort ?? [\"RATING_DESC\"],\n page: options.page ?? 1,\n perPage: options.perPage ?? 20,\n };\n\n const data = await this.request<{\n Media: {\n recommendations: {\n pageInfo: PagedResult<Recommendation>[\"pageInfo\"];\n nodes: Recommendation[];\n };\n };\n }>(QUERY_RECOMMENDATIONS, variables);\n\n return {\n pageInfo: data.Media.recommendations.pageInfo,\n results: data.Media.recommendations.nodes,\n };\n }\n\n /**\n * Get anime (or manga) for a specific season and year.\n *\n * @param options - Season, year and optional filter / pagination parameters\n * @returns Paginated list of media for the given season\n *\n * @example\n * ```ts\n * import { MediaSeason } from \"ani-client\";\n *\n * const winter2026 = await client.getMediaBySeason({\n * season: MediaSeason.WINTER,\n * seasonYear: 2026,\n * perPage: 10,\n * });\n * ```\n */\n async getMediaBySeason(options: GetSeasonOptions): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(\n QUERY_MEDIA_BY_SEASON,\n {\n season: options.season,\n seasonYear: options.seasonYear,\n type: options.type ?? \"ANIME\",\n sort: options.sort ?? [\"POPULARITY_DESC\"],\n page: options.page ?? 1,\n perPage: this.clampPerPage(options.perPage ?? 20),\n },\n \"media\",\n );\n }\n\n /**\n * Get a user's anime or manga list.\n *\n * Provide either `userId` or `userName` to identify the user.\n * Requires `type` (ANIME or MANGA). Optionally filter by list status.\n *\n * @param options - User identifier, media type, and optional filters\n * @returns Paginated list of media list entries\n *\n * @example\n * ```ts\n * import { MediaType, MediaListStatus } from \"ani-client\";\n *\n * // Get a user's completed anime list\n * const list = await client.getUserMediaList({\n * userName: \"AniList\",\n * type: MediaType.ANIME,\n * status: MediaListStatus.COMPLETED,\n * });\n * list.results.forEach((entry) =>\n * console.log(`${entry.media.title.romaji} — ${entry.score}/100`)\n * );\n * ```\n */\n async getUserMediaList(options: GetUserMediaListOptions): Promise<PagedResult<MediaListEntry>> {\n if (!options.userId && !options.userName) {\n throw new Error(\"Either userId or userName must be provided\");\n }\n\n return this.pagedRequest<MediaListEntry>(\n QUERY_USER_MEDIA_LIST,\n {\n userId: options.userId,\n userName: options.userName,\n type: options.type,\n status: options.status,\n sort: options.sort,\n page: options.page ?? 1,\n perPage: this.clampPerPage(options.perPage ?? 20),\n },\n \"mediaList\",\n );\n }\n\n /**\n * Fetch a studio by its AniList ID.\n *\n * Returns studio details along with its most popular productions.\n *\n * @param id - The AniList studio ID\n */\n async getStudio(id: number): Promise<StudioDetail> {\n const data = await this.request<{ Studio: StudioDetail }>(QUERY_STUDIO_BY_ID, { id });\n return data.Studio;\n }\n\n /**\n * Search for studios by name.\n *\n * @param options - Search / pagination parameters\n * @returns Paginated list of studios\n *\n * @example\n * ```ts\n * const studios = await client.searchStudios({ query: \"MAPPA\" });\n * ```\n */\n async searchStudios(options: SearchStudioOptions = {}): Promise<PagedResult<StudioDetail>> {\n return this.pagedRequest<StudioDetail>(\n QUERY_STUDIO_SEARCH,\n {\n search: options.query,\n page: options.page ?? 1,\n perPage: this.clampPerPage(options.perPage ?? 20),\n },\n \"studios\",\n );\n }\n\n /**\n * Get all available genres on AniList.\n *\n * @returns Array of genre strings (e.g. \"Action\", \"Adventure\", ...)\n */\n async getGenres(): Promise<string[]> {\n const data = await this.request<{ GenreCollection: string[] }>(QUERY_GENRES);\n return data.GenreCollection;\n }\n\n /**\n * Get all available media tags on AniList.\n *\n * @returns Array of tag objects with id, name, description, category, isAdult\n */\n async getTags(): Promise<MediaTag[]> {\n const data = await this.request<{ MediaTagCollection: MediaTag[] }>(QUERY_TAGS);\n return data.MediaTagCollection;\n }\n\n /**\n * Auto-paginating async iterator.\n *\n * Wraps any paginated method and yields individual items across all pages.\n * Stops when `hasNextPage` is `false` or `maxPages` is reached.\n *\n * @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`\n * @param maxPages - Maximum number of pages to fetch (default: Infinity)\n * @returns An async iterable iterator of individual items\n *\n * @example\n * ```ts\n * // Iterate over all search results\n * for await (const anime of client.paginate((page) =>\n * client.searchMedia({ query: \"Naruto\", page, perPage: 10 })\n * )) {\n * console.log(anime.title.romaji);\n * }\n *\n * // Limit to 3 pages\n * for await (const anime of client.paginate(\n * (page) => client.getTrending(MediaType.ANIME, page, 20),\n * 3,\n * )) {\n * console.log(anime.title.romaji);\n * }\n * ```\n */\n async *paginate<T>(\n fetchPage: (page: number) => Promise<PagedResult<T>>,\n maxPages = Number.POSITIVE_INFINITY,\n ): AsyncGenerator<T, void, undefined> {\n let page = 1;\n let hasNext = true;\n\n while (hasNext && page <= maxPages) {\n const result = await fetchPage(page);\n for (const item of result.results) {\n yield item;\n }\n hasNext = result.pageInfo.hasNextPage === true;\n page++;\n }\n }\n\n // ── Batch queries ──\n\n /**\n * Fetch multiple media entries in a single API request.\n * Uses GraphQL aliases to batch up to 50 IDs per call.\n *\n * @param ids - Array of AniList media IDs\n * @returns Array of media objects (same order as input IDs)\n */\n async getMediaBatch(ids: number[]): Promise<Media[]> {\n if (ids.length === 0) return [];\n if (ids.length === 1) return [await this.getMedia(ids[0])];\n return this.executeBatch<Media>(ids, buildBatchMediaQuery, \"m\");\n }\n\n /**\n * Fetch multiple characters in a single API request.\n *\n * @param ids - Array of AniList character IDs\n * @returns Array of character objects (same order as input IDs)\n */\n async getCharacterBatch(ids: number[]): Promise<Character[]> {\n if (ids.length === 0) return [];\n if (ids.length === 1) return [await this.getCharacter(ids[0])];\n return this.executeBatch<Character>(ids, buildBatchCharacterQuery, \"c\");\n }\n\n /**\n * Fetch multiple staff members in a single API request.\n *\n * @param ids - Array of AniList staff IDs\n * @returns Array of staff objects (same order as input IDs)\n */\n async getStaffBatch(ids: number[]): Promise<Staff[]> {\n if (ids.length === 0) return [];\n if (ids.length === 1) return [await this.getStaff(ids[0])];\n return this.executeBatch<Staff>(ids, buildBatchStaffQuery, \"s\");\n }\n\n /** @internal */\n private async executeBatch<T>(ids: number[], buildQuery: (ids: number[]) => string, prefix: string): Promise<T[]> {\n const chunks = this.chunk(ids, 50);\n\n const chunkResults = await Promise.all(\n chunks.map(async (chunk) => {\n const query = buildQuery(chunk);\n const data = await this.request<Record<string, T>>(query);\n return chunk.map((_, i) => data[`${prefix}${i}`]);\n }),\n );\n\n return chunkResults.flat();\n }\n\n /** @internal */\n private chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n }\n\n // ── Cache management ──\n\n /**\n * Clear the entire response cache.\n */\n async clearCache(): Promise<void> {\n await this.cacheAdapter.clear();\n }\n\n /**\n * Number of entries currently in the cache (sync).\n * For async adapters like Redis, this may be approximate.\n */\n get cacheSize(): number {\n return this.cacheAdapter.size;\n }\n\n /**\n * Remove cache entries whose key matches the given pattern.\n *\n * @param pattern — A string (converted to RegExp) or RegExp\n * @returns Number of entries removed\n */\n async invalidateCache(pattern: string | RegExp): Promise<number> {\n if (this.cacheAdapter.invalidate) {\n return this.cacheAdapter.invalidate(pattern);\n }\n\n const allKeys = await this.cacheAdapter.keys();\n const regex = typeof pattern === \"string\" ? new RegExp(pattern) : pattern;\n let count = 0;\n for (const key of allKeys) {\n if (regex.test(key)) {\n await this.cacheAdapter.delete(key);\n count++;\n }\n }\n return count;\n }\n}\n","import type { CacheAdapter } from \"../types\";\n\n/**\n * Minimal interface representing a Redis client.\n * Compatible with both `ioredis` and `redis` (node-redis v4+).\n */\nexport interface RedisLikeClient {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, ...args: unknown[]): Promise<unknown>;\n del(...keys: (string | string[])[]): Promise<number>;\n keys(pattern: string): Promise<string[]>;\n /** Optional SCAN-based iteration — used when available to avoid blocking the server. */\n scanIterator?(options: { MATCH: string; COUNT?: number }): AsyncIterable<string>;\n}\n\nexport interface RedisCacheOptions {\n /** A Redis client instance (ioredis or node-redis). */\n client: RedisLikeClient;\n /** Key prefix to namespace ani-client entries (default: `\"ani:\"`) */\n prefix?: string;\n /** TTL in seconds (default: 86 400 = 24 h) */\n ttl?: number;\n}\n\n/**\n * Redis-backed cache adapter for AniListClient.\n *\n * @example\n * ```ts\n * import Redis from \"ioredis\";\n * import { AniListClient, RedisCache } from \"ani-client\";\n *\n * const redis = new Redis();\n * const client = new AniListClient({\n * cacheAdapter: new RedisCache({ client: redis }),\n * });\n * ```\n */\nexport class RedisCache implements CacheAdapter {\n private readonly client: RedisLikeClient;\n private readonly prefix: string;\n private readonly ttl: number;\n\n constructor(options: RedisCacheOptions) {\n this.client = options.client;\n this.prefix = options.prefix ?? \"ani:\";\n this.ttl = options.ttl ?? 86_400;\n }\n\n private prefixedKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n const raw = await this.client.get(this.prefixedKey(key));\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n await this.client.set(this.prefixedKey(key), JSON.stringify(data), \"EX\", this.ttl);\n }\n\n async delete(key: string): Promise<boolean> {\n const count = await this.client.del(this.prefixedKey(key));\n return count > 0;\n }\n\n /**\n * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.\n *\n * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.\n * Provide a client with `scanIterator` support for production use.\n * @internal\n */\n private async collectKeys(pattern: string): Promise<string[]> {\n if (this.client.scanIterator) {\n const keys: string[] = [];\n for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {\n keys.push(key);\n }\n return keys;\n }\n return this.client.keys(pattern);\n }\n\n async clear(): Promise<void> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n if (keys.length > 0) {\n await this.client.del(...keys);\n }\n }\n\n /**\n * Returns -1 because Redis keys can expire silently via TTL.\n * Use `getSize()` for an accurate count.\n */\n get size(): number {\n return -1;\n }\n\n /** Get the actual number of keys with this prefix in Redis. */\n async getSize(): Promise<number> {\n const keys = await this.client.keys(`${this.prefix}*`);\n return keys.length;\n }\n\n async keys(): Promise<string[]> {\n const raw = await this.collectKeys(`${this.prefix}*`);\n return raw.map((k) => k.slice(this.prefix.length));\n }\n\n /**\n * Remove all entries whose key matches the given glob pattern.\n *\n * @param pattern — A glob pattern (e.g. `\"*Media*\"`)\n * @returns Number of entries removed.\n */\n async invalidate(pattern: string | RegExp): Promise<number> {\n if (typeof pattern === \"string\") {\n const keys = await this.collectKeys(`${this.prefix}${pattern}`);\n if (keys.length === 0) return 0;\n return this.client.del(...keys);\n }\n // RegExp: collect all keys and filter\n const allKeys = await this.collectKeys(`${this.prefix}*`);\n const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));\n if (matching.length === 0) return 0;\n return this.client.del(...matching);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/queries/index.ts","../src/cache/index.ts","../src/errors/index.ts","../src/rate-limiter/index.ts","../src/types/media.ts","../src/types/character.ts","../src/types/staff.ts","../src/types/lists.ts","../src/utils/index.ts","../src/client/index.ts","../src/cache/redis.ts"],"names":["MediaType","MediaFormat","MediaStatus","MediaSeason","MediaSort","AiringSort","MediaRelationType","RecommendationSort","CharacterSort","CharacterRole","StaffSort","MediaListStatus","MediaListSort","data"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { MediaIncludeOptions } from \"../types\";\n\n/** Core media fields — always returned. Does NOT include relations (opt-in via include). */\nconst MEDIA_FIELDS_BASE = `\n id\n idMal\n title { romaji english native userPreferred }\n type\n format\n status\n description(asHtml: false)\n startDate { year month day }\n endDate { year month day }\n season\n seasonYear\n episodes\n duration\n chapters\n volumes\n countryOfOrigin\n isLicensed\n source\n hashtag\n trailer { id site thumbnail }\n coverImage { extraLarge large medium color }\n bannerImage\n genres\n synonyms\n averageScore\n meanScore\n popularity\n favourites\n trending\n tags { id name description category rank isMediaSpoiler }\n studios { nodes { id name isAnimationStudio siteUrl } }\n isAdult\n siteUrl\n`;\n\nconst RELATIONS_FIELDS = `\n relations {\n edges {\n relationType(version: 2)\n node {\n id\n title { romaji english native userPreferred }\n type\n format\n status\n coverImage { large medium }\n siteUrl\n }\n }\n }\n`;\n\n/** Full media fields with relations — used by existing queries for backward compat. */\nconst MEDIA_FIELDS = `\n ${MEDIA_FIELDS_BASE}\n ${RELATIONS_FIELDS}\n`;\n\n/** Character fields without back-reference to media (used when embedding characters inside a Media query). */\nconst CHARACTER_FIELDS_COMPACT = `\n id\n name { first middle last full native alternative }\n image { large medium }\n description(asHtml: false)\n gender\n dateOfBirth { year month day }\n age\n bloodType\n favourites\n siteUrl\n`;\n\nconst CHARACTER_MEDIA_NODES = `\n media(perPage: 10) {\n nodes {\n id\n title { romaji english native userPreferred }\n type\n coverImage { large medium }\n siteUrl\n }\n }\n`;\n\n/** Compact voice actor fields — lightweight subset for embedding inside character edges. */\nconst VOICE_ACTOR_FIELDS_COMPACT = `\n id\n name { first middle last full native userPreferred }\n languageV2\n image { large medium }\n gender\n primaryOccupations\n siteUrl\n`;\n\nconst CHARACTER_MEDIA_EDGES_WITH_VA = `\n media(perPage: 10) {\n edges {\n voiceActors {\n ${VOICE_ACTOR_FIELDS_COMPACT}\n }\n node {\n id\n title { romaji english native userPreferred }\n type\n coverImage { large medium }\n siteUrl\n }\n }\n }\n`;\n\nconst CHARACTER_FIELDS = `\n ${CHARACTER_FIELDS_COMPACT}\n ${CHARACTER_MEDIA_NODES}\n`;\n\nconst CHARACTER_FIELDS_WITH_VA = `\n ${CHARACTER_FIELDS_COMPACT}\n ${CHARACTER_MEDIA_EDGES_WITH_VA}\n`;\n\nconst STAFF_FIELDS = `\n id\n name { first middle last full native }\n language\n image { large medium }\n description(asHtml: false)\n primaryOccupations\n gender\n dateOfBirth { year month day }\n dateOfDeath { year month day }\n age\n yearsActive\n homeTown\n bloodType\n favourites\n siteUrl\n`;\n\nconst STAFF_MEDIA_FIELDS = `\n staffMedia(perPage: $perPage, sort: [POPULARITY_DESC]) {\n nodes {\n id\n title { romaji english native userPreferred }\n type\n format\n status\n coverImage { extraLarge large medium color }\n bannerImage\n genres\n averageScore\n meanScore\n popularity\n favourites\n episodes\n trending\n hashtag\n season\n seasonYear\n startDate { year month day }\n endDate { year month day }\n nextAiringEpisode {\n id\n airingAt\n episode\n mediaId\n timeUntilAiring\n }\n studios {\n edges {\n node {\n name\n }\n }\n }\n siteUrl\n }\n }\n`;\n\nconst USER_FIELDS = `\n id\n name\n about(asHtml: false)\n avatar { large medium }\n bannerImage\n isFollowing\n isFollower\n donatorTier\n donatorBadge\n createdAt\n siteUrl\n statistics {\n anime { count meanScore minutesWatched episodesWatched chaptersRead volumesRead }\n manga { count meanScore minutesWatched episodesWatched chaptersRead volumesRead }\n }\n`;\n\nexport const QUERY_MEDIA_BY_ID = `\nquery ($id: Int!) {\n Media(id: $id) {\n ${MEDIA_FIELDS}\n }\n}`;\n\nexport const QUERY_MEDIA_SEARCH = `\nquery (\n $search: String,\n $type: MediaType,\n $format: MediaFormat,\n $status: MediaStatus,\n $season: MediaSeason,\n $seasonYear: Int,\n $genre: String,\n $tag: String,\n $isAdult: Boolean,\n $sort: [MediaSort],\n $page: Int,\n $perPage: Int\n) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(\n search: $search,\n type: $type,\n format: $format,\n status: $status,\n season: $season,\n seasonYear: $seasonYear,\n genre: $genre,\n tag: $tag,\n isAdult: $isAdult,\n sort: $sort\n ) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_TRENDING = `\nquery ($type: MediaType, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(type: $type, sort: TRENDING_DESC) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_CHARACTER_BY_ID = `\nquery ($id: Int!) {\n Character(id: $id) {\n ${CHARACTER_FIELDS}\n }\n}`;\n\nexport const QUERY_CHARACTER_BY_ID_WITH_VA = `\nquery ($id: Int!) {\n Character(id: $id) {\n ${CHARACTER_FIELDS_WITH_VA}\n }\n}`;\n\nexport const QUERY_CHARACTER_SEARCH = `\nquery ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n characters(search: $search, sort: $sort) {\n ${CHARACTER_FIELDS}\n }\n }\n}`;\n\nexport const QUERY_CHARACTER_SEARCH_WITH_VA = `\nquery ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n characters(search: $search, sort: $sort) {\n ${CHARACTER_FIELDS_WITH_VA}\n }\n }\n}`;\n\nexport const QUERY_STAFF_BY_ID = `\nquery ($id: Int!) {\n Staff(id: $id) {\n ${STAFF_FIELDS}\n }\n}`;\n\nexport const QUERY_STAFF_BY_ID_WITH_MEDIA = `\nquery ($id: Int!, $perPage: Int) {\n Staff(id: $id) {\n ${STAFF_FIELDS}\n ${STAFF_MEDIA_FIELDS}\n }\n}`;\n\nexport const QUERY_STAFF_SEARCH = `\nquery ($search: String, $sort: [StaffSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n staff(search: $search, sort: $sort) {\n ${STAFF_FIELDS}\n }\n }\n}`;\n\nexport const QUERY_USER_BY_ID = `\nquery ($id: Int!) {\n User(id: $id) {\n ${USER_FIELDS}\n }\n}`;\n\nexport const QUERY_USER_BY_NAME = `\nquery ($name: String!) {\n User(name: $name) {\n ${USER_FIELDS}\n }\n}`;\n\nexport const QUERY_AIRING_SCHEDULE = `\nquery ($airingAt_greater: Int, $airingAt_lesser: Int, $sort: [AiringSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n airingSchedules(airingAt_greater: $airingAt_greater, airingAt_lesser: $airingAt_lesser, sort: $sort) {\n id\n airingAt\n timeUntilAiring\n episode\n mediaId\n media {\n ${MEDIA_FIELDS_BASE}\n }\n }\n }\n}`;\n\nexport const QUERY_RECENT_CHAPTERS = `\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(type: MANGA, status: RELEASING, sort: UPDATED_AT_DESC) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_PLANNING = `\nquery ($type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(type: $type, status: NOT_YET_RELEASED, sort: $sort) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nexport const QUERY_MEDIA_BY_SEASON = `\nquery ($season: MediaSeason!, $seasonYear: Int!, $type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n media(season: $season, seasonYear: $seasonYear, type: $type, sort: $sort) {\n ${MEDIA_FIELDS_BASE}\n }\n }\n}`;\n\nconst MEDIA_LIST_FIELDS = `\n id\n mediaId\n status\n score(format: POINT_100)\n progress\n progressVolumes\n repeat\n priority\n private\n notes\n startedAt { year month day }\n completedAt { year month day }\n updatedAt\n createdAt\n media {\n ${MEDIA_FIELDS_BASE}\n }\n`;\n\nexport const QUERY_RECOMMENDATIONS = `\nquery ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {\n Media(id: $mediaId) {\n recommendations(page: $page, perPage: $perPage, sort: $sort) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n nodes {\n id\n rating\n userRating\n mediaRecommendation {\n id\n idMal\n title { romaji english native userPreferred }\n type\n format\n status\n coverImage { extraLarge large medium color }\n bannerImage\n genres\n averageScore\n meanScore\n popularity\n favourites\n siteUrl\n }\n user {\n id\n name\n avatar { large medium }\n }\n }\n }\n }\n}`;\n\nexport const QUERY_USER_MEDIA_LIST = `\nquery ($userId: Int, $userName: String, $type: MediaType!, $status: MediaListStatus, $sort: [MediaListSort], $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n mediaList(userId: $userId, userName: $userName, type: $type, status: $status, sort: $sort) {\n ${MEDIA_LIST_FIELDS}\n }\n }\n}`;\n\nconst STUDIO_FIELDS = `\n id\n name\n isAnimationStudio\n siteUrl\n favourites\n media(page: 1, perPage: 25, sort: POPULARITY_DESC) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n nodes {\n id\n title { romaji english native userPreferred }\n type\n format\n coverImage { large medium }\n siteUrl\n }\n }\n`;\n\nexport const QUERY_STUDIO_BY_ID = `\nquery ($id: Int!) {\n Studio(id: $id) {\n ${STUDIO_FIELDS}\n }\n}`;\n\nexport const QUERY_STUDIO_SEARCH = `\nquery ($search: String, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n studios(search: $search) {\n ${STUDIO_FIELDS}\n }\n }\n}`;\n\nexport const QUERY_GENRES = `\nquery {\n GenreCollection\n}`;\n\nexport const QUERY_TAGS = `\nquery {\n MediaTagCollection {\n id\n name\n description\n category\n isAdult\n }\n}`;\n\n// ── Dynamic media query builder ──\n\n/**\n * Build a `Media(id: $id)` query that optionally includes characters, staff,\n * relations, streaming episodes, external links, stats, and recommendations.\n *\n * When no include options are given, the query is identical to QUERY_MEDIA_BY_ID.\n *\n * @internal\n */\nexport function buildMediaByIdQuery(include?: MediaIncludeOptions): string {\n if (!include) return QUERY_MEDIA_BY_ID;\n\n const extra: string[] = [];\n\n // Relations — included by default (backward compat), opt-out with `relations: false`\n if (include.relations !== false) {\n extra.push(RELATIONS_FIELDS);\n }\n\n // Characters\n if (include.characters) {\n const opts = typeof include.characters === \"object\" ? include.characters : {};\n const perPage = opts.perPage ?? 25;\n const sortClause = opts.sort !== false ? \", sort: [ROLE, RELEVANCE, ID]\" : \"\";\n const voiceActorBlock = opts.voiceActors\n ? `\\n voiceActors {\n ${VOICE_ACTOR_FIELDS_COMPACT}\n }`\n : \"\";\n extra.push(`\n characters(perPage: ${perPage}${sortClause}) {\n edges {\n role\n node {\n ${CHARACTER_FIELDS_COMPACT}\n }${voiceActorBlock}\n }\n }`);\n }\n\n // Staff\n if (include.staff) {\n const opts = typeof include.staff === \"object\" ? include.staff : {};\n const perPage = opts.perPage ?? 25;\n const sortClause = opts.sort !== false ? \", sort: [RELEVANCE, ID]\" : \"\";\n extra.push(`\n staff(perPage: ${perPage}${sortClause}) {\n edges {\n role\n node {\n ${STAFF_FIELDS}\n }\n }\n }`);\n }\n\n // Recommendations\n if (include.recommendations) {\n const perPage = typeof include.recommendations === \"object\" ? (include.recommendations.perPage ?? 10) : 10;\n extra.push(`\n recommendations(perPage: ${perPage}, sort: [RATING_DESC]) {\n nodes {\n id\n rating\n mediaRecommendation {\n id\n title { romaji english native userPreferred }\n type\n format\n coverImage { large medium }\n averageScore\n siteUrl\n }\n }\n }`);\n }\n\n // Streaming episodes\n if (include.streamingEpisodes) {\n extra.push(`\n streamingEpisodes {\n title\n thumbnail\n url\n site\n }`);\n }\n\n // External links\n if (include.externalLinks) {\n extra.push(`\n externalLinks {\n id\n url\n site\n type\n icon\n color\n }`);\n }\n\n // Stats (score & status distribution)\n if (include.stats) {\n extra.push(`\n stats {\n scoreDistribution { score amount }\n statusDistribution { status amount }\n }`);\n }\n\n return `\nquery ($id: Int!) {\n Media(id: $id) {\n ${MEDIA_FIELDS_BASE}\n ${extra.join(\"\\n\")}\n }\n}`;\n}\n\n// ── Batch query builders ──\n\n/** @internal Build a batched GraphQL query using aliases. */\nfunction buildBatchQuery(ids: number[], typeName: string, fields: string, prefix: string): string {\n const aliases = ids.map((id, i) => `${prefix}${i}: ${typeName}(id: ${id}) { ${fields} }`).join(\"\\n \");\n return `query {\\n ${aliases}\\n}`;\n}\n\nexport const buildBatchMediaQuery = (ids: number[]): string => buildBatchQuery(ids, \"Media\", MEDIA_FIELDS_BASE, \"m\");\n\nexport const buildBatchCharacterQuery = (ids: number[]): string =>\n buildBatchQuery(ids, \"Character\", CHARACTER_FIELDS, \"c\");\n\nexport const buildBatchStaffQuery = (ids: number[]): string => buildBatchQuery(ids, \"Staff\", STAFF_FIELDS, \"s\");\n","import type { CacheAdapter, CacheOptions } from \"../types\";\n\n/**\n * Simple in-memory cache with configurable TTL.\n * Used internally by AniListClient to avoid redundant API calls.\n */\nexport type { CacheOptions };\n\ninterface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\nexport class MemoryCache implements CacheAdapter {\n private readonly ttl: number;\n private readonly maxSize: number;\n private readonly enabled: boolean;\n private readonly store = new Map<string, CacheEntry<unknown>>();\n\n constructor(options: CacheOptions = {}) {\n this.ttl = options.ttl ?? ONE_DAY_MS;\n this.maxSize = options.maxSize ?? 500;\n this.enabled = options.enabled ?? true;\n }\n\n /** Build a deterministic cache key from a query + variables pair. */\n static key(query: string, variables: Record<string, unknown>): string {\n const normalized = query.replace(/\\s+/g, \" \").trim();\n return `${normalized}|${JSON.stringify(variables, Object.keys(variables).sort())}`;\n }\n\n /** Retrieve a cached value, or `undefined` if missing / expired. */\n get<T>(key: string): T | undefined {\n if (!this.enabled) return undefined;\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n // LRU: promote to most-recently-used by re-inserting at the end\n this.store.delete(key);\n this.store.set(key, entry);\n return entry.data as T;\n }\n\n /** Store a value in the cache. */\n set<T>(key: string, data: T): void {\n if (!this.enabled) return;\n\n // Remove first so re-insert places it at the end (MRU position)\n this.store.delete(key);\n\n // Evict least-recently-used entry if at capacity\n if (this.maxSize > 0 && this.store.size >= this.maxSize) {\n const firstKey = this.store.keys().next().value;\n if (firstKey !== undefined) this.store.delete(firstKey);\n }\n\n this.store.set(key, { data, expiresAt: Date.now() + this.ttl });\n }\n\n /** Remove a specific entry. */\n delete(key: string): boolean {\n return this.store.delete(key);\n }\n\n /** Clear the entire cache. */\n clear(): void {\n this.store.clear();\n }\n\n /** Number of entries currently stored. */\n get size(): number | Promise<number> {\n return this.store.size;\n }\n\n /** Return all cache keys. */\n keys(): string[] {\n return [...this.store.keys()];\n }\n\n /**\n * Remove all entries whose key matches the given pattern.\n *\n * @param pattern — A string (converted to RegExp) or RegExp.\n * @returns Number of entries removed.\n */\n invalidate(pattern: string | RegExp): number {\n const regex = typeof pattern === \"string\" ? new RegExp(pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")) : pattern;\n const toDelete: string[] = [];\n for (const key of this.store.keys()) {\n if (regex.test(key)) toDelete.push(key);\n }\n for (const key of toDelete) this.store.delete(key);\n return toDelete.length;\n }\n}\n","/**\n * Custom error class for AniList API errors.\n */\nexport class AniListError extends Error {\n /** HTTP status code returned by the API */\n public readonly status: number;\n /** Raw error body from the API response */\n public readonly errors: unknown[];\n\n constructor(message: string, status: number, errors: unknown[] = []) {\n super(message);\n this.name = \"AniListError\";\n this.status = status;\n this.errors = errors;\n Object.setPrototypeOf(this, AniListError.prototype);\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AniListError);\n }\n }\n}\n","/**\n * Rate limiter with automatic retry for AniList API.\n *\n * AniList allows 90 requests per minute.\n * When a 429 (Too Many Requests) is received, the client\n * waits for the Retry-After header and retries automatically.\n */\n\nimport type { RateLimitOptions } from \"../types\";\nexport type { RateLimitOptions };\n\nexport class RateLimiter {\n private readonly maxRequests: number;\n private readonly windowMs: number;\n private readonly maxRetries: number;\n private readonly retryDelayMs: number;\n private readonly enabled: boolean;\n private readonly timeoutMs: number;\n private readonly retryOnNetworkError: boolean;\n\n /** @internal */\n private timestamps: number[] = [];\n\n constructor(options: RateLimitOptions = {}) {\n this.maxRequests = options.maxRequests ?? 85;\n this.windowMs = options.windowMs ?? 60_000;\n this.maxRetries = options.maxRetries ?? 3;\n this.retryDelayMs = options.retryDelayMs ?? 2_000;\n this.enabled = options.enabled ?? true;\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.retryOnNetworkError = options.retryOnNetworkError ?? true;\n }\n\n /**\n * Wait until it's safe to make a request (respects rate limit window).\n */\n async acquire(): Promise<void> {\n if (!this.enabled) return;\n\n const now = Date.now();\n this.timestamps = this.timestamps.filter((t) => now - t < this.windowMs);\n\n if (this.timestamps.length >= this.maxRequests) {\n // biome-ignore lint/style/noNonNullAssertion: length check guarantees [0] exists\n const oldest = this.timestamps[0]!;\n const waitMs = this.windowMs - (now - oldest) + 50;\n await this.sleep(waitMs);\n return this.acquire(); // Re-check after waiting\n }\n\n this.timestamps.push(Date.now());\n }\n\n /**\n * Execute a fetch with automatic retry on 429 responses and network errors.\n */\n async fetchWithRetry(\n url: string,\n init: RequestInit,\n hooks?: {\n onRetry?: (attempt: number, reason: string, delayMs: number) => void;\n onRateLimit?: (retryAfterMs: number) => void;\n },\n ): Promise<Response> {\n await this.acquire();\n\n let lastResponse: Response | undefined;\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const res = await this.fetchWithTimeout(url, init);\n\n if (res.status !== 429) return res;\n\n lastResponse = res;\n if (attempt === this.maxRetries) break;\n\n const retryAfter = res.headers.get(\"Retry-After\");\n const delayMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1000 : this.retryDelayMs * (attempt + 1);\n\n hooks?.onRateLimit?.(delayMs);\n hooks?.onRetry?.(attempt + 1, \"HTTP 429\", delayMs);\n\n await this.sleep(delayMs);\n await this.acquire();\n } catch (err) {\n lastError = err;\n\n if (this.retryOnNetworkError && isNetworkError(err) && attempt < this.maxRetries) {\n const delayMs = this.retryDelayMs * (attempt + 1);\n hooks?.onRetry?.(attempt + 1, `Network error: ${(err as Error).message}`, delayMs);\n await this.sleep(delayMs);\n await this.acquire();\n continue;\n }\n\n throw err;\n }\n }\n\n if (lastResponse) return lastResponse;\n throw lastError;\n }\n\n /** @internal */\n private async fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n if (this.timeoutMs <= 0) return fetch(url, init);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/** Set of Node.js error codes that indicate a transient network failure. */\nconst RETRYABLE_NETWORK_CODES = new Set([\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n \"UND_ERR_SOCKET\",\n]);\n\n/** @internal */\nfunction isNetworkError(err: unknown): boolean {\n if (err instanceof TypeError && err.message === \"fetch failed\") return true;\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code && RETRYABLE_NETWORK_CODES.has(code)) return true;\n const cause = (err as { cause?: { code?: string } })?.cause?.code;\n if (cause && RETRYABLE_NETWORK_CODES.has(cause)) return true;\n return false;\n}\n","import type { Character, CharacterRole } from \"./character\";\nimport type { ExternalLink, FuzzyDate, PageInfo } from \"./common\";\nimport type { MediaListStatus } from \"./lists\";\nimport type { Staff, VoiceActor } from \"./staff\";\nimport type { StudioConnection } from \"./studio\";\nimport type { UserAvatar } from \"./user\";\n\nexport enum MediaType {\n ANIME = \"ANIME\",\n MANGA = \"MANGA\",\n}\n\nexport enum MediaFormat {\n TV = \"TV\",\n TV_SHORT = \"TV_SHORT\",\n MOVIE = \"MOVIE\",\n SPECIAL = \"SPECIAL\",\n OVA = \"OVA\",\n ONA = \"ONA\",\n MUSIC = \"MUSIC\",\n MANGA = \"MANGA\",\n NOVEL = \"NOVEL\",\n ONE_SHOT = \"ONE_SHOT\",\n}\n\nexport enum MediaStatus {\n FINISHED = \"FINISHED\",\n RELEASING = \"RELEASING\",\n NOT_YET_RELEASED = \"NOT_YET_RELEASED\",\n CANCELLED = \"CANCELLED\",\n HIATUS = \"HIATUS\",\n}\n\nexport enum MediaSeason {\n WINTER = \"WINTER\",\n SPRING = \"SPRING\",\n SUMMER = \"SUMMER\",\n FALL = \"FALL\",\n}\n\nexport enum MediaSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n TITLE_ROMAJI = \"TITLE_ROMAJI\",\n TITLE_ROMAJI_DESC = \"TITLE_ROMAJI_DESC\",\n TITLE_ENGLISH = \"TITLE_ENGLISH\",\n TITLE_ENGLISH_DESC = \"TITLE_ENGLISH_DESC\",\n TITLE_NATIVE = \"TITLE_NATIVE\",\n TITLE_NATIVE_DESC = \"TITLE_NATIVE_DESC\",\n TYPE = \"TYPE\",\n TYPE_DESC = \"TYPE_DESC\",\n FORMAT = \"FORMAT\",\n FORMAT_DESC = \"FORMAT_DESC\",\n START_DATE = \"START_DATE\",\n START_DATE_DESC = \"START_DATE_DESC\",\n END_DATE = \"END_DATE\",\n END_DATE_DESC = \"END_DATE_DESC\",\n SCORE = \"SCORE\",\n SCORE_DESC = \"SCORE_DESC\",\n POPULARITY = \"POPULARITY\",\n POPULARITY_DESC = \"POPULARITY_DESC\",\n TRENDING = \"TRENDING\",\n TRENDING_DESC = \"TRENDING_DESC\",\n EPISODES = \"EPISODES\",\n EPISODES_DESC = \"EPISODES_DESC\",\n DURATION = \"DURATION\",\n DURATION_DESC = \"DURATION_DESC\",\n STATUS = \"STATUS\",\n STATUS_DESC = \"STATUS_DESC\",\n FAVOURITES = \"FAVOURITES\",\n FAVOURITES_DESC = \"FAVOURITES_DESC\",\n UPDATED_AT = \"UPDATED_AT\",\n UPDATED_AT_DESC = \"UPDATED_AT_DESC\",\n SEARCH_MATCH = \"SEARCH_MATCH\",\n}\n\nexport enum AiringSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n MEDIA_ID = \"MEDIA_ID\",\n MEDIA_ID_DESC = \"MEDIA_ID_DESC\",\n TIME = \"TIME\",\n TIME_DESC = \"TIME_DESC\",\n EPISODE = \"EPISODE\",\n EPISODE_DESC = \"EPISODE_DESC\",\n}\n\nexport interface MediaTitle {\n romaji: string | null;\n english: string | null;\n native: string | null;\n userPreferred: string | null;\n}\n\nexport interface MediaCoverImage {\n extraLarge: string | null;\n large: string | null;\n medium: string | null;\n color: string | null;\n}\n\nexport interface MediaTrailer {\n id: string | null;\n site: string | null;\n thumbnail: string | null;\n}\n\nexport interface MediaTag {\n id: number;\n name: string;\n description: string | null;\n category: string | null;\n rank: number | null;\n isMediaSpoiler: boolean | null;\n}\n\nexport enum MediaRelationType {\n ADAPTATION = \"ADAPTATION\",\n PREQUEL = \"PREQUEL\",\n SEQUEL = \"SEQUEL\",\n PARENT = \"PARENT\",\n SIDE_STORY = \"SIDE_STORY\",\n CHARACTER = \"CHARACTER\",\n SUMMARY = \"SUMMARY\",\n ALTERNATIVE = \"ALTERNATIVE\",\n SPIN_OFF = \"SPIN_OFF\",\n OTHER = \"OTHER\",\n SOURCE = \"SOURCE\",\n COMPILATION = \"COMPILATION\",\n CONTAINS = \"CONTAINS\",\n}\n\nexport interface MediaEdge {\n relationType: MediaRelationType;\n node: Pick<Media, \"id\" | \"title\" | \"type\" | \"format\" | \"status\" | \"coverImage\" | \"siteUrl\">;\n}\n\nexport interface MediaConnection {\n edges: MediaEdge[];\n}\n\nexport interface MediaCharacterEdge {\n role: CharacterRole;\n node: Omit<Character, \"media\">;\n voiceActors?: VoiceActor[];\n}\n\nexport interface MediaCharacterConnection {\n edges: MediaCharacterEdge[];\n}\n\nexport interface MediaStaffEdge {\n role: string;\n node: Staff;\n}\n\nexport interface MediaStaffConnection {\n edges: MediaStaffEdge[];\n}\n\nexport interface StreamingEpisode {\n title: string | null;\n thumbnail: string | null;\n url: string | null;\n site: string | null;\n}\n\nexport interface ScoreDistribution {\n score: number;\n amount: number;\n}\n\nexport interface StatusDistribution {\n status: MediaListStatus | string;\n amount: number;\n}\n\nexport interface MediaStats {\n scoreDistribution: ScoreDistribution[];\n statusDistribution: StatusDistribution[];\n}\n\nexport interface MediaRecommendationNode {\n id: number;\n rating: number | null;\n mediaRecommendation: Pick<Media, \"id\" | \"title\" | \"type\" | \"format\" | \"coverImage\" | \"averageScore\" | \"siteUrl\">;\n}\n\nexport interface Media {\n id: number;\n idMal: number | null;\n title: MediaTitle;\n type: MediaType;\n format: MediaFormat | null;\n status: MediaStatus | null;\n description: string | null;\n startDate: FuzzyDate | null;\n endDate: FuzzyDate | null;\n season: MediaSeason | null;\n seasonYear: number | null;\n episodes: number | null;\n duration: number | null;\n chapters: number | null;\n volumes: number | null;\n countryOfOrigin: string | null;\n isLicensed: boolean | null;\n source: string | null;\n hashtag: string | null;\n trailer: MediaTrailer | null;\n coverImage: MediaCoverImage;\n bannerImage: string | null;\n genres: string[];\n synonyms: string[];\n averageScore: number | null;\n meanScore: number | null;\n popularity: number | null;\n favourites: number | null;\n trending: number | null;\n tags: MediaTag[];\n studios: StudioConnection;\n relations: MediaConnection | null;\n characters?: MediaCharacterConnection;\n staff?: MediaStaffConnection;\n streamingEpisodes?: StreamingEpisode[];\n externalLinks?: ExternalLink[];\n stats?: MediaStats;\n recommendations?: { nodes: MediaRecommendationNode[] };\n isAdult: boolean | null;\n siteUrl: string | null;\n}\n\nexport interface SearchMediaOptions {\n query?: string;\n type?: MediaType;\n format?: MediaFormat;\n status?: MediaStatus;\n season?: MediaSeason;\n seasonYear?: number;\n genre?: string;\n tag?: string;\n isAdult?: boolean;\n sort?: MediaSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface GetAiringOptions {\n /** Only show episodes that aired after this UNIX timestamp */\n airingAtGreater?: number;\n /** Only show episodes that aired before this UNIX timestamp */\n airingAtLesser?: number;\n /** Sort order (default: TIME_DESC) */\n sort?: AiringSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface GetRecentChaptersOptions {\n /** Page number (default: 1) */\n page?: number;\n /** Results per page (default: 20, max 50) */\n perPage?: number;\n}\n\nexport interface GetPlanningOptions {\n /** Filter by ANIME or MANGA (returns both if omitted) */\n type?: MediaType;\n /** Sort order (default: POPULARITY_DESC) */\n sort?: MediaSort[];\n page?: number;\n perPage?: number;\n}\n\nexport enum RecommendationSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n RATING = \"RATING\",\n RATING_DESC = \"RATING_DESC\",\n}\n\nexport interface Recommendation {\n id: number;\n rating: number | null;\n userRating: string | null;\n mediaRecommendation: Media;\n user: {\n id: number;\n name: string;\n avatar: UserAvatar;\n } | null;\n}\n\nexport interface GetRecommendationsOptions {\n /** The AniList media ID to get recommendations for */\n mediaId: number;\n /** Sort order (default: RATING_DESC) */\n sort?: RecommendationSort[];\n page?: number;\n perPage?: number;\n}\n\nexport interface GetSeasonOptions {\n /** The season (WINTER, SPRING, SUMMER, FALL) */\n season: MediaSeason;\n /** The year */\n seasonYear: number;\n /** Filter by ANIME or MANGA (defaults to ANIME) */\n type?: MediaType;\n /** Sort order (default: POPULARITY_DESC) */\n sort?: MediaSort[];\n page?: number;\n perPage?: number;\n}\n\n/**\n * Options to include additional related data when fetching a media entry.\n * Pass `true` to include with defaults, or an object to customize.\n */\nexport interface MediaIncludeOptions {\n /** Include characters with their roles (MAIN, SUPPORTING, BACKGROUND).\n * `true` = 25 results sorted by role. Object form to customize. */\n characters?: boolean | { perPage?: number; sort?: boolean; voiceActors?: boolean };\n /** Include staff members with their roles.\n * `true` = 25 results sorted by relevance. Object form to customize. */\n staff?: boolean | { perPage?: number; sort?: boolean };\n /** Include relations (default: `true` for backward compat). Set to `false` to exclude. */\n relations?: boolean;\n /** Include streaming episode links (Crunchyroll, Funimation, etc.) */\n streamingEpisodes?: boolean;\n /** Include external links (MAL, official site, etc.) */\n externalLinks?: boolean;\n /** Include score & status distribution stats */\n stats?: boolean;\n /** Include user recommendations. `true` = 10 results, or customize with `{ perPage }`. */\n recommendations?: boolean | { perPage?: number };\n}\n\nexport interface AiringSchedule {\n id: number;\n airingAt: number;\n timeUntilAiring: number;\n episode: number;\n mediaId: number;\n media: Media;\n}\n","import type { FuzzyDate } from \"./common\";\nimport type { Media } from \"./media\";\nimport type { VoiceActor } from \"./staff\";\n\nexport enum CharacterSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n ROLE = \"ROLE\",\n ROLE_DESC = \"ROLE_DESC\",\n SEARCH_MATCH = \"SEARCH_MATCH\",\n FAVOURITES = \"FAVOURITES\",\n FAVOURITES_DESC = \"FAVOURITES_DESC\",\n}\n\nexport enum CharacterRole {\n MAIN = \"MAIN\",\n SUPPORTING = \"SUPPORTING\",\n BACKGROUND = \"BACKGROUND\",\n}\n\nexport interface CharacterName {\n first: string | null;\n middle: string | null;\n last: string | null;\n full: string | null;\n native: string | null;\n alternative: string[];\n}\n\nexport interface CharacterImage {\n large: string | null;\n medium: string | null;\n}\n\nexport type CharacterMediaNode = Pick<Media, \"id\" | \"title\" | \"type\" | \"coverImage\" | \"siteUrl\">;\n\nexport interface CharacterMediaEdge {\n node: CharacterMediaNode;\n voiceActors?: VoiceActor[];\n}\n\nexport interface Character {\n id: number;\n name: CharacterName;\n image: CharacterImage;\n description: string | null;\n gender: string | null;\n dateOfBirth: FuzzyDate | null;\n age: string | null;\n bloodType: string | null;\n favourites: number | null;\n siteUrl: string | null;\n media: {\n nodes?: CharacterMediaNode[];\n edges?: CharacterMediaEdge[];\n } | null;\n}\n\n/** Options for including extra data when fetching a character. */\nexport interface CharacterIncludeOptions {\n /** Include voice actors for each media the character appears in. */\n voiceActors?: boolean;\n}\n\nexport interface SearchCharacterOptions {\n query?: string;\n sort?: CharacterSort[];\n page?: number;\n perPage?: number;\n /** Include voice actors for each media the character appears in. */\n voiceActors?: boolean;\n}\n","import type { FuzzyDate } from \"./common\";\nimport type { MediaCoverImage, MediaFormat, MediaSeason, MediaStatus, MediaTitle, MediaType } from \"./media\";\n\nexport enum StaffSort {\n ID = \"ID\",\n ID_DESC = \"ID_DESC\",\n ROLE = \"ROLE\",\n ROLE_DESC = \"ROLE_DESC\",\n LANGUAGE = \"LANGUAGE\",\n LANGUAGE_DESC = \"LANGUAGE_DESC\",\n SEARCH_MATCH = \"SEARCH_MATCH\",\n FAVOURITES = \"FAVOURITES\",\n FAVOURITES_DESC = \"FAVOURITES_DESC\",\n RELEVANCE = \"RELEVANCE\",\n}\n\nexport interface StaffName {\n first: string | null;\n middle: string | null;\n last: string | null;\n full: string | null;\n native: string | null;\n}\n\nexport interface StaffImage {\n large: string | null;\n medium: string | null;\n}\n\n/** A media node returned inside `Staff.staffMedia`. */\nexport interface StaffMediaNode {\n id: number;\n title: MediaTitle;\n type: MediaType;\n format: MediaFormat | null;\n status: MediaStatus | null;\n coverImage: MediaCoverImage;\n bannerImage: string | null;\n genres: string[];\n averageScore: number | null;\n meanScore: number | null;\n popularity: number | null;\n favourites: number | null;\n episodes: number | null;\n trending: number | null;\n hashtag: string | null;\n season: MediaSeason | null;\n seasonYear: number | null;\n startDate: FuzzyDate | null;\n endDate: FuzzyDate | null;\n nextAiringEpisode: {\n id: number;\n airingAt: number;\n episode: number;\n mediaId: number;\n timeUntilAiring: number;\n } | null;\n studios: { edges: { node: { name: string } }[] } | null;\n siteUrl: string | null;\n}\n\nexport interface Staff {\n id: number;\n name: StaffName;\n language: string | null;\n image: StaffImage;\n description: string | null;\n primaryOccupations: string[];\n gender: string | null;\n dateOfBirth: FuzzyDate | null;\n dateOfDeath: FuzzyDate | null;\n age: string | null;\n yearsActive: number[];\n homeTown: string | null;\n bloodType: string | null;\n favourites: number | null;\n siteUrl: string | null;\n /** Media the staff member has worked on — only present when requested via include options. */\n staffMedia?: {\n nodes: StaffMediaNode[];\n } | null;\n}\n\n/** Options to include additional related data when fetching a staff member by ID. */\nexport interface StaffIncludeOptions {\n /** Include media the staff member has worked on.\n * `true` = 25 results sorted by popularity. Object form to customize. */\n media?: boolean | { perPage?: number; sort?: boolean };\n}\n\nexport interface SearchStaffOptions {\n query?: string;\n sort?: StaffSort[];\n page?: number;\n perPage?: number;\n}\n\n/** Compact voice actor data returned inside character edges. */\nexport interface VoiceActor {\n id: number;\n name: {\n first: string | null;\n middle: string | null;\n last: string | null;\n full: string | null;\n native: string | null;\n userPreferred: string | null;\n };\n languageV2: string | null;\n image: StaffImage;\n gender: string | null;\n primaryOccupations: string[];\n siteUrl: string | null;\n}\n","import type { FuzzyDate } from \"./common\";\nimport type { Media, MediaType } from \"./media\";\n\nexport enum MediaListStatus {\n CURRENT = \"CURRENT\",\n PLANNING = \"PLANNING\",\n COMPLETED = \"COMPLETED\",\n DROPPED = \"DROPPED\",\n PAUSED = \"PAUSED\",\n REPEATING = \"REPEATING\",\n}\n\nexport enum MediaListSort {\n MEDIA_ID = \"MEDIA_ID\",\n MEDIA_ID_DESC = \"MEDIA_ID_DESC\",\n SCORE = \"SCORE\",\n SCORE_DESC = \"SCORE_DESC\",\n STATUS = \"STATUS\",\n STATUS_DESC = \"STATUS_DESC\",\n PROGRESS = \"PROGRESS\",\n PROGRESS_DESC = \"PROGRESS_DESC\",\n PROGRESS_VOLUMES = \"PROGRESS_VOLUMES\",\n PROGRESS_VOLUMES_DESC = \"PROGRESS_VOLUMES_DESC\",\n REPEAT = \"REPEAT\",\n REPEAT_DESC = \"REPEAT_DESC\",\n PRIORITY = \"PRIORITY\",\n PRIORITY_DESC = \"PRIORITY_DESC\",\n STARTED_ON = \"STARTED_ON\",\n STARTED_ON_DESC = \"STARTED_ON_DESC\",\n FINISHED_ON = \"FINISHED_ON\",\n FINISHED_ON_DESC = \"FINISHED_ON_DESC\",\n ADDED_TIME = \"ADDED_TIME\",\n ADDED_TIME_DESC = \"ADDED_TIME_DESC\",\n UPDATED_TIME = \"UPDATED_TIME\",\n UPDATED_TIME_DESC = \"UPDATED_TIME_DESC\",\n MEDIA_TITLE_ROMAJI = \"MEDIA_TITLE_ROMAJI\",\n MEDIA_TITLE_ROMAJI_DESC = \"MEDIA_TITLE_ROMAJI_DESC\",\n MEDIA_TITLE_ENGLISH = \"MEDIA_TITLE_ENGLISH\",\n MEDIA_TITLE_ENGLISH_DESC = \"MEDIA_TITLE_ENGLISH_DESC\",\n MEDIA_TITLE_NATIVE = \"MEDIA_TITLE_NATIVE\",\n MEDIA_TITLE_NATIVE_DESC = \"MEDIA_TITLE_NATIVE_DESC\",\n MEDIA_POPULARITY = \"MEDIA_POPULARITY\",\n MEDIA_POPULARITY_DESC = \"MEDIA_POPULARITY_DESC\",\n}\n\nexport interface MediaListEntry {\n id: number;\n mediaId: number;\n status: MediaListStatus;\n score: number | null;\n progress: number | null;\n progressVolumes: number | null;\n repeat: number | null;\n priority: number | null;\n private: boolean | null;\n notes: string | null;\n startedAt: FuzzyDate | null;\n completedAt: FuzzyDate | null;\n updatedAt: number | null;\n createdAt: number | null;\n media: Media;\n}\n\nexport interface GetUserMediaListOptions {\n /** User ID (provide either userId or userName) */\n userId?: number;\n /** Username (provide either userId or userName) */\n userName?: string;\n /** ANIME or MANGA */\n type: MediaType;\n /** Filter by list status (CURRENT, COMPLETED, etc.) */\n status?: MediaListStatus;\n /** Sort order */\n sort?: MediaListSort[];\n page?: number;\n perPage?: number;\n}\n","/**\n * Utility functions for internal use.\n */\n\n/**\n * Clamp a number to a maximum and minimum value.\n * Used internally to ensure perPage does not exceed AniList's limit of 50.\n *\n * @internal\n * @param value - The number to clamp\n * @returns The clamped number between 1 and 50\n */\nexport function clampPerPage(value: number): number {\n return Math.min(Math.max(value, 1), 50);\n}\n\n/**\n * Split an array into smaller chunks of a specific size.\n * Used internally for batching GraphQL queries.\n *\n * @internal\n * @param arr - The array to chunk\n * @param size - The maximum size of each chunk\n * @returns An array of chunks\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n","import {\n QUERY_AIRING_SCHEDULE,\n QUERY_CHARACTER_BY_ID,\n QUERY_CHARACTER_BY_ID_WITH_VA,\n QUERY_CHARACTER_SEARCH,\n QUERY_CHARACTER_SEARCH_WITH_VA,\n QUERY_GENRES,\n QUERY_MEDIA_BY_ID,\n QUERY_MEDIA_BY_SEASON,\n QUERY_MEDIA_SEARCH,\n QUERY_PLANNING,\n QUERY_RECENT_CHAPTERS,\n QUERY_RECOMMENDATIONS,\n QUERY_STAFF_BY_ID,\n QUERY_STAFF_BY_ID_WITH_MEDIA,\n QUERY_STAFF_SEARCH,\n QUERY_STUDIO_BY_ID,\n QUERY_STUDIO_SEARCH,\n QUERY_TAGS,\n QUERY_TRENDING,\n QUERY_USER_BY_ID,\n QUERY_USER_BY_NAME,\n QUERY_USER_MEDIA_LIST,\n buildBatchCharacterQuery,\n buildBatchMediaQuery,\n buildBatchStaffQuery,\n buildMediaByIdQuery,\n} from \"../queries\";\n\nimport { MemoryCache } from \"../cache\";\nimport { AniListError } from \"../errors\";\nimport { RateLimiter } from \"../rate-limiter\";\n\nimport { MediaType } from \"../types\";\nimport type {\n AiringSchedule,\n AniListClientOptions,\n AniListHooks,\n CacheAdapter,\n Character,\n CharacterIncludeOptions,\n GetAiringOptions,\n GetPlanningOptions,\n GetRecentChaptersOptions,\n GetRecommendationsOptions,\n GetSeasonOptions,\n GetUserMediaListOptions,\n Media,\n MediaIncludeOptions,\n MediaListEntry,\n MediaTag,\n PageInfo,\n PagedResult,\n Recommendation,\n SearchCharacterOptions,\n SearchMediaOptions,\n SearchStaffOptions,\n SearchStudioOptions,\n Staff,\n StaffIncludeOptions,\n StudioDetail,\n User,\n} from \"../types\";\n\nimport { chunk, clampPerPage } from \"../utils\";\n\nconst DEFAULT_API_URL = \"https://graphql.anilist.co\";\n\n/**\n * Lightweight AniList GraphQL client with built-in caching and rate limiting.\n *\n * @example\n * ```ts\n * import { AniListClient } from \"ani-client\";\n *\n * const client = new AniListClient();\n * const anime = await client.getMedia(1); // Cowboy Bebop\n * console.log(anime.title.romaji);\n *\n * // Custom cache & rate limit options\n * const client2 = new AniListClient({\n * cache: { ttl: 1000 * 60 * 60 }, // 1 hour cache\n * rateLimit: { maxRequests: 60 },\n * });\n * ```\n */\nexport class AniListClient {\n private readonly apiUrl: string;\n private readonly headers: Record<string, string>;\n private readonly cacheAdapter: CacheAdapter;\n private readonly rateLimiter: RateLimiter;\n private readonly hooks: AniListHooks;\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n constructor(options: AniListClientOptions = {}) {\n this.apiUrl = options.apiUrl ?? DEFAULT_API_URL;\n this.headers = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (options.token) {\n this.headers.Authorization = `Bearer ${options.token}`;\n }\n this.cacheAdapter = options.cacheAdapter ?? new MemoryCache(options.cache);\n this.rateLimiter = new RateLimiter(options.rateLimit);\n this.hooks = options.hooks ?? {};\n }\n\n /**\n * @internal\n */\n private async request<T>(query: string, variables: Record<string, unknown> = {}): Promise<T> {\n const cacheKey = MemoryCache.key(query, variables);\n\n // Check cache (await handles both sync and async adapters)\n const cached = await this.cacheAdapter.get<T>(cacheKey);\n if (cached !== undefined) {\n this.hooks.onCacheHit?.(cacheKey);\n this.hooks.onResponse?.(query, 0, true);\n return cached;\n }\n\n // Request deduplication — reuse in-flight request for the same key\n const existing = this.inFlight.get(cacheKey);\n if (existing) return existing as Promise<T>;\n\n const promise = this.executeRequest<T>(query, variables, cacheKey);\n this.inFlight.set(cacheKey, promise);\n\n try {\n return await promise;\n } finally {\n this.inFlight.delete(cacheKey);\n }\n }\n\n /** @internal */\n private async executeRequest<T>(query: string, variables: Record<string, unknown>, cacheKey: string): Promise<T> {\n const start = Date.now();\n this.hooks.onRequest?.(query, variables);\n\n const minifiedQuery = query.replace(/\\s+/g, \" \").trim();\n\n const res = await this.rateLimiter.fetchWithRetry(\n this.apiUrl,\n {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify({ query: minifiedQuery, variables }),\n },\n { onRetry: this.hooks.onRetry, onRateLimit: this.hooks.onRateLimit },\n );\n\n const json = (await res.json()) as { data?: T; errors?: unknown[] };\n\n if (!res.ok || json.errors) {\n const message =\n (json.errors as Array<{ message?: string }>)?.[0]?.message ?? `AniList API error (HTTP ${res.status})`;\n throw new AniListError(message, res.status, json.errors ?? []);\n }\n\n const data = json.data as T;\n await this.cacheAdapter.set(cacheKey, data);\n this.hooks.onResponse?.(query, Date.now() - start, false);\n return data;\n }\n\n /**\n * @internal\n * Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.\n */\n private async pagedRequest<T>(\n query: string,\n variables: Record<string, unknown>,\n field: string,\n ): Promise<PagedResult<T>> {\n const data = await this.request<{ Page: Record<string, unknown> & { pageInfo: PageInfo } }>(query, variables);\n const results = data.Page[field];\n if (!Array.isArray(results)) {\n throw new AniListError(`Unexpected response: missing field \"${field}\" in Page`, 0, []);\n }\n return { pageInfo: data.Page.pageInfo, results: results as T[] };\n }\n\n /**\n * Fetch a single media entry by its AniList ID.\n *\n * Optionally include related data (characters, staff, relations, etc.) via the `include` parameter.\n *\n * @param id - The AniList media ID\n * @param include - Optional related data to include\n * @returns The media object\n *\n * @example\n * ```ts\n * // Basic usage — same as before (includes relations by default)\n * const anime = await client.getMedia(1);\n *\n * // Include characters sorted by role, 25 results\n * const anime = await client.getMedia(1, { characters: true });\n *\n * // Include characters with voice actors\n * const anime = await client.getMedia(1, { characters: { voiceActors: true } });\n *\n * // Full control\n * const anime = await client.getMedia(1, {\n * characters: { perPage: 50, sort: true },\n * staff: true,\n * relations: true,\n * streamingEpisodes: true,\n * externalLinks: true,\n * stats: true,\n * recommendations: { perPage: 5 },\n * });\n *\n * // Exclude relations for a lighter response\n * const anime = await client.getMedia(1, { characters: true, relations: false });\n * ```\n */\n async getMedia(id: number, include?: MediaIncludeOptions): Promise<Media> {\n const query = include ? buildMediaByIdQuery(include) : QUERY_MEDIA_BY_ID;\n const data = await this.request<{ Media: Media }>(query, { id });\n return data.Media;\n }\n\n /**\n * Search for anime or manga.\n *\n * @param options - Search / filter parameters\n * @returns Paginated results with matching media\n *\n * @example\n * ```ts\n * const results = await client.searchMedia({\n * query: \"Naruto\",\n * type: MediaType.ANIME,\n * perPage: 5,\n * });\n * ```\n */\n async searchMedia(options: SearchMediaOptions = {}): Promise<PagedResult<Media>> {\n const { query: search, page = 1, perPage = 20, ...filters } = options;\n return this.pagedRequest<Media>(\n QUERY_MEDIA_SEARCH,\n { search, ...filters, page, perPage: clampPerPage(perPage) },\n \"media\",\n );\n }\n\n /**\n * Get currently trending anime or manga.\n *\n * @param type - `MediaType.ANIME` or `MediaType.MANGA` (defaults to ANIME)\n * @param page - Page number (default 1)\n * @param perPage - Results per page (default 20, max 50)\n */\n async getTrending(type: MediaType = MediaType.ANIME, page = 1, perPage = 20): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(QUERY_TRENDING, { type, page, perPage: clampPerPage(perPage) }, \"media\");\n }\n\n /**\n * Fetch a character by AniList ID.\n *\n * @param id - The AniList character ID\n * @param include - Optional include options (e.g. voice actors)\n * @returns The character object\n *\n * @example\n * ```ts\n * const spike = await client.getCharacter(1);\n * console.log(spike.name.full); // \"Spike Spiegel\"\n *\n * // With voice actors\n * const spike = await client.getCharacter(1, { voiceActors: true });\n * spike.media?.edges?.forEach((e) => {\n * console.log(e.node.title.romaji);\n * e.voiceActors?.forEach((va) => console.log(` VA: ${va.name.full}`));\n * });\n * ```\n */\n async getCharacter(id: number, include?: CharacterIncludeOptions): Promise<Character> {\n const query = include?.voiceActors ? QUERY_CHARACTER_BY_ID_WITH_VA : QUERY_CHARACTER_BY_ID;\n const data = await this.request<{ Character: Character }>(query, { id });\n return data.Character;\n }\n\n /**\n * Search for characters by name.\n *\n * @param options - Search / pagination parameters (includes optional `voiceActors`)\n * @returns Paginated results with matching characters\n *\n * @example\n * ```ts\n * const result = await client.searchCharacters({ query: \"Luffy\", perPage: 5 });\n *\n * // With voice actors\n * const result = await client.searchCharacters({ query: \"Luffy\", voiceActors: true });\n * ```\n */\n async searchCharacters(options: SearchCharacterOptions = {}): Promise<PagedResult<Character>> {\n const { query: search, page = 1, perPage = 20, voiceActors, ...rest } = options;\n const gqlQuery = voiceActors ? QUERY_CHARACTER_SEARCH_WITH_VA : QUERY_CHARACTER_SEARCH;\n return this.pagedRequest<Character>(\n gqlQuery,\n { search, ...rest, page, perPage: clampPerPage(perPage) },\n \"characters\",\n );\n }\n\n /**\n * Fetch a staff member by AniList ID.\n *\n * @param id - The AniList staff ID\n * @param include - Optional include options to fetch related data (e.g. media)\n * @returns The staff object\n *\n * @example\n * ```ts\n * const staff = await client.getStaff(95001);\n * console.log(staff.name.full);\n *\n * // With media the staff worked on\n * const staff = await client.getStaff(95001, { media: true });\n * staff.staffMedia?.nodes.forEach((m) => console.log(m.title.romaji));\n * ```\n */\n async getStaff(id: number, include?: StaffIncludeOptions): Promise<Staff> {\n if (include?.media) {\n const opts = typeof include.media === \"object\" ? include.media : {};\n const perPage = opts.perPage ?? 25;\n const data = await this.request<{ Staff: Staff }>(QUERY_STAFF_BY_ID_WITH_MEDIA, { id, perPage });\n return data.Staff;\n }\n const data = await this.request<{ Staff: Staff }>(QUERY_STAFF_BY_ID, { id });\n return data.Staff;\n }\n\n /**\n * Search for staff (voice actors, directors, etc.).\n *\n * @param options - Search / pagination parameters\n * @returns Paginated results with matching staff\n *\n * @example\n * ```ts\n * const result = await client.searchStaff({ query: \"Miyazaki\", perPage: 5 });\n * ```\n */\n async searchStaff(options: SearchStaffOptions = {}): Promise<PagedResult<Staff>> {\n const { query: search, page = 1, perPage = 20, sort } = options;\n return this.pagedRequest<Staff>(\n QUERY_STAFF_SEARCH,\n { search, sort, page, perPage: clampPerPage(perPage) },\n \"staff\",\n );\n }\n\n /**\n * Fetch a user by AniList ID.\n *\n * @param id - The AniList user ID\n * @returns The user object\n *\n * @example\n * ```ts\n * const user = await client.getUser(1);\n * console.log(user.name);\n * ```\n */\n async getUser(id: number): Promise<User> {\n const data = await this.request<{ User: User }>(QUERY_USER_BY_ID, { id });\n return data.User;\n }\n\n /**\n * Fetch a user by username.\n *\n * @param name - The AniList username\n * @returns The user object\n *\n * @example\n * ```ts\n * const user = await client.getUserByName(\"AniList\");\n * console.log(user.statistics);\n * ```\n */\n async getUserByName(name: string): Promise<User> {\n const data = await this.request<{ User: User }>(QUERY_USER_BY_NAME, { name });\n return data.User;\n }\n\n /**\n * Execute an arbitrary GraphQL query against the AniList API.\n * Useful for advanced use-cases not covered by the built-in methods.\n *\n * @param query - A valid GraphQL query string\n * @param variables - Optional variables object\n */\n async raw<T = unknown>(query: string, variables?: Record<string, unknown>): Promise<T> {\n return this.request<T>(query, variables);\n }\n\n /**\n * Get recently aired anime episodes.\n *\n * By default returns episodes that aired in the last 24 hours.\n *\n * @param options - Filter / pagination parameters\n * @returns Paginated list of airing schedule entries\n *\n * @example\n * ```ts\n * // Episodes that aired in the last 48h\n * const recent = await client.getAiredEpisodes({\n * airingAtGreater: Math.floor(Date.now() / 1000) - 48 * 3600,\n * });\n * ```\n */\n async getAiredEpisodes(options: GetAiringOptions = {}): Promise<PagedResult<AiringSchedule>> {\n const now = Math.floor(Date.now() / 1000);\n const variables: Record<string, unknown> = {\n airingAt_greater: options.airingAtGreater ?? now - 24 * 3600,\n airingAt_lesser: options.airingAtLesser ?? now,\n sort: options.sort ?? [\"TIME_DESC\"],\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n };\n\n return this.pagedRequest<AiringSchedule>(QUERY_AIRING_SCHEDULE, variables, \"airingSchedules\");\n }\n\n /**\n * Get manga that are currently releasing, sorted by most recently updated.\n *\n * This is the closest equivalent to \"recently released chapters\" on AniList,\n * since the API does not expose per-chapter airing schedules for manga.\n *\n * @param options - Pagination parameters\n * @returns Paginated list of currently releasing manga\n *\n * @example\n * ```ts\n * const chapters = await client.getAiredChapters({ perPage: 10 });\n * ```\n */\n async getAiredChapters(options: GetRecentChaptersOptions = {}): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(\n QUERY_RECENT_CHAPTERS,\n {\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n },\n \"media\",\n );\n }\n\n /**\n * Get upcoming (not yet released) anime and/or manga, sorted by popularity.\n *\n * @param options - Filter / pagination parameters\n * @returns Paginated list of planned media\n *\n * @example\n * ```ts\n * import { MediaType } from \"ani-client\";\n *\n * // Most anticipated upcoming anime\n * const planning = await client.getPlanning({ type: MediaType.ANIME, perPage: 10 });\n * ```\n */\n async getPlanning(options: GetPlanningOptions = {}): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(\n QUERY_PLANNING,\n {\n type: options.type,\n sort: options.sort ?? [\"POPULARITY_DESC\"],\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n },\n \"media\",\n );\n }\n\n /**\n * Get recommendations for a specific media.\n *\n * Returns other anime/manga that users have recommended based on the given media.\n *\n * @param mediaId - The AniList media ID\n * @param options - Optional sort / pagination parameters\n * @returns Paginated list of recommendations\n *\n * @example\n * ```ts\n * // Get recommendations for Cowboy Bebop\n * const recs = await client.getRecommendations(1);\n * recs.results.forEach((r) =>\n * console.log(`${r.mediaRecommendation.title.romaji} (rating: ${r.rating})`)\n * );\n * ```\n */\n async getRecommendations(\n mediaId: number,\n options: Omit<GetRecommendationsOptions, \"mediaId\"> = {},\n ): Promise<PagedResult<Recommendation>> {\n const variables: Record<string, unknown> = {\n mediaId,\n sort: options.sort ?? [\"RATING_DESC\"],\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n };\n\n const data = await this.request<{\n Media: {\n recommendations: {\n pageInfo: PagedResult<Recommendation>[\"pageInfo\"];\n nodes: Recommendation[];\n };\n };\n }>(QUERY_RECOMMENDATIONS, variables);\n\n return {\n pageInfo: data.Media.recommendations.pageInfo,\n results: data.Media.recommendations.nodes,\n };\n }\n\n /**\n * Get anime (or manga) for a specific season and year.\n *\n * @param options - Season, year and optional filter / pagination parameters\n * @returns Paginated list of media for the given season\n *\n * @example\n * ```ts\n * import { MediaSeason } from \"ani-client\";\n *\n * const winter2026 = await client.getMediaBySeason({\n * season: MediaSeason.WINTER,\n * seasonYear: 2026,\n * perPage: 10,\n * });\n * ```\n */\n async getMediaBySeason(options: GetSeasonOptions): Promise<PagedResult<Media>> {\n return this.pagedRequest<Media>(\n QUERY_MEDIA_BY_SEASON,\n {\n season: options.season,\n seasonYear: options.seasonYear,\n type: options.type ?? \"ANIME\",\n sort: options.sort ?? [\"POPULARITY_DESC\"],\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n },\n \"media\",\n );\n }\n\n /**\n * Get a user's anime or manga list.\n *\n * Provide either `userId` or `userName` to identify the user.\n * Requires `type` (ANIME or MANGA). Optionally filter by list status.\n *\n * @param options - User identifier, media type, and optional filters\n * @returns Paginated list of media list entries\n *\n * @example\n * ```ts\n * import { MediaType, MediaListStatus } from \"ani-client\";\n *\n * // Get a user's completed anime list\n * const list = await client.getUserMediaList({\n * userName: \"AniList\",\n * type: MediaType.ANIME,\n * status: MediaListStatus.COMPLETED,\n * });\n * list.results.forEach((entry) =>\n * console.log(`${entry.media.title.romaji} — ${entry.score}/100`)\n * );\n * ```\n */\n async getUserMediaList(options: GetUserMediaListOptions): Promise<PagedResult<MediaListEntry>> {\n if (!options.userId && !options.userName) {\n throw new Error(\"Either userId or userName must be provided\");\n }\n\n return this.pagedRequest<MediaListEntry>(\n QUERY_USER_MEDIA_LIST,\n {\n userId: options.userId,\n userName: options.userName,\n type: options.type,\n status: options.status,\n sort: options.sort,\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n },\n \"mediaList\",\n );\n }\n\n /**\n * Fetch a studio by its AniList ID.\n *\n * Returns studio details along with its most popular productions.\n *\n * @param id - The AniList studio ID\n */\n async getStudio(id: number): Promise<StudioDetail> {\n const data = await this.request<{ Studio: StudioDetail }>(QUERY_STUDIO_BY_ID, { id });\n return data.Studio;\n }\n\n /**\n * Search for studios by name.\n *\n * @param options - Search / pagination parameters\n * @returns Paginated list of studios\n *\n * @example\n * ```ts\n * const studios = await client.searchStudios({ query: \"MAPPA\" });\n * ```\n */\n async searchStudios(options: SearchStudioOptions = {}): Promise<PagedResult<StudioDetail>> {\n return this.pagedRequest<StudioDetail>(\n QUERY_STUDIO_SEARCH,\n {\n search: options.query,\n page: options.page ?? 1,\n perPage: clampPerPage(options.perPage ?? 20),\n },\n \"studios\",\n );\n }\n\n /**\n * Get all available genres on AniList.\n *\n * @returns Array of genre strings (e.g. \"Action\", \"Adventure\", ...)\n */\n async getGenres(): Promise<string[]> {\n const data = await this.request<{ GenreCollection: string[] }>(QUERY_GENRES);\n return data.GenreCollection;\n }\n\n /**\n * Get all available media tags on AniList.\n *\n * @returns Array of tag objects with id, name, description, category, isAdult\n */\n async getTags(): Promise<MediaTag[]> {\n const data = await this.request<{ MediaTagCollection: MediaTag[] }>(QUERY_TAGS);\n return data.MediaTagCollection;\n }\n\n /**\n * Auto-paginating async iterator.\n *\n * Wraps any paginated method and yields individual items across all pages.\n * Stops when `hasNextPage` is `false` or `maxPages` is reached.\n *\n * @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`\n * @param maxPages - Maximum number of pages to fetch (default: Infinity)\n * @returns An async iterable iterator of individual items\n *\n * @example\n * ```ts\n * // Iterate over all search results\n * for await (const anime of client.paginate((page) =>\n * client.searchMedia({ query: \"Naruto\", page, perPage: 10 })\n * )) {\n * console.log(anime.title.romaji);\n * }\n *\n * // Limit to 3 pages\n * for await (const anime of client.paginate(\n * (page) => client.getTrending(MediaType.ANIME, page, 20),\n * 3,\n * )) {\n * console.log(anime.title.romaji);\n * }\n * ```\n */\n async *paginate<T>(\n fetchPage: (page: number) => Promise<PagedResult<T>>,\n maxPages = Number.POSITIVE_INFINITY,\n ): AsyncGenerator<T, void, undefined> {\n let page = 1;\n let hasNext = true;\n\n while (hasNext && page <= maxPages) {\n const result = await fetchPage(page);\n for (const item of result.results) {\n yield item;\n }\n hasNext = result.pageInfo.hasNextPage === true;\n page++;\n }\n }\n\n // ── Batch queries ──\n\n /**\n * Fetch multiple media entries in a single API request.\n * Uses GraphQL aliases to batch up to 50 IDs per call.\n *\n * @param ids - Array of AniList media IDs\n * @returns Array of media objects (same order as input IDs)\n */\n async getMediaBatch(ids: number[]): Promise<Media[]> {\n if (ids.length === 0) return [];\n if (ids.length === 1) return [await this.getMedia(ids[0])];\n return this.executeBatch<Media>(ids, buildBatchMediaQuery, \"m\");\n }\n\n /**\n * Fetch multiple characters in a single API request.\n *\n * @param ids - Array of AniList character IDs\n * @returns Array of character objects (same order as input IDs)\n */\n async getCharacterBatch(ids: number[]): Promise<Character[]> {\n if (ids.length === 0) return [];\n if (ids.length === 1) return [await this.getCharacter(ids[0])];\n return this.executeBatch<Character>(ids, buildBatchCharacterQuery, \"c\");\n }\n\n /**\n * Fetch multiple staff members in a single API request.\n *\n * @param ids - Array of AniList staff IDs\n * @returns Array of staff objects (same order as input IDs)\n */\n async getStaffBatch(ids: number[]): Promise<Staff[]> {\n if (ids.length === 0) return [];\n if (ids.length === 1) return [await this.getStaff(ids[0])];\n return this.executeBatch<Staff>(ids, buildBatchStaffQuery, \"s\");\n }\n\n /** @internal */\n private async executeBatch<T>(ids: number[], buildQuery: (ids: number[]) => string, prefix: string): Promise<T[]> {\n const chunks = chunk(ids, 50);\n\n const chunkResults: T[][] = [];\n // Process chunks sequentially to prevent overloading the network queue\n for (const idChunk of chunks) {\n const query = buildQuery(idChunk);\n const data = await this.request<Record<string, T>>(query);\n chunkResults.push(idChunk.map((_, i) => data[`${prefix}${i}`]));\n }\n\n return chunkResults.flat();\n }\n\n // ── Cache management ──\n\n /**\n * Clear the entire response cache.\n */\n async clearCache(): Promise<void> {\n await this.cacheAdapter.clear();\n }\n\n /**\n * Number of entries currently in the cache.\n * For async adapters like Redis, this may return a Promise.\n */\n get cacheSize(): number | Promise<number> {\n return this.cacheAdapter.size;\n }\n\n /**\n * Remove cache entries whose key matches the given pattern.\n *\n * @param pattern — A string (converted to RegExp) or RegExp\n * @returns Number of entries removed\n */\n async invalidateCache(pattern: string | RegExp): Promise<number> {\n if (this.cacheAdapter.invalidate) {\n return this.cacheAdapter.invalidate(pattern);\n }\n\n const allKeys = await this.cacheAdapter.keys();\n const regex = typeof pattern === \"string\" ? new RegExp(pattern) : pattern;\n let count = 0;\n for (const key of allKeys) {\n if (regex.test(key)) {\n await this.cacheAdapter.delete(key);\n count++;\n }\n }\n return count;\n }\n}\n","import type { CacheAdapter } from \"../types\";\n\n/**\n * Minimal interface representing a Redis client.\n * Compatible with both `ioredis` and `redis` (node-redis v4+).\n */\nexport interface RedisLikeClient {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, ...args: unknown[]): Promise<unknown>;\n del(...keys: (string | string[])[]): Promise<number>;\n keys(pattern: string): Promise<string[]>;\n /** Optional SCAN-based iteration — used when available to avoid blocking the server. */\n scanIterator?(options: { MATCH: string; COUNT?: number }): AsyncIterable<string>;\n}\n\nexport interface RedisCacheOptions {\n /** A Redis client instance (ioredis or node-redis). */\n client: RedisLikeClient;\n /** Key prefix to namespace ani-client entries (default: `\"ani:\"`) */\n prefix?: string;\n /** TTL in seconds (default: 86 400 = 24 h) */\n ttl?: number;\n}\n\n/**\n * Redis-backed cache adapter for AniListClient.\n *\n * @example\n * ```ts\n * import Redis from \"ioredis\";\n * import { AniListClient, RedisCache } from \"ani-client\";\n *\n * const redis = new Redis();\n * const client = new AniListClient({\n * cacheAdapter: new RedisCache({ client: redis }),\n * });\n * ```\n */\nexport class RedisCache implements CacheAdapter {\n private readonly client: RedisLikeClient;\n private readonly prefix: string;\n private readonly ttl: number;\n\n constructor(options: RedisCacheOptions) {\n this.client = options.client;\n this.prefix = options.prefix ?? \"ani:\";\n this.ttl = options.ttl ?? 86_400;\n }\n\n private prefixedKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n const raw = await this.client.get(this.prefixedKey(key));\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n await this.client.set(this.prefixedKey(key), JSON.stringify(data), \"EX\", this.ttl);\n }\n\n async delete(key: string): Promise<boolean> {\n const count = await this.client.del(this.prefixedKey(key));\n return count > 0;\n }\n\n /**\n * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.\n *\n * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.\n * Provide a client with `scanIterator` support for production use.\n * @internal\n */\n private async collectKeys(pattern: string): Promise<string[]> {\n if (this.client.scanIterator) {\n const keys: string[] = [];\n for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {\n keys.push(key);\n }\n return keys;\n }\n return this.client.keys(pattern);\n }\n\n async clear(): Promise<void> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n if (keys.length > 0) {\n await this.client.del(...keys);\n }\n }\n\n /**\n * Get the actual number of keys with this prefix in Redis.\n */\n get size(): Promise<number> {\n return this.getSize();\n }\n\n /** @internal */\n private async getSize(): Promise<number> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n return keys.length;\n }\n\n async keys(): Promise<string[]> {\n const raw = await this.collectKeys(`${this.prefix}*`);\n return raw.map((k) => k.slice(this.prefix.length));\n }\n\n /**\n * Remove all entries whose key matches the given glob pattern.\n *\n * @param pattern — A glob pattern (e.g. `\"*Media*\"`)\n * @returns Number of entries removed.\n */\n async invalidate(pattern: string | RegExp): Promise<number> {\n if (typeof pattern === \"string\") {\n const keys = await this.collectKeys(`${this.prefix}${pattern}`);\n if (keys.length === 0) return 0;\n return this.client.del(...keys);\n }\n // RegExp: collect all keys and filter\n const allKeys = await this.collectKeys(`${this.prefix}*`);\n const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));\n if (matching.length === 0) return 0;\n return this.client.del(...matching);\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ani-client",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "A simple and typed client to fetch anime, manga, characters and user data from AniList",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": ["dist"],
|
|
16
|
+
"sideEffects": false,
|
|
16
17
|
"scripts": {
|
|
17
18
|
"build": "tsup",
|
|
18
19
|
"dev": "tsup --watch",
|
|
@@ -22,7 +23,10 @@
|
|
|
22
23
|
"test": "vitest run && tsx tests/client.test.ts",
|
|
23
24
|
"test:unit": "vitest run",
|
|
24
25
|
"test:integration": "tsx tests/client.test.ts",
|
|
25
|
-
"prepublishOnly": "pnpm run build"
|
|
26
|
+
"prepublishOnly": "pnpm run build",
|
|
27
|
+
"docs:dev": "vitepress dev docs",
|
|
28
|
+
"docs:build": "vitepress build docs",
|
|
29
|
+
"docs:preview": "vitepress preview docs"
|
|
26
30
|
},
|
|
27
31
|
"keywords": ["anilist", "anime", "manga", "graphql", "api", "client", "typescript"],
|
|
28
32
|
"author": "gonzyui",
|
|
@@ -34,7 +38,7 @@
|
|
|
34
38
|
"type": "git",
|
|
35
39
|
"url": "https://github.com/gonzyui/ani-client.git"
|
|
36
40
|
},
|
|
37
|
-
"homepage": "https://
|
|
41
|
+
"homepage": "https://ani-client-docs.vercel.app/",
|
|
38
42
|
"bugs": {
|
|
39
43
|
"url": "https://github.com/gonzyui/ani-client/issues"
|
|
40
44
|
},
|
|
@@ -44,7 +48,9 @@
|
|
|
44
48
|
"tsup": "^8.5.1",
|
|
45
49
|
"tsx": "^4.21.0",
|
|
46
50
|
"typescript": "^5.9.3",
|
|
47
|
-
"
|
|
51
|
+
"vitepress": "^1.6.4",
|
|
52
|
+
"vitest": "^3.0.0",
|
|
53
|
+
"vue": "^3.5.28"
|
|
48
54
|
},
|
|
49
55
|
"packageManager": "pnpm@10.30.0"
|
|
50
56
|
}
|