ani-client 1.3.0 → 1.4.0

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
@@ -29,21 +29,37 @@ declare enum MediaSeason {
29
29
  }
30
30
  declare enum MediaSort {
31
31
  ID = "ID",
32
+ ID_DESC = "ID_DESC",
32
33
  TITLE_ROMAJI = "TITLE_ROMAJI",
34
+ TITLE_ROMAJI_DESC = "TITLE_ROMAJI_DESC",
33
35
  TITLE_ENGLISH = "TITLE_ENGLISH",
36
+ TITLE_ENGLISH_DESC = "TITLE_ENGLISH_DESC",
34
37
  TITLE_NATIVE = "TITLE_NATIVE",
38
+ TITLE_NATIVE_DESC = "TITLE_NATIVE_DESC",
35
39
  TYPE = "TYPE",
40
+ TYPE_DESC = "TYPE_DESC",
36
41
  FORMAT = "FORMAT",
42
+ FORMAT_DESC = "FORMAT_DESC",
37
43
  START_DATE = "START_DATE",
44
+ START_DATE_DESC = "START_DATE_DESC",
38
45
  END_DATE = "END_DATE",
46
+ END_DATE_DESC = "END_DATE_DESC",
39
47
  SCORE = "SCORE",
48
+ SCORE_DESC = "SCORE_DESC",
40
49
  POPULARITY = "POPULARITY",
50
+ POPULARITY_DESC = "POPULARITY_DESC",
41
51
  TRENDING = "TRENDING",
52
+ TRENDING_DESC = "TRENDING_DESC",
42
53
  EPISODES = "EPISODES",
54
+ EPISODES_DESC = "EPISODES_DESC",
43
55
  DURATION = "DURATION",
56
+ DURATION_DESC = "DURATION_DESC",
44
57
  STATUS = "STATUS",
58
+ STATUS_DESC = "STATUS_DESC",
45
59
  FAVOURITES = "FAVOURITES",
60
+ FAVOURITES_DESC = "FAVOURITES_DESC",
46
61
  UPDATED_AT = "UPDATED_AT",
62
+ UPDATED_AT_DESC = "UPDATED_AT_DESC",
47
63
  SEARCH_MATCH = "SEARCH_MATCH"
48
64
  }
