ani-client 1.7.0 → 1.8.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 +104 -44
- package/dist/index.d.mts +117 -6
- package/dist/index.d.ts +117 -6
- package/dist/index.js +277 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +277 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,39 +1,40 @@
|
|
|
1
|
-
# ani-client
|
|
1
|
+
# ani-client
|
|
2
2
|
|
|
3
3
|

|
|
4
|
+
|
|
4
5
|
[](https://github.com/gonzyui/ani-client/actions/workflows/ci.yml)
|
|
5
|
-
[](https://www.npmjs.com/package/ani-client)
|
|
6
|
+
[](https://www.npmjs.com/package/ani-client)
|
|
7
|
+
[](https://www.npmjs.com/package/ani-client)
|
|
8
|
+
[](https://bundlephobia.com/package/ani-client)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
6
10
|
[](LICENSE)
|
|
7
11
|
|
|
8
|
-
> A
|
|
9
|
-
|
|
10
|
-
✨ **Showcase**: [Check here](https://ani-client.js.org/showcase) to see which projects use this package!
|
|
11
|
-
|
|
12
|
-
- **Zero dependencies** — uses the native `fetch` API
|
|
13
|
-
- **Universal** — Node.js ≥ 20, Bun, Deno and modern browsers
|
|
14
|
-
- **Dual format** — ships ESM + CJS with full TypeScript declarations
|
|
15
|
-
- **Reliable** — Built-in caching, Rate-limit protections with exponential backoff, automatic retries & request deduplication!
|
|
12
|
+
> A fully typed, zero-dependency client for the [AniList](https://anilist.co) GraphQL API.
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
✨ **Showcase**: [See who's using ani-client](https://ani-client.js.org/showcase)
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
## Highlights
|
|
20
17
|
|
|
21
|
-
**
|
|
18
|
+
- **Zero dependencies** — uses the native `fetch` API
|
|
19
|
+
- **Universal** — Node.js ≥ 20, Bun, Deno, and modern browsers
|
|
20
|
+
- **Dual format** — ships ESM + CJS with full `.d.ts` declarations
|
|
21
|
+
- **LRU cache** with TTL, stale-while-revalidate, and hit/miss stats
|
|
22
|
+
- **Rate-limit protection** with exponential backoff, retries, and custom strategies
|
|
23
|
+
- **Request deduplication** — concurrent identical queries share a single in-flight request
|
|
24
|
+
- **Batch queries** — fetch up to 50 media/characters/staff in one API call
|
|
25
|
+
- **Auto-pagination** — async iterator that yields items across pages
|
|
26
|
+
- **AbortSignal support** — cancel globally or per-request with `withSignal()`
|
|
27
|
+
- **Injectable logger** — plug in `console`, pino, winston, or any compatible logger
|
|
28
|
+
- **Redis-ready** — swap the cache adapter with the built-in `RedisCache` for distributed setups
|
|
22
29
|
|
|
23
30
|
## Install
|
|
24
31
|
|
|
25
32
|
```bash
|
|
26
|
-
# npm
|
|
27
33
|
npm install ani-client
|
|
28
|
-
|
|
29
|
-
# pnpm
|
|
34
|
+
# or
|
|
30
35
|
pnpm add ani-client
|
|
31
|
-
|
|
32
|
-
# yarn
|
|
36
|
+
# or
|
|
33
37
|
yarn add ani-client
|
|
34
|
-
|
|
35
|
-
# bun
|
|
36
|
-
bun add ani-client
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
## Quick start
|
|
@@ -43,59 +44,118 @@ import { AniListClient, MediaType } from "ani-client";
|
|
|
43
44
|
|
|
44
45
|
const client = new AniListClient();
|
|
45
46
|
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
console.log(
|
|
47
|
+
// Fetch an anime by AniList ID
|
|
48
|
+
const bebop = await client.getMedia(1);
|
|
49
|
+
console.log(bebop.title.romaji); // "Cowboy Bebop"
|
|
49
50
|
|
|
50
|
-
// Search
|
|
51
|
+
// Search with filters
|
|
51
52
|
const results = await client.searchMedia({
|
|
52
53
|
query: "Naruto",
|
|
53
54
|
type: MediaType.ANIME,
|
|
54
|
-
|
|
55
|
+
genres: ["Action"],
|
|
56
|
+
perPage: 10,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Cross-platform lookup by MyAnimeList ID
|
|
60
|
+
const fma = await client.getMediaByMalId(5114);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Features at a glance
|
|
64
|
+
|
|
65
|
+
### Caching & stale-while-revalidate
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const client = new AniListClient({
|
|
69
|
+
cache: {
|
|
70
|
+
ttl: 1000 * 60 * 5, // 5 min TTL
|
|
71
|
+
maxSize: 200, // LRU capacity
|
|
72
|
+
staleWhileRevalidateMs: 60_000, // serve stale for 1 min after expiry
|
|
73
|
+
},
|
|
55
74
|
});
|
|
56
|
-
|
|
75
|
+
|
|
76
|
+
// Check cache performance
|
|
77
|
+
console.log(client.cacheStats);
|
|
78
|
+
// { hits: 42, misses: 8, stales: 2, hitRate: 0.84 }
|
|
57
79
|
```
|
|
58
80
|
|
|
59
|
-
###
|
|
81
|
+
### Per-request cancellation
|
|
60
82
|
|
|
61
83
|
```ts
|
|
62
|
-
const
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const scoped = client.withSignal(controller.signal);
|
|
86
|
+
|
|
87
|
+
setTimeout(() => controller.abort(), 3_000);
|
|
88
|
+
const anime = await scoped.getMedia(1);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Structured logging
|
|
63
92
|
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
```ts
|
|
94
|
+
const client = new AniListClient({ logger: console });
|
|
95
|
+
// debug: "API request" { query: "query { Media(id: 1) { ... } }" }
|
|
96
|
+
// debug: "Request complete" { durationMs: 120, status: 200 }
|
|
66
97
|
```
|
|
67
98
|
|
|
68
|
-
###
|
|
99
|
+
### Rate limiting & retries
|
|
69
100
|
|
|
70
101
|
```ts
|
|
71
102
|
const client = new AniListClient({
|
|
72
103
|
rateLimit: {
|
|
104
|
+
maxRequests: 85,
|
|
105
|
+
windowMs: 60_000,
|
|
106
|
+
maxRetries: 3,
|
|
107
|
+
retryOnNetworkError: true,
|
|
73
108
|
retryStrategy: (attempt) => (attempt + 1) * 1000, // linear backoff
|
|
74
109
|
},
|
|
75
110
|
});
|
|
76
111
|
|
|
77
|
-
|
|
112
|
+
console.log(client.rateLimitInfo);
|
|
113
|
+
// { remaining: 82, limit: 85, reset: 1741104000 }
|
|
114
|
+
```
|
|
78
115
|
|
|
79
|
-
|
|
80
|
-
console.log(`${info?.remaining}/${info?.limit} requests remaining`);
|
|
116
|
+
### Batch & pagination
|
|
81
117
|
|
|
82
|
-
|
|
83
|
-
|
|
118
|
+
```ts
|
|
119
|
+
// Fetch 100 anime in 2 API calls (50 per batch)
|
|
120
|
+
const batch = await client.getMediaBatch([1, 2, 3, /* ...up to 100 IDs */]);
|
|
121
|
+
|
|
122
|
+
// Auto-paginate through all results
|
|
123
|
+
for await (const anime of client.paginate(
|
|
124
|
+
(page) => client.searchMedia({ query: "Gundam", page, perPage: 50 }),
|
|
125
|
+
5, // max 5 pages
|
|
126
|
+
)) {
|
|
127
|
+
console.log(anime.title.romaji);
|
|
128
|
+
}
|
|
84
129
|
```
|
|
85
130
|
|
|
86
|
-
###
|
|
131
|
+
### Users, characters, studios & more
|
|
87
132
|
|
|
88
133
|
```ts
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
await client.
|
|
134
|
+
const user = await client.getUser("AniList");
|
|
135
|
+
const favs = await client.getUserFavorites("AniList", { perPage: 50 });
|
|
136
|
+
const char = await client.getCharacter(1, { voiceActors: true });
|
|
137
|
+
const studio = await client.getStudio(21, { media: { perPage: 50 } });
|
|
138
|
+
const schedule = await client.getWeeklySchedule();
|
|
94
139
|
```
|
|
95
140
|
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
Full API reference, guides (caching, pagination, includes, hooks, etc.) and configuration examples:
|
|
144
|
+
|
|
145
|
+
**[ani-client.js.org](https://ani-client.js.org)**
|
|
146
|
+
|
|
147
|
+
## Requirements
|
|
148
|
+
|
|
149
|
+
| Runtime | Version |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| Node.js | ≥ 20 |
|
|
152
|
+
| Bun | ≥ 1.0 |
|
|
153
|
+
| Deno | ≥ 1.28 |
|
|
154
|
+
| Browsers | Any with `fetch` + `AbortController` |
|
|
155
|
+
|
|
96
156
|
## Contributing
|
|
97
157
|
|
|
98
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, coding standards, and
|
|
158
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, coding standards, and PR guidelines.
|
|
99
159
|
|
|
100
160
|
## License
|
|
101
161
|
|
package/dist/index.d.mts
CHANGED
|
@@ -50,6 +50,12 @@ interface CacheOptions {
|
|
|
50
50
|
maxSize?: number;
|
|
51
51
|
/** Set to false to disable caching entirely */
|
|
52
52
|
enabled?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Stale-while-revalidate grace period in milliseconds (default: 0 = disabled).
|
|
55
|
+
* When set, expired entries are still returned within the grace window,
|
|
56
|
+
* allowing the caller to refresh in the background.
|
|
57
|
+
*/
|
|
58
|
+
staleWhileRevalidateMs?: number;
|
|
53
59
|
}
|
|
54
60
|
/** Rate limiter configuration options. */
|
|
55
61
|
interface RateLimitOptions {
|
|
@@ -111,6 +117,16 @@ interface ResponseMeta {
|
|
|
111
117
|
/** Rate limit information from the API response headers (not present for cached responses). */
|
|
112
118
|
rateLimitInfo?: RateLimitInfo;
|
|
113
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Minimal logger interface for structured log output.
|
|
122
|
+
* Compatible with `console`, `pino`, `winston`, etc.
|
|
123
|
+
*/
|
|
124
|
+
interface Logger {
|
|
125
|
+
debug(message: string, ...args: unknown[]): void;
|
|
126
|
+
info(message: string, ...args: unknown[]): void;
|
|
127
|
+
warn(message: string, ...args: unknown[]): void;
|
|
128
|
+
error(message: string, ...args: unknown[]): void;
|
|
129
|
+
}
|
|
114
130
|
interface AniListClientOptions {
|
|
115
131
|
/** Optional AniList OAuth token for authenticated requests */
|
|
116
132
|
token?: string;
|
|
@@ -126,6 +142,17 @@ interface AniListClientOptions {
|
|
|
126
142
|
hooks?: AniListHooks;
|
|
127
143
|
/** Optional AbortSignal to cancel all requests made by this client */
|
|
128
144
|
signal?: AbortSignal;
|
|
145
|
+
/**
|
|
146
|
+
* Optional logger for structured log output.
|
|
147
|
+
* Accepts any object with `debug`, `info`, `warn`, `error` methods.
|
|
148
|
+
* Compatible with `console`, `pino`, `winston`, etc.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const client = new AniListClient({ logger: console });
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
logger?: Logger;
|
|
129
156
|
}
|
|
130
157
|
|
|
131
158
|
declare enum StaffSort {
|
|
@@ -394,6 +421,16 @@ interface SearchStudioOptions {
|
|
|
394
421
|
page?: number;
|
|
395
422
|
perPage?: number;
|
|
396
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Options for controlling embedded media when fetching a single studio.
|
|
426
|
+
* Pass `{ media: { perPage: 50 } }` to fetch more media per studio.
|
|
427
|
+
*/
|
|
428
|
+
interface StudioIncludeOptions {
|
|
429
|
+
/** Customize the number of media returned. `true` = 25 (default), or `{ perPage }`. */
|
|
430
|
+
media?: boolean | {
|
|
431
|
+
perPage?: number;
|
|
432
|
+
};
|
|
433
|
+
}
|
|
397
434
|
|
|
398
435
|
declare enum UserSort {
|
|
399
436
|
ID = "ID",
|
|
@@ -485,6 +522,13 @@ interface UserFavorites {
|
|
|
485
522
|
staff: FavoriteStaffNode[];
|
|
486
523
|
studios: FavoriteStudioNode[];
|
|
487
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Options for controlling the number of results when fetching user favorites.
|
|
527
|
+
*/
|
|
528
|
+
interface UserFavoritesOptions {
|
|
529
|
+
/** Number of items per category (default: 25, max: 50). */
|
|
530
|
+
perPage?: number;
|
|
531
|
+
}
|
|
488
532
|
|
|
489
533
|
declare enum MediaType {
|
|
490
534
|
ANIME = "ANIME",
|
|
@@ -945,6 +989,7 @@ declare class AniListClient {
|
|
|
945
989
|
private readonly cacheAdapter;
|
|
946
990
|
private readonly rateLimiter;
|
|
947
991
|
private readonly hooks;
|
|
992
|
+
private readonly logger?;
|
|
948
993
|
private readonly signal?;
|
|
949
994
|
private readonly inFlight;
|
|
950
995
|
private _rateLimitInfo?;
|
|
@@ -1000,6 +1045,13 @@ declare class AniListClient {
|
|
|
1000
1045
|
* @deprecated Use `getRecentlyUpdatedManga` instead. This alias will be removed in v2.
|
|
1001
1046
|
*/
|
|
1002
1047
|
getAiredChapters(options?: GetRecentChaptersOptions): Promise<PagedResult<Media>>;
|
|
1048
|
+
/**
|
|
1049
|
+
* Fetch a media entry by its MyAnimeList (MAL) ID.
|
|
1050
|
+
*
|
|
1051
|
+
* @param malId - The MyAnimeList ID
|
|
1052
|
+
* @param type - Optional media type to disambiguate (some MAL IDs map to both ANIME and MANGA)
|
|
1053
|
+
*/
|
|
1054
|
+
getMediaByMalId(malId: number, type?: MediaType): Promise<Media>;
|
|
1003
1055
|
/** Get the detailed schedule for the current week, sorted by day. */
|
|
1004
1056
|
getWeeklySchedule(date?: Date): Promise<WeeklySchedule>;
|
|
1005
1057
|
/** Get upcoming (not yet released) media. */
|
|
@@ -1030,17 +1082,29 @@ declare class AniListClient {
|
|
|
1030
1082
|
* Fetch a user's favorite anime, manga, characters, staff, and studios.
|
|
1031
1083
|
*
|
|
1032
1084
|
* @param idOrName - AniList user ID (number) or username (string)
|
|
1085
|
+
* @param options - Optional pagination options (perPage per category)
|
|
1033
1086
|
* @returns The user's favorites grouped by category
|
|
1034
1087
|
*
|
|
1035
1088
|
* @example
|
|
1036
1089
|
* ```typescript
|
|
1037
1090
|
* const favs = await client.getUserFavorites("AniList");
|
|
1038
1091
|
* favs.anime.forEach(a => console.log(a.title.romaji));
|
|
1092
|
+
*
|
|
1093
|
+
* // Fetch more results per category
|
|
1094
|
+
* const moreResults = await client.getUserFavorites(1, { perPage: 50 });
|
|
1095
|
+
* ```
|
|
1096
|
+
*/
|
|
1097
|
+
getUserFavorites(idOrName: number | string, options?: UserFavoritesOptions): Promise<UserFavorites>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Fetch a studio by its AniList ID.
|
|
1100
|
+
* Pass `include` to customise the number of media returned.
|
|
1101
|
+
*
|
|
1102
|
+
* @example
|
|
1103
|
+
* ```typescript
|
|
1104
|
+
* const studio = await client.getStudio(21, { media: { perPage: 50 } });
|
|
1039
1105
|
* ```
|
|
1040
1106
|
*/
|
|
1041
|
-
|
|
1042
|
-
/** Fetch a studio by its AniList ID. */
|
|
1043
|
-
getStudio(id: number): Promise<Studio>;
|
|
1107
|
+
getStudio(id: number, include?: StudioIncludeOptions): Promise<Studio>;
|
|
1044
1108
|
/** Search for studios by name. */
|
|
1045
1109
|
searchStudios(options?: SearchStudioOptions): Promise<PagedResult<Studio>>;
|
|
1046
1110
|
/** Fetch a forum thread by its AniList ID. */
|
|
@@ -1076,6 +1140,20 @@ declare class AniListClient {
|
|
|
1076
1140
|
invalidateCache(pattern: string | RegExp): Promise<number>;
|
|
1077
1141
|
/** Clean up resources held by the client. */
|
|
1078
1142
|
destroy(): Promise<void>;
|
|
1143
|
+
/**
|
|
1144
|
+
* Return a scoped view of this client where every request uses the given `AbortSignal`.
|
|
1145
|
+
* The returned object shares the same cache, rate limiter, and hooks.
|
|
1146
|
+
*
|
|
1147
|
+
* @example
|
|
1148
|
+
* ```ts
|
|
1149
|
+
* const controller = new AbortController();
|
|
1150
|
+
* const media = await client.withSignal(controller.signal).getMedia(1);
|
|
1151
|
+
*
|
|
1152
|
+
* // Cancel all in-flight requests made through the scoped view
|
|
1153
|
+
* controller.abort();
|
|
1154
|
+
* ```
|
|
1155
|
+
*/
|
|
1156
|
+
withSignal(signal: AbortSignal): AniListClient;
|
|
1079
1157
|
}
|
|
1080
1158
|
|
|
1081
1159
|
/**
|
|
@@ -1089,26 +1167,59 @@ declare class AniListError extends Error {
|
|
|
1089
1167
|
constructor(message: string, status: number, errors?: unknown[]);
|
|
1090
1168
|
}
|
|
1091
1169
|
|
|
1170
|
+
/** Cache performance statistics. */
|
|
1171
|
+
interface CacheStats {
|
|
1172
|
+
/** Total cache hits. */
|
|
1173
|
+
hits: number;
|
|
1174
|
+
/** Total cache misses. */
|
|
1175
|
+
misses: number;
|
|
1176
|
+
/** Stale entries returned (only with stale-while-revalidate). */
|
|
1177
|
+
stales: number;
|
|
1178
|
+
/** Hit rate as a ratio 0–1 (NaN if no requests yet). */
|
|
1179
|
+
hitRate: number;
|
|
1180
|
+
}
|
|
1092
1181
|
declare class MemoryCache implements CacheAdapter {
|
|
1093
1182
|
private readonly ttl;
|
|
1094
1183
|
private readonly maxSize;
|
|
1095
1184
|
private readonly enabled;
|
|
1185
|
+
private readonly swrMs;
|
|
1096
1186
|
private readonly store;
|
|
1187
|
+
private _hits;
|
|
1188
|
+
private _misses;
|
|
1189
|
+
private _stales;
|
|
1097
1190
|
constructor(options?: CacheOptions);
|
|
1098
1191
|
/** Build a deterministic cache key from a query + variables pair. */
|
|
1099
1192
|
static key(query: string, variables: Record<string, unknown>): string;
|
|
1100
|
-
/**
|
|
1193
|
+
/**
|
|
1194
|
+
* Retrieve a cached value, or `undefined` if missing / expired.
|
|
1195
|
+
* With stale-while-revalidate enabled, returns stale data within the grace window
|
|
1196
|
+
* and flags it so the caller can refresh in the background.
|
|
1197
|
+
*/
|
|
1101
1198
|
get<T>(key: string): T | undefined;
|
|
1102
1199
|
/** Store a value in the cache. */
|
|
1103
1200
|
set<T>(key: string, data: T): void;
|
|
1104
1201
|
/** Remove a specific entry. */
|
|
1105
1202
|
delete(key: string): boolean;
|
|
1106
|
-
/** Clear the entire cache. */
|
|
1203
|
+
/** Clear the entire cache and reset statistics. */
|
|
1107
1204
|
clear(): void;
|
|
1108
1205
|
/** Number of entries currently stored. */
|
|
1109
1206
|
get size(): number | Promise<number>;
|
|
1110
1207
|
/** Return all cache keys. */
|
|
1111
1208
|
keys(): string[];
|
|
1209
|
+
/**
|
|
1210
|
+
* Get cache performance statistics.
|
|
1211
|
+
*
|
|
1212
|
+
* @example
|
|
1213
|
+
* ```ts
|
|
1214
|
+
* const cache = new MemoryCache();
|
|
1215
|
+
* // ... after some usage ...
|
|
1216
|
+
* console.log(cache.stats);
|
|
1217
|
+
* // { hits: 42, misses: 8, stales: 0, hitRate: 0.84 }
|
|
1218
|
+
* ```
|
|
1219
|
+
*/
|
|
1220
|
+
get stats(): CacheStats;
|
|
1221
|
+
/** Reset cache statistics without clearing stored data. */
|
|
1222
|
+
resetStats(): void;
|
|
1112
1223
|
/**
|
|
1113
1224
|
* Remove all entries whose key matches the given pattern.
|
|
1114
1225
|
*
|
|
@@ -1239,4 +1350,4 @@ declare class RateLimiter {
|
|
|
1239
1350
|
|
|
1240
1351
|
declare function parseAniListMarkdown(text: string): string;
|
|
1241
1352
|
|
|
1242
|
-
export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, type AniListHooks, type CacheAdapter, type CacheOptions, type Character, type CharacterImage, type CharacterIncludeOptions, type CharacterMediaEdge, type CharacterName, CharacterRole, CharacterSort, type DayOfWeek, type ExternalLink, type FavoriteCharacterNode, type FavoriteMediaNode, type FavoriteStaffNode, type FavoriteStudioNode, 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, MediaSource, type MediaStaffConnection, type MediaStaffEdge, type MediaStats, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type NextAiringEpisode, type PageInfo, type PagedResult, type RateLimitInfo, type RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, RedisCache, type RedisCacheOptions, type RedisLikeClient, type ResponseMeta, type ScoreDistribution, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type SearchStudioOptions, type SearchThreadOptions, type SearchUserOptions, type Staff, type StaffImage, type StaffIncludeOptions, type StaffMediaNode, type StaffName, StaffSort, type StatusDistribution, type StreamingEpisode, type Studio, type StudioConnection, StudioSort, type Thread, type ThreadCategory, type ThreadMediaCategory, ThreadSort, type User, type UserAvatar, type UserFavorites, UserSort, type UserStatistics, type VoiceActor, type WeeklySchedule, parseAniListMarkdown };
|
|
1353
|
+
export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, type AniListHooks, type CacheAdapter, type CacheOptions, type CacheStats, type Character, type CharacterImage, type CharacterIncludeOptions, type CharacterMediaEdge, type CharacterName, CharacterRole, CharacterSort, type DayOfWeek, type ExternalLink, type FavoriteCharacterNode, type FavoriteMediaNode, type FavoriteStaffNode, type FavoriteStudioNode, type FuzzyDate, type GetAiringOptions, type GetPlanningOptions, type GetRecentChaptersOptions, type GetRecommendationsOptions, type GetSeasonOptions, type GetUserMediaListOptions, type Logger, type Media, type MediaCharacterConnection, type MediaCharacterEdge, type MediaConnection, type MediaCoverImage, type MediaEdge, MediaFormat, type MediaIncludeOptions, type MediaListEntry, MediaListSort, MediaListStatus, type MediaRecommendationNode, MediaRelationType, MediaSeason, MediaSort, MediaSource, type MediaStaffConnection, type MediaStaffEdge, type MediaStats, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type NextAiringEpisode, type PageInfo, type PagedResult, type RateLimitInfo, type RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, RedisCache, type RedisCacheOptions, type RedisLikeClient, type ResponseMeta, type ScoreDistribution, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type SearchStudioOptions, type SearchThreadOptions, type SearchUserOptions, type Staff, type StaffImage, type StaffIncludeOptions, type StaffMediaNode, type StaffName, StaffSort, type StatusDistribution, type StreamingEpisode, type Studio, type StudioConnection, type StudioIncludeOptions, StudioSort, type Thread, type ThreadCategory, type ThreadMediaCategory, ThreadSort, type User, type UserAvatar, type UserFavorites, type UserFavoritesOptions, UserSort, type UserStatistics, type VoiceActor, type WeeklySchedule, parseAniListMarkdown };
|
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,12 @@ interface CacheOptions {
|
|
|
50
50
|
maxSize?: number;
|
|
51
51
|
/** Set to false to disable caching entirely */
|
|
52
52
|
enabled?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Stale-while-revalidate grace period in milliseconds (default: 0 = disabled).
|
|
55
|
+
* When set, expired entries are still returned within the grace window,
|
|
56
|
+
* allowing the caller to refresh in the background.
|
|
57
|
+
*/
|
|
58
|
+
staleWhileRevalidateMs?: number;
|
|
53
59
|
}
|
|
54
60
|
/** Rate limiter configuration options. */
|
|
55
61
|
interface RateLimitOptions {
|
|
@@ -111,6 +117,16 @@ interface ResponseMeta {
|
|
|
111
117
|
/** Rate limit information from the API response headers (not present for cached responses). */
|
|
112
118
|
rateLimitInfo?: RateLimitInfo;
|
|
113
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Minimal logger interface for structured log output.
|
|
122
|
+
* Compatible with `console`, `pino`, `winston`, etc.
|
|
123
|
+
*/
|
|
124
|
+
interface Logger {
|
|
125
|
+
debug(message: string, ...args: unknown[]): void;
|
|
126
|
+
info(message: string, ...args: unknown[]): void;
|
|
127
|
+
warn(message: string, ...args: unknown[]): void;
|
|
128
|
+
error(message: string, ...args: unknown[]): void;
|
|
129
|
+
}
|
|
114
130
|
interface AniListClientOptions {
|
|
115
131
|
/** Optional AniList OAuth token for authenticated requests */
|
|
116
132
|
token?: string;
|
|
@@ -126,6 +142,17 @@ interface AniListClientOptions {
|
|
|
126
142
|
hooks?: AniListHooks;
|
|
127
143
|
/** Optional AbortSignal to cancel all requests made by this client */
|
|
128
144
|
signal?: AbortSignal;
|
|
145
|
+
/**
|
|
146
|
+
* Optional logger for structured log output.
|
|
147
|
+
* Accepts any object with `debug`, `info`, `warn`, `error` methods.
|
|
148
|
+
* Compatible with `console`, `pino`, `winston`, etc.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const client = new AniListClient({ logger: console });
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
logger?: Logger;
|
|
129
156
|
}
|
|
130
157
|
|
|
131
158
|
declare enum StaffSort {
|
|
@@ -394,6 +421,16 @@ interface SearchStudioOptions {
|
|
|
394
421
|
page?: number;
|
|
395
422
|
perPage?: number;
|
|
396
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Options for controlling embedded media when fetching a single studio.
|
|
426
|
+
* Pass `{ media: { perPage: 50 } }` to fetch more media per studio.
|
|
427
|
+
*/
|
|
428
|
+
interface StudioIncludeOptions {
|
|
429
|
+
/** Customize the number of media returned. `true` = 25 (default), or `{ perPage }`. */
|
|
430
|
+
media?: boolean | {
|
|
431
|
+
perPage?: number;
|
|
432
|
+
};
|
|
433
|
+
}
|
|
397
434
|
|
|
398
435
|
declare enum UserSort {
|
|
399
436
|
ID = "ID",
|
|
@@ -485,6 +522,13 @@ interface UserFavorites {
|
|
|
485
522
|
staff: FavoriteStaffNode[];
|
|
486
523
|
studios: FavoriteStudioNode[];
|
|
487
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Options for controlling the number of results when fetching user favorites.
|
|
527
|
+
*/
|
|
528
|
+
interface UserFavoritesOptions {
|
|
529
|
+
/** Number of items per category (default: 25, max: 50). */
|
|
530
|
+
perPage?: number;
|
|
531
|
+
}
|
|
488
532
|
|
|
489
533
|
declare enum MediaType {
|
|
490
534
|
ANIME = "ANIME",
|
|
@@ -945,6 +989,7 @@ declare class AniListClient {
|
|
|
945
989
|
private readonly cacheAdapter;
|
|
946
990
|
private readonly rateLimiter;
|
|
947
991
|
private readonly hooks;
|
|
992
|
+
private readonly logger?;
|
|
948
993
|
private readonly signal?;
|
|
949
994
|
private readonly inFlight;
|
|
950
995
|
private _rateLimitInfo?;
|
|
@@ -1000,6 +1045,13 @@ declare class AniListClient {
|
|
|
1000
1045
|
* @deprecated Use `getRecentlyUpdatedManga` instead. This alias will be removed in v2.
|
|
1001
1046
|
*/
|
|
1002
1047
|
getAiredChapters(options?: GetRecentChaptersOptions): Promise<PagedResult<Media>>;
|
|
1048
|
+
/**
|
|
1049
|
+
* Fetch a media entry by its MyAnimeList (MAL) ID.
|
|
1050
|
+
*
|
|
1051
|
+
* @param malId - The MyAnimeList ID
|
|
1052
|
+
* @param type - Optional media type to disambiguate (some MAL IDs map to both ANIME and MANGA)
|
|
1053
|
+
*/
|
|
1054
|
+
getMediaByMalId(malId: number, type?: MediaType): Promise<Media>;
|
|
1003
1055
|
/** Get the detailed schedule for the current week, sorted by day. */
|
|
1004
1056
|
getWeeklySchedule(date?: Date): Promise<WeeklySchedule>;
|
|
1005
1057
|
/** Get upcoming (not yet released) media. */
|
|
@@ -1030,17 +1082,29 @@ declare class AniListClient {
|
|
|
1030
1082
|
* Fetch a user's favorite anime, manga, characters, staff, and studios.
|
|
1031
1083
|
*
|
|
1032
1084
|
* @param idOrName - AniList user ID (number) or username (string)
|
|
1085
|
+
* @param options - Optional pagination options (perPage per category)
|
|
1033
1086
|
* @returns The user's favorites grouped by category
|
|
1034
1087
|
*
|
|
1035
1088
|
* @example
|
|
1036
1089
|
* ```typescript
|
|
1037
1090
|
* const favs = await client.getUserFavorites("AniList");
|
|
1038
1091
|
* favs.anime.forEach(a => console.log(a.title.romaji));
|
|
1092
|
+
*
|
|
1093
|
+
* // Fetch more results per category
|
|
1094
|
+
* const moreResults = await client.getUserFavorites(1, { perPage: 50 });
|
|
1095
|
+
* ```
|
|
1096
|
+
*/
|
|
1097
|
+
getUserFavorites(idOrName: number | string, options?: UserFavoritesOptions): Promise<UserFavorites>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Fetch a studio by its AniList ID.
|
|
1100
|
+
* Pass `include` to customise the number of media returned.
|
|
1101
|
+
*
|
|
1102
|
+
* @example
|
|
1103
|
+
* ```typescript
|
|
1104
|
+
* const studio = await client.getStudio(21, { media: { perPage: 50 } });
|
|
1039
1105
|
* ```
|
|
1040
1106
|
*/
|
|
1041
|
-
|
|
1042
|
-
/** Fetch a studio by its AniList ID. */
|
|
1043
|
-
getStudio(id: number): Promise<Studio>;
|
|
1107
|
+
getStudio(id: number, include?: StudioIncludeOptions): Promise<Studio>;
|
|
1044
1108
|
/** Search for studios by name. */
|
|
1045
1109
|
searchStudios(options?: SearchStudioOptions): Promise<PagedResult<Studio>>;
|
|
1046
1110
|
/** Fetch a forum thread by its AniList ID. */
|
|
@@ -1076,6 +1140,20 @@ declare class AniListClient {
|
|
|
1076
1140
|
invalidateCache(pattern: string | RegExp): Promise<number>;
|
|
1077
1141
|
/** Clean up resources held by the client. */
|
|
1078
1142
|
destroy(): Promise<void>;
|
|
1143
|
+
/**
|
|
1144
|
+
* Return a scoped view of this client where every request uses the given `AbortSignal`.
|
|
1145
|
+
* The returned object shares the same cache, rate limiter, and hooks.
|
|
1146
|
+
*
|
|
1147
|
+
* @example
|
|
1148
|
+
* ```ts
|
|
1149
|
+
* const controller = new AbortController();
|
|
1150
|
+
* const media = await client.withSignal(controller.signal).getMedia(1);
|
|
1151
|
+
*
|
|
1152
|
+
* // Cancel all in-flight requests made through the scoped view
|
|
1153
|
+
* controller.abort();
|
|
1154
|
+
* ```
|
|
1155
|
+
*/
|
|
1156
|
+
withSignal(signal: AbortSignal): AniListClient;
|
|
1079
1157
|
}
|
|
1080
1158
|
|
|
1081
1159
|
/**
|
|
@@ -1089,26 +1167,59 @@ declare class AniListError extends Error {
|
|
|
1089
1167
|
constructor(message: string, status: number, errors?: unknown[]);
|
|
1090
1168
|
}
|
|
1091
1169
|
|
|
1170
|
+
/** Cache performance statistics. */
|
|
1171
|
+
interface CacheStats {
|
|
1172
|
+
/** Total cache hits. */
|
|
1173
|
+
hits: number;
|
|
1174
|
+
/** Total cache misses. */
|
|
1175
|
+
misses: number;
|
|
1176
|
+
/** Stale entries returned (only with stale-while-revalidate). */
|
|
1177
|
+
stales: number;
|
|
1178
|
+
/** Hit rate as a ratio 0–1 (NaN if no requests yet). */
|
|
1179
|
+
hitRate: number;
|
|
1180
|
+
}
|
|
1092
1181
|
declare class MemoryCache implements CacheAdapter {
|
|
1093
1182
|
private readonly ttl;
|
|
1094
1183
|
private readonly maxSize;
|
|
1095
1184
|
private readonly enabled;
|
|
1185
|
+
private readonly swrMs;
|
|
1096
1186
|
private readonly store;
|
|
1187
|
+
private _hits;
|
|
1188
|
+
private _misses;
|
|
1189
|
+
private _stales;
|
|
1097
1190
|
constructor(options?: CacheOptions);
|
|
1098
1191
|
/** Build a deterministic cache key from a query + variables pair. */
|
|
1099
1192
|
static key(query: string, variables: Record<string, unknown>): string;
|
|
1100
|
-
/**
|
|
1193
|
+
/**
|
|
1194
|
+
* Retrieve a cached value, or `undefined` if missing / expired.
|
|
1195
|
+
* With stale-while-revalidate enabled, returns stale data within the grace window
|
|
1196
|
+
* and flags it so the caller can refresh in the background.
|
|
1197
|
+
*/
|
|
1101
1198
|
get<T>(key: string): T | undefined;
|
|
1102
1199
|
/** Store a value in the cache. */
|
|
1103
1200
|
set<T>(key: string, data: T): void;
|
|
1104
1201
|
/** Remove a specific entry. */
|
|
1105
1202
|
delete(key: string): boolean;
|
|
1106
|
-
/** Clear the entire cache. */
|
|
1203
|
+
/** Clear the entire cache and reset statistics. */
|
|
1107
1204
|
clear(): void;
|
|
1108
1205
|
/** Number of entries currently stored. */
|
|
1109
1206
|
get size(): number | Promise<number>;
|
|
1110
1207
|
/** Return all cache keys. */
|
|
1111
1208
|
keys(): string[];
|
|
1209
|
+
/**
|
|
1210
|
+
* Get cache performance statistics.
|
|
1211
|
+
*
|
|
1212
|
+
* @example
|
|
1213
|
+
* ```ts
|
|
1214
|
+
* const cache = new MemoryCache();
|
|
1215
|
+
* // ... after some usage ...
|
|
1216
|
+
* console.log(cache.stats);
|
|
1217
|
+
* // { hits: 42, misses: 8, stales: 0, hitRate: 0.84 }
|
|
1218
|
+
* ```
|
|
1219
|
+
*/
|
|
1220
|
+
get stats(): CacheStats;
|
|
1221
|
+
/** Reset cache statistics without clearing stored data. */
|
|
1222
|
+
resetStats(): void;
|
|
1112
1223
|
/**
|
|
1113
1224
|
* Remove all entries whose key matches the given pattern.
|
|
1114
1225
|
*
|
|
@@ -1239,4 +1350,4 @@ declare class RateLimiter {
|
|
|
1239
1350
|
|
|
1240
1351
|
declare function parseAniListMarkdown(text: string): string;
|
|
1241
1352
|
|
|
1242
|
-
export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, type AniListHooks, type CacheAdapter, type CacheOptions, type Character, type CharacterImage, type CharacterIncludeOptions, type CharacterMediaEdge, type CharacterName, CharacterRole, CharacterSort, type DayOfWeek, type ExternalLink, type FavoriteCharacterNode, type FavoriteMediaNode, type FavoriteStaffNode, type FavoriteStudioNode, 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, MediaSource, type MediaStaffConnection, type MediaStaffEdge, type MediaStats, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type NextAiringEpisode, type PageInfo, type PagedResult, type RateLimitInfo, type RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, RedisCache, type RedisCacheOptions, type RedisLikeClient, type ResponseMeta, type ScoreDistribution, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type SearchStudioOptions, type SearchThreadOptions, type SearchUserOptions, type Staff, type StaffImage, type StaffIncludeOptions, type StaffMediaNode, type StaffName, StaffSort, type StatusDistribution, type StreamingEpisode, type Studio, type StudioConnection, StudioSort, type Thread, type ThreadCategory, type ThreadMediaCategory, ThreadSort, type User, type UserAvatar, type UserFavorites, UserSort, type UserStatistics, type VoiceActor, type WeeklySchedule, parseAniListMarkdown };
|
|
1353
|
+
export { type AiringSchedule, AiringSort, AniListClient, type AniListClientOptions, AniListError, type AniListHooks, type CacheAdapter, type CacheOptions, type CacheStats, type Character, type CharacterImage, type CharacterIncludeOptions, type CharacterMediaEdge, type CharacterName, CharacterRole, CharacterSort, type DayOfWeek, type ExternalLink, type FavoriteCharacterNode, type FavoriteMediaNode, type FavoriteStaffNode, type FavoriteStudioNode, type FuzzyDate, type GetAiringOptions, type GetPlanningOptions, type GetRecentChaptersOptions, type GetRecommendationsOptions, type GetSeasonOptions, type GetUserMediaListOptions, type Logger, type Media, type MediaCharacterConnection, type MediaCharacterEdge, type MediaConnection, type MediaCoverImage, type MediaEdge, MediaFormat, type MediaIncludeOptions, type MediaListEntry, MediaListSort, MediaListStatus, type MediaRecommendationNode, MediaRelationType, MediaSeason, MediaSort, MediaSource, type MediaStaffConnection, type MediaStaffEdge, type MediaStats, MediaStatus, type MediaTag, type MediaTitle, type MediaTrailer, MediaType, MemoryCache, type NextAiringEpisode, type PageInfo, type PagedResult, type RateLimitInfo, type RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, RedisCache, type RedisCacheOptions, type RedisLikeClient, type ResponseMeta, type ScoreDistribution, type SearchCharacterOptions, type SearchMediaOptions, type SearchStaffOptions, type SearchStudioOptions, type SearchThreadOptions, type SearchUserOptions, type Staff, type StaffImage, type StaffIncludeOptions, type StaffMediaNode, type StaffName, StaffSort, type StatusDistribution, type StreamingEpisode, type Studio, type StudioConnection, type StudioIncludeOptions, StudioSort, type Thread, type ThreadCategory, type ThreadMediaCategory, ThreadSort, type User, type UserAvatar, type UserFavorites, type UserFavoritesOptions, UserSort, type UserStatistics, type VoiceActor, type WeeklySchedule, parseAniListMarkdown };
|