diff --git a/README.md b/README.md index 411d8cb..4f14f4c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,13 @@

An awesome library to connect with the new MyAnimeList's API v2!
- Deno      |      NodeJS + Deno      |      Example

+ +# LICENSE + +[MIT][license-url] + +[license-url]: https://github.com/droidxrx/anime-mal-api/blob/master/LICENSE +[npm-url]: https://npmjs.org/package/anime-mal-api diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..d7b0515 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,251 @@ +interface codepair { + code_challenge: string; + code_verifier: string; +} +type baseTypeForList = { + score: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; + priority: 0 | 1 | 2; + tags: string; + comments: string; +}; +interface Obj { + [key: string]: any; +} +type AnimeFullArray = Array<"id" | "title" | "main_picture" | "alternative_titles" | "start_date" | "end_date" | "synopsis" | "mean" | "rank" | "popularity" | "num_list_users" | "num_scoring_users" | "nsfw" | "genres" | "created_at" | "updated_at" | "media_type" | "status" | "my_list_status" | "num_episodes" | "start_season" | "broadcast" | "source" | "average_episode_duration" | "rating" | "studios" | "pictures" | "background" | "related_anime" | "related_manga" | "recommendations" | "statistics">; +type AnimeInListArray = Array<"id" | "title" | "main_picture" | "alternative_titles" | "start_date" | "end_date" | "synopsis" | "mean" | "rank" | "popularity" | "num_list_users" | "num_scoring_users" | "nsfw" | "genres" | "created_at" | "updated_at" | "media_type" | "status" | "my_list_status" | "num_episodes" | "start_season" | "broadcast" | "source" | "average_episode_duration" | "rating" | "studios">; +type AnimeListArray = Array<"status" | "score" | "num_watched_episodes" | "is_rewatching" | "start_date" | "finish_date" | "priority" | "num_times_rewatched" | "rewatch_value" | "tags" | "updated_at" | "id" | "title" | "main_picture" | "alternative_titles" | "end_date" | "synopsis" | "mean" | "rank" | "popularity" | "num_list_users" | "num_scoring_users" | "nsfw" | "genres" | "created_at" | "media_type" | "my_list_status" | "num_episodes" | "start_season" | "broadcast" | "source" | "average_episode_duration" | "rating" | "studios">; +type MangaFullArray = Array<"id" | "title" | "main_picture" | "alternative_titles" | "start_date" | "end_date" | "synopsis" | "mean" | "rank" | "popularity" | "num_list_users" | "num_scoring_users" | "nsfw" | "genres" | "created_at" | "updated_at" | "media_type" | "status" | "my_list_status" | "num_volumes" | "num_chapters" | "authors" | "pictures" | "background" | "related_anime" | "related_manga" | "recommendations" | "serialization">; +type MangaInListArray = Array<"id" | "title" | "main_picture" | "alternative_titles" | "start_date" | "end_date" | "synopsis" | "mean" | "rank" | "popularity" | "num_list_users" | "num_scoring_users" | "nsfw" | "genres" | "created_at" | "updated_at" | "media_type" | "status" | "my_list_status" | "num_volumes" | "num_chapters" | "authors">; +type MangaListArray = Array<"status" | "score" | "num_volumes_read" | "num_chapters_read" | "is_rereading" | "start_date" | "finish_date" | "priority" | "num_times_reread" | "reread_value" | "tags" | "updated_at" | "id" | "title" | "main_picture" | "alternative_titles" | "end_date" | "synopsis" | "mean" | "rank" | "popularity" | "num_list_users" | "num_scoring_users" | "nsfw" | "genres" | "created_at" | "media_type" | "my_list_status" | "num_volumes" | "num_chapters" | "authors">; +type UserArray = Array<"id" | "name" | "picture" | "gender" | "birthday" | "location" | "joined_at" | "anime_statistics" | "time_zone" | "is_supporter">; +type animeid = string | number; +type rankingtype = "all" | "airing" | "upcoming" | "tv" | "ova" | "movie" | "special" | "bypopularity" | "favorite"; +type season$0 = "winter" | "spring" | "summer" | "fall"; +type sort = "anime_score" | "anime_num_list_users" | ""; +declare class anime { + #private; + constructor(access_token: string); + /** + * Specific anime by id, and return the anime with all details + * @param id — MAL Anime ID + * @param fields — Array + */ + // prettier-ignore + id(id: animeid, fields?: AnimeFullArray): Promise; + /** + * List of animes via a query text search + * @param q — Text + * @param offset — Default 0 + * @param limit — Default 100 + * @param fields — Array + */ + // prettier-ignore + search(q: string, offset?: number, limit?: number, fields?: AnimeInListArray): Promise; + /** + * Ranking animes, with all type of rankings + * @param ranking_type — string {@link rankingtype ranking_type} + * @param offset — Default 0 + * @param limit — Default 100 + * @param fields — Array + */ + // prettier-ignore + ranking(ranking_type?: rankingtype, offset?: number, limit?: number, fields?: AnimeInListArray): Promise; + /** + * Seasonal Anime, by default is filled at actual season + * @param year — Default Current Year + * @param season — Default Current Season + * @param offset — Default 0 + * @param limit — Default 100 + * @param sort — Default "" + * @param fields — Array + */ + // prettier-ignore + seasonal(year?: number, season?: season$0, offset?: number, limit?: number, sort?: sort, fields?: AnimeInListArray): Promise; + /** + * Anime suggestion from MAL + * @param offset — number default 0 + * @param limit — number default 100 + * @param fields — array + */ + suggestions(offset?: number, limit?: number, fields?: AnimeInListArray): Promise; +} +declare module animeWrapper { + export { anime }; +} +import ANIME = animeWrapper.anime; +interface updateList extends baseTypeForList { + status: "watching" | "completed" | "on_hold" | "dropped" | "plan_to_watch"; + is_rewatching: boolean; + num_watched_episodes: number; + num_times_rewatched: number; + rewatch_value: 0 | 1 | 2 | 3 | 4 | 5; +} +declare class userAnimeList { + #private; + constructor(access_token: string); + /** + * Get list anime from a user + * @param user_name — Default "@me" + * @param offset — Default 0 + * @param limit — Default 100 + * @param fields — Array + */ + getList(user_name?: string, offset?: number, limit?: number, fields?: AnimeListArray): Promise; + /** + * Delete a entry of the user's list. + * @param anime_id + */ + deleteList(anime_id: number): Promise; + /** + * Update a entry of the user's list. + * @param anime_id + * @param fieldsToUdpate + */ + updateList(anime_id: number, fieldsToUdpate: updateList): Promise; +} +declare module userAnimeListWrapper { + export { userAnimeList }; +} +import ANIME_LIST = userAnimeListWrapper.userAnimeList; +type typeRanking = "all" | "manga" | "novels" | "oneshots" | "doujin" | "manhwa" | "manhua" | "bypopularity" | "favorite"; +declare class manga { + #private; + constructor(access_token: string); + /** + * Specific manga by id, and return the manga with all details + * @param id — MAL Manga ID + * @param fields — Array + */ + id(id: number, fields?: MangaFullArray): Promise; + /** + * List of mangas via a query text search + * @param q — Text + * @param offset — Default 0 + * @param limit — Default 100 + * @param fields — Array + */ + search(q: string, offset?: number, limit?: number, fields?: MangaInListArray): Promise; + /** + * Ranking mangas, with all type of rankings + * @param ranking_type — Default "all" + * @param offset — Default 0 + * @param limit — Default 100 + * @param fields — Array + */ + // prettier-ignore + ranking(ranking_type?: typeRanking, offset?: number, limit?: number, fields?: MangaInListArray): Promise; +} +declare module mangaWrapper { + export { manga }; +} +import MANGA = mangaWrapper.manga; +interface updateList$0 extends baseTypeForList { + status: "reading" | "completed" | "on_hold" | "dropped" | "plan_to_read"; + is_rereading: boolean; + num_volumes_read: number; + num_chapters_read: number; + num_times_reread: number; + reread_value: 0 | 1 | 2 | 3 | 4 | 5; +} +declare class userMangaList { + #private; + constructor(access_token: string); + /** + * Get list anime from a user + * @param user_name — Default "@me" + * @param offset — Default 0 + * @param limit — Default 100 + * @param fields — Array + */ + getList(user_name?: string, offset?: number, limit?: number, fields?: MangaListArray): Promise; + /** + * Delete a entry of the user's list. + * @param manga_id + */ + deleteList(manga_id: number): Promise; + /** + * Update a entry of the user's list. + * @param manga_id + * @param fieldsToUdpate + */ + updateList(manga_id: number, fieldsToUdpate: updateList$0): Promise; +} +declare module userMangaListWrapper { + export { userMangaList }; +} +import MANGA_LIST = userMangaListWrapper.userMangaList; +declare class user { + #private; + constructor(access_token: string); + /** + * Get my user information + * @param fields Array + */ + me(fields?: UserArray): Promise; +} +declare module userWrapper { + export { user }; +} +import USER = userWrapper.user; +declare class oauth2 { + #private; + /** + * Class to get url authorization & access tokens + * + * @param {string} CLIENT_ID {@link https://myanimelist.net/apiconfig/create Get Client ID} + * @param {string} [CLIENT_SECRET] Optional + */ + constructor(CLIENT_ID: string, CLIENT_SECRET?: string); + /** + * + * @param length Optional default is "43" + * @returns + */ + generatePKCEChallenge(length?: number): codepair; + /** + * + * @param code_verifier Get this from method generatePKCEChallenge + * @param code_challenge Get this from method generatePKCEChallenge + * @returns + */ + verifyPKCEChallenge(code_verifier: string, code_challenge: string): boolean; + /** + * + * @param code_challenge Get this from method generatePKCEChallenge + * @param urlRedirect Optional if your set only one url redirect from api config + * @returns + */ + urlAuthorize(code_challenge: string, urlRedirect?: string): string; + /** + * + * @param code To get code you must access url from method urlAuthorize + * @param code_challenge Get this from method generatePKCEChallenge + * @param urlRedirect If you set url from method urlAuthorize so url must be same + * @returns + */ + accessToken(code: string, code_challenge: string, urlRedirect?: string): Promise; + /** + * + * @param refresh_token Get this from method accessToken + * @returns + */ + refreshToken(refresh_token: string): Promise; +} +declare module oauth2Wrapper { + export { oauth2 }; +} +import OAUTH2 = oauth2Wrapper.oauth2; +declare class API { + #private; + constructor(ACCESS_TOKEN: string); + ANIME(): ANIME; + USER_ANIME_LIST(): ANIME_LIST; + MANGA(): MANGA; + USER_MANGA_LIST(): MANGA_LIST; + USER(): USER; +} +declare const _default: { + API: typeof API; + OAUTH2: typeof OAUTH2; +}; +export { _default as default, API, OAUTH2 }; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..792d8b5 --- /dev/null +++ b/dist/index.js @@ -0,0 +1 @@ +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("axios"),t=require("crypto");function _interopDefaultLegacy(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=_interopDefaultLegacy(e);function _classExtractFieldDescriptor(e,t,a){if(!t.has(e))throw new TypeError("attempted to "+a+" private field on non-instance");return t.get(e)}function _classPrivateFieldGet(e,t){return function(e,t){return t.get?t.get.call(e):t.value}(e,_classExtractFieldDescriptor(e,t,"get"))}function _classPrivateFieldSet(e,t,a){return function(e,t,a){if(t.set)t.set.call(e,a);else{if(!t.writable)throw new TypeError("attempted to set read only private field");t.value=a}}(e,_classExtractFieldDescriptor(e,t,"set"),a),a}function stringnify(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",isArray=e=>Array.isArray(e),filterObj=e=>{var t={};for(var a in e){var i=e[a];[void 0,null,""].includes(i)||(isArray(i)?t[a]=i.toString():t[a]=i)}return t},a="".concat(t).concat(new URLSearchParams(filterObj(e)).toString());return decodeURIComponent(a)}function generateVerifier(e){return function(e,a){for(var i="",s=t.randomBytes(e),r=Math.pow(2,8),n=r/Math.min(a.length,r),l=0;l3&&void 0!==arguments[3]?arguments[3]:{},n=i();n.defaults.baseURL="https://api.myanimelist.net/v2",n.defaults.headers.Authorization="Bearer ".concat(e);var l=stringnify(s);return new Promise((function(e,i){"delete"===a?n.delete(t,r).then((t=>e(t.data))).catch((e=>i(e.response.data))):"get"===a?n.get(t,{params:l}).then((t=>e(t.data))).catch((e=>i(e.response.data))):"put"===a&&n.put(t,l,r).then((t=>e(t.data))).catch((e=>i(e.response.data)))}))}function httpOauth2(e){return new Promise((function(t,a){s("https://myanimelist.net/v1/oauth2",e,r).then((e=>t(e.data))).catch((e=>a(e.response.data)))}))}var n=["id","title","main_picture","alternative_titles","start_date","end_date","synopsis","mean","rank","popularity","num_list_users","num_scoring_users","nsfw","genres","created_at","updated_at","media_type","status","my_list_status","num_episodes","start_season","broadcast","source","average_episode_duration","rating","studios","pictures","background","related_anime","related_manga","recommendations","statistics"],l=["id","title","main_picture","alternative_titles","start_date","end_date","synopsis","mean","rank","popularity","num_list_users","num_scoring_users","nsfw","genres","created_at","updated_at","media_type","status","my_list_status","num_episodes","start_season","broadcast","source","average_episode_duration","rating","studios"],o=["status","score","num_watched_episodes","is_rewatching","start_date","finish_date","priority","num_times_rewatched","rewatch_value","tags","updated_at","id","title","main_picture","alternative_titles","end_date","synopsis","mean","rank","popularity","num_list_users","num_scoring_users","nsfw","genres","created_at","media_type","my_list_status","num_episodes","start_season","broadcast","source","average_episode_duration","rating","studios"],d=["id","title","main_picture","alternative_titles","start_date","end_date","synopsis","mean","rank","popularity","num_list_users","num_scoring_users","nsfw","genres","created_at","updated_at","media_type","status","my_list_status","num_volumes","num_chapters","authors","pictures","background","related_anime","related_manga","recommendations","serialization"],c=["id","title","main_picture","alternative_titles","start_date","end_date","synopsis","mean","rank","popularity","num_list_users","num_scoring_users","nsfw","genres","created_at","updated_at","media_type","status","my_list_status","num_volumes","num_chapters","authors"],u=["status","score","num_volumes_read","num_chapters_read","is_rereading","start_date","finish_date","priority","num_times_reread","reread_value","tags","updated_at","id","title","main_picture","alternative_titles","end_date","synopsis","mean","rank","popularity","num_list_users","num_scoring_users","nsfw","genres","created_at","media_type","my_list_status","num_volumes","num_chapters","authors"],_=["id","name","picture","gender","birthday","location","joined_at","anime_statistics","time_zone","is_supporter"],h=new WeakMap;class anime{constructor(e){h.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,h,e)}id(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:n;return t=t||n,httpApi(_classPrivateFieldGet(this,h),"/anime/".concat(e),"get",{fields:t})}search(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:100,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:l;return i=i||l,httpApi(_classPrivateFieldGet(this,h),"/anime","get",{q:e,limit:a,offset:t,fields:i})}ranking(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"all",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:100,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:l;return i=i||l,httpApi(_classPrivateFieldGet(this,h),"/anime/ranking","get",{ranking_type:e,limit:a,offset:t,fields:i})}seasonal(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:(new Date).getFullYear(),a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(e=(new Date).getMonth())<3?"winter":e>2&&e<6?"spring":e>5&&e<9?"summer":"fall",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:100,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"",n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:l,o={status:!1,return:{error:"Enter a valid season: winter, spring, summer, fall"}};return n=n||l,function(e){return["winter","spring","summer","fall"].find((t=>t===e))===e}(a)?httpApi(_classPrivateFieldGet(this,h),"/anime/season/".concat(t,"/").concat(a),"get",{year:t,season:a,offset:i,limit:s,sort:r,fields:n}):new Promise((function(e,t){e(o),t(o)}))}suggestions(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:l;return a=a||l,httpApi(_classPrivateFieldGet(this,h),"/anime/suggestions","get",{offset:e,limit:t,fields:a})}}var v=new WeakMap;class userAnimeList{constructor(e){v.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,v,e)}getList(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"@me",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:100,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:o;return i=i||o,httpApi(_classPrivateFieldGet(this,v),"/users/".concat(e,"/animelist"),"get",{offset:t,limit:a,fields:i})}deleteList(e){return httpApi(_classPrivateFieldGet(this,v),"/anime/".concat(e,"/my_list_status"),"delete")}updateList(e,t){return httpApi(_classPrivateFieldGet(this,v),"/anime/".concat(e,"/my_list_status"),"put",t)}}var p=new WeakMap;class manga{constructor(e){p.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,p,e)}id(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:d;return t=t||d,httpApi(_classPrivateFieldGet(this,p),"/manga/".concat(e),"get",{fields:t})}search(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:100,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:c;return i=i||c,httpApi(_classPrivateFieldGet(this,p),"/manga","get",{q:e,limit:a,offset:t,fields:i})}ranking(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"all",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:100,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:c;return i=i||c,httpApi(_classPrivateFieldGet(this,p),"/manga/ranking","get",{ranking_type:e,limit:a,offset:t,fields:i})}}var g=new WeakMap;class userMangaList{constructor(e){g.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,g,e)}getList(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"@me",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:100,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:u;return i=i||u,httpApi(_classPrivateFieldGet(this,g),"/users/".concat(e,"/mangalist"),"get",{offset:t,limit:a,fields:i})}deleteList(e){return httpApi(_classPrivateFieldGet(this,g),"/manga/".concat(e,"/my_list_status"),"delete")}updateList(e,t){return httpApi(_classPrivateFieldGet(this,g),"/manga/".concat(e,"/my_list_status"),"put",t)}}var m=new WeakMap;class user{constructor(e){m.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,m,e)}me(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:_;return e=e||_,httpApi(_classPrivateFieldGet(this,m),"/users/@me","get",{fields:e})}}var f=new WeakMap,w=new WeakMap;class oauth2{constructor(e,t){f.set(this,{writable:!0,value:void 0}),w.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,f,e),_classPrivateFieldSet(this,w,t)}generatePKCEChallenge(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:43;if(e<43)throw new Error("Length Minimal 43");if(e>128)throw new Error("Length Maximal 128");var t=function(e){if(e||(e=43),e<43||e>128)throw"Expected a length between 43 and 128. Received ".concat(e,".");var t=generateVerifier(e);return{code_challenge:generateChallenge(t),code_verifier:t}}(e);return{code_challenge:t.code_challenge,code_verifier:t.code_verifier}}verifyPKCEChallenge(e,t){return function(e,t){return generateChallenge(e)===t}(e,t)}urlAuthorize(e){var t,a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i={client_id:_classPrivateFieldGet(this,f),code_challenge:e,code_challenge_method:"plain",redirect_uri:a,response_type:"code"};return t=i,"".concat("https://myanimelist.net/v1/oauth2/authorize").concat(stringnify(t,"?"))}accessToken(e,t){var a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return httpOauth2({client_id:_classPrivateFieldGet(this,f),client_secret:_classPrivateFieldGet(this,w),code:e,code_verifier:t,grant_type:"authorization_code",redirect_uri:a})}refreshToken(e){return httpOauth2({client_id:_classPrivateFieldGet(this,f),client_secret:_classPrivateFieldGet(this,w),refresh_token:e,grant_type:"refresh_token"})}}var P=new WeakMap;class API{constructor(e){P.set(this,{writable:!0,value:void 0}),_classPrivateFieldSet(this,P,e)}ANIME(){if(!_classPrivateFieldGet(this,P))throw new Error("Please insert Access Token");return new anime(_classPrivateFieldGet(this,P))}USER_ANIME_LIST(){if(!_classPrivateFieldGet(this,P))throw new Error("Please insert Access Token");return new userAnimeList(_classPrivateFieldGet(this,P))}MANGA(){if(!_classPrivateFieldGet(this,P))throw new Error("Please insert Access Token");return new manga(_classPrivateFieldGet(this,P))}USER_MANGA_LIST(){if(!_classPrivateFieldGet(this,P))throw new Error("Please insert Access Token");return new userMangaList(_classPrivateFieldGet(this,P))}USER(){if(!_classPrivateFieldGet(this,P))throw new Error("Please insert Access Token");return new user(_classPrivateFieldGet(this,P))}}var y={API:API,OAUTH2:oauth2};exports.API=API,exports.OAUTH2=oauth2,exports.default=y; diff --git a/package.json b/package.json new file mode 100644 index 0000000..fff9433 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "anime-mal-api", + "version": "1.0.1", + "description": "An awesome library to connect with the new MyAnimeList's API v2!", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "rollup -c ./rollup.config.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/droidxrx/anime-mal-api.git" + }, + "keywords": [ + "anime", + "manga", + "mal", + "mal-api" + ], + "author": "DroidXrX (https://github.com/droidxrx)", + "license": "MIT", + "bugs": { + "url": "https://github.com/droidxrx/anime-mal-api/issues" + }, + "homepage": "https://github.com/droidxrx/anime-mal-api#readme", + "dependencies": { + "axios": "^0.21.1" + } +}