@irfanshadikrishad/anilist 1.0.0 → 1.0.1

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 CHANGED
@@ -51,64 +51,78 @@ here `<client-id>` and `<client-secret>` should be replaced by the ones that you
51
51
  | **`user`** | `-un (username)` | Get information about a specific AniList user |
52
52
  | **`lists`** <br> _(alias: `ls`)_ | `-a, --anime` <br> `-m, --manga` | Fetch anime or manga lists of the logged-in user |
53
53
  | **`delete`** <br> _(alias: `del`)_ | `-a, --anime` <br> `-m, --manga` | Delete collections of anime or manga |
54
+ | **`upcoming`** <br> _(alias:`up`)_ | `-c (default: 10)` | Fetch upcoming anime (default count is 10) |
55
+ | **`anime`** | `anime Id` | Get anime details by Anime Id |
54
56
 
55
57
  #### Command Breakdown:
56
58
 
57
- ### `login`:
59
+ #### `login`:
58
60
 
59
61
  - **Options**:
60
62
  - `-i, --id`: Specify AniList Client ID
61
63
  - `-s, --secret`: Provide the AniList Client Secret
62
64
  - **Usage**: Authenticate and log in to AniList using your ID and secret credentials.
63
65
 
64
- ### `logout`:
66
+ #### `logout`:
65
67
 
66
68
  - **Description**: End the current session and log out from your AniList account.
67
69
 
68
- ### `me`:
70
+ #### `me`:
69
71
 
70
72
  - **Description**: Retrieve and display information about the currently logged-in user, including stats and profile details.
71
73
 
72
- ### `-V, --version`:
74
+ #### `-V, --version`:
73
75
 
74
76
  - **Description**: Quickly check which version of the CLI you are running.
75
77
 
76
- ### `-h, --help`:
78
+ #### `-h, --help`:
77
79
 
78
80
  - **Description**: List all available commands and their usage details for quick reference.
79
81
 
80
- ### `trending` _(alias: `tr`)_:
82
+ #### `trending` _(alias: `tr`)_:
81
83
 
82
84
  - **Options**:
83
85
  - `-c (count)`: Specify how many trending anime to fetch (default: 10).
84
86
  - **Description**: Fetch the current trending anime series, with the option to customize how many results to display.
85
87
 
86
- ### `popular` _(alias: `plr`)_:
88
+ #### `popular` _(alias: `plr`)_:
87
89
 
88
90
  - **Options**:
89
91
  - `-c (count)`: Specify how many popular anime to fetch (default: 10).
90
92
  - **Description**: Fetch the most popular anime series, with the option to customize how many results to display.
91
93
 
92
- ### `user`:
94
+ #### `upcoming` _(alias: `up`)_:
95
+
96
+ - **Options**:
97
+ - `-c (count)`: Specify how many upcoming anime to fetch (default: 10).
98
+ - **Description**: Fetch the upcoming anime series next season, with the option to customize how many results to display.
99
+
100
+ #### `user`:
93
101
 
94
102
  - **Options**:
95
103
  - `-un (username)`: Specify the AniList username to fetch.
96
104
  - **Description**: Retrieve profile information about a specific AniList user.
97
105
 
98
- ### `lists` _(alias: `ls`)_:
106
+ #### `lists` _(alias: `ls`)_:
99
107
 
100
108
  - **Options**:
101
109
  - `-a, --anime`: Fetch the authenticated user's anime list.
102
110
  - `-m, --manga`: Fetch the authenticated user's manga list.
103
111
  - **Description**: Get the anime or manga lists of the logged-in user.
104
112
 
105
- ### `delete` _(alias: `del`)_:
113
+ #### `delete` _(alias: `del`)_:
106
114
 
107
115
  - **Options**:
108
116
  - `-a, --anime`: Delete your specific anime collection that you want.
109
117
  - `-m, --manga`: Delete your specific manga collection that you want.
110
118
  - **Description**: Delete the entire anime or manga collection from the logged-in user's profile.
111
119
 
120
+ #### `anime`
121
+
122
+ - **Options**
123
+ - `anime Id` _(eg: 21)_ : Id of the anime you want to get details of.
124
+ - **Description**: Get anime details by anime Id.
125
+
112
126
  #### Security
113
127
 
114
128
  Since you are creating your own API client for login no else else can get your credentials and the generated access token will be stored in your own system. So, As long as you don't share your device (in case you do, just logout) you are safe.
@@ -13,8 +13,9 @@ import path from "path";
13
13
  import inquirer from "inquirer";
14
14
  import open from "open";
15
15
  import fetch from "node-fetch";
16
- import { currentUserQuery } from "./queries.js";
17
- import { aniListEndpoint, redirectUri } from "./workers.js";
16
+ import { currentUserQuery, userActivityQuery } from "./queries.js";
17
+ import { aniListEndpoint, getTitle, redirectUri } from "./workers.js";
18
+ import { fetcher } from "./fetcher.js";
18
19
  const home_dir = os.homedir();
19
20
  const save_path = path.join(home_dir, ".anilist_token");
