@irfanshadikrishad/anilist 1.2.4 → 1.2.5
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 +23 -20
- package/bin/helpers/auth.js +30 -14
- package/bin/helpers/lists.d.ts +4 -1
- package/bin/helpers/lists.js +110 -9
- package/bin/helpers/queries.d.ts +4 -2
- package/bin/helpers/queries.js +16 -2
- package/bin/helpers/types.d.ts +123 -4
- package/bin/helpers/workers.d.ts +5 -4
- package/bin/helpers/workers.js +33 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -44,24 +44,24 @@ anilist login -i <client-id> -s <client-secret>
|
|
|
44
44
|
|
|
45
45
|
#### CLI Commands Overview
|
|
46
46
|
|
|
47
|
-
| **Command** | **Options** | **Description**
|
|
48
|
-
| ------------------------------------------- | ----------------------------------------------------------------------- |
|
|
49
|
-
| **`login`** | `-i, --id` `-s, --secret` | Log in with your AniList credentials
|
|
50
|
-
| **`logout`** | _None_ | Log out from your AniList account
|
|
51
|
-
| **`whoami`** | _None_ | Display information about the logged-in user
|
|
52
|
-
| **`-V, --version`** | _None_ | Display the current version of the CLI
|
|
53
|
-
| **`-h, --help`** | _None_ | Display available commands and options
|
|
54
|
-
| **`trending`** <br> _(alias: `tr`)_ | `-c (default: 10)` | Fetch trending anime (default count is 10)
|
|
55
|
-
| **`popular`** <br> _(alias: `plr`)_ | `-c (default: 10)` | Fetch popular anime (default count is 10)
|
|
56
|
-
| **`user`** | `<username>` | Get information about a specific AniList user
|
|
57
|
-
| **`lists`** <br> _(alias: `ls`)_ | `-a, --anime` <br> `-m, --manga` | Fetch anime or manga lists of the logged-in user
|
|
58
|
-
| **`delete`** <br> _(alias: `del`)_ | `-a, --anime` <br> `-m, --manga` <br> `-s, --activity` | Delete collections of anime, manga or activities
|
|
59
|
-
| **`upcoming`** <br> _(alias:`up`)_ | `-c (default: 10)` | Fetch upcoming anime (default count is 10)
|
|
60
|
-
| **`anime`** | `<anime-id>` | Get anime details by Anime Id
|
|
61
|
-
| **`search`** <br> _(alias:`srch`/`find`)_ | `<query>` <br> `-a, --anime` <br> `-m, --manga` <br> `-c (default: 10)` | Get anime/manga search results
|
|
62
|
-
| **`status`** <br> _(alias: `write`/`post`)_ | `<status>` | Write a status... (text/markdown/html)
|
|
63
|
-
| **`export`** <br> _(alias: `exp`)_ | `-a, --anime` <br> `-m, --manga` | Export anime or manga list in JSON, CSV or XML (MyAnimeList)
|
|
64
|
-
| **`import`** <br> _(alias: `imp`)_ | `-a, --anime` <br> `-m, --manga` | Import anime or manga list from exported JSON
|
|
47
|
+
| **Command** | **Options** | **Description** |
|
|
48
|
+
| ------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
49
|
+
| **`login`** | `-i, --id` `-s, --secret` | Log in with your AniList credentials |
|
|
50
|
+
| **`logout`** | _None_ | Log out from your AniList account |
|
|
51
|
+
| **`whoami`** | _None_ | Display information about the logged-in user |
|
|
52
|
+
| **`-V, --version`** | _None_ | Display the current version of the CLI |
|
|
53
|
+
| **`-h, --help`** | _None_ | Display available commands and options |
|
|
54
|
+
| **`trending`** <br> _(alias: `tr`)_ | `-c (default: 10)` | Fetch trending anime (default count is 10) |
|
|
55
|
+
| **`popular`** <br> _(alias: `plr`)_ | `-c (default: 10)` | Fetch popular anime (default count is 10) |
|
|
56
|
+
| **`user`** | `<username>` | Get information about a specific AniList user |
|
|
57
|
+
| **`lists`** <br> _(alias: `ls`)_ | `-a, --anime` <br> `-m, --manga` | Fetch anime or manga lists of the logged-in user |
|
|
58
|
+
| **`delete`** <br> _(alias: `del`)_ | `-a, --anime` <br> `-m, --manga` <br> `-s, --activity` | Delete collections of anime, manga or activities |
|
|
59
|
+
| **`upcoming`** <br> _(alias:`up`)_ | `-c (default: 10)` | Fetch upcoming anime (default count is 10) |
|
|
60
|
+
| **`anime`** | `<anime-id>` | Get anime details by Anime Id |
|
|
61
|
+
| **`search`** <br> _(alias:`srch`/`find`)_ | `<query>` <br> `-a, --anime` <br> `-m, --manga` <br> `-c (default: 10)` | Get anime/manga search results |
|
|
62
|
+
| **`status`** <br> _(alias: `write`/`post`)_ | `<status>` | Write a status... (text/markdown/html) |
|
|
63
|
+
| **`export`** <br> _(alias: `exp`)_ | `-a, --anime` <br> `-m, --manga` | Export anime or manga list in JSON, CSV or XML (MyAnimeList/AniDB) |
|
|
64
|
+
| **`import`** <br> _(alias: `imp`)_ | `-a, --anime` <br> `-m, --manga` | Import anime or manga list from exported JSON, MyAnimeList (XML) or AniDB (json-large) |
|
|
65
65
|
|
|
66
66
|
#### Command Breakdown:
|
|
67
67
|
|
|
@@ -213,7 +213,7 @@ anilist export -a
|
|
|
213
213
|
- **Options**:
|
|
214
214
|
- `-a, --anime`: To export anime list.
|
|
215
215
|
- `-m, --manga`: To export manga list.
|
|
216
|
-
- **Description**: Export anime or manga list. For `XML (MyAnimeList)` file, to import it on MyAnimeList, go [here](https://myanimelist.net/import.php) and choose `MyAnimeList Import`.
|
|
216
|
+
- **Description**: Export anime or manga list. For `XML (MyAnimeList/AniDB)` file, to import it on MyAnimeList, go [here](https://myanimelist.net/import.php) and choose `MyAnimeList Import` for `AniDB` go [here](https://anidb.net/user/import) and select `MyAnimeList.net - XML anime list export`.
|
|
217
217
|
|
|
218
218
|
#### `import` _(alias: `imp`)_:
|
|
219
219
|
|
|
@@ -224,7 +224,10 @@ anilist import -m
|
|
|
224
224
|
- **Options**:
|
|
225
225
|
- `-a, --anime`: To import anime list.
|
|
226
226
|
- `-m, --manga`: To import manga list.
|
|
227
|
-
- **Description**: Import anime or manga list. If you want to import anime/manga list from MyAnimeList
|
|
227
|
+
- **Description**: Import anime or manga list. If you want to import anime/manga list from `MyAnimeList`, export the XML from [here](https://myanimelist.net/panel.php?go=export), for exporting list from `AniDB` go [here](https://anidb.net/user/export).
|
|
228
|
+
|
|
229
|
+
> [!NOTE]
|
|
230
|
+
> If you have exported from `AniDB`, you will have to unzip it, and there should be a file named `mylist.json`, copy and paste it in your systems download folder, and select it from import option.
|
|
228
231
|
|
|
229
232
|
> [!IMPORTANT]
|
|
230
233
|
> If you are importing from a file, place the file in the system specific download folder, And the exported file will also be exported there as well.
|
package/bin/helpers/auth.js
CHANGED
|
@@ -14,9 +14,9 @@ import open from "open";
|
|
|
14
14
|
import os from "os";
|
|
15
15
|
import path from "path";
|
|
16
16
|
import { fetcher } from "./fetcher.js";
|
|
17
|
-
import { AniList, MyAnimeList } from "./lists.js";
|
|
17
|
+
import { AniDB, AniList, MyAnimeList } from "./lists.js";
|
|
18
18
|
import { deleteActivityMutation, saveTextActivityMutation, } from "./mutations.js";
|
|
19
|
-
import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, userActivityQuery, } from "./queries.js";
|
|
19
|
+
import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, userActivityQuery, userFollowersQuery, userFollowingQuery, } from "./queries.js";
|
|
20
20
|
import { aniListEndpoint, getTitle, redirectUri, timestampToTimeAgo, } from "./workers.js";
|
|
21
21
|
const home_dir = os.homedir();
|
|
22
22
|
const save_path = path.join(home_dir, ".anilist_token");
|
|
@@ -109,7 +109,7 @@ class Auth {
|
|
|
109
109
|
}
|
|
110
110
|
static Myself() {
|
|
111
111
|
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
112
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
113
113
|
try {
|
|
114
114
|
if (yield Auth.isLoggedIn()) {
|
|
115
115
|
const headers = {
|
|
@@ -130,30 +130,42 @@ class Auth {
|
|
|
130
130
|
perPage: 10,
|
|
131
131
|
});
|
|
132
132
|
const activities = (_b = (_a = activiResponse === null || activiResponse === void 0 ? void 0 : activiResponse.data) === null || _a === void 0 ? void 0 : _a.Page) === null || _b === void 0 ? void 0 : _b.activities;
|
|
133
|
+
// Get follower/following information
|
|
134
|
+
const req_followers = yield fetcher(userFollowersQuery, {
|
|
135
|
+
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
136
|
+
});
|
|
137
|
+
const req_following = yield fetcher(userFollowingQuery, {
|
|
138
|
+
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
139
|
+
});
|
|
140
|
+
const followersCount = ((_e = (_d = (_c = req_followers === null || req_followers === void 0 ? void 0 : req_followers.data) === null || _c === void 0 ? void 0 : _c.Page) === null || _d === void 0 ? void 0 : _d.pageInfo) === null || _e === void 0 ? void 0 : _e.total) || 0;
|
|
141
|
+
const followingCount = ((_h = (_g = (_f = req_following === null || req_following === void 0 ? void 0 : req_following.data) === null || _f === void 0 ? void 0 : _f.Page) === null || _g === void 0 ? void 0 : _g.pageInfo) === null || _h === void 0 ? void 0 : _h.total) || 0;
|
|
133
142
|
console.log(`
|
|
134
143
|
ID: ${user === null || user === void 0 ? void 0 : user.id}
|
|
135
144
|
Name: ${user === null || user === void 0 ? void 0 : user.name}
|
|
136
145
|
siteUrl: ${user === null || user === void 0 ? void 0 : user.siteUrl}
|
|
137
|
-
profileColor: ${(
|
|
138
|
-
timeZone: ${(
|
|
139
|
-
activityMergeTime: ${(
|
|
146
|
+
profileColor: ${(_j = user === null || user === void 0 ? void 0 : user.options) === null || _j === void 0 ? void 0 : _j.profileColor}
|
|
147
|
+
timeZone: ${(_k = user === null || user === void 0 ? void 0 : user.options) === null || _k === void 0 ? void 0 : _k.timezone}
|
|
148
|
+
activityMergeTime: ${(_l = user === null || user === void 0 ? void 0 : user.options) === null || _l === void 0 ? void 0 : _l.activityMergeTime}
|
|
140
149
|
donatorTier: ${user === null || user === void 0 ? void 0 : user.donatorTier}
|
|
141
150
|
donatorBadge: ${user === null || user === void 0 ? void 0 : user.donatorBadge}
|
|
142
151
|
unreadNotificationCount:${user === null || user === void 0 ? void 0 : user.unreadNotificationCount}
|
|
143
152
|
Account Created: ${new Date((user === null || user === void 0 ? void 0 : user.createdAt) * 1000).toUTCString()}
|
|
144
153
|
Account Updated: ${new Date((user === null || user === void 0 ? void 0 : user.updatedAt) * 1000).toUTCString()}
|
|
154
|
+
|
|
155
|
+
Followers: ${followersCount}
|
|
156
|
+
Following: ${followingCount}
|
|
145
157
|
|
|
146
158
|
Statistics (Anime):
|
|
147
|
-
Count: ${(
|
|
148
|
-
Mean Score: ${(
|
|
149
|
-
Minutes Watched: ${(
|
|
150
|
-
Episodes Watched: ${(
|
|
159
|
+
Count: ${(_o = (_m = user === null || user === void 0 ? void 0 : user.statistics) === null || _m === void 0 ? void 0 : _m.anime) === null || _o === void 0 ? void 0 : _o.count}
|
|
160
|
+
Mean Score: ${(_q = (_p = user === null || user === void 0 ? void 0 : user.statistics) === null || _p === void 0 ? void 0 : _p.anime) === null || _q === void 0 ? void 0 : _q.meanScore}
|
|
161
|
+
Minutes Watched: ${(_s = (_r = user === null || user === void 0 ? void 0 : user.statistics) === null || _r === void 0 ? void 0 : _r.anime) === null || _s === void 0 ? void 0 : _s.minutesWatched}
|
|
162
|
+
Episodes Watched: ${(_u = (_t = user === null || user === void 0 ? void 0 : user.statistics) === null || _t === void 0 ? void 0 : _t.anime) === null || _u === void 0 ? void 0 : _u.episodesWatched}
|
|
151
163
|
|
|
152
164
|
Statistics (Manga):
|
|
153
|
-
Count: ${(
|
|
154
|
-
Mean Score: ${(
|
|
155
|
-
Chapters Read: ${(
|
|
156
|
-
Volumes Read: ${(
|
|
165
|
+
Count: ${(_w = (_v = user === null || user === void 0 ? void 0 : user.statistics) === null || _v === void 0 ? void 0 : _v.manga) === null || _w === void 0 ? void 0 : _w.count}
|
|
166
|
+
Mean Score: ${(_y = (_x = user === null || user === void 0 ? void 0 : user.statistics) === null || _x === void 0 ? void 0 : _x.manga) === null || _y === void 0 ? void 0 : _y.meanScore}
|
|
167
|
+
Chapters Read: ${(_0 = (_z = user === null || user === void 0 ? void 0 : user.statistics) === null || _z === void 0 ? void 0 : _z.manga) === null || _0 === void 0 ? void 0 : _0.chaptersRead}
|
|
168
|
+
Volumes Read: ${(_2 = (_1 = user === null || user === void 0 ? void 0 : user.statistics) === null || _1 === void 0 ? void 0 : _1.manga) === null || _2 === void 0 ? void 0 : _2.volumesRead}
|
|
157
169
|
`);
|
|
158
170
|
console.log(`\nRecent Activities:`);
|
|
159
171
|
if (activities.length > 0) {
|
|
@@ -528,6 +540,7 @@ Statistics (Manga):
|
|
|
528
540
|
choices: [
|
|
529
541
|
{ name: "Exported JSON file.", value: 1 },
|
|
530
542
|
{ name: "MyAnimeList (XML)", value: 2 },
|
|
543
|
+
{ name: "AniDB (json-large)", value: 3 },
|
|
531
544
|
],
|
|
532
545
|
pageSize: 10,
|
|
533
546
|
},
|
|
@@ -539,6 +552,9 @@ Statistics (Manga):
|
|
|
539
552
|
case 2:
|
|
540
553
|
yield MyAnimeList.importAnime();
|
|
541
554
|
break;
|
|
555
|
+
case 3:
|
|
556
|
+
yield AniDB.importAnime();
|
|
557
|
+
break;
|
|
542
558
|
default:
|
|
543
559
|
console.log(`\nInvalid Choice.`);
|
|
544
560
|
break;
|
package/bin/helpers/lists.d.ts
CHANGED
package/bin/helpers/lists.js
CHANGED
|
@@ -10,13 +10,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { XMLParser } from "fast-xml-parser";
|
|
11
11
|
import { readFile, writeFile } from "fs/promises";
|
|
12
12
|
import inquirer from "inquirer";
|
|
13
|
+
import { jsonrepair } from "jsonrepair";
|
|
14
|
+
import open from "open";
|
|
13
15
|
import { join } from "path";
|
|
14
16
|
import { Auth } from "./auth.js";
|
|
15
17
|
import { fetcher } from "./fetcher.js";
|
|
16
18
|
import { addAnimeToListMutation, addMangaToListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from "./mutations.js";
|
|
17
|
-
import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userQuery, } from "./queries.js";
|
|
19
|
+
import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
|
|
18
20
|
import { AniListMediaStatus, } from "./types.js";
|
|
19
|
-
import { createAnimeListXML, createMangaListXML, formatDateObject, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, timestampToTimeAgo, } from "./workers.js";
|
|
21
|
+
import { anidbToanilistMapper, createAnimeListXML, createMangaListXML, formatDateObject, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, timestampToTimeAgo, } from "./workers.js";
|
|
20
22
|
class AniList {
|
|
21
23
|
static importAnime() {
|
|
22
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -121,7 +123,7 @@ class AniList {
|
|
|
121
123
|
choices: [
|
|
122
124
|
{ name: "CSV", value: 1 },
|
|
123
125
|
{ name: "JSON", value: 2 },
|
|
124
|
-
{ name: "XML (MyAnimeList)", value: 3 },
|
|
126
|
+
{ name: "XML (MyAnimeList/AniDB)", value: 3 },
|
|
125
127
|
],
|
|
126
128
|
pageSize: 10,
|
|
127
129
|
},
|
|
@@ -612,7 +614,7 @@ class AniList {
|
|
|
612
614
|
}
|
|
613
615
|
static getUserByUsername(username) {
|
|
614
616
|
return __awaiter(this, void 0, void 0, function* () {
|
|
615
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
617
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
616
618
|
try {
|
|
617
619
|
const response = yield fetcher(userQuery, { username });
|
|
618
620
|
if (!((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.User)) {
|
|
@@ -625,6 +627,15 @@ class AniList {
|
|
|
625
627
|
perPage: 10,
|
|
626
628
|
});
|
|
627
629
|
const activities = (_f = (_e = (_d = userActivityResponse === null || userActivityResponse === void 0 ? void 0 : userActivityResponse.data) === null || _d === void 0 ? void 0 : _d.Page) === null || _e === void 0 ? void 0 : _e.activities) !== null && _f !== void 0 ? _f : [];
|
|
630
|
+
// Get follower/following information
|
|
631
|
+
const req_followers = yield fetcher(userFollowersQuery, {
|
|
632
|
+
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
633
|
+
});
|
|
634
|
+
const req_following = yield fetcher(userFollowingQuery, {
|
|
635
|
+
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
636
|
+
});
|
|
637
|
+
const followersCount = ((_j = (_h = (_g = req_followers === null || req_followers === void 0 ? void 0 : req_followers.data) === null || _g === void 0 ? void 0 : _g.Page) === null || _h === void 0 ? void 0 : _h.pageInfo) === null || _j === void 0 ? void 0 : _j.total) || 0;
|
|
638
|
+
const followingCount = ((_m = (_l = (_k = req_following === null || req_following === void 0 ? void 0 : req_following.data) === null || _k === void 0 ? void 0 : _k.Page) === null || _l === void 0 ? void 0 : _l.pageInfo) === null || _m === void 0 ? void 0 : _m.total) || 0;
|
|
628
639
|
console.log(`\nID:\t\t${user.id}`);
|
|
629
640
|
console.log(`Name:\t\t${user.name}`);
|
|
630
641
|
console.log(`Site URL:\t${user.siteUrl}`);
|
|
@@ -635,10 +646,12 @@ class AniList {
|
|
|
635
646
|
console.log(`Blocked:\t${user.isBlocked}`);
|
|
636
647
|
console.log(`Follower:\t${user.isFollower}`);
|
|
637
648
|
console.log(`Following:\t${user.isFollowing}`);
|
|
638
|
-
console.log(`Profile Color:\t${(
|
|
639
|
-
console.log(`Timezone:\t${(
|
|
640
|
-
console.log(`\
|
|
641
|
-
console.log(`
|
|
649
|
+
console.log(`Profile Color:\t${(_o = user.options) === null || _o === void 0 ? void 0 : _o.profileColor}`);
|
|
650
|
+
console.log(`Timezone:\t${((_p = user.options) === null || _p === void 0 ? void 0 : _p.timezone) ? (_q = user.options) === null || _q === void 0 ? void 0 : _q.timezone : "N/A"}`);
|
|
651
|
+
console.log(`\nFollowers:\t${followersCount}`);
|
|
652
|
+
console.log(`Following:\t${followingCount}`);
|
|
653
|
+
console.log(`\nStatistics (Anime)\n\tCount: ${((_s = (_r = user.statistics) === null || _r === void 0 ? void 0 : _r.anime) === null || _s === void 0 ? void 0 : _s.count) || 0}\tEpisodes Watched: ${((_u = (_t = user.statistics) === null || _t === void 0 ? void 0 : _t.anime) === null || _u === void 0 ? void 0 : _u.episodesWatched) || 0}\tMinutes Watched: ${((_w = (_v = user.statistics) === null || _v === void 0 ? void 0 : _v.anime) === null || _w === void 0 ? void 0 : _w.minutesWatched) || 0}`);
|
|
654
|
+
console.log(`Statistics (Manga)\n\tCount: ${((_y = (_x = user.statistics) === null || _x === void 0 ? void 0 : _x.manga) === null || _y === void 0 ? void 0 : _y.count) || 0}\tChapters Read: ${((_0 = (_z = user.statistics) === null || _z === void 0 ? void 0 : _z.manga) === null || _0 === void 0 ? void 0 : _0.chaptersRead) || 0}\tVolumes Read: ${((_2 = (_1 = user.statistics) === null || _1 === void 0 ? void 0 : _1.manga) === null || _2 === void 0 ? void 0 : _2.volumesRead) || 0}`);
|
|
642
655
|
if (activities.length > 0) {
|
|
643
656
|
console.log(`\nRecent Activities:`);
|
|
644
657
|
activities.forEach(({ status, progress, media, createdAt }) => {
|
|
@@ -1008,4 +1021,92 @@ class MyAnimeList {
|
|
|
1008
1021
|
});
|
|
1009
1022
|
}
|
|
1010
1023
|
}
|
|
1011
|
-
|
|
1024
|
+
class AniDB {
|
|
1025
|
+
static importAnime() {
|
|
1026
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1027
|
+
var _a, _b;
|
|
1028
|
+
try {
|
|
1029
|
+
const filename = yield selectFile(".json");
|
|
1030
|
+
const filePath = join(getDownloadFolderPath(), filename);
|
|
1031
|
+
const fileContent = yield readFile(filePath, "utf8");
|
|
1032
|
+
const js0n_repaired = jsonrepair(fileContent);
|
|
1033
|
+
if (fileContent) {
|
|
1034
|
+
const obj3ct = yield JSON.parse(js0n_repaired);
|
|
1035
|
+
const animeList = obj3ct === null || obj3ct === void 0 ? void 0 : obj3ct.anime;
|
|
1036
|
+
if ((animeList === null || animeList === void 0 ? void 0 : animeList.length) > 0) {
|
|
1037
|
+
let count = 0;
|
|
1038
|
+
let iteration = 0;
|
|
1039
|
+
let missed = [];
|
|
1040
|
+
for (const anime of animeList) {
|
|
1041
|
+
iteration++;
|
|
1042
|
+
const anidbId = anime.id;
|
|
1043
|
+
const released = anime.broadcastDate; // DD-MM-YYYY (eg: "23.07.2016")
|
|
1044
|
+
const status = anime.status;
|
|
1045
|
+
// const type = anime.type
|
|
1046
|
+
const totalEpisodes = anime.totalEpisodes;
|
|
1047
|
+
const ownEpisodes = anime.ownEpisodes;
|
|
1048
|
+
const romanjiName = anime.romanjiName;
|
|
1049
|
+
const englishName = anime.englishName;
|
|
1050
|
+
function getStatus(anidbStatus, episodesSeen) {
|
|
1051
|
+
if (anidbStatus === "complete") {
|
|
1052
|
+
return AniListMediaStatus.COMPLETED;
|
|
1053
|
+
}
|
|
1054
|
+
else if (anidbStatus === "incomplete" &&
|
|
1055
|
+
Number(episodesSeen) > 0) {
|
|
1056
|
+
return AniListMediaStatus.CURRENT;
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
return AniListMediaStatus.PLANNING;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
let anilistId = yield anidbToanilistMapper(romanjiName, Number(released.split(".")[2]), englishName);
|
|
1063
|
+
if (anilistId) {
|
|
1064
|
+
try {
|
|
1065
|
+
const saveResponse = yield fetcher(saveAnimeWithProgressMutation, {
|
|
1066
|
+
mediaId: anilistId,
|
|
1067
|
+
progress: ownEpisodes - 2,
|
|
1068
|
+
status: getStatus(status, ownEpisodes),
|
|
1069
|
+
hiddenFromStatusLists: false,
|
|
1070
|
+
private: false,
|
|
1071
|
+
});
|
|
1072
|
+
const entryId = (_b = (_a = saveResponse === null || saveResponse === void 0 ? void 0 : saveResponse.data) === null || _a === void 0 ? void 0 : _a.SaveMediaListEntry) === null || _b === void 0 ? void 0 : _b.id;
|
|
1073
|
+
if (entryId) {
|
|
1074
|
+
count++;
|
|
1075
|
+
console.log(`[${count}]\t${entryId} ✅\t${anidbId}\t${anilistId}\t(${ownEpisodes}/${totalEpisodes})\t${status}→${getStatus(status, ownEpisodes)}`);
|
|
1076
|
+
}
|
|
1077
|
+
// Rate limit each API call to avoid server overload
|
|
1078
|
+
// await new Promise((resolve) => setTimeout(resolve, 1100))
|
|
1079
|
+
}
|
|
1080
|
+
catch (error) {
|
|
1081
|
+
console.error(`Error processing AniDB ID ${anidbId}: ${error.message}`);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
missed.push({
|
|
1086
|
+
anidbId: anidbId,
|
|
1087
|
+
englishTitle: englishName,
|
|
1088
|
+
romajiTitle: romanjiName,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
console.log(`\nAccuracy: ${(((animeList.length - missed.length) / animeList.length) * 100).toFixed(2)}%\tTotal Processed: ${iteration}\tMissed: ${missed.length}`);
|
|
1093
|
+
if (missed.length > 0) {
|
|
1094
|
+
console.log(`Exporting missed entries to JSON file, Please add them manually.`);
|
|
1095
|
+
yield saveJSONasJSON(missed, "anidb-missed");
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
console.log(`\nNo anime list found in the file.`);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
console.log(`\nNo content found in the file or unable to read.`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
catch (error) {
|
|
1107
|
+
console.error(`\nError in AniDB import process: ${error.message}`);
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
export { AniDB, AniList, MyAnimeList };
|
package/bin/helpers/queries.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaLis
|
|
|
9
9
|
declare const upcomingAnimesQuery = "query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {\n Page(perPage: $perPage) {\n media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {\n id title { romaji english native userPreferred } season seasonYear startDate { year month day }\n episodes description genres\n }\n }\n}";
|
|
10
10
|
declare const animeDetailsQuery = "query ($id: Int) {\n Media(id: $id) {\n id idMal title { romaji english native userPreferred } episodes nextAiringEpisode { id }\n duration startDate { year month day } endDate { year month day } countryOfOrigin description isAdult status season format genres siteUrl\n stats { scoreDistribution { score amount } statusDistribution { status amount } }\n }\n}";
|
|
11
11
|
declare const userActivityQuery = "query ($id: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $id, type_in: [ANIME_LIST, MANGA_LIST], sort: ID_DESC) {\n ... on ListActivity { id status progress createdAt media { id title { romaji english } } }\n }\n }\n}";
|
|
12
|
-
declare const animeSearchQuery = "query ($search: String, $perPage: Int) {\n Page(perPage: $perPage) {\n media(search: $search, type: ANIME) { id title { romaji english native userPreferred } episodes status description }\n }\n}";
|
|
12
|
+
declare const animeSearchQuery = "query ($search: String, $perPage: Int) {\n Page(perPage: $perPage) {\n media(search: $search, type: ANIME) { id title { romaji english native userPreferred } startDate { day month year } episodes status description }\n }\n}";
|
|
13
13
|
declare const mangaSearchQuery = "query ($search: String, $perPage: Int) {\n Page(perPage: $perPage) {\n media(search: $search, type: MANGA) { id title { romaji english native userPreferred } chapters status description }\n }\n}";
|
|
14
14
|
declare const activityTextQuery = "query ($userId: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $userId, type: TEXT, sort: ID_DESC) {\n ... on TextActivity { id type text createdAt user { id name } }\n }\n }\n}";
|
|
15
15
|
declare const activityAnimeListQuery = "query ($userId: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $userId, type: ANIME_LIST, sort: ID_DESC) {\n ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }\n }\n }\n}";
|
|
@@ -19,4 +19,6 @@ declare const activityAllQuery = "query ($userId: Int, $page: Int, $perPage: Int
|
|
|
19
19
|
declare const activityMediaList = "query ($userId: Int, $page: Int, $perPage: Int, $type: ActivityType) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total currentPage lastPage hasNextPage perPage }\n activities(userId: $userId, type: $type, sort: ID_DESC) {\n ... on ListActivity { id type status progress media { id title { romaji english native } format } createdAt }\n }\n }\n}";
|
|
20
20
|
declare const malIdToAnilistAnimeId = "query ($malId: Int) {\n Media(idMal: $malId, type: ANIME) {\n id title { romaji english } } \n}\n";
|
|
21
21
|
declare const malIdToAnilistMangaId = "query ($malId: Int) {\n Media(idMal: $malId, type: MANGA) {\n id title { romaji english } } \n}\n";
|
|
22
|
-
|
|
22
|
+
declare const userFollowingQuery = "query ($userId: Int!) {\n Page {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }\n }\n}\n";
|
|
23
|
+
declare const userFollowersQuery = "query ($userId: Int!) {\n Page {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }\n }\n}\n";
|
|
24
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
package/bin/helpers/queries.js
CHANGED
|
@@ -66,7 +66,7 @@ const userActivityQuery = `query ($id: Int, $page: Int, $perPage: Int) {
|
|
|
66
66
|
}`;
|
|
67
67
|
const animeSearchQuery = `query ($search: String, $perPage: Int) {
|
|
68
68
|
Page(perPage: $perPage) {
|
|
69
|
-
media(search: $search, type: ANIME) { id title { romaji english native userPreferred } episodes status description }
|
|
69
|
+
media(search: $search, type: ANIME) { id title { romaji english native userPreferred } startDate { day month year } episodes status description }
|
|
70
70
|
}
|
|
71
71
|
}`;
|
|
72
72
|
const mangaSearchQuery = `query ($search: String, $perPage: Int) {
|
|
@@ -129,4 +129,18 @@ const malIdToAnilistMangaId = `query ($malId: Int) {
|
|
|
129
129
|
id title { romaji english } }
|
|
130
130
|
}
|
|
131
131
|
`;
|
|
132
|
-
|
|
132
|
+
const userFollowingQuery = `query ($userId: Int!) {
|
|
133
|
+
Page {
|
|
134
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
135
|
+
following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
const userFollowersQuery = `query ($userId: Int!) {
|
|
140
|
+
Page {
|
|
141
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
142
|
+
followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
package/bin/helpers/types.d.ts
CHANGED
|
@@ -171,9 +171,9 @@ interface Myself {
|
|
|
171
171
|
}[];
|
|
172
172
|
}
|
|
173
173
|
interface DateMonthYear {
|
|
174
|
-
day?:
|
|
175
|
-
month?:
|
|
176
|
-
year?:
|
|
174
|
+
day?: number;
|
|
175
|
+
month?: number;
|
|
176
|
+
year?: number;
|
|
177
177
|
}
|
|
178
178
|
interface AnimeDetails {
|
|
179
179
|
data?: {
|
|
@@ -212,4 +212,123 @@ interface MediaListEntry {
|
|
|
212
212
|
hiddenFromStatusLists?: boolean;
|
|
213
213
|
private?: boolean;
|
|
214
214
|
}
|
|
215
|
-
|
|
215
|
+
type UserActivitiesResponse = {
|
|
216
|
+
data?: {
|
|
217
|
+
Page: {
|
|
218
|
+
activities: {
|
|
219
|
+
status: string;
|
|
220
|
+
progress: number;
|
|
221
|
+
createdAt: number;
|
|
222
|
+
media: {
|
|
223
|
+
title: MediaTitle;
|
|
224
|
+
};
|
|
225
|
+
}[];
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
errors?: {
|
|
229
|
+
message: string;
|
|
230
|
+
}[];
|
|
231
|
+
};
|
|
232
|
+
type UserResponse = {
|
|
233
|
+
data?: {
|
|
234
|
+
User: {
|
|
235
|
+
id: number;
|
|
236
|
+
name: string;
|
|
237
|
+
siteUrl: string;
|
|
238
|
+
donatorTier: string;
|
|
239
|
+
donatorBadge: string;
|
|
240
|
+
createdAt: number;
|
|
241
|
+
updatedAt: number;
|
|
242
|
+
isBlocked: boolean;
|
|
243
|
+
isFollower: boolean;
|
|
244
|
+
isFollowing: boolean;
|
|
245
|
+
options: {
|
|
246
|
+
profileColor: string;
|
|
247
|
+
timezone: string;
|
|
248
|
+
};
|
|
249
|
+
statistics: {
|
|
250
|
+
anime: {
|
|
251
|
+
count: number;
|
|
252
|
+
episodesWatched: number;
|
|
253
|
+
minutesWatched: number;
|
|
254
|
+
};
|
|
255
|
+
manga: {
|
|
256
|
+
count: number;
|
|
257
|
+
chaptersRead: number;
|
|
258
|
+
volumesRead: number;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
errors?: {
|
|
264
|
+
message: string;
|
|
265
|
+
}[];
|
|
266
|
+
};
|
|
267
|
+
type UserFollower = {
|
|
268
|
+
data?: {
|
|
269
|
+
Page: {
|
|
270
|
+
pageInfo: {
|
|
271
|
+
total: number;
|
|
272
|
+
perPage: number;
|
|
273
|
+
currentPage: number;
|
|
274
|
+
lastPage: number;
|
|
275
|
+
hasNextPage: boolean;
|
|
276
|
+
};
|
|
277
|
+
followers: {
|
|
278
|
+
id: number;
|
|
279
|
+
name: string;
|
|
280
|
+
avatar: {
|
|
281
|
+
large: string;
|
|
282
|
+
medium: string;
|
|
283
|
+
};
|
|
284
|
+
bannerImage: string;
|
|
285
|
+
}[];
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
errors?: {
|
|
289
|
+
message: string;
|
|
290
|
+
}[];
|
|
291
|
+
};
|
|
292
|
+
type UserFollowing = {
|
|
293
|
+
data?: {
|
|
294
|
+
Page: {
|
|
295
|
+
pageInfo: {
|
|
296
|
+
total: number;
|
|
297
|
+
perPage: number;
|
|
298
|
+
currentPage: number;
|
|
299
|
+
lastPage: number;
|
|
300
|
+
hasNextPage: boolean;
|
|
301
|
+
};
|
|
302
|
+
following: {
|
|
303
|
+
id: number;
|
|
304
|
+
name: string;
|
|
305
|
+
avatar: {
|
|
306
|
+
large: string;
|
|
307
|
+
medium: string;
|
|
308
|
+
};
|
|
309
|
+
bannerImage: string;
|
|
310
|
+
}[];
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
errors?: {
|
|
314
|
+
message: string;
|
|
315
|
+
}[];
|
|
316
|
+
};
|
|
317
|
+
type AnimeSearchResponse = {
|
|
318
|
+
data?: {
|
|
319
|
+
Page: {
|
|
320
|
+
media: {
|
|
321
|
+
id: number;
|
|
322
|
+
title: MediaTitle;
|
|
323
|
+
startDate: DateMonthYear;
|
|
324
|
+
episodes: number;
|
|
325
|
+
status: string;
|
|
326
|
+
description: string;
|
|
327
|
+
}[];
|
|
328
|
+
};
|
|
329
|
+
};
|
|
330
|
+
errors?: {
|
|
331
|
+
message: string;
|
|
332
|
+
}[];
|
|
333
|
+
};
|
|
334
|
+
export { AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteMangaResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MediaEntry, MediaList, MediaListEntry, MediaTitle, MediaWithProgress, Myself, UserActivitiesResponse, UserFollower, UserFollowing, UserResponse, saveAnimeWithProgressResponse, };
|
package/bin/helpers/workers.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ declare function getTitle(title: {
|
|
|
6
6
|
romaji?: string;
|
|
7
7
|
}): string;
|
|
8
8
|
declare function formatDateObject(dateObj: {
|
|
9
|
-
day?:
|
|
10
|
-
month?:
|
|
11
|
-
year?:
|
|
9
|
+
day?: number;
|
|
10
|
+
month?: number;
|
|
11
|
+
year?: number;
|
|
12
12
|
} | null): string;
|
|
13
13
|
declare function getNextSeasonAndYear(): {
|
|
14
14
|
nextSeason: string;
|
|
@@ -36,4 +36,5 @@ declare function createAnimeListXML(mediaWithProgress: MediaWithProgress[]): Pro
|
|
|
36
36
|
declare function createMangaListXML(mediaWithProgress: MediaWithProgress[]): Promise<string>;
|
|
37
37
|
declare function getCurrentPackageVersion(): string | null;
|
|
38
38
|
declare function timestampToTimeAgo(timestamp: number): string;
|
|
39
|
-
|
|
39
|
+
declare const anidbToanilistMapper: (romanjiName: string, year: number, englishName?: string) => Promise<number | null>;
|
|
40
|
+
export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, timestampToTimeAgo, };
|
package/bin/helpers/workers.js
CHANGED
|
@@ -17,7 +17,9 @@ import { homedir } from "os";
|
|
|
17
17
|
import { join } from "path";
|
|
18
18
|
import process from "process";
|
|
19
19
|
import { Auth } from "./auth.js";
|
|
20
|
-
import {
|
|
20
|
+
import { fetcher } from "./fetcher.js";
|
|
21
|
+
import { animeSearchQuery } from "./queries.js";
|
|
22
|
+
import { MALAnimeStatus, MALMangaStatus, } from "./types.js";
|
|
21
23
|
const aniListEndpoint = `https://graphql.anilist.co`;
|
|
22
24
|
const redirectUri = "https://anilist.co/api/v2/oauth/pin";
|
|
23
25
|
function getTitle(title) {
|
|
@@ -314,4 +316,33 @@ function timestampToTimeAgo(timestamp) {
|
|
|
314
316
|
return `${years} year${years === 1 ? "" : "s"} ago`;
|
|
315
317
|
}
|
|
316
318
|
}
|
|
317
|
-
|
|
319
|
+
const anidbToanilistMapper = (romanjiName, year, englishName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
320
|
+
const fetchAnime = (search) => __awaiter(void 0, void 0, void 0, function* () {
|
|
321
|
+
var _a;
|
|
322
|
+
try {
|
|
323
|
+
const response = yield fetcher(animeSearchQuery, {
|
|
324
|
+
search,
|
|
325
|
+
perPage: 50,
|
|
326
|
+
});
|
|
327
|
+
return ((_a = response.data) === null || _a === void 0 ? void 0 : _a.Page.media) || [];
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
console.error("Error fetching AniList data:", error);
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
// Search using romanjiName first
|
|
335
|
+
let results = yield fetchAnime(romanjiName);
|
|
336
|
+
// If no results, fallback to englishName
|
|
337
|
+
if (!results.length && englishName) {
|
|
338
|
+
results = yield fetchAnime(englishName);
|
|
339
|
+
}
|
|
340
|
+
// Match using year
|
|
341
|
+
for (const anime of results) {
|
|
342
|
+
if (anime.startDate.year === year) {
|
|
343
|
+
return anime.id;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
});
|
|
348
|
+
export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, timestampToTimeAgo, };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@irfanshadikrishad/anilist",
|
|
3
3
|
"description": "Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts",
|
|
4
4
|
"author": "Irfan Shadik Rishad",
|
|
5
|
-
"version": "1.2.
|
|
5
|
+
"version": "1.2.5",
|
|
6
6
|
"main": "./bin/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"types": "./bin/index.d.ts",
|
|
@@ -54,10 +54,12 @@
|
|
|
54
54
|
},
|
|
55
55
|
"license": "MPL-2.0",
|
|
56
56
|
"devDependencies": {
|
|
57
|
+
"@babel/preset-env": "^7.26.0",
|
|
57
58
|
"@eslint/js": "^9.17.0",
|
|
58
59
|
"@types/jest": "^29.5.14",
|
|
59
60
|
"@types/json2csv": "^5.0.7",
|
|
60
61
|
"@types/node": "^22.10.5",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
|
61
63
|
"eslint": "^9.17.0",
|
|
62
64
|
"globals": "^15.14.0",
|
|
63
65
|
"jest": "^29.7.0",
|
|
@@ -65,15 +67,14 @@
|
|
|
65
67
|
"prettier-plugin-organize-imports": "^4.1.0",
|
|
66
68
|
"ts-jest": "^29.2.5",
|
|
67
69
|
"ts-node": "^10.9.2",
|
|
68
|
-
"typescript": "^5.7.2"
|
|
69
|
-
"@babel/preset-env": "^7.26.0",
|
|
70
|
-
"@typescript-eslint/eslint-plugin": "^8.19.0"
|
|
70
|
+
"typescript": "^5.7.2"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"commander": "^13.0.0",
|
|
74
74
|
"fast-xml-parser": "^4.5.1",
|
|
75
75
|
"inquirer": "^12.3.0",
|
|
76
76
|
"json2csv": "^6.0.0-alpha.2",
|
|
77
|
+
"jsonrepair": "^3.11.2",
|
|
77
78
|
"node-fetch": "^3.3.2",
|
|
78
79
|
"open": "^10.1.0"
|
|
79
80
|
}
|