ani-client 2.2.0 → 2.2.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 +21 -6
- package/dist/cache/redis.d.mts +1 -1
- package/dist/cache/redis.d.ts +1 -1
- package/dist/cache/redis.js +1 -1
- package/dist/cache/redis.js.map +1 -1
- package/dist/cache/redis.mjs +1 -1
- package/dist/cache/redis.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{redis-AFbnh0Xa.d.mts → redis-UeRs8nqC.d.mts} +1 -1
- package/dist/{redis-AFbnh0Xa.d.ts → redis-UeRs8nqC.d.ts} +1 -1
- package/package.json +3 -13
package/README.md
CHANGED
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
> A fully typed, zero-dependency client for the [AniList](https://anilist.co) GraphQL API.
|
|
11
11
|
> Supports Node.js, Bun, Deno, and modern browsers.
|
|
12
12
|
|
|
13
|
-
> 📌 **Note** – thanks for **1K+ downloads** this month on `npm` 🎉
|
|
14
|
-
|
|
15
13
|
## Features
|
|
16
14
|
|
|
17
15
|
- **Zero dependencies** — uses the native `fetch` API
|
|
@@ -73,9 +71,6 @@ const client = new AniListClient({
|
|
|
73
71
|
staleWhileRevalidateMs: 60_000, // serve stale for 1 min after expiry
|
|
74
72
|
},
|
|
75
73
|
});
|
|
76
|
-
|
|
77
|
-
console.log(client.cacheStats);
|
|
78
|
-
// { hits: 42, misses: 8, stales: 2, hitRate: 0.84 }
|
|
79
74
|
```
|
|
80
75
|
|
|
81
76
|
For distributed setups, swap to the built-in Redis adapter:
|
|
@@ -220,7 +215,7 @@ const client = new AniListClient({
|
|
|
220
215
|
|
|
221
216
|
| Runtime | Version |
|
|
222
217
|
|----------|--------------------|
|
|
223
|
-
| Node.js | ≥
|
|
218
|
+
| Node.js | ≥ 22.13.0 |
|
|
224
219
|
| Bun | ≥ 1.0 |
|
|
225
220
|
| Deno | ≥ 1.28 |
|
|
226
221
|
| Browsers | `fetch` + `AbortController` required |
|
|
@@ -246,6 +241,26 @@ Before opening an issue or a pull request, please read:
|
|
|
246
241
|
|
|
247
242
|
This repository also includes GitHub issue templates and a pull request template to help keep reports and contributions consistent.
|
|
248
243
|
|
|
244
|
+
## Development
|
|
245
|
+
|
|
246
|
+
Quick commands for local development and CI checks:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pnpm install
|
|
250
|
+
pnpm run build # build dist
|
|
251
|
+
pnpm run typecheck # TypeScript strict checks
|
|
252
|
+
pnpm run lint # lint the codebase
|
|
253
|
+
pnpm test # run unit + integration tests
|
|
254
|
+
pnpm run docs:dev # run documentation site locally
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Notes:
|
|
258
|
+
- `tsconfig.json` has `strict: true` enabled to enforce stricter TypeScript checks.
|
|
259
|
+
- Dependabot is configured to open weekly dependency PRs — you will review and merge them manually.
|
|
260
|
+
- CI should validate `build`, `typecheck`, `lint`, and `test` before merging PRs.
|
|
261
|
+
|
|
262
|
+
This repository also includes GitHub issue templates and a pull request template to help keep reports and contributions consistent.
|
|
263
|
+
|
|
249
264
|
## License
|
|
250
265
|
|
|
251
266
|
[MIT](LICENSE) © [gonzyui](https://github.com/gonzyui)
|
package/dist/cache/redis.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from '../redis-
|
|
1
|
+
export { f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from '../redis-UeRs8nqC.mjs';
|
package/dist/cache/redis.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from '../redis-
|
|
1
|
+
export { f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from '../redis-UeRs8nqC.js';
|
package/dist/cache/redis.js
CHANGED
|
@@ -8,7 +8,7 @@ var RedisCache = class {
|
|
|
8
8
|
constructor(options) {
|
|
9
9
|
this.client = options.client;
|
|
10
10
|
this.prefix = options.prefix ?? "ani:";
|
|
11
|
-
this.ttl = options.ttl
|
|
11
|
+
this.ttl = options.ttl !== void 0 ? Math.floor(options.ttl / 1e3) : 86400;
|
|
12
12
|
}
|
|
13
13
|
prefixedKey(key) {
|
|
14
14
|
return `${this.prefix}${key}`;
|
package/dist/cache/redis.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cache/redis.ts"],"names":[],"mappings":";;;AAsCO,IAAM,aAAN,MAAyC;AAAA,EAC7B,MAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EAEjB,YAAY,OAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,MAAA,GAAS,QAAQ,MAAA,IAAU,MAAA;AAChC,IAAA,IAAA,CAAK,GAAA,GAAM,QAAQ,GAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/cache/redis.ts"],"names":[],"mappings":"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","file":"redis.js","sourcesContent":["import type { CacheAdapter } from \"../types\";\n\n/**\n * Minimal interface representing a Redis client.\n * Compatible with both `ioredis` and `redis` (node-redis v4+).\n */\nexport interface RedisLikeClient {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, ...args: unknown[]): Promise<unknown>;\n del(...keys: (string | string[])[]): Promise<number>;\n keys(pattern: string): Promise<string[]>;\n /** Optional SCAN-based iteration — used when available to avoid blocking the server. */\n scanIterator?(options: { MATCH: string; COUNT?: number }): AsyncIterable<string>;\n}\n\nexport interface RedisCacheOptions {\n /** A Redis client instance (ioredis or node-redis). */\n client: RedisLikeClient;\n /** Key prefix to namespace ani-client entries (default: `\"ani:\"`) */\n prefix?: string;\n /** TTL in milliseconds (default: 86 400 000 = 24h) */\n ttl?: number;\n}\n\n/**\n * Redis-backed cache adapter for AniListClient.\n *\n * @example\n * ```ts\n * import Redis from \"ioredis\";\n * import { AniListClient, RedisCache } from \"ani-client\";\n *\n * const redis = new Redis();\n * const client = new AniListClient({\n * cacheAdapter: new RedisCache({ client: redis }),\n * });\n * ```\n */\nexport class RedisCache implements CacheAdapter {\n private readonly client: RedisLikeClient;\n private readonly prefix: string;\n private readonly ttl: number;\n\n constructor(options: RedisCacheOptions) {\n this.client = options.client;\n this.prefix = options.prefix ?? \"ani:\";\n this.ttl = options.ttl !== undefined ? Math.floor(options.ttl / 1000) : 86_400;\n }\n\n private prefixedKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n const raw = await this.client.get(this.prefixedKey(key));\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n await this.client.set(this.prefixedKey(key), JSON.stringify(data), \"EX\", this.ttl);\n }\n\n async delete(key: string): Promise<boolean> {\n const count = await this.client.del(this.prefixedKey(key));\n return count > 0;\n }\n\n /**\n * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.\n *\n * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.\n * Provide a client with `scanIterator` support for production use.\n * @internal\n */\n private async collectKeys(pattern: string): Promise<string[]> {\n if (this.client.scanIterator) {\n const keys: string[] = [];\n for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {\n keys.push(key);\n }\n return keys;\n }\n return this.client.keys(pattern);\n }\n\n async clear(): Promise<void> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n if (keys.length > 0) {\n await this.client.del(...keys);\n }\n }\n\n /**\n * Get the actual number of keys with this prefix in Redis.\n */\n get size(): Promise<number> {\n return this.getSize();\n }\n\n /** @internal */\n private async getSize(): Promise<number> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n return keys.length;\n }\n\n async keys(): Promise<string[]> {\n const raw = await this.collectKeys(`${this.prefix}*`);\n return raw.map((k) => k.slice(this.prefix.length));\n }\n\n /**\n * Remove all entries whose key matches the given pattern.\n *\n * - **String**: treated as a substring match (e.g. `\"Media\"` removes all keys containing `\"Media\"`).\n * - **RegExp**: tested against each key directly.\n *\n * @param pattern — A string (substring match) or RegExp.\n * @returns Number of entries removed.\n */\n async invalidate(pattern: string | RegExp): Promise<number> {\n if (typeof pattern === \"string\") {\n const keys = await this.collectKeys(`${this.prefix}*${pattern}*`);\n if (keys.length === 0) return 0;\n return this.client.del(...keys);\n }\n\n const allKeys = await this.collectKeys(`${this.prefix}*`);\n const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));\n if (matching.length === 0) return 0;\n return this.client.del(...matching);\n }\n}\n"]}
|
package/dist/cache/redis.mjs
CHANGED
|
@@ -6,7 +6,7 @@ var RedisCache = class {
|
|
|
6
6
|
constructor(options) {
|
|
7
7
|
this.client = options.client;
|
|
8
8
|
this.prefix = options.prefix ?? "ani:";
|
|
9
|
-
this.ttl = options.ttl
|
|
9
|
+
this.ttl = options.ttl !== void 0 ? Math.floor(options.ttl / 1e3) : 86400;
|
|
10
10
|
}
|
|
11
11
|
prefixedKey(key) {
|
|
12
12
|
return `${this.prefix}${key}`;
|
package/dist/cache/redis.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cache/redis.ts"],"names":[],"mappings":";AAsCO,IAAM,aAAN,MAAyC;AAAA,EAC7B,MAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EAEjB,YAAY,OAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,MAAA,GAAS,QAAQ,MAAA,IAAU,MAAA;AAChC,IAAA,IAAA,CAAK,GAAA,GAAM,QAAQ,GAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/cache/redis.ts"],"names":[],"mappings":";AAsCO,IAAM,aAAN,MAAyC;AAAA,EAC7B,MAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EAEjB,YAAY,OAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,MAAA,GAAS,QAAQ,MAAA,IAAU,MAAA;AAChC,IAAA,IAAA,CAAK,GAAA,GAAM,QAAQ,GAAA,KAAQ,MAAA,GAAY,KAAK,KAAA,CAAM,OAAA,CAAQ,GAAA,GAAM,GAAI,CAAA,GAAI,KAAA;AAAA,EAC1E;AAAA,EAEQ,YAAY,GAAA,EAAqB;AACvC,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,EAAG,GAAG,CAAA,CAAA;AAAA,EAC7B;AAAA,EAEA,MAAM,IAAO,GAAA,EAAqC;AAChD,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,IAAI,IAAA,CAAK,WAAA,CAAY,GAAG,CAAC,CAAA;AACvD,IAAA,IAAI,GAAA,KAAQ,MAAM,OAAO,MAAA;AACzB,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,IACvB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,GAAA,CAAO,GAAA,EAAa,IAAA,EAAwB;AAChD,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,WAAA,CAAY,GAAG,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,EAAG,IAAA,EAAM,KAAK,GAAG,CAAA;AAAA,EACnF;AAAA,EAEA,MAAM,OAAO,GAAA,EAA+B;AAC1C,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,MAAA,CAAO,IAAI,IAAA,CAAK,WAAA,CAAY,GAAG,CAAC,CAAA;AACzD,IAAA,OAAO,KAAA,GAAQ,CAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YAAY,OAAA,EAAoC;AAC5D,IAAA,IAAI,IAAA,CAAK,OAAO,YAAA,EAAc;AAC5B,MAAA,MAAM,OAAiB,EAAC;AACxB,MAAA,WAAA,MAAiB,GAAA,IAAO,IAAA,CAAK,MAAA,CAAO,YAAA,CAAa,EAAE,OAAO,OAAA,EAAS,KAAA,EAAO,GAAA,EAAK,CAAA,EAAG;AAChF,QAAA,IAAA,CAAK,KAAK,GAAG,CAAA;AAAA,MACf;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA;AAAA,EACjC;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,MAAM,OAAO,MAAM,IAAA,CAAK,YAAY,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,CAAG,CAAA;AACrD,IAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG;AACnB,MAAA,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,IAAI,CAAA;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,IAAA,GAAwB;AAC1B,IAAA,OAAO,KAAK,OAAA,EAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,MAAc,OAAA,GAA2B;AACvC,IAAA,MAAM,OAAO,MAAM,IAAA,CAAK,YAAY,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,CAAG,CAAA;AACrD,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,MAAM,IAAA,GAA0B;AAC9B,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,YAAY,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,CAAG,CAAA;AACpD,IAAA,OAAO,GAAA,CAAI,IAAI,CAAC,CAAA,KAAM,EAAE,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WAAW,OAAA,EAA2C;AAC1D,IAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,WAAA,CAAY,GAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,CAAG,CAAA;AAChE,MAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAC9B,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,IAAI,CAAA;AAAA,IAChC;AAEA,IAAA,MAAM,UAAU,MAAM,IAAA,CAAK,YAAY,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,CAAG,CAAA;AACxD,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAAM,CAAC,CAAC,CAAA;AAChF,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAClC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,QAAQ,CAAA;AAAA,EACpC;AACF","file":"redis.mjs","sourcesContent":["import type { CacheAdapter } from \"../types\";\n\n/**\n * Minimal interface representing a Redis client.\n * Compatible with both `ioredis` and `redis` (node-redis v4+).\n */\nexport interface RedisLikeClient {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, ...args: unknown[]): Promise<unknown>;\n del(...keys: (string | string[])[]): Promise<number>;\n keys(pattern: string): Promise<string[]>;\n /** Optional SCAN-based iteration — used when available to avoid blocking the server. */\n scanIterator?(options: { MATCH: string; COUNT?: number }): AsyncIterable<string>;\n}\n\nexport interface RedisCacheOptions {\n /** A Redis client instance (ioredis or node-redis). */\n client: RedisLikeClient;\n /** Key prefix to namespace ani-client entries (default: `\"ani:\"`) */\n prefix?: string;\n /** TTL in milliseconds (default: 86 400 000 = 24h) */\n ttl?: number;\n}\n\n/**\n * Redis-backed cache adapter for AniListClient.\n *\n * @example\n * ```ts\n * import Redis from \"ioredis\";\n * import { AniListClient, RedisCache } from \"ani-client\";\n *\n * const redis = new Redis();\n * const client = new AniListClient({\n * cacheAdapter: new RedisCache({ client: redis }),\n * });\n * ```\n */\nexport class RedisCache implements CacheAdapter {\n private readonly client: RedisLikeClient;\n private readonly prefix: string;\n private readonly ttl: number;\n\n constructor(options: RedisCacheOptions) {\n this.client = options.client;\n this.prefix = options.prefix ?? \"ani:\";\n this.ttl = options.ttl !== undefined ? Math.floor(options.ttl / 1000) : 86_400;\n }\n\n private prefixedKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n const raw = await this.client.get(this.prefixedKey(key));\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n await this.client.set(this.prefixedKey(key), JSON.stringify(data), \"EX\", this.ttl);\n }\n\n async delete(key: string): Promise<boolean> {\n const count = await this.client.del(this.prefixedKey(key));\n return count > 0;\n }\n\n /**\n * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.\n *\n * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.\n * Provide a client with `scanIterator` support for production use.\n * @internal\n */\n private async collectKeys(pattern: string): Promise<string[]> {\n if (this.client.scanIterator) {\n const keys: string[] = [];\n for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {\n keys.push(key);\n }\n return keys;\n }\n return this.client.keys(pattern);\n }\n\n async clear(): Promise<void> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n if (keys.length > 0) {\n await this.client.del(...keys);\n }\n }\n\n /**\n * Get the actual number of keys with this prefix in Redis.\n */\n get size(): Promise<number> {\n return this.getSize();\n }\n\n /** @internal */\n private async getSize(): Promise<number> {\n const keys = await this.collectKeys(`${this.prefix}*`);\n return keys.length;\n }\n\n async keys(): Promise<string[]> {\n const raw = await this.collectKeys(`${this.prefix}*`);\n return raw.map((k) => k.slice(this.prefix.length));\n }\n\n /**\n * Remove all entries whose key matches the given pattern.\n *\n * - **String**: treated as a substring match (e.g. `\"Media\"` removes all keys containing `\"Media\"`).\n * - **RegExp**: tested against each key directly.\n *\n * @param pattern — A string (substring match) or RegExp.\n * @returns Number of entries removed.\n */\n async invalidate(pattern: string | RegExp): Promise<number> {\n if (typeof pattern === \"string\") {\n const keys = await this.collectKeys(`${this.prefix}*${pattern}*`);\n if (keys.length === 0) return 0;\n return this.client.del(...keys);\n }\n\n const allKeys = await this.collectKeys(`${this.prefix}*`);\n const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));\n if (matching.length === 0) return 0;\n return this.client.del(...matching);\n }\n}\n"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FuzzyDate, P as PageInfo, E as ExternalLink, C as CacheAdapter, a as CacheOptions, b as PagedResult, A as AniListClientOptions, R as RateLimitInfo, c as ResponseMeta, d as RateLimitOptions } from './redis-
|
|
2
|
-
export { e as AniListHooks, L as Logger, f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from './redis-
|
|
1
|
+
import { F as FuzzyDate, P as PageInfo, E as ExternalLink, C as CacheAdapter, a as CacheOptions, b as PagedResult, A as AniListClientOptions, R as RateLimitInfo, c as ResponseMeta, d as RateLimitOptions } from './redis-UeRs8nqC.mjs';
|
|
2
|
+
export { e as AniListHooks, L as Logger, f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from './redis-UeRs8nqC.mjs';
|
|
3
3
|
|
|
4
4
|
declare enum MediaListStatus {
|
|
5
5
|
CURRENT = "CURRENT",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FuzzyDate, P as PageInfo, E as ExternalLink, C as CacheAdapter, a as CacheOptions, b as PagedResult, A as AniListClientOptions, R as RateLimitInfo, c as ResponseMeta, d as RateLimitOptions } from './redis-
|
|
2
|
-
export { e as AniListHooks, L as Logger, f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from './redis-
|
|
1
|
+
import { F as FuzzyDate, P as PageInfo, E as ExternalLink, C as CacheAdapter, a as CacheOptions, b as PagedResult, A as AniListClientOptions, R as RateLimitInfo, c as ResponseMeta, d as RateLimitOptions } from './redis-UeRs8nqC.js';
|
|
2
|
+
export { e as AniListHooks, L as Logger, f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from './redis-UeRs8nqC.js';
|
|
3
3
|
|
|
4
4
|
declare enum MediaListStatus {
|
|
5
5
|
CURRENT = "CURRENT",
|
package/dist/index.js
CHANGED
|
@@ -485,7 +485,7 @@ var RedisCache = class {
|
|
|
485
485
|
constructor(options) {
|
|
486
486
|
this.client = options.client;
|
|
487
487
|
this.prefix = options.prefix ?? "ani:";
|
|
488
|
-
this.ttl = options.ttl
|
|
488
|
+
this.ttl = options.ttl !== void 0 ? Math.floor(options.ttl / 1e3) : 86400;
|
|
489
489
|
}
|
|
490
490
|
prefixedKey(key) {
|
|
491
491
|
return `${this.prefix}${key}`;
|
|
@@ -2541,7 +2541,7 @@ function mapFavorites(fav) {
|
|
|
2541
2541
|
|
|
2542
2542
|
// src/client/index.ts
|
|
2543
2543
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
2544
|
-
var LIB_VERSION = "2.2.
|
|
2544
|
+
var LIB_VERSION = "2.2.1" ;
|
|
2545
2545
|
var AniListClient = class {
|
|
2546
2546
|
apiUrl;
|
|
2547
2547
|
headers;
|
|
@@ -2654,7 +2654,8 @@ var AniListClient = class {
|
|
|
2654
2654
|
throw error;
|
|
2655
2655
|
}
|
|
2656
2656
|
if (!res.ok || json.errors) {
|
|
2657
|
-
const
|
|
2657
|
+
const msgs = (json.errors || []).map((e) => e.message).filter(Boolean);
|
|
2658
|
+
const message = msgs.length > 0 ? msgs.join(" | ") : `AniList API error (HTTP ${res.status})`;
|
|
2658
2659
|
const error = new AniListError(message, res.status, json.errors ?? []);
|
|
2659
2660
|
this.logger?.error("Request failed", { error: error.message, status: error.status });
|
|
2660
2661
|
this.hooks.onError?.(error, query, variables);
|