49
65
  declare enum AiringSort {
@@ -58,9 +74,17 @@ declare enum AiringSort {
58
74
  }
59
75
  declare enum CharacterSort {
60
76
  ID = "ID",
77
+ ID_DESC = "ID_DESC",
61
78
  ROLE = "ROLE",
79
+ ROLE_DESC = "ROLE_DESC",
62
80
  SEARCH_MATCH = "SEARCH_MATCH",
63
- FAVOURITES = "FAVOURITES"
81
+ FAVOURITES = "FAVOURITES",
82
+ FAVOURITES_DESC = "FAVOURITES_DESC"
83
+ }
84
+ declare enum CharacterRole {
85
+ MAIN = "MAIN",
86
+ SUPPORTING = "SUPPORTING",
87
+ BACKGROUND = "BACKGROUND"
64
88
  }
65
89
  interface MediaTitle {
66
90
  romaji: string | null;
@@ -135,6 +159,51 @@ interface CharacterImage {
135
159
  large: string | null;
136
160
  medium: string | null;
137
161
  }
162
+ interface MediaCharacterEdge {
163
+ role: CharacterRole;
164
+ node: Omit<Character, "media">;
165
+ }
166
+ interface MediaCharacterConnection {
167
+ edges: MediaCharacterEdge[];
168
+ }
169
+ interface MediaStaffEdge {
170
+ role: string;
171
+ node: Staff;
172
+ }
173
+ interface MediaStaffConnection {
174
+ edges: MediaStaffEdge[];
175
+ }
176
+ interface StreamingEpisode {
177
+ title: string | null;
178
+ thumbnail: string | null;
179
+ url: string | null;
180
+ site: string | null;
181
+ }
182
+ interface ExternalLink {
183
+ id: number;
184
+ url: string | null;
185
+ site: string;
186
+ type: string | null;
187
+ icon: string | null;
188
+ color: string | null;
189
+ }
190
+ interface ScoreDistribution {
191
+ score: number;
192
+ amount: number;
193
+ }
194
+ interface StatusDistribution {
195
+ status: MediaListStatus | string;
196
+ amount: number;
197
+ }
198
+ interface MediaStats {
199
+ scoreDistribution: ScoreDistribution[];
200
+ statusDistribution: StatusDistribution[];
201
+ }
202
+ interface MediaRecommendationNode {
203
+ id: number;
204
+ rating: number | null;
205
+ mediaRecommendation: Pick<Media, "id" | "title" | "type" | "format" | "coverImage" | "averageScore" | "siteUrl">;
206
+ }
138
207
  interface Media {
139
208
  id: number;
140
209
  idMal: number | null;
@@ -168,6 +237,14 @@ interface Media {
168
237
  tags: MediaTag[];
169
238
  studios: StudioConnection;
170
239
  relations: MediaConnection | null;
240
+ characters?: MediaCharacterConnection;
241
+ staff?: MediaStaffConnection;
242
+ streamingEpisodes?: StreamingEpisode[];
243
+ externalLinks?: ExternalLink[];
244
+ stats?: MediaStats;
245
+ recommendations?: {
246
+ nodes: MediaRecommendationNode[];
247
+ };
171
248
  isAdult: boolean | null;
172
249
  siteUrl: string | null;
173
250
  }
@@ -207,7 +284,7 @@ interface Staff {
207
284
  gender: string | null;
208
285
  dateOfBirth: FuzzyDate | null;
209
286
  dateOfDeath: FuzzyDate | null;
210
- age: number | null;
287
+ age: string | null;
211
288
  yearsActive: number[];
212
289
  homeTown: string | null;
213
290
  bloodType: string | null;
@@ -280,6 +357,7 @@ interface SearchCharacterOptions {
280
357
  }
281
358
  interface SearchStaffOptions {
282
359
  query?: string;
360
+ sort?: CharacterSort[];
283
361
  page?: number;
284
362
  perPage?: number;
285
363
  }
@@ -435,6 +513,36 @@ interface SearchStudioOptions {
435
513
  page?: number;
436
514
  perPage?: number;
437
515
  }
516
+ /**
517
+ * Options to include additional related data when fetching a media entry.
518
+ * Pass `true` to include with defaults, or an object to customize.
519
+ */
520
+ interface MediaIncludeOptions {
521
+ /** Include characters with their roles (MAIN, SUPPORTING, BACKGROUND).
522
+ * `true` = 25 results sorted by role. Object form to customize. */
523
+ characters?: boolean | {
524
+ perPage?: number;
525
+ sort?: boolean;
526
+ };
527
+ /** Include staff members with their roles.
528
+ * `true` = 25 results sorted by relevance. Object form to customize. */
529
+ staff?: boolean | {
530
+ perPage?: number;
531
+ sort?: boolean;
532
+ };
533
+ /** Include relations (default: `true` for backward compat). Set to `false` to exclude. */
534
+ relations?: boolean;
535
+ /** Include streaming episode links (Crunchyroll, Funimation, etc.) */
536
+ streamingEpisodes?: boolean;
537
+ /** Include external links (MAL, official site, etc.) */
538
+ externalLinks?: boolean;
539
+ /** Include score & status distribution stats */
540
+ stats?: boolean;
541
+ /** Include user recommendations. `true` = 10 results, or customize with `{ perPage }`. */
542
+ recommendations?: boolean | {
543
+ perPage?: number;
544
+ };
545
+ }
438
546
  /**
439
547
  * Interface that all cache adapters must implement.
440
548
  * Methods may return sync values or Promises — the client awaits all calls.
@@ -451,10 +559,36 @@ interface CacheAdapter {
451
559
  /** Number of entries currently stored (sync). Returns -1 if unknown. */
452
560
  readonly size: number;
453
561
  /** Return all cache keys. */
454
- keys(): IterableIterator<string> | string[] | Promise<string[]>;
562
+ keys(): string[] | Promise<string[]>;
455
563
  /** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */
456
564
  invalidate?(pattern: string | RegExp): number | Promise<number>;
457
565
  }
566
+ /** Cache configuration options. */
567
+ interface CacheOptions {
568
+ /** Time-to-live in milliseconds (default: 86 400 000 = 24h) */
569
+ ttl?: number;
570
+ /** Maximum number of cached entries (default: 500, 0 = unlimited) */
571
+ maxSize?: number;
572
+ /** Set to false to disable caching entirely */
573
+ enabled?: boolean;
574
+ }
575
+ /** Rate limiter configuration options. */
576
+ interface RateLimitOptions {
577
+ /** Max requests per window (default: 85) */
578
+ maxRequests?: number;
579
+ /** Window size in ms (default: 60 000) */
580
+ windowMs?: number;
581
+ /** Max retries on 429 (default: 3) */
582
+ maxRetries?: number;
583
+ /** Retry delay in ms when Retry-After header is absent (default: 2000) */
584
+ retryDelayMs?: number;
585
+ /** Set to false to disable rate limiting entirely */
586
+ enabled?: boolean;
587
+ /** Timeout per request in ms (default: 30 000). 0 = no timeout. */
588
+ timeoutMs?: number;
589
+ /** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */
590
+ retryOnNetworkError?: boolean;
591
+ }
458
592
  /** Event hooks for logging, debugging, and monitoring. */
459
593
  interface AniListHooks {
460
594
  /** Called before every API request. */
@@ -474,33 +608,11 @@ interface AniListClientOptions {
474
608
  /** Custom API endpoint (defaults to https://graphql.anilist.co) */
475
609
  apiUrl?: string;
476
610
  /** Cache configuration (enabled by default, 24h TTL) */
477
- cache?: {
478
- /** Time-to-live in milliseconds (default: 86 400 000 = 24h) */
479
- ttl?: number;
480
- /** Maximum number of cached entries (default: 500, 0 = unlimited) */
481
- maxSize?: number;
482
- /** Set to false to disable caching entirely */
483
- enabled?: boolean;
484
- };
611
+ cache?: CacheOptions;
485
612
  /** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */
486
613
  cacheAdapter?: CacheAdapter;
487
614
  /** Rate limiter configuration (enabled by default, 85 req/min) */
488
- rateLimit?: {
489
- /** Max requests per window (default: 85) */
490
- maxRequests?: number;
491
- /** Window size in ms (default: 60 000) */
492
- windowMs?: number;
493
- /** Max retries on 429 (default: 3) */
494
- maxRetries?: number;
495
- /** Retry delay in ms when Retry-After header is absent (default: 2000) */
496
- retryDelayMs?: number;
497
- /** Set to false to disable rate limiting entirely */
498
- enabled?: boolean;
499
- /** Timeout per request in ms (default: 30 000). 0 = no timeout. */
500
- timeoutMs?: number;
501
- /** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */
502
- retryOnNetworkError?: boolean;
503
- };
615
+ rateLimit?: RateLimitOptions;
504
616
  /** Event hooks for logging, debugging, and monitoring */
505
617
  hooks?: AniListHooks;
506
618
  }
@@ -542,13 +654,44 @@ declare class AniListClient {
542
654
  * Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.
543
655
  */
544
656
  private pagedRequest;
657
+ /**
658
+ * @internal
659
+ * Clamp perPage to AniList's maximum of 50.
660
+ */
661
+ private clampPerPage;
545
662
  /**
546
663
  * Fetch a single media entry by its AniList ID.
547
664
  *
665
+ * Optionally include related data (characters, staff, relations, etc.) via the `include` parameter.
666
+ *
548
667
  * @param id - The AniList media ID
668
+ * @param include - Optional related data to include
549
669
  * @returns The media object
670
+ *
671
+ * @example
672
+ * ```ts
673
+ * // Basic usage — same as before (includes relations by default)
674
+ * const anime = await client.getMedia(1);
675
+ *
676
+ * // Include characters sorted by role, 25 results
677
+ * const anime = await client.getMedia(1, { characters: true });
678
+ *
679
+ * // Full control
680
+ * const anime = await client.getMedia(1, {
681
+ * characters: { perPage: 50, sort: true },
682
+ * staff: true,
683
+ * relations: true,
684
+ * streamingEpisodes: true,
685
+ * externalLinks: true,
686
+ * stats: true,
687
+ * recommendations: { perPage: 5 },
688
+ * });
689
+ *
690
+ * // Exclude relations for a lighter response
691
+ * const anime = await client.getMedia(1, { characters: true, relations: false });
692
+ * ```
550
693
  */
551
- getMedia(id: number): Promise<Media>;
694
+ getMedia(id: number, include?: MediaIncludeOptions): Promise<Media>;
552
695
  /**
553
696
  * Search for anime or manga.
554
697
  *
@@ -575,26 +718,78 @@ declare class AniListClient {
575
718
  getTrending(type?: MediaType, page?: number, perPage?: number): Promise<PagedResult<Media>>;
576
719
  /**
577
720
  * Fetch a character by AniList ID.
721
+ *
722
+ * @param id - The AniList character ID
723
+ * @returns The character object
724
+ *
725
+ * @example
726
+ * ```ts
727
+ * const spike = await client.getCharacter(1);
728
+ * console.log(spike.name.full); // "Spike Spiegel"
729
+ * ```
578
730
  */
579
731
  getCharacter(id: number): Promise<Character>;
580
732
  /**
581
733
  * Search for characters by name.
734
+ *
735
+ * @param options - Search / pagination parameters
736
+ * @returns Paginated results with matching characters
737
+ *
738
+ * @example
739
+ * ```ts
740
+ * const result = await client.searchCharacters({ query: "Luffy", perPage: 5 });
741
+ * ```
582
742
  */
583
743
  searchCharacters(options?: SearchCharacterOptions): Promise<PagedResult<Character>>;
584
744
  /**
585
745
  * Fetch a staff member by AniList ID.
746
+ *
747
+ * @param id - The AniList staff ID
748
+ * @returns The staff object
749
+ *
750
+ * @example
751
+ * ```ts
752
+ * const staff = await client.getStaff(95001);
753
+ * console.log(staff.name.full);
754
+ * ```
586
755
  */
587
756
  getStaff(id: number): Promise<Staff>;
588
757
  /**
589
758
  * Search for staff (voice actors, directors, etc.).
759
+ *
760
+ * @param options - Search / pagination parameters
761
+ * @returns Paginated results with matching staff
762
+ *
763
+ * @example
764
+ * ```ts
765
+ * const result = await client.searchStaff({ query: "Miyazaki", perPage: 5 });
766
+ * ```
590
767
  */
591
768
  searchStaff(options?: SearchStaffOptions): Promise<PagedResult<Staff>>;
592
769
  /**
593
770
  * Fetch a user by AniList ID.
771
+ *
772
+ * @param id - The AniList user ID
773
+ * @returns The user object
774
+ *
775
+ * @example
776
+ * ```ts
777
+ * const user = await client.getUser(1);
778
+ * console.log(user.name);
779
+ * ```
594
780
  */
595
781
  getUser(id: number): Promise<User>;
596
782
  /**
597
783
  * Fetch a user by username.
784
+ *
785
+ * @param name - The AniList username
786
+ * @returns The user object
787
+ *
788
+ * @example
789
+ * ```ts
790
+ * const user = await client.getUserByName("AniList");
791
+ * console.log(user.statistics);
792
+ * ```
598
793
  */
599
794
  getUserByName(name: string): Promise<User>;
600
795
  /**
@@ -830,18 +1025,6 @@ declare class AniListError extends Error {
830
1025
  constructor(message: string, status: number, errors?: unknown[]);
831
1026
  }
832
1027
 
833
- /**
834
- * Simple in-memory cache with configurable TTL.
835
- * Used internally by AniListClient to avoid redundant API calls.
836
- */
837
- interface CacheOptions {
838
- /** Time-to-live in milliseconds (default: 24 hours) */
839
- ttl?: number;
840
- /** Maximum number of entries to keep (default: 500, 0 = unlimited) */
841
- maxSize?: number;
842
- /** Disable caching entirely (default: false) */
843
- enabled?: boolean;
844
- }
845
1028
  declare class MemoryCache implements CacheAdapter {
846
1029
  private readonly ttl;
847
1030
  private readonly maxSize;
@@ -860,8 +1043,8 @@ declare class MemoryCache implements CacheAdapter {
860
1043
  clear(): void;
861
1044
  /** Number of entries currently stored. */
862
1045
  get size(): number;
863
- /** Return an iterator over all cache keys. */
864
- keys(): IterableIterator<string>;
1046
+ /** Return all cache keys. */
1047
+ keys(): string[];
865
1048
  /**
866
1049
  * Remove all entries whose key matches the given pattern.
867
1050
  *
@@ -880,6 +1063,11 @@ interface RedisLikeClient {
880
1063
  set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
881
1064
  del(...keys: (string | string[])[]): Promise<number>;
882
1065
  keys(pattern: string): Promise<string[]>;
1066
+ /** Optional SCAN-based iteration — used when available to avoid blocking the server. */
1067
+ scanIterator?(options: {
1068
+ MATCH: string;
1069
+ COUNT?: number;
1070
+ }): AsyncIterable<string>;
883
1071
  }
884
1072
  interface RedisCacheOptions {
885
1073
  /** A Redis client instance (ioredis or node-redis). */
@@ -912,6 +1100,14 @@ declare class RedisCache implements CacheAdapter {
912
1100
  get<T>(key: string): Promise<T | undefined>;
913
1101
  set<T>(key: string, data: T): Promise<void>;
914
1102
  delete(key: string): Promise<boolean>;
1103
+ /**
1104
+ * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.
1105
+ *
1106
+ * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.
1107
+ * Provide a client with `scanIterator` support for production use.
1108
+ * @internal
1109
+ */
1110
+ private collectKeys;
915
1111
  clear(): Promise<void>;
916
1112
  /**
917
1113
  * Returns -1 because Redis keys can expire silently via TTL.
@@ -927,7 +1123,7 @@ declare class RedisCache implements CacheAdapter {
927
1123
  * @param pattern — A glob pattern (e.g. `"*Media*"`)
928
1124
  * @returns Number of entries removed.
929
1125
  */
930
- invalidate(pattern: string): Promise<number>;
1126
+ invalidate(pattern: string | RegExp): Promise<number>;
931
1127
  }
932
1128
 
933
1129
  /**
@@ -937,22 +1133,7 @@ declare class RedisCache implements CacheAdapter {
937
1133
  * When a 429 (Too Many Requests) is received, the client
938
1134
  * waits for the Retry-After header and retries automatically.
939
1135
  */
940
- interface RateLimitOptions {
941
- /** Max requests per window (default: 85, conservative under AniList's 90/min) */
942
- maxRequests?: number;
943
- /** Window size in milliseconds (default: 60 000 = 1 minute) */
944
- windowMs?: number;
945
- /** Max number of retries on 429 responses (default: 3) */
946
- maxRetries?: number;
947
- /** Default retry delay in ms when Retry-After header is missing (default: 2000) */
948
- retryDelayMs?: number;
949
- /** Disable rate limiting entirely (default: false) */
950
- enabled?: boolean;
951
- /** Timeout per request in milliseconds (default: 30 000). 0 = no timeout. */
952
- timeoutMs?: number;
953
- /** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */
954
- retryOnNetworkError?: boolean;
955
- }
1136
+
956
1137
  declare class RateLimiter {
957
1138
  private readonly maxRequests;
958
1139
  private readonly windowMs;
@@ -980,4 +1161,4 @@ declare class RateLimiter {
980
1161
  private sleep;
981
1162
  }
982
1163
 
983
- export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, type AniListHooks, type CacheAdapter, type CacheOptions, type Character, type CharacterImage, type CharacterName, CharacterSort, type FuzzyDate, type GetAiringOptions, type GetPlanningOptions, type GetRecentChaptersOptions, type GetRecommendationsOptions, type GetSeasonOptions, type GetUserMediaListOptions, type Media, type MediaConnection, type MediaCoverImage, type MediaEdge, MediaFormat, type MediaListEntry, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type PageInfo, type PagedResult, type RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, RedisCache, type RedisCacheOptions, type RedisLikeClient, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type SearchStudioOptions, type Staff, type StaffImage, type StaffName, type Studio, type StudioConnection, type StudioDetail, type User, type UserAvatar, type UserStatistics };
1164
+ export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, type AniListHooks, type CacheAdapter, type CacheOptions, type Character, type CharacterImage, type CharacterName, CharacterRole, CharacterSort, type ExternalLink, type FuzzyDate, type GetAiringOptions, type GetPlanningOptions, type GetRecentChaptersOptions, type GetRecommendationsOptions, type GetSeasonOptions, type GetUserMediaListOptions, type Media, type MediaCharacterConnection, type MediaCharacterEdge, type MediaConnection, type MediaCoverImage, type MediaEdge, MediaFormat, type MediaIncludeOptions, type MediaListEntry, MediaListSort, MediaListStatus, type MediaRecommendationNode, MediaRelationType, MediaSeason, MediaSort, type MediaStaffConnection, type MediaStaffEdge, type MediaStats, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type PageInfo, type PagedResult, type RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, RedisCache, type RedisCacheOptions, type RedisLikeClient, type ScoreDistribution, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type SearchStudioOptions, type Staff, type StaffImage, type StaffName, type StatusDistribution, type StreamingEpisode, type Studio, type StudioConnection, type StudioDetail, type User, type UserAvatar, type UserStatistics };