ani-client 1.1.0 → 1.3.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 +363 -127
- package/dist/index.d.mts +440 -7
- package/dist/index.d.ts +440 -7
- package/dist/index.js +703 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +699 -82
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -15
package/dist/index.d.mts
CHANGED
|
@@ -101,6 +101,28 @@ interface Studio {
|
|
|
101
101
|
interface StudioConnection {
|
|
102
102
|
nodes: Studio[];
|
|
103
103
|
}
|
|
104
|
+
declare enum MediaRelationType {
|
|
105
|
+
ADAPTATION = "ADAPTATION",
|
|
106
|
+
PREQUEL = "PREQUEL",
|
|
107
|
+
SEQUEL = "SEQUEL",
|
|
108
|
+
PARENT = "PARENT",
|
|
109
|
+
SIDE_STORY = "SIDE_STORY",
|
|
110
|
+
CHARACTER = "CHARACTER",
|
|
111
|
+
SUMMARY = "SUMMARY",
|
|
112
|
+
ALTERNATIVE = "ALTERNATIVE",
|
|
113
|
+
SPIN_OFF = "SPIN_OFF",
|
|
114
|
+
OTHER = "OTHER",
|
|
115
|
+
SOURCE = "SOURCE",
|
|
116
|
+
COMPILATION = "COMPILATION",
|
|
117
|
+
CONTAINS = "CONTAINS"
|
|
118
|
+
}
|
|
119
|
+
interface MediaEdge {
|
|
120
|
+
relationType: MediaRelationType;
|
|
121
|
+
node: Pick<Media, "id" | "title" | "type" | "format" | "status" | "coverImage" | "siteUrl">;
|
|
122
|
+
}
|
|
123
|
+
interface MediaConnection {
|
|
124
|
+
edges: MediaEdge[];
|
|
125
|
+
}
|
|
104
126
|
interface CharacterName {
|
|
105
127
|
first: string | null;
|
|
106
128
|
middle: string | null;
|
|
@@ -145,6 +167,7 @@ interface Media {
|
|
|
145
167
|
trending: number | null;
|
|
146
168
|
tags: MediaTag[];
|
|
147
169
|
studios: StudioConnection;
|
|
170
|
+
relations: MediaConnection | null;
|
|
148
171
|
isAdult: boolean | null;
|
|
149
172
|
siteUrl: string | null;
|
|
150
173
|
}
|
|
@@ -288,6 +311,163 @@ interface GetPlanningOptions {
|
|
|
288
311
|
page?: number;
|
|
289
312
|
perPage?: number;
|
|
290
313
|
}
|
|
314
|
+
declare enum RecommendationSort {
|
|
315
|
+
ID = "ID",
|
|
316
|
+
ID_DESC = "ID_DESC",
|
|
317
|
+
RATING = "RATING",
|
|
318
|
+
RATING_DESC = "RATING_DESC"
|
|
319
|
+
}
|
|
320
|
+
interface Recommendation {
|
|
321
|
+
id: number;
|
|
322
|
+
rating: number | null;
|
|
323
|
+
userRating: string | null;
|
|
324
|
+
mediaRecommendation: Media;
|
|
325
|
+
user: {
|
|
326
|
+
id: number;
|
|
327
|
+
name: string;
|
|
328
|
+
avatar: UserAvatar;
|
|
329
|
+
} | null;
|
|
330
|
+
}
|
|
331
|
+
interface GetRecommendationsOptions {
|
|
332
|
+
/** The AniList media ID to get recommendations for */
|
|
333
|
+
mediaId: number;
|
|
334
|
+
/** Sort order (default: RATING_DESC) */
|
|
335
|
+
sort?: RecommendationSort[];
|
|
336
|
+
page?: number;
|
|
337
|
+
perPage?: number;
|
|
338
|
+
}
|
|
339
|
+
declare enum MediaListStatus {
|
|
340
|
+
CURRENT = "CURRENT",
|
|
341
|
+
PLANNING = "PLANNING",
|
|
342
|
+
COMPLETED = "COMPLETED",
|
|
343
|
+
DROPPED = "DROPPED",
|
|
344
|
+
PAUSED = "PAUSED",
|
|
345
|
+
REPEATING = "REPEATING"
|
|
346
|
+
}
|
|
347
|
+
declare enum MediaListSort {
|
|
348
|
+
MEDIA_ID = "MEDIA_ID",
|
|
349
|
+
MEDIA_ID_DESC = "MEDIA_ID_DESC",
|
|
350
|
+
SCORE = "SCORE",
|
|
351
|
+
SCORE_DESC = "SCORE_DESC",
|
|
352
|
+
STATUS = "STATUS",
|
|
353
|
+
STATUS_DESC = "STATUS_DESC",
|
|
354
|
+
PROGRESS = "PROGRESS",
|
|
355
|
+
PROGRESS_DESC = "PROGRESS_DESC",
|
|
356
|
+
PROGRESS_VOLUMES = "PROGRESS_VOLUMES",
|
|
357
|
+
PROGRESS_VOLUMES_DESC = "PROGRESS_VOLUMES_DESC",
|
|
358
|
+
REPEAT = "REPEAT",
|
|
359
|
+
REPEAT_DESC = "REPEAT_DESC",
|
|
360
|
+
PRIORITY = "PRIORITY",
|
|
361
|
+
PRIORITY_DESC = "PRIORITY_DESC",
|
|
362
|
+
STARTED_ON = "STARTED_ON",
|
|
363
|
+
STARTED_ON_DESC = "STARTED_ON_DESC",
|
|
364
|
+
FINISHED_ON = "FINISHED_ON",
|
|
365
|
+
FINISHED_ON_DESC = "FINISHED_ON_DESC",
|
|
366
|
+
ADDED_TIME = "ADDED_TIME",
|
|
367
|
+
ADDED_TIME_DESC = "ADDED_TIME_DESC",
|
|
368
|
+
UPDATED_TIME = "UPDATED_TIME",
|
|
369
|
+
UPDATED_TIME_DESC = "UPDATED_TIME_DESC",
|
|
370
|
+
MEDIA_TITLE_ROMAJI = "MEDIA_TITLE_ROMAJI",
|
|
371
|
+
MEDIA_TITLE_ROMAJI_DESC = "MEDIA_TITLE_ROMAJI_DESC",
|
|
372
|
+
MEDIA_TITLE_ENGLISH = "MEDIA_TITLE_ENGLISH",
|
|
373
|
+
MEDIA_TITLE_ENGLISH_DESC = "MEDIA_TITLE_ENGLISH_DESC",
|
|
374
|
+
MEDIA_TITLE_NATIVE = "MEDIA_TITLE_NATIVE",
|
|
375
|
+
MEDIA_TITLE_NATIVE_DESC = "MEDIA_TITLE_NATIVE_DESC",
|
|
376
|
+
MEDIA_POPULARITY = "MEDIA_POPULARITY",
|
|
377
|
+
MEDIA_POPULARITY_DESC = "MEDIA_POPULARITY_DESC"
|
|
378
|
+
}
|
|
379
|
+
interface MediaListEntry {
|
|
380
|
+
id: number;
|
|
381
|
+
mediaId: number;
|
|
382
|
+
status: MediaListStatus;
|
|
383
|
+
score: number | null;
|
|
384
|
+
progress: number | null;
|
|
385
|
+
progressVolumes: number | null;
|
|
386
|
+
repeat: number | null;
|
|
387
|
+
priority: number | null;
|
|
388
|
+
private: boolean | null;
|
|
389
|
+
notes: string | null;
|
|
390
|
+
startedAt: FuzzyDate | null;
|
|
391
|
+
completedAt: FuzzyDate | null;
|
|
392
|
+
updatedAt: number | null;
|
|
393
|
+
createdAt: number | null;
|
|
394
|
+
media: Media;
|
|
395
|
+
}
|
|
396
|
+
interface GetSeasonOptions {
|
|
397
|
+
/** The season (WINTER, SPRING, SUMMER, FALL) */
|
|
398
|
+
season: MediaSeason;
|
|
399
|
+
/** The year */
|
|
400
|
+
seasonYear: number;
|
|
401
|
+
/** Filter by ANIME or MANGA (defaults to ANIME) */
|
|
402
|
+
type?: MediaType;
|
|
403
|
+
/** Sort order (default: POPULARITY_DESC) */
|
|
404
|
+
sort?: MediaSort[];
|
|
405
|
+
page?: number;
|
|
406
|
+
perPage?: number;
|
|
407
|
+
}
|
|
408
|
+
interface GetUserMediaListOptions {
|
|
409
|
+
/** User ID (provide either userId or userName) */
|
|
410
|
+
userId?: number;
|
|
411
|
+
/** Username (provide either userId or userName) */
|
|
412
|
+
userName?: string;
|
|
413
|
+
/** ANIME or MANGA */
|
|
414
|
+
type: MediaType;
|
|
415
|
+
/** Filter by list status (CURRENT, COMPLETED, etc.) */
|
|
416
|
+
status?: MediaListStatus;
|
|
417
|
+
/** Sort order */
|
|
418
|
+
sort?: MediaListSort[];
|
|
419
|
+
page?: number;
|
|
420
|
+
perPage?: number;
|
|
421
|
+
}
|
|
422
|
+
interface StudioDetail {
|
|
423
|
+
id: number;
|
|
424
|
+
name: string;
|
|
425
|
+
isAnimationStudio: boolean;
|
|
426
|
+
siteUrl: string | null;
|
|
427
|
+
favourites: number | null;
|
|
428
|
+
media: {
|
|
429
|
+
pageInfo: PageInfo;
|
|
430
|
+
nodes: Pick<Media, "id" | "title" | "type" | "format" | "coverImage" | "siteUrl">[];
|
|
431
|
+
} | null;
|
|
432
|
+
}
|
|
433
|
+
interface SearchStudioOptions {
|
|
434
|
+
query?: string;
|
|
435
|
+
page?: number;
|
|
436
|
+
perPage?: number;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Interface that all cache adapters must implement.
|
|
440
|
+
* Methods may return sync values or Promises — the client awaits all calls.
|
|
441
|
+
*/
|
|
442
|
+
interface CacheAdapter {
|
|
443
|
+
/** Retrieve a cached value, or `undefined` if missing / expired. */
|
|
444
|
+
get<T>(key: string): T | undefined | Promise<T | undefined>;
|
|
445
|
+
/** Store a value in the cache. */
|
|
446
|
+
set<T>(key: string, data: T): void | Promise<void>;
|
|
447
|
+
/** Remove a specific entry. Returns `true` if the key existed. */
|
|
448
|
+
delete(key: string): boolean | Promise<boolean>;
|
|
449
|
+
/** Clear the entire cache. */
|
|
450
|
+
clear(): void | Promise<void>;
|
|
451
|
+
/** Number of entries currently stored (sync). Returns -1 if unknown. */
|
|
452
|
+
readonly size: number;
|
|
453
|
+
/** Return all cache keys. */
|
|
454
|
+
keys(): IterableIterator<string> | string[] | Promise<string[]>;
|
|
455
|
+
/** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */
|
|
456
|
+
invalidate?(pattern: string | RegExp): number | Promise<number>;
|
|
457
|
+
}
|
|
458
|
+
/** Event hooks for logging, debugging, and monitoring. */
|
|
459
|
+
interface AniListHooks {
|
|
460
|
+
/** Called before every API request. */
|
|
461
|
+
onRequest?: (query: string, variables: Record<string, unknown>) => void;
|
|
462
|
+
/** Called when a response is served from cache. */
|
|
463
|
+
onCacheHit?: (key: string) => void;
|
|
464
|
+
/** Called when the rate limiter enforces a wait (429 received). */
|
|
465
|
+
onRateLimit?: (retryAfterMs: number) => void;
|
|
466
|
+
/** Called when a request is retried (429 or network error). */
|
|
467
|
+
onRetry?: (attempt: number, reason: string, delayMs: number) => void;
|
|
468
|
+
/** Called when a request completes. */
|
|
469
|
+
onResponse?: (query: string, durationMs: number, fromCache: boolean) => void;
|
|
470
|
+
}
|
|
291
471
|
interface AniListClientOptions {
|
|
292
472
|
/** Optional AniList OAuth token for authenticated requests */
|
|
293
473
|
token?: string;
|
|
@@ -302,6 +482,8 @@ interface AniListClientOptions {
|
|
|
302
482
|
/** Set to false to disable caching entirely */
|
|
303
483
|
enabled?: boolean;
|
|
304
484
|
};
|
|
485
|
+
/** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */
|
|
486
|
+
cacheAdapter?: CacheAdapter;
|
|
305
487
|
/** Rate limiter configuration (enabled by default, 85 req/min) */
|
|
306
488
|
rateLimit?: {
|
|
307
489
|
/** Max requests per window (default: 85) */
|
|
@@ -314,7 +496,13 @@ interface AniListClientOptions {
|
|
|
314
496
|
retryDelayMs?: number;
|
|
315
497
|
/** Set to false to disable rate limiting entirely */
|
|
316
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;
|
|
317
503
|
};
|
|
504
|
+
/** Event hooks for logging, debugging, and monitoring */
|
|
505
|
+
hooks?: AniListHooks;
|
|
318
506
|
}
|
|
319
507
|
|
|
320
508
|
/**
|
|
@@ -338,13 +526,22 @@ interface AniListClientOptions {
|
|
|
338
526
|
declare class AniListClient {
|
|
339
527
|
private readonly apiUrl;
|
|
340
528
|
private readonly headers;
|
|
341
|
-
private readonly
|
|
529
|
+
private readonly cacheAdapter;
|
|
342
530
|
private readonly rateLimiter;
|
|
531
|
+
private readonly hooks;
|
|
532
|
+
private readonly inFlight;
|
|
343
533
|
constructor(options?: AniListClientOptions);
|
|
344
534
|
/**
|
|
345
535
|
* @internal
|
|
346
536
|
*/
|
|
347
537
|
private request;
|
|
538
|
+
/** @internal */
|
|
539
|
+
private executeRequest;
|
|
540
|
+
/**
|
|
541
|
+
* @internal
|
|
542
|
+
* Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.
|
|
543
|
+
*/
|
|
544
|
+
private pagedRequest;
|
|
348
545
|
/**
|
|
349
546
|
* Fetch a single media entry by its AniList ID.
|
|
350
547
|
*
|
|
@@ -455,14 +652,171 @@ declare class AniListClient {
|
|
|
455
652
|
* ```
|
|
456
653
|
*/
|
|
457
654
|
getPlanning(options?: GetPlanningOptions): Promise<PagedResult<Media>>;
|
|
655
|
+
/**
|
|
656
|
+
* Get recommendations for a specific media.
|
|
657
|
+
*
|
|
658
|
+
* Returns other anime/manga that users have recommended based on the given media.
|
|
659
|
+
*
|
|
660
|
+
* @param mediaId - The AniList media ID
|
|
661
|
+
* @param options - Optional sort / pagination parameters
|
|
662
|
+
* @returns Paginated list of recommendations
|
|
663
|
+
*
|
|
664
|
+
* @example
|
|
665
|
+
* ```ts
|
|
666
|
+
* // Get recommendations for Cowboy Bebop
|
|
667
|
+
* const recs = await client.getRecommendations(1);
|
|
668
|
+
* recs.results.forEach((r) =>
|
|
669
|
+
* console.log(`${r.mediaRecommendation.title.romaji} (rating: ${r.rating})`)
|
|
670
|
+
* );
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
getRecommendations(mediaId: number, options?: Omit<GetRecommendationsOptions, "mediaId">): Promise<PagedResult<Recommendation>>;
|
|
674
|
+
/**
|
|
675
|
+
* Get anime (or manga) for a specific season and year.
|
|
676
|
+
*
|
|
677
|
+
* @param options - Season, year and optional filter / pagination parameters
|
|
678
|
+
* @returns Paginated list of media for the given season
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```ts
|
|
682
|
+
* import { MediaSeason } from "ani-client";
|
|
683
|
+
*
|
|
684
|
+
* const winter2026 = await client.getMediaBySeason({
|
|
685
|
+
* season: MediaSeason.WINTER,
|
|
686
|
+
* seasonYear: 2026,
|
|
687
|
+
* perPage: 10,
|
|
688
|
+
* });
|
|
689
|
+
* ```
|
|
690
|
+
*/
|
|
691
|
+
getMediaBySeason(options: GetSeasonOptions): Promise<PagedResult<Media>>;
|
|
692
|
+
/**
|
|
693
|
+
* Get a user's anime or manga list.
|
|
694
|
+
*
|
|
695
|
+
* Provide either `userId` or `userName` to identify the user.
|
|
696
|
+
* Requires `type` (ANIME or MANGA). Optionally filter by list status.
|
|
697
|
+
*
|
|
698
|
+
* @param options - User identifier, media type, and optional filters
|
|
699
|
+
* @returns Paginated list of media list entries
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```ts
|
|
703
|
+
* import { MediaType, MediaListStatus } from "ani-client";
|
|
704
|
+
*
|
|
705
|
+
* // Get a user's completed anime list
|
|
706
|
+
* const list = await client.getUserMediaList({
|
|
707
|
+
* userName: "AniList",
|
|
708
|
+
* type: MediaType.ANIME,
|
|
709
|
+
* status: MediaListStatus.COMPLETED,
|
|
710
|
+
* });
|
|
711
|
+
* list.results.forEach((entry) =>
|
|
712
|
+
* console.log(`${entry.media.title.romaji} — ${entry.score}/100`)
|
|
713
|
+
* );
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
716
|
+
getUserMediaList(options: GetUserMediaListOptions): Promise<PagedResult<MediaListEntry>>;
|
|
717
|
+
/**
|
|
718
|
+
* Fetch a studio by its AniList ID.
|
|
719
|
+
*
|
|
720
|
+
* Returns studio details along with its most popular productions.
|
|
721
|
+
*
|
|
722
|
+
* @param id - The AniList studio ID
|
|
723
|
+
*/
|
|
724
|
+
getStudio(id: number): Promise<StudioDetail>;
|
|
725
|
+
/**
|
|
726
|
+
* Search for studios by name.
|
|
727
|
+
*
|
|
728
|
+
* @param options - Search / pagination parameters
|
|
729
|
+
* @returns Paginated list of studios
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```ts
|
|
733
|
+
* const studios = await client.searchStudios({ query: "MAPPA" });
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
736
|
+
searchStudios(options?: SearchStudioOptions): Promise<PagedResult<StudioDetail>>;
|
|
737
|
+
/**
|
|
738
|
+
* Get all available genres on AniList.
|
|
739
|
+
*
|
|
740
|
+
* @returns Array of genre strings (e.g. "Action", "Adventure", ...)
|
|
741
|
+
*/
|
|
742
|
+
getGenres(): Promise<string[]>;
|
|
743
|
+
/**
|
|
744
|
+
* Get all available media tags on AniList.
|
|
745
|
+
*
|
|
746
|
+
* @returns Array of tag objects with id, name, description, category, isAdult
|
|
747
|
+
*/
|
|
748
|
+
getTags(): Promise<MediaTag[]>;
|
|
749
|
+
/**
|
|
750
|
+
* Auto-paginating async iterator.
|
|
751
|
+
*
|
|
752
|
+
* Wraps any paginated method and yields individual items across all pages.
|
|
753
|
+
* Stops when `hasNextPage` is `false` or `maxPages` is reached.
|
|
754
|
+
*
|
|
755
|
+
* @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`
|
|
756
|
+
* @param maxPages - Maximum number of pages to fetch (default: Infinity)
|
|
757
|
+
* @returns An async iterable iterator of individual items
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```ts
|
|
761
|
+
* // Iterate over all search results
|
|
762
|
+
* for await (const anime of client.paginate((page) =>
|
|
763
|
+
* client.searchMedia({ query: "Naruto", page, perPage: 10 })
|
|
764
|
+
* )) {
|
|
765
|
+
* console.log(anime.title.romaji);
|
|
766
|
+
* }
|
|
767
|
+
*
|
|
768
|
+
* // Limit to 3 pages
|
|
769
|
+
* for await (const anime of client.paginate(
|
|
770
|
+
* (page) => client.getTrending(MediaType.ANIME, page, 20),
|
|
771
|
+
* 3,
|
|
772
|
+
* )) {
|
|
773
|
+
* console.log(anime.title.romaji);
|
|
774
|
+
* }
|
|
775
|
+
* ```
|
|
776
|
+
*/
|
|
777
|
+
paginate<T>(fetchPage: (page: number) => Promise<PagedResult<T>>, maxPages?: number): AsyncGenerator<T, void, undefined>;
|
|
778
|
+
/**
|
|
779
|
+
* Fetch multiple media entries in a single API request.
|
|
780
|
+
* Uses GraphQL aliases to batch up to 50 IDs per call.
|
|
781
|
+
*
|
|
782
|
+
* @param ids - Array of AniList media IDs
|
|
783
|
+
* @returns Array of media objects (same order as input IDs)
|
|
784
|
+
*/
|
|
785
|
+
getMediaBatch(ids: number[]): Promise<Media[]>;
|
|
786
|
+
/**
|
|
787
|
+
* Fetch multiple characters in a single API request.
|
|
788
|
+
*
|
|
789
|
+
* @param ids - Array of AniList character IDs
|
|
790
|
+
* @returns Array of character objects (same order as input IDs)
|
|
791
|
+
*/
|
|
792
|
+
getCharacterBatch(ids: number[]): Promise<Character[]>;
|
|
793
|
+
/**
|
|
794
|
+
* Fetch multiple staff members in a single API request.
|
|
795
|
+
*
|
|
796
|
+
* @param ids - Array of AniList staff IDs
|
|
797
|
+
* @returns Array of staff objects (same order as input IDs)
|
|
798
|
+
*/
|
|
799
|
+
getStaffBatch(ids: number[]): Promise<Staff[]>;
|
|
800
|
+
/** @internal */
|
|
801
|
+
private executeBatch;
|
|
802
|
+
/** @internal */
|
|
803
|
+
private chunk;
|
|
458
804
|
/**
|
|
459
805
|
* Clear the entire response cache.
|
|
460
806
|
*/
|
|
461
|
-
clearCache(): void
|
|
807
|
+
clearCache(): Promise<void>;
|
|
462
808
|
/**
|
|
463
|
-
* Number of entries currently in the cache.
|
|
809
|
+
* Number of entries currently in the cache (sync).
|
|
810
|
+
* For async adapters like Redis, this may be approximate.
|
|
464
811
|
*/
|
|
465
812
|
get cacheSize(): number;
|
|
813
|
+
/**
|
|
814
|
+
* Remove cache entries whose key matches the given pattern.
|
|
815
|
+
*
|
|
816
|
+
* @param pattern — A string (converted to RegExp) or RegExp
|
|
817
|
+
* @returns Number of entries removed
|
|
818
|
+
*/
|
|
819
|
+
invalidateCache(pattern: string | RegExp): Promise<number>;
|
|
466
820
|
}
|
|
467
821
|
|
|
468
822
|
/**
|
|
@@ -488,7 +842,7 @@ interface CacheOptions {
|
|
|
488
842
|
/** Disable caching entirely (default: false) */
|
|
489
843
|
enabled?: boolean;
|
|
490
844
|
}
|
|
491
|
-
declare class MemoryCache {
|
|
845
|
+
declare class MemoryCache implements CacheAdapter {
|
|
492
846
|
private readonly ttl;
|
|
493
847
|
private readonly maxSize;
|
|
494
848
|
private readonly enabled;
|
|
@@ -506,6 +860,74 @@ declare class MemoryCache {
|
|
|
506
860
|
clear(): void;
|
|
507
861
|
/** Number of entries currently stored. */
|
|
508
862
|
get size(): number;
|
|
863
|
+
/** Return an iterator over all cache keys. */
|
|
864
|
+
keys(): IterableIterator<string>;
|
|
865
|
+
/**
|
|
866
|
+
* Remove all entries whose key matches the given pattern.
|
|
867
|
+
*
|
|
868
|
+
* @param pattern — A string (converted to RegExp) or RegExp.
|
|
869
|
+
* @returns Number of entries removed.
|
|
870
|
+
*/
|
|
871
|
+
invalidate(pattern: string | RegExp): number;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Minimal interface representing a Redis client.
|
|
876
|
+
* Compatible with both `ioredis` and `redis` (node-redis v4+).
|
|
877
|
+
*/
|
|
878
|
+
interface RedisLikeClient {
|
|
879
|
+
get(key: string): Promise<string | null>;
|
|
880
|
+
set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
|
|
881
|
+
del(...keys: (string | string[])[]): Promise<number>;
|
|
882
|
+
keys(pattern: string): Promise<string[]>;
|
|
883
|
+
}
|
|
884
|
+
interface RedisCacheOptions {
|
|
885
|
+
/** A Redis client instance (ioredis or node-redis). */
|
|
886
|
+
client: RedisLikeClient;
|
|
887
|
+
/** Key prefix to namespace ani-client entries (default: `"ani:"`) */
|
|
888
|
+
prefix?: string;
|
|
889
|
+
/** TTL in seconds (default: 86 400 = 24 h) */
|
|
890
|
+
ttl?: number;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Redis-backed cache adapter for AniListClient.
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* ```ts
|
|
897
|
+
* import Redis from "ioredis";
|
|
898
|
+
* import { AniListClient, RedisCache } from "ani-client";
|
|
899
|
+
*
|
|
900
|
+
* const redis = new Redis();
|
|
901
|
+
* const client = new AniListClient({
|
|
902
|
+
* cacheAdapter: new RedisCache({ client: redis }),
|
|
903
|
+
* });
|
|
904
|
+
* ```
|
|
905
|
+
*/
|
|
906
|
+
declare class RedisCache implements CacheAdapter {
|
|
907
|
+
private readonly client;
|
|
908
|
+
private readonly prefix;
|
|
909
|
+
private readonly ttl;
|
|
910
|
+
constructor(options: RedisCacheOptions);
|
|
911
|
+
private prefixedKey;
|
|
912
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
913
|
+
set<T>(key: string, data: T): Promise<void>;
|
|
914
|
+
delete(key: string): Promise<boolean>;
|
|
915
|
+
clear(): Promise<void>;
|
|
916
|
+
/**
|
|
917
|
+
* Returns -1 because Redis keys can expire silently via TTL.
|
|
918
|
+
* Use `getSize()` for an accurate count.
|
|
919
|
+
*/
|
|
920
|
+
get size(): number;
|
|
921
|
+
/** Get the actual number of keys with this prefix in Redis. */
|
|
922
|
+
getSize(): Promise<number>;
|
|
923
|
+
keys(): Promise<string[]>;
|
|
924
|
+
/**
|
|
925
|
+
* Remove all entries whose key matches the given glob pattern.
|
|
926
|
+
*
|
|
927
|
+
* @param pattern — A glob pattern (e.g. `"*Media*"`)
|
|
928
|
+
* @returns Number of entries removed.
|
|
929
|
+
*/
|
|
930
|
+
invalidate(pattern: string): Promise<number>;
|
|
509
931
|
}
|
|
510
932
|
|
|
511
933
|
/**
|
|
@@ -526,6 +948,10 @@ interface RateLimitOptions {
|
|
|
526
948
|
retryDelayMs?: number;
|
|
527
949
|
/** Disable rate limiting entirely (default: false) */
|
|
528
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;
|
|
529
955
|
}
|
|
530
956
|
declare class RateLimiter {
|
|
531
957
|
private readonly maxRequests;
|
|
@@ -533,6 +959,8 @@ declare class RateLimiter {
|
|
|
533
959
|
private readonly maxRetries;
|
|
534
960
|
private readonly retryDelayMs;
|
|
535
961
|
private readonly enabled;
|
|
962
|
+
private readonly timeoutMs;
|
|
963
|
+
private readonly retryOnNetworkError;
|
|
536
964
|
/** @internal */
|
|
537
965
|
private timestamps;
|
|
538
966
|
constructor(options?: RateLimitOptions);
|
|
@@ -541,10 +969,15 @@ declare class RateLimiter {
|
|
|
541
969
|
*/
|
|
542
970
|
acquire(): Promise<void>;
|
|
543
971
|
/**
|
|
544
|
-
* Execute a fetch with automatic retry on 429 responses.
|
|
972
|
+
* Execute a fetch with automatic retry on 429 responses and network errors.
|
|
545
973
|
*/
|
|
546
|
-
fetchWithRetry(url: string, init: RequestInit
|
|
974
|
+
fetchWithRetry(url: string, init: RequestInit, hooks?: {
|
|
975
|
+
onRetry?: (attempt: number, reason: string, delayMs: number) => void;
|
|
976
|
+
onRateLimit?: (retryAfterMs: number) => void;
|
|
977
|
+
}): Promise<Response>;
|
|
978
|
+
/** @internal */
|
|
979
|
+
private fetchWithTimeout;
|
|
547
980
|
private sleep;
|
|
548
981
|
}
|
|
549
982
|
|
|
550
|
-
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 Media, type MediaCoverImage, MediaFormat, MediaSeason, MediaSort, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type PageInfo, type PagedResult, type RateLimitOptions, RateLimiter, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type Staff, type StaffImage, type StaffName, type Studio, type StudioConnection, type User, type UserAvatar, type UserStatistics };
|
|
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 };
|