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.mjs
CHANGED
|
@@ -1,3 +1,78 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/utils/dataloader.ts
|
|
8
|
+
var BatchLoader = class {
|
|
9
|
+
constructor(batchFetch, maxWaitMs = 50, maxBatchSize = 50) {
|
|
10
|
+
this.batchFetch = batchFetch;
|
|
11
|
+
this.maxWaitMs = maxWaitMs;
|
|
12
|
+
this.maxBatchSize = maxBatchSize;
|
|
13
|
+
}
|
|
14
|
+
queue = /* @__PURE__ */ new Map();
|
|
15
|
+
timeout = null;
|
|
16
|
+
/**
|
|
17
|
+
* Queue an ID to be fetched in the next batch.
|
|
18
|
+
* Returns a Promise that resolves when the batch request completes.
|
|
19
|
+
*/
|
|
20
|
+
async load(id) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
let callbacks = this.queue.get(id);
|
|
23
|
+
if (!callbacks) {
|
|
24
|
+
callbacks = [];
|
|
25
|
+
this.queue.set(id, callbacks);
|
|
26
|
+
}
|
|
27
|
+
callbacks.push({ resolve, reject });
|
|
28
|
+
if (this.queue.size >= this.maxBatchSize) {
|
|
29
|
+
if (this.timeout) {
|
|
30
|
+
clearTimeout(this.timeout);
|
|
31
|
+
this.timeout = null;
|
|
32
|
+
}
|
|
33
|
+
void this.dispatch();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (this.timeout === null) {
|
|
37
|
+
this.timeout = setTimeout(() => {
|
|
38
|
+
void this.dispatch();
|
|
39
|
+
}, this.maxWaitMs);
|
|
40
|
+
const timer = this.timeout;
|
|
41
|
+
if (typeof timer.unref === "function") {
|
|
42
|
+
timer.unref();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async dispatch() {
|
|
48
|
+
this.timeout = null;
|
|
49
|
+
if (this.queue.size === 0) return;
|
|
50
|
+
const currentQueue = this.queue;
|
|
51
|
+
this.queue = /* @__PURE__ */ new Map();
|
|
52
|
+
const ids = Array.from(currentQueue.keys());
|
|
53
|
+
try {
|
|
54
|
+
const results = await this.batchFetch(ids);
|
|
55
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const item of results) {
|
|
57
|
+
resultMap.set(item.id, item);
|
|
58
|
+
}
|
|
59
|
+
for (const [id, callbacks] of currentQueue.entries()) {
|
|
60
|
+
const result = resultMap.get(id);
|
|
61
|
+
if (result) {
|
|
62
|
+
for (const cb of callbacks) cb.resolve(result);
|
|
63
|
+
} else {
|
|
64
|
+
const err = new Error(`Item with ID ${id} not found in batch response`);
|
|
65
|
+
for (const cb of callbacks) cb.reject(err);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
for (const callbacks of currentQueue.values()) {
|
|
70
|
+
for (const cb of callbacks) cb.reject(err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
1
76
|
// src/utils/markdown.ts
|
|
2
77
|
function isSafeUrl(url) {
|
|
3
78
|
return /^https?:\/\//i.test(url);
|
|
@@ -573,6 +648,25 @@ var AniListError = class _AniListError extends Error {
|
|
|
573
648
|
};
|
|
574
649
|
|
|
575
650
|
// src/queries/fragments.ts
|
|
651
|
+
var fragments_exports = {};
|
|
652
|
+
__export(fragments_exports, {
|
|
653
|
+
CHARACTER_FIELDS: () => CHARACTER_FIELDS,
|
|
654
|
+
CHARACTER_FIELDS_COMPACT: () => CHARACTER_FIELDS_COMPACT,
|
|
655
|
+
CHARACTER_FIELDS_WITH_VA: () => CHARACTER_FIELDS_WITH_VA,
|
|
656
|
+
MEDIA_FIELDS: () => MEDIA_FIELDS,
|
|
657
|
+
MEDIA_FIELDS_BASE: () => MEDIA_FIELDS_BASE,
|
|
658
|
+
MEDIA_FIELDS_LIGHT: () => MEDIA_FIELDS_LIGHT,
|
|
659
|
+
MEDIA_LIST_FIELDS: () => MEDIA_LIST_FIELDS,
|
|
660
|
+
MEDIA_RECOMMENDATION_FIELDS: () => MEDIA_RECOMMENDATION_FIELDS,
|
|
661
|
+
RELATIONS_FIELDS: () => RELATIONS_FIELDS,
|
|
662
|
+
STAFF_FIELDS: () => STAFF_FIELDS,
|
|
663
|
+
STAFF_MEDIA_FIELDS: () => STAFF_MEDIA_FIELDS,
|
|
664
|
+
STUDIO_FIELDS: () => STUDIO_FIELDS,
|
|
665
|
+
THREAD_FIELDS: () => THREAD_FIELDS,
|
|
666
|
+
USER_FAVORITES_FIELDS: () => USER_FAVORITES_FIELDS,
|
|
667
|
+
USER_FIELDS: () => USER_FIELDS,
|
|
668
|
+
VOICE_ACTOR_FIELDS_COMPACT: () => VOICE_ACTOR_FIELDS_COMPACT
|
|
669
|
+
});
|
|
576
670
|
var MEDIA_FIELDS_LIGHT = `
|
|
577
671
|
__typename
|
|
578
672
|
id
|
|
@@ -1263,6 +1357,9 @@ function buildMediaIncludeQuery(include) {
|
|
|
1263
1357
|
statusDistribution { status amount }
|
|
1264
1358
|
}`);
|
|
1265
1359
|
}
|
|
1360
|
+
if (include.customFields) {
|
|
1361
|
+
extra.push(include.customFields);
|
|
1362
|
+
}
|
|
1266
1363
|
return extra;
|
|
1267
1364
|
}
|
|
1268
1365
|
function buildMediaByIdQuery(include) {
|
|
@@ -1490,6 +1587,14 @@ query ($id: Int!) {
|
|
|
1490
1587
|
${CHARACTER_FIELDS_WITH_VA}
|
|
1491
1588
|
}
|
|
1492
1589
|
}`;
|
|
1590
|
+
function buildCharacterByIdQuery(include) {
|
|
1591
|
+
if (!include?.customFields) {
|
|
1592
|
+
return include?.voiceActors ? QUERY_CHARACTER_BY_ID_WITH_VA : QUERY_CHARACTER_BY_ID;
|
|
1593
|
+
}
|
|
1594
|
+
const fields = include.voiceActors ? CHARACTER_FIELDS_WITH_VA : CHARACTER_FIELDS;
|
|
1595
|
+
return `query ($id: Int!) { Character(id: $id) { ${fields}
|
|
1596
|
+
${include.customFields} } }`;
|
|
1597
|
+
}
|
|
1493
1598
|
var QUERY_CHARACTER_SEARCH = `
|
|
1494
1599
|
query ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {
|
|
1495
1600
|
Page(page: $page, perPage: $perPage) {
|
|
@@ -1608,6 +1713,15 @@ query ($id: Int!, $perPage: Int) {
|
|
|
1608
1713
|
${STAFF_MEDIA_FIELDS}
|
|
1609
1714
|
}
|
|
1610
1715
|
}`;
|
|
1716
|
+
function buildStaffByIdQuery(include) {
|
|
1717
|
+
if (!include?.customFields) {
|
|
1718
|
+
return include?.characterRoles || include?.media ? QUERY_STAFF_BY_ID_WITH_MEDIA : QUERY_STAFF_BY_ID;
|
|
1719
|
+
}
|
|
1720
|
+
const extra = include?.characterRoles || include?.media ? `
|
|
1721
|
+
${STAFF_MEDIA_FIELDS}` : "";
|
|
1722
|
+
return `query ($id: Int!, $perPage: Int) { Staff(id: $id) { ${STAFF_FIELDS}${extra}
|
|
1723
|
+
${include.customFields} } }`;
|
|
1724
|
+
}
|
|
1611
1725
|
var QUERY_STAFF_SEARCH = `
|
|
1612
1726
|
query ($search: String, $sort: [StaffSort], $page: Int, $perPage: Int) {
|
|
1613
1727
|
Page(page: $page, perPage: $perPage) {
|
|
@@ -1625,9 +1739,15 @@ query ($id: Int!) {
|
|
|
1625
1739
|
${STUDIO_FIELDS}
|
|
1626
1740
|
}
|
|
1627
1741
|
}`;
|
|
1628
|
-
function buildStudioByIdQuery(
|
|
1629
|
-
|
|
1630
|
-
|
|
1742
|
+
function buildStudioByIdQuery(include) {
|
|
1743
|
+
const custom = include?.customFields ? `
|
|
1744
|
+
${include.customFields}` : "";
|
|
1745
|
+
if (!include?.media) {
|
|
1746
|
+
if (!custom) return QUERY_STUDIO_BY_ID;
|
|
1747
|
+
return `query ($id: Int!) { Studio(id: $id) { ${STUDIO_FIELDS}${custom} } }`;
|
|
1748
|
+
}
|
|
1749
|
+
const mediaOpts = typeof include.media === "object" ? include.media : {};
|
|
1750
|
+
const pp = clampPerPage(mediaOpts.perPage ?? 25);
|
|
1631
1751
|
return `
|
|
1632
1752
|
query ($id: Int!) {
|
|
1633
1753
|
Studio(id: $id) {
|
|
@@ -1641,7 +1761,7 @@ query ($id: Int!) {
|
|
|
1641
1761
|
nodes {
|
|
1642
1762
|
${MEDIA_FIELDS_LIGHT}
|
|
1643
1763
|
}
|
|
1644
|
-
}
|
|
1764
|
+
}${custom}
|
|
1645
1765
|
}
|
|
1646
1766
|
}`;
|
|
1647
1767
|
}
|
|
@@ -1924,7 +2044,7 @@ function isNetworkError(err) {
|
|
|
1924
2044
|
// src/client/character.ts
|
|
1925
2045
|
async function getCharacter(client, id, include) {
|
|
1926
2046
|
validateId(id, "characterId");
|
|
1927
|
-
const query = include
|
|
2047
|
+
const query = buildCharacterByIdQuery(include);
|
|
1928
2048
|
const data = await client.request(query, { id });
|
|
1929
2049
|
return data.Character;
|
|
1930
2050
|
}
|
|
@@ -2401,12 +2521,13 @@ async function searchReviews(client, options = {}) {
|
|
|
2401
2521
|
// src/client/staff.ts
|
|
2402
2522
|
async function getStaff(client, id, include) {
|
|
2403
2523
|
validateId(id, "staffId");
|
|
2404
|
-
|
|
2524
|
+
const query = buildStaffByIdQuery(include);
|
|
2525
|
+
if (include?.characterRoles || include?.media) {
|
|
2405
2526
|
const perPage = typeof include.media === "object" ? include.media.perPage ?? 25 : 25;
|
|
2406
|
-
const data2 = await client.request(
|
|
2527
|
+
const data2 = await client.request(query, { id, perPage });
|
|
2407
2528
|
return data2.Staff;
|
|
2408
2529
|
}
|
|
2409
|
-
const data = await client.request(
|
|
2530
|
+
const data = await client.request(query, { id });
|
|
2410
2531
|
return data.Staff;
|
|
2411
2532
|
}
|
|
2412
2533
|
async function searchStaff(client, options = {}) {
|
|
@@ -2421,13 +2542,8 @@ async function searchStaff(client, options = {}) {
|
|
|
2421
2542
|
// src/client/studio.ts
|
|
2422
2543
|
async function getStudio(client, id, include) {
|
|
2423
2544
|
validateId(id, "studioId");
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
const query = buildStudioByIdQuery(perPage);
|
|
2427
|
-
const data2 = await client.request(query, { id });
|
|
2428
|
-
return data2.Studio;
|
|
2429
|
-
}
|
|
2430
|
-
const data = await client.request(QUERY_STUDIO_BY_ID, { id });
|
|
2545
|
+
const query = buildStudioByIdQuery(include);
|
|
2546
|
+
const data = await client.request(query, { id });
|
|
2431
2547
|
return data.Studio;
|
|
2432
2548
|
}
|
|
2433
2549
|
async function searchStudios(client, options = {}) {
|
|
@@ -2529,7 +2645,7 @@ function mapFavorites(fav) {
|
|
|
2529
2645
|
|
|
2530
2646
|
// src/client/index.ts
|
|
2531
2647
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
2532
|
-
var LIB_VERSION = "2.
|
|
2648
|
+
var LIB_VERSION = "2.5.0" ;
|
|
2533
2649
|
var AniListClient = class {
|
|
2534
2650
|
apiUrl;
|
|
2535
2651
|
headers;
|
|
@@ -2541,6 +2657,10 @@ var AniListClient = class {
|
|
|
2541
2657
|
inFlight = /* @__PURE__ */ new Map();
|
|
2542
2658
|
_rateLimitInfo;
|
|
2543
2659
|
_lastRequestMeta;
|
|
2660
|
+
mediaLoader;
|
|
2661
|
+
characterLoader;
|
|
2662
|
+
staffLoader;
|
|
2663
|
+
batchingEnabled;
|
|
2544
2664
|
constructor(options = {}) {
|
|
2545
2665
|
this.apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
2546
2666
|
this.headers = {
|
|
@@ -2556,6 +2676,12 @@ var AniListClient = class {
|
|
|
2556
2676
|
this.hooks = options.hooks ?? {};
|
|
2557
2677
|
this.logger = options.logger;
|
|
2558
2678
|
this.signal = options.signal;
|
|
2679
|
+
this.batchingEnabled = options.batching?.enabled ?? true;
|
|
2680
|
+
const windowMs = options.batching?.windowMs ?? 50;
|
|
2681
|
+
const maxBatchSize = options.batching?.maxBatchSize ?? 50;
|
|
2682
|
+
this.mediaLoader = new BatchLoader((ids) => this.getMediaBatch(ids), windowMs, maxBatchSize);
|
|
2683
|
+
this.characterLoader = new BatchLoader((ids) => this.getCharacterBatch(ids), windowMs, maxBatchSize);
|
|
2684
|
+
this.staffLoader = new BatchLoader((ids) => this.getStaffBatch(ids), windowMs, maxBatchSize);
|
|
2559
2685
|
}
|
|
2560
2686
|
/**
|
|
2561
2687
|
* The current rate limit information from the last API response.
|
|
@@ -2658,6 +2784,17 @@ var AniListClient = class {
|
|
|
2658
2784
|
remaining: Number.parseInt(rlRemaining, 10),
|
|
2659
2785
|
reset: Number.parseInt(rlReset, 10)
|
|
2660
2786
|
};
|
|
2787
|
+
const remainingPercent = this._rateLimitInfo.remaining / this._rateLimitInfo.limit;
|
|
2788
|
+
if (remainingPercent <= 0.15) {
|
|
2789
|
+
const resetIn = this._rateLimitInfo.reset - Math.floor(Date.now() / 1e3);
|
|
2790
|
+
const resetInSeconds = resetIn > 0 ? resetIn : 0;
|
|
2791
|
+
this.logger?.warn("Smart Alert: AniList API rate limit is critically low", {
|
|
2792
|
+
remaining: this._rateLimitInfo.remaining,
|
|
2793
|
+
limit: this._rateLimitInfo.limit,
|
|
2794
|
+
resetInSeconds
|
|
2795
|
+
});
|
|
2796
|
+
this.hooks.onRateLimitAlert?.(this._rateLimitInfo.remaining, resetInSeconds);
|
|
2797
|
+
}
|
|
2661
2798
|
}
|
|
2662
2799
|
const durationMs = Date.now() - start;
|
|
2663
2800
|
const data = json.data;
|
|
@@ -2686,7 +2823,10 @@ var AniListClient = class {
|
|
|
2686
2823
|
* @param include - Optional related data to include
|
|
2687
2824
|
*/
|
|
2688
2825
|
async getMedia(id, include) {
|
|
2689
|
-
|
|
2826
|
+
if (!this.batchingEnabled || include) {
|
|
2827
|
+
return getMedia(this, id, include);
|
|
2828
|
+
}
|
|
2829
|
+
return this.mediaLoader.load(id);
|
|
2690
2830
|
}
|
|
2691
2831
|
async getMediaCharacters(mediaId, options = {}) {
|
|
2692
2832
|
return getMediaCharacters(this, mediaId, options);
|
|
@@ -2754,7 +2894,10 @@ var AniListClient = class {
|
|
|
2754
2894
|
}
|
|
2755
2895
|
/** Fetch a character by AniList ID. Pass `{ voiceActors: true }` to include VA data. */
|
|
2756
2896
|
async getCharacter(id, include) {
|
|
2757
|
-
|
|
2897
|
+
if (!this.batchingEnabled || include) {
|
|
2898
|
+
return getCharacter(this, id, include);
|
|
2899
|
+
}
|
|
2900
|
+
return this.characterLoader.load(id);
|
|
2758
2901
|
}
|
|
2759
2902
|
/** Search for characters by name. */
|
|
2760
2903
|
async searchCharacters(options = {}) {
|
|
@@ -2762,7 +2905,10 @@ var AniListClient = class {
|
|
|
2762
2905
|
}
|
|
2763
2906
|
/** Fetch a staff member by AniList ID. Pass `{ media: true }` or `{ media: { perPage } }` for media credits. */
|
|
2764
2907
|
async getStaff(id, include) {
|
|
2765
|
-
|
|
2908
|
+
if (!this.batchingEnabled || include) {
|
|
2909
|
+
return getStaff(this, id, include);
|
|
2910
|
+
}
|
|
2911
|
+
return this.staffLoader.load(id);
|
|
2766
2912
|
}
|
|
2767
2913
|
/** Search for staff (voice actors, directors, etc.). */
|
|
2768
2914
|
async searchStaff(options = {}) {
|
|
@@ -2876,12 +3022,25 @@ var AniListClient = class {
|
|
|
2876
3022
|
page++;
|
|
2877
3023
|
}
|
|
2878
3024
|
}
|
|
3025
|
+
/**
|
|
3026
|
+
* Utility to fetch all pages of a paginated request and return them as a single array.
|
|
3027
|
+
*
|
|
3028
|
+
* @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`
|
|
3029
|
+
* @param maxPages - Maximum number of pages to fetch (default: Infinity)
|
|
3030
|
+
*/
|
|
3031
|
+
async fetchAll(fetchPage, maxPages = Number.POSITIVE_INFINITY) {
|
|
3032
|
+
const results = [];
|
|
3033
|
+
for await (const item of this.paginate(fetchPage, maxPages)) {
|
|
3034
|
+
results.push(item);
|
|
3035
|
+
}
|
|
3036
|
+
return results;
|
|
3037
|
+
}
|
|
2879
3038
|
/** Fetch multiple media entries in a single API request. */
|
|
2880
3039
|
async getMediaBatch(ids) {
|
|
2881
3040
|
if (ids.length === 0) return [];
|
|
2882
3041
|
validateIds(ids, "mediaId");
|
|
2883
3042
|
const [singleMediaId] = ids;
|
|
2884
|
-
if (ids.length === 1 && singleMediaId !== void 0) return [await
|
|
3043
|
+
if (ids.length === 1 && singleMediaId !== void 0) return [await getMedia(this, singleMediaId)];
|
|
2885
3044
|
return this.executeBatch(ids, buildBatchMediaQuery, "m");
|
|
2886
3045
|
}
|
|
2887
3046
|
/** Fetch multiple characters in a single API request. */
|
|
@@ -2889,7 +3048,8 @@ var AniListClient = class {
|
|
|
2889
3048
|
if (ids.length === 0) return [];
|
|
2890
3049
|
validateIds(ids, "characterId");
|
|
2891
3050
|
const [singleCharId] = ids;
|
|
2892
|
-
if (ids.length === 1 && singleCharId !== void 0)
|
|
3051
|
+
if (ids.length === 1 && singleCharId !== void 0)
|
|
3052
|
+
return [await getCharacter(this, singleCharId)];
|
|
2893
3053
|
return this.executeBatch(ids, buildBatchCharacterQuery, "c");
|
|
2894
3054
|
}
|
|
2895
3055
|
/** Fetch multiple staff members in a single API request. */
|
|
@@ -2897,7 +3057,7 @@ var AniListClient = class {
|
|
|
2897
3057
|
if (ids.length === 0) return [];
|
|
2898
3058
|
validateIds(ids, "staffId");
|
|
2899
3059
|
const [singleStaffId] = ids;
|
|
2900
|
-
if (ids.length === 1 && singleStaffId !== void 0) return [await
|
|
3060
|
+
if (ids.length === 1 && singleStaffId !== void 0) return [await getStaff(this, singleStaffId)];
|
|
2901
3061
|
return this.executeBatch(ids, buildBatchStaffQuery, "s");
|
|
2902
3062
|
}
|
|
2903
3063
|
/** @internal */
|
|
@@ -2962,6 +3122,6 @@ var AniListClient = class {
|
|
|
2962
3122
|
}
|
|
2963
3123
|
};
|
|
2964
3124
|
|
|
2965
|
-
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, NormalizedCache, RateLimiter, RecommendationSort, RedisCache, ReviewSort, StaffSort, StudioSort, ThreadSort, UserSort, parseAniListMarkdown };
|
|
3125
|
+
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, fragments_exports as Fragments, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, NormalizedCache, RateLimiter, RecommendationSort, RedisCache, ReviewSort, StaffSort, StudioSort, ThreadSort, UserSort, parseAniListMarkdown };
|
|
2966
3126
|
//# sourceMappingURL=index.mjs.map
|
|
2967
3127
|
//# sourceMappingURL=index.mjs.map
|