20
21
  function getAccessTokenFromUser() {
@@ -76,34 +77,48 @@ function anilistUserLogin(cID, cSECRET) {
76
77
  }
77
78
  function currentUserInfo() {
78
79
  return __awaiter(this, void 0, void 0, function* () {
79
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
80
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
80
81
  const loggedIn = yield isLoggedIn();
81
82
  if (loggedIn) {
82
83
  const sToken = yield retriveAccessToken();
84
+ const headers = {
85
+ "Content-Type": "application/json",
86
+ Authorization: `Bearer ${sToken}`,
87
+ };
83
88
  const request = yield fetch(aniListEndpoint, {
84
89
  method: "POST",
85
- headers: {
86
- "Content-Type": "application/json",
87
- Authorization: `Bearer ${sToken}`,
88
- },
90
+ headers: headers,
89
91
  body: JSON.stringify({ query: currentUserQuery }),
90
92
  });
91
93
  const { data, errors } = yield request.json();
92
94
  if (request.status === 200) {
93
95
  const user = data === null || data === void 0 ? void 0 : data.Viewer;
96
+ const activiResponse = yield fetcher(userActivityQuery, {
97
+ id: user === null || user === void 0 ? void 0 : user.id,
98
+ page: 1,
99
+ perPage: 10,
100
+ }, headers);
101
+ 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;
94
102
  console.log(`\nID:\t\t\t${user === null || user === void 0 ? void 0 : user.id}`);
95
103
  console.log(`Name:\t\t\t${user === null || user === void 0 ? void 0 : user.name}`);
96
104
  console.log(`siteUrl:\t\t${user === null || user === void 0 ? void 0 : user.siteUrl}`);
97
- console.log(`profileColor:\t\t${(_a = user === null || user === void 0 ? void 0 : user.options) === null || _a === void 0 ? void 0 : _a.profileColor}`);
98
- console.log(`timeZone:\t\t${(_b = user === null || user === void 0 ? void 0 : user.options) === null || _b === void 0 ? void 0 : _b.timezone}`);
99
- console.log(`activityMergeTime:\t${(_c = user === null || user === void 0 ? void 0 : user.options) === null || _c === void 0 ? void 0 : _c.activityMergeTime}`);
105
+ console.log(`profileColor:\t\t${(_c = user === null || user === void 0 ? void 0 : user.options) === null || _c === void 0 ? void 0 : _c.profileColor}`);
106
+ console.log(`timeZone:\t\t${(_d = user === null || user === void 0 ? void 0 : user.options) === null || _d === void 0 ? void 0 : _d.timezone}`);
107
+ console.log(`activityMergeTime:\t${(_e = user === null || user === void 0 ? void 0 : user.options) === null || _e === void 0 ? void 0 : _e.activityMergeTime}`);
100
108
  console.log(`donatorTier:\t\t${user === null || user === void 0 ? void 0 : user.donatorTier}`);
101
109
  console.log(`donatorBadge:\t\t${user === null || user === void 0 ? void 0 : user.donatorBadge}`);
102
110
  console.log(`unreadNotificationCount:${user === null || user === void 0 ? void 0 : user.unreadNotificationCount}`);
103
111
  console.log(`Account Created:\t${new Date((user === null || user === void 0 ? void 0 : user.createdAt) * 1000).toUTCString()}`);
104
112
  console.log(`Account Updated:\t${new Date((user === null || user === void 0 ? void 0 : user.updatedAt) * 1000).toUTCString()}`);
105
- console.log(`Statistics (Anime)\nCount: ${(_e = (_d = user === null || user === void 0 ? void 0 : user.statistics) === null || _d === void 0 ? void 0 : _d.anime) === null || _e === void 0 ? void 0 : _e.count} meanScore: ${(_g = (_f = user === null || user === void 0 ? void 0 : user.statistics) === null || _f === void 0 ? void 0 : _f.anime) === null || _g === void 0 ? void 0 : _g.meanScore} minutesWatched: ${(_j = (_h = user === null || user === void 0 ? void 0 : user.statistics) === null || _h === void 0 ? void 0 : _h.anime) === null || _j === void 0 ? void 0 : _j.minutesWatched}`);
106
- console.log(`Statistics (Manga)\nCount: ${(_l = (_k = user === null || user === void 0 ? void 0 : user.statistics) === null || _k === void 0 ? void 0 : _k.manga) === null || _l === void 0 ? void 0 : _l.count} Chapter Read: ${(_o = (_m = user === null || user === void 0 ? void 0 : user.statistics) === null || _m === void 0 ? void 0 : _m.manga) === null || _o === void 0 ? void 0 : _o.chaptersRead} Volumes Read: ${(_q = (_p = user === null || user === void 0 ? void 0 : user.statistics) === null || _p === void 0 ? void 0 : _p.manga) === null || _q === void 0 ? void 0 : _q.volumesRead}`);
113
+ console.log(`\nStatistics (Anime)\nCount: ${(_g = (_f = user === null || user === void 0 ? void 0 : user.statistics) === null || _f === void 0 ? void 0 : _f.anime) === null || _g === void 0 ? void 0 : _g.count} meanScore: ${(_j = (_h = user === null || user === void 0 ? void 0 : user.statistics) === null || _h === void 0 ? void 0 : _h.anime) === null || _j === void 0 ? void 0 : _j.meanScore} minutesWatched: ${(_l = (_k = user === null || user === void 0 ? void 0 : user.statistics) === null || _k === void 0 ? void 0 : _k.anime) === null || _l === void 0 ? void 0 : _l.minutesWatched}`);
114
+ console.log(`Statistics (Manga)\nCount: ${(_o = (_m = user === null || user === void 0 ? void 0 : user.statistics) === null || _m === void 0 ? void 0 : _m.manga) === null || _o === void 0 ? void 0 : _o.count} Chapter Read: ${(_q = (_p = user === null || user === void 0 ? void 0 : user.statistics) === null || _p === void 0 ? void 0 : _p.manga) === null || _q === void 0 ? void 0 : _q.chaptersRead} Volumes Read: ${(_s = (_r = user === null || user === void 0 ? void 0 : user.statistics) === null || _r === void 0 ? void 0 : _r.manga) === null || _s === void 0 ? void 0 : _s.volumesRead}`);
115
+ console.log(`\nRecent Activities:`);
116
+ activities.length > 0 &&
117
+ activities.map(({ id, status, progress, createdAt, media }, idx) => {
118
+ progress
119
+ ? console.log(`${status} ${progress} of ${getTitle(media === null || media === void 0 ? void 0 : media.title)}`)
120
+ : console.log(`${status} ${getTitle(media === null || media === void 0 ? void 0 : media.title)}`);
121
+ });
107
122
  }
108
123
  else {
109
124
  console.log(`Something went wrong. Please log in again. ${errors[0].message}`);
@@ -0,0 +1,5 @@
1
+ declare function colorize_Error(text: string): void;
2
+ declare function colorize_Anilist(text: string): string;
3
+ declare function colorize_Brown(text: string): string;
4
+ declare function colorize_Hex(text: string, hex: string): void;
5
+ export { colorize_Error, colorize_Anilist, colorize_Brown, colorize_Hex };
@@ -0,0 +1,14 @@
1
+ import chalk from "chalk";
2
+ function colorize_Error(text) {
3
+ console.log(chalk.red(text));
4
+ }
5
+ function colorize_Anilist(text) {
6
+ return chalk.hex("#03a8fc")(text);
7
+ }
8
+ function colorize_Brown(text) {
9
+ return chalk.hex("#ce9c69")(text);
10
+ }
11
+ function colorize_Hex(text, hex) {
12
+ console.log(chalk.hex(hex)(text));
13
+ }
14
+ export { colorize_Error, colorize_Anilist, colorize_Brown, colorize_Hex };
@@ -0,0 +1,2 @@
1
+ declare function fetcher(query: string, variables: object, headers: HeadersInit): Promise<any>;
2
+ export { fetcher };
@@ -0,0 +1,36 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fetch from "node-fetch";
11
+ import { aniListEndpoint } from "./workers.js";
12
+ function fetcher(query, variables, headers) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ var _a;
15
+ try {
16
+ const request = yield fetch(aniListEndpoint, {
17
+ method: "POST",
18
+ headers: headers,
19
+ body: JSON.stringify({ query, variables }),
20
+ });
21
+ const response = yield request.json();
22
+ if (request.status === 200) {
23
+ return response;
24
+ }
25
+ else {
26
+ console.error(`Error from fetcher. ${(_a = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _a === void 0 ? void 0 : _a.message}.`);
27
+ return null;
28
+ }
29
+ }
30
+ catch (error) {
31
+ console.error(`Something went wrong. ${error.message}.`);
32
+ return null;
33
+ }
34
+ });
35
+ }
36
+ export { fetcher };
@@ -4,4 +4,5 @@ declare function loggedInUsersAnimeLists(): Promise<void>;
4
4
  declare function loggedInUsersMangaLists(): Promise<void>;
5
5
  declare function deleteAnimeCollection(): Promise<void>;
6
6
  declare function deleteMangaCollection(): Promise<void>;
7
- export { getTrending, getPopular, loggedInUsersAnimeLists, loggedInUsersMangaLists, deleteAnimeCollection, deleteMangaCollection, };
7
+ declare function getUpcomingAnimes(count: number): Promise<void>;
8
+ export { getTrending, getPopular, getUpcomingAnimes, loggedInUsersAnimeLists, loggedInUsersMangaLists, deleteAnimeCollection, deleteMangaCollection, };
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import fetch from "node-fetch";
11
11
  import inquirer from "inquirer";
12
- import { aniListEndpoint, getTitle } from "./workers.js";
13
- import { deleteMangaEntryMutation, deleteMediaEntryMutation, popularQuery, trendingQuery, } from "./queries.js";
12
+ import { aniListEndpoint, getNextSeasonAndYear, getTitle } from "./workers.js";
13
+ import { deleteMangaEntryMutation, deleteMediaEntryMutation, popularQuery, trendingQuery, upcomingAnimesQuery, } from "./queries.js";
14
14
  import { currentUserAnimeList, currentUserMangaList } from "./queries.js";
15
15
  import { isLoggedIn, currentUsersId, retriveAccessToken } from "./auth.js";
16
16
  function getTrending(count) {
@@ -375,4 +375,43 @@ function deleteMangaByMangaId(id, title) {
375
375
  }
376
376
  });
377
377
  }
378
- export { getTrending, getPopular, loggedInUsersAnimeLists, loggedInUsersMangaLists, deleteAnimeCollection, deleteMangaCollection, };
378
+ function getUpcomingAnimes(count) {
379
+ return __awaiter(this, void 0, void 0, function* () {
380
+ var _a, _b, _c, _d;
381
+ try {
382
+ const { nextSeason, nextYear } = getNextSeasonAndYear();
383
+ const loggedIn = yield isLoggedIn();
384
+ let headers = {
385
+ "content-type": "application/json",
386
+ };
387
+ if (loggedIn) {
388
+ headers["Authorization"] = `Bearer ${yield retriveAccessToken()}`;
389
+ }
390
+ const request = yield fetch(aniListEndpoint, {
391
+ method: "POST",
392
+ headers: headers,
393
+ body: JSON.stringify({
394
+ query: upcomingAnimesQuery,
395
+ variables: { nextSeason, nextYear, perPage: count },
396
+ }),
397
+ });
398
+ const response = yield request.json();
399
+ if (request.status === 200) {
400
+ const upcoming = (_c = (_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.Page) === null || _b === void 0 ? void 0 : _b.media) !== null && _c !== void 0 ? _c : [];
401
+ console.log("");
402
+ upcoming.forEach(({ id, title, startDate, genres }, idx) => {
403
+ const titleName = (title === null || title === void 0 ? void 0 : title.userPreffered) || getTitle(title);
404
+ const formattedDate = `${(startDate === null || startDate === void 0 ? void 0 : startDate.day) ? `${startDate === null || startDate === void 0 ? void 0 : startDate.day}/` : ""}${startDate === null || startDate === void 0 ? void 0 : startDate.month}/${startDate === null || startDate === void 0 ? void 0 : startDate.year}`;
405
+ console.log(`[${idx + 1}] ${titleName}\n\t${formattedDate} • ${genres.join(", ")}`);
406
+ });
407
+ }
408
+ else {
409
+ console.error(`Something went wrong. ${(_d = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _d === void 0 ? void 0 : _d.message}`);
410
+ }
411
+ }
412
+ catch (error) {
413
+ console.error(`Error getting upcoming animes. ${error.message}`);
414
+ }
415
+ });
416
+ }
417
+ export { getTrending, getPopular, getUpcomingAnimes, loggedInUsersAnimeLists, loggedInUsersMangaLists, deleteAnimeCollection, deleteMangaCollection, };
@@ -1,2 +1,3 @@
1
1
  declare function getUserInfoByUsername(username: string): Promise<void>;
2
- export { getUserInfoByUsername };
2
+ declare function getAnimeDetailsByID(anilistID: number): Promise<void>;
3
+ export { getUserInfoByUsername, getAnimeDetailsByID };
@@ -8,12 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import fetch from "node-fetch";
11
- import { userQuery } from "./queries.js";
11
+ import { animeDetailsQuery, userActivityQuery, userQuery } from "./queries.js";
12
12
  import { isLoggedIn, retriveAccessToken } from "./auth.js";
13
- import { aniListEndpoint } from "./workers.js";
13
+ import { aniListEndpoint, formatDateObject, getTitle, removeHtmlAndMarkdown, } from "./workers.js";
14
+ import { fetcher } from "./fetcher.js";
15
+ import { colorize_Anilist, colorize_Brown } from "./colorize.js";
14
16
  function getUserInfoByUsername(username) {
15
17
  return __awaiter(this, void 0, void 0, function* () {
16
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
18
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
17
19
  try {
18
20
  const loggedIn = yield isLoggedIn();
19
21
  let headers = {
@@ -30,6 +32,12 @@ function getUserInfoByUsername(username) {
30
32
  const response = yield request.json();
31
33
  if (request.status === 200) {
32
34
  const user = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.User;
35
+ const { data } = yield fetcher(userActivityQuery, {
36
+ id: user === null || user === void 0 ? void 0 : user.id,
37
+ page: 1,
38
+ perPage: 10,
39
+ }, headers);
40
+ const activities = (_b = data === null || data === void 0 ? void 0 : data.Page) === null || _b === void 0 ? void 0 : _b.activities;
33
41
  console.log(`\nID:\t\t${user === null || user === void 0 ? void 0 : user.id}`);
34
42
  console.log(`Name:\t\t${user === null || user === void 0 ? void 0 : user.name}`);
35
43
  console.log(`siteUrl:\t${user === null || user === void 0 ? void 0 : user.siteUrl}`);
@@ -43,13 +51,20 @@ function getUserInfoByUsername(username) {
43
51
  console.log(`I blocked?\t${user === null || user === void 0 ? void 0 : user.isBlocked}`);
44
52
  console.log(`My follower:\t${user === null || user === void 0 ? void 0 : user.isFollower}`);
45
53
  console.log(`I'm following:\t${user === null || user === void 0 ? void 0 : user.isFollowing}`);
46
- console.log(`Color:\t${(_b = user === null || user === void 0 ? void 0 : user.options) === null || _b === void 0 ? void 0 : _b.profileColor}`);
47
- console.log(`Timezone:\t${(_c = user === null || user === void 0 ? void 0 : user.options) === null || _c === void 0 ? void 0 : _c.timezone}`);
48
- console.log(`Statistics (Anime)\nCount: ${(_e = (_d = user === null || user === void 0 ? void 0 : user.statistics) === null || _d === void 0 ? void 0 : _d.anime) === null || _e === void 0 ? void 0 : _e.count} episodesWatched: ${(_g = (_f = user === null || user === void 0 ? void 0 : user.statistics) === null || _f === void 0 ? void 0 : _f.anime) === null || _g === void 0 ? void 0 : _g.episodesWatched} minutesWatched: ${(_j = (_h = user === null || user === void 0 ? void 0 : user.statistics) === null || _h === void 0 ? void 0 : _h.anime) === null || _j === void 0 ? void 0 : _j.minutesWatched}`);
49
- console.log(`Statistics (Manga)\nCount: ${(_l = (_k = user === null || user === void 0 ? void 0 : user.statistics) === null || _k === void 0 ? void 0 : _k.manga) === null || _l === void 0 ? void 0 : _l.count} Chapter Read: ${(_o = (_m = user === null || user === void 0 ? void 0 : user.statistics) === null || _m === void 0 ? void 0 : _m.manga) === null || _o === void 0 ? void 0 : _o.chaptersRead} Volumes Read: ${(_q = (_p = user === null || user === void 0 ? void 0 : user.statistics) === null || _p === void 0 ? void 0 : _p.manga) === null || _q === void 0 ? void 0 : _q.volumesRead}`);
54
+ console.log(`Color:\t${(_c = user === null || user === void 0 ? void 0 : user.options) === null || _c === void 0 ? void 0 : _c.profileColor}`);
55
+ console.log(`Timezone:\t${(_d = user === null || user === void 0 ? void 0 : user.options) === null || _d === void 0 ? void 0 : _d.timezone}`);
56
+ console.log(`\nStatistics (Anime)\nCount: ${(_f = (_e = user === null || user === void 0 ? void 0 : user.statistics) === null || _e === void 0 ? void 0 : _e.anime) === null || _f === void 0 ? void 0 : _f.count} episodesWatched: ${(_h = (_g = user === null || user === void 0 ? void 0 : user.statistics) === null || _g === void 0 ? void 0 : _g.anime) === null || _h === void 0 ? void 0 : _h.episodesWatched} minutesWatched: ${(_k = (_j = user === null || user === void 0 ? void 0 : user.statistics) === null || _j === void 0 ? void 0 : _j.anime) === null || _k === void 0 ? void 0 : _k.minutesWatched}`);
57
+ console.log(`Statistics (Manga)\nCount: ${(_m = (_l = user === null || user === void 0 ? void 0 : user.statistics) === null || _l === void 0 ? void 0 : _l.manga) === null || _m === void 0 ? void 0 : _m.count} Chapter Read: ${(_p = (_o = user === null || user === void 0 ? void 0 : user.statistics) === null || _o === void 0 ? void 0 : _o.manga) === null || _p === void 0 ? void 0 : _p.chaptersRead} Volumes Read: ${(_r = (_q = user === null || user === void 0 ? void 0 : user.statistics) === null || _q === void 0 ? void 0 : _q.manga) === null || _r === void 0 ? void 0 : _r.volumesRead}`);
58
+ console.log(`\nRecent Activities:`);
59
+ activities.length > 0 &&
60
+ activities.map(({ id, status, progress, createdAt, media }, idx) => {
61
+ progress
62
+ ? console.log(`${status} ${progress} of ${getTitle(media === null || media === void 0 ? void 0 : media.title)}`)
63
+ : console.log(`${status} ${getTitle(media === null || media === void 0 ? void 0 : media.title)}`);
64
+ });
50
65
  }
51
66
  else {
52
- console.log(`Something went wrong. ${(_r = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _r === void 0 ? void 0 : _r.message}`);
67
+ console.log(`Something went wrong. ${(_s = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _s === void 0 ? void 0 : _s.message}`);
53
68
  }
54
69
  }
55
70
  catch (error) {
@@ -57,4 +72,36 @@ function getUserInfoByUsername(username) {
57
72
  }
58
73
  });
59
74
  }
60
- export { getUserInfoByUsername };
75
+ function getAnimeDetailsByID(anilistID) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ var _a;
78
+ const loggedIn = yield isLoggedIn();
79
+ let query = animeDetailsQuery;
80
+ let variables = { id: anilistID };
81
+ let headers = { "content-type": "application/json" };
82
+ if (loggedIn) {
83
+ headers["Authorization"] = `Bearer ${yield retriveAccessToken()}`;
84
+ }
85
+ const details = yield fetcher(query, variables, headers);
86
+ if (details) {
87
+ const { id, idMal, title, description, episodes, nextAiringEpisode, duration, startDate, endDate, countryOfOrigin, isAdult, status, season, format, genres, siteUrl, stats, } = (_a = details === null || details === void 0 ? void 0 : details.data) === null || _a === void 0 ? void 0 : _a.Media;
88
+ let titl = colorize_Anilist((title === null || title === void 0 ? void 0 : title.userPreffered) || getTitle(title));
89
+ let st_tus = colorize_Anilist(String(status));
90
+ let descri = colorize_Brown(removeHtmlAndMarkdown(description));
91
+ console.log(`\nID: ${id}`);
92
+ console.log(`Title: `, titl);
93
+ console.log(`Description: `, descri);
94
+ console.log(`Episode Duration: ${duration}min`);
95
+ console.log(`Origin: ${countryOfOrigin}`);
96
+ console.log(`Status: `, st_tus);
97
+ console.log(`Format: ${format}`);
98
+ console.log(`Genres: ${genres.join(", ")}`);
99
+ console.log(`Season: ${season}`);
100
+ console.log(`Url: `, siteUrl);
101
+ console.log(`isAdult: ${isAdult}`);
102
+ console.log(`Released: ${formatDateObject(startDate)}`);
103
+ console.log(`Finished: ${formatDateObject(endDate)}`);
104
+ }
105
+ });
106
+ }
107
+ export { getUserInfoByUsername, getAnimeDetailsByID };
@@ -1,9 +1,12 @@
1
- declare const currentUserQuery = "\n{\n Viewer {\n id\n name\n about\n bans\n siteUrl\n options {\n profileColor\n timezone\n activityMergeTime\n }\n donatorTier\n donatorBadge\n createdAt\n updatedAt\n unreadNotificationCount\n previousNames {\n name\n createdAt\n updatedAt\n }\n moderatorRoles\n favourites {\n anime {\n nodes {\n id\n title {\n romaji\n english\n }\n }\n }\n manga {\n nodes {\n id\n title {\n romaji\n english\n }\n }\n }\n }\n statistics {\n anime {\n count\n meanScore\n minutesWatched\n }\n manga {\n count\n chaptersRead\n volumesRead\n }\n }\n mediaListOptions {\n scoreFormat\n rowOrder\n animeList {\n sectionOrder\n }\n mangaList {\n sectionOrder\n }\n }\n }\n}\n";
2
- declare const trendingQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n media(sort: TRENDING_DESC, type: ANIME) {\n id\n title {\n romaji\n english\n }\n }\n }\n}\n";
3
- declare const popularQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n media(sort: POPULARITY_DESC, type: ANIME) {\n id\n title {\n romaji\n english\n }\n }\n }\n}\n";
4
- declare const userQuery = "\nquery ($username: String) {\n User(name: $username) {\n id\n name\n siteUrl\n donatorTier\n donatorBadge\n createdAt\n updatedAt\n previousNames {\n name\n createdAt\n updatedAt\n }\n isBlocked\n isFollower\n isFollowing\n options {\n profileColor\n timezone\n activityMergeTime\n }\n statistics {\n anime {\n count\n episodesWatched\n minutesWatched\n }\n manga {\n count\n chaptersRead\n volumesRead\n }\n }\n }\n}\n";
5
- declare const currentUserAnimeList = "\nquery ($id: Int) {\n MediaListCollection(userId: $id, type: ANIME) {\n lists {\n name\n entries {\n id\n media {\n id\n title {\n romaji\n english\n }\n }\n }\n }\n }\n}\n";
6
- declare const currentUserMangaList = "\n query ($id: Int) {\n MediaListCollection(userId: $id, type: MANGA) {\n lists {\n name\n entries {\n id\n media {\n title {\n romaji\n english\n }\n }\n }\n }\n }\n }\n";
7
- declare const deleteMediaEntryMutation = "\n mutation($id: Int!) {\n DeleteMediaListEntry(id: $id) {\n deleted\n }\n }";
8
- declare const deleteMangaEntryMutation = "\n mutation ($id: Int) {\n DeleteMediaListEntry(id: $id) {\n deleted\n }\n }\n";
9
- export { currentUserQuery, trendingQuery, popularQuery, userQuery, currentUserAnimeList, currentUserMangaList, deleteMediaEntryMutation, deleteMangaEntryMutation, };
1
+ declare const currentUserQuery = "\n{\n Viewer {\n id name about bans siteUrl\n options { profileColor timezone activityMergeTime }\n donatorTier donatorBadge createdAt updatedAt\n unreadNotificationCount\n previousNames { name createdAt updatedAt }\n moderatorRoles\n favourites {\n anime { nodes { id title { romaji english } } }\n manga { nodes { id title { romaji english } } }\n }\n statistics {\n anime { count meanScore minutesWatched }\n manga { count chaptersRead volumesRead }\n }\n mediaListOptions {\n scoreFormat rowOrder\n animeList { sectionOrder }\n mangaList { sectionOrder }\n }\n }\n}";
2
+ declare const trendingQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n media(sort: TRENDING_DESC, type: ANIME) {\n id title { romaji english }\n }\n }\n}";
3
+ declare const popularQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n media(sort: POPULARITY_DESC, type: ANIME) {\n id title { romaji english }\n }\n }\n}";
4
+ declare const userQuery = "\nquery ($username: String) {\n User(name: $username) {\n id name siteUrl donatorTier donatorBadge createdAt updatedAt\n previousNames { name createdAt updatedAt }\n isBlocked isFollower isFollowing\n options { profileColor timezone activityMergeTime }\n statistics {\n anime { count episodesWatched minutesWatched }\n manga { count chaptersRead volumesRead }\n }\n }\n}";
5
+ declare const currentUserAnimeList = "\nquery ($id: Int) {\n MediaListCollection(userId: $id, type: ANIME) {\n lists { name entries { id media { id title { romaji english } } } }\n }\n}";
6
+ declare const currentUserMangaList = "\nquery ($id: Int) {\n MediaListCollection(userId: $id, type: MANGA) {\n lists { name entries { id media { title { romaji english } } } }\n }\n}";
7
+ declare const deleteMediaEntryMutation = "\nmutation($id: Int!) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
8
+ declare const deleteMangaEntryMutation = "\nmutation ($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
9
+ declare const upcomingAnimesQuery = "\nquery 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 }\n season seasonYear startDate { year month day }\n episodes description genres\n }\n }\n}";
10
+ declare const animeDetailsQuery = "\nquery ($id: Int) {\n Media(id: $id) {\n id idMal title { romaji english native userPreferred }\n episodes nextAiringEpisode { id }\n duration startDate { year month day }\n endDate { year month day }\n countryOfOrigin description isAdult status season format genres siteUrl\n stats {\n scoreDistribution { score amount }\n statusDistribution { status amount }\n }\n }\n}";
11
+ declare const userActivityQuery = "\nquery ($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 {\n id status progress createdAt\n media { id title { romaji english } }\n }\n }\n }\n}";
12
+ export { currentUserQuery, trendingQuery, popularQuery, userQuery, currentUserAnimeList, currentUserMangaList, deleteMediaEntryMutation, deleteMangaEntryMutation, upcomingAnimesQuery, animeDetailsQuery, userActivityQuery, };
@@ -1,184 +1,109 @@
1
1
  const currentUserQuery = `
2
2
  {
3
3
  Viewer {
4
- id
5
- name
6
- about
7
- bans
8
- siteUrl
9
- options {
10
- profileColor
11
- timezone
12
- activityMergeTime
13
- }
14
- donatorTier
15
- donatorBadge
16
- createdAt
17
- updatedAt
4
+ id name about bans siteUrl
5
+ options { profileColor timezone activityMergeTime }
6
+ donatorTier donatorBadge createdAt updatedAt
18
7
  unreadNotificationCount
19
- previousNames {
20
- name
21
- createdAt
22
- updatedAt
23
- }
8
+ previousNames { name createdAt updatedAt }
24
9
  moderatorRoles
25
10
  favourites {
26
- anime {
27
- nodes {
28
- id
29
- title {
30
- romaji
31
- english
32
- }
33
- }
34
- }
35
- manga {
36
- nodes {
37
- id
38
- title {
39
- romaji
40
- english
41
- }
42
- }
43
- }
11
+ anime { nodes { id title { romaji english } } }
12
+ manga { nodes { id title { romaji english } } }
44
13
  }
45
14
  statistics {
46
- anime {
47
- count
48
- meanScore
49
- minutesWatched
50
- }
51
- manga {
52
- count
53
- chaptersRead
54
- volumesRead
55
- }
15
+ anime { count meanScore minutesWatched }
16
+ manga { count chaptersRead volumesRead }
56
17
  }
57
18
  mediaListOptions {
58
- scoreFormat
59
- rowOrder
60
- animeList {
61
- sectionOrder
62
- }
63
- mangaList {
64
- sectionOrder
65
- }
19
+ scoreFormat rowOrder
20
+ animeList { sectionOrder }
21
+ mangaList { sectionOrder }
66
22
  }
67
23
  }
68
- }
69
- `;
24
+ }`;
70
25
  const trendingQuery = `
