ani-client 1.6.1 → 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/dist/index.mjs CHANGED
@@ -1,4 +1,7 @@
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;
@@ -9,13 +12,22 @@ function parseAniListMarkdown(text) {
9
12
  html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
10
13
  html = html.replace(/~!(.*?)!~/gs, '<span class="anilist-spoiler">$1</span>');
11
14
  html = html.replace(/~~~(.*?)~~~/gs, '<div class="anilist-center">$1</div>');
12
- html = html.replace(/img(\d+)\((.*?)\)/gi, '<img src="$2" width="$1" alt="" class="anilist-image" />');
13
- html = html.replace(/img\((.*?)\)/gi, '<img src="$1" alt="" class="anilist-image" />');
14
15
  html = html.replace(
15
- /youtube\((.*?)\)/gi,
16
- '<iframe src="https://www.youtube.com/embed/$1" frameborder="0" allowfullscreen class="anilist-youtube"></iframe>'
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" />` : ""
22
+ );
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>` : ""
17
30
  );
18
- html = html.replace(/webm\((.*?)\)/gi, '<video src="$1" controls class="anilist-webm"></video>');
19
31
  html = html.replace(/^######\s+(.+)$/gm, "<h6>$1</h6>");
20
32
  html = html.replace(/^#####\s+(.+)$/gm, "<h5>$1</h5>");
21
33
  html = html.replace(/^####\s+(.+)$/gm, "<h4>$1</h4>");
@@ -27,7 +39,10 @@ function parseAniListMarkdown(text) {
27
39
  html = html.replace(/_(.*?)_/g, "<em>$1</em>");
28
40
  html = html.replace(/(?<!\*)\*(?!\*)(.*?)(?<!\*)\*(?!\*)/g, "<em>$1</em>");
29
41
  html = html.replace(/~~(.*?)~~/g, "<del>$1</del>");
30
- html = html.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
42
+ html = html.replace(
43
+ /\[(.*?)\]\((.*?)\)/g,
44
+ (_match, text2, url) => isSafeUrl(url) ? `<a href="${url}" target="_blank" rel="noopener noreferrer">${text2}</a>` : text2
45
+ );
31
46
  html = html.replace(/\r\n/g, "\n");
32
47
  const lines = html.split("\n");
33
48
  const processed = [];
@@ -471,6 +486,48 @@ var STUDIO_FIELDS = `
471
486
  }
472
487
  }
473
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
+ `;
474
531
 
475
532
  // src/queries/media.ts
476
533
  var QUERY_MEDIA_BY_ID = `
@@ -719,10 +776,10 @@ query ($id: Int!) {
719
776
  }
720
777
  }`;
721
778
  var QUERY_STUDIO_SEARCH = `
