ani-client 1.2.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/README.md +380 -290
- package/dist/index.d.mts +398 -58
- package/dist/index.d.ts +398 -58
- package/dist/index.js +739 -268
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +738 -269
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
package/dist/index.d.mts
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:
|
|
287
|
+
age: string | null;
|
|
211
288
|
yearsActive: number[];
|
|
212
289
|
homeTown: string | null;
|
|
213
290
|
bloodType: string | null;
|
|
@@ -280,10 +357,7 @@ interface SearchCharacterOptions {
|
|
|
280
357
|
}
|
|
281
358
|
interface SearchStaffOptions {
|
|
282
359
|
query?: string;
|
|
283
|
-
|
|
284
|
-
perPage?: number;
|
|
285
|
-
}
|
|
286
|
-
interface PaginatedOptions {
|
|
360
|
+
sort?: CharacterSort[];
|
|
287
361
|
page?: number;
|
|
288
362
|
perPage?: number;
|
|
289
363
|
}
|
|
@@ -439,33 +513,108 @@ interface SearchStudioOptions {
|
|
|
439
513
|
page?: number;
|
|
440
514
|
perPage?: number;
|
|
441
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
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Interface that all cache adapters must implement.
|
|
548
|
+
* Methods may return sync values or Promises — the client awaits all calls.
|
|
549
|
+
*/
|
|
550
|
+
interface CacheAdapter {
|
|
551
|
+
/** Retrieve a cached value, or `undefined` if missing / expired. */
|
|
552
|
+
get<T>(key: string): T | undefined | Promise<T | undefined>;
|
|
553
|
+
/** Store a value in the cache. */
|
|
554
|
+
set<T>(key: string, data: T): void | Promise<void>;
|
|
555
|
+
/** Remove a specific entry. Returns `true` if the key existed. */
|
|
556
|
+
delete(key: string): boolean | Promise<boolean>;
|
|
557
|
+
/** Clear the entire cache. */
|
|
558
|
+
clear(): void | Promise<void>;
|
|
559
|
+
/** Number of entries currently stored (sync). Returns -1 if unknown. */
|
|
560
|
+
readonly size: number;
|
|
561
|
+
/** Return all cache keys. */
|
|
562
|
+
keys(): string[] | Promise<string[]>;
|
|
563
|
+
/** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */
|
|
564
|
+
invalidate?(pattern: string | RegExp): number | Promise<number>;
|
|
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
|
+
}
|
|
592
|
+
/** Event hooks for logging, debugging, and monitoring. */
|
|
593
|
+
interface AniListHooks {
|
|
594
|
+
/** Called before every API request. */
|
|
595
|
+
onRequest?: (query: string, variables: Record<string, unknown>) => void;
|
|
596
|
+
/** Called when a response is served from cache. */
|
|
597
|
+
onCacheHit?: (key: string) => void;
|
|
598
|
+
/** Called when the rate limiter enforces a wait (429 received). */
|
|
599
|
+
onRateLimit?: (retryAfterMs: number) => void;
|
|
600
|
+
/** Called when a request is retried (429 or network error). */
|
|
601
|
+
onRetry?: (attempt: number, reason: string, delayMs: number) => void;
|
|
602
|
+
/** Called when a request completes. */
|
|
603
|
+
onResponse?: (query: string, durationMs: number, fromCache: boolean) => void;
|
|
604
|
+
}
|
|
442
605
|
interface AniListClientOptions {
|
|
443
606
|
/** Optional AniList OAuth token for authenticated requests */
|
|
444
607
|
token?: string;
|
|
445
608
|
/** Custom API endpoint (defaults to https://graphql.anilist.co) */
|
|
446
609
|
apiUrl?: string;
|
|
447
610
|
/** Cache configuration (enabled by default, 24h TTL) */
|
|
448
|
-
cache?:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
/** Maximum number of cached entries (default: 500, 0 = unlimited) */
|
|
452
|
-
maxSize?: number;
|
|
453
|
-
/** Set to false to disable caching entirely */
|
|
454
|
-
enabled?: boolean;
|
|
455
|
-
};
|
|
611
|
+
cache?: CacheOptions;
|
|
612
|
+
/** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */
|
|
613
|
+
cacheAdapter?: CacheAdapter;
|
|
456
614
|
/** Rate limiter configuration (enabled by default, 85 req/min) */
|
|
457
|
-
rateLimit?:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
/** Window size in ms (default: 60 000) */
|
|
461
|
-
windowMs?: number;
|
|
462
|
-
/** Max retries on 429 (default: 3) */
|
|
463
|
-
maxRetries?: number;
|
|
464
|
-
/** Retry delay in ms when Retry-After header is absent (default: 2000) */
|
|
465
|
-
retryDelayMs?: number;
|
|
466
|
-
/** Set to false to disable rate limiting entirely */
|
|
467
|
-
enabled?: boolean;
|
|
468
|
-
};
|
|
615
|
+
rateLimit?: RateLimitOptions;
|
|
616
|
+
/** Event hooks for logging, debugging, and monitoring */
|
|
617
|
+
hooks?: AniListHooks;
|
|
469
618
|
}
|
|
470
619
|
|
|
471
620
|
/**
|
|
@@ -489,20 +638,60 @@ interface AniListClientOptions {
|
|
|
489
638
|
declare class AniListClient {
|
|
490
639
|
private readonly apiUrl;
|
|
491
640
|
private readonly headers;
|
|
492
|
-
private readonly
|
|
641
|
+
private readonly cacheAdapter;
|
|
493
642
|
private readonly rateLimiter;
|
|
643
|
+
private readonly hooks;
|
|
644
|
+
private readonly inFlight;
|
|
494
645
|
constructor(options?: AniListClientOptions);
|
|
495
646
|
/**
|
|
496
647
|
* @internal
|
|
497
648
|
*/
|
|
498
649
|
private request;
|
|
650
|
+
/** @internal */
|
|
651
|
+
private executeRequest;
|
|
652
|
+
/**
|
|
653
|
+
* @internal
|
|
654
|
+
* Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.
|
|
655
|
+
*/
|
|
656
|
+
private pagedRequest;
|
|
657
|
+
/**
|
|
658
|
+
* @internal
|
|
659
|
+
* Clamp perPage to AniList's maximum of 50.
|
|
660
|
+
*/
|
|
661
|
+
private clampPerPage;
|
|
499
662
|
/**
|
|
500
663
|
* Fetch a single media entry by its AniList ID.
|
|
501
664
|
*
|
|
665
|
+
* Optionally include related data (characters, staff, relations, etc.) via the `include` parameter.
|
|
666
|
+
*
|
|
502
667
|
* @param id - The AniList media ID
|
|
668
|
+
* @param include - Optional related data to include
|
|
503
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
|
+
* ```
|
|
504
693
|
*/
|
|
505
|
-
getMedia(id: number): Promise<Media>;
|
|
694
|
+
getMedia(id: number, include?: MediaIncludeOptions): Promise<Media>;
|
|
506
695
|
/**
|
|
507
696
|
* Search for anime or manga.
|
|
508
697
|
*
|
|
@@ -529,26 +718,78 @@ declare class AniListClient {
|
|
|
529
718
|
getTrending(type?: MediaType, page?: number, perPage?: number): Promise<PagedResult<Media>>;
|
|
530
719
|
/**
|
|
531
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
|
+
* ```
|
|
532
730
|
*/
|
|
533
731
|
getCharacter(id: number): Promise<Character>;
|
|
534
732
|
/**
|
|
535
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
|
+
* ```
|
|
536
742
|
*/
|
|
537
743
|
searchCharacters(options?: SearchCharacterOptions): Promise<PagedResult<Character>>;
|
|
538
744
|
/**
|
|
539
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
|
+
* ```
|
|
540
755
|
*/
|
|
541
756
|
getStaff(id: number): Promise<Staff>;
|
|
542
757
|
/**
|
|
543
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
|
+
* ```
|
|
544
767
|
*/
|
|
545
768
|
searchStaff(options?: SearchStaffOptions): Promise<PagedResult<Staff>>;
|
|
546
769
|
/**
|
|
547
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
|
+
* ```
|
|
548
780
|
*/
|
|
549
781
|
getUser(id: number): Promise<User>;
|
|
550
782
|
/**
|
|
551
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
|
+
* ```
|
|
552
793
|
*/
|
|
553
794
|
getUserByName(name: string): Promise<User>;
|
|
554
795
|
/**
|
|
@@ -729,14 +970,48 @@ declare class AniListClient {
|
|
|
729
970
|
* ```
|
|
730
971
|
*/
|
|
731
972
|
paginate<T>(fetchPage: (page: number) => Promise<PagedResult<T>>, maxPages?: number): AsyncGenerator<T, void, undefined>;
|
|
973
|
+
/**
|
|
974
|
+
* Fetch multiple media entries in a single API request.
|
|
975
|
+
* Uses GraphQL aliases to batch up to 50 IDs per call.
|
|
976
|
+
*
|
|
977
|
+
* @param ids - Array of AniList media IDs
|
|
978
|
+
* @returns Array of media objects (same order as input IDs)
|
|
979
|
+
*/
|
|
980
|
+
getMediaBatch(ids: number[]): Promise<Media[]>;
|
|
981
|
+
/**
|
|
982
|
+
* Fetch multiple characters in a single API request.
|
|
983
|
+
*
|
|
984
|
+
* @param ids - Array of AniList character IDs
|
|
985
|
+
* @returns Array of character objects (same order as input IDs)
|
|
986
|
+
*/
|
|
987
|
+
getCharacterBatch(ids: number[]): Promise<Character[]>;
|
|
988
|
+
/**
|
|
989
|
+
* Fetch multiple staff members in a single API request.
|
|
990
|
+
*
|
|
991
|
+
* @param ids - Array of AniList staff IDs
|
|
992
|
+
* @returns Array of staff objects (same order as input IDs)
|
|
993
|
+
*/
|
|
994
|
+
getStaffBatch(ids: number[]): Promise<Staff[]>;
|
|
995
|
+
/** @internal */
|
|
996
|
+
private executeBatch;
|
|
997
|
+
/** @internal */
|
|
998
|
+
private chunk;
|
|
732
999
|
/**
|
|
733
1000
|
* Clear the entire response cache.
|
|
734
1001
|
*/
|
|
735
|
-
clearCache(): void
|
|
1002
|
+
clearCache(): Promise<void>;
|
|
736
1003
|
/**
|
|
737
|
-
* Number of entries currently in the cache.
|
|
1004
|
+
* Number of entries currently in the cache (sync).
|
|
1005
|
+
* For async adapters like Redis, this may be approximate.
|
|
738
1006
|
*/
|
|
739
1007
|
get cacheSize(): number;
|
|
1008
|
+
/**
|
|
1009
|
+
* Remove cache entries whose key matches the given pattern.
|
|
1010
|
+
*
|
|
1011
|
+
* @param pattern — A string (converted to RegExp) or RegExp
|
|
1012
|
+
* @returns Number of entries removed
|
|
1013
|
+
*/
|
|
1014
|
+
invalidateCache(pattern: string | RegExp): Promise<number>;
|
|
740
1015
|
}
|
|
741
1016
|
|
|
742
1017
|
/**
|
|
@@ -750,19 +1025,7 @@ declare class AniListError extends Error {
|
|
|
750
1025
|
constructor(message: string, status: number, errors?: unknown[]);
|
|
751
1026
|
}
|
|
752
1027
|
|
|
753
|
-
|
|
754
|
-
* Simple in-memory cache with configurable TTL.
|
|
755
|
-
* Used internally by AniListClient to avoid redundant API calls.
|
|
756
|
-
*/
|
|
757
|
-
interface CacheOptions {
|
|
758
|
-
/** Time-to-live in milliseconds (default: 24 hours) */
|
|
759
|
-
ttl?: number;
|
|
760
|
-
/** Maximum number of entries to keep (default: 500, 0 = unlimited) */
|
|
761
|
-
maxSize?: number;
|
|
762
|
-
/** Disable caching entirely (default: false) */
|
|
763
|
-
enabled?: boolean;
|
|
764
|
-
}
|
|
765
|
-
declare class MemoryCache {
|
|
1028
|
+
declare class MemoryCache implements CacheAdapter {
|
|
766
1029
|
private readonly ttl;
|
|
767
1030
|
private readonly maxSize;
|
|
768
1031
|
private readonly enabled;
|
|
@@ -780,6 +1043,87 @@ declare class MemoryCache {
|
|
|
780
1043
|
clear(): void;
|
|
781
1044
|
/** Number of entries currently stored. */
|
|
782
1045
|
get size(): number;
|
|
1046
|
+
/** Return all cache keys. */
|
|
1047
|
+
keys(): string[];
|
|
1048
|
+
/**
|
|
1049
|
+
* Remove all entries whose key matches the given pattern.
|
|
1050
|
+
*
|
|
1051
|
+
* @param pattern — A string (converted to RegExp) or RegExp.
|
|
1052
|
+
* @returns Number of entries removed.
|
|
1053
|
+
*/
|
|
1054
|
+
invalidate(pattern: string | RegExp): number;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Minimal interface representing a Redis client.
|
|
1059
|
+
* Compatible with both `ioredis` and `redis` (node-redis v4+).
|
|
1060
|
+
*/
|
|
1061
|
+
interface RedisLikeClient {
|
|
1062
|
+
get(key: string): Promise<string | null>;
|
|
1063
|
+
set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
|
|
1064
|
+
del(...keys: (string | string[])[]): Promise<number>;
|
|
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>;
|
|
1071
|
+
}
|
|
1072
|
+
interface RedisCacheOptions {
|
|
1073
|
+
/** A Redis client instance (ioredis or node-redis). */
|
|
1074
|
+
client: RedisLikeClient;
|
|
1075
|
+
/** Key prefix to namespace ani-client entries (default: `"ani:"`) */
|
|
1076
|
+
prefix?: string;
|
|
1077
|
+
/** TTL in seconds (default: 86 400 = 24 h) */
|
|
1078
|
+
ttl?: number;
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Redis-backed cache adapter for AniListClient.
|
|
1082
|
+
*
|
|
1083
|
+
* @example
|
|
1084
|
+
* ```ts
|
|
1085
|
+
* import Redis from "ioredis";
|
|
1086
|
+
* import { AniListClient, RedisCache } from "ani-client";
|
|
1087
|
+
*
|
|
1088
|
+
* const redis = new Redis();
|
|
1089
|
+
* const client = new AniListClient({
|
|
1090
|
+
* cacheAdapter: new RedisCache({ client: redis }),
|
|
1091
|
+
* });
|
|
1092
|
+
* ```
|
|
1093
|
+
*/
|
|
1094
|
+
declare class RedisCache implements CacheAdapter {
|
|
1095
|
+
private readonly client;
|
|
1096
|
+
private readonly prefix;
|
|
1097
|
+
private readonly ttl;
|
|
1098
|
+
constructor(options: RedisCacheOptions);
|
|
1099
|
+
private prefixedKey;
|
|
1100
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
1101
|
+
set<T>(key: string, data: T): Promise<void>;
|
|
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;
|
|
1111
|
+
clear(): Promise<void>;
|
|
1112
|
+
/**
|
|
1113
|
+
* Returns -1 because Redis keys can expire silently via TTL.
|
|
1114
|
+
* Use `getSize()` for an accurate count.
|
|
1115
|
+
*/
|
|
1116
|
+
get size(): number;
|
|
1117
|
+
/** Get the actual number of keys with this prefix in Redis. */
|
|
1118
|
+
getSize(): Promise<number>;
|
|
1119
|
+
keys(): Promise<string[]>;
|
|
1120
|
+
/**
|
|
1121
|
+
* Remove all entries whose key matches the given glob pattern.
|
|
1122
|
+
*
|
|
1123
|
+
* @param pattern — A glob pattern (e.g. `"*Media*"`)
|
|
1124
|
+
* @returns Number of entries removed.
|
|
1125
|
+
*/
|
|
1126
|
+
invalidate(pattern: string | RegExp): Promise<number>;
|
|
783
1127
|
}
|
|
784
1128
|
|
|
785
1129
|
/**
|
|
@@ -789,24 +1133,15 @@ declare class MemoryCache {
|
|
|
789
1133
|
* When a 429 (Too Many Requests) is received, the client
|
|
790
1134
|
* waits for the Retry-After header and retries automatically.
|
|
791
1135
|
*/
|
|
792
|
-
|
|
793
|
-
/** Max requests per window (default: 85, conservative under AniList's 90/min) */
|
|
794
|
-
maxRequests?: number;
|
|
795
|
-
/** Window size in milliseconds (default: 60 000 = 1 minute) */
|
|
796
|
-
windowMs?: number;
|
|
797
|
-
/** Max number of retries on 429 responses (default: 3) */
|
|
798
|
-
maxRetries?: number;
|
|
799
|
-
/** Default retry delay in ms when Retry-After header is missing (default: 2000) */
|
|
800
|
-
retryDelayMs?: number;
|
|
801
|
-
/** Disable rate limiting entirely (default: false) */
|
|
802
|
-
enabled?: boolean;
|
|
803
|
-
}
|
|
1136
|
+
|
|
804
1137
|
declare class RateLimiter {
|
|
805
1138
|
private readonly maxRequests;
|
|
806
1139
|
private readonly windowMs;
|
|
807
1140
|
private readonly maxRetries;
|
|
808
1141
|
private readonly retryDelayMs;
|
|
809
1142
|
private readonly enabled;
|
|
1143
|
+
private readonly timeoutMs;
|
|
1144
|
+
private readonly retryOnNetworkError;
|
|
810
1145
|
/** @internal */
|
|
811
1146
|
private timestamps;
|
|
812
1147
|
constructor(options?: RateLimitOptions);
|
|
@@ -815,10 +1150,15 @@ declare class RateLimiter {
|
|
|
815
1150
|
*/
|
|
816
1151
|
acquire(): Promise<void>;
|
|
817
1152
|
/**
|
|
818
|
-
* Execute a fetch with automatic retry on 429 responses.
|
|
1153
|
+
* Execute a fetch with automatic retry on 429 responses and network errors.
|
|
819
1154
|
*/
|
|
820
|
-
fetchWithRetry(url: string, init: RequestInit
|
|
1155
|
+
fetchWithRetry(url: string, init: RequestInit, hooks?: {
|
|
1156
|
+
onRetry?: (attempt: number, reason: string, delayMs: number) => void;
|
|
1157
|
+
onRateLimit?: (retryAfterMs: number) => void;
|
|
1158
|
+
}): Promise<Response>;
|
|
1159
|
+
/** @internal */
|
|
1160
|
+
private fetchWithTimeout;
|
|
821
1161
|
private sleep;
|
|
822
1162
|
}
|
|
823
1163
|
|
|
824
|
-
export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, 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
|
|
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 };
|