ani-client 1.7.0 → 1.8.1
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 +103 -45
- package/dist/index.d.mts +389 -274
- package/dist/index.d.ts +389 -274
- package/dist/index.js +582 -318
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +582 -318
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -6
package/README.md
CHANGED
|
@@ -1,39 +1,38 @@
|
|
|
1
|
-
# ani-client
|
|
1
|
+
# ani-client
|
|
2
2
|
|
|
3
|
-

|
|
4
3
|
[](https://github.com/gonzyui/ani-client/actions/workflows/ci.yml)
|
|
5
|
-
[](https://www.npmjs.com/package/ani-client)
|
|
4
|
+
[](https://www.npmjs.com/package/ani-client)
|
|
5
|
+
[](https://www.npmjs.com/package/ani-client)
|
|
6
|
+
[](https://codecov.io/gh/gonzyui/ani-client)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
6
8
|
[](LICENSE)
|
|
7
9
|
|
|
8
|
-
> A
|
|
10
|
+
> A fully typed, zero-dependency client for the [AniList](https://anilist.co) GraphQL API.
|
|
9
11
|
|
|
10
|
-
✨ **Showcase**: [
|
|
12
|
+
✨ **Showcase**: [See who's using ani-client](https://ani-client.js.org/showcase)
|
|
11
13
|
|
|
12
|
-
|
|
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!
|
|
16
|
-
|
|
17
|
-
## 📖 Documentation
|
|
14
|
+
## Highlights
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
**
|
|
16
|
+
- **Zero dependencies** — uses the native `fetch` API
|
|
17
|
+
- **Universal** — Node.js ≥ 20, Bun, Deno, and modern browsers
|
|
18
|
+
- **Dual format** — ships ESM + CJS with full `.d.ts` declarations
|
|
19
|
+
- **LRU cache** with TTL, stale-while-revalidate, and hit/miss stats
|
|
20
|
+
- **Rate-limit protection** with exponential backoff, retries, and custom strategies
|
|
21
|
+
- **Request deduplication** — concurrent identical queries share a single in-flight request
|
|
22
|
+
- **Batch queries** — fetch up to 50 media/characters/staff in one API call
|
|
23
|
+
- **Auto-pagination** — async iterator that yields items across pages
|
|
24
|
+
- **AbortSignal support** — cancel globally or per-request with `withSignal()`
|
|
25
|
+
- **Injectable logger** — plug in `console`, pino, winston, or any compatible logger
|
|
26
|
+
- **Redis-ready** — swap the cache adapter with the built-in `RedisCache` for distributed setups
|
|
22
27
|
|
|
23
28
|
## Install
|
|
24
29
|
|
|
25
30
|
```bash
|
|
26
|
-
# npm
|
|
27
31
|
npm install ani-client
|
|
28
|
-
|
|
29
|
-
# pnpm
|
|
32
|
+
# or
|
|
30
33
|
pnpm add ani-client
|
|
31
|
-
|
|
32
|
-
# yarn
|
|
34
|
+
# or
|
|
33
35
|
yarn add ani-client
|
|
34
|
-
|
|
35
|
-
# bun
|
|
36
|
-
bun add ani-client
|
|
37
36
|
```
|
|
38
37
|
|
|
39
38
|
## Quick start
|
|
@@ -43,59 +42,118 @@ import { AniListClient, MediaType } from "ani-client";
|
|
|
43
42
|
|
|
44
43
|
const client = new AniListClient();
|
|
45
44
|
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
console.log(
|
|
45
|
+
// Fetch an anime by AniList ID
|
|
46
|
+
const bebop = await client.getMedia(1);
|
|
47
|
+
console.log(bebop.title.romaji); // "Cowboy Bebop"
|
|
49
48
|
|
|
50
|
-
// Search
|
|
49
|
+
// Search with filters
|
|
51
50
|
const results = await client.searchMedia({
|
|
52
51
|
query: "Naruto",
|
|
53
52
|
type: MediaType.ANIME,
|
|
54
|
-
|
|
53
|
+
genres: ["Action"],
|
|
54
|
+
perPage: 10,
|
|
55
55
|
});
|
|
56
|
-
|
|
56
|
+
|
|
57
|
+
// Cross-platform lookup by MyAnimeList ID
|
|
58
|
+
const fma = await client.getMediaByMalId(5114);
|
|
57
59
|
```
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
## Features at a glance
|
|
62
|
+
|
|
63
|
+
### Caching & stale-while-revalidate
|
|
60
64
|
|
|
61
65
|
```ts
|
|
62
|
-
const
|
|
66
|
+
const client = new AniListClient({
|
|
67
|
+
cache: {
|
|
68
|
+
ttl: 1000 * 60 * 5, // 5 min TTL
|
|
69
|
+
maxSize: 200, // LRU capacity
|
|
70
|
+
staleWhileRevalidateMs: 60_000, // serve stale for 1 min after expiry
|
|
71
|
+
},
|
|
72
|
+
});
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
// Check cache performance
|
|
75
|
+
console.log(client.cacheStats);
|
|
76
|
+
// { hits: 42, misses: 8, stales: 2, hitRate: 0.84 }
|
|
66
77
|
```
|
|
67
78
|
|
|
68
|
-
###
|
|
79
|
+
### Per-request cancellation
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const scoped = client.withSignal(controller.signal);
|
|
84
|
+
|
|
85
|
+
setTimeout(() => controller.abort(), 3_000);
|
|
86
|
+
const anime = await scoped.getMedia(1);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Structured logging
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const client = new AniListClient({ logger: console });
|
|
93
|
+
// debug: "API request" { query: "query { Media(id: 1) { ... } }" }
|
|
94
|
+
// debug: "Request complete" { durationMs: 120, status: 200 }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Rate limiting & retries
|
|
69
98
|
|
|
70
99
|
```ts
|
|
71
100
|
const client = new AniListClient({
|
|
72
101
|
rateLimit: {
|
|
102
|
+
maxRequests: 85,
|
|
103
|
+
windowMs: 60_000,
|
|
104
|
+
maxRetries: 3,
|
|
105
|
+
retryOnNetworkError: true,
|
|
73
106
|
retryStrategy: (attempt) => (attempt + 1) * 1000, // linear backoff
|
|
74
107
|
},
|
|
75
108
|
});
|
|
76
109
|
|
|
77
|
-
|
|
110
|
+
console.log(client.rateLimitInfo);
|
|
111
|
+
// { remaining: 82, limit: 85, reset: 1741104000 }
|
|
112
|
+
```
|
|
78
113
|
|
|
79
|
-
|
|
80
|
-
console.log(`${info?.remaining}/${info?.limit} requests remaining`);
|
|
114
|
+
### Batch & pagination
|
|
81
115
|
|
|
82
|
-
|
|
83
|
-
|
|
116
|
+
```ts
|
|
117
|
+
// Fetch 100 anime in 2 API calls (50 per batch)
|
|
118
|
+
const batch = await client.getMediaBatch([1, 2, 3, /* ...up to 100 IDs */]);
|
|
119
|
+
|
|
120
|
+
// Auto-paginate through all results
|
|
121
|
+
for await (const anime of client.paginate(
|
|
122
|
+
(page) => client.searchMedia({ query: "Gundam", page, perPage: 50 }),
|
|
123
|
+
5, // max 5 pages
|
|
124
|
+
)) {
|
|
125
|
+
console.log(anime.title.romaji);
|
|
126
|
+
}
|
|
84
127
|
```
|
|
85
128
|
|
|
86
|
-
###
|
|
129
|
+
### Users, characters, studios & more
|
|
87
130
|
|
|
88
131
|
```ts
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
await client.
|
|
132
|
+
const user = await client.getUser("AniList");
|
|
133
|
+
const favs = await client.getUserFavorites("AniList", { perPage: 50 });
|
|
134
|
+
const char = await client.getCharacter(1, { voiceActors: true });
|
|
135
|
+
const studio = await client.getStudio(21, { media: { perPage: 50 } });
|
|
136
|
+
const schedule = await client.getWeeklySchedule();
|
|
94
137
|
```
|
|
95
138
|
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
Full API reference, guides (caching, pagination, includes, hooks, etc.) and configuration examples:
|
|
142
|
+
|
|
143
|
+
**[ani-client.js.org](https://ani-client.js.org)**
|
|
144
|
+
|
|
145
|
+
## Requirements
|
|
146
|
+
|
|
147
|
+
| Runtime | Version |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| Node.js | ≥ 20 |
|
|
150
|
+
| Bun | ≥ 1.0 |
|
|
151
|
+
| Deno | ≥ 1.28 |
|
|
152
|
+
| Browsers | Any with `fetch` + `AbortController` |
|
|
153
|
+
|
|
96
154
|
## Contributing
|
|
97
155
|
|
|
98
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, coding standards, and
|
|
156
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, coding standards, and PR guidelines.
|
|
99
157
|
|
|
100
158
|
## License
|
|
101
159
|
|