722
- query ($search: String, $page: Int, $perPage: Int) {
779
+ query ($search: String, $sort: [StudioSort], $page: Int, $perPage: Int) {
723
780
  Page(page: $page, perPage: $perPage) {
724
781
  pageInfo { total perPage currentPage lastPage hasNextPage }
725
- studios(search: $search) {
782
+ studios(search: $search, sort: $sort) {
726
783
  ${STUDIO_FIELDS}
727
784
  }
728
785
  }
@@ -753,7 +810,7 @@ function buildMediaByIdQuery(include) {
753
810
  }
754
811
  if (include.characters) {
755
812
  const opts = typeof include.characters === "object" ? include.characters : {};
756
- const perPage = opts.perPage ?? 25;
813
+ const perPage = clampPerPage(opts.perPage ?? 25);
757
814
  const sortClause = opts.sort !== false ? ", sort: [ROLE, RELEVANCE, ID]" : "";
758
815
  const voiceActorBlock = opts.voiceActors ? `
759
816
  voiceActors {
@@ -771,7 +828,7 @@ function buildMediaByIdQuery(include) {
771
828
  }
772
829
  if (include.staff) {
773
830
  const opts = typeof include.staff === "object" ? include.staff : {};
774
- const perPage = opts.perPage ?? 25;
831
+ const perPage = clampPerPage(opts.perPage ?? 25);
775
832
  const sortClause = opts.sort !== false ? ", sort: [RELEVANCE, ID]" : "";
776
833
  extra.push(`
777
834
  staff(perPage: ${perPage}${sortClause}) {
@@ -784,7 +841,9 @@ function buildMediaByIdQuery(include) {
784
841
  }`);
785
842
  }
786
843
  if (include.recommendations) {
787
- const perPage = typeof include.recommendations === "object" ? include.recommendations.perPage ?? 10 : 10;
844
+ const perPage = clampPerPage(
845
+ typeof include.recommendations === "object" ? include.recommendations.perPage ?? 10 : 10
846
+ );
788
847
  extra.push(`
789
848
  recommendations(perPage: ${perPage}, sort: [RATING_DESC]) {
790
849
  nodes {
@@ -848,48 +907,6 @@ var buildBatchCharacterQuery = (ids) => buildBatchQuery(ids, "Character", CHARAC
848
907
  var buildBatchStaffQuery = (ids) => buildBatchQuery(ids, "Staff", STAFF_FIELDS, "s");
849
908
 
850
909
  // src/queries/thread.ts
851
- var THREAD_FIELDS = `
852
- id
853
- title
854
- body(asHtml: false)
855
- userId
856
- replyUserId
857
- replyCommentId
858
- replyCount
859
- viewCount
860
- isLocked
861
- isSticky
862
- isSubscribed
863
- repliedAt
864
- createdAt
865
- updatedAt
866
- siteUrl
867
- user {
868
- id
869
- name
870
- avatar { large medium }
871
- }
872
- replyUser {
873
- id
874
- name
875
- avatar { large medium }
876
- }
877
- categories {
878
- id
879
- name
880
- }
881
- mediaCategories {
882
- id
883
- title { romaji english native userPreferred }
884
- type
885
- coverImage { large medium }
886
- siteUrl
887
- }
888
- likes {
889
- id
890
- name
891
- }
892
- `;
893
910
  var QUERY_THREAD_BY_ID = `
894
911
  query ($id: Int!) {
895
912
  Thread(id: $id) {
@@ -1223,6 +1240,18 @@ var UserSort = /* @__PURE__ */ ((UserSort2) => {
1223
1240
  return UserSort2;
1224
1241
  })(UserSort || {});
1225
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
+
1226
1255
  // src/types/lists.ts
1227
1256
  var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
1228
1257
  MediaListStatus2["CURRENT"] = "CURRENT";
@@ -1335,7 +1364,7 @@ async function getAiredEpisodes(client, options = {}) {
1335
1364
  "airingSchedules"
1336
1365
  );
1337
1366
  }
1338
- async function getAiredChapters(client, options = {}) {
1367
+ async function getRecentlyUpdatedManga(client, options = {}) {
1339
1368
  return client.pagedRequest(
1340
1369
  QUERY_RECENT_CHAPTERS,
1341
1370
  {
@@ -1358,6 +1387,7 @@ async function getPlanning(client, options = {}) {
1358
1387
  );
1359
1388
  }
1360
1389
  async function getRecommendations(client, mediaId, options = {}) {
1390
+ validateId(mediaId, "mediaId");
1361
1391
  const data = await client.request(QUERY_RECOMMENDATIONS, {
1362
1392
  mediaId,
1363
1393
  page: options.page ?? 1,
@@ -1407,7 +1437,8 @@ async function getWeeklySchedule(client, date = /* @__PURE__ */ new Date()) {
1407
1437
  airingAtLesser: endTimestamp,
1408
1438
  page,
1409
1439
  perPage: 50
1410
- })
1440
+ }),
1441
+ 20
1411
1442
  );
1412
1443
  const names = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
1413
1444
  for await (const episode of iterator) {
@@ -1445,12 +1476,14 @@ async function getStudio(client, id) {
1445
1476
  return data.Studio;
1446
1477
  }
1447
1478
  async function searchStudios(client, options = {}) {
1479
+ const { query: search, page = 1, perPage = 20, sort } = options;
1448
1480
  return client.pagedRequest(
1449
1481
  QUERY_STUDIO_SEARCH,
1450
1482
  {
1451
- search: options.query,
1452
- page: options.page ?? 1,
1453
- perPage: clampPerPage(options.perPage ?? 20)
1483
+ search,
1484
+ sort,
1485
+ page,
1486
+ perPage: clampPerPage(perPage)
1454
1487
  },
1455
1488
  "studios"
1456
1489
  );
@@ -1496,6 +1529,9 @@ async function getUserMediaList(client, options) {
1496
1529
  if (!options.userId && !options.userName) {
1497
1530
  throw new AniListError("getUserMediaList requires either userId or userName", 0, []);
1498
1531
  }
1532
+ if (options.userId) {
1533
+ validateId(options.userId, "userId");
1534
+ }
1499
1535
  return client.pagedRequest(
1500
1536
  QUERY_USER_MEDIA_LIST,
1501
1537
  {
@@ -1535,7 +1571,7 @@ function mapFavorites(fav) {
1535
1571
 
1536
1572
  // src/client/index.ts
1537
1573
  var DEFAULT_API_URL = "https://graphql.anilist.co";
1538
- var LIB_VERSION = "1.6.1" ;
1574
+ var LIB_VERSION = "1.7.0" ;
1539
1575
  var AniListClient = class {
1540
1576
  constructor(options = {}) {
1541
1577
  this.inFlight = /* @__PURE__ */ new Map();
@@ -1680,9 +1716,19 @@ var AniListClient = class {
1680
1716
  async getAiredEpisodes(options = {}) {
1681
1717
  return getAiredEpisodes(this, options);
1682
1718
  }
1683
- /** Get currently releasing manga. */
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
+ */
1684
1730
  async getAiredChapters(options = {}) {
1685
- return getAiredChapters(this, options);
1731
+ return this.getRecentlyUpdatedManga(options);
1686
1732
  }
1687
1733
  /** Get the detailed schedule for the current week, sorted by day. */
1688
1734
  async getWeeklySchedule(date) {
@@ -1943,6 +1989,6 @@ var RedisCache = class {
1943
1989
  }
1944
1990
  };
1945
1991
 
1946
- 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 };
1947
1993
  //# sourceMappingURL=index.mjs.map
1948
1994
  //# sourceMappingURL=index.mjs.map