├── .codeclimate.yml ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── lib ├── Api.js ├── Base │ ├── Beatmap.js │ ├── Event.js │ ├── Game.js │ ├── Match.js │ ├── MultiplayerScore.js │ ├── Score.js │ └── User.js ├── Constants.js └── utils.js ├── package.json ├── test ├── sampleMatch.json └── test.js └── yarn.lock /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | enabled: true 4 | channel: "eslint-5" 5 | config: .eslintrc.js 6 | duplication: 7 | enabled: false 8 | config: 9 | languages: 10 | - javascript 11 | fixme: 12 | enabled: true 13 | ratings: 14 | paths: 15 | - "**.js" 16 | exclude_paths: 17 | - test/* 18 | - examples/* 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es6: true, 4 | node: true, 5 | mocha: true 6 | }, 7 | parserOptions: { 8 | ecmaVersion: 2020, 9 | sourceType: 'module' 10 | }, 11 | extends: [ 12 | 'eslint:recommended' 13 | ], 14 | rules: { 15 | 'no-console': 1, 16 | 'no-control-regex': 0, 17 | 'no-alert': 1, 18 | 'no-else-return': 1, 19 | 'no-redeclare': 2, 20 | 'no-useless-escape': 1, 21 | 'no-inner-declarations': 0, 22 | 'array-bracket-spacing': ['warn', 'never'], 23 | 'brace-style': ['warn', '1tbs', { 24 | allowSingleLine: true 25 | }], 26 | 'no-trailing-spaces': 1, 27 | 'space-before-function-paren': ['warn', { 28 | anonymous: 'never', 29 | named: 'never', 30 | asyncArrow: 'always' 31 | }], 32 | 'arrow-spacing': 1, 33 | 'comma-spacing': ['warn', { 34 | before: false, 35 | after: true 36 | }], 37 | 'comma-dangle': 1, 38 | 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'], 39 | indent: ['error', 'tab', { 40 | SwitchCase: 1 41 | }], 42 | 'array-callback-return': 'error', 43 | eqeqeq: ['warn', 'always', { null: 'ignore' }], 44 | 'no-empty-function': ['error', { 45 | allow: [ 46 | 'arrowFunctions', 47 | 'functions', 48 | 'methods', 49 | ] 50 | }], 51 | 'no-eval': 'error', 52 | 'no-implied-eval': 'error', 53 | 'no-return-assign': 'error', 54 | 'no-unmodified-loop-condition': 'off', 55 | 'no-empty': 'error', 56 | 'no-extra-semi': 'error', 57 | 'no-invalid-regexp': 'error', 58 | 'no-irregular-whitespace': 'error', 59 | 'no-regex-spaces': 'error', 60 | 'no-unreachable': 'error', 61 | 'no-warning-comments': ['warn', { 62 | terms: ['todo', 'fixme'], 63 | location: 'start' 64 | }], 65 | 'valid-typeof': ['error', { requireStringLiterals: false }], 66 | 'constructor-super': 'error', 67 | 'no-const-assign': 'error', 68 | 'no-dupe-class-members': 'error', 69 | 'no-var': 'error', 70 | 'prefer-const': ['error', { 71 | destructuring: 'any', 72 | ignoreReadBeforeAssign: false 73 | }], 74 | 'no-lonely-if': 'error', 75 | 'object-shorthand': ['error', 'always', { 76 | ignoreConstructors: false, 77 | avoidQuotes: true 78 | }], 79 | 'block-spacing': ['error', 'always'], 80 | 'eol-last': ['error', 'always'], 81 | semi: ['error', 'always'] 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/auth.txt 2 | node_modules 3 | *.tmp 4 | *.log 5 | .vscode 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Brandon Russell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-osu 2 | [![Code Climate](https://codeclimate.com/github/brussell98/node-osu/badges/gpa.svg)](https://codeclimate.com/github/brussell98/node-osu) 3 | 4 | Making the osu api easy to use. 5 | 6 | *NOTE: This is for version 1.0 of the osu!api.* 7 | 8 | ## Getting Started 9 | Get your osu api key from https://osu.ppy.sh/p/api 10 | 11 | Install node-osu 12 | `yarn add node-osu` / `npm i node-osu` 13 | 14 | ## Running tests 15 | Install the dev dependencies: `mocha` and `chai` 16 | 17 | Run `yarn/npm test` 18 | 19 | ## Contributing 20 | - Follow the rules in `.eslintrc.js` 21 | - Separate changes into different PRs 22 | - Prioritize backwards-compatibility 23 | 24 | ## Documentation 25 | 26 | Require node-osu 27 | ```js 28 | const osu = require('node-osu'); 29 | ``` 30 | 31 | ### osu.Constants 32 | - Mods: An object containing the bitwise representation for each mod 33 | - URLSchemas: An object containing osu url schema generating functions 34 | - multiplayerMatch: Function taking `, [password]` 35 | - edit: Function taking `, [objects]` 36 | - channel: Function taking `` 37 | - download: Function taking `` 38 | - spectate: Function taking `` 39 | - Beatmaps: Beatmap constants 40 | - approved: Get approval state from a number representation 41 | - genre: Get map genre from a number representation 42 | - language: Get map language from a number representation 43 | - mode: Get game mode from a number representation 44 | - Multiplayer: Multiplayer match constants 45 | - scoringType: Get a game's scoring mode from a number representation 46 | - teamType: Get a game's team mode from a number representation 47 | - team: Get a user's team from a number representation 48 | - AccuracyMethods: Calculate accuracy from a score's counts 49 | 50 | ### osu.Api 51 | All methods return a Promise. 52 | `options` refers to the url parameters listed here: https://github.com/ppy/osu-api/wiki 53 | 54 | #### Constructor 55 | ```js 56 | const osuApi = new osu.Api('A3tGREAemXk213gfJJUewH9675g', { 57 | // baseUrl: sets the base api url (default: https://osu.ppy.sh/api) 58 | notFoundAsError: true, // Throw an error on not found instead of returning nothing. (default: true) 59 | completeScores: false, // When fetching scores also fetch the beatmap they are for (Allows getting accuracy) (default: false) 60 | parseNumeric: false // Parse numeric values into numbers/floats, excluding ids 61 | }); 62 | ``` 63 | 64 | #### apiCall(endpoint, options) 65 | Make an api call. Should generally not be used. 66 | ```js 67 | osuApi.apiCall('/get_user', { u: 'brussell98' }).then(user => { 68 | console.log(user[0].username); 69 | }); 70 | ``` 71 | 72 | #### getBeatmaps(options) 73 | Returns an array of Beatmap objects. 74 | ```js 75 | osuApi.getBeatmaps({ b: '765567' }).then(beatmaps => { 76 | console.log(beatmaps[0].title); 77 | }); 78 | ``` 79 | 80 | #### getUser(options) 81 | Returns an User object. 82 | ```js 83 | osuApi.getUser({ u: 'brussell98' }).then(user => { 84 | console.log(user.name); 85 | }); 86 | ``` 87 | 88 | #### getScores(options) 89 | Returns an array of Score objects. 90 | ```js 91 | osuApi.getScores({ b: '1036655' }).then(scores => { 92 | console.log(scores[0].score); 93 | }); 94 | 95 | // or with completeScores set to true 96 | osuApi.getScores({ b: '1036655' }).then(scores => { 97 | console.log(scores[0].score); 98 | console.log(scores[0].beatmap.title); 99 | console.log(scores[0].accuracy); 100 | }); 101 | ``` 102 | 103 | #### getUserBest(options) 104 | Returns an array of Score objects. 105 | ```js 106 | osuApi.getUserBest({ u: 'brussell98' }).then(scores => { 107 | console.log(scores[0].score); 108 | }); 109 | 110 | // or with completeScores set to true 111 | osuApi.getUserBest({ u: 'brussell98' }).then(scores => { 112 | console.log(scores[0].score); 113 | console.log(scores[0].beatmap.title); 114 | console.log(scores[0].accuracy); 115 | }); 116 | ``` 117 | 118 | #### getUserRecent(options) 119 | Returns an array of Score objects. If the user has not submitted a score in the past 24 hours, this will return as not found. 120 | ```js 121 | osuApi.getUserRecent({ u: 'brussell98' }).then(scores => { 122 | console.log(scores[0].score); 123 | }); 124 | 125 | // or with completeScores set to true 126 | osuApi.getUserRecent({ u: 'brussell98' }).then(scores => { 127 | console.log(scores[0].score); 128 | console.log(scores[0].beatmap.title); 129 | console.log(scores[0].accuracy); 130 | }); 131 | ``` 132 | 133 | #### getMatch(options) 134 | Returns an Match object. 135 | ```js 136 | osuApi.getMatch({ mp: '25576650' }).then(match => { 137 | console.log(match.name); 138 | }); 139 | ``` 140 | 141 | #### getReplay(options) 142 | Returns a replay object. **Do not spam this endpoint**. 143 | ```js 144 | const fs = require('fs'); 145 | osuApi.getReplay({ m: '0', b: '1337', u: 'brussell98' }).then(replay => { 146 | fs.writeFile('replay.txt', replay.content); 147 | }); 148 | ``` 149 | 150 | ### osu.User 151 | ```js 152 | User { 153 | id: '7541046', 154 | name: 'brussell98', 155 | counts: { 156 | '50': '34327', 157 | '100': '393959', 158 | '300': '4008334', 159 | SSH: '2', 160 | SS: '4', 161 | SH: '14', 162 | S: '379', 163 | A: '1785', 164 | plays: '16951' 165 | }, 166 | scores: { 167 | ranked: '8625602786', 168 | total: '20612840665' 169 | }, 170 | pp: { 171 | raw: '2669.26', 172 | rank: '134346', 173 | countryRank: '22842' 174 | }, 175 | country: 'US', 176 | level: '99.3151', 177 | accuracy: '98.3110122680664', 178 | secondsPlayed: '1239538', 179 | raw_joinDate: '2015-12-09 02:27:02', 180 | events: [ Event {...}, ...], 181 | 182 | // Getters 183 | accuracyFormatted: String 184 | joinDate: Date 185 | } 186 | ``` 187 | 188 | ### osu.Event 189 | ```js 190 | Event { 191 | html: ' brussell98 achieved rank #62 on Morimori Atsushi - Toono Gensou Monogatari (MRM REMIX) [Nardo\'s Futsuu] (osu!taiko)', 192 | beatmapId: '2244449', 193 | beatmapsetId: '812992', 194 | raw_date: '2020-01-04 05:02:09', 195 | epicFactor: '1', 196 | 197 | // Getters 198 | date: Date 199 | } 200 | ``` 201 | 202 | ### osu.Beatmap 203 | ```js 204 | Beatmap { 205 | id: '765567', 206 | beatmapSetId: '346872', 207 | hash: '49ae1a43f732d07aff8efab2b0f22bdf', 208 | title: 'GATE~Sore wa Akatsuki no you ni~ (TV size)', 209 | creator: 'Del05', 210 | version: 'Insane', 211 | source: 'GATE 自衛隊 彼の地にて、斯く戦えり', 212 | artist: 'KISIDA KYODAN & THE AKEBOSI ROCKETS', 213 | genre: 'Anime', 214 | language: 'Japanese', 215 | rating: '9.45067', 216 | bpm: '200', 217 | mode: 'Standard', 218 | tags: [ 219 | 'jieitai', 220 | 'kanochi', 221 | 'nite', 222 | 'kaku', 223 | 'tatakaeri', 224 | 'opening', 225 | 'kyle', 226 | 'y', 227 | 'walaowey', 228 | 'rory', 229 | 'tuka', 230 | 'ゲート' 231 | ], 232 | approvalStatus: 'Ranked', 233 | raw_submitDate: '2015-08-18 14:01:13', 234 | raw_approvedDate: '2016-03-18 18:21:22', 235 | raw_lastUpdate: '2016-03-02 15:14:22', 236 | maxCombo: '549', 237 | objects: { 238 | normal: '213', 239 | slider: '165', 240 | spinner: '0' 241 | }, 242 | difficulty: { 243 | rating: '4.68783', 244 | aim: '2.36005', 245 | speed: '2.29552', 246 | size: '4', 247 | overall: '7', 248 | approach: '9', 249 | drain: '6' 250 | }, 251 | length: { 252 | total: '89', 253 | drain: '89' 254 | }, 255 | counts: { 256 | favorites: '1127', 257 | favourites: '1127', 258 | plays: '1506571', 259 | passes: '262113' 260 | }, 261 | hasDownload: true, 262 | hasAudio: true, 263 | 264 | // Getters 265 | submitDate: Date, 266 | approvedDate: Date, 267 | lastUpdate: Date 268 | } 269 | ``` 270 | 271 | ### osu.Score 272 | ```js 273 | Score { 274 | score: '10380039', 275 | user: { 276 | name: 'Sarah', // null when using a getUserX method 277 | id: '7777836' 278 | }, 279 | beatmapId: null, // When using getScores() without completeScores this will be null 280 | counts: { 281 | '50': '0', 282 | '100': '5', 283 | '300': '414', 284 | geki: '92', 285 | katu: '5', 286 | miss: '0' 287 | }, 288 | maxCombo: '826', 289 | perfect: true, 290 | raw_date: '2018-09-10 22:36:08', 291 | rank: 'SH', 292 | pp: '240.73', // Can be null (in recent user scores for example) 293 | hasReplay: true, 294 | raw_mods: 88, 295 | beatmap: undefined, // or `Beatmap {...}` with completeScores 296 | 297 | // Getters 298 | date: Date, 299 | mods: [Constants.Mods], 300 | accuracy: Number 301 | } 302 | ``` 303 | 304 | ### osu.Match 305 | ```js 306 | Match { 307 | id: '57155016', 308 | name: 'OWC2019: (United States) vs (South Korea)', 309 | raw_start: '2019-12-22 02:48:47', 310 | raw_end: '2019-12-22 04:29:11', 311 | games: [ Game {...}, ...], 312 | 313 | // Getters 314 | start: Date, 315 | end: Date 316 | } 317 | ``` 318 | 319 | ### osu.Game 320 | ```js 321 | Game { 322 | id: '298230665', 323 | raw_start: '2019-12-22 03:57:20', 324 | raw_end: '2019-12-22 04:00:19', 325 | beatmapId: '1656914', 326 | mode: 'Standard', 327 | matchType: '0', // Unknown purpose 328 | scoringType: 'Score v2', 329 | teamType: 'Team vs', 330 | raw_mods: 64, 331 | scores: [ MultiplayerScore {...}, ...] // Will be empty if in progress 332 | 333 | // Getters 334 | start: Date, 335 | end: Date, 336 | mods: [ 'DoubleTime' ] // Array of `Constants.Mods` required for all players 337 | } 338 | ``` 339 | 340 | ### osu.MultiplayerScore 341 | ```js 342 | MultiplayerScore { 343 | slot: '0', 344 | team: 'Red', 345 | userId: '4194445', 346 | score: '353891', 347 | maxCombo: '710', 348 | rank: null, // Not used 349 | counts: { 350 | '50': '27', 351 | '100': '73', 352 | '300': '690', 353 | geki: '129', 354 | katu: '38', 355 | miss: '38' 356 | }, 357 | perfect: false, 358 | pass: true, 359 | raw_mods: 1, 360 | 361 | // Getters 362 | mods: [ 'NoFail' ] // Array of `Constants.Mods` used by the player 363 | } 364 | ``` 365 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /** Declaration file generated by dts-gen */ 2 | export class Api { 3 | /** 4 | * Creates a new node-osu object 5 | * @param {String} apiKey your osu api key 6 | * @param {Object} [options] 7 | * @param {String} [options.baseUrl="https://osu.ppy.sh/api"] Sets the base api url 8 | * @param {Boolean} [options.notFoundAsError=true] Throw an error on not found instead of returning nothing 9 | * @param {Boolean} [options.completeScores=false] When fetching scores also fetch the beatmap they are for (Allows getting accuracy) 10 | * @param {Boolean} [options.parseNumeric=false] Parse numeric properties into numbers. May have overflow 11 | */ 12 | constructor(apiKey: string, options: constructorOptions); 13 | 14 | /** 15 | * Makes an api call 16 | * @param {String} endpoint 17 | * @param {Object} options 18 | * @param {Date} [options.since] Return all beatmaps ranked or loved since this date 19 | * @param {String} [options.s] Specify a beatmapSetId to return metadata from 20 | * @param {String} [options.b] Specify a beatmapId to return metadata from 21 | * @param {String} [options.u] Specify a userId or a username to return metadata from 22 | * @param {"string"|"id"} [options.type] Specify if `u` is a userId or a username 23 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 24 | * @param {0|1} [options.a] Specify whether converted beatmaps are included 25 | * @param {String} [options.h] The beatmap hash 26 | * @param {Number} [options.limit] Amount of results. Default and maximum are 500 27 | * @param {Number} [options.mods] Mods that apply to the beatmap requested. Default is 0 28 | * @param {Number} [options.event_days] Max number of days between now and last event date. Range of 1-31. Default value is 1 29 | * @param {String} [options.mp] Match id to get information from 30 | * @returns {Promise} The response body 31 | */ 32 | apiCall(endpoint, options: apiCallOptions): Promise; 33 | 34 | /** 35 | * Returns an array of Beatmap objects 36 | * @param {Object} options 37 | * @param {String} options.b Specify a beatmapId to return metadata from 38 | * @param {Date} [options.since] Return all beatmaps ranked or loved since this date 39 | * @param {String} [options.s] Specify a beatmapSetId to return metadata from 40 | * @param {String} [options.u] Specify a userId or a username to return metadata from 41 | * @param {"string"|"id"} [options.type] Specify if `u` is a userId or a username 42 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 43 | * @param {0|1} [options.a] Specify whether converted beatmaps are included 44 | * @param {String} [options.h] The beatmap hash 45 | * @param {Number} [options.limit] Amount of results. Default and maximum are 500 46 | * @param {Number} [options.mods] Mods that apply to the beatmap requested. Default is 0 47 | * @returns {Promise} 48 | */ 49 | getBeatmaps(options: getBeatmapsOptions): Promise; 50 | 51 | /** 52 | * Returns a Match object. 53 | * @param {Object} options 54 | * @param {String} options.mp Match id to get information from 55 | * @returns {Promise} 56 | */ 57 | getMatch(options: getMatchOptions): Promise; 58 | 59 | /** 60 | * Returns a replay object. **Do not spam this endpoint.** 61 | * @param {Object} options 62 | * @param {0|1|2|3} options.m Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 63 | * @param {String} options.b The beatmapId in which the replay was played 64 | * @param {String} options.u The user that has played the beatmap (required) 65 | * @param {"string"|"id"} [options.type] Specify if u is a userId or a username 66 | * @param {Number} [options.mods] Specify a mod or mod combination 67 | * 68 | */ 69 | getReplay(options: getReplayOptions): Promise; 70 | 71 | /** 72 | * Returns an array of Score objects 73 | * @param {Object} options 74 | * @param {String} options.b Specify a beatmapId to return score information from 75 | * @param {String} [options.u] Specify a userId or a username to return information for 76 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 77 | * @param {"string"|"id"} [options.type] Specify if u is a user_id or a username 78 | * @param {Number} [options.limit] Amount of results from the top (range between 1 and 100 - defaults to 50) 79 | * @returns {Promise} 80 | */ 81 | getScores(options: getScoresOptions): Promise; 82 | 83 | /** 84 | * Returns a User object 85 | * @param {Object} options 86 | * @param {String} options.u Specify a userId or a username to return metadata from 87 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 88 | * @param {"string"|"id"} [options.type] Specify if u is a user_id or a username 89 | * @param {Number} [options.event_days] Max number of days between now and last event date. Range of 1-31. Default value is 1 90 | * @returns {Promise} 91 | */ 92 | getUser(options: getUserOptions): Promise; 93 | 94 | 95 | /** 96 | * Returns an array of Score objects 97 | * @param {Object} options 98 | * @param {String} options.u Specify a userId or a username to return best scores from 99 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 100 | * @param {"string"|"id"} [options.type] Specify if u is a user_id or a username 101 | * @param {Number} [options.limit] Amount of results (range between 1 and 100 - defaults to 10) 102 | * @returns {Promise} 103 | */ 104 | getUserBest(options: getUserBestOptions): Promise; 105 | 106 | 107 | /** 108 | * Returns an array of Score objects. 109 | * Will return not found if the user has not submitted any scores in the past 24 hours 110 | * @param {Object} options 111 | * @param {String} options.u Specify a userId or a username to return recent plays from 112 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 113 | * @param {"string"|"id"} [options.type] Specify if `u` is a user_id or a username 114 | * @param {Number} [options.limit] Amount of results (range between 1 and 50 - defaults to 10) 115 | * @returns {Promise} 116 | */ 117 | getUserRecent(options: getUserRecentOptions): Promise; 118 | 119 | /** 120 | * Returns a not found error or the response, depending on the config 121 | * @param {Object} response 122 | * @returns {Object} 123 | */ 124 | notFound(response: notFoundResponse): any; 125 | 126 | } 127 | 128 | export class Beatmap { 129 | id: string; 130 | beatmapSetId: string; 131 | hash: string; 132 | title: string; 133 | creator: string; 134 | version: string; 135 | 136 | source: string; 137 | artist: string; 138 | genre: Constants['Beatmaps']['genre']; 139 | language: Constants['Beatmaps']['language']; 140 | 141 | rating: number; 142 | bpm: number; 143 | mode: Constants['Beatmaps']['mode']; 144 | tags: string[]; 145 | approvalStatus: Constants['Beatmaps']['approved']; 146 | raw_submitDate: string; 147 | raw_approvedDate: string; 148 | raw_lastUpdate: string; 149 | maxCombo: number; 150 | objects: { 151 | normal: number, 152 | slider: number, 153 | spinner: number 154 | }; 155 | difficulty: { 156 | rating: number, 157 | aim: number, 158 | speed: number, 159 | size: number, 160 | overall: number, 161 | approach: number, 162 | drain: number 163 | }; 164 | length: { 165 | total: number, 166 | drain: number 167 | }; 168 | counts: { 169 | favorites: number, 170 | favourites: number, 171 | plays: number, 172 | passes: number 173 | }; 174 | hasDownload: boolean; 175 | hasAudio: boolean; 176 | 177 | } 178 | 179 | export class Event { 180 | html: string; 181 | beatmapId: string; 182 | beatmapsetId: string; 183 | raw_date: string; 184 | epicFactor: number; 185 | 186 | } 187 | 188 | export class Match { 189 | id: string; 190 | name: string; 191 | raw_start: string; 192 | raw_end: string; 193 | games: Game[]; 194 | 195 | // Getters 196 | start: Date; 197 | end: Date 198 | 199 | } 200 | 201 | export class Game { 202 | id: string; 203 | raw_start: string; 204 | raw_end: string; 205 | beatmapId: string; 206 | mode: string; 207 | matchType: string; // Unknown purpose 208 | scoringType: string; 209 | teamType: string; 210 | raw_mods: number; 211 | scores: MultiplayerScore[] // Will be empty if in progress 212 | 213 | // Getters 214 | start: Date; 215 | end: Date; 216 | mods: Constants['Mods'][] // Array of `Constants.Mods` required for all players 217 | 218 | } 219 | 220 | export class MultiplayerScore { 221 | constructor(...args: any[]); 222 | 223 | } 224 | 225 | export class Score { 226 | score: number; 227 | user: { 228 | 'name': string | null, 229 | 'id': string 230 | }; 231 | beatmapId: string | Beatmap; 232 | counts: { 233 | '300': number, 234 | '100': number, 235 | '50': number, 236 | 'geki': number, 237 | 'katu': number, 238 | 'miss': number 239 | }; 240 | maxCombo: number; 241 | perfect: boolean; 242 | raw_date: string; 243 | rank: string; 244 | pp: number; 245 | hasReplay: boolean; 246 | 247 | raw_mods: number; 248 | 249 | beatmap: Beatmap; 250 | 251 | date: Date | string; 252 | 253 | mods: string[] | string; 254 | 255 | accuracy: undefined | string | number; 256 | } 257 | 258 | export class User { 259 | id: number; 260 | name: string; 261 | counts: { 262 | '300': number, 263 | '100': number, 264 | '50': number, 265 | 'SSH': number, 266 | 'SS': number, 267 | 'SH': number, 268 | 'S': number, 269 | 'A': number, 270 | 'plays': number 271 | }; 272 | scores: { 273 | ranked: number; 274 | total: number; 275 | }; 276 | pp: { 277 | raw: Number, 278 | rank: Number, 279 | countryRank: Number 280 | }; 281 | country: string; 282 | level: number; 283 | accuracy: number; 284 | secondsPlayed: number; 285 | raw_joinDate: string; 286 | events; 287 | 288 | joinDate: Date | string; 289 | accuracyFormatted: string 290 | } 291 | 292 | export class Constants { 293 | AccuracyMethods: { 294 | "Catch the Beat": any; 295 | Mania: any; 296 | Standard: any; 297 | Taiko: any; 298 | }; 299 | Beatmaps: { 300 | approved: { 301 | "-1": string; 302 | "-2": string; 303 | "0": string; 304 | "1": string; 305 | "2": string; 306 | "3": string; 307 | "4": string; 308 | }; 309 | genre: { 310 | "0": string; 311 | "1": string; 312 | "10": string; 313 | "2": string; 314 | "3": string; 315 | "4": string; 316 | "5": string; 317 | "6": string; 318 | "7": string; 319 | "9": string; 320 | }; 321 | language: { 322 | "0": string; 323 | "1": string; 324 | "10": string; 325 | "11": string; 326 | "2": string; 327 | "3": string; 328 | "4": string; 329 | "5": string; 330 | "6": string; 331 | "7": string; 332 | "8": string; 333 | "9": string; 334 | }; 335 | mode: { 336 | "0": string; 337 | "1": string; 338 | "2": string; 339 | "3": string; 340 | }; 341 | }; 342 | Mods: { 343 | Autoplay: number; 344 | Cinema: number; 345 | DoubleTime: number; 346 | Easy: number; 347 | FadeIn: number; 348 | Flashlight: number; 349 | FreeModAllowed: number; 350 | HalfTime: number; 351 | HardRock: number; 352 | Hidden: number; 353 | Key1: number; 354 | Key2: number; 355 | Key3: number; 356 | Key4: number; 357 | Key5: number; 358 | Key6: number; 359 | Key7: number; 360 | Key8: number; 361 | Key9: number; 362 | KeyCoop: number; 363 | KeyMod: number; 364 | Mirror: number; 365 | Nightcore: number; 366 | NoFail: number; 367 | None: number; 368 | Perfect: number; 369 | Random: number; 370 | Relax: number; 371 | Relax2: number; 372 | ScoreIncreaseMods: number; 373 | ScoreV2: number; 374 | SpunOut: number; 375 | SuddenDeath: number; 376 | Target: number; 377 | TouchDevice: number; 378 | }; 379 | Multiplayer: { 380 | scoringType: { 381 | "0": string; 382 | "1": string; 383 | "2": string; 384 | "3": string; 385 | }; 386 | team: { 387 | "0": string; 388 | "1": string; 389 | "2": string; 390 | }; 391 | teamType: { 392 | "0": string; 393 | "1": string; 394 | "2": string; 395 | "3": string; 396 | }; 397 | }; 398 | URLSchemas: { 399 | channel: any; 400 | download: any; 401 | edit: any; 402 | multiplayerMatch: any; 403 | spectate: any; 404 | }; 405 | } 406 | 407 | 408 | //Options 409 | 410 | declare class constructorOptions { 411 | baseUrl?: boolean; 412 | notFoundAsError?: boolean; 413 | completeScores?: boolean; 414 | parseNumeric?: boolean; 415 | } 416 | 417 | declare class apiCallOptions { 418 | since?: Date 419 | s?: string; 420 | b?: string 421 | u?: string; 422 | type?: 'string' | 'id'; 423 | m?: 0 | 1 | 2 | 3; 424 | a?: 0 | 1; 425 | h?: string; 426 | limit?: number; 427 | mods?: number; 428 | event_days?: number; 429 | mp?: string; 430 | } 431 | 432 | declare class getBeatmapsOptions { 433 | b?: string; 434 | since?: Date; 435 | s?: string; 436 | u?: string; 437 | type?: 'string' | 'id'; 438 | m?: 0 | 1 | 2 | 3; 439 | a?: 0 | 1; 440 | h?: string; 441 | limit?: number; 442 | mods?: number; 443 | } 444 | declare class getMatchOptions { 445 | mp?: string; 446 | } 447 | 448 | declare class getReplayOptions { 449 | m?: 0 | 1 | 2 | 3; 450 | b?: string; 451 | u?: string; 452 | type?: 'string' | 'id'; 453 | mods?: number; 454 | } 455 | 456 | declare class getScoresOptions { 457 | b?: string; 458 | u?: string; 459 | m?: 0 | 1 | 2 | 3; 460 | type?: 'string' | 'id'; 461 | limit?: number; 462 | } 463 | 464 | declare class getUserOptions { 465 | u?: string; 466 | m?: 0 | 1 | 2 | 3; 467 | type?: 'string' | 'id'; 468 | event_days?: number; 469 | } 470 | 471 | declare class getUserBestOptions { 472 | u?: string; 473 | m?: 0 | 1 | 2 | 3; 474 | type?: 'string' | 'id'; 475 | limit?: number; 476 | } 477 | 478 | declare class getUserRecentOptions { 479 | u?: string; 480 | m?: 0 | 1 | 2 | 3; 481 | type?: 'string' | 'id'; 482 | limit?: number; 483 | } 484 | 485 | declare class notFoundResponse { 486 | response?: object; 487 | } 488 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Api: require('./lib/Api.js'), 3 | Constants: require('./lib/Constants.js'), 4 | Beatmap: require('./lib/base/Beatmap.js'), 5 | Score: require('./lib/base/Score.js'), 6 | User: require('./lib/base/User.js'), 7 | Match: require('./lib/base/Match.js'), 8 | Game: require('./lib/base/Game.js'), 9 | MultiplayerScore: require('./lib/base/MultiplayerScore.js'), 10 | Event: require('./lib/base/Event.js') 11 | }; 12 | -------------------------------------------------------------------------------- /lib/Api.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const userAgent = `node-osu v${require('../package.json').version} (https://github.com/brussell98/node-osu)`; 3 | const Beatmap = require('./base/Beatmap.js'); 4 | const Score = require('./base/Score.js'); 5 | const Match = require('./base/Match.js'); 6 | const User = require('./base/User.js'); 7 | 8 | class Api { 9 | /** 10 | * Creates a new node-osu object 11 | * @param {String} apiKey your osu api key 12 | * @param {Object} [options] 13 | * @param {String} [options.baseUrl="https://osu.ppy.sh/api"] Sets the base api url 14 | * @param {Boolean} [options.notFoundAsError=true] Throw an error on not found instead of returning nothing 15 | * @param {Boolean} [options.completeScores=false] When fetching scores also fetch the beatmap they are for (Allows getting accuracy) 16 | * @param {Boolean} [options.parseNumeric=false] Parse numeric properties into numbers. May have overflow 17 | */ 18 | constructor(apiKey, options = { }) { 19 | this.apiKey = apiKey; 20 | this.baseUrl = options.baseUrl || 'https://osu.ppy.sh/api'; 21 | this.notFoundAsError = options.notFoundAsError === undefined ? true : !!options.notFoundAsError; 22 | this.completeScores = !!options.completeScores; 23 | this.parseNumeric = !!options.parseNumeric; 24 | } 25 | 26 | get config() { 27 | return { 28 | notFoundAsError: this.notFoundAsError, 29 | completeScores: this.completeScores, 30 | parseNumeric: this.parseNumeric 31 | }; 32 | } 33 | 34 | /** 35 | * Makes an api call 36 | * @param {String} endpoint 37 | * @param {Object} options 38 | * @param {Date} [options.since] Return all beatmaps ranked or loved since this date 39 | * @param {String} [options.s] Specify a beatmapSetId to return metadata from 40 | * @param {String} [options.b] Specify a beatmapId to return metadata from 41 | * @param {String} [options.u] Specify a userId or a username to return metadata from 42 | * @param {"string"|"id"} [options.type] Specify if `u` is a userId or a username 43 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 44 | * @param {0|1} [options.a] Specify whether converted beatmaps are included 45 | * @param {String} [options.h] The beatmap hash 46 | * @param {Number} [options.limit] Amount of results. Default and maximum are 500 47 | * @param {Number} [options.mods] Mods that apply to the beatmap requested. Default is 0 48 | * @param {Number} [options.event_days] Max number of days between now and last event date. Range of 1-31. Default value is 1 49 | * @param {String} [options.mp] Match id to get information from 50 | * @returns {Promise} The response body 51 | */ 52 | async apiCall(endpoint, options) { 53 | if (!this.apiKey) 54 | throw new Error('apiKey not set'); 55 | options.k = this.apiKey; 56 | 57 | try { 58 | const resp = await request.get(this.baseUrl + endpoint) 59 | .set('User-Agent', userAgent) 60 | .query(options); 61 | 62 | return resp.body; 63 | } catch (error) { 64 | throw new Error(error.response || error); 65 | } 66 | } 67 | 68 | /** 69 | * Returns a not found error or the response, depending on the config 70 | * @param {Object} response 71 | * @returns {Object} 72 | */ 73 | notFound(response) { 74 | if (this.notFoundAsError) 75 | throw new Error('Not found'); 76 | 77 | return response; 78 | } 79 | 80 | /** 81 | * Returns an array of Beatmap objects 82 | * @param {Object} options 83 | * @param {String} options.b Specify a beatmapId to return metadata from 84 | * @param {Date} [options.since] Return all beatmaps ranked or loved since this date 85 | * @param {String} [options.s] Specify a beatmapSetId to return metadata from 86 | * @param {String} [options.u] Specify a userId or a username to return metadata from 87 | * @param {"string"|"id"} [options.type] Specify if `u` is a userId or a username 88 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 89 | * @param {0|1} [options.a] Specify whether converted beatmaps are included 90 | * @param {String} [options.h] The beatmap hash 91 | * @param {Number} [options.limit] Amount of results. Default and maximum are 500 92 | * @param {Number} [options.mods] Mods that apply to the beatmap requested. Default is 0 93 | * @returns {Promise} 94 | */ 95 | async getBeatmaps(options) { 96 | const resp = await this.apiCall('/get_beatmaps', options); 97 | 98 | if (resp.length === 0) 99 | return this.notFound(resp); 100 | 101 | return resp.map(bm => new Beatmap(this.config, bm)); 102 | } 103 | 104 | /** 105 | * Returns a User object 106 | * @param {Object} options 107 | * @param {String} options.u Specify a userId or a username to return metadata from 108 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 109 | * @param {"string"|"id"} [options.type] Specify if u is a user_id or a username 110 | * @param {Number} [options.event_days] Max number of days between now and last event date. Range of 1-31. Default value is 1 111 | * @returns {Promise} 112 | */ 113 | async getUser(options) { 114 | const resp = await this.apiCall('/get_user', options); 115 | 116 | if (resp.length === 0) 117 | return this.notFound(resp); 118 | 119 | return new User(this.config, resp[0]); 120 | } 121 | 122 | /** 123 | * Returns an array of Score objects 124 | * @param {Object} options 125 | * @param {String} options.b Specify a beatmapId to return score information from 126 | * @param {String} [options.u] Specify a userId or a username to return information for 127 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 128 | * @param {"string"|"id"} [options.type] Specify if u is a user_id or a username 129 | * @param {Number} [options.limit] Amount of results from the top (range between 1 and 100 - defaults to 50) 130 | * @returns {Promise} 131 | */ 132 | async getScores(options) { 133 | const resp = await this.apiCall('/get_scores', options); 134 | 135 | if (resp.length === 0) 136 | return this.notFound(resp); 137 | 138 | if (!this.completeScores) 139 | return resp.map(sc => new Score(this.config, sc)); 140 | 141 | const beatmaps = await this.getBeatmaps({ b: options.b }); 142 | return resp.map(sc => new Score(this.config, sc, beatmaps[0])); 143 | } 144 | 145 | /** 146 | * Returns an array of Score objects 147 | * @param {Object} options 148 | * @param {String} options.u Specify a userId or a username to return best scores from 149 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 150 | * @param {"string"|"id"} [options.type] Specify if u is a user_id or a username 151 | * @param {Number} [options.limit] Amount of results (range between 1 and 100 - defaults to 10) 152 | * @returns {Promise} 153 | */ 154 | async getUserBest(options) { 155 | const resp = await this.apiCall('/get_user_best', options); 156 | 157 | if (resp.length === 0) 158 | return this.notFound(resp); 159 | 160 | if (!this.completeScores) 161 | return resp.map(sc => new Score(this.config, sc)); 162 | 163 | const scores = resp.map(sc => new Score(this.config, sc)); 164 | for (const score of scores) 165 | score.beatmap = (await this.getBeatmaps({ b: score.beatmapId }))[0]; 166 | 167 | return scores; 168 | } 169 | 170 | /** 171 | * Returns an array of Score objects. 172 | * Will return not found if the user has not submitted any scores in the past 24 hours 173 | * @param {Object} options 174 | * @param {String} options.u Specify a userId or a username to return recent plays from 175 | * @param {0|1|2|3} [options.m] Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 176 | * @param {"string"|"id"} [options.type] Specify if `u` is a user_id or a username 177 | * @param {Number} [options.limit] Amount of results (range between 1 and 50 - defaults to 10) 178 | * @returns {Promise} 179 | */ 180 | async getUserRecent(options) { 181 | const resp = await this.apiCall('/get_user_recent', options); 182 | 183 | if (resp.length === 0) 184 | return this.notFound(resp); 185 | 186 | if (!this.completeScores) 187 | return resp.map(sc => new Score(this.config, sc)); 188 | 189 | const scores = resp.map(sc => new Score(this.config, sc)); 190 | for (const score of scores) 191 | score.beatmap = (await this.getBeatmaps({ b: score.beatmapId }))[0]; 192 | 193 | return scores; 194 | } 195 | 196 | /** 197 | * Returns a Match object. 198 | * @param {Object} options 199 | * @param {String} options.mp Match id to get information from 200 | * @returns {Promise} 201 | */ 202 | async getMatch(options) { 203 | const resp = await this.apiCall('/get_match', options); 204 | 205 | if (resp.match === 0) 206 | return this.notFound(resp); 207 | 208 | return new Match(this.config, resp); 209 | } 210 | 211 | /** 212 | * Returns a replay object. **Do not spam this endpoint.** 213 | * @param {Object} options 214 | * @param {0|1|2|3} options.m Mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania) 215 | * @param {String} options.b The beatmapId in which the replay was played 216 | * @param {String} options.u The user that has played the beatmap (required) 217 | * @param {"string"|"id"} [options.type] Specify if u is a userId or a username 218 | * @param {Number} [options.mods] Specify a mod or mod combination 219 | * 220 | */ 221 | async getReplay(options) { 222 | return await this.apiCall('/get_replay', options); 223 | } 224 | } 225 | 226 | module.exports = Api; 227 | -------------------------------------------------------------------------------- /lib/Base/Beatmap.js: -------------------------------------------------------------------------------- 1 | const { getNumeric } = require('../utils.js'); 2 | const Constants = require('../Constants.js'); 3 | 4 | /** 5 | * A beatmap 6 | * @prop {String} id 7 | * @prop {String} beatmapSetId 8 | * @prop {String} hash 9 | * @prop {String} title 10 | * @prop {String} creator 11 | * @prop {String} version 12 | * @prop {String} source 13 | * @prop {String} artist 14 | * @prop {String} genre 15 | * @prop {String} language 16 | * @prop {String|Number} rating 17 | * @prop {String|Number} bpm 18 | * @prop {String} mode 19 | * @prop {String[]} tags 20 | * @prop {String} approvalStatus 21 | * @prop {String} raw_submitDate 22 | * @prop {String} raw_approvedDate 23 | * @prop {String} raw_lastUpdate 24 | * @prop {String|Number} maxCombo 25 | * @prop {Object} objects 26 | * @prop {String|Number} objects.normal 27 | * @prop {String|Number} objects.slider 28 | * @prop {String|Number} objects.spinner 29 | * @prop {Object} difficulty 30 | * @prop {String|Number} difficulty.rating 31 | * @prop {String|Number} difficulty.aim 32 | * @prop {String|Number} difficulty.speed 33 | * @prop {String|Number} difficulty.size 34 | * @prop {String|Number} difficulty.overall 35 | * @prop {String|Number} difficulty.approach 36 | * @prop {String|Number} difficulty.drain 37 | * @prop {Object} length 38 | * @prop {String|Number} length.total 39 | * @prop {String|Number} length.drain 40 | * @prop {Object} counts 41 | * @prop {String|Number} counts.favorites 42 | * @prop {String|Number} counts.favourites 43 | * @prop {String|Number} counts.plays 44 | * @prop {String|Number} counts.passes 45 | * @prop {Boolean} hasDownload 46 | * @prop {Boolean} hasAudio 47 | * @prop {Date} submitDate 48 | * @prop {Date} approvedDate 49 | * @prop {Date} lastUpdate 50 | */ 51 | class Beatmap { 52 | constructor(config, data) { 53 | const num = getNumeric(config.parseNumeric); 54 | 55 | this.id = data.beatmap_id; 56 | this.beatmapSetId = data.beatmapset_id; 57 | this.hash = data.file_md5; 58 | this.title = data.title; 59 | this.creator = data.creator; 60 | this.version = data.version; 61 | 62 | this.source = data.source; 63 | this.artist = data.artist; 64 | this.genre = Constants.Beatmaps.genre[data.genre_id]; 65 | this.language = Constants.Beatmaps.language[data.language_id]; 66 | 67 | this.rating = num(data.rating); 68 | this.bpm = num(data.bpm); 69 | this.mode = Constants.Beatmaps.mode[data.mode]; 70 | this.tags = data.tags.split(' '); 71 | this.approvalStatus = Constants.Beatmaps.approved[data.approved]; 72 | this.raw_submitDate = data.submit_date; 73 | this.raw_approvedDate = data.approved_date; 74 | this.raw_lastUpdate = data.last_update; 75 | this.maxCombo = num(data.max_combo); 76 | this.objects = { 77 | normal: num(data.count_normal), 78 | slider: num(data.count_slider), 79 | spinner: num(data.count_spinner) 80 | }; 81 | this.difficulty = { 82 | rating: num(data.difficultyrating), 83 | aim: num(data.diff_aim), 84 | speed: num(data.diff_speed), 85 | size: num(data.diff_size), 86 | overall: num(data.diff_overall), 87 | approach: num(data.diff_approach), 88 | drain: num(data.diff_drain) 89 | }; 90 | this.length = { 91 | total: num(data.total_length), 92 | drain: num(data.hit_length) 93 | }; 94 | this.counts = { 95 | favorites: num(data.favourite_count), 96 | favourites: num(data.favourite_count), 97 | plays: num(data.playcount), 98 | passes: num(data.passcount) 99 | }; 100 | this.hasDownload = data.download_unavailable === '0'; 101 | this.hasAudio = data.audio_unavailable === '0'; 102 | } 103 | 104 | get submitDate() { 105 | if (this._submitDate !== undefined) 106 | return this._submitDate; 107 | 108 | this._submitDate = new Date(this.raw_submitDate + ' UTC'); 109 | return this._submitDate; 110 | } 111 | 112 | get approvedDate() { 113 | if (this._approvedDate !== undefined) 114 | return this._approvedDate; 115 | 116 | this._approvedDate = this.raw_approvedDate ? new Date(this.raw_approvedDate + ' UTC') : null; 117 | return this._approvedDate; 118 | } 119 | 120 | get lastUpdate() { 121 | if (this._lastUpdate !== undefined) 122 | return this._lastUpdate; 123 | 124 | this._lastUpdate = new Date(this.raw_lastUpdate + ' UTC'); 125 | return this._lastUpdate; 126 | } 127 | 128 | } 129 | 130 | module.exports = Beatmap; 131 | -------------------------------------------------------------------------------- /lib/Base/Event.js: -------------------------------------------------------------------------------- 1 | const { getNumeric } = require('../utils.js'); 2 | 3 | /** 4 | * A timeline event for a user 5 | * @prop {String} html 6 | * @prop {String} beatmapId 7 | * @prop {String} beatmapSetId 8 | * @prop {String} raw_date 9 | * @prop {String|Number} epicFactor How "epic" this event is (from 1-32) 10 | * @prop {Date} date 11 | */ 12 | class Event { 13 | constructor(config, data) { 14 | const num = getNumeric(config.parseNumeric); 15 | 16 | this.html = data.display_html; 17 | this.beatmapId = data.beatmap_id; 18 | this.beatmapsetId = data.beatmapset_id; 19 | this.raw_date = data.date; 20 | this.epicFactor = num(data.epicfactor); 21 | } 22 | 23 | get date() { 24 | if (this._date !== undefined) 25 | return this._date; 26 | 27 | this._date = new Date(this.raw_date + ' UTC'); 28 | return this._date; 29 | } 30 | } 31 | 32 | module.exports = Event; 33 | -------------------------------------------------------------------------------- /lib/Base/Game.js: -------------------------------------------------------------------------------- 1 | const MultiplayerScore = require('./MultiplayerScore.js'); 2 | const Constants = require('../Constants.js'); 3 | 4 | /** 5 | * A multiplayer game 6 | * @prop {String} id 7 | * @prop {String} raw_start 8 | * @prop {String} raw_end 9 | * @prop {String} beatmapId 10 | * @prop {String} mode 11 | * @prop {"0"} matchType 12 | * @prop {String} scoringType 13 | * @prop {String} teamType 14 | * @prop {Number} raw_mods 15 | * @prop {MultiplayerScore[]} scores 16 | * @prop {Date} start 17 | * @prop {Date} end 18 | * @prop {String[]} mods 19 | */ 20 | class Game { 21 | constructor(config, data) { 22 | this.id = data.game_id; 23 | this.raw_start = data.start_time; 24 | this.raw_end = data.end_time; 25 | this.beatmapId = data.beatmap_id; 26 | this.mode = Constants.Beatmaps.mode[data.play_mode]; 27 | this.matchType = data.match_type; // Unknown 28 | this.scoringType = Constants.Multiplayer.scoringType[data.scoring_type]; 29 | this.teamType = Constants.Multiplayer.teamType[data.team_type]; 30 | this.raw_mods = parseInt(data.mods); 31 | this.scores = data.scores.map(g => new MultiplayerScore(config, g)); 32 | } 33 | 34 | get start() { 35 | if (this._start !== undefined) 36 | return this._start; 37 | 38 | this._start = new Date(this.raw_start + ' UTC'); 39 | return this._start; 40 | } 41 | 42 | get end() { 43 | if (this._end !== undefined) 44 | return this._end; 45 | 46 | this._end = new Date(this.raw_end + ' UTC'); 47 | return this._end; 48 | } 49 | 50 | get mods() { 51 | if (this._mods !== undefined) 52 | return this._mods; 53 | 54 | this._mods = []; 55 | for (const mod in Constants.Mods) 56 | if (this.raw_mods & Constants.Mods[mod]) 57 | this._mods.push(mod); 58 | 59 | 60 | return this._mods; 61 | } 62 | } 63 | 64 | module.exports = Game; 65 | -------------------------------------------------------------------------------- /lib/Base/Match.js: -------------------------------------------------------------------------------- 1 | const Game = require('./Game.js'); 2 | 3 | /** 4 | * A multiplayer match 5 | * @prop {String} id 6 | * @prop {String} name 7 | * @prop {String} raw_start 8 | * @prop {?String} raw_end null if not finished 9 | * @prop {Game[]} games 10 | * @prop {Date} start 11 | * @prop {Date} end 12 | */ 13 | class Match { 14 | constructor(config, data) { 15 | this.id = data.match.match_id; 16 | this.name = data.match.name; 17 | this.raw_start = data.match.start_time; 18 | this.raw_end = data.match.end_time; 19 | 20 | this.games = data.games.map(g => new Game(config, g)); 21 | } 22 | 23 | get start() { 24 | if (this._start !== undefined) 25 | return this._start; 26 | 27 | this._start = new Date(this.raw_start + ' UTC'); 28 | return this._start; 29 | } 30 | 31 | get end() { 32 | if (this._end !== undefined) 33 | return this._end; 34 | 35 | this._end = new Date(this.raw_end + ' UTC'); 36 | return this._end; 37 | } 38 | } 39 | 40 | module.exports = Match; 41 | -------------------------------------------------------------------------------- /lib/Base/MultiplayerScore.js: -------------------------------------------------------------------------------- 1 | const { getNumeric } = require('../utils.js'); 2 | const Constants = require('../Constants.js'); 3 | 4 | /** 5 | * A multiplayer game score 6 | * @prop {String|Number} slot 7 | * @prop {String} team 8 | * @prop {String} userId 9 | * @prop {String|Number} score 10 | * @prop {String|Number} maxCombo 11 | * @prop {Null} rank 12 | * @prop {Object} counts 13 | * @prop {String|Number} counts.300 14 | * @prop {String|Number} counts.100 15 | * @prop {String|Number} counts.50 16 | * @prop {String|Number} counts.geki 17 | * @prop {String|Number} counts.katu 18 | * @prop {String|Number} counts.miss 19 | * @prop {Boolean} perfect 20 | * @prop {Boolean} pass 21 | * @prop {Number} raw_mods 22 | * @prop {String[]} mods 23 | */ 24 | class MultiplayerScore { 25 | constructor(config, data) { 26 | const num = getNumeric(config.parseNumeric); 27 | 28 | this.slot = num(data.slot); 29 | this.team = Constants.Multiplayer.team[data.team]; 30 | this.userId = data.user_id; 31 | this.score = num(data.score); 32 | this.maxCombo = num(data.maxcombo); 33 | this.rank = null; // Not used 34 | this.counts = { 35 | '300': num(data.count300), 36 | '100': num(data.count100), 37 | '50': num(data.count50), 38 | 'geki': num(data.countgeki), 39 | 'katu': num(data.countkatu), 40 | 'miss': num(data.countmiss) 41 | }; 42 | this.perfect = data.perfect === '1'; 43 | this.pass = data.pass === '1'; 44 | this.raw_mods = parseInt(data.enabled_mods || '0'); 45 | } 46 | 47 | get mods() { 48 | if (this._mods !== undefined) 49 | return this._mods; 50 | 51 | this._mods = []; 52 | for (const mod in Constants.Mods) 53 | if (this.raw_mods & Constants.Mods[mod]) 54 | this._mods.push(mod); 55 | 56 | 57 | return this._mods; 58 | } 59 | } 60 | 61 | module.exports = MultiplayerScore; 62 | -------------------------------------------------------------------------------- /lib/Base/Score.js: -------------------------------------------------------------------------------- 1 | const { getNumeric } = require('../utils.js'); 2 | const { Mods, AccuracyMethods } = require('../Constants.js'); 3 | 4 | /** 5 | * A score for a beatmap 6 | * @prop {String|Number} score 7 | * @prop {Object} user 8 | * @prop {?String} user.name Username of the player. Will be null if using a getUserX method 9 | * @prop {String} user.id 10 | * @prop {?String} beatmapId 11 | * @prop {Object} counts 12 | * @prop {String|Number} counts.300 13 | * @prop {String|Number} counts.100 14 | * @prop {String|Number} counts.50 15 | * @prop {String|Number} counts.geki 16 | * @prop {String|Number} counts.katu 17 | * @prop {String|Number} counts.miss 18 | * @prop {String|Number} maxCombo 19 | * @prop {Boolean} perfect 20 | * @prop {String} raw_date 21 | * @prop {String} rank 22 | * @prop {?String|?Number} pp 23 | * @prop {Boolean} hasReplay 24 | * @prop {Number} raw_mods bitwise representation of mods used 25 | * @prop {?Beatmap} beatmap 26 | * @prop {Date} date 27 | * @prop {String[]} mods 28 | * @prop {Number|undefined} accuracy The score's accuracy, if beatmap is defined, otherwise undefined 29 | */ 30 | class Score { 31 | constructor(config, data, beatmap) { 32 | const num = getNumeric(config.parseNumeric); 33 | 34 | this.score = num(data.score); 35 | this.user = { 36 | 'name': data.username || null, 37 | 'id': data.user_id 38 | }; 39 | this.beatmapId = data.beatmap_id || (beatmap ? beatmap.id : null); 40 | this.counts = { 41 | '300': num(data.count300), 42 | '100': num(data.count100), 43 | '50': num(data.count50), 44 | 'geki': num(data.countgeki), 45 | 'katu': num(data.countkatu), 46 | 'miss': num(data.countmiss) 47 | }; 48 | this.maxCombo = num(data.maxcombo); 49 | this.perfect = data.perfect === '1'; 50 | this.raw_date = data.date; 51 | this.rank = data.rank; 52 | this.pp = num(data.pp || null); 53 | this.hasReplay = data.replay_available === '1'; 54 | 55 | this.raw_mods = parseInt(data.enabled_mods); 56 | 57 | this._beatmap = beatmap; // Optional 58 | } 59 | 60 | get beatmap() { 61 | return this._beatmap; 62 | } 63 | 64 | set beatmap(beatmap) { 65 | this.beatmapId = beatmap.id; 66 | this._beatmap = beatmap; 67 | } 68 | 69 | get date() { 70 | if (this._date !== undefined) 71 | return this._date; 72 | 73 | this._date = new Date(this.raw_date + ' UTC'); 74 | return this._date; 75 | } 76 | 77 | get mods() { 78 | if (this._mods !== undefined) 79 | return this._mods; 80 | 81 | this._mods = []; 82 | for (const mod in Mods) 83 | if (this.raw_mods & Mods[mod]) 84 | this._mods.push(mod); 85 | 86 | return this._mods; 87 | } 88 | 89 | get accuracy() { 90 | if (!this.beatmap) 91 | return undefined; 92 | 93 | if (this._accuracy !== undefined) 94 | return this._accuracy; 95 | 96 | const intCounts = { }; 97 | for (const c in this.counts) 98 | intCounts[c] = parseInt(this.counts[c], 10); 99 | 100 | this._accuracy = AccuracyMethods[this.beatmap.mode](intCounts); 101 | return this._accuracy; 102 | } 103 | } 104 | 105 | module.exports = Score; 106 | -------------------------------------------------------------------------------- /lib/Base/User.js: -------------------------------------------------------------------------------- 1 | const { getNumeric } = require('../utils.js'); 2 | const Event = require('./Event.js'); 3 | 4 | /** 5 | * A user 6 | * @prop {String} id 7 | * @prop {String} name 8 | * @prop {Object} counts 9 | * @prop {String|Number} counts.300 10 | * @prop {String|Number} counts.100 11 | * @prop {String|Number} counts.50 12 | * @prop {String|Number} counts.SSH 13 | * @prop {String|Number} counts.SS 14 | * @prop {String|Number} counts.SH 15 | * @prop {String|Number} counts.S 16 | * @prop {String|Number} counts.A 17 | * @prop {String|Number} counts.plays 18 | * @prop {Object} scores 19 | * @prop {String|Number} scores.ranked 20 | * @prop {String|Number} scores.total 21 | * @prop {Object} pp 22 | * @prop {String|Number} pp.raw 23 | * @prop {String|Number} pp.rank 24 | * @prop {String|Number} pp.countryRank 25 | * @prop {String} country 26 | * @prop {String|Number} level 27 | * @prop {String|Number} accuracy 28 | * @prop {String|Number} secondsPlayed 29 | * @prop {String} raw_joinDate 30 | * @prop {Event[]} events 31 | * @prop {String} accuracyFormatted 32 | * @prop {Date} joinDate 33 | */ 34 | class User { 35 | constructor(config, data) { 36 | const num = getNumeric(config.parseNumeric); 37 | 38 | this.id = data.user_id; 39 | this.name = data.username; 40 | this.counts = { 41 | '300': num(data.count300), 42 | '100': num(data.count100), 43 | '50': num(data.count50), 44 | 'SSH': num(data.count_rank_ssh), 45 | 'SS': num(data.count_rank_ss), 46 | 'SH': num(data.count_rank_sh), 47 | 'S': num(data.count_rank_s), 48 | 'A': num(data.count_rank_a), 49 | 'plays': num(data.playcount) 50 | }; 51 | this.scores = { 52 | ranked: num(data.ranked_score), 53 | total: num(data.total_score) 54 | }; 55 | this.pp = { 56 | raw: num(data.pp_raw), 57 | rank: num(data.pp_rank), 58 | countryRank: num(data.pp_country_rank) 59 | }; 60 | this.country = data.country; 61 | this.level = num(data.level); 62 | this.accuracy = num(data.accuracy); 63 | this.secondsPlayed = num(data.total_seconds_played); 64 | this.raw_joinDate = data.join_date; 65 | 66 | this.events = data.events.map(ev => new Event(config, ev)); 67 | } 68 | 69 | get joinDate() { 70 | if (this._joinDate !== undefined) 71 | return this._joinDate; 72 | 73 | this._joinDate = new Date(this.raw_joinDate + ' UTC'); 74 | return this._joinDate; 75 | } 76 | 77 | get accuracyFormatted() { 78 | return parseFloat(this.accuracy).toFixed(2) + '%'; 79 | } 80 | 81 | } 82 | 83 | module.exports = User; 84 | -------------------------------------------------------------------------------- /lib/Constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /** 3 | * An enum of mods with their bitwise representation 4 | * @readonly 5 | * @enum {Number} 6 | */ 7 | Mods: { 8 | 'None' : 0, 9 | 'NoFail' : 1, 10 | 'Easy' : 1 << 1, 11 | 'TouchDevice' : 1 << 2, 12 | 'Hidden' : 1 << 3, 13 | 'HardRock' : 1 << 4, 14 | 'SuddenDeath' : 1 << 5, 15 | 'DoubleTime' : 1 << 6, 16 | 'Relax' : 1 << 7, 17 | 'HalfTime' : 1 << 8, 18 | 'Nightcore' : 1 << 9, // Triple speed 19 | 'Flashlight' : 1 << 10, 20 | 'Autoplay' : 1 << 11, 21 | 'SpunOut' : 1 << 12, 22 | 'Relax2' : 1 << 13, // Autopilot 23 | 'Perfect' : 1 << 14, // SuddenDeath mod 24 | 'Key4' : 1 << 15, 25 | 'Key5' : 1 << 16, 26 | 'Key6' : 1 << 17, 27 | 'Key7' : 1 << 18, 28 | 'Key8' : 1 << 19, 29 | 'FadeIn' : 1 << 20, 30 | 'Random' : 1 << 21, 31 | 'Cinema' : 1 << 22, 32 | 'Target' : 1 << 23, 33 | 'Key9' : 1 << 24, 34 | 'KeyCoop' : 1 << 25, 35 | 'Key1' : 1 << 26, 36 | 'Key3' : 1 << 27, 37 | 'Key2' : 1 << 28, 38 | 'ScoreV2' : 1 << 29, 39 | 'Mirror' : 1 << 30, 40 | 'KeyMod' : 521109504, 41 | 'FreeModAllowed': 522171579, 42 | 'ScoreIncreaseMods': 1049662 43 | }, 44 | /** An object containing functions to generate osu protocol URLs */ 45 | URLSchemas: { 46 | /** Joins a multiplayer match */ 47 | multiplayerMatch: (id, password) => `osu://mp/${id}${password !== undefined ? '/' + password : ''}`, 48 | /** Links to a certain part of a map in the editor */ 49 | edit: (position, objects) => `osu://edit/${position}${objects !== undefined ? ' ' + objects : ''}`, 50 | /** Joins a chat channel */ 51 | channel: name => `osu://chan/#${name}`, 52 | /** Downloads a beatmap in the game */ 53 | download: id => `osu://dl/${id}`, 54 | /** Spectates a player */ 55 | spectate: user => `osu://spectate/${user}` 56 | }, 57 | /** Enums for beatmaps */ 58 | Beatmaps: { 59 | /** 60 | * Approval states 61 | * @readonly 62 | * @enum {String} 63 | */ 64 | approved: { 65 | '-2': 'Graveyard', 66 | '-1': 'WIP', 67 | '0': 'Pending', 68 | '1': 'Ranked', 69 | '2': 'Approved', 70 | '3': 'Qualified', 71 | '4': 'Loved' 72 | }, 73 | /** 74 | * Song genres 75 | * @readonly 76 | * @enum {String} 77 | */ 78 | genre: { 79 | '0': 'Any', 80 | '1': 'Unspecified', 81 | '2': 'Video Game', 82 | '3': 'Anime', 83 | '4': 'Rock', 84 | '5': 'Pop', 85 | '6': 'Other', 86 | '7': 'Novelty', 87 | '9': 'Hip Hop', 88 | '10': 'Electronic' 89 | }, 90 | /** 91 | * Song languages 92 | * @readonly 93 | * @enum {String} 94 | */ 95 | language: { 96 | '0': 'Any', 97 | '1': 'Other', 98 | '2': 'English', 99 | '3': 'Japanese', 100 | '4': 'Chinese', 101 | '5': 'Instrumental', 102 | '6': 'Korean', 103 | '7': 'French', 104 | '8': 'German', 105 | '9': 'Swedish', 106 | '10': 'Spanish', 107 | '11': 'Italian' 108 | }, 109 | /** 110 | * Game modes 111 | * @readonly 112 | * @enum {String} 113 | */ 114 | mode: { 115 | '0': 'Standard', 116 | '1': 'Taiko', 117 | '2': 'Catch the Beat', 118 | '3': 'Mania' 119 | } 120 | }, 121 | /** Enums for multiplayer matches */ 122 | Multiplayer: { 123 | /** 124 | * Scoring types 125 | * @readonly 126 | * @enum {String} 127 | */ 128 | scoringType: { 129 | '0': 'Score', 130 | '1': 'Accuracy', 131 | '2': 'Combo', 132 | '3': 'Score v2' 133 | }, 134 | /** 135 | * Team setup 136 | * @readonly 137 | * @enum {String} 138 | */ 139 | teamType: { 140 | '0': 'Head to Head', 141 | '1': 'Tag Co-op', 142 | '2': 'Team vs', 143 | '3': 'Tag Team vs' 144 | }, 145 | /** 146 | * Team of a player 147 | * @readonly 148 | * @enum {String} 149 | */ 150 | team: { 151 | '0': 'None', 152 | '1': 'Blue', 153 | '2': 'Red' 154 | } 155 | }, 156 | /** Methods to calculate accuracy based on the game mode */ 157 | AccuracyMethods: { 158 | /** 159 | * Calculates accuracy based on hit counts for standard games 160 | * @param {Object} c Hit counts 161 | */ 162 | Standard: c => { 163 | const total = c['50'] + c['100'] + c['300'] + c.miss; 164 | return total === 0 ? 0 : ((c['300'] * 300 + c['100'] * 100 + c['50'] * 50) / (total * 300)); 165 | }, 166 | /** 167 | * Calculates accuracy based on hit counts for taiko games 168 | * @param {Object} c Hit counts 169 | */ 170 | Taiko: c => { 171 | const total = c['100'] + c['300'] + c.miss; 172 | return total === 0 ? 0 : (((c['300'] + c['100'] * .5) * 300) / (total * 300)); 173 | }, 174 | /** 175 | * Calculates accuracy based on hit counts for CtB games 176 | * @param {Object} c Hit counts 177 | */ 178 | 'Catch the Beat': c => { 179 | const total = c['50'] + c['100'] + c['300'] + c.katu + c.miss; 180 | return total === 0 ? 0 : ((c['50'] + c['100'] + c['300']) / total); 181 | }, 182 | /** 183 | * Calculates accuracy based on hit counts for mania games 184 | * @param {Object} c Hit counts 185 | */ 186 | Mania: c => { // (count50 * 50 + count100 * 100 + countKatu * 200 + (count300 + countGeki) * 300) / (totalHits * 300) 187 | const total = c['50'] + c['100'] + c['300'] + c.katu + c.geki + c.miss; 188 | return total === 0 ? 0 : ((c['50'] * 50 + c['100'] * 100 + c.katu * 200 + (c['300'] + c.geki) * 300) / (total * 300)); 189 | } 190 | } 191 | }; 192 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getNumeric(parseNumeric) { 3 | return parseNumeric 4 | ? v => v === undefined || v === null ? v : parseFloat(v) 5 | : v => v; 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-osu", 3 | "version": "2.2.1", 4 | "description": "A library for interacting with the osu api", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha -t 10000 --reporter spec" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/brussell98/node-osu.git" 12 | }, 13 | "keywords": [ 14 | "osu", 15 | "api", 16 | "promise", 17 | "async" 18 | ], 19 | "author": "brussell98", 20 | "license": "MIT", 21 | "types": "index.d.ts", 22 | "bugs": { 23 | "url": "https://github.com/brussell98/node-osu/issues" 24 | }, 25 | "homepage": "https://github.com/brussell98/node-osu#readme", 26 | "dependencies": { 27 | "superagent": "^5.2.1" 28 | }, 29 | "devDependencies": { 30 | "chai": "^3.5.0", 31 | "eslint": "^6.8.0", 32 | "mocha": "^3.0.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/sampleMatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "match": { 3 | "match_id": "57155016", 4 | "name": "OWC2019: (United States) vs (South Korea)", 5 | "start_time": "2019-12-22 02:48:47", 6 | "end_time": "2019-12-22 04:29:11" 7 | }, 8 | "games": [ 9 | { 10 | "game_id": "298226497", 11 | "start_time": "2019-12-22 03:01:55", 12 | "end_time": "2019-12-22 03:04:00", 13 | "beatmap_id": "1988434", 14 | "play_mode": "0", 15 | "match_type": "0", 16 | "scoring_type": "3", 17 | "team_type": "2", 18 | "mods": "0", 19 | "scores": [ 20 | { 21 | "slot": "0", 22 | "team": "2", 23 | "user_id": "4194445", 24 | "score": "353891", 25 | "maxcombo": "710", 26 | "rank": "0", 27 | "count50": "27", 28 | "count100": "73", 29 | "count300": "690", 30 | "countmiss": "38", 31 | "countgeki": "129", 32 | "countkatu": "38", 33 | "perfect": "0", 34 | "pass": "1", 35 | "enabled_mods": "1" 36 | }, 37 | { 38 | "slot": "1", 39 | "team": "2", 40 | "user_id": "3533958", 41 | "score": "206948", 42 | "maxcombo": "411", 43 | "rank": "0", 44 | "count50": "20", 45 | "count100": "82", 46 | "count300": "699", 47 | "countmiss": "27", 48 | "countgeki": "129", 49 | "countkatu": "42", 50 | "perfect": "0", 51 | "pass": "0", 52 | "enabled_mods": "0" 53 | }, 54 | { 55 | "slot": "2", 56 | "team": "2", 57 | "user_id": "4650315", 58 | "score": "291343", 59 | "maxcombo": "526", 60 | "rank": "0", 61 | "count50": "13", 62 | "count100": "67", 63 | "count300": "723", 64 | "countmiss": "25", 65 | "countgeki": "141", 66 | "countkatu": "41", 67 | "perfect": "0", 68 | "pass": "1", 69 | "enabled_mods": "1" 70 | }, 71 | { 72 | "slot": "3", 73 | "team": "2", 74 | "user_id": "2644828", 75 | "score": "379967", 76 | "maxcombo": "517", 77 | "rank": "0", 78 | "count50": "14", 79 | "count100": "60", 80 | "count300": "745", 81 | "countmiss": "9", 82 | "countgeki": "147", 83 | "countkatu": "38", 84 | "perfect": "0", 85 | "pass": "1", 86 | "enabled_mods": "1" 87 | }, 88 | { 89 | "slot": "4", 90 | "team": "1", 91 | "user_id": "1206417", 92 | "score": "125737", 93 | "maxcombo": "303", 94 | "rank": "0", 95 | "count50": "23", 96 | "count100": "112", 97 | "count300": "646", 98 | "countmiss": "47", 99 | "countgeki": "118", 100 | "countkatu": "38", 101 | "perfect": "0", 102 | "pass": "1", 103 | "enabled_mods": "1" 104 | }, 105 | { 106 | "slot": "5", 107 | "team": "1", 108 | "user_id": "1516650", 109 | "score": "157212", 110 | "maxcombo": "362", 111 | "rank": "0", 112 | "count50": "27", 113 | "count100": "109", 114 | "count300": "664", 115 | "countmiss": "28", 116 | "countgeki": "120", 117 | "countkatu": "48", 118 | "perfect": "0", 119 | "pass": "1", 120 | "enabled_mods": "4096" 121 | }, 122 | { 123 | "slot": "6", 124 | "team": "1", 125 | "user_id": "9224078", 126 | "score": "183446", 127 | "maxcombo": "249", 128 | "rank": "0", 129 | "count50": "16", 130 | "count100": "120", 131 | "count300": "677", 132 | "countmiss": "15", 133 | "countgeki": "115", 134 | "countkatu": "62", 135 | "perfect": "0", 136 | "pass": "1", 137 | "enabled_mods": "25" 138 | }, 139 | { 140 | "slot": "7", 141 | "team": "1", 142 | "user_id": "7892320", 143 | "score": "157952", 144 | "maxcombo": "364", 145 | "rank": "0", 146 | "count50": "27", 147 | "count100": "122", 148 | "count300": "629", 149 | "countmiss": "50", 150 | "countgeki": "104", 151 | "countkatu": "58", 152 | "perfect": "0", 153 | "pass": "1", 154 | "enabled_mods": "25" 155 | } 156 | ] 157 | }, 158 | { 159 | "game_id": "298226843", 160 | "start_time": "2019-12-22 03:06:24", 161 | "end_time": "2019-12-22 03:10:46", 162 | "beatmap_id": "2263301", 163 | "play_mode": "0", 164 | "match_type": "0", 165 | "scoring_type": "3", 166 | "team_type": "2", 167 | "mods": "0", 168 | "scores": [ 169 | { 170 | "slot": "0", 171 | "team": "2", 172 | "user_id": "4194445", 173 | "score": "59349", 174 | "maxcombo": "260", 175 | "rank": "0", 176 | "count50": "53", 177 | "count100": "110", 178 | "count300": "146", 179 | "countmiss": "188", 180 | "countgeki": "35", 181 | "countkatu": "43", 182 | "perfect": "0", 183 | "pass": "1", 184 | "enabled_mods": "1" 185 | }, 186 | { 187 | "slot": "1", 188 | "team": "2", 189 | "user_id": "3533958", 190 | "score": "79935", 191 | "maxcombo": "323", 192 | "rank": "0", 193 | "count50": "64", 194 | "count100": "106", 195 | "count300": "169", 196 | "countmiss": "158", 197 | "countgeki": "50", 198 | "countkatu": "40", 199 | "perfect": "0", 200 | "pass": "1", 201 | "enabled_mods": "1" 202 | }, 203 | { 204 | "slot": "2", 205 | "team": "2", 206 | "user_id": "4650315", 207 | "score": "66586", 208 | "maxcombo": "261", 209 | "rank": "0", 210 | "count50": "66", 211 | "count100": "103", 212 | "count300": "170", 213 | "countmiss": "158", 214 | "countgeki": "37", 215 | "countkatu": "52", 216 | "perfect": "0", 217 | "pass": "1", 218 | "enabled_mods": "1" 219 | }, 220 | { 221 | "slot": "3", 222 | "team": "2", 223 | "user_id": "2644828", 224 | "score": "66018", 225 | "maxcombo": "317", 226 | "rank": "0", 227 | "count50": "39", 228 | "count100": "24", 229 | "count300": "131", 230 | "countmiss": "303", 231 | "countgeki": "47", 232 | "countkatu": "11", 233 | "perfect": "0", 234 | "pass": "1", 235 | "enabled_mods": "1" 236 | }, 237 | { 238 | "slot": "4", 239 | "team": "1", 240 | "user_id": "1206417", 241 | "score": "75598", 242 | "maxcombo": "326", 243 | "rank": "0", 244 | "count50": "55", 245 | "count100": "100", 246 | "count300": "150", 247 | "countmiss": "192", 248 | "countgeki": "44", 249 | "countkatu": "35", 250 | "perfect": "0", 251 | "pass": "1", 252 | "enabled_mods": "1" 253 | }, 254 | { 255 | "slot": "5", 256 | "team": "1", 257 | "user_id": "1516650", 258 | "score": "28667", 259 | "maxcombo": "113", 260 | "rank": "0", 261 | "count50": "83", 262 | "count100": "105", 263 | "count300": "150", 264 | "countmiss": "159", 265 | "countgeki": "34", 266 | "countkatu": "47", 267 | "perfect": "0", 268 | "pass": "1", 269 | "enabled_mods": "4097" 270 | }, 271 | { 272 | "slot": "6", 273 | "team": "1", 274 | "user_id": "9224078", 275 | "score": "81738", 276 | "maxcombo": "326", 277 | "rank": "0", 278 | "count50": "99", 279 | "count100": "93", 280 | "count300": "176", 281 | "countmiss": "129", 282 | "countgeki": "47", 283 | "countkatu": "38", 284 | "perfect": "0", 285 | "pass": "1", 286 | "enabled_mods": "1" 287 | }, 288 | { 289 | "slot": "7", 290 | "team": "1", 291 | "user_id": "7892320", 292 | "score": "58435", 293 | "maxcombo": "269", 294 | "rank": "0", 295 | "count50": "59", 296 | "count100": "148", 297 | "count300": "162", 298 | "countmiss": "128", 299 | "countgeki": "28", 300 | "countkatu": "52", 301 | "perfect": "0", 302 | "pass": "1", 303 | "enabled_mods": "1" 304 | } 305 | ] 306 | }, 307 | { 308 | "game_id": "298228132", 309 | "start_time": "2019-12-22 03:24:00", 310 | "end_time": "2019-12-22 03:26:27", 311 | "beatmap_id": "2012009", 312 | "play_mode": "0", 313 | "match_type": "0", 314 | "scoring_type": "3", 315 | "team_type": "2", 316 | "mods": "0", 317 | "scores": [ 318 | { 319 | "slot": "0", 320 | "team": "2", 321 | "user_id": "4650315", 322 | "score": "544696", 323 | "maxcombo": "883", 324 | "rank": "0", 325 | "count50": "4", 326 | "count100": "40", 327 | "count300": "997", 328 | "countmiss": "13", 329 | "countgeki": "278", 330 | "countkatu": "15", 331 | "perfect": "0", 332 | "pass": "1", 333 | "enabled_mods": "1" 334 | }, 335 | { 336 | "slot": "1", 337 | "team": "2", 338 | "user_id": "3533958", 339 | "score": "337531", 340 | "maxcombo": "731", 341 | "rank": "0", 342 | "count50": "7", 343 | "count100": "97", 344 | "count300": "945", 345 | "countmiss": "5", 346 | "countgeki": "247", 347 | "countkatu": "43", 348 | "perfect": "0", 349 | "pass": "1", 350 | "enabled_mods": "1" 351 | }, 352 | { 353 | "slot": "2", 354 | "team": "2", 355 | "user_id": "1650010", 356 | "score": "395467", 357 | "maxcombo": "609", 358 | "rank": "0", 359 | "count50": "9", 360 | "count100": "34", 361 | "count300": "1005", 362 | "countmiss": "6", 363 | "countgeki": "275", 364 | "countkatu": "15", 365 | "perfect": "0", 366 | "pass": "1", 367 | "enabled_mods": "1" 368 | }, 369 | { 370 | "slot": "3", 371 | "team": "2", 372 | "user_id": "2644828", 373 | "score": "439544", 374 | "maxcombo": "566", 375 | "rank": "0", 376 | "count50": "6", 377 | "count100": "15", 378 | "count300": "1026", 379 | "countmiss": "7", 380 | "countgeki": "280", 381 | "countkatu": "13", 382 | "perfect": "0", 383 | "pass": "1", 384 | "enabled_mods": "1" 385 | }, 386 | { 387 | "slot": "4", 388 | "team": "1", 389 | "user_id": "1516650", 390 | "score": "304975", 391 | "maxcombo": "554", 392 | "rank": "0", 393 | "count50": "15", 394 | "count100": "51", 395 | "count300": "976", 396 | "countmiss": "12", 397 | "countgeki": "253", 398 | "countkatu": "31", 399 | "perfect": "0", 400 | "pass": "1", 401 | "enabled_mods": "1" 402 | }, 403 | { 404 | "slot": "5", 405 | "team": "1", 406 | "user_id": "3478883", 407 | "score": "311774", 408 | "maxcombo": "447", 409 | "rank": "0", 410 | "count50": "11", 411 | "count100": "53", 412 | "count300": "981", 413 | "countmiss": "9", 414 | "countgeki": "256", 415 | "countkatu": "31", 416 | "perfect": "0", 417 | "pass": "1", 418 | "enabled_mods": "1" 419 | }, 420 | { 421 | "slot": "6", 422 | "team": "1", 423 | "user_id": "9224078", 424 | "score": "407475", 425 | "maxcombo": "601", 426 | "rank": "0", 427 | "count50": "8", 428 | "count100": "37", 429 | "count300": "1002", 430 | "countmiss": "7", 431 | "countgeki": "264", 432 | "countkatu": "24", 433 | "perfect": "0", 434 | "pass": "1", 435 | "enabled_mods": "1" 436 | }, 437 | { 438 | "slot": "7", 439 | "team": "1", 440 | "user_id": "7892320", 441 | "score": "247429", 442 | "maxcombo": "525", 443 | "rank": "0", 444 | "count50": "9", 445 | "count100": "76", 446 | "count300": "943", 447 | "countmiss": "26", 448 | "countgeki": "247", 449 | "countkatu": "35", 450 | "perfect": "0", 451 | "pass": "1", 452 | "enabled_mods": "1" 453 | } 454 | ] 455 | }, 456 | { 457 | "game_id": "298228615", 458 | "start_time": "2019-12-22 03:30:16", 459 | "end_time": "2019-12-22 03:32:50", 460 | "beatmap_id": "1512325", 461 | "play_mode": "0", 462 | "match_type": "0", 463 | "scoring_type": "3", 464 | "team_type": "2", 465 | "mods": "0", 466 | "scores": [ 467 | { 468 | "slot": "0", 469 | "team": "2", 470 | "user_id": "4650315", 471 | "score": "476405", 472 | "maxcombo": "648", 473 | "rank": "0", 474 | "count50": "6", 475 | "count100": "85", 476 | "count300": "547", 477 | "countmiss": "4", 478 | "countgeki": "142", 479 | "countkatu": "60", 480 | "perfect": "0", 481 | "pass": "1", 482 | "enabled_mods": "17" 483 | }, 484 | { 485 | "slot": "1", 486 | "team": "2", 487 | "user_id": "3533958", 488 | "score": "272316", 489 | "maxcombo": "261", 490 | "rank": "0", 491 | "count50": "8", 492 | "count100": "47", 493 | "count300": "570", 494 | "countmiss": "17", 495 | "countgeki": "161", 496 | "countkatu": "35", 497 | "perfect": "0", 498 | "pass": "1", 499 | "enabled_mods": "17" 500 | }, 501 | { 502 | "slot": "2", 503 | "team": "2", 504 | "user_id": "1650010", 505 | "score": "384575", 506 | "maxcombo": "482", 507 | "rank": "0", 508 | "count50": "6", 509 | "count100": "80", 510 | "count300": "552", 511 | "countmiss": "4", 512 | "countgeki": "144", 513 | "countkatu": "58", 514 | "perfect": "0", 515 | "pass": "1", 516 | "enabled_mods": "17" 517 | }, 518 | { 519 | "slot": "3", 520 | "team": "2", 521 | "user_id": "4787150", 522 | "score": "569247", 523 | "maxcombo": "598", 524 | "rank": "0", 525 | "count50": "2", 526 | "count100": "41", 527 | "count300": "596", 528 | "countmiss": "3", 529 | "countgeki": "172", 530 | "countkatu": "33", 531 | "perfect": "0", 532 | "pass": "1", 533 | "enabled_mods": "17" 534 | }, 535 | { 536 | "slot": "4", 537 | "team": "1", 538 | "user_id": "8775024", 539 | "score": "361676", 540 | "maxcombo": "440", 541 | "rank": "0", 542 | "count50": "5", 543 | "count100": "82", 544 | "count300": "545", 545 | "countmiss": "10", 546 | "countgeki": "155", 547 | "countkatu": "47", 548 | "perfect": "0", 549 | "pass": "1", 550 | "enabled_mods": "17" 551 | }, 552 | { 553 | "slot": "5", 554 | "team": "1", 555 | "user_id": "3478883", 556 | "score": "387746", 557 | "maxcombo": "531", 558 | "rank": "0", 559 | "count50": "3", 560 | "count100": "92", 561 | "count300": "543", 562 | "countmiss": "4", 563 | "countgeki": "150", 564 | "countkatu": "55", 565 | "perfect": "0", 566 | "pass": "1", 567 | "enabled_mods": "17" 568 | }, 569 | { 570 | "slot": "6", 571 | "team": "1", 572 | "user_id": "9224078", 573 | "score": "296947", 574 | "maxcombo": "312", 575 | "rank": "0", 576 | "count50": "3", 577 | "count100": "109", 578 | "count300": "527", 579 | "countmiss": "3", 580 | "countgeki": "140", 581 | "countkatu": "65", 582 | "perfect": "0", 583 | "pass": "1", 584 | "enabled_mods": "17" 585 | }, 586 | { 587 | "slot": "7", 588 | "team": "1", 589 | "user_id": "7892320", 590 | "score": "206844", 591 | "maxcombo": "278", 592 | "rank": "0", 593 | "count50": "14", 594 | "count100": "127", 595 | "count300": "494", 596 | "countmiss": "7", 597 | "countgeki": "126", 598 | "countkatu": "69", 599 | "perfect": "0", 600 | "pass": "1", 601 | "enabled_mods": "17" 602 | } 603 | ] 604 | }, 605 | { 606 | "game_id": "298229191", 607 | "start_time": "2019-12-22 03:38:04", 608 | "end_time": "2019-12-22 03:41:38", 609 | "beatmap_id": "1908543", 610 | "play_mode": "0", 611 | "match_type": "0", 612 | "scoring_type": "3", 613 | "team_type": "2", 614 | "mods": "0", 615 | "scores": [ 616 | { 617 | "slot": "0", 618 | "team": "2", 619 | "user_id": "4650315", 620 | "score": "682103", 621 | "maxcombo": "879", 622 | "rank": "0", 623 | "count50": "0", 624 | "count100": "21", 625 | "count300": "1058", 626 | "countmiss": "9", 627 | "countgeki": "231", 628 | "countkatu": "15", 629 | "perfect": "0", 630 | "pass": "1", 631 | "enabled_mods": "17" 632 | }, 633 | { 634 | "slot": "1", 635 | "team": "2", 636 | "user_id": "4194445", 637 | "score": "585958", 638 | "maxcombo": "857", 639 | "rank": "0", 640 | "count50": "2", 641 | "count100": "12", 642 | "count300": "1070", 643 | "countmiss": "4", 644 | "countgeki": "235", 645 | "countkatu": "10", 646 | "perfect": "0", 647 | "pass": "1", 648 | "enabled_mods": "1" 649 | }, 650 | { 651 | "slot": "2", 652 | "team": "2", 653 | "user_id": "2757689", 654 | "score": "571640", 655 | "maxcombo": "722", 656 | "rank": "0", 657 | "count50": "2", 658 | "count100": "19", 659 | "count300": "1063", 660 | "countmiss": "4", 661 | "countgeki": "231", 662 | "countkatu": "13", 663 | "perfect": "0", 664 | "pass": "1", 665 | "enabled_mods": "9" 666 | }, 667 | { 668 | "slot": "3", 669 | "team": "2", 670 | "user_id": "4787150", 671 | "score": "999048", 672 | "maxcombo": "1378", 673 | "rank": "0", 674 | "count50": "0", 675 | "count100": "14", 676 | "count300": "1074", 677 | "countmiss": "0", 678 | "countgeki": "238", 679 | "countkatu": "12", 680 | "perfect": "0", 681 | "pass": "1", 682 | "enabled_mods": "1" 683 | }, 684 | { 685 | "slot": "4", 686 | "team": "1", 687 | "user_id": "8775024", 688 | "score": "424714", 689 | "maxcombo": "517", 690 | "rank": "0", 691 | "count50": "4", 692 | "count100": "38", 693 | "count300": "1037", 694 | "countmiss": "9", 695 | "countgeki": "221", 696 | "countkatu": "20", 697 | "perfect": "0", 698 | "pass": "1", 699 | "enabled_mods": "9" 700 | }, 701 | { 702 | "slot": "5", 703 | "team": "1", 704 | "user_id": "3478883", 705 | "score": "619025", 706 | "maxcombo": "924", 707 | "rank": "0", 708 | "count50": "1", 709 | "count100": "44", 710 | "count300": "1040", 711 | "countmiss": "3", 712 | "countgeki": "219", 713 | "countkatu": "29", 714 | "perfect": "0", 715 | "pass": "1", 716 | "enabled_mods": "1" 717 | }, 718 | { 719 | "slot": "6", 720 | "team": "1", 721 | "user_id": "9224078", 722 | "score": "553613", 723 | "maxcombo": "863", 724 | "rank": "0", 725 | "count50": "5", 726 | "count100": "64", 727 | "count300": "1010", 728 | "countmiss": "9", 729 | "countgeki": "200", 730 | "countkatu": "40", 731 | "perfect": "0", 732 | "pass": "1", 733 | "enabled_mods": "25" 734 | }, 735 | { 736 | "slot": "7", 737 | "team": "1", 738 | "user_id": "1777162", 739 | "score": "424033", 740 | "maxcombo": "533", 741 | "rank": "0", 742 | "count50": "0", 743 | "count100": "29", 744 | "count300": "1054", 745 | "countmiss": "5", 746 | "countgeki": "221", 747 | "countkatu": "25", 748 | "perfect": "0", 749 | "pass": "1", 750 | "enabled_mods": "1" 751 | } 752 | ] 753 | }, 754 | { 755 | "game_id": "298229724", 756 | "start_time": "2019-12-22 03:45:02", 757 | "end_time": "2019-12-22 03:47:21", 758 | "beatmap_id": "2256378", 759 | "play_mode": "0", 760 | "match_type": "0", 761 | "scoring_type": "3", 762 | "team_type": "2", 763 | "mods": "0", 764 | "scores": [ 765 | { 766 | "slot": "0", 767 | "team": "2", 768 | "user_id": "4650315", 769 | "score": "408444", 770 | "maxcombo": "220", 771 | "rank": "0", 772 | "count50": "1", 773 | "count100": "28", 774 | "count300": "621", 775 | "countmiss": "9", 776 | "countgeki": "85", 777 | "countkatu": "17", 778 | "perfect": "0", 779 | "pass": "1", 780 | "enabled_mods": "17" 781 | }, 782 | { 783 | "slot": "1", 784 | "team": "2", 785 | "user_id": "3533958", 786 | "score": "573047", 787 | "maxcombo": "464", 788 | "rank": "0", 789 | "count50": "0", 790 | "count100": "6", 791 | "count300": "650", 792 | "countmiss": "3", 793 | "countgeki": "98", 794 | "countkatu": "6", 795 | "perfect": "0", 796 | "pass": "1", 797 | "enabled_mods": "1" 798 | }, 799 | { 800 | "slot": "2", 801 | "team": "2", 802 | "user_id": "2757689", 803 | "score": "613320", 804 | "maxcombo": "568", 805 | "rank": "0", 806 | "count50": "2", 807 | "count100": "7", 808 | "count300": "632", 809 | "countmiss": "18", 810 | "countgeki": "94", 811 | "countkatu": "4", 812 | "perfect": "0", 813 | "pass": "1", 814 | "enabled_mods": "9" 815 | }, 816 | { 817 | "slot": "3", 818 | "team": "2", 819 | "user_id": "4787150", 820 | "score": "655232", 821 | "maxcombo": "478", 822 | "rank": "0", 823 | "count50": "1", 824 | "count100": "5", 825 | "count300": "653", 826 | "countmiss": "0", 827 | "countgeki": "101", 828 | "countkatu": "5", 829 | "perfect": "0", 830 | "pass": "1", 831 | "enabled_mods": "1" 832 | }, 833 | { 834 | "slot": "4", 835 | "team": "1", 836 | "user_id": "8775024", 837 | "score": "571150", 838 | "maxcombo": "483", 839 | "rank": "0", 840 | "count50": "9", 841 | "count100": "12", 842 | "count300": "631", 843 | "countmiss": "7", 844 | "countgeki": "99", 845 | "countkatu": "3", 846 | "perfect": "0", 847 | "pass": "1", 848 | "enabled_mods": "9" 849 | }, 850 | { 851 | "slot": "5", 852 | "team": "1", 853 | "user_id": "3478883", 854 | "score": "488028", 855 | "maxcombo": "340", 856 | "rank": "0", 857 | "count50": "1", 858 | "count100": "26", 859 | "count300": "629", 860 | "countmiss": "3", 861 | "countgeki": "88", 862 | "countkatu": "15", 863 | "perfect": "0", 864 | "pass": "1", 865 | "enabled_mods": "17" 866 | }, 867 | { 868 | "slot": "6", 869 | "team": "1", 870 | "user_id": "9224078", 871 | "score": "520431", 872 | "maxcombo": "482", 873 | "rank": "0", 874 | "count50": "8", 875 | "count100": "21", 876 | "count300": "630", 877 | "countmiss": "0", 878 | "countgeki": "90", 879 | "countkatu": "13", 880 | "perfect": "0", 881 | "pass": "1", 882 | "enabled_mods": "1" 883 | }, 884 | { 885 | "slot": "7", 886 | "team": "1", 887 | "user_id": "1206417", 888 | "score": "433000", 889 | "maxcombo": "293", 890 | "rank": "0", 891 | "count50": "3", 892 | "count100": "8", 893 | "count300": "641", 894 | "countmiss": "7", 895 | "countgeki": "97", 896 | "countkatu": "3", 897 | "perfect": "0", 898 | "pass": "1", 899 | "enabled_mods": "1" 900 | } 901 | ] 902 | }, 903 | { 904 | "game_id": "298230146", 905 | "start_time": "2019-12-22 03:50:44", 906 | "end_time": "2019-12-22 03:54:49", 907 | "beatmap_id": "499839", 908 | "play_mode": "0", 909 | "match_type": "0", 910 | "scoring_type": "3", 911 | "team_type": "2", 912 | "mods": "0", 913 | "scores": [ 914 | { 915 | "slot": "0", 916 | "team": "2", 917 | "user_id": "4650315", 918 | "score": "1039002", 919 | "maxcombo": "1514", 920 | "rank": "0", 921 | "count50": "0", 922 | "count100": "23", 923 | "count300": "958", 924 | "countmiss": "0", 925 | "countgeki": "155", 926 | "countkatu": "20", 927 | "perfect": "0", 928 | "pass": "1", 929 | "enabled_mods": "17" 930 | }, 931 | { 932 | "slot": "1", 933 | "team": "2", 934 | "user_id": "3533958", 935 | "score": "986467", 936 | "maxcombo": "1463", 937 | "rank": "0", 938 | "count50": "1", 939 | "count100": "26", 940 | "count300": "954", 941 | "countmiss": "0", 942 | "countgeki": "154", 943 | "countkatu": "20", 944 | "perfect": "0", 945 | "pass": "1", 946 | "enabled_mods": "17" 947 | }, 948 | { 949 | "slot": "2", 950 | "team": "2", 951 | "user_id": "2757689", 952 | "score": "450879", 953 | "maxcombo": "495", 954 | "rank": "0", 955 | "count50": "4", 956 | "count100": "16", 957 | "count300": "951", 958 | "countmiss": "10", 959 | "countgeki": "153", 960 | "countkatu": "14", 961 | "perfect": "0", 962 | "pass": "1", 963 | "enabled_mods": "17" 964 | }, 965 | { 966 | "slot": "3", 967 | "team": "2", 968 | "user_id": "4787150", 969 | "score": "465843", 970 | "maxcombo": "652", 971 | "rank": "0", 972 | "count50": "2", 973 | "count100": "46", 974 | "count300": "931", 975 | "countmiss": "2", 976 | "countgeki": "140", 977 | "countkatu": "31", 978 | "perfect": "0", 979 | "pass": "1", 980 | "enabled_mods": "17" 981 | }, 982 | { 983 | "slot": "4", 984 | "team": "1", 985 | "user_id": "7892320", 986 | "score": "721062", 987 | "maxcombo": "1203", 988 | "rank": "0", 989 | "count50": "4", 990 | "count100": "39", 991 | "count300": "935", 992 | "countmiss": "3", 993 | "countgeki": "139", 994 | "countkatu": "30", 995 | "perfect": "0", 996 | "pass": "1", 997 | "enabled_mods": "17" 998 | }, 999 | { 1000 | "slot": "5", 1001 | "team": "1", 1002 | "user_id": "3478883", 1003 | "score": "870595", 1004 | "maxcombo": "1419", 1005 | "rank": "0", 1006 | "count50": "1", 1007 | "count100": "58", 1008 | "count300": "920", 1009 | "countmiss": "2", 1010 | "countgeki": "132", 1011 | "countkatu": "42", 1012 | "perfect": "0", 1013 | "pass": "1", 1014 | "enabled_mods": "17" 1015 | }, 1016 | { 1017 | "slot": "6", 1018 | "team": "1", 1019 | "user_id": "9224078", 1020 | "score": "467571", 1021 | "maxcombo": "710", 1022 | "rank": "0", 1023 | "count50": "4", 1024 | "count100": "48", 1025 | "count300": "925", 1026 | "countmiss": "4", 1027 | "countgeki": "133", 1028 | "countkatu": "37", 1029 | "perfect": "0", 1030 | "pass": "1", 1031 | "enabled_mods": "17" 1032 | }, 1033 | { 1034 | "slot": "7", 1035 | "team": "1", 1036 | "user_id": "1206417", 1037 | "score": "226153", 1038 | "maxcombo": "213", 1039 | "rank": "0", 1040 | "count50": "10", 1041 | "count100": "78", 1042 | "count300": "877", 1043 | "countmiss": "16", 1044 | "countgeki": "120", 1045 | "countkatu": "38", 1046 | "perfect": "0", 1047 | "pass": "1", 1048 | "enabled_mods": "17" 1049 | } 1050 | ] 1051 | }, 1052 | { 1053 | "game_id": "298230665", 1054 | "start_time": "2019-12-22 03:57:20", 1055 | "end_time": "2019-12-22 04:00:19", 1056 | "beatmap_id": "1656914", 1057 | "play_mode": "0", 1058 | "match_type": "0", 1059 | "scoring_type": "3", 1060 | "team_type": "2", 1061 | "mods": "64", 1062 | "scores": [ 1063 | { 1064 | "slot": "0", 1065 | "team": "2", 1066 | "user_id": "4650315", 1067 | "score": "1044199", 1068 | "maxcombo": "1335", 1069 | "rank": "0", 1070 | "count50": "4", 1071 | "count100": "62", 1072 | "count300": "826", 1073 | "countmiss": "0", 1074 | "countgeki": "187", 1075 | "countkatu": "48", 1076 | "perfect": "0", 1077 | "pass": "1", 1078 | "enabled_mods": "65" 1079 | }, 1080 | { 1081 | "slot": "1", 1082 | "team": "2", 1083 | "user_id": "2757689", 1084 | "score": "512432", 1085 | "maxcombo": "412", 1086 | "rank": "0", 1087 | "count50": "3", 1088 | "count100": "32", 1089 | "count300": "856", 1090 | "countmiss": "1", 1091 | "countgeki": "209", 1092 | "countkatu": "24", 1093 | "perfect": "0", 1094 | "pass": "1", 1095 | "enabled_mods": "65" 1096 | }, 1097 | { 1098 | "slot": "2", 1099 | "team": "2", 1100 | "user_id": "2644828", 1101 | "score": "712852", 1102 | "maxcombo": "871", 1103 | "rank": "0", 1104 | "count50": "7", 1105 | "count100": "38", 1106 | "count300": "847", 1107 | "countmiss": "0", 1108 | "countgeki": "204", 1109 | "countkatu": "30", 1110 | "perfect": "0", 1111 | "pass": "1", 1112 | "enabled_mods": "65" 1113 | }, 1114 | { 1115 | "slot": "3", 1116 | "team": "2", 1117 | "user_id": "4787150", 1118 | "score": "1174546", 1119 | "maxcombo": "1338", 1120 | "rank": "0", 1121 | "count50": "0", 1122 | "count100": "13", 1123 | "count300": "879", 1124 | "countmiss": "0", 1125 | "countgeki": "225", 1126 | "countkatu": "12", 1127 | "perfect": "0", 1128 | "pass": "1", 1129 | "enabled_mods": "65" 1130 | }, 1131 | { 1132 | "slot": "4", 1133 | "team": "1", 1134 | "user_id": "7892320", 1135 | "score": "394600", 1136 | "maxcombo": "591", 1137 | "rank": "0", 1138 | "count50": "7", 1139 | "count100": "131", 1140 | "count300": "751", 1141 | "countmiss": "3", 1142 | "countgeki": "146", 1143 | "countkatu": "82", 1144 | "perfect": "0", 1145 | "pass": "1", 1146 | "enabled_mods": "65" 1147 | }, 1148 | { 1149 | "slot": "5", 1150 | "team": "1", 1151 | "user_id": "3478883", 1152 | "score": "574598", 1153 | "maxcombo": "802", 1154 | "rank": "0", 1155 | "count50": "5", 1156 | "count100": "103", 1157 | "count300": "782", 1158 | "countmiss": "2", 1159 | "countgeki": "162", 1160 | "countkatu": "70", 1161 | "perfect": "0", 1162 | "pass": "1", 1163 | "enabled_mods": "65" 1164 | }, 1165 | { 1166 | "slot": "6", 1167 | "team": "1", 1168 | "user_id": "9224078", 1169 | "score": "1020969", 1170 | "maxcombo": "1339", 1171 | "rank": "0", 1172 | "count50": "1", 1173 | "count100": "73", 1174 | "count300": "818", 1175 | "countmiss": "0", 1176 | "countgeki": "179", 1177 | "countkatu": "57", 1178 | "perfect": "0", 1179 | "pass": "1", 1180 | "enabled_mods": "65" 1181 | }, 1182 | { 1183 | "slot": "7", 1184 | "team": "1", 1185 | "user_id": "1516650", 1186 | "score": "593590", 1187 | "maxcombo": "874", 1188 | "rank": "0", 1189 | "count50": "3", 1190 | "count100": "80", 1191 | "count300": "809", 1192 | "countmiss": "0", 1193 | "countgeki": "179", 1194 | "countkatu": "55", 1195 | "perfect": "0", 1196 | "pass": "1", 1197 | "enabled_mods": "65" 1198 | } 1199 | ] 1200 | }, 1201 | { 1202 | "game_id": "298231213", 1203 | "start_time": "2019-12-22 04:04:17", 1204 | "end_time": "2019-12-22 04:08:07", 1205 | "beatmap_id": "2250670", 1206 | "play_mode": "0", 1207 | "match_type": "0", 1208 | "scoring_type": "3", 1209 | "team_type": "2", 1210 | "mods": "0", 1211 | "scores": [ 1212 | { 1213 | "slot": "0", 1214 | "team": "2", 1215 | "user_id": "4650315", 1216 | "score": "621207", 1217 | "maxcombo": "773", 1218 | "rank": "0", 1219 | "count50": "0", 1220 | "count100": "11", 1221 | "count300": "1195", 1222 | "countmiss": "5", 1223 | "countgeki": "280", 1224 | "countkatu": "9", 1225 | "perfect": "0", 1226 | "pass": "1", 1227 | "enabled_mods": "1" 1228 | }, 1229 | { 1230 | "slot": "1", 1231 | "team": "2", 1232 | "user_id": "1650010", 1233 | "score": "509386", 1234 | "maxcombo": "697", 1235 | "rank": "0", 1236 | "count50": "2", 1237 | "count100": "6", 1238 | "count300": "1194", 1239 | "countmiss": "9", 1240 | "countgeki": "279", 1241 | "countkatu": "5", 1242 | "perfect": "0", 1243 | "pass": "1", 1244 | "enabled_mods": "1" 1245 | }, 1246 | { 1247 | "slot": "2", 1248 | "team": "2", 1249 | "user_id": "4384207", 1250 | "score": "337794", 1251 | "maxcombo": "494", 1252 | "rank": "0", 1253 | "count50": "7", 1254 | "count100": "9", 1255 | "count300": "1168", 1256 | "countmiss": "27", 1257 | "countgeki": "266", 1258 | "countkatu": "5", 1259 | "perfect": "0", 1260 | "pass": "1", 1261 | "enabled_mods": "1" 1262 | }, 1263 | { 1264 | "slot": "3", 1265 | "team": "2", 1266 | "user_id": "4787150", 1267 | "score": "825357", 1268 | "maxcombo": "1341", 1269 | "rank": "0", 1270 | "count50": "1", 1271 | "count100": "6", 1272 | "count300": "1203", 1273 | "countmiss": "1", 1274 | "countgeki": "282", 1275 | "countkatu": "6", 1276 | "perfect": "0", 1277 | "pass": "1", 1278 | "enabled_mods": "1" 1279 | }, 1280 | { 1281 | "slot": "4", 1282 | "team": "1", 1283 | "user_id": "7892320", 1284 | "score": "538465", 1285 | "maxcombo": "755", 1286 | "rank": "0", 1287 | "count50": "1", 1288 | "count100": "10", 1289 | "count300": "1195", 1290 | "countmiss": "5", 1291 | "countgeki": "279", 1292 | "countkatu": "8", 1293 | "perfect": "0", 1294 | "pass": "1", 1295 | "enabled_mods": "1" 1296 | }, 1297 | { 1298 | "slot": "5", 1299 | "team": "1", 1300 | "user_id": "1777162", 1301 | "score": "520741", 1302 | "maxcombo": "747", 1303 | "rank": "0", 1304 | "count50": "1", 1305 | "count100": "4", 1306 | "count300": "1202", 1307 | "countmiss": "4", 1308 | "countgeki": "282", 1309 | "countkatu": "4", 1310 | "perfect": "0", 1311 | "pass": "1", 1312 | "enabled_mods": "1" 1313 | }, 1314 | { 1315 | "slot": "6", 1316 | "team": "1", 1317 | "user_id": "9224078", 1318 | "score": "641546", 1319 | "maxcombo": "851", 1320 | "rank": "0", 1321 | "count50": "0", 1322 | "count100": "8", 1323 | "count300": "1202", 1324 | "countmiss": "1", 1325 | "countgeki": "281", 1326 | "countkatu": "8", 1327 | "perfect": "0", 1328 | "pass": "1", 1329 | "enabled_mods": "1" 1330 | }, 1331 | { 1332 | "slot": "7", 1333 | "team": "1", 1334 | "user_id": "8775024", 1335 | "score": "558022", 1336 | "maxcombo": "780", 1337 | "rank": "0", 1338 | "count50": "1", 1339 | "count100": "4", 1340 | "count300": "1187", 1341 | "countmiss": "19", 1342 | "countgeki": "278", 1343 | "countkatu": "4", 1344 | "perfect": "0", 1345 | "pass": "1", 1346 | "enabled_mods": "1" 1347 | } 1348 | ] 1349 | }, 1350 | { 1351 | "game_id": "298232308", 1352 | "start_time": "2019-12-22 04:19:00", 1353 | "end_time": "2019-12-22 04:24:13", 1354 | "beatmap_id": "2256387", 1355 | "play_mode": "0", 1356 | "match_type": "0", 1357 | "scoring_type": "3", 1358 | "team_type": "2", 1359 | "mods": "0", 1360 | "scores": [ 1361 | { 1362 | "slot": "0", 1363 | "team": "2", 1364 | "user_id": "4650315", 1365 | "score": "355895", 1366 | "maxcombo": "831", 1367 | "rank": "0", 1368 | "count50": "10", 1369 | "count100": "53", 1370 | "count300": "1633", 1371 | "countmiss": "21", 1372 | "countgeki": "346", 1373 | "countkatu": "30", 1374 | "perfect": "0", 1375 | "pass": "1", 1376 | "enabled_mods": "1" 1377 | }, 1378 | { 1379 | "slot": "1", 1380 | "team": "2", 1381 | "user_id": "1650010", 1382 | "score": "603132", 1383 | "maxcombo": "1584", 1384 | "rank": "0", 1385 | "count50": "3", 1386 | "count100": "36", 1387 | "count300": "1676", 1388 | "countmiss": "2", 1389 | "countgeki": "356", 1390 | "countkatu": "29", 1391 | "perfect": "0", 1392 | "pass": "1", 1393 | "enabled_mods": "1" 1394 | }, 1395 | { 1396 | "slot": "2", 1397 | "team": "2", 1398 | "user_id": "2644828", 1399 | "score": "269728", 1400 | "maxcombo": "518", 1401 | "rank": "0", 1402 | "count50": "16", 1403 | "count100": "70", 1404 | "count300": "1610", 1405 | "countmiss": "21", 1406 | "countgeki": "332", 1407 | "countkatu": "33", 1408 | "perfect": "0", 1409 | "pass": "1", 1410 | "enabled_mods": "1" 1411 | }, 1412 | { 1413 | "slot": "3", 1414 | "team": "2", 1415 | "user_id": "4787150", 1416 | "score": "304998", 1417 | "maxcombo": "551", 1418 | "rank": "0", 1419 | "count50": "8", 1420 | "count100": "58", 1421 | "count300": "1623", 1422 | "countmiss": "28", 1423 | "countgeki": "337", 1424 | "countkatu": "35", 1425 | "perfect": "0", 1426 | "pass": "1", 1427 | "enabled_mods": "1" 1428 | }, 1429 | { 1430 | "slot": "4", 1431 | "team": "1", 1432 | "user_id": "8775024", 1433 | "score": "284613", 1434 | "maxcombo": "932", 1435 | "rank": "0", 1436 | "count50": "14", 1437 | "count100": "109", 1438 | "count300": "1563", 1439 | "countmiss": "31", 1440 | "countgeki": "311", 1441 | "countkatu": "58", 1442 | "perfect": "0", 1443 | "pass": "1", 1444 | "enabled_mods": "1" 1445 | }, 1446 | { 1447 | "slot": "5", 1448 | "team": "1", 1449 | "user_id": "1777162", 1450 | "score": "199362", 1451 | "maxcombo": "318", 1452 | "rank": "0", 1453 | "count50": "23", 1454 | "count100": "63", 1455 | "count300": "1571", 1456 | "countmiss": "60", 1457 | "countgeki": "318", 1458 | "countkatu": "28", 1459 | "perfect": "0", 1460 | "pass": "1", 1461 | "enabled_mods": "0" 1462 | }, 1463 | { 1464 | "slot": "6", 1465 | "team": "1", 1466 | "user_id": "7892320", 1467 | "score": "208082", 1468 | "maxcombo": "349", 1469 | "rank": "0", 1470 | "count50": "8", 1471 | "count100": "125", 1472 | "count300": "1552", 1473 | "countmiss": "32", 1474 | "countgeki": "301", 1475 | "countkatu": "64", 1476 | "perfect": "0", 1477 | "pass": "1", 1478 | "enabled_mods": "1" 1479 | }, 1480 | { 1481 | "slot": "7", 1482 | "team": "1", 1483 | "user_id": "9224078", 1484 | "score": "332678", 1485 | "maxcombo": "585", 1486 | "rank": "0", 1487 | "count50": "10", 1488 | "count100": "59", 1489 | "count300": "1632", 1490 | "countmiss": "16", 1491 | "countgeki": "333", 1492 | "countkatu": "39", 1493 | "perfect": "0", 1494 | "pass": "1", 1495 | "enabled_mods": "1" 1496 | }, 1497 | { 1498 | "slot": "8", 1499 | "team": "2", 1500 | "user_id": "4194445", 1501 | "score": "226542", 1502 | "maxcombo": "402", 1503 | "rank": "0", 1504 | "count50": "16", 1505 | "count100": "85", 1506 | "count300": "1583", 1507 | "countmiss": "33", 1508 | "countgeki": "314", 1509 | "countkatu": "43", 1510 | "perfect": "0", 1511 | "pass": "1", 1512 | "enabled_mods": "1" 1513 | }, 1514 | { 1515 | "slot": "9", 1516 | "team": "2", 1517 | "user_id": "3533958", 1518 | "score": "98413", 1519 | "maxcombo": "160", 1520 | "rank": "0", 1521 | "count50": "41", 1522 | "count100": "159", 1523 | "count300": "1389", 1524 | "countmiss": "128", 1525 | "countgeki": "219", 1526 | "countkatu": "84", 1527 | "perfect": "0", 1528 | "pass": "1", 1529 | "enabled_mods": "17" 1530 | }, 1531 | { 1532 | "slot": "10", 1533 | "team": "1", 1534 | "user_id": "1206417", 1535 | "score": "135648", 1536 | "maxcombo": "361", 1537 | "rank": "0", 1538 | "count50": "39", 1539 | "count100": "131", 1540 | "count300": "1457", 1541 | "countmiss": "90", 1542 | "countgeki": "262", 1543 | "countkatu": "66", 1544 | "perfect": "0", 1545 | "pass": "1", 1546 | "enabled_mods": "1" 1547 | }, 1548 | { 1549 | "slot": "11", 1550 | "team": "2", 1551 | "user_id": "4384207", 1552 | "score": "272194", 1553 | "maxcombo": "478", 1554 | "rank": "0", 1555 | "count50": "10", 1556 | "count100": "76", 1557 | "count300": "1612", 1558 | "countmiss": "19", 1559 | "countgeki": "321", 1560 | "countkatu": "47", 1561 | "perfect": "0", 1562 | "pass": "1", 1563 | "enabled_mods": "1" 1564 | }, 1565 | { 1566 | "slot": "12", 1567 | "team": "1", 1568 | "user_id": "1516650", 1569 | "score": "318215", 1570 | "maxcombo": "932", 1571 | "rank": "0", 1572 | "count50": "12", 1573 | "count100": "105", 1574 | "count300": "1580", 1575 | "countmiss": "20", 1576 | "countgeki": "305", 1577 | "countkatu": "63", 1578 | "perfect": "0", 1579 | "pass": "1", 1580 | "enabled_mods": "1" 1581 | }, 1582 | { 1583 | "slot": "13", 1584 | "team": "2", 1585 | "user_id": "2757689", 1586 | "score": "214113", 1587 | "maxcombo": "438", 1588 | "rank": "0", 1589 | "count50": "20", 1590 | "count100": "102", 1591 | "count300": "1550", 1592 | "countmiss": "45", 1593 | "countgeki": "308", 1594 | "countkatu": "43", 1595 | "perfect": "0", 1596 | "pass": "1", 1597 | "enabled_mods": "1" 1598 | }, 1599 | { 1600 | "slot": "14", 1601 | "team": "1", 1602 | "user_id": "3478883", 1603 | "score": "320316", 1604 | "maxcombo": "1040", 1605 | "rank": "0", 1606 | "count50": "21", 1607 | "count100": "137", 1608 | "count300": "1552", 1609 | "countmiss": "7", 1610 | "countgeki": "296", 1611 | "countkatu": "80", 1612 | "perfect": "0", 1613 | "pass": "1", 1614 | "enabled_mods": "1" 1615 | }, 1616 | { 1617 | "slot": "15", 1618 | "team": "1", 1619 | "user_id": "4852013", 1620 | "score": "326913", 1621 | "maxcombo": "645", 1622 | "rank": "0", 1623 | "count50": "3", 1624 | "count100": "57", 1625 | "count300": "1646", 1626 | "countmiss": "11", 1627 | "countgeki": "338", 1628 | "countkatu": "38", 1629 | "perfect": "0", 1630 | "pass": "1", 1631 | "enabled_mods": "1" 1632 | } 1633 | ] 1634 | } 1635 | ] 1636 | } -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const osu = require('../index.js'); 3 | const osuApi = new osu.Api(); 4 | 5 | try { 6 | osuApi.apiKey = process.env.osuApiKey || (require('fs')).readFileSync(__dirname + '/auth.txt'); 7 | } catch(error) { 8 | if (error.code === 'ENOENT') 9 | throw new Error('No osuApiKey environment variable or auth.txt file found. Please create one and place your api key in it.'); 10 | else 11 | throw error; 12 | } 13 | 14 | describe('osu!api methods', function() { 15 | describe('getUser()', function() { 16 | it('Should resolve with a valid User', function() { 17 | return osuApi.getUser({ u: 'brussell98' }).then(user => { 18 | expect(user).to.be.an.instanceof(osu.User); 19 | expect(user.name).to.equal('brussell98'); 20 | expect(user.scores.total).to.be.a('string'); 21 | 22 | if (user.events.length !== 0) { 23 | expect(user.events[0]).to.be.instanceof(osu.Event); 24 | expect(parseInt(user.events[0].epicFactor)).to.be.within(1, 32); 25 | expect(user.events[0].date).to.be.a('date'); 26 | } 27 | }).catch(error => { 28 | throw error; 29 | }); 30 | }); 31 | 32 | it('Should reject when given an invalid user', function() { 33 | return osuApi.getUser({ u: '' }).catch(error => { 34 | expect(error).to.be.an('error'); 35 | expect(error.message).to.equal('Not found'); 36 | }); 37 | }); 38 | }); 39 | 40 | describe('getScores()', function() { 41 | it('Should resolve with an array of valid Scores', function() { 42 | return osuApi.getScores({ b: '1036655' }).then(scores => { 43 | expect(scores).to.be.an('array'); 44 | expect(scores).to.have.lengthOf(50); 45 | expect(scores[0]).to.be.an.instanceOf(osu.Score); 46 | expect(scores[0].user.name).to.be.a('string'); 47 | expect(scores[0].perfect).to.be.a('boolean'); 48 | expect(scores[0].pp).to.not.equal(null); 49 | expect(scores[0].date).to.be.a('date'); 50 | 51 | if (scores[0].mods.length !== 0) 52 | expect(scores[0].mods[0]).to.be.a('string'); 53 | }).catch(error => { 54 | throw error; 55 | }); 56 | }); 57 | }); 58 | 59 | describe('getBeatmaps()', function() { 60 | it('Should resolve with an array of valid Beatmaps', function() { 61 | return osuApi.getBeatmaps({ b: '765567' }).then(beatmaps => { 62 | expect(beatmaps).to.be.an('array'); 63 | expect(beatmaps).to.have.lengthOf(1); 64 | expect(beatmaps[0]).to.be.an.instanceOf(osu.Beatmap); 65 | expect(beatmaps[0].source).to.equal('GATE 自衛隊 彼の地にて、斯く戦えり'); 66 | expect(beatmaps[0].tags).to.be.an('array'); 67 | expect(beatmaps[0].approvedDate).to.be.a('date'); 68 | expect(beatmaps[0].hasDownload).to.be.true; 69 | 70 | const langs = []; 71 | for (const lang in osu.Constants.Beatmaps.language) 72 | langs.push(osu.Constants.Beatmaps.language[lang]); 73 | expect(beatmaps[0].language).to.be.oneOf(langs); 74 | }).catch(error => { 75 | throw error; 76 | }); 77 | }); 78 | }); 79 | 80 | describe('getUserBest()', function() { 81 | it('Should resolve with an array of valid Scores', function() { 82 | return osuApi.getUserBest({ u: 'brussell98' }).then(scores => { 83 | expect(scores).to.be.an('array'); 84 | expect(scores).to.have.length.below(11); 85 | expect(scores[0]).to.be.an.instanceOf(osu.Score); 86 | expect(scores).to.satisfy(scores => scores.filter(s => s.pp && s.beatmapId).length === scores.length); 87 | expect(scores[4].date).to.be.a('date'); 88 | expect(scores[7].perfect).to.be.a('boolean'); 89 | expect(scores[2].mods).to.be.an('array'); 90 | expect(scores[0].beatmap).to.equal(undefined); 91 | }).catch(error => { 92 | throw error; 93 | }); 94 | }); 95 | }); 96 | 97 | describe('getUserRecent()', function() { 98 | it('Should resolve with an array of valid Scores', function() { 99 | return osuApi.getUserRecent({ u: 'brussell98' }).then(scores => { 100 | expect(scores).to.be.an('array'); 101 | expect(scores).to.have.length.below(11); 102 | expect(scores[0]).to.be.an.instanceOf(osu.Score); 103 | expect(scores).to.satisfy(scores => scores.filter(s => s.score && s.beatmapId).length === scores.length); 104 | expect(scores[4].date).to.be.a('date'); 105 | expect(scores[3].perfect).to.be.a('boolean'); 106 | expect(scores[2].mods).to.be.an('array'); 107 | }).catch(error => { 108 | throw error; 109 | }); 110 | }); 111 | }); 112 | 113 | describe('new Match()', function() { 114 | it('Should return a valid Match', function() { 115 | const sampleMatch = require('./sampleMatch.json'); 116 | const match = new osu.Match({ }, sampleMatch); 117 | expect(match.start).to.be.a('date'); 118 | expect(match.name).to.equal(sampleMatch.match.name); 119 | expect(match.games).to.be.an('array'); 120 | 121 | const modes = []; 122 | const scoringTypes = []; 123 | const teamTypes = []; 124 | const teams = []; 125 | for (const mode in osu.Constants.Beatmaps.mode) 126 | modes.push(osu.Constants.Beatmaps.mode[mode]); 127 | for (const st in osu.Constants.Multiplayer.scoringType) 128 | scoringTypes.push(osu.Constants.Multiplayer.scoringType[st]); 129 | for (const tt in osu.Constants.Multiplayer.teamType) 130 | teamTypes.push(osu.Constants.Multiplayer.teamType[tt]); 131 | for (const team in osu.Constants.Multiplayer.team) 132 | teams.push(osu.Constants.Multiplayer.team[team]); 133 | 134 | expect(match.games[0]).to.be.an.instanceOf(osu.Game); 135 | expect(match.games[7].scores[0].mods).to.include.members(['NoFail', 'DoubleTime']); 136 | expect(match.games[0].beatmapId).to.equal(sampleMatch.games[0].beatmap_id); 137 | expect(match.games[2].mode).to.be.oneOf(modes); 138 | expect(match.games[2].scoringType).to.be.oneOf(scoringTypes); 139 | expect(match.games[2].teamType).to.be.oneOf(teamTypes); 140 | 141 | expect(match.games[2].scores).to.be.an('array'); 142 | expect(match.games[2].scores[0]).to.be.an.instanceOf(osu.MultiplayerScore); 143 | expect(match.games[2].scores[0].team).to.be.oneOf(teams); 144 | expect(match.games[2].scores[0].pass).to.equal(sampleMatch.games[2].scores[0].pass === '1'); 145 | }); 146 | }); 147 | 148 | describe('Scores with competeScores enabled', function() { 149 | it('Should have a beatmap included', async function() { 150 | osuApi.completeScores = true; 151 | 152 | const scores = await osuApi.getScores({ u: 'brussell98', b: '1416386', limit: 1 }); 153 | const scoresBest = await osuApi.getUserBest({ u: 'brussell98', limit: 1 }); 154 | 155 | expect(scores[0].beatmapId).to.equal('1416386'); 156 | expect(scores[0].beatmap).to.be.an.instanceof(osu.Beatmap); 157 | 158 | expect(scoresBest[0].beatmapId).to.exist; 159 | expect(scoresBest[0].beatmap).to.be.an.instanceof(osu.Beatmap); 160 | 161 | osuApi.completeScores = false; 162 | }); 163 | 164 | it('Should compute accuracy correctly', async function() { 165 | osuApi.completeScores = true; 166 | 167 | const scores = await osuApi.getScores({ u: 'brussell98', b: '1416386', limit: 1 }); 168 | 169 | expect(scores[0].accuracy).to.be.closeTo(.9658, .0001); 170 | 171 | osuApi.completeScores = false; 172 | }); 173 | }); 174 | 175 | describe('parseNumeric', function() { 176 | it('Should return numeric non-id values as numbers', function() { 177 | osuApi.parseNumeric = true; 178 | 179 | return osuApi.getScores({ b: '1036655' }).then(scores => { 180 | expect(scores[0].user.id).to.be.a('string'); 181 | expect(scores[0].pp).to.be.a('number'); 182 | 183 | osuApi.parseNumeric = false; 184 | }).catch(error => { 185 | throw error; 186 | }); 187 | }); 188 | }); 189 | }); 190 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.8.3" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" 8 | integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== 9 | dependencies: 10 | "@babel/highlight" "^7.8.3" 11 | 12 | "@babel/highlight@^7.8.3": 13 | version "7.8.3" 14 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" 15 | integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== 16 | dependencies: 17 | chalk "^2.0.0" 18 | esutils "^2.0.2" 19 | js-tokens "^4.0.0" 20 | 21 | acorn-jsx@^5.1.0: 22 | version "5.1.0" 23 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" 24 | integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== 25 | 26 | acorn@^7.1.0: 27 | version "7.1.0" 28 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" 29 | integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== 30 | 31 | ajv@^6.10.0, ajv@^6.10.2: 32 | version "6.11.0" 33 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" 34 | integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA== 35 | dependencies: 36 | fast-deep-equal "^3.1.1" 37 | fast-json-stable-stringify "^2.0.0" 38 | json-schema-traverse "^0.4.1" 39 | uri-js "^4.2.2" 40 | 41 | ansi-escapes@^4.2.1: 42 | version "4.3.0" 43 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" 44 | integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== 45 | dependencies: 46 | type-fest "^0.8.1" 47 | 48 | ansi-regex@^4.1.0: 49 | version "4.1.0" 50 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" 51 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== 52 | 53 | ansi-regex@^5.0.0: 54 | version "5.0.0" 55 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 56 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 57 | 58 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 59 | version "3.2.1" 60 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 61 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 62 | dependencies: 63 | color-convert "^1.9.0" 64 | 65 | argparse@^1.0.7: 66 | version "1.0.10" 67 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 68 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 69 | dependencies: 70 | sprintf-js "~1.0.2" 71 | 72 | assertion-error@^1.0.1: 73 | version "1.1.0" 74 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 75 | integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== 76 | 77 | astral-regex@^1.0.0: 78 | version "1.0.0" 79 | resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" 80 | integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== 81 | 82 | asynckit@^0.4.0: 83 | version "0.4.0" 84 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 85 | integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= 86 | 87 | balanced-match@^1.0.0: 88 | version "1.0.0" 89 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 90 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 91 | 92 | brace-expansion@^1.1.7: 93 | version "1.1.11" 94 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 95 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 96 | dependencies: 97 | balanced-match "^1.0.0" 98 | concat-map "0.0.1" 99 | 100 | browser-stdout@1.3.0: 101 | version "1.3.0" 102 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 103 | integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= 104 | 105 | callsites@^3.0.0: 106 | version "3.1.0" 107 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 108 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 109 | 110 | chai@^3.5.0: 111 | version "3.5.0" 112 | resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" 113 | integrity sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc= 114 | dependencies: 115 | assertion-error "^1.0.1" 116 | deep-eql "^0.1.3" 117 | type-detect "^1.0.0" 118 | 119 | chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: 120 | version "2.4.2" 121 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 122 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 123 | dependencies: 124 | ansi-styles "^3.2.1" 125 | escape-string-regexp "^1.0.5" 126 | supports-color "^5.3.0" 127 | 128 | chardet@^0.7.0: 129 | version "0.7.0" 130 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 131 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 132 | 133 | cli-cursor@^3.1.0: 134 | version "3.1.0" 135 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" 136 | integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== 137 | dependencies: 138 | restore-cursor "^3.1.0" 139 | 140 | cli-width@^2.0.0: 141 | version "2.2.0" 142 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 143 | integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= 144 | 145 | color-convert@^1.9.0: 146 | version "1.9.3" 147 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 148 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 149 | dependencies: 150 | color-name "1.1.3" 151 | 152 | color-name@1.1.3: 153 | version "1.1.3" 154 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 155 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 156 | 157 | combined-stream@^1.0.8: 158 | version "1.0.8" 159 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 160 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 161 | dependencies: 162 | delayed-stream "~1.0.0" 163 | 164 | commander@2.9.0: 165 | version "2.9.0" 166 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 167 | integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= 168 | dependencies: 169 | graceful-readlink ">= 1.0.0" 170 | 171 | component-emitter@^1.3.0: 172 | version "1.3.0" 173 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" 174 | integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== 175 | 176 | concat-map@0.0.1: 177 | version "0.0.1" 178 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 179 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 180 | 181 | cookiejar@^2.1.2: 182 | version "2.1.2" 183 | resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" 184 | integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== 185 | 186 | cross-spawn@^6.0.5: 187 | version "6.0.5" 188 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 189 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== 190 | dependencies: 191 | nice-try "^1.0.4" 192 | path-key "^2.0.1" 193 | semver "^5.5.0" 194 | shebang-command "^1.2.0" 195 | which "^1.2.9" 196 | 197 | debug@2.6.8: 198 | version "2.6.8" 199 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 200 | integrity sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw= 201 | dependencies: 202 | ms "2.0.0" 203 | 204 | debug@^4.0.1, debug@^4.1.1: 205 | version "4.1.1" 206 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 207 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 208 | dependencies: 209 | ms "^2.1.1" 210 | 211 | deep-eql@^0.1.3: 212 | version "0.1.3" 213 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" 214 | integrity sha1-71WKyrjeJSBs1xOQbXTlaTDrafI= 215 | dependencies: 216 | type-detect "0.1.1" 217 | 218 | deep-is@~0.1.3: 219 | version "0.1.3" 220 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 221 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 222 | 223 | delayed-stream@~1.0.0: 224 | version "1.0.0" 225 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 226 | integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= 227 | 228 | diff@3.2.0: 229 | version "3.2.0" 230 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 231 | integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= 232 | 233 | doctrine@^3.0.0: 234 | version "3.0.0" 235 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 236 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 237 | dependencies: 238 | esutils "^2.0.2" 239 | 240 | emoji-regex@^7.0.1: 241 | version "7.0.3" 242 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" 243 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== 244 | 245 | emoji-regex@^8.0.0: 246 | version "8.0.0" 247 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 248 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 249 | 250 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: 251 | version "1.0.5" 252 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 253 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 254 | 255 | eslint-scope@^5.0.0: 256 | version "5.0.0" 257 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" 258 | integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== 259 | dependencies: 260 | esrecurse "^4.1.0" 261 | estraverse "^4.1.1" 262 | 263 | eslint-utils@^1.4.3: 264 | version "1.4.3" 265 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" 266 | integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== 267 | dependencies: 268 | eslint-visitor-keys "^1.1.0" 269 | 270 | eslint-visitor-keys@^1.1.0: 271 | version "1.1.0" 272 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" 273 | integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== 274 | 275 | eslint@^6.8.0: 276 | version "6.8.0" 277 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" 278 | integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== 279 | dependencies: 280 | "@babel/code-frame" "^7.0.0" 281 | ajv "^6.10.0" 282 | chalk "^2.1.0" 283 | cross-spawn "^6.0.5" 284 | debug "^4.0.1" 285 | doctrine "^3.0.0" 286 | eslint-scope "^5.0.0" 287 | eslint-utils "^1.4.3" 288 | eslint-visitor-keys "^1.1.0" 289 | espree "^6.1.2" 290 | esquery "^1.0.1" 291 | esutils "^2.0.2" 292 | file-entry-cache "^5.0.1" 293 | functional-red-black-tree "^1.0.1" 294 | glob-parent "^5.0.0" 295 | globals "^12.1.0" 296 | ignore "^4.0.6" 297 | import-fresh "^3.0.0" 298 | imurmurhash "^0.1.4" 299 | inquirer "^7.0.0" 300 | is-glob "^4.0.0" 301 | js-yaml "^3.13.1" 302 | json-stable-stringify-without-jsonify "^1.0.1" 303 | levn "^0.3.0" 304 | lodash "^4.17.14" 305 | minimatch "^3.0.4" 306 | mkdirp "^0.5.1" 307 | natural-compare "^1.4.0" 308 | optionator "^0.8.3" 309 | progress "^2.0.0" 310 | regexpp "^2.0.1" 311 | semver "^6.1.2" 312 | strip-ansi "^5.2.0" 313 | strip-json-comments "^3.0.1" 314 | table "^5.2.3" 315 | text-table "^0.2.0" 316 | v8-compile-cache "^2.0.3" 317 | 318 | espree@^6.1.2: 319 | version "6.1.2" 320 | resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" 321 | integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== 322 | dependencies: 323 | acorn "^7.1.0" 324 | acorn-jsx "^5.1.0" 325 | eslint-visitor-keys "^1.1.0" 326 | 327 | esprima@^4.0.0: 328 | version "4.0.1" 329 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 330 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 331 | 332 | esquery@^1.0.1: 333 | version "1.0.1" 334 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 335 | integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== 336 | dependencies: 337 | estraverse "^4.0.0" 338 | 339 | esrecurse@^4.1.0: 340 | version "4.2.1" 341 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 342 | integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== 343 | dependencies: 344 | estraverse "^4.1.0" 345 | 346 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 347 | version "4.3.0" 348 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 349 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 350 | 351 | esutils@^2.0.2: 352 | version "2.0.3" 353 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 354 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 355 | 356 | external-editor@^3.0.3: 357 | version "3.1.0" 358 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" 359 | integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== 360 | dependencies: 361 | chardet "^0.7.0" 362 | iconv-lite "^0.4.24" 363 | tmp "^0.0.33" 364 | 365 | fast-deep-equal@^3.1.1: 366 | version "3.1.1" 367 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" 368 | integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== 369 | 370 | fast-json-stable-stringify@^2.0.0: 371 | version "2.1.0" 372 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 373 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 374 | 375 | fast-levenshtein@~2.0.6: 376 | version "2.0.6" 377 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 378 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 379 | 380 | fast-safe-stringify@^2.0.7: 381 | version "2.0.7" 382 | resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" 383 | integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== 384 | 385 | figures@^3.0.0: 386 | version "3.1.0" 387 | resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" 388 | integrity sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg== 389 | dependencies: 390 | escape-string-regexp "^1.0.5" 391 | 392 | file-entry-cache@^5.0.1: 393 | version "5.0.1" 394 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" 395 | integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== 396 | dependencies: 397 | flat-cache "^2.0.1" 398 | 399 | flat-cache@^2.0.1: 400 | version "2.0.1" 401 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" 402 | integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== 403 | dependencies: 404 | flatted "^2.0.0" 405 | rimraf "2.6.3" 406 | write "1.0.3" 407 | 408 | flatted@^2.0.0: 409 | version "2.0.1" 410 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" 411 | integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== 412 | 413 | form-data@^3.0.0: 414 | version "3.0.0" 415 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" 416 | integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== 417 | dependencies: 418 | asynckit "^0.4.0" 419 | combined-stream "^1.0.8" 420 | mime-types "^2.1.12" 421 | 422 | formidable@^1.2.1: 423 | version "1.2.1" 424 | resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" 425 | integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== 426 | 427 | fs.realpath@^1.0.0: 428 | version "1.0.0" 429 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 430 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 431 | 432 | functional-red-black-tree@^1.0.1: 433 | version "1.0.1" 434 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 435 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 436 | 437 | glob-parent@^5.0.0: 438 | version "5.1.0" 439 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" 440 | integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== 441 | dependencies: 442 | is-glob "^4.0.1" 443 | 444 | glob@7.1.1: 445 | version "7.1.1" 446 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 447 | integrity sha1-gFIR3wT6rxxjo2ADBs31reULLsg= 448 | dependencies: 449 | fs.realpath "^1.0.0" 450 | inflight "^1.0.4" 451 | inherits "2" 452 | minimatch "^3.0.2" 453 | once "^1.3.0" 454 | path-is-absolute "^1.0.0" 455 | 456 | glob@^7.1.3: 457 | version "7.1.6" 458 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 459 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 460 | dependencies: 461 | fs.realpath "^1.0.0" 462 | inflight "^1.0.4" 463 | inherits "2" 464 | minimatch "^3.0.4" 465 | once "^1.3.0" 466 | path-is-absolute "^1.0.0" 467 | 468 | globals@^12.1.0: 469 | version "12.3.0" 470 | resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" 471 | integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw== 472 | dependencies: 473 | type-fest "^0.8.1" 474 | 475 | "graceful-readlink@>= 1.0.0": 476 | version "1.0.1" 477 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 478 | integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= 479 | 480 | growl@1.9.2: 481 | version "1.9.2" 482 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 483 | integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= 484 | 485 | has-flag@^1.0.0: 486 | version "1.0.0" 487 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 488 | integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= 489 | 490 | has-flag@^3.0.0: 491 | version "3.0.0" 492 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 493 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 494 | 495 | he@1.1.1: 496 | version "1.1.1" 497 | resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 498 | integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= 499 | 500 | iconv-lite@^0.4.24: 501 | version "0.4.24" 502 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 503 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 504 | dependencies: 505 | safer-buffer ">= 2.1.2 < 3" 506 | 507 | ignore@^4.0.6: 508 | version "4.0.6" 509 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" 510 | integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== 511 | 512 | import-fresh@^3.0.0: 513 | version "3.2.1" 514 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" 515 | integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== 516 | dependencies: 517 | parent-module "^1.0.0" 518 | resolve-from "^4.0.0" 519 | 520 | imurmurhash@^0.1.4: 521 | version "0.1.4" 522 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 523 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 524 | 525 | inflight@^1.0.4: 526 | version "1.0.6" 527 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 528 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 529 | dependencies: 530 | once "^1.3.0" 531 | wrappy "1" 532 | 533 | inherits@2, inherits@^2.0.3: 534 | version "2.0.4" 535 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 536 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 537 | 538 | inquirer@^7.0.0: 539 | version "7.0.3" 540 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.3.tgz#f9b4cd2dff58b9f73e8d43759436ace15bed4567" 541 | integrity sha512-+OiOVeVydu4hnCGLCSX+wedovR/Yzskv9BFqUNNKq9uU2qg7LCcCo3R86S2E7WLo0y/x2pnEZfZe1CoYnORUAw== 542 | dependencies: 543 | ansi-escapes "^4.2.1" 544 | chalk "^2.4.2" 545 | cli-cursor "^3.1.0" 546 | cli-width "^2.0.0" 547 | external-editor "^3.0.3" 548 | figures "^3.0.0" 549 | lodash "^4.17.15" 550 | mute-stream "0.0.8" 551 | run-async "^2.2.0" 552 | rxjs "^6.5.3" 553 | string-width "^4.1.0" 554 | strip-ansi "^5.1.0" 555 | through "^2.3.6" 556 | 557 | is-extglob@^2.1.1: 558 | version "2.1.1" 559 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 560 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 561 | 562 | is-fullwidth-code-point@^2.0.0: 563 | version "2.0.0" 564 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 565 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 566 | 567 | is-fullwidth-code-point@^3.0.0: 568 | version "3.0.0" 569 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 570 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 571 | 572 | is-glob@^4.0.0, is-glob@^4.0.1: 573 | version "4.0.1" 574 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 575 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 576 | dependencies: 577 | is-extglob "^2.1.1" 578 | 579 | is-promise@^2.1.0: 580 | version "2.1.0" 581 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 582 | integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= 583 | 584 | isexe@^2.0.0: 585 | version "2.0.0" 586 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 587 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 588 | 589 | js-tokens@^4.0.0: 590 | version "4.0.0" 591 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 592 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 593 | 594 | js-yaml@^3.13.1: 595 | version "3.13.1" 596 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 597 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 598 | dependencies: 599 | argparse "^1.0.7" 600 | esprima "^4.0.0" 601 | 602 | json-schema-traverse@^0.4.1: 603 | version "0.4.1" 604 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 605 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 606 | 607 | json-stable-stringify-without-jsonify@^1.0.1: 608 | version "1.0.1" 609 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 610 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 611 | 612 | json3@3.3.2: 613 | version "3.3.2" 614 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 615 | integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= 616 | 617 | levn@^0.3.0, levn@~0.3.0: 618 | version "0.3.0" 619 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 620 | integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= 621 | dependencies: 622 | prelude-ls "~1.1.2" 623 | type-check "~0.3.2" 624 | 625 | lodash._baseassign@^3.0.0: 626 | version "3.2.0" 627 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 628 | integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4= 629 | dependencies: 630 | lodash._basecopy "^3.0.0" 631 | lodash.keys "^3.0.0" 632 | 633 | lodash._basecopy@^3.0.0: 634 | version "3.0.1" 635 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 636 | integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= 637 | 638 | lodash._basecreate@^3.0.0: 639 | version "3.0.3" 640 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 641 | integrity sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE= 642 | 643 | lodash._getnative@^3.0.0: 644 | version "3.9.1" 645 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 646 | integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= 647 | 648 | lodash._isiterateecall@^3.0.0: 649 | version "3.0.9" 650 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 651 | integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= 652 | 653 | lodash.create@3.1.1: 654 | version "3.1.1" 655 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 656 | integrity sha1-1/KEnw29p+BGgruM1yqwIkYd6+c= 657 | dependencies: 658 | lodash._baseassign "^3.0.0" 659 | lodash._basecreate "^3.0.0" 660 | lodash._isiterateecall "^3.0.0" 661 | 662 | lodash.isarguments@^3.0.0: 663 | version "3.1.0" 664 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 665 | integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= 666 | 667 | lodash.isarray@^3.0.0: 668 | version "3.0.4" 669 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 670 | integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= 671 | 672 | lodash.keys@^3.0.0: 673 | version "3.1.2" 674 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 675 | integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= 676 | dependencies: 677 | lodash._getnative "^3.0.0" 678 | lodash.isarguments "^3.0.0" 679 | lodash.isarray "^3.0.0" 680 | 681 | lodash@^4.17.14, lodash@^4.17.15: 682 | version "4.17.15" 683 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 684 | integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== 685 | 686 | methods@^1.1.2: 687 | version "1.1.2" 688 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 689 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 690 | 691 | mime-db@1.43.0: 692 | version "1.43.0" 693 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" 694 | integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== 695 | 696 | mime-types@^2.1.12: 697 | version "2.1.26" 698 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" 699 | integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== 700 | dependencies: 701 | mime-db "1.43.0" 702 | 703 | mime@^2.4.4: 704 | version "2.4.4" 705 | resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" 706 | integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== 707 | 708 | mimic-fn@^2.1.0: 709 | version "2.1.0" 710 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" 711 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== 712 | 713 | minimatch@^3.0.2, minimatch@^3.0.4: 714 | version "3.0.4" 715 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 716 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 717 | dependencies: 718 | brace-expansion "^1.1.7" 719 | 720 | minimist@0.0.8: 721 | version "0.0.8" 722 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 723 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 724 | 725 | mkdirp@0.5.1, mkdirp@^0.5.1: 726 | version "0.5.1" 727 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 728 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 729 | dependencies: 730 | minimist "0.0.8" 731 | 732 | mocha@^3.0.2: 733 | version "3.5.3" 734 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" 735 | integrity sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg== 736 | dependencies: 737 | browser-stdout "1.3.0" 738 | commander "2.9.0" 739 | debug "2.6.8" 740 | diff "3.2.0" 741 | escape-string-regexp "1.0.5" 742 | glob "7.1.1" 743 | growl "1.9.2" 744 | he "1.1.1" 745 | json3 "3.3.2" 746 | lodash.create "3.1.1" 747 | mkdirp "0.5.1" 748 | supports-color "3.1.2" 749 | 750 | ms@2.0.0: 751 | version "2.0.0" 752 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 753 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 754 | 755 | ms@^2.1.1: 756 | version "2.1.2" 757 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 758 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 759 | 760 | mute-stream@0.0.8: 761 | version "0.0.8" 762 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" 763 | integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== 764 | 765 | natural-compare@^1.4.0: 766 | version "1.4.0" 767 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 768 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 769 | 770 | nice-try@^1.0.4: 771 | version "1.0.5" 772 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 773 | integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== 774 | 775 | once@^1.3.0: 776 | version "1.4.0" 777 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 778 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 779 | dependencies: 780 | wrappy "1" 781 | 782 | onetime@^5.1.0: 783 | version "5.1.0" 784 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" 785 | integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== 786 | dependencies: 787 | mimic-fn "^2.1.0" 788 | 789 | optionator@^0.8.3: 790 | version "0.8.3" 791 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" 792 | integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== 793 | dependencies: 794 | deep-is "~0.1.3" 795 | fast-levenshtein "~2.0.6" 796 | levn "~0.3.0" 797 | prelude-ls "~1.1.2" 798 | type-check "~0.3.2" 799 | word-wrap "~1.2.3" 800 | 801 | os-tmpdir@~1.0.2: 802 | version "1.0.2" 803 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 804 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 805 | 806 | parent-module@^1.0.0: 807 | version "1.0.1" 808 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 809 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 810 | dependencies: 811 | callsites "^3.0.0" 812 | 813 | path-is-absolute@^1.0.0: 814 | version "1.0.1" 815 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 816 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 817 | 818 | path-key@^2.0.1: 819 | version "2.0.1" 820 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 821 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 822 | 823 | prelude-ls@~1.1.2: 824 | version "1.1.2" 825 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 826 | integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= 827 | 828 | progress@^2.0.0: 829 | version "2.0.3" 830 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 831 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 832 | 833 | punycode@^2.1.0: 834 | version "2.1.1" 835 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 836 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 837 | 838 | qs@^6.9.1: 839 | version "6.9.1" 840 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9" 841 | integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA== 842 | 843 | readable-stream@^3.4.0: 844 | version "3.5.0" 845 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606" 846 | integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA== 847 | dependencies: 848 | inherits "^2.0.3" 849 | string_decoder "^1.1.1" 850 | util-deprecate "^1.0.1" 851 | 852 | regexpp@^2.0.1: 853 | version "2.0.1" 854 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" 855 | integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== 856 | 857 | resolve-from@^4.0.0: 858 | version "4.0.0" 859 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 860 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 861 | 862 | restore-cursor@^3.1.0: 863 | version "3.1.0" 864 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" 865 | integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== 866 | dependencies: 867 | onetime "^5.1.0" 868 | signal-exit "^3.0.2" 869 | 870 | rimraf@2.6.3: 871 | version "2.6.3" 872 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 873 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 874 | dependencies: 875 | glob "^7.1.3" 876 | 877 | run-async@^2.2.0: 878 | version "2.3.0" 879 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 880 | integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= 881 | dependencies: 882 | is-promise "^2.1.0" 883 | 884 | rxjs@^6.5.3: 885 | version "6.5.4" 886 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" 887 | integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== 888 | dependencies: 889 | tslib "^1.9.0" 890 | 891 | safe-buffer@~5.2.0: 892 | version "5.2.0" 893 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 894 | integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== 895 | 896 | "safer-buffer@>= 2.1.2 < 3": 897 | version "2.1.2" 898 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 899 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 900 | 901 | semver@^5.5.0: 902 | version "5.7.1" 903 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 904 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 905 | 906 | semver@^6.1.2, semver@^6.3.0: 907 | version "6.3.0" 908 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 909 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 910 | 911 | shebang-command@^1.2.0: 912 | version "1.2.0" 913 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 914 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 915 | dependencies: 916 | shebang-regex "^1.0.0" 917 | 918 | shebang-regex@^1.0.0: 919 | version "1.0.0" 920 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 921 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 922 | 923 | signal-exit@^3.0.2: 924 | version "3.0.2" 925 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 926 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 927 | 928 | slice-ansi@^2.1.0: 929 | version "2.1.0" 930 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" 931 | integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== 932 | dependencies: 933 | ansi-styles "^3.2.0" 934 | astral-regex "^1.0.0" 935 | is-fullwidth-code-point "^2.0.0" 936 | 937 | sprintf-js@~1.0.2: 938 | version "1.0.3" 939 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 940 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 941 | 942 | string-width@^3.0.0: 943 | version "3.1.0" 944 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 945 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== 946 | dependencies: 947 | emoji-regex "^7.0.1" 948 | is-fullwidth-code-point "^2.0.0" 949 | strip-ansi "^5.1.0" 950 | 951 | string-width@^4.1.0: 952 | version "4.2.0" 953 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" 954 | integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== 955 | dependencies: 956 | emoji-regex "^8.0.0" 957 | is-fullwidth-code-point "^3.0.0" 958 | strip-ansi "^6.0.0" 959 | 960 | string_decoder@^1.1.1: 961 | version "1.3.0" 962 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 963 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 964 | dependencies: 965 | safe-buffer "~5.2.0" 966 | 967 | strip-ansi@^5.1.0, strip-ansi@^5.2.0: 968 | version "5.2.0" 969 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" 970 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== 971 | dependencies: 972 | ansi-regex "^4.1.0" 973 | 974 | strip-ansi@^6.0.0: 975 | version "6.0.0" 976 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 977 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 978 | dependencies: 979 | ansi-regex "^5.0.0" 980 | 981 | strip-json-comments@^3.0.1: 982 | version "3.0.1" 983 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" 984 | integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== 985 | 986 | superagent@^5.2.1: 987 | version "5.2.1" 988 | resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.2.1.tgz#9f5f86b705be0865a8c1b9bcde533ab7773ff63a" 989 | integrity sha512-46b4Lkwnlz7Ebdv2FBbfuqb3kVkG1jV/SK3EW6NnwL9a3T4h5hHtegNEQfbXvTFbDoUZXId4W3dMgap2f6ic1g== 990 | dependencies: 991 | component-emitter "^1.3.0" 992 | cookiejar "^2.1.2" 993 | debug "^4.1.1" 994 | fast-safe-stringify "^2.0.7" 995 | form-data "^3.0.0" 996 | formidable "^1.2.1" 997 | methods "^1.1.2" 998 | mime "^2.4.4" 999 | qs "^6.9.1" 1000 | readable-stream "^3.4.0" 1001 | semver "^6.3.0" 1002 | 1003 | supports-color@3.1.2: 1004 | version "3.1.2" 1005 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 1006 | integrity sha1-cqJiiU2dQIuVbKBf83su2KbiotU= 1007 | dependencies: 1008 | has-flag "^1.0.0" 1009 | 1010 | supports-color@^5.3.0: 1011 | version "5.5.0" 1012 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1013 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1014 | dependencies: 1015 | has-flag "^3.0.0" 1016 | 1017 | table@^5.2.3: 1018 | version "5.4.6" 1019 | resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" 1020 | integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== 1021 | dependencies: 1022 | ajv "^6.10.2" 1023 | lodash "^4.17.14" 1024 | slice-ansi "^2.1.0" 1025 | string-width "^3.0.0" 1026 | 1027 | text-table@^0.2.0: 1028 | version "0.2.0" 1029 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1030 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 1031 | 1032 | through@^2.3.6: 1033 | version "2.3.8" 1034 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1035 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 1036 | 1037 | tmp@^0.0.33: 1038 | version "0.0.33" 1039 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 1040 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 1041 | dependencies: 1042 | os-tmpdir "~1.0.2" 1043 | 1044 | tslib@^1.9.0: 1045 | version "1.10.0" 1046 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" 1047 | integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== 1048 | 1049 | type-check@~0.3.2: 1050 | version "0.3.2" 1051 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1052 | integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= 1053 | dependencies: 1054 | prelude-ls "~1.1.2" 1055 | 1056 | type-detect@0.1.1: 1057 | version "0.1.1" 1058 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" 1059 | integrity sha1-C6XsKohWQORw6k6FBZcZANrFiCI= 1060 | 1061 | type-detect@^1.0.0: 1062 | version "1.0.0" 1063 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" 1064 | integrity sha1-diIXzAbbJY7EiQihKY6LlRIejqI= 1065 | 1066 | type-fest@^0.8.1: 1067 | version "0.8.1" 1068 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" 1069 | integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== 1070 | 1071 | uri-js@^4.2.2: 1072 | version "4.2.2" 1073 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 1074 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 1075 | dependencies: 1076 | punycode "^2.1.0" 1077 | 1078 | util-deprecate@^1.0.1: 1079 | version "1.0.2" 1080 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1081 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1082 | 1083 | v8-compile-cache@^2.0.3: 1084 | version "2.1.0" 1085 | resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" 1086 | integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== 1087 | 1088 | which@^1.2.9: 1089 | version "1.3.1" 1090 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1091 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1092 | dependencies: 1093 | isexe "^2.0.0" 1094 | 1095 | word-wrap@~1.2.3: 1096 | version "1.2.3" 1097 | resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" 1098 | integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== 1099 | 1100 | wrappy@1: 1101 | version "1.0.2" 1102 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1103 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1104 | 1105 | write@1.0.3: 1106 | version "1.0.3" 1107 | resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" 1108 | integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== 1109 | dependencies: 1110 | mkdirp "^0.5.1" 1111 | --------------------------------------------------------------------------------