71
26
  query ($page: Int, $perPage: Int) {
72
27
  Page(page: $page, perPage: $perPage) {
73
28
  media(sort: TRENDING_DESC, type: ANIME) {
74
- id
75
- title {
76
- romaji
77
- english
78
- }
29
+ id title { romaji english }
79
30
  }
80
31
  }
81
- }
82
- `;
32
+ }`;
83
33
  const popularQuery = `
84
34
  query ($page: Int, $perPage: Int) {
85
35
  Page(page: $page, perPage: $perPage) {
86
36
  media(sort: POPULARITY_DESC, type: ANIME) {
87
- id
88
- title {
89
- romaji
90
- english
91
- }
37
+ id title { romaji english }
92
38
  }
93
39
  }
94
- }
95
- `;
40
+ }`;
96
41
  const userQuery = `
97
42
  query ($username: String) {
98
43
  User(name: $username) {
99
- id
100
- name
101
- siteUrl
102
- donatorTier
103
- donatorBadge
104
- createdAt
105
- updatedAt
106
- previousNames {
107
- name
108
- createdAt
109
- updatedAt
110
- }
111
- isBlocked
112
- isFollower
113
- isFollowing
114
- options {
115
- profileColor
116
- timezone
117
- activityMergeTime
118
- }
44
+ id name siteUrl donatorTier donatorBadge createdAt updatedAt
45
+ previousNames { name createdAt updatedAt }
46
+ isBlocked isFollower isFollowing
47
+ options { profileColor timezone activityMergeTime }
119
48
  statistics {
120
- anime {
121
- count
122
- episodesWatched
123
- minutesWatched
124
- }
125
- manga {
126
- count
127
- chaptersRead
128
- volumesRead
129
- }
49
+ anime { count episodesWatched minutesWatched }
50
+ manga { count chaptersRead volumesRead }
130
51
  }
131
52
  }
132
- }
133
- `;
53
+ }`;
134
54
  const currentUserAnimeList = `
135
55
  query ($id: Int) {
136
56
  MediaListCollection(userId: $id, type: ANIME) {
137
- lists {
138
- name
139
- entries {
140
- id
141
- media {
142
- id
143
- title {
144
- romaji
145
- english
146
- }
147
- }
148
- }
149
- }
57
+ lists { name entries { id media { id title { romaji english } } } }
150
58
  }
151
- }
152
- `;
59
+ }`;
153
60
  const currentUserMangaList = `
