ani-client 1.4.2 → 1.4.3

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/dist/index.d.ts CHANGED
@@ -1,3 +1,320 @@
1
+ interface PageInfo {
2
+ total: number | null;
3
+ perPage: number | null;
4
+ currentPage: number | null;
5
+ lastPage: number | null;
6
+ hasNextPage: boolean | null;
7
+ }
8
+ interface PagedResult<T> {
9
+ pageInfo: PageInfo;
10
+ results: T[];
11
+ }
12
+ interface FuzzyDate {
13
+ year: number | null;
14
+ month: number | null;
15
+ day: number | null;
16
+ }
17
+ interface ExternalLink {
18
+ id: number;
19
+ url: string | null;
20
+ site: string;
21
+ type: string | null;
22
+ icon: string | null;
23
+ color: string | null;
24
+ }
25
+ /**
26
+ * Interface that all cache adapters must implement.
27
+ * Methods may return sync values or Promises — the client awaits all calls.
28
+ */
29
+ interface CacheAdapter {
30
+ /** Retrieve a cached value, or `undefined` if missing / expired. */
31
+ get<T>(key: string): T | undefined | Promise<T | undefined>;
32
+ /** Store a value in the cache. */
33
+ set<T>(key: string, data: T): void | Promise<void>;
34
+ /** Remove a specific entry. Returns `true` if the key existed. */
35
+ delete(key: string): boolean | Promise<boolean>;
36
+ /** Clear the entire cache. */
37
+ clear(): void | Promise<void>;
38
+ /** Number of entries currently stored. */
39
+ readonly size: number | Promise<number>;
40
+ /** Return all cache keys. */
41
+ keys(): string[] | Promise<string[]>;
42
+ /** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */
43
+ invalidate?(pattern: string | RegExp): number | Promise<number>;
44
+ }
45
+ /** Cache configuration options. */
46
+ interface CacheOptions {
47
+ /** Time-to-live in milliseconds (default: 86 400 000 = 24h) */
48
+ ttl?: number;
49
+ /** Maximum number of cached entries (default: 500, 0 = unlimited) */
50
+ maxSize?: number;
51
+ /** Set to false to disable caching entirely */
52
+ enabled?: boolean;
53
+ }
54
+ /** Rate limiter configuration options. */
55
+ interface RateLimitOptions {
56
+ /** Max requests per window (default: 85) */
57
+ maxRequests?: number;
58
+ /** Window size in ms (default: 60 000) */
59
+ windowMs?: number;
60
+ /** Max retries on 429 (default: 3) */
61
+ maxRetries?: number;
62
+ /** Retry delay in ms when Retry-After header is absent (default: 2000) */
63
+ retryDelayMs?: number;
64
+ /** Set to false to disable rate limiting entirely */
65
+ enabled?: boolean;
66
+ /** Timeout per request in ms (default: 30 000). 0 = no timeout. */
67
+ timeoutMs?: number;
68
+ /** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */
69
+ retryOnNetworkError?: boolean;
70
+ }
71
+ /** Event hooks for logging, debugging, and monitoring. */
72
+ interface AniListHooks {
73
+ /** Called before every API request. */
74
+ onRequest?: (query: string, variables: Record<string, unknown>) => void;
75
+ /** Called when a response is served from cache. */
76
+ onCacheHit?: (key: string) => void;
77
+ /** Called when the rate limiter enforces a wait (429 received). */
78
+ onRateLimit?: (retryAfterMs: number) => void;
79
+ /** Called when a request is retried (429 or network error). */
80
+ onRetry?: (attempt: number, reason: string, delayMs: number) => void;
81
+ /** Called when a request completes. */
82
+ onResponse?: (query: string, durationMs: number, fromCache: boolean) => void;
83
+ }
84
+ interface AniListClientOptions {
85
+ /** Optional AniList OAuth token for authenticated requests */
86
+ token?: string;
87
+ /** Custom API endpoint (defaults to https://graphql.anilist.co) */
88
+ apiUrl?: string;
89
+ /** Cache configuration (enabled by default, 24h TTL) */
90
+ cache?: CacheOptions;
91
+ /** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */
92
+ cacheAdapter?: CacheAdapter;
93
+ /** Rate limiter configuration (enabled by default, 85 req/min) */
94
+ rateLimit?: RateLimitOptions;
95
+ /** Event hooks for logging, debugging, and monitoring */
96
+ hooks?: AniListHooks;
97
+ }
98
+
99
+ interface StaffName {
100
+ first: string | null;
101
+ middle: string | null;
102
+ last: string | null;
103
+ full: string | null;
104
+ native: string | null;
105
+ }
106
+ interface StaffImage {
107
+ large: string | null;
108
+ medium: string | null;
109
+ }
110
+ /** A media node returned inside `Staff.staffMedia`. */
111
+ interface StaffMediaNode {
112
+ id: number;
113
+ title: MediaTitle;
114
+ type: MediaType;
115
+ format: MediaFormat | null;
116
+ status: MediaStatus | null;
117
+ coverImage: MediaCoverImage;
118
+ bannerImage: string | null;
119
+ genres: string[];
120
+ averageScore: number | null;
121
+ meanScore: number | null;
122
+ popularity: number | null;
123
+ favourites: number | null;
124
+ episodes: number | null;
125
+ trending: number | null;
126
+ hashtag: string | null;
127
+ season: MediaSeason | null;
128
+ seasonYear: number | null;
129
+ startDate: FuzzyDate | null;
130
+ endDate: FuzzyDate | null;
131
+ nextAiringEpisode: {
132
+ id: number;
133
+ airingAt: number;
134
+ episode: number;
135
+ mediaId: number;
136
+ timeUntilAiring: number;
137
+ } | null;
138
+ studios: {
139
+ edges: {
140
+ node: {
141
+ name: string;
142
+ };
143
+ }[];
144
+ } | null;
145
+ siteUrl: string | null;
146
+ }
147
+ interface Staff {
148
+ id: number;
149
+ name: StaffName;
150
+ language: string | null;
151
+ image: StaffImage;
152
+ description: string | null;
153
+ primaryOccupations: string[];
154
+ gender: string | null;
155
+ dateOfBirth: FuzzyDate | null;
156
+ dateOfDeath: FuzzyDate | null;
157
+ age: string | null;
158
+ yearsActive: number[];
159
+ homeTown: string | null;
160
+ bloodType: string | null;
161
+ favourites: number | null;
162
+ siteUrl: string | null;
163
+ /** Media the staff member has worked on — only present when requested via include options. */
164
+ staffMedia?: {
165
+ nodes: StaffMediaNode[];
166
+ } | null;
167
+ }
168
+ /** Options to include additional related data when fetching a staff member by ID. */
169
+ interface StaffIncludeOptions {
170
+ /** Include media the staff member has worked on.
171
+ * `true` = 25 results sorted by popularity. Object form to customize. */
172
+ media?: boolean | {
173
+ perPage?: number;
174
+ sort?: boolean;
175
+ };
176
+ }
177
+ interface SearchStaffOptions {
178
+ query?: string;
179
+ sort?: CharacterSort[];
180
+ page?: number;
181
+ perPage?: number;
182
+ }
183
+ /** Compact voice actor data returned inside character edges. */
184
+ interface VoiceActor {
185
+ id: number;
186
+ name: {
187
+ first: string | null;
188
+ middle: string | null;
189
+ last: string | null;
190
+ full: string | null;
191
+ native: string | null;
192
+ userPreferred: string | null;
193
+ };
194
+ languageV2: string | null;
195
+ image: StaffImage;
196
+ gender: string | null;
197
+ primaryOccupations: string[];
198
+ siteUrl: string | null;
199
+ }
200
+
201
+ declare enum CharacterSort {
202
+ ID = "ID",
203
+ ID_DESC = "ID_DESC",
204
+ ROLE = "ROLE",
205
+ ROLE_DESC = "ROLE_DESC",
206
+ SEARCH_MATCH = "SEARCH_MATCH",
207
+ FAVOURITES = "FAVOURITES",
208
+ FAVOURITES_DESC = "FAVOURITES_DESC"
209
+ }
210
+ declare enum CharacterRole {
211
+ MAIN = "MAIN",
212
+ SUPPORTING = "SUPPORTING",
213
+ BACKGROUND = "BACKGROUND"
214
+ }
215
+ interface CharacterName {
216
+ first: string | null;
217
+ middle: string | null;
218
+ last: string | null;
219
+ full: string | null;
220
+ native: string | null;
221
+ alternative: string[];
222
+ }
223
+ interface CharacterImage {
224
+ large: string | null;
225
+ medium: string | null;
226
+ }
227
+ type CharacterMediaNode = Pick<Media, "id" | "title" | "type" | "coverImage" | "siteUrl">;
228
+ interface CharacterMediaEdge {
229
+ node: CharacterMediaNode;
230
+ voiceActors?: VoiceActor[];
231
+ }
232
+ interface Character {
233
+ id: number;
234
+ name: CharacterName;
235
+ image: CharacterImage;
236
+ description: string | null;
237
+ gender: string | null;
238
+ dateOfBirth: FuzzyDate | null;
239
+ age: string | null;
240
+ bloodType: string | null;
241
+ favourites: number | null;
242
+ siteUrl: string | null;
243
+ media: {
244
+ nodes?: CharacterMediaNode[];
245
+ edges?: CharacterMediaEdge[];
246
+ } | null;
247
+ }
248
+ /** Options for including extra data when fetching a character. */
249
+ interface CharacterIncludeOptions {
250
+ /** Include voice actors for each media the character appears in. */
251
+ voiceActors?: boolean;
252
+ }
253
+ interface SearchCharacterOptions {
254
+ query?: string;
255
+ sort?: CharacterSort[];
256
+ page?: number;
257
+ perPage?: number;
258
+ /** Include voice actors for each media the character appears in. */
259
+ voiceActors?: boolean;
260
+ }
261
+
262
+ interface Studio {
263
+ id: number;
264
+ name: string;
265
+ isAnimationStudio: boolean;
266
+ siteUrl: string | null;
267
+ }
268
+ interface StudioConnection {
269
+ nodes: Studio[];
270
+ }
271
+ interface StudioDetail {
272
+ id: number;
273
+ name: string;
274
+ isAnimationStudio: boolean;
275
+ siteUrl: string | null;
276
+ favourites: number | null;
277
+ media: {
278
+ pageInfo: PageInfo;
279
+ nodes: Pick<Media, "id" | "title" | "type" | "format" | "coverImage" | "siteUrl">[];
280
+ } | null;
281
+ }
282
+ interface SearchStudioOptions {
283
+ query?: string;
284
+ page?: number;
285
+ perPage?: number;
286
+ }
287
+
288
+ interface UserAvatar {
289
+ large: string | null;
290
+ medium: string | null;
291
+ }
292
+ interface UserStatistics {
293
+ count: number;
294
+ meanScore: number;
295
+ minutesWatched: number;
296
+ episodesWatched: number;
297
+ chaptersRead: number;
298
+ volumesRead: number;
299
+ }
300
+ interface User {
301
+ id: number;
302
+ name: string;
303
+ about: string | null;
304
+ avatar: UserAvatar;
305
+ bannerImage: string | null;
306
+ isFollowing: boolean | null;
307
+ isFollower: boolean | null;
308
+ donatorTier: number | null;
309
+ donatorBadge: string | null;
310
+ createdAt: number | null;
311
+ siteUrl: string | null;
312
+ statistics: {
313
+ anime: UserStatistics;
314
+ manga: UserStatistics;
315
+ } | null;
316
+ }
317
+
1
318
  declare enum MediaType {
2
319
  ANIME = "ANIME",
3
320
  MANGA = "MANGA"
@@ -72,20 +389,6 @@ declare enum AiringSort {
72
389
  EPISODE = "EPISODE",
73
390
  EPISODE_DESC = "EPISODE_DESC"
74
391
  }
75
- declare enum CharacterSort {
76
- ID = "ID",
77
- ID_DESC = "ID_DESC",
78
- ROLE = "ROLE",
79
- ROLE_DESC = "ROLE_DESC",
80
- SEARCH_MATCH = "SEARCH_MATCH",
81
- FAVOURITES = "FAVOURITES",
82
- FAVOURITES_DESC = "FAVOURITES_DESC"
83
- }
84
- declare enum CharacterRole {
85
- MAIN = "MAIN",
86
- SUPPORTING = "SUPPORTING",
87
- BACKGROUND = "BACKGROUND"
88
- }
89
392
  interface MediaTitle {
90
393
  romaji: string | null;
91
394
  english: string | null;
@@ -98,11 +401,6 @@ interface MediaCoverImage {
98
401
  medium: string | null;
99
402
  color: string | null;
100
403
  }
101
- interface FuzzyDate {
102
- year: number | null;
103
- month: number | null;
104
- day: number | null;
105
- }
106
404
  interface MediaTrailer {
107
405
  id: string | null;
108
406
  site: string | null;
@@ -116,15 +414,6 @@ interface MediaTag {
116
414
  rank: number | null;
117
415
  isMediaSpoiler: boolean | null;
118
416
  }
119
- interface Studio {
120
- id: number;
121
- name: string;
122
- isAnimationStudio: boolean;
123
- siteUrl: string | null;
124
- }
125
- interface StudioConnection {
126
- nodes: Studio[];
127
- }
128
417
  declare enum MediaRelationType {
129
418
  ADAPTATION = "ADAPTATION",
130
419
  PREQUEL = "PREQUEL",
@@ -147,35 +436,6 @@ interface MediaEdge {
147
436
  interface MediaConnection {
148
437
  edges: MediaEdge[];
149
438
  }
150
- interface CharacterName {
151
- first: string | null;
152
- middle: string | null;
153
- last: string | null;
154
- full: string | null;
155
- native: string | null;
156
- alternative: string[];
157
- }
158
- interface CharacterImage {
159
- large: string | null;
160
- medium: string | null;
161
- }
162
- /** Compact voice actor data returned inside character edges. */
163
- interface VoiceActor {
164
- id: number;
165
- name: {
166
- first: string | null;
167
- middle: string | null;
168
- last: string | null;
169
- full: string | null;
170
- native: string | null;
171
- userPreferred: string | null;
172
- };
173
- languageV2: string | null;
174
- image: StaffImage;
175
- gender: string | null;
176
- primaryOccupations: string[];
177
- siteUrl: string | null;
178
- }
179
439
  interface MediaCharacterEdge {
180
440
  role: CharacterRole;
181
441
  node: Omit<Character, "media">;
@@ -197,20 +457,12 @@ interface StreamingEpisode {
197
457
  url: string | null;
198
458
  site: string | null;
199
459
  }
200
- interface ExternalLink {
201
- id: number;
202
- url: string | null;
203
- site: string;
204
- type: string | null;
205
- icon: string | null;
206
- color: string | null;
207
- }
208
460
  interface ScoreDistribution {
209
461
  score: number;
210
462
  amount: number;
211
463
  }
212
464
  interface StatusDistribution {
213
- status: MediaListStatus | string;
465
+ status: string;
214
466
  amount: number;
215
467
  }
216
468
  interface MediaStats {
@@ -236,183 +488,35 @@ interface Media {
236
488
  seasonYear: number | null;
237
489
  episodes: number | null;
238
490
  duration: number | null;
239
- chapters: number | null;
240
- volumes: number | null;
241
- countryOfOrigin: string | null;
242
- isLicensed: boolean | null;
243
- source: string | null;
244
- hashtag: string | null;
245
- trailer: MediaTrailer | null;
246
- coverImage: MediaCoverImage;
247
- bannerImage: string | null;
248
- genres: string[];
249
- synonyms: string[];
250
- averageScore: number | null;
251
- meanScore: number | null;
252
- popularity: number | null;
253
- favourites: number | null;
254
- trending: number | null;
255
- tags: MediaTag[];
256
- studios: StudioConnection;
257
- relations: MediaConnection | null;
258
- characters?: MediaCharacterConnection;
259
- staff?: MediaStaffConnection;
260
- streamingEpisodes?: StreamingEpisode[];
261
- externalLinks?: ExternalLink[];
262
- stats?: MediaStats;
263
- recommendations?: {
264
- nodes: MediaRecommendationNode[];
265
- };
266
- isAdult: boolean | null;
267
- siteUrl: string | null;
268
- }
269
- type CharacterMediaNode = Pick<Media, "id" | "title" | "type" | "coverImage" | "siteUrl">;
270
- interface CharacterMediaEdge {
271
- node: CharacterMediaNode;
272
- voiceActors?: VoiceActor[];
273
- }
274
- interface Character {
275
- id: number;
276
- name: CharacterName;
277
- image: CharacterImage;
278
- description: string | null;
279
- gender: string | null;
280
- dateOfBirth: FuzzyDate | null;
281
- age: string | null;
282
- bloodType: string | null;
283
- favourites: number | null;
284
- siteUrl: string | null;
285
- media: {
286
- nodes?: CharacterMediaNode[];
287
- edges?: CharacterMediaEdge[];
288
- } | null;
289
- }
290
- /** Options for including extra data when fetching a character. */
291
- interface CharacterIncludeOptions {
292
- /** Include voice actors for each media the character appears in. */
293
- voiceActors?: boolean;
294
- }
295
- interface StaffName {
296
- first: string | null;
297
- middle: string | null;
298
- last: string | null;
299
- full: string | null;
300
- native: string | null;
301
- }
302
- interface StaffImage {
303
- large: string | null;
304
- medium: string | null;
305
- }
306
- /** A media node returned inside `Staff.staffMedia`. */
307
- interface StaffMediaNode {
308
- id: number;
309
- title: MediaTitle;
310
- type: MediaType;
311
- format: MediaFormat | null;
312
- status: MediaStatus | null;
313
- coverImage: MediaCoverImage;
314
- bannerImage: string | null;
315
- genres: string[];
316
- averageScore: number | null;
317
- meanScore: number | null;
318
- popularity: number | null;
319
- favourites: number | null;
320
- episodes: number | null;
321
- trending: number | null;
322
- hashtag: string | null;
323
- season: MediaSeason | null;
324
- seasonYear: number | null;
325
- startDate: FuzzyDate | null;
326
- endDate: FuzzyDate | null;
327
- nextAiringEpisode: {
328
- id: number;
329
- airingAt: number;
330
- episode: number;
331
- mediaId: number;
332
- timeUntilAiring: number;
333
- } | null;
334
- studios: {
335
- edges: {
336
- node: {
337
- name: string;
338
- };
339
- }[];
340
- } | null;
341
- siteUrl: string | null;
342
- }
343
- interface Staff {
344
- id: number;
345
- name: StaffName;
346
- language: string | null;
347
- image: StaffImage;
348
- description: string | null;
349
- primaryOccupations: string[];
350
- gender: string | null;
351
- dateOfBirth: FuzzyDate | null;
352
- dateOfDeath: FuzzyDate | null;
353
- age: string | null;
354
- yearsActive: number[];
355
- homeTown: string | null;
356
- bloodType: string | null;
357
- favourites: number | null;
358
- siteUrl: string | null;
359
- /** Media the staff member has worked on — only present when requested via include options. */
360
- staffMedia?: {
361
- nodes: StaffMediaNode[];
362
- } | null;
363
- }
364
- /** Options to include additional related data when fetching a staff member by ID. */
365
- interface StaffIncludeOptions {
366
- /** Include media the staff member has worked on.
367
- * `true` = 25 results sorted by popularity. Object form to customize. */
368
- media?: boolean | {
369
- perPage?: number;
370
- sort?: boolean;
371
- };
372
- }
373
- interface UserAvatar {
374
- large: string | null;
375
- medium: string | null;
376
- }
377
- interface UserStatistics {
378
- count: number;
379
- meanScore: number;
380
- minutesWatched: number;
381
- episodesWatched: number;
382
- chaptersRead: number;
383
- volumesRead: number;
384
- }
385
- interface User {
386
- id: number;
387
- name: string;
388
- about: string | null;
389
- avatar: UserAvatar;
491
+ chapters: number | null;
492
+ volumes: number | null;
493
+ countryOfOrigin: string | null;
494
+ isLicensed: boolean | null;
495
+ source: string | null;
496
+ hashtag: string | null;
497
+ trailer: MediaTrailer | null;
498
+ coverImage: MediaCoverImage;
390
499
  bannerImage: string | null;
391
- isFollowing: boolean | null;
392
- isFollower: boolean | null;
393
- donatorTier: number | null;
394
- donatorBadge: string | null;
395
- createdAt: number | null;
500
+ genres: string[];
501
+ synonyms: string[];
502
+ averageScore: number | null;
503
+ meanScore: number | null;
504
+ popularity: number | null;
505
+ favourites: number | null;
506
+ trending: number | null;
507
+ tags: MediaTag[];
508
+ studios: StudioConnection;
509
+ relations: MediaConnection | null;
510
+ characters?: MediaCharacterConnection;
511
+ staff?: MediaStaffConnection;
512
+ streamingEpisodes?: StreamingEpisode[];
513
+ externalLinks?: ExternalLink[];
514
+ stats?: MediaStats;
515
+ recommendations?: {
516
+ nodes: MediaRecommendationNode[];
517
+ };
518
+ isAdult: boolean | null;
396
519
  siteUrl: string | null;
397
- statistics: {
398
- anime: UserStatistics;
399
- manga: UserStatistics;
400
- } | null;
401
- }
402
- interface AiringSchedule {
403
- id: number;
404
- airingAt: number;
405
- timeUntilAiring: number;
406
- episode: number;
407
- mediaId: number;
408
- media: Media;
409
- }
410
- interface PageInfo {
411
- total: number | null;
412
- perPage: number | null;
413
- currentPage: number | null;
414
- lastPage: number | null;
415
- hasNextPage: boolean | null;
416
520
  }
417
521
  interface SearchMediaOptions {
418
522
  query?: string;
@@ -428,24 +532,6 @@ interface SearchMediaOptions {
428
532
  page?: number;
429
533
  perPage?: number;
430
534
  }
431
- interface SearchCharacterOptions {
432
- query?: string;
433
- sort?: CharacterSort[];
434
- page?: number;
435
- perPage?: number;
436
- /** Include voice actors for each media the character appears in. */
437
- voiceActors?: boolean;
438
- }
439
- interface SearchStaffOptions {
440
- query?: string;
441
- sort?: CharacterSort[];
442
- page?: number;
443
- perPage?: number;
444
- }
445
- interface PagedResult<T> {
446
- pageInfo: PageInfo;
447
- results: T[];
448
- }
449
535
  interface GetAiringOptions {
450
536
  /** Only show episodes that aired after this UNIX timestamp */
451
537
  airingAtGreater?: number;
@@ -495,6 +581,58 @@ interface GetRecommendationsOptions {
495
581
  page?: number;
496
582
  perPage?: number;
497
583
  }
584
+ interface GetSeasonOptions {
585
+ /** The season (WINTER, SPRING, SUMMER, FALL) */
586
+ season: MediaSeason;
587
+ /** The year */
588
+ seasonYear: number;
589
+ /** Filter by ANIME or MANGA (defaults to ANIME) */
590
+ type?: MediaType;
591
+ /** Sort order (default: POPULARITY_DESC) */
592
+ sort?: MediaSort[];
593
+ page?: number;
594
+ perPage?: number;
595
+ }
596
+ /**
597
+ * Options to include additional related data when fetching a media entry.
598
+ * Pass `true` to include with defaults, or an object to customize.
599
+ */
600
+ interface MediaIncludeOptions {
601
+ /** Include characters with their roles (MAIN, SUPPORTING, BACKGROUND).
602
+ * `true` = 25 results sorted by role. Object form to customize. */
603
+ characters?: boolean | {
604
+ perPage?: number;
605
+ sort?: boolean;
606
+ voiceActors?: boolean;
607
+ };
608
+ /** Include staff members with their roles.
609
+ * `true` = 25 results sorted by relevance. Object form to customize. */
610
+ staff?: boolean | {
611
+ perPage?: number;
612
+ sort?: boolean;
613
+ };
614
+ /** Include relations (default: `true` for backward compat). Set to `false` to exclude. */
615
+ relations?: boolean;
616
+ /** Include streaming episode links (Crunchyroll, Funimation, etc.) */
617
+ streamingEpisodes?: boolean;
618
+ /** Include external links (MAL, official site, etc.) */
619
+ externalLinks?: boolean;
620
+ /** Include score & status distribution stats */
621
+ stats?: boolean;
622
+ /** Include user recommendations. `true` = 10 results, or customize with `{ perPage }`. */
623
+ recommendations?: boolean | {
624
+ perPage?: number;
625
+ };
626
+ }
627
+ interface AiringSchedule {
628
+ id: number;
629
+ airingAt: number;
630
+ timeUntilAiring: number;
631
+ episode: number;
632
+ mediaId: number;
633
+ media: Media;
634
+ }
635
+
498
636
  declare enum MediaListStatus {
499
637
  CURRENT = "CURRENT",
500
638
  PLANNING = "PLANNING",
@@ -552,18 +690,6 @@ interface MediaListEntry {
552
690
  createdAt: number | null;
553
691
  media: Media;
554
692
  }
555
- interface GetSeasonOptions {
556
- /** The season (WINTER, SPRING, SUMMER, FALL) */
557
- season: MediaSeason;
558
- /** The year */
559
- seasonYear: number;
560
- /** Filter by ANIME or MANGA (defaults to ANIME) */
561
- type?: MediaType;
562
- /** Sort order (default: POPULARITY_DESC) */
563
- sort?: MediaSort[];
564
- page?: number;
565
- perPage?: number;
566
- }
567
693
  interface GetUserMediaListOptions {
568
694
  /** User ID (provide either userId or userName) */
569
695
  userId?: number;
@@ -578,126 +704,6 @@ interface GetUserMediaListOptions {
578
704
  page?: number;
579
705
  perPage?: number;
580
706
  }
581
- interface StudioDetail {
582
- id: number;
583
- name: string;
584
- isAnimationStudio: boolean;
585
- siteUrl: string | null;
586
- favourites: number | null;
587
- media: {
588
- pageInfo: PageInfo;
589
- nodes: Pick<Media, "id" | "title" | "type" | "format" | "coverImage" | "siteUrl">[];
590
- } | null;
591
- }
592
- interface SearchStudioOptions {
593
- query?: string;
594
- page?: number;
595
- perPage?: number;
596
- }
597
- /**
598
- * Options to include additional related data when fetching a media entry.
599
- * Pass `true` to include with defaults, or an object to customize.
600
- */
601
- interface MediaIncludeOptions {
602
- /** Include characters with their roles (MAIN, SUPPORTING, BACKGROUND).
603
- * `true` = 25 results sorted by role. Object form to customize. */
604
- characters?: boolean | {
605
- perPage?: number;
606
- sort?: boolean;
607
- voiceActors?: boolean;
608
- };
609
- /** Include staff members with their roles.
610
- * `true` = 25 results sorted by relevance. Object form to customize. */
611
- staff?: boolean | {
612
- perPage?: number;
613
- sort?: boolean;
614
- };
615
- /** Include relations (default: `true` for backward compat). Set to `false` to exclude. */
616
- relations?: boolean;
617
- /** Include streaming episode links (Crunchyroll, Funimation, etc.) */
618
- streamingEpisodes?: boolean;
619
- /** Include external links (MAL, official site, etc.) */
620
- externalLinks?: boolean;
621
- /** Include score & status distribution stats */
622
- stats?: boolean;
623
- /** Include user recommendations. `true` = 10 results, or customize with `{ perPage }`. */
624
- recommendations?: boolean | {
625
- perPage?: number;
626
- };
627
- }
628
- /**
629
- * Interface that all cache adapters must implement.
630
- * Methods may return sync values or Promises — the client awaits all calls.
631
- */
632
- interface CacheAdapter {
633
- /** Retrieve a cached value, or `undefined` if missing / expired. */
634
- get<T>(key: string): T | undefined | Promise<T | undefined>;
635
- /** Store a value in the cache. */
636
- set<T>(key: string, data: T): void | Promise<void>;
637
- /** Remove a specific entry. Returns `true` if the key existed. */
638
- delete(key: string): boolean | Promise<boolean>;
639
- /** Clear the entire cache. */
640
- clear(): void | Promise<void>;
641
- /** Number of entries currently stored (sync). Returns -1 if unknown. */
642
- readonly size: number;
643
- /** Return all cache keys. */
644
- keys(): string[] | Promise<string[]>;
645
- /** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */
646
- invalidate?(pattern: string | RegExp): number | Promise<number>;
647
- }
648
- /** Cache configuration options. */
649
- interface CacheOptions {
650
- /** Time-to-live in milliseconds (default: 86 400 000 = 24h) */
651
- ttl?: number;
652
- /** Maximum number of cached entries (default: 500, 0 = unlimited) */
653
- maxSize?: number;
654
- /** Set to false to disable caching entirely */
655
- enabled?: boolean;
656
- }
657
- /** Rate limiter configuration options. */
658
- interface RateLimitOptions {
659
- /** Max requests per window (default: 85) */
660
- maxRequests?: number;
661
- /** Window size in ms (default: 60 000) */
662
- windowMs?: number;
663
- /** Max retries on 429 (default: 3) */
664
- maxRetries?: number;
665
- /** Retry delay in ms when Retry-After header is absent (default: 2000) */
666
- retryDelayMs?: number;
667
- /** Set to false to disable rate limiting entirely */
668
- enabled?: boolean;
669
- /** Timeout per request in ms (default: 30 000). 0 = no timeout. */
670
- timeoutMs?: number;
671
- /** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */
672
- retryOnNetworkError?: boolean;
673
- }
674
- /** Event hooks for logging, debugging, and monitoring. */
675
- interface AniListHooks {
676
- /** Called before every API request. */
677
- onRequest?: (query: string, variables: Record<string, unknown>) => void;
678
- /** Called when a response is served from cache. */
679
- onCacheHit?: (key: string) => void;
680
- /** Called when the rate limiter enforces a wait (429 received). */
681
- onRateLimit?: (retryAfterMs: number) => void;
682
- /** Called when a request is retried (429 or network error). */
683
- onRetry?: (attempt: number, reason: string, delayMs: number) => void;
684
- /** Called when a request completes. */
685
- onResponse?: (query: string, durationMs: number, fromCache: boolean) => void;
686
- }
687
- interface AniListClientOptions {
688
- /** Optional AniList OAuth token for authenticated requests */
689
- token?: string;
690
- /** Custom API endpoint (defaults to https://graphql.anilist.co) */
691
- apiUrl?: string;
692
- /** Cache configuration (enabled by default, 24h TTL) */
693
- cache?: CacheOptions;
694
- /** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */
695
- cacheAdapter?: CacheAdapter;
696
- /** Rate limiter configuration (enabled by default, 85 req/min) */
697
- rateLimit?: RateLimitOptions;
698
- /** Event hooks for logging, debugging, and monitoring */
699
- hooks?: AniListHooks;
700
- }
701
707
 
702
708
  /**
703
709
  * Lightweight AniList GraphQL client with built-in caching and rate limiting.
@@ -736,11 +742,6 @@ declare class AniListClient {
736
742
  * Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.
737
743
  */
738
744
  private pagedRequest;
739
- /**
740
- * @internal
741
- * Clamp perPage to AniList's maximum of 50.
742
- */
743
- private clampPerPage;
744
745
  /**
745
746
  * Fetch a single media entry by its AniList ID.
746
747
  *
@@ -1095,17 +1096,15 @@ declare class AniListClient {
1095
1096
  getStaffBatch(ids: number[]): Promise<Staff[]>;
1096
1097
  /** @internal */
1097
1098
  private executeBatch;
1098
- /** @internal */
1099
- private chunk;
1100
1099
  /**
1101
1100
  * Clear the entire response cache.
1102
1101
  */
1103
1102
  clearCache(): Promise<void>;
1104
1103
  /**
1105
- * Number of entries currently in the cache (sync).
1106
- * For async adapters like Redis, this may be approximate.
1104
+ * Number of entries currently in the cache.
1105
+ * For async adapters like Redis, this may return a Promise.
1107
1106
  */
1108
- get cacheSize(): number;
1107
+ get cacheSize(): number | Promise<number>;
1109
1108
  /**
1110
1109
  * Remove cache entries whose key matches the given pattern.
1111
1110
  *
@@ -1143,7 +1142,7 @@ declare class MemoryCache implements CacheAdapter {
1143
1142
  /** Clear the entire cache. */
1144
1143
  clear(): void;
1145
1144
  /** Number of entries currently stored. */
1146
- get size(): number;
1145
+ get size(): number | Promise<number>;
1147
1146
  /** Return all cache keys. */
1148
1147
  keys(): string[];
1149
1148
  /**
@@ -1211,12 +1210,11 @@ declare class RedisCache implements CacheAdapter {
1211
1210
  private collectKeys;
1212
1211
  clear(): Promise<void>;
1213
1212
  /**
1214
- * Returns -1 because Redis keys can expire silently via TTL.
1215
- * Use `getSize()` for an accurate count.
1213
+ * Get the actual number of keys with this prefix in Redis.
1216
1214
  */
1217
- get size(): number;
1218
- /** Get the actual number of keys with this prefix in Redis. */
1219
- getSize(): Promise<number>;
1215
+ get size(): Promise<number>;
1216
+ /** @internal */
1217
+ private getSize;
1220
1218
  keys(): Promise<string[]>;
1221
1219
  /**
1222
1220
  * Remove all entries whose key matches the given glob pattern.