ani-client 1.6.0 → 1.7.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 +2 -2
- package/dist/index.d.mts +37 -46
- package/dist/index.d.ts +37 -46
- package/dist/index.js +208 -101
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +208 -102
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,31 +1,89 @@
|
|
|
1
1
|
// src/utils/markdown.ts
|
|
2
|
+
function isSafeUrl(url) {
|
|
3
|
+
return /^https?:\/\//i.test(url);
|
|
4
|
+
}
|
|
2
5
|
function parseAniListMarkdown(text) {
|
|
3
6
|
if (!text) return "";
|
|
4
7
|
let html = text;
|
|
8
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
9
|
+
html = html.replace(/```([\s\S]*?)```/g, (_match, code) => {
|
|
10
|
+
return `<pre><code>${code.trim()}</code></pre>`;
|
|
11
|
+
});
|
|
12
|
+
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
5
13
|
html = html.replace(/~!(.*?)!~/gs, '<span class="anilist-spoiler">$1</span>');
|
|
6
14
|
html = html.replace(/~~~(.*?)~~~/gs, '<div class="anilist-center">$1</div>');
|
|
7
|
-
html = html.replace(/img(\d+)\((.*?)\)/gi, '<img src="$2" width="$1" alt="" class="anilist-image" />');
|
|
8
|
-
html = html.replace(/img\((.*?)\)/gi, '<img src="$1" alt="" class="anilist-image" />');
|
|
9
15
|
html = html.replace(
|
|
10
|
-
/
|
|
11
|
-
|
|
16
|
+
/img(\d+)\((.*?)\)/gi,
|
|
17
|
+
(_match, width, url) => isSafeUrl(url) ? `<img src="${url}" width="${width}" alt="" class="anilist-image" />` : ""
|
|
18
|
+
);
|
|
19
|
+
html = html.replace(
|
|
20
|
+
/img\((.*?)\)/gi,
|
|
21
|
+
(_match, url) => isSafeUrl(url) ? `<img src="${url}" alt="" class="anilist-image" />` : ""
|
|
12
22
|
);
|
|
13
|
-
html = html.replace(/
|
|
23
|
+
html = html.replace(/youtube\((.*?)\)/gi, (_match, id) => {
|
|
24
|
+
if (!/^[\w-]+$/.test(id)) return "";
|
|
25
|
+
return `<iframe src="https://www.youtube.com/embed/${id}" frameborder="0" allowfullscreen class="anilist-youtube"></iframe>`;
|
|
26
|
+
});
|
|
27
|
+
html = html.replace(
|
|
28
|
+
/webm\((.*?)\)/gi,
|
|
29
|
+
(_match, url) => isSafeUrl(url) ? `<video src="${url}" controls class="anilist-webm"></video>` : ""
|
|
30
|
+
);
|
|
31
|
+
html = html.replace(/^######\s+(.+)$/gm, "<h6>$1</h6>");
|
|
32
|
+
html = html.replace(/^#####\s+(.+)$/gm, "<h5>$1</h5>");
|
|
33
|
+
html = html.replace(/^####\s+(.+)$/gm, "<h4>$1</h4>");
|
|
34
|
+
html = html.replace(/^###\s+(.+)$/gm, "<h3>$1</h3>");
|
|
35
|
+
html = html.replace(/^##\s+(.+)$/gm, "<h2>$1</h2>");
|
|
36
|
+
html = html.replace(/^#\s+(.+)$/gm, "<h1>$1</h1>");
|
|
14
37
|
html = html.replace(/__(.*?)__/g, "<strong>$1</strong>");
|
|
15
38
|
html = html.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
|
|
16
39
|
html = html.replace(/_(.*?)_/g, "<em>$1</em>");
|
|
17
40
|
html = html.replace(/(?<!\*)\*(?!\*)(.*?)(?<!\*)\*(?!\*)/g, "<em>$1</em>");
|
|
18
41
|
html = html.replace(/~~(.*?)~~/g, "<del>$1</del>");
|
|
19
|
-
html = html.replace(
|
|
42
|
+
html = html.replace(
|
|
43
|
+
/\[(.*?)\]\((.*?)\)/g,
|
|
44
|
+
(_match, text2, url) => isSafeUrl(url) ? `<a href="${url}" target="_blank" rel="noopener noreferrer">${text2}</a>` : text2
|
|
45
|
+
);
|
|
20
46
|
html = html.replace(/\r\n/g, "\n");
|
|
47
|
+
const lines = html.split("\n");
|
|
48
|
+
const processed = [];
|
|
49
|
+
let listType = null;
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
const ulMatch = line.match(/^[\s]*[-*]\s+(.*)/);
|
|
52
|
+
const olMatch = line.match(/^[\s]*\d+\.\s+(.*)/);
|
|
53
|
+
if (ulMatch) {
|
|
54
|
+
if (listType !== "ul") {
|
|
55
|
+
if (listType) processed.push(`</${listType}>`);
|
|
56
|
+
processed.push("<ul>");
|
|
57
|
+
listType = "ul";
|
|
58
|
+
}
|
|
59
|
+
processed.push(`<li>${ulMatch[1]}</li>`);
|
|
60
|
+
} else if (olMatch) {
|
|
61
|
+
if (listType !== "ol") {
|
|
62
|
+
if (listType) processed.push(`</${listType}>`);
|
|
63
|
+
processed.push("<ol>");
|
|
64
|
+
listType = "ol";
|
|
65
|
+
}
|
|
66
|
+
processed.push(`<li>${olMatch[1]}</li>`);
|
|
67
|
+
} else {
|
|
68
|
+
if (listType) {
|
|
69
|
+
processed.push(`</${listType}>`);
|
|
70
|
+
listType = null;
|
|
71
|
+
}
|
|
72
|
+
processed.push(line);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (listType) processed.push(`</${listType}>`);
|
|
76
|
+
html = processed.join("\n");
|
|
21
77
|
const paragraphs = html.split(/\n{2,}/);
|
|
22
78
|
html = paragraphs.map((p) => {
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
79
|
+
const trimmed = p.trim();
|
|
80
|
+
if (!trimmed) return "";
|
|
81
|
+
const withBr = trimmed.replace(/\n/g, "<br />");
|
|
82
|
+
if (withBr.match(/^(<div|<iframe|<video|<img|<h[1-6]|<ul|<ol|<pre)/)) {
|
|
25
83
|
return withBr;
|
|
26
84
|
}
|
|
27
85
|
return `<p>${withBr}</p>`;
|
|
28
|
-
}).join("\n");
|
|
86
|
+
}).filter(Boolean).join("\n");
|
|
29
87
|
return html;
|
|
30
88
|
}
|
|
31
89
|
|
|
@@ -428,6 +486,48 @@ var STUDIO_FIELDS = `
|
|
|
428
486
|
}
|
|
429
487
|
}
|
|
430
488
|
`;
|
|
489
|
+
var THREAD_FIELDS = `
|
|
490
|
+
id
|
|
491
|
+
title
|
|
492
|
+
body(asHtml: false)
|
|
493
|
+
userId
|
|
494
|
+
replyUserId
|
|
495
|
+
replyCommentId
|
|
496
|
+
replyCount
|
|
497
|
+
viewCount
|
|
498
|
+
isLocked
|
|
499
|
+
isSticky
|
|
500
|
+
isSubscribed
|
|
501
|
+
repliedAt
|
|
502
|
+
createdAt
|
|
503
|
+
updatedAt
|
|
504
|
+
siteUrl
|
|
505
|
+
user {
|
|
506
|
+
id
|
|
507
|
+
name
|
|
508
|
+
avatar { large medium }
|
|
509
|
+
}
|
|
510
|
+
replyUser {
|
|
511
|
+
id
|
|
512
|
+
name
|
|
513
|
+
avatar { large medium }
|
|
514
|
+
}
|
|
515
|
+
categories {
|
|
516
|
+
id
|
|
517
|
+
name
|
|
518
|
+
}
|
|
519
|
+
mediaCategories {
|
|
520
|
+
id
|
|
521
|
+
title { romaji english native userPreferred }
|
|
522
|
+
type
|
|
523
|
+
coverImage { large medium }
|
|
524
|
+
siteUrl
|
|
525
|
+
}
|
|
526
|
+
likes {
|
|
527
|
+
id
|
|
528
|
+
name
|
|
529
|
+
}
|
|
530
|
+
`;
|
|
431
531
|
|
|
432
532
|
// src/queries/media.ts
|
|
433
533
|
var QUERY_MEDIA_BY_ID = `
|
|
@@ -676,10 +776,10 @@ query ($id: Int!) {
|
|
|
676
776
|
}
|
|
677
777
|
}`;
|
|
678
778
|
var QUERY_STUDIO_SEARCH = `
|
|
679
|
-
query ($search: String, $page: Int, $perPage: Int) {
|
|
779
|
+
query ($search: String, $sort: [StudioSort], $page: Int, $perPage: Int) {
|
|
680
780
|
Page(page: $page, perPage: $perPage) {
|
|
681
781
|
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
682
|
-
studios(search: $search) {
|
|
782
|
+
studios(search: $search, sort: $sort) {
|
|
683
783
|
${STUDIO_FIELDS}
|
|
684
784
|
}
|
|
685
785
|
}
|
|
@@ -710,7 +810,7 @@ function buildMediaByIdQuery(include) {
|
|
|
710
810
|
}
|
|
711
811
|
if (include.characters) {
|
|
712
812
|
const opts = typeof include.characters === "object" ? include.characters : {};
|
|
713
|
-
const perPage = opts.perPage ?? 25;
|
|
813
|
+
const perPage = clampPerPage(opts.perPage ?? 25);
|
|
714
814
|
const sortClause = opts.sort !== false ? ", sort: [ROLE, RELEVANCE, ID]" : "";
|
|
715
815
|
const voiceActorBlock = opts.voiceActors ? `
|
|
716
816
|
voiceActors {
|
|
@@ -728,7 +828,7 @@ function buildMediaByIdQuery(include) {
|
|
|
728
828
|
}
|
|
729
829
|
if (include.staff) {
|
|
730
830
|
const opts = typeof include.staff === "object" ? include.staff : {};
|
|
731
|
-
const perPage = opts.perPage ?? 25;
|
|
831
|
+
const perPage = clampPerPage(opts.perPage ?? 25);
|
|
732
832
|
const sortClause = opts.sort !== false ? ", sort: [RELEVANCE, ID]" : "";
|
|
733
833
|
extra.push(`
|
|
734
834
|
staff(perPage: ${perPage}${sortClause}) {
|
|
@@ -741,7 +841,9 @@ function buildMediaByIdQuery(include) {
|
|
|
741
841
|
}`);
|
|
742
842
|
}
|
|
743
843
|
if (include.recommendations) {
|
|
744
|
-
const perPage =
|
|
844
|
+
const perPage = clampPerPage(
|
|
845
|
+
typeof include.recommendations === "object" ? include.recommendations.perPage ?? 10 : 10
|
|
846
|
+
);
|
|
745
847
|
extra.push(`
|
|
746
848
|
recommendations(perPage: ${perPage}, sort: [RATING_DESC]) {
|
|
747
849
|
nodes {
|
|
@@ -800,53 +902,11 @@ function buildBatchQuery(ids, typeName, fields, prefix) {
|
|
|
800
902
|
${aliases}
|
|
801
903
|
}`;
|
|
802
904
|
}
|
|
803
|
-
var buildBatchMediaQuery = (ids) => buildBatchQuery(ids, "Media",
|
|
905
|
+
var buildBatchMediaQuery = (ids) => buildBatchQuery(ids, "Media", MEDIA_FIELDS, "m");
|
|
804
906
|
var buildBatchCharacterQuery = (ids) => buildBatchQuery(ids, "Character", CHARACTER_FIELDS, "c");
|
|
805
907
|
var buildBatchStaffQuery = (ids) => buildBatchQuery(ids, "Staff", STAFF_FIELDS, "s");
|
|
806
908
|
|
|
807
909
|
// src/queries/thread.ts
|
|
808
|
-
var THREAD_FIELDS = `
|
|
809
|
-
id
|
|
810
|
-
title
|
|
811
|
-
body(asHtml: false)
|
|
812
|
-
userId
|
|
813
|
-
replyUserId
|
|
814
|
-
replyCommentId
|
|
815
|
-
replyCount
|
|
816
|
-
viewCount
|
|
817
|
-
isLocked
|
|
818
|
-
isSticky
|
|
819
|
-
isSubscribed
|
|
820
|
-
repliedAt
|
|
821
|
-
createdAt
|
|
822
|
-
updatedAt
|
|
823
|
-
siteUrl
|
|
824
|
-
user {
|
|
825
|
-
id
|
|
826
|
-
name
|
|
827
|
-
avatar { large medium }
|
|
828
|
-
}
|
|
829
|
-
replyUser {
|
|
830
|
-
id
|
|
831
|
-
name
|
|
832
|
-
avatar { large medium }
|
|
833
|
-
}
|
|
834
|
-
categories {
|
|
835
|
-
id
|
|
836
|
-
name
|
|
837
|
-
}
|
|
838
|
-
mediaCategories {
|
|
839
|
-
id
|
|
840
|
-
title { romaji english native userPreferred }
|
|
841
|
-
type
|
|
842
|
-
coverImage { large medium }
|
|
843
|
-
siteUrl
|
|
844
|
-
}
|
|
845
|
-
likes {
|
|
846
|
-
id
|
|
847
|
-
name
|
|
848
|
-
}
|
|
849
|
-
`;
|
|
850
910
|
var QUERY_THREAD_BY_ID = `
|
|
851
911
|
query ($id: Int!) {
|
|
852
912
|
Thread(id: $id) {
|
|
@@ -868,6 +928,8 @@ var RateLimiter = class {
|
|
|
868
928
|
constructor(options = {}) {
|
|
869
929
|
this.head = 0;
|
|
870
930
|
this.count = 0;
|
|
931
|
+
/** @internal — active sleep timers for cleanup */
|
|
932
|
+
this.activeTimers = /* @__PURE__ */ new Set();
|
|
871
933
|
this.maxRequests = options.maxRequests ?? 85;
|
|
872
934
|
this.windowMs = options.windowMs ?? 6e4;
|
|
873
935
|
this.maxRetries = options.maxRetries ?? 3;
|
|
@@ -885,8 +947,7 @@ var RateLimiter = class {
|
|
|
885
947
|
if (!this.enabled) return;
|
|
886
948
|
if (this.count >= this.maxRequests) {
|
|
887
949
|
const oldest = this.timestamps[this.head];
|
|
888
|
-
const
|
|
889
|
-
const elapsed = now2 - oldest;
|
|
950
|
+
const elapsed = Date.now() - oldest;
|
|
890
951
|
if (elapsed < this.windowMs) {
|
|
891
952
|
const waitMs = this.windowMs - elapsed + 50;
|
|
892
953
|
await this.sleep(waitMs);
|
|
@@ -950,14 +1011,32 @@ var RateLimiter = class {
|
|
|
950
1011
|
if (this.timeoutMs <= 0) return fetch(url, init);
|
|
951
1012
|
const controller = new AbortController();
|
|
952
1013
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1014
|
+
const signals = [controller.signal, init.signal].filter(Boolean);
|
|
1015
|
+
const combinedSignal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
|
|
953
1016
|
try {
|
|
954
|
-
return await fetch(url, { ...init, signal:
|
|
1017
|
+
return await fetch(url, { ...init, signal: combinedSignal });
|
|
955
1018
|
} finally {
|
|
956
1019
|
clearTimeout(timer);
|
|
957
1020
|
}
|
|
958
1021
|
}
|
|
959
1022
|
sleep(ms) {
|
|
960
|
-
return new Promise((resolve) =>
|
|
1023
|
+
return new Promise((resolve) => {
|
|
1024
|
+
const timer = setTimeout(() => {
|
|
1025
|
+
this.activeTimers.delete(timer);
|
|
1026
|
+
resolve();
|
|
1027
|
+
}, ms);
|
|
1028
|
+
this.activeTimers.add(timer);
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
/** Cancel all pending sleep timers and reset internal state. */
|
|
1032
|
+
dispose() {
|
|
1033
|
+
for (const timer of this.activeTimers) {
|
|
1034
|
+
clearTimeout(timer);
|
|
1035
|
+
}
|
|
1036
|
+
this.activeTimers.clear();
|
|
1037
|
+
this.head = 0;
|
|
1038
|
+
this.count = 0;
|
|
1039
|
+
this.timestamps.fill(0);
|
|
961
1040
|
}
|
|
962
1041
|
};
|
|
963
1042
|
var RETRYABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
@@ -1161,6 +1240,18 @@ var UserSort = /* @__PURE__ */ ((UserSort2) => {
|
|
|
1161
1240
|
return UserSort2;
|
|
1162
1241
|
})(UserSort || {});
|
|
1163
1242
|
|
|
1243
|
+
// src/types/studio.ts
|
|
1244
|
+
var StudioSort = /* @__PURE__ */ ((StudioSort2) => {
|
|
1245
|
+
StudioSort2["ID"] = "ID";
|
|
1246
|
+
StudioSort2["ID_DESC"] = "ID_DESC";
|
|
1247
|
+
StudioSort2["NAME"] = "NAME";
|
|
1248
|
+
StudioSort2["NAME_DESC"] = "NAME_DESC";
|
|
1249
|
+
StudioSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
|
|
1250
|
+
StudioSort2["FAVOURITES"] = "FAVOURITES";
|
|
1251
|
+
StudioSort2["FAVOURITES_DESC"] = "FAVOURITES_DESC";
|
|
1252
|
+
return StudioSort2;
|
|
1253
|
+
})(StudioSort || {});
|
|
1254
|
+
|
|
1164
1255
|
// src/types/lists.ts
|
|
1165
1256
|
var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
|
|
1166
1257
|
MediaListStatus2["CURRENT"] = "CURRENT";
|
|
@@ -1273,7 +1364,7 @@ async function getAiredEpisodes(client, options = {}) {
|
|
|
1273
1364
|
"airingSchedules"
|
|
1274
1365
|
);
|
|
1275
1366
|
}
|
|
1276
|
-
async function
|
|
1367
|
+
async function getRecentlyUpdatedManga(client, options = {}) {
|
|
1277
1368
|
return client.pagedRequest(
|
|
1278
1369
|
QUERY_RECENT_CHAPTERS,
|
|
1279
1370
|
{
|
|
@@ -1296,6 +1387,7 @@ async function getPlanning(client, options = {}) {
|
|
|
1296
1387
|
);
|
|
1297
1388
|
}
|
|
1298
1389
|
async function getRecommendations(client, mediaId, options = {}) {
|
|
1390
|
+
validateId(mediaId, "mediaId");
|
|
1299
1391
|
const data = await client.request(QUERY_RECOMMENDATIONS, {
|
|
1300
1392
|
mediaId,
|
|
1301
1393
|
page: options.page ?? 1,
|
|
@@ -1331,14 +1423,12 @@ async function getWeeklySchedule(client, date = /* @__PURE__ */ new Date()) {
|
|
|
1331
1423
|
Saturday: [],
|
|
1332
1424
|
Sunday: []
|
|
1333
1425
|
};
|
|
1334
|
-
const
|
|
1335
|
-
const
|
|
1336
|
-
const
|
|
1337
|
-
startOfWeek.setDate(diff);
|
|
1338
|
-
startOfWeek.setHours(0, 0, 0, 0);
|
|
1426
|
+
const utcDay = date.getUTCDay();
|
|
1427
|
+
const diff = utcDay === 0 ? -6 : 1 - utcDay;
|
|
1428
|
+
const startOfWeek = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + diff, 0, 0, 0));
|
|
1339
1429
|
const endOfWeek = new Date(startOfWeek);
|
|
1340
|
-
endOfWeek.
|
|
1341
|
-
endOfWeek.
|
|
1430
|
+
endOfWeek.setUTCDate(startOfWeek.getUTCDate() + 6);
|
|
1431
|
+
endOfWeek.setUTCHours(23, 59, 59, 999);
|
|
1342
1432
|
const startTimestamp = Math.floor(startOfWeek.getTime() / 1e3);
|
|
1343
1433
|
const endTimestamp = Math.floor(endOfWeek.getTime() / 1e3);
|
|
1344
1434
|
const iterator = client.paginate(
|
|
@@ -1347,12 +1437,13 @@ async function getWeeklySchedule(client, date = /* @__PURE__ */ new Date()) {
|
|
|
1347
1437
|
airingAtLesser: endTimestamp,
|
|
1348
1438
|
page,
|
|
1349
1439
|
perPage: 50
|
|
1350
|
-
})
|
|
1440
|
+
}),
|
|
1441
|
+
20
|
|
1351
1442
|
);
|
|
1352
1443
|
const names = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
1353
1444
|
for await (const episode of iterator) {
|
|
1354
1445
|
const epDate = new Date(episode.airingAt * 1e3);
|
|
1355
|
-
const dayName = names[epDate.
|
|
1446
|
+
const dayName = names[epDate.getUTCDay()];
|
|
1356
1447
|
schedule[dayName].push(episode);
|
|
1357
1448
|
}
|
|
1358
1449
|
return schedule;
|
|
@@ -1385,12 +1476,14 @@ async function getStudio(client, id) {
|
|
|
1385
1476
|
return data.Studio;
|
|
1386
1477
|
}
|
|
1387
1478
|
async function searchStudios(client, options = {}) {
|
|
1479
|
+
const { query: search, page = 1, perPage = 20, sort } = options;
|
|
1388
1480
|
return client.pagedRequest(
|
|
1389
1481
|
QUERY_STUDIO_SEARCH,
|
|
1390
1482
|
{
|
|
1391
|
-
search
|
|
1392
|
-
|
|
1393
|
-
|
|
1483
|
+
search,
|
|
1484
|
+
sort,
|
|
1485
|
+
page,
|
|
1486
|
+
perPage: clampPerPage(perPage)
|
|
1394
1487
|
},
|
|
1395
1488
|
"studios"
|
|
1396
1489
|
);
|
|
@@ -1436,6 +1529,9 @@ async function getUserMediaList(client, options) {
|
|
|
1436
1529
|
if (!options.userId && !options.userName) {
|
|
1437
1530
|
throw new AniListError("getUserMediaList requires either userId or userName", 0, []);
|
|
1438
1531
|
}
|
|
1532
|
+
if (options.userId) {
|
|
1533
|
+
validateId(options.userId, "userId");
|
|
1534
|
+
}
|
|
1439
1535
|
return client.pagedRequest(
|
|
1440
1536
|
QUERY_USER_MEDIA_LIST,
|
|
1441
1537
|
{
|
|
@@ -1475,13 +1571,15 @@ function mapFavorites(fav) {
|
|
|
1475
1571
|
|
|
1476
1572
|
// src/client/index.ts
|
|
1477
1573
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
1574
|
+
var LIB_VERSION = "1.7.0" ;
|
|
1478
1575
|
var AniListClient = class {
|
|
1479
1576
|
constructor(options = {}) {
|
|
1480
1577
|
this.inFlight = /* @__PURE__ */ new Map();
|
|
1481
1578
|
this.apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
1482
1579
|
this.headers = {
|
|
1483
1580
|
"Content-Type": "application/json",
|
|
1484
|
-
Accept: "application/json"
|
|
1581
|
+
Accept: "application/json",
|
|
1582
|
+
"User-Agent": `ani-client/${LIB_VERSION}`
|
|
1485
1583
|
};
|
|
1486
1584
|
if (options.token) {
|
|
1487
1585
|
this.headers.Authorization = `Bearer ${options.token}`;
|
|
@@ -1505,7 +1603,6 @@ var AniListClient = class {
|
|
|
1505
1603
|
get lastRequestMeta() {
|
|
1506
1604
|
return this._lastRequestMeta;
|
|
1507
1605
|
}
|
|
1508
|
-
// ── Core infrastructure (internal) ──
|
|
1509
1606
|
/** @internal */
|
|
1510
1607
|
async request(query, variables = {}) {
|
|
1511
1608
|
const cacheKey = MemoryCache.key(query, variables);
|
|
@@ -1532,20 +1629,29 @@ var AniListClient = class {
|
|
|
1532
1629
|
const start = Date.now();
|
|
1533
1630
|
this.hooks.onRequest?.(query, variables);
|
|
1534
1631
|
const minifiedQuery = normalizeQuery(query);
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1632
|
+
let res;
|
|
1633
|
+
try {
|
|
1634
|
+
res = await this.rateLimiter.fetchWithRetry(
|
|
1635
|
+
this.apiUrl,
|
|
1636
|
+
{
|
|
1637
|
+
method: "POST",
|
|
1638
|
+
headers: this.headers,
|
|
1639
|
+
body: JSON.stringify({ query: minifiedQuery, variables }),
|
|
1640
|
+
signal: this.signal
|
|
1641
|
+
},
|
|
1642
|
+
{ onRetry: this.hooks.onRetry, onRateLimit: this.hooks.onRateLimit }
|
|
1643
|
+
);
|
|
1644
|
+
} catch (err) {
|
|
1645
|
+
const error = err instanceof AniListError ? err : new AniListError(err.message ?? "Network request failed", 0, [err]);
|
|
1646
|
+
this.hooks.onError?.(error, query, variables);
|
|
1647
|
+
throw error;
|
|
1648
|
+
}
|
|
1545
1649
|
const json = await res.json();
|
|
1546
1650
|
if (!res.ok || json.errors) {
|
|
1547
1651
|
const message = json.errors?.[0]?.message ?? `AniList API error (HTTP ${res.status})`;
|
|
1548
|
-
|
|
1652
|
+
const error = new AniListError(message, res.status, json.errors ?? []);
|
|
1653
|
+
this.hooks.onError?.(error, query, variables);
|
|
1654
|
+
throw error;
|
|
1549
1655
|
}
|
|
1550
1656
|
const rlLimit = res.headers.get("X-RateLimit-Limit");
|
|
1551
1657
|
const rlRemaining = res.headers.get("X-RateLimit-Remaining");
|
|
@@ -1574,7 +1680,6 @@ var AniListClient = class {
|
|
|
1574
1680
|
}
|
|
1575
1681
|
return { pageInfo: data.Page.pageInfo, results };
|
|
1576
1682
|
}
|
|
1577
|
-
// ── Media ──
|
|
1578
1683
|
/**
|
|
1579
1684
|
* Fetch a single media entry by its AniList ID.
|
|
1580
1685
|
*
|
|
@@ -1611,9 +1716,19 @@ var AniListClient = class {
|
|
|
1611
1716
|
async getAiredEpisodes(options = {}) {
|
|
1612
1717
|
return getAiredEpisodes(this, options);
|
|
1613
1718
|
}
|
|
1614
|
-
/**
|
|
1719
|
+
/**
|
|
1720
|
+
* Get currently releasing manga sorted by most recently updated.
|
|
1721
|
+
*
|
|
1722
|
+
* @param options - Pagination parameters
|
|
1723
|
+
*/
|
|
1724
|
+
async getRecentlyUpdatedManga(options = {}) {
|
|
1725
|
+
return getRecentlyUpdatedManga(this, options);
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* @deprecated Use `getRecentlyUpdatedManga` instead. This alias will be removed in v2.
|
|
1729
|
+
*/
|
|
1615
1730
|
async getAiredChapters(options = {}) {
|
|
1616
|
-
return
|
|
1731
|
+
return this.getRecentlyUpdatedManga(options);
|
|
1617
1732
|
}
|
|
1618
1733
|
/** Get the detailed schedule for the current week, sorted by day. */
|
|
1619
1734
|
async getWeeklySchedule(date) {
|
|
@@ -1631,7 +1746,6 @@ var AniListClient = class {
|
|
|
1631
1746
|
async getMediaBySeason(options) {
|
|
1632
1747
|
return getMediaBySeason(this, options);
|
|
1633
1748
|
}
|
|
1634
|
-
// ── Characters ──
|
|
1635
1749
|
/** Fetch a character by AniList ID. Pass `{ voiceActors: true }` to include VA data. */
|
|
1636
1750
|
async getCharacter(id, include) {
|
|
1637
1751
|
return getCharacter(this, id, include);
|
|
@@ -1640,7 +1754,6 @@ var AniListClient = class {
|
|
|
1640
1754
|
async searchCharacters(options = {}) {
|
|
1641
1755
|
return searchCharacters(this, options);
|
|
1642
1756
|
}
|
|
1643
|
-
// ── Staff ──
|
|
1644
1757
|
/** Fetch a staff member by AniList ID. Pass `{ media: true }` or `{ media: { perPage } }` for media credits. */
|
|
1645
1758
|
async getStaff(id, include) {
|
|
1646
1759
|
return getStaff(this, id, include);
|
|
@@ -1649,7 +1762,6 @@ var AniListClient = class {
|
|
|
1649
1762
|
async searchStaff(options = {}) {
|
|
1650
1763
|
return searchStaff(this, options);
|
|
1651
1764
|
}
|
|
1652
|
-
// ── Users ──
|
|
1653
1765
|
/**
|
|
1654
1766
|
* Fetch a user by AniList ID or username.
|
|
1655
1767
|
*
|
|
@@ -1681,7 +1793,6 @@ var AniListClient = class {
|
|
|
1681
1793
|
async getUserFavorites(idOrName) {
|
|
1682
1794
|
return getUserFavorites(this, idOrName);
|
|
1683
1795
|
}
|
|
1684
|
-
// ── Studios ──
|
|
1685
1796
|
/** Fetch a studio by its AniList ID. */
|
|
1686
1797
|
async getStudio(id) {
|
|
1687
1798
|
return getStudio(this, id);
|
|
@@ -1690,8 +1801,6 @@ var AniListClient = class {
|
|
|
1690
1801
|
async searchStudios(options = {}) {
|
|
1691
1802
|
return searchStudios(this, options);
|
|
1692
1803
|
}
|
|
1693
|
-
// ── Metadata ──
|
|
1694
|
-
// ── Threads ──
|
|
1695
1804
|
/** Fetch a forum thread by its AniList ID. */
|
|
1696
1805
|
async getThread(id) {
|
|
1697
1806
|
return getThread(this, id);
|
|
@@ -1710,12 +1819,10 @@ var AniListClient = class {
|
|
|
1710
1819
|
const data = await this.request(QUERY_TAGS);
|
|
1711
1820
|
return data.MediaTagCollection;
|
|
1712
1821
|
}
|
|
1713
|
-
// ── Raw query ──
|
|
1714
1822
|
/** Execute an arbitrary GraphQL query against the AniList API. */
|
|
1715
1823
|
async raw(query, variables) {
|
|
1716
1824
|
return this.request(query, variables ?? {});
|
|
1717
1825
|
}
|
|
1718
|
-
// ── Pagination ──
|
|
1719
1826
|
/**
|
|
1720
1827
|
* Auto-paginating async iterator. Yields individual items across all pages.
|
|
1721
1828
|
*
|
|
@@ -1734,7 +1841,6 @@ var AniListClient = class {
|
|
|
1734
1841
|
page++;
|
|
1735
1842
|
}
|
|
1736
1843
|
}
|
|
1737
|
-
// ── Batch queries ──
|
|
1738
1844
|
/** Fetch multiple media entries in a single API request. */
|
|
1739
1845
|
async getMediaBatch(ids) {
|
|
1740
1846
|
if (ids.length === 0) return [];
|
|
@@ -1768,13 +1874,12 @@ var AniListClient = class {
|
|
|
1768
1874
|
);
|
|
1769
1875
|
return chunkResults.flat();
|
|
1770
1876
|
}
|
|
1771
|
-
// ── Cache management ──
|
|
1772
1877
|
/** Clear the entire response cache. */
|
|
1773
1878
|
async clearCache() {
|
|
1774
1879
|
await this.cacheAdapter.clear();
|
|
1775
1880
|
}
|
|
1776
1881
|
/** Number of entries currently in the cache. */
|
|
1777
|
-
|
|
1882
|
+
async cacheSize() {
|
|
1778
1883
|
return this.cacheAdapter.size;
|
|
1779
1884
|
}
|
|
1780
1885
|
/** Remove cache entries whose key matches the given pattern. */
|
|
@@ -1797,6 +1902,7 @@ var AniListClient = class {
|
|
|
1797
1902
|
async destroy() {
|
|
1798
1903
|
await this.cacheAdapter.clear();
|
|
1799
1904
|
this.inFlight.clear();
|
|
1905
|
+
this.rateLimiter.dispose();
|
|
1800
1906
|
}
|
|
1801
1907
|
};
|
|
1802
1908
|
|
|
@@ -1883,6 +1989,6 @@ var RedisCache = class {
|
|
|
1883
1989
|
}
|
|
1884
1990
|
};
|
|
1885
1991
|
|
|
1886
|
-
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache, StaffSort, ThreadSort, UserSort, parseAniListMarkdown };
|
|
1992
|
+
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache, StaffSort, StudioSort, ThreadSort, UserSort, parseAniListMarkdown };
|
|
1887
1993
|
//# sourceMappingURL=index.mjs.map
|
|
1888
1994
|
//# sourceMappingURL=index.mjs.map
|