154
- query ($id: Int) {
155
- MediaListCollection(userId: $id, type: MANGA) {
156
- lists {
157
- name
158
- entries {
159
- id
160
- media {
161
- title {
162
- romaji
163
- english
164
- }
165
- }
166
- }
167
- }
168
- }
61
+ query ($id: Int) {
62
+ MediaListCollection(userId: $id, type: MANGA) {
63
+ lists { name entries { id media { title { romaji english } } } }
169
64
  }
170
- `;
65
+ }`;
171
66
  const deleteMediaEntryMutation = `
172
- mutation($id: Int!) {
173
- DeleteMediaListEntry(id: $id) {
174
- deleted
175
- }
176
- }`;
67
+ mutation($id: Int!) {
68
+ DeleteMediaListEntry(id: $id) { deleted }
69
+ }`;
177
70
  const deleteMangaEntryMutation = `
178
- mutation ($id: Int) {
179
- DeleteMediaListEntry(id: $id) {
180
- deleted
71
+ mutation ($id: Int) {
72
+ DeleteMediaListEntry(id: $id) { deleted }
73
+ }`;
74
+ const upcomingAnimesQuery = `
75
+ query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {
76
+ Page(perPage: $perPage) {
77
+ media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {
78
+ id title { romaji english native userPreferred }
79
+ season seasonYear startDate { year month day }
80
+ episodes description genres
81
+ }
82
+ }
83
+ }`;
84
+ const animeDetailsQuery = `
85
+ query ($id: Int) {
86
+ Media(id: $id) {
87
+ id idMal title { romaji english native userPreferred }
88
+ episodes nextAiringEpisode { id }
89
+ duration startDate { year month day }
90
+ endDate { year month day }
91
+ countryOfOrigin description isAdult status season format genres siteUrl
92
+ stats {
93
+ scoreDistribution { score amount }
94
+ statusDistribution { status amount }
95
+ }
96
+ }
97
+ }`;
98
+ const userActivityQuery = `
99
+ query ($id: Int, $page: Int, $perPage: Int) {
100
+ Page(page: $page, perPage: $perPage) {
101
+ activities(userId: $id, type_in: [ANIME_LIST, MANGA_LIST], sort: ID_DESC) {
102
+ ... on ListActivity {
103
+ id status progress createdAt
104
+ media { id title { romaji english } }
105
+ }
181
106
  }
182
107
  }
183
- `;
184
- export { currentUserQuery, trendingQuery, popularQuery, userQuery, currentUserAnimeList, currentUserMangaList, deleteMediaEntryMutation, deleteMangaEntryMutation, };
108
+ }`;
109
+ export { currentUserQuery, trendingQuery, popularQuery, userQuery, currentUserAnimeList, currentUserMangaList, deleteMediaEntryMutation, deleteMangaEntryMutation, upcomingAnimesQuery, animeDetailsQuery, userActivityQuery, };
@@ -4,4 +4,14 @@ declare function getTitle(title: {
4
4
  english?: string;
5
5
  romaji?: string;
6
6
  }): string;
7
- export { aniListEndpoint, redirectUri, getTitle };
7
+ declare function formatDateObject(dateObj: {
8
+ day?: string;
9
+ month?: string;
10
+ year?: string;
11
+ } | null): string;
12
+ declare function getNextSeasonAndYear(): {
13
+ nextSeason: string;
14
+ nextYear: number;
15
+ };
16
+ declare function removeHtmlAndMarkdown(input: string): string;
17
+ export { aniListEndpoint, redirectUri, getTitle, getNextSeasonAndYear, formatDateObject, removeHtmlAndMarkdown, };
@@ -11,4 +11,52 @@ function getTitle(title) {
11
11
  return "???";
12
12
  }
13
13
  }
14
- export { aniListEndpoint, redirectUri, getTitle };
14
+ function formatDateObject(dateObj) {
15
+ if (!dateObj)
16
+ return "null";
17
+ const { day = "", month = "", year = "" } = dateObj;
18
+ return [day, month, year].filter(Boolean).join("/");
19
+ }
20
+ function getNextSeasonAndYear() {
21
+ const currentMonth = new Date().getMonth() + 1;
22
+ const currentYear = new Date().getFullYear();
23
+ let nextSeason;
24
+ let nextYear;
25
+ // Determine the current season
26
+ if (currentMonth >= 12 || currentMonth <= 2) {
27
+ nextSeason = "SPRING";
28
+ nextYear = currentMonth === 12 ? currentYear + 1 : currentYear;
29
+ }
30
+ else if (currentMonth >= 3 && currentMonth <= 5) {
31
+ nextSeason = "SUMMER";
32
+ nextYear = currentYear;
33
+ }
34
+ else if (currentMonth >= 6 && currentMonth <= 8) {
35
+ nextSeason = "FALL";
36
+ nextYear = currentYear;
37
+ }
38
+ else if (currentMonth >= 9 && currentMonth <= 11) {
39
+ nextSeason = "WINTER";
40
+ nextYear = currentYear + 1;
41
+ }
42
+ return { nextSeason, nextYear };
43
+ }
44
+ function removeHtmlAndMarkdown(input) {
45
+ if (input) {
46
+ input = input.replace(/<\/?[^>]+(>|$)/g, "");
47
+ input = input.replace(/(^|\n)#{1,6}\s+(.+?)(\n|$)/g, "$2 ");
48
+ input = input.replace(/(\*\*|__)(.*?)\1/g, "$2");
49
+ input = input.replace(/(\*|_)(.*?)\1/g, "$2");
50
+ input = input.replace(/`(.+?)`/g, "$1");
51
+ input = input.replace(/\[(.*?)\]\(.*?\)/g, "$1");
52
+ input = input.replace(/!\[(.*?)\]\(.*?\)/g, "$1");
53
+ input = input.replace(/(^|\n)>\s+(.+?)(\n|$)/g, "$2 ");
54
+ input = input.replace(/(^|\n)-\s+(.+?)(\n|$)/g, "$2 ");
55
+ input = input.replace(/(^|\n)\d+\.\s+(.+?)(\n|$)/g, "$2 ");
56
+ input = input.replace(/(^|\n)\s*([-*_]){3,}\s*(\n|$)/g, "$1");
57
+ input = input.replace(/~~(.*?)~~/g, "$1");
58
+ input = input.replace(/\s+/g, " ").trim();
59
+ }
60
+ return input;
61
+ }
62
+ export { aniListEndpoint, redirectUri, getTitle, getNextSeasonAndYear, formatDateObject, removeHtmlAndMarkdown, };
package/bin/index.js CHANGED
@@ -10,8 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  import { Command } from "commander";
12
12
  import { anilistUserLogin, currentUserInfo, logoutUser, } from "./helpers/auth.js";
13
- import { deleteAnimeCollection, deleteMangaCollection, getPopular, getTrending, loggedInUsersAnimeLists, loggedInUsersMangaLists, } from "./helpers/lists.js";
14
- import { getUserInfoByUsername } from "./helpers/more.js";
13
+ import { deleteAnimeCollection, deleteMangaCollection, getPopular, getTrending, getUpcomingAnimes, loggedInUsersAnimeLists, loggedInUsersMangaLists, } from "./helpers/lists.js";
14
+ import { getAnimeDetailsByID, getUserInfoByUsername } from "./helpers/more.js";
15
15
  const cli = new Command();
16
16
  cli.name("anilist").description("Unofficial AniList CLI").version("1.0.0");
17
17
  cli
@@ -70,7 +70,7 @@ cli
70
70
  .option("-m, --manga", "For manga list of authenticated user", false)
71
71
  .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
72
72
  if ((!anime && !manga) || (anime && manga)) {
73
- console.log(`Must select an option, either --anime or --manga`);
73
+ console.error(`Must select an option, either --anime or --manga`);
74
74
  }
75
75
  else if (anime) {
76
76
  yield loggedInUsersAnimeLists();
@@ -87,7 +87,7 @@ cli
87
87
  .option("-m, --manga", "For manga list of authenticated user", false)
88
88
  .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
89
89
  if ((!anime && !manga) || (anime && manga)) {
90
- console.log(`Must select an option, either --anime or --manga`);
90
+ console.error(`Must select an option, either --anime or --manga`);
91
91
  }
92
92
  else if (anime) {
93
93
  yield deleteAnimeCollection();
@@ -96,4 +96,23 @@ cli
96
96
  yield deleteMangaCollection();
97
97
  }
98
98
  }));
99
+ cli
100
+ .command("upcoming")
101
+ .alias("up")
102
+ .description("Anime that will be released in upcoming season")
103
+ .option("-c, --count <number>", "Number of items to get", "10")
104
+ .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ count }) {
105
+ yield getUpcomingAnimes(Number(count));
106
+ }));
107
+ cli
108
+ .command("anime [id]")
109
+ .description("Get anime details by their ID")
110
+ .action((id) => __awaiter(void 0, void 0, void 0, function* () {
111
+ if (id && !Number.isNaN(Number(id))) {
112
+ yield getAnimeDetailsByID(Number(id));
113
+ }
114
+ else {
115
+ console.error("Invalid or missing ID. Please provide a valid numeric ID.");
116
+ }
117
+ }));
99
118
  cli.parse(process.argv);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@irfanshadikrishad/anilist",
3
3
  "description": "Unofficial AniList CLI",
4
4
  "author": "Irfan Shadik Rishad",
5
- "version": "1.0.0",
5
+ "version": "1.0.1",
6
6
  "main": "./bin/index.js",
7
7
  "type": "module",
8
8
  "types": "./bin/index.d.ts",
@@ -32,6 +32,7 @@
32
32
  "typescript": "^5.6.3"
33
33
  },
34
34
  "dependencies": {
35
+ "chalk": "^5.3.0",
35
36
  "commander": "^12.1.0",
36
37
  "inquirer": "^12.0.0",
37
38
  "node-fetch": "^3.3.2",