ani-client 2.1.1 → 2.1.3
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 +7 -1
- package/dist/cache/redis.d.mts +1 -0
- package/dist/cache/redis.d.ts +1 -0
- package/dist/cache/redis.js +94 -0
- package/dist/cache/redis.js.map +1 -0
- package/dist/cache/redis.mjs +92 -0
- package/dist/cache/redis.mjs.map +1 -0
- package/dist/index.d.mts +63 -229
- package/dist/index.d.ts +63 -229
- package/dist/index.js +266 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +266 -11
- package/dist/index.mjs.map +1 -1
- package/dist/redis-AFbnh0Xa.d.mts +243 -0
- package/dist/redis-AFbnh0Xa.d.ts +243 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -236,7 +236,13 @@ Full API reference, configuration options, and guides (caching, pagination, hook
|
|
|
236
236
|
|
|
237
237
|
## Contributing
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
Contributions are welcome.
|
|
240
|
+
|
|
241
|
+
Before opening an issue or a pull request, please read:
|
|
242
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
243
|
+
- [SECURITY.md](SECURITY.md)
|
|
244
|
+
|
|
245
|
+
This repository also includes GitHub issue templates and a pull request template to help keep reports and contributions consistent.
|
|
240
246
|
|
|
241
247
|
## License
|
|
242
248
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from '../redis-AFbnh0Xa.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from '../redis-AFbnh0Xa.js';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/cache/redis.ts
|
|
4
|
+
var RedisCache = class {
|
|
5
|
+
client;
|
|
6
|
+
prefix;
|
|
7
|
+
ttl;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.client = options.client;
|
|
10
|
+
this.prefix = options.prefix ?? "ani:";
|
|
11
|
+
this.ttl = options.ttl ?? 86400;
|
|
12
|
+
}
|
|
13
|
+
prefixedKey(key) {
|
|
14
|
+
return `${this.prefix}${key}`;
|
|
15
|
+
}
|
|
16
|
+
async get(key) {
|
|
17
|
+
const raw = await this.client.get(this.prefixedKey(key));
|
|
18
|
+
if (raw === null) return void 0;
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
} catch {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async set(key, data) {
|
|
26
|
+
await this.client.set(this.prefixedKey(key), JSON.stringify(data), "EX", this.ttl);
|
|
27
|
+
}
|
|
28
|
+
async delete(key) {
|
|
29
|
+
const count = await this.client.del(this.prefixedKey(key));
|
|
30
|
+
return count > 0;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.
|
|
34
|
+
*
|
|
35
|
+
* **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.
|
|
36
|
+
* Provide a client with `scanIterator` support for production use.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
async collectKeys(pattern) {
|
|
40
|
+
if (this.client.scanIterator) {
|
|
41
|
+
const keys = [];
|
|
42
|
+
for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {
|
|
43
|
+
keys.push(key);
|
|
44
|
+
}
|
|
45
|
+
return keys;
|
|
46
|
+
}
|
|
47
|
+
return this.client.keys(pattern);
|
|
48
|
+
}
|
|
49
|
+
async clear() {
|
|
50
|
+
const keys = await this.collectKeys(`${this.prefix}*`);
|
|
51
|
+
if (keys.length > 0) {
|
|
52
|
+
await this.client.del(...keys);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the actual number of keys with this prefix in Redis.
|
|
57
|
+
*/
|
|
58
|
+
get size() {
|
|
59
|
+
return this.getSize();
|
|
60
|
+
}
|
|
61
|
+
/** @internal */
|
|
62
|
+
async getSize() {
|
|
63
|
+
const keys = await this.collectKeys(`${this.prefix}*`);
|
|
64
|
+
return keys.length;
|
|
65
|
+
}
|
|
66
|
+
async keys() {
|
|
67
|
+
const raw = await this.collectKeys(`${this.prefix}*`);
|
|
68
|
+
return raw.map((k) => k.slice(this.prefix.length));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Remove all entries whose key matches the given pattern.
|
|
72
|
+
*
|
|
73
|
+
* - **String**: treated as a substring match (e.g. `"Media"` removes all keys containing `"Media"`).
|
|
74
|
+
* - **RegExp**: tested against each key directly.
|
|
75
|
+
*
|
|
76
|
+
* @param pattern — A string (substring match) or RegExp.
|
|
77
|
+
* @returns Number of entries removed.
|
|
78
|
+
*/
|
|
79
|
+
async invalidate(pattern) {
|
|
80
|
+
if (typeof pattern === "string") {
|
|
81
|
+
const keys = await this.collectKeys(`${this.prefix}*${pattern}*`);
|
|
82
|
+
if (keys.length === 0) return 0;
|
|
83
|
+
return this.client.del(...keys);
|
|
84
|
+
}
|
|
85
|
+
const allKeys = await this.collectKeys(`${this.prefix}*`);
|
|
86
|
+
const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));
|
|
87
|
+
if (matching.length === 0) return 0;
|
|
88
|
+
return this.client.del(...matching);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
exports.RedisCache = RedisCache;
|
|
93
|
+
//# sourceMappingURL=redis.js.map
|
|
94
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 seconds (default: 86 400 = 24 h) */\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 ?? 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"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/cache/redis.ts
|
|
2
|
+
var RedisCache = class {
|
|
3
|
+
client;
|
|
4
|
+
prefix;
|
|
5
|
+
ttl;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.client = options.client;
|
|
8
|
+
this.prefix = options.prefix ?? "ani:";
|
|
9
|
+
this.ttl = options.ttl ?? 86400;
|
|
10
|
+
}
|
|
11
|
+
prefixedKey(key) {
|
|
12
|
+
return `${this.prefix}${key}`;
|
|
13
|
+
}
|
|
14
|
+
async get(key) {
|
|
15
|
+
const raw = await this.client.get(this.prefixedKey(key));
|
|
16
|
+
if (raw === null) return void 0;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(raw);
|
|
19
|
+
} catch {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async set(key, data) {
|
|
24
|
+
await this.client.set(this.prefixedKey(key), JSON.stringify(data), "EX", this.ttl);
|
|
25
|
+
}
|
|
26
|
+
async delete(key) {
|
|
27
|
+
const count = await this.client.del(this.prefixedKey(key));
|
|
28
|
+
return count > 0;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.
|
|
32
|
+
*
|
|
33
|
+
* **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.
|
|
34
|
+
* Provide a client with `scanIterator` support for production use.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
async collectKeys(pattern) {
|
|
38
|
+
if (this.client.scanIterator) {
|
|
39
|
+
const keys = [];
|
|
40
|
+
for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {
|
|
41
|
+
keys.push(key);
|
|
42
|
+
}
|
|
43
|
+
return keys;
|
|
44
|
+
}
|
|
45
|
+
return this.client.keys(pattern);
|
|
46
|
+
}
|
|
47
|
+
async clear() {
|
|
48
|
+
const keys = await this.collectKeys(`${this.prefix}*`);
|
|
49
|
+
if (keys.length > 0) {
|
|
50
|
+
await this.client.del(...keys);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the actual number of keys with this prefix in Redis.
|
|
55
|
+
*/
|
|
56
|
+
get size() {
|
|
57
|
+
return this.getSize();
|
|
58
|
+
}
|
|
59
|
+
/** @internal */
|
|
60
|
+
async getSize() {
|
|
61
|
+
const keys = await this.collectKeys(`${this.prefix}*`);
|
|
62
|
+
return keys.length;
|
|
63
|
+
}
|
|
64
|
+
async keys() {
|
|
65
|
+
const raw = await this.collectKeys(`${this.prefix}*`);
|
|
66
|
+
return raw.map((k) => k.slice(this.prefix.length));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Remove all entries whose key matches the given pattern.
|
|
70
|
+
*
|
|
71
|
+
* - **String**: treated as a substring match (e.g. `"Media"` removes all keys containing `"Media"`).
|
|
72
|
+
* - **RegExp**: tested against each key directly.
|
|
73
|
+
*
|
|
74
|
+
* @param pattern — A string (substring match) or RegExp.
|
|
75
|
+
* @returns Number of entries removed.
|
|
76
|
+
*/
|
|
77
|
+
async invalidate(pattern) {
|
|
78
|
+
if (typeof pattern === "string") {
|
|
79
|
+
const keys = await this.collectKeys(`${this.prefix}*${pattern}*`);
|
|
80
|
+
if (keys.length === 0) return 0;
|
|
81
|
+
return this.client.del(...keys);
|
|
82
|
+
}
|
|
83
|
+
const allKeys = await this.collectKeys(`${this.prefix}*`);
|
|
84
|
+
const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));
|
|
85
|
+
if (matching.length === 0) return 0;
|
|
86
|
+
return this.client.del(...matching);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export { RedisCache };
|
|
91
|
+
//# sourceMappingURL=redis.mjs.map
|
|
92
|
+
//# sourceMappingURL=redis.mjs.map
|
|
@@ -0,0 +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,IAAO,KAAA;AAAA,EAC5B;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 seconds (default: 86 400 = 24 h) */\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 ?? 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,159 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
perPage: number;
|
|
4
|
-
currentPage: number;
|
|
5
|
-
lastPage: number;
|
|
6
|
-
hasNextPage: boolean;
|
|
7
|
-
}
|
|
8
|
-
interface PagedResult<T> {
|
|
9
|
-
pageInfo: PageInfo;
|
|
10
|
-
results: T[];
|
|
11
|
-
}
|
|
12
|
-
interface FuzzyDate {
|
|
13
|
-
year: number | null;
|
|
14
|
-
month: number | null;
|
|
15
|
-
day: number | null;
|
|
16
|
-
}
|
|
17
|
-
interface ExternalLink {
|
|
18
|
-
id: number;
|
|
19
|
-
url: string | null;
|
|
20
|
-
site: string;
|
|
21
|
-
type: string | null;
|
|
22
|
-
icon: string | null;
|
|
23
|
-
color: string | null;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Interface that all cache adapters must implement.
|
|
27
|
-
* Methods may return sync values or Promises — the client awaits all calls.
|
|
28
|
-
*/
|
|
29
|
-
interface CacheAdapter {
|
|
30
|
-
/** Retrieve a cached value, or `undefined` if missing / expired. */
|
|
31
|
-
get<T>(key: string): T | undefined | Promise<T | undefined>;
|
|
32
|
-
/** Store a value in the cache. */
|
|
33
|
-
set<T>(key: string, data: T): void | Promise<void>;
|
|
34
|
-
/** Remove a specific entry. Returns `true` if the key existed. */
|
|
35
|
-
delete(key: string): boolean | Promise<boolean>;
|
|
36
|
-
/** Clear the entire cache. */
|
|
37
|
-
clear(): void | Promise<void>;
|
|
38
|
-
/** Number of entries currently stored. */
|
|
39
|
-
readonly size: number | Promise<number>;
|
|
40
|
-
/** Return all cache keys. */
|
|
41
|
-
keys(): string[] | Promise<string[]>;
|
|
42
|
-
/** Bulk-remove entries matching a pattern. Optional — the client provides a fallback. */
|
|
43
|
-
invalidate?(pattern: string | RegExp): number | Promise<number>;
|
|
44
|
-
}
|
|
45
|
-
/** Cache configuration options. */
|
|
46
|
-
interface CacheOptions {
|
|
47
|
-
/** Time-to-live in milliseconds (default: 86 400 000 = 24h) */
|
|
48
|
-
ttl?: number;
|
|
49
|
-
/** Maximum number of cached entries (default: 500, 0 = unlimited) */
|
|
50
|
-
maxSize?: number;
|
|
51
|
-
/** Set to false to disable caching entirely */
|
|
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;
|
|
59
|
-
}
|
|
60
|
-
/** Rate limiter configuration options. */
|
|
61
|
-
interface RateLimitOptions {
|
|
62
|
-
/** Max requests per window (default: 25) */
|
|
63
|
-
maxRequests?: number;
|
|
64
|
-
/** Window size in ms (default: 60 000) */
|
|
65
|
-
windowMs?: number;
|
|
66
|
-
/** Max retries on 429 (default: 3) */
|
|
67
|
-
maxRetries?: number;
|
|
68
|
-
/** Retry delay in ms when Retry-After header is absent (default: 2000) */
|
|
69
|
-
retryDelayMs?: number;
|
|
70
|
-
/** Set to false to disable rate limiting entirely */
|
|
71
|
-
enabled?: boolean;
|
|
72
|
-
/** Timeout per request in ms (default: 30 000). 0 = no timeout. */
|
|
73
|
-
timeoutMs?: number;
|
|
74
|
-
/** Retry on network errors like ECONNRESET / ETIMEDOUT (default: true) */
|
|
75
|
-
retryOnNetworkError?: boolean;
|
|
76
|
-
/**
|
|
77
|
-
* Custom retry delay strategy. Receives the attempt number (0-based) and the base delay,
|
|
78
|
-
* and should return the delay in ms before retrying.
|
|
79
|
-
* When omitted, the default exponential backoff with jitter is used.
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* // Linear backoff: 1s, 2s, 3s, ...
|
|
83
|
-
* retryStrategy: (attempt) => (attempt + 1) * 1000
|
|
84
|
-
*/
|
|
85
|
-
retryStrategy?: (attempt: number, baseDelayMs: number) => number;
|
|
86
|
-
}
|
|
87
|
-
/** Event hooks for logging, debugging, and monitoring. */
|
|
88
|
-
interface AniListHooks {
|
|
89
|
-
/** Called before every API request. */
|
|
90
|
-
onRequest?: (query: string, variables: Record<string, unknown>) => void;
|
|
91
|
-
/** Called when a response is served from cache. */
|
|
92
|
-
onCacheHit?: (key: string) => void;
|
|
93
|
-
/** Called when the rate limiter enforces a wait (429 received). */
|
|
94
|
-
onRateLimit?: (retryAfterMs: number) => void;
|
|
95
|
-
/** Called when a request is retried (429 or network error). */
|
|
96
|
-
onRetry?: (attempt: number, reason: string, delayMs: number) => void;
|
|
97
|
-
/** Called when a request completes. */
|
|
98
|
-
onResponse?: (query: string, durationMs: number, fromCache: boolean, rateLimitInfo?: RateLimitInfo) => void;
|
|
99
|
-
/** Called when a request fails with an error. */
|
|
100
|
-
onError?: (error: Error, query: string, variables: Record<string, unknown>) => void;
|
|
101
|
-
}
|
|
102
|
-
/** Rate limit information parsed from AniList API response headers. */
|
|
103
|
-
interface RateLimitInfo {
|
|
104
|
-
/** Maximum number of requests allowed per window. */
|
|
105
|
-
limit: number;
|
|
106
|
-
/** Remaining requests in the current window. */
|
|
107
|
-
remaining: number;
|
|
108
|
-
/** UNIX timestamp (seconds) when the rate limit window resets. */
|
|
109
|
-
reset: number;
|
|
110
|
-
}
|
|
111
|
-
/** Metadata about the last request, useful for debugging and monitoring. */
|
|
112
|
-
interface ResponseMeta {
|
|
113
|
-
/** Duration of the request in milliseconds. */
|
|
114
|
-
durationMs: number;
|
|
115
|
-
/** Whether the response was served from cache. */
|
|
116
|
-
fromCache: boolean;
|
|
117
|
-
/** Rate limit information from the API response headers (not present for cached responses). */
|
|
118
|
-
rateLimitInfo?: RateLimitInfo;
|
|
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
|
-
}
|
|
130
|
-
interface AniListClientOptions {
|
|
131
|
-
/** Optional AniList OAuth token for authenticated requests */
|
|
132
|
-
token?: string;
|
|
133
|
-
/** Custom API endpoint (defaults to https://graphql.anilist.co) */
|
|
134
|
-
apiUrl?: string;
|
|
135
|
-
/** Cache configuration (enabled by default, 24h TTL) */
|
|
136
|
-
cache?: CacheOptions;
|
|
137
|
-
/** Custom cache adapter (e.g. RedisCache). Takes precedence over `cache`. */
|
|
138
|
-
cacheAdapter?: CacheAdapter;
|
|
139
|
-
/** Rate limiter configuration (enabled by default, 85 req/min) */
|
|
140
|
-
rateLimit?: RateLimitOptions;
|
|
141
|
-
/** Event hooks for logging, debugging, and monitoring */
|
|
142
|
-
hooks?: AniListHooks;
|
|
143
|
-
/** Optional AbortSignal to cancel all requests made by this client */
|
|
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;
|
|
156
|
-
}
|
|
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-AFbnh0Xa.mjs';
|
|
2
|
+
export { e as AniListHooks, L as Logger, f as RedisCache, g as RedisCacheOptions, h as RedisLikeClient } from './redis-AFbnh0Xa.mjs';
|
|
157
3
|
|
|
158
4
|
declare enum MediaListStatus {
|
|
159
5
|
CURRENT = "CURRENT",
|
|
@@ -1070,6 +916,52 @@ interface SearchThreadOptions {
|
|
|
1070
916
|
perPage?: number;
|
|
1071
917
|
}
|
|
1072
918
|
|
|
919
|
+
/**
|
|
920
|
+
* Normalized Cache Adapter for AniListClient.
|
|
921
|
+
*
|
|
922
|
+
* This cache intercepts GraphQL responses, extracts objects with `__typename` and `id`,
|
|
923
|
+
* and stores them flat in an entity store.
|
|
924
|
+
* This ensures data consistency across all queries (e.g., `getMedia(1)` and `searchMedia`
|
|
925
|
+
* share the exact same `Media` object).
|
|
926
|
+
*/
|
|
927
|
+
declare class NormalizedCache implements CacheAdapter {
|
|
928
|
+
private readonly ttl;
|
|
929
|
+
private readonly maxSize;
|
|
930
|
+
private readonly enabled;
|
|
931
|
+
private readonly swrMs;
|
|
932
|
+
private readonly queryStore;
|
|
933
|
+
private readonly entityStore;
|
|
934
|
+
private _hits;
|
|
935
|
+
private _misses;
|
|
936
|
+
private _stales;
|
|
937
|
+
constructor(options?: CacheOptions);
|
|
938
|
+
static key(query: string, variables: Record<string, unknown>): string;
|
|
939
|
+
/** Normalizes a GraphQL response, extracting entities and returning a tree of references. */
|
|
940
|
+
private normalize;
|
|
941
|
+
/** Reconstructs a GraphQL response from references. */
|
|
942
|
+
private denormalize;
|
|
943
|
+
getWithMeta<T>(key: string): {
|
|
944
|
+
data: T;
|
|
945
|
+
stale: boolean;
|
|
946
|
+
} | undefined;
|
|
947
|
+
get<T>(key: string): T | undefined;
|
|
948
|
+
set<T>(key: string, data: T): void;
|
|
949
|
+
delete(key: string): boolean;
|
|
950
|
+
clear(): void;
|
|
951
|
+
get size(): number;
|
|
952
|
+
keys(): string[];
|
|
953
|
+
invalidate(pattern: string | RegExp): number;
|
|
954
|
+
get stats(): CacheStats & {
|
|
955
|
+
entitiesCount: number;
|
|
956
|
+
};
|
|
957
|
+
resetStats(): void;
|
|
958
|
+
/**
|
|
959
|
+
* Garbage-collect orphaned entities that are no longer referenced by any query.
|
|
960
|
+
* Called automatically on LRU eviction to prevent unbounded entity store growth.
|
|
961
|
+
*/
|
|
962
|
+
gc(): number;
|
|
963
|
+
}
|
|
964
|
+
|
|
1073
965
|
/** Cache performance statistics. */
|
|
1074
966
|
interface CacheStats {
|
|
1075
967
|
/** Total cache hits. */
|
|
@@ -1094,10 +986,14 @@ declare class MemoryCache implements CacheAdapter {
|
|
|
1094
986
|
/** Build a deterministic cache key from a query + variables pair. */
|
|
1095
987
|
static key(query: string, variables: Record<string, unknown>): string;
|
|
1096
988
|
/**
|
|
1097
|
-
* Retrieve a cached value
|
|
989
|
+
* Retrieve a cached value and its stale status.
|
|
1098
990
|
* With stale-while-revalidate enabled, returns stale data within the grace window
|
|
1099
991
|
* and flags it so the caller can refresh in the background.
|
|
1100
992
|
*/
|
|
993
|
+
getWithMeta<T>(key: string): {
|
|
994
|
+
data: T;
|
|
995
|
+
stale: boolean;
|
|
996
|
+
} | undefined;
|
|
1101
997
|
get<T>(key: string): T | undefined;
|
|
1102
998
|
/** Store a value in the cache. */
|
|
1103
999
|
set<T>(key: string, data: T): void;
|
|
@@ -1136,77 +1032,15 @@ declare class MemoryCache implements CacheAdapter {
|
|
|
1136
1032
|
}
|
|
1137
1033
|
|
|
1138
1034
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
*
|
|
1141
|
-
*/
|
|
1142
|
-
interface RedisLikeClient {
|
|
1143
|
-
get(key: string): Promise<string | null>;
|
|
1144
|
-
set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
|
|
1145
|
-
del(...keys: (string | string[])[]): Promise<number>;
|
|
1146
|
-
keys(pattern: string): Promise<string[]>;
|
|
1147
|
-
/** Optional SCAN-based iteration — used when available to avoid blocking the server. */
|
|
1148
|
-
scanIterator?(options: {
|
|
1149
|
-
MATCH: string;
|
|
1150
|
-
COUNT?: number;
|
|
1151
|
-
}): AsyncIterable<string>;
|
|
1152
|
-
}
|
|
1153
|
-
interface RedisCacheOptions {
|
|
1154
|
-
/** A Redis client instance (ioredis or node-redis). */
|
|
1155
|
-
client: RedisLikeClient;
|
|
1156
|
-
/** Key prefix to namespace ani-client entries (default: `"ani:"`) */
|
|
1157
|
-
prefix?: string;
|
|
1158
|
-
/** TTL in seconds (default: 86 400 = 24 h) */
|
|
1159
|
-
ttl?: number;
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Redis-backed cache adapter for AniListClient.
|
|
1035
|
+
* Base interface for the client context shared by all domain method modules.
|
|
1036
|
+
* Exposes only the internal request methods that domain functions need.
|
|
1163
1037
|
*
|
|
1164
|
-
* @
|
|
1165
|
-
* ```ts
|
|
1166
|
-
* import Redis from "ioredis";
|
|
1167
|
-
* import { AniListClient, RedisCache } from "ani-client";
|
|
1168
|
-
*
|
|
1169
|
-
* const redis = new Redis();
|
|
1170
|
-
* const client = new AniListClient({
|
|
1171
|
-
* cacheAdapter: new RedisCache({ client: redis }),
|
|
1172
|
-
* });
|
|
1173
|
-
* ```
|
|
1038
|
+
* @internal
|
|
1174
1039
|
*/
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
constructor(options: RedisCacheOptions);
|
|
1180
|
-
private prefixedKey;
|
|
1181
|
-
get<T>(key: string): Promise<T | undefined>;
|
|
1182
|
-
set<T>(key: string, data: T): Promise<void>;
|
|
1183
|
-
delete(key: string): Promise<boolean>;
|
|
1184
|
-
/**
|
|
1185
|
-
* Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.
|
|
1186
|
-
*
|
|
1187
|
-
* **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.
|
|
1188
|
-
* Provide a client with `scanIterator` support for production use.
|
|
1189
|
-
* @internal
|
|
1190
|
-
*/
|
|
1191
|
-
private collectKeys;
|
|
1192
|
-
clear(): Promise<void>;
|
|
1193
|
-
/**
|
|
1194
|
-
* Get the actual number of keys with this prefix in Redis.
|
|
1195
|
-
*/
|
|
1196
|
-
get size(): Promise<number>;
|
|
1197
|
-
/** @internal */
|
|
1198
|
-
private getSize;
|
|
1199
|
-
keys(): Promise<string[]>;
|
|
1200
|
-
/**
|
|
1201
|
-
* Remove all entries whose key matches the given pattern.
|
|
1202
|
-
*
|
|
1203
|
-
* - **String**: treated as a substring match (e.g. `"Media"` removes all keys containing `"Media"`).
|
|
1204
|
-
* - **RegExp**: tested against each key directly.
|
|
1205
|
-
*
|
|
1206
|
-
* @param pattern — A string (substring match) or RegExp.
|
|
1207
|
-
* @returns Number of entries removed.
|
|
1208
|
-
*/
|
|
1209
|
-
invalidate(pattern: string | RegExp): Promise<number>;
|
|
1040
|
+
interface ClientBase {
|
|
1041
|
+
request<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
1042
|
+
pagedRequest<T>(query: string, variables: Record<string, unknown>, field: string): Promise<PagedResult<T>>;
|
|
1043
|
+
paginate<T>(fetchPage: (page: number) => Promise<PagedResult<T>>, maxPages?: number): AsyncGenerator<T, void, undefined>;
|
|
1210
1044
|
}
|
|
1211
1045
|
|
|
1212
1046
|
/**
|
|
@@ -1227,7 +1061,7 @@ declare class RedisCache implements CacheAdapter {
|
|
|
1227
1061
|
* });
|
|
1228
1062
|
* ```
|
|
1229
1063
|
*/
|
|
1230
|
-
declare class AniListClient {
|
|
1064
|
+
declare class AniListClient implements ClientBase {
|
|
1231
1065
|
private readonly apiUrl;
|
|
1232
1066
|
private readonly headers;
|
|
1233
1067
|
private readonly cacheAdapter;
|
|
@@ -1299,7 +1133,7 @@ declare class AniListClient {
|
|
|
1299
1133
|
*/
|
|
1300
1134
|
getMediaByMalId(malId: number, type?: MediaType): Promise<Media>;
|
|
1301
1135
|
/** Get the detailed schedule for the current week, sorted by day. */
|
|
1302
|
-
getWeeklySchedule(date?: Date): Promise<WeeklySchedule>;
|
|
1136
|
+
getWeeklySchedule(date?: Date, idNotIn?: number[]): Promise<WeeklySchedule>;
|
|
1303
1137
|
/** Get upcoming (not yet released) media. */
|
|
1304
1138
|
getPlanning(options?: GetPlanningOptions): Promise<PagedResult<Media>>;
|
|
1305
1139
|
/** Get recommendations for a specific media. */
|
|
@@ -1473,4 +1307,4 @@ declare class RateLimiter {
|
|
|
1473
1307
|
|
|
1474
1308
|
declare function parseAniListMarkdown(text: string): string;
|
|
1475
1309
|
|
|
1476
|
-
export { type AiringSchedule, AiringSort, AniListClient,
|
|
1310
|
+
export { type AiringSchedule, AiringSort, AniListClient, AniListClientOptions, AniListError, CacheAdapter, CacheOptions, type CacheStats, type Character, type CharacterImage, type CharacterIncludeOptions, type CharacterMediaEdge, type CharacterName, CharacterRole, CharacterSort, type DayOfWeek, ExternalLink, type FavoriteCharacterNode, type FavoriteMediaNode, type FavoriteStaffNode, type FavoriteStudioNode, FuzzyDate, type GetAiringOptions, type GetMediaCharactersOptions, type GetMediaStaffOptions, 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, NormalizedCache, PageInfo, PagedResult, RateLimitInfo, RateLimitOptions, RateLimiter, type Recommendation, RecommendationSort, ResponseMeta, type Review, ReviewSort, type ScoreDistribution, type SearchCharacterOptions, type SearchMediaOptions, type SearchReviewOptions, 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 };
|