ani-client 2.3.0 → 2.5.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 +6 -1
- package/dist/cache/redis.d.mts +1 -1
- package/dist/cache/redis.d.ts +1 -1
- package/dist/index.d.mts +75 -4
- package/dist/index.d.ts +75 -4
- package/dist/index.js +183 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +183 -23
- package/dist/index.mjs.map +1 -1
- package/dist/{redis-UeRs8nqC.d.mts → redis-ClB2nNrs.d.mts} +13 -0
- package/dist/{redis-UeRs8nqC.d.ts → redis-ClB2nNrs.d.ts} +13 -0
- package/package.json +10 -9
package/dist/index.js
CHANGED
|
@@ -1,5 +1,80 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// src/utils/dataloader.ts
|
|
10
|
+
var BatchLoader = class {
|
|
11
|
+
constructor(batchFetch, maxWaitMs = 50, maxBatchSize = 50) {
|
|
12
|
+
this.batchFetch = batchFetch;
|
|
13
|
+
this.maxWaitMs = maxWaitMs;
|
|
14
|
+
this.maxBatchSize = maxBatchSize;
|
|
15
|
+
}
|
|
16
|
+
queue = /* @__PURE__ */ new Map();
|
|
17
|
+
timeout = null;
|
|
18
|
+
/**
|
|
19
|
+
* Queue an ID to be fetched in the next batch.
|
|
20
|
+
* Returns a Promise that resolves when the batch request completes.
|
|
21
|
+
*/
|
|
22
|
+
async load(id) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
let callbacks = this.queue.get(id);
|
|
25
|
+
if (!callbacks) {
|
|
26
|
+
callbacks = [];
|
|
27
|
+
this.queue.set(id, callbacks);
|
|
28
|
+
}
|
|
29
|
+
callbacks.push({ resolve, reject });
|
|
30
|
+
if (this.queue.size >= this.maxBatchSize) {
|
|
31
|
+
if (this.timeout) {
|
|
32
|
+
clearTimeout(this.timeout);
|
|
33
|
+
this.timeout = null;
|
|
34
|
+
}
|
|
35
|
+
void this.dispatch();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (this.timeout === null) {
|
|
39
|
+
this.timeout = setTimeout(() => {
|
|
40
|
+
void this.dispatch();
|
|
41
|
+
}, this.maxWaitMs);
|
|
42
|
+
const timer = this.timeout;
|
|
43
|
+
if (typeof timer.unref === "function") {
|
|
44
|
+
timer.unref();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async dispatch() {
|
|
50
|
+
this.timeout = null;
|
|
51
|
+
if (this.queue.size === 0) return;
|
|
52
|
+
const currentQueue = this.queue;
|
|
53
|
+
this.queue = /* @__PURE__ */ new Map();
|
|
54
|
+
const ids = Array.from(currentQueue.keys());
|
|
55
|
+
try {
|
|
56
|
+
const results = await this.batchFetch(ids);
|
|
57
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const item of results) {
|
|
59
|
+
resultMap.set(item.id, item);
|
|
60
|
+
}
|
|
61
|
+
for (const [id, callbacks] of currentQueue.entries()) {
|
|
62
|
+
const result = resultMap.get(id);
|
|
63
|
+
if (result) {
|
|
64
|
+
for (const cb of callbacks) cb.resolve(result);
|
|
65
|
+
} else {
|
|
66
|
+
const err = new Error(`Item with ID ${id} not found in batch response`);
|
|
67
|
+
for (const cb of callbacks) cb.reject(err);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
for (const callbacks of currentQueue.values()) {
|
|
72
|
+
for (const cb of callbacks) cb.reject(err);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
3
78
|
// src/utils/markdown.ts
|
|
4
79
|
function isSafeUrl(url) {
|
|
5
80
|
return /^https?:\/\//i.test(url);
|
|
@@ -575,6 +650,25 @@ var AniListError = class _AniListError extends Error {
|
|
|
575
650
|
};
|
|
576
651
|
|
|
577
652
|
// src/queries/fragments.ts
|
|
653
|
+
var fragments_exports = {};
|
|
654
|
+
__export(fragments_exports, {
|
|
655
|
+
CHARACTER_FIELDS: () => CHARACTER_FIELDS,
|
|
656
|
+
CHARACTER_FIELDS_COMPACT: () => CHARACTER_FIELDS_COMPACT,
|
|
657
|
+
CHARACTER_FIELDS_WITH_VA: () => CHARACTER_FIELDS_WITH_VA,
|
|
658
|
+
MEDIA_FIELDS: () => MEDIA_FIELDS,
|
|
659
|
+
MEDIA_FIELDS_BASE: () => MEDIA_FIELDS_BASE,
|
|
660
|
+
MEDIA_FIELDS_LIGHT: () => MEDIA_FIELDS_LIGHT,
|
|
661
|
+
MEDIA_LIST_FIELDS: () => MEDIA_LIST_FIELDS,
|
|
662
|
+
MEDIA_RECOMMENDATION_FIELDS: () => MEDIA_RECOMMENDATION_FIELDS,
|
|
663
|
+
RELATIONS_FIELDS: () => RELATIONS_FIELDS,
|
|
664
|
+
STAFF_FIELDS: () => STAFF_FIELDS,
|
|
665
|
+
STAFF_MEDIA_FIELDS: () => STAFF_MEDIA_FIELDS,
|
|
666
|
+
STUDIO_FIELDS: () => STUDIO_FIELDS,
|
|
667
|
+
THREAD_FIELDS: () => THREAD_FIELDS,
|
|
668
|
+
USER_FAVORITES_FIELDS: () => USER_FAVORITES_FIELDS,
|
|
669
|
+
USER_FIELDS: () => USER_FIELDS,
|
|
670
|
+
VOICE_ACTOR_FIELDS_COMPACT: () => VOICE_ACTOR_FIELDS_COMPACT
|
|
671
|
+
});
|
|
578
672
|
var MEDIA_FIELDS_LIGHT = `
|
|
579
673
|
__typename
|
|
580
674
|
id
|
|
@@ -1265,6 +1359,9 @@ function buildMediaIncludeQuery(include) {
|
|
|
1265
1359
|
statusDistribution { status amount }
|
|
1266
1360
|
}`);
|
|
1267
1361
|
}
|
|
1362
|
+
if (include.customFields) {
|
|
1363
|
+
extra.push(include.customFields);
|
|
1364
|
+
}
|
|
1268
1365
|
return extra;
|
|
1269
1366
|
}
|
|
1270
1367
|
function buildMediaByIdQuery(include) {
|
|
@@ -1492,6 +1589,14 @@ query ($id: Int!) {
|
|
|
1492
1589
|
${CHARACTER_FIELDS_WITH_VA}
|
|
1493
1590
|
}
|
|
1494
1591
|
}`;
|
|
1592
|
+
function buildCharacterByIdQuery(include) {
|
|
1593
|
+
if (!include?.customFields) {
|
|
1594
|
+
return include?.voiceActors ? QUERY_CHARACTER_BY_ID_WITH_VA : QUERY_CHARACTER_BY_ID;
|
|
1595
|
+
}
|
|
1596
|
+
const fields = include.voiceActors ? CHARACTER_FIELDS_WITH_VA : CHARACTER_FIELDS;
|
|
1597
|
+
return `query ($id: Int!) { Character(id: $id) { ${fields}
|
|
1598
|
+
${include.customFields} } }`;
|
|
1599
|
+
}
|
|
1495
1600
|
var QUERY_CHARACTER_SEARCH = `
|
|
1496
1601
|
query ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {
|
|
1497
1602
|
Page(page: $page, perPage: $perPage) {
|
|
@@ -1610,6 +1715,15 @@ query ($id: Int!, $perPage: Int) {
|
|
|
1610
1715
|
${STAFF_MEDIA_FIELDS}
|
|
1611
1716
|
}
|
|
1612
1717
|
}`;
|
|
1718
|
+
function buildStaffByIdQuery(include) {
|
|
1719
|
+
if (!include?.customFields) {
|
|
1720
|
+
return include?.characterRoles || include?.media ? QUERY_STAFF_BY_ID_WITH_MEDIA : QUERY_STAFF_BY_ID;
|
|
1721
|
+
}
|
|
1722
|
+
const extra = include?.characterRoles || include?.media ? `
|
|
1723
|
+
${STAFF_MEDIA_FIELDS}` : "";
|
|
1724
|
+
return `query ($id: Int!, $perPage: Int) { Staff(id: $id) { ${STAFF_FIELDS}${extra}
|
|
1725
|
+
${include.customFields} } }`;
|
|
1726
|
+
}
|
|
1613
1727
|
var QUERY_STAFF_SEARCH = `
|
|
1614
1728
|
query ($search: String, $sort: [StaffSort], $page: Int, $perPage: Int) {
|
|
1615
1729
|
Page(page: $page, perPage: $perPage) {
|
|
@@ -1627,9 +1741,15 @@ query ($id: Int!) {
|
|
|
1627
1741
|
${STUDIO_FIELDS}
|
|
1628
1742
|
}
|
|
1629
1743
|
}`;
|
|
1630
|
-
function buildStudioByIdQuery(
|
|
1631
|
-
|
|
1632
|
-
|
|
1744
|
+
function buildStudioByIdQuery(include) {
|
|
1745
|
+
const custom = include?.customFields ? `
|
|
1746
|
+
${include.customFields}` : "";
|
|
1747
|
+
if (!include?.media) {
|
|
1748
|
+
if (!custom) return QUERY_STUDIO_BY_ID;
|
|
1749
|
+
return `query ($id: Int!) { Studio(id: $id) { ${STUDIO_FIELDS}${custom} } }`;
|
|
1750
|
+
}
|
|
1751
|
+
const mediaOpts = typeof include.media === "object" ? include.media : {};
|
|
1752
|
+
const pp = clampPerPage(mediaOpts.perPage ?? 25);
|
|
1633
1753
|
return `
|
|
1634
1754
|
query ($id: Int!) {
|
|
1635
1755
|
Studio(id: $id) {
|
|
@@ -1643,7 +1763,7 @@ query ($id: Int!) {
|
|
|
1643
1763
|
nodes {
|
|
1644
1764
|
${MEDIA_FIELDS_LIGHT}
|
|
1645
1765
|
}
|
|
1646
|
-
}
|
|
1766
|
+
}${custom}
|
|
1647
1767
|
}
|
|
1648
1768
|
}`;
|
|
1649
1769
|
}
|
|
@@ -1926,7 +2046,7 @@ function isNetworkError(err) {
|
|
|
1926
2046
|
// src/client/character.ts
|
|
1927
2047
|
async function getCharacter(client, id, include) {
|
|
1928
2048
|
validateId(id, "characterId");
|
|
1929
|
-
const query = include
|
|
2049
|
+
const query = buildCharacterByIdQuery(include);
|
|
1930
2050
|
const data = await client.request(query, { id });
|
|
1931
2051
|
return data.Character;
|
|
1932
2052
|
}
|
|
@@ -2403,12 +2523,13 @@ async function searchReviews(client, options = {}) {
|
|
|
2403
2523
|
// src/client/staff.ts
|
|
2404
2524
|
async function getStaff(client, id, include) {
|
|
2405
2525
|
validateId(id, "staffId");
|
|
2406
|
-
|
|
2526
|
+
const query = buildStaffByIdQuery(include);
|
|
2527
|
+
if (include?.characterRoles || include?.media) {
|
|
2407
2528
|
const perPage = typeof include.media === "object" ? include.media.perPage ?? 25 : 25;
|
|
2408
|
-
const data2 = await client.request(
|
|
2529
|
+
const data2 = await client.request(query, { id, perPage });
|
|
2409
2530
|
return data2.Staff;
|
|
2410
2531
|
}
|
|
2411
|
-
const data = await client.request(
|
|
2532
|
+
const data = await client.request(query, { id });
|
|
2412
2533
|
return data.Staff;
|
|
2413
2534
|
}
|
|
2414
2535
|
async function searchStaff(client, options = {}) {
|
|
@@ -2423,13 +2544,8 @@ async function searchStaff(client, options = {}) {
|
|
|
2423
2544
|
// src/client/studio.ts
|
|
2424
2545
|
async function getStudio(client, id, include) {
|
|
2425
2546
|
validateId(id, "studioId");
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
const query = buildStudioByIdQuery(perPage);
|
|
2429
|
-
const data2 = await client.request(query, { id });
|
|
2430
|
-
return data2.Studio;
|
|
2431
|
-
}
|
|
2432
|
-
const data = await client.request(QUERY_STUDIO_BY_ID, { id });
|
|
2547
|
+
const query = buildStudioByIdQuery(include);
|
|
2548
|
+
const data = await client.request(query, { id });
|
|
2433
2549
|
return data.Studio;
|
|
2434
2550
|
}
|
|
2435
2551
|
async function searchStudios(client, options = {}) {
|
|
@@ -2531,7 +2647,7 @@ function mapFavorites(fav) {
|
|
|
2531
2647
|
|
|
2532
2648
|
// src/client/index.ts
|
|
2533
2649
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
2534
|
-
var LIB_VERSION = "2.
|
|
2650
|
+
var LIB_VERSION = "2.5.0" ;
|
|
2535
2651
|
var AniListClient = class {
|
|
2536
2652
|
apiUrl;
|
|
2537
2653
|
headers;
|
|
@@ -2543,6 +2659,10 @@ var AniListClient = class {
|
|
|
2543
2659
|
inFlight = /* @__PURE__ */ new Map();
|
|
2544
2660
|
_rateLimitInfo;
|
|
2545
2661
|
_lastRequestMeta;
|
|
2662
|
+
mediaLoader;
|
|
2663
|
+
characterLoader;
|
|
2664
|
+
staffLoader;
|
|
2665
|
+
batchingEnabled;
|
|
2546
2666
|
constructor(options = {}) {
|
|
2547
2667
|
this.apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
2548
2668
|
this.headers = {
|
|
@@ -2558,6 +2678,12 @@ var AniListClient = class {
|
|
|
2558
2678
|
this.hooks = options.hooks ?? {};
|
|
2559
2679
|
this.logger = options.logger;
|
|
2560
2680
|
this.signal = options.signal;
|
|
2681
|
+
this.batchingEnabled = options.batching?.enabled ?? true;
|
|
2682
|
+
const windowMs = options.batching?.windowMs ?? 50;
|
|
2683
|
+
const maxBatchSize = options.batching?.maxBatchSize ?? 50;
|
|
2684
|
+
this.mediaLoader = new BatchLoader((ids) => this.getMediaBatch(ids), windowMs, maxBatchSize);
|
|
2685
|
+
this.characterLoader = new BatchLoader((ids) => this.getCharacterBatch(ids), windowMs, maxBatchSize);
|
|
2686
|
+
this.staffLoader = new BatchLoader((ids) => this.getStaffBatch(ids), windowMs, maxBatchSize);
|
|
2561
2687
|
}
|
|
2562
2688
|
/**
|
|
2563
2689
|
* The current rate limit information from the last API response.
|
|
@@ -2660,6 +2786,17 @@ var AniListClient = class {
|
|
|
2660
2786
|
remaining: Number.parseInt(rlRemaining, 10),
|
|
2661
2787
|
reset: Number.parseInt(rlReset, 10)
|
|
2662
2788
|
};
|
|
2789
|
+
const remainingPercent = this._rateLimitInfo.remaining / this._rateLimitInfo.limit;
|
|
2790
|
+
if (remainingPercent <= 0.15) {
|
|
2791
|
+
const resetIn = this._rateLimitInfo.reset - Math.floor(Date.now() / 1e3);
|
|
2792
|
+
const resetInSeconds = resetIn > 0 ? resetIn : 0;
|
|
2793
|
+
this.logger?.warn("Smart Alert: AniList API rate limit is critically low", {
|
|
2794
|
+
remaining: this._rateLimitInfo.remaining,
|
|
2795
|
+
limit: this._rateLimitInfo.limit,
|
|
2796
|
+
resetInSeconds
|
|
2797
|
+
});
|
|
2798
|
+
this.hooks.onRateLimitAlert?.(this._rateLimitInfo.remaining, resetInSeconds);
|
|
2799
|
+
}
|
|
2663
2800
|
}
|
|
2664
2801
|
const durationMs = Date.now() - start;
|
|
2665
2802
|
const data = json.data;
|
|
@@ -2688,7 +2825,10 @@ var AniListClient = class {
|
|
|
2688
2825
|
* @param include - Optional related data to include
|
|
2689
2826
|
*/
|
|
2690
2827
|
async getMedia(id, include) {
|
|
2691
|
-
|
|
2828
|
+
if (!this.batchingEnabled || include) {
|
|
2829
|
+
return getMedia(this, id, include);
|
|
2830
|
+
}
|
|
2831
|
+
return this.mediaLoader.load(id);
|
|
2692
2832
|
}
|
|
2693
2833
|
async getMediaCharacters(mediaId, options = {}) {
|
|
2694
2834
|
return getMediaCharacters(this, mediaId, options);
|
|
@@ -2756,7 +2896,10 @@ var AniListClient = class {
|
|
|
2756
2896
|
}
|
|
2757
2897
|
/** Fetch a character by AniList ID. Pass `{ voiceActors: true }` to include VA data. */
|
|
2758
2898
|
async getCharacter(id, include) {
|
|
2759
|
-
|
|
2899
|
+
if (!this.batchingEnabled || include) {
|
|
2900
|
+
return getCharacter(this, id, include);
|
|
2901
|
+
}
|
|
2902
|
+
return this.characterLoader.load(id);
|
|
2760
2903
|
}
|
|
2761
2904
|
/** Search for characters by name. */
|
|
2762
2905
|
async searchCharacters(options = {}) {
|
|
@@ -2764,7 +2907,10 @@ var AniListClient = class {
|
|
|
2764
2907
|
}
|
|
2765
2908
|
/** Fetch a staff member by AniList ID. Pass `{ media: true }` or `{ media: { perPage } }` for media credits. */
|
|
2766
2909
|
async getStaff(id, include) {
|
|
2767
|
-
|
|
2910
|
+
if (!this.batchingEnabled || include) {
|
|
2911
|
+
return getStaff(this, id, include);
|
|
2912
|
+
}
|
|
2913
|
+
return this.staffLoader.load(id);
|
|
2768
2914
|
}
|
|
2769
2915
|
/** Search for staff (voice actors, directors, etc.). */
|
|
2770
2916
|
async searchStaff(options = {}) {
|
|
@@ -2878,12 +3024,25 @@ var AniListClient = class {
|
|
|
2878
3024
|
page++;
|
|
2879
3025
|
}
|
|
2880
3026
|
}
|
|
3027
|
+
/**
|
|
3028
|
+
* Utility to fetch all pages of a paginated request and return them as a single array.
|
|
3029
|
+
*
|
|
3030
|
+
* @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`
|
|
3031
|
+
* @param maxPages - Maximum number of pages to fetch (default: Infinity)
|
|
3032
|
+
*/
|
|
3033
|
+
async fetchAll(fetchPage, maxPages = Number.POSITIVE_INFINITY) {
|
|
3034
|
+
const results = [];
|
|
3035
|
+
for await (const item of this.paginate(fetchPage, maxPages)) {
|
|
3036
|
+
results.push(item);
|
|
3037
|
+
}
|
|
3038
|
+
return results;
|
|
3039
|
+
}
|
|
2881
3040
|
/** Fetch multiple media entries in a single API request. */
|
|
2882
3041
|
async getMediaBatch(ids) {
|
|
2883
3042
|
if (ids.length === 0) return [];
|
|
2884
3043
|
validateIds(ids, "mediaId");
|
|
2885
3044
|
const [singleMediaId] = ids;
|
|
2886
|
-
if (ids.length === 1 && singleMediaId !== void 0) return [await
|
|
3045
|
+
if (ids.length === 1 && singleMediaId !== void 0) return [await getMedia(this, singleMediaId)];
|
|
2887
3046
|
return this.executeBatch(ids, buildBatchMediaQuery, "m");
|
|
2888
3047
|
}
|
|
2889
3048
|
/** Fetch multiple characters in a single API request. */
|
|
@@ -2891,7 +3050,8 @@ var AniListClient = class {
|
|
|
2891
3050
|
if (ids.length === 0) return [];
|
|
2892
3051
|
validateIds(ids, "characterId");
|
|
2893
3052
|
const [singleCharId] = ids;
|
|
2894
|
-
if (ids.length === 1 && singleCharId !== void 0)
|
|
3053
|
+
if (ids.length === 1 && singleCharId !== void 0)
|
|
3054
|
+
return [await getCharacter(this, singleCharId)];
|
|
2895
3055
|
return this.executeBatch(ids, buildBatchCharacterQuery, "c");
|
|
2896
3056
|
}
|
|
2897
3057
|
/** Fetch multiple staff members in a single API request. */
|
|
@@ -2899,7 +3059,7 @@ var AniListClient = class {
|
|
|
2899
3059
|
if (ids.length === 0) return [];
|
|
2900
3060
|
validateIds(ids, "staffId");
|
|
2901
3061
|
const [singleStaffId] = ids;
|
|
2902
|
-
if (ids.length === 1 && singleStaffId !== void 0) return [await
|
|
3062
|
+
if (ids.length === 1 && singleStaffId !== void 0) return [await getStaff(this, singleStaffId)];
|
|
2903
3063
|
return this.executeBatch(ids, buildBatchStaffQuery, "s");
|
|
2904
3064
|
}
|
|
2905
3065
|
/** @internal */
|
|
@@ -2969,6 +3129,7 @@ exports.AniListClient = AniListClient;
|
|
|
2969
3129
|
exports.AniListError = AniListError;
|
|
2970
3130
|
exports.CharacterRole = CharacterRole;
|
|
2971
3131
|
exports.CharacterSort = CharacterSort;
|
|
3132
|
+
exports.Fragments = fragments_exports;
|
|
2972
3133
|
exports.MediaFormat = MediaFormat;
|
|
2973
3134
|
exports.MediaListSort = MediaListSort;
|
|
2974
3135
|
exports.MediaListStatus = MediaListStatus;
|