Compare commits

...

27 Commits

Author SHA1 Message Date
maxswa
91066b5366 Add deadmanPoints 2023-12-17 20:13:06 -05:00
maxswa
4872d04301 v2.16.2 2023-11-26 19:53:20 -05:00
Max Swartwout
55312ba0fd Merge pull request #85 from seditionist/main
Add Custom Errors
2023-11-26 19:52:07 -05:00
maxswa
331a448503 Disabled eslint rule. 2023-11-26 19:43:35 -05:00
maxswa
84c704a846 Add test for excluded gamemodes. 2023-11-26 19:42:46 -05:00
seditionist
561495632c Fix gamemode checks 2023-11-26 18:55:26 -05:00
seditionist
bdfdf1eb14 Merge branch 'maxswa-main' 2023-11-26 18:23:31 -05:00
seditionist
e349776060 Merge branch 'main' of github.com:maxswa/osrs-json-hiscores into maxswa-main
# Conflicts:
#	src/hiscores.ts
2023-11-26 18:20:03 -05:00
seditionist
546bc5acf7 Remove unnecessary type cast 2023-11-26 17:43:38 -05:00
seditionist
66528cd9d9 Restore error message constants 2023-11-26 17:42:55 -05:00
maxswa
99ea3fb722 v2.16.1 2023-11-25 22:07:55 -05:00
Max Swartwout
7f16d26e3c Merge pull request #89 from maxswa/bump-axios
Bump `axios`
2023-11-25 22:06:07 -05:00
maxswa
ed9fcf47b3 Bump axios 2023-11-25 22:04:48 -05:00
Max Swartwout
bb935bb6c9 Merge pull request #86 from maxswa/dependabot/npm_and_yarn/babel/traverse-7.23.2
Bump @babel/traverse from 7.22.8 to 7.23.2
2023-11-25 21:36:52 -05:00
dependabot[bot]
d046fffa9e Bump @babel/traverse from 7.22.8 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.8 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-19 11:00:07 +00:00
seditionist
5483e54d78 cleanup 2023-08-17 00:58:37 -04:00
seditionist
97532b0c03 fix tests 2023-08-17 00:35:22 -04:00
seditionist
f9a56e2b3a update hiscores to throw new custom errors 2023-08-17 00:32:23 -04:00
seditionist
9f43f0c39f update validRSN to use InvalidRSNError 2023-08-17 00:12:27 -04:00
seditionist
d67ffa3e20 created custom errors 2023-08-17 00:11:18 -04:00
maxswa
7bdde9f5d1 v2.16.0 2023-08-15 21:26:53 -04:00
Max Swartwout
b4a50e69c8 Merge pull request #83 from maxswa/json-endpoint
Use Official JSON Endpoint
2023-08-15 21:25:57 -04:00
maxswa
7935c5a523 Update name matching to use lowercase values. 2023-08-12 11:19:33 -04:00
maxswa
f1e2155428 Fix tests. 2023-08-12 11:01:06 -04:00
maxswa
dd90fe1260 Update README with new formatted names. 2023-08-11 15:11:38 -04:00
maxswa
12c4e530a2 Update get stats functions to use JSON endpoint. 2023-08-11 14:59:49 -04:00
maxswa
5a2b0d9ad8 Add functions to support JSON endpoint. 2023-08-11 14:52:02 -04:00
16 changed files with 4332 additions and 4465 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.idea/
node_modules
/lib
.vscode

View File

@@ -110,16 +110,17 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| Bounty Hunter (Legacy - Hunter) | `hunterBH` |
| Bounty Hunter (Rogue) | `rogueBHV2` |
| Bounty Hunter (Hunter) | `hunterBHV2` |
| Last Man Standing | `lastManStanding` |
| PvP Arena | `pvpArena` |
| LMS - Rank | `lastManStanding` |
| PvP Arena - Rank | `pvpArena` |
| Soul Wars Zeal | `soulWarsZeal` |
| Rifts Closed | `riftsClosed` |
| Rifts closed | `riftsClosed` |
### Leagues
### Points
| Activity | Param |
| ------------- | :------------: |
| -------------- | :-------------: |
| League Points | `leaguePoints` |
| Deadman Points | `deadmanPoints` |
### Bosses
@@ -156,7 +157,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| K'ril Tsutsaroth | `krilTsutsaroth` |
| Mimic | `mimic` |
| Nex | `nex` |
| The Nightmare of Ashihama | `nightmare` |
| Nightmare | `nightmare` |
| Phosani's Nightmare | `phosanisNightmare` |
| Obor | `obor` |
| Phantom Muspah | `phantomMuspah` |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

99
__tests__/b0atyStats.csv Normal file
View File

@@ -0,0 +1,99 @@
3745,2277,840426611
24080,99,30276246
9559,99,31228199
2212,99,143599407
3550,99,123248914
6564,99,109953707
4244,99,15144846
4107,99,57072224
135556,99,13353511
25855,99,16903268
53472,99,13456850
22453,99,16654917
17690,99,19118307
45816,99,14059776
10409,99,16530015
27702,99,14765145
39430,99,13235063
6722,99,15754877
51813,99,14023027
5324,99,33835543
3154,99,69527777
4719,99,22733384
15135,99,17952893
1089,99,17998715
-1,-1
-1,-1
26164,10
12886,3
148658,8
103161,7
377,9556
862,825
148,6000
2153,1606
42107,298
504,459
1816,368
14701,2521
-1,-1
6776,14780
4773,1060
2741,2192
90203,594
29573,143
3668,1830
206778,5
32897,293
64112,58
18492,1905
178941,34
2379,501
82902,50
147358,26
51973,287
2377,2000
147406,41
32225,707
42229,698
32212,714
79605,25
9517,688
19125,1228
65849,474
2080,2140
16433,182
2006,2046
48575,580
115056,1908
10406,920
2196,1444
1005,33
28413,620
4157,744
4261,500
993,217
21848,251
66260,119
108898,26
373975,9
2005,1569
40001,273
192608,7
78367,201
5679,583
478,1760
22932,250
15059,7
7161,3066
101555,35
610,933
15733,2
-1,-1
2051,2078
194,7096
69260,51
14039,2780
876367,62
165341,25
226318,275
Can't render this file because it has a wrong number of fields in line 25.

201
__tests__/b0atyStats.json Normal file
View File

@@ -0,0 +1,201 @@
{
"skills": [
{
"id": 0,
"name": "Overall",
"rank": 3745,
"level": 2277,
"xp": 840426611
},
{ "id": 1, "name": "Attack", "rank": 24080, "level": 99, "xp": 30276246 },
{ "id": 2, "name": "Defence", "rank": 9559, "level": 99, "xp": 31228199 },
{ "id": 3, "name": "Strength", "rank": 2212, "level": 99, "xp": 143599407 },
{
"id": 4,
"name": "Hitpoints",
"rank": 3550,
"level": 99,
"xp": 123248914
},
{ "id": 5, "name": "Ranged", "rank": 6564, "level": 99, "xp": 109953707 },
{ "id": 6, "name": "Prayer", "rank": 4244, "level": 99, "xp": 15144846 },
{ "id": 7, "name": "Magic", "rank": 4107, "level": 99, "xp": 57072224 },
{ "id": 8, "name": "Cooking", "rank": 135556, "level": 99, "xp": 13353511 },
{
"id": 9,
"name": "Woodcutting",
"rank": 25855,
"level": 99,
"xp": 16903268
},
{
"id": 10,
"name": "Fletching",
"rank": 53472,
"level": 99,
"xp": 13456850
},
{ "id": 11, "name": "Fishing", "rank": 22453, "level": 99, "xp": 16654917 },
{
"id": 12,
"name": "Firemaking",
"rank": 17690,
"level": 99,
"xp": 19118307
},
{
"id": 13,
"name": "Crafting",
"rank": 45816,
"level": 99,
"xp": 14059776
},
{
"id": 14,
"name": "Smithing",
"rank": 10409,
"level": 99,
"xp": 16530015
},
{ "id": 15, "name": "Mining", "rank": 27702, "level": 99, "xp": 14765145 },
{
"id": 16,
"name": "Herblore",
"rank": 39430,
"level": 99,
"xp": 13235063
},
{ "id": 17, "name": "Agility", "rank": 6722, "level": 99, "xp": 15754877 },
{
"id": 18,
"name": "Thieving",
"rank": 51813,
"level": 99,
"xp": 14023027
},
{ "id": 19, "name": "Slayer", "rank": 5324, "level": 99, "xp": 33835543 },
{ "id": 20, "name": "Farming", "rank": 3154, "level": 99, "xp": 69527777 },
{
"id": 21,
"name": "Runecraft",
"rank": 4719,
"level": 99,
"xp": 22733384
},
{ "id": 22, "name": "Hunter", "rank": 15135, "level": 99, "xp": 17952893 },
{
"id": 23,
"name": "Construction",
"rank": 1089,
"level": 99,
"xp": 17998715
}
],
"activities": [
{ "id": 0, "name": "League Points", "rank": -1, "score": -1 },
{ "id": 1, "name": "Deadman Points", "rank": -1, "score": -1 },
{ "id": 2, "name": "Bounty Hunter - Hunter", "rank": 26164, "score": 10 },
{ "id": 3, "name": "Bounty Hunter - Rogue", "rank": 12886, "score": 3 },
{
"id": 4,
"name": "Bounty Hunter (Legacy) - Hunter",
"rank": 148658,
"score": 8
},
{
"id": 5,
"name": "Bounty Hunter (Legacy) - Rogue",
"rank": 103161,
"score": 7
},
{ "id": 6, "name": "Clue Scrolls (all)", "rank": 377, "score": 9556 },
{ "id": 7, "name": "Clue Scrolls (beginner)", "rank": 862, "score": 825 },
{ "id": 8, "name": "Clue Scrolls (easy)", "rank": 148, "score": 6000 },
{ "id": 9, "name": "Clue Scrolls (medium)", "rank": 2153, "score": 1606 },
{ "id": 10, "name": "Clue Scrolls (hard)", "rank": 42107, "score": 298 },
{ "id": 11, "name": "Clue Scrolls (elite)", "rank": 504, "score": 459 },
{ "id": 12, "name": "Clue Scrolls (master)", "rank": 1816, "score": 368 },
{ "id": 13, "name": "LMS - Rank", "rank": 14701, "score": 2521 },
{ "id": 14, "name": "PvP Arena - Rank", "rank": -1, "score": -1 },
{ "id": 15, "name": "Soul Wars Zeal", "rank": 6776, "score": 14780 },
{ "id": 16, "name": "Rifts closed", "rank": 4773, "score": 1060 },
{ "id": 17, "name": "Abyssal Sire", "rank": 2741, "score": 2192 },
{ "id": 18, "name": "Alchemical Hydra", "rank": 90203, "score": 594 },
{ "id": 19, "name": "Artio", "rank": 29573, "score": 143 },
{ "id": 20, "name": "Barrows Chests", "rank": 3668, "score": 1830 },
{ "id": 21, "name": "Bryophyta", "rank": 206778, "score": 5 },
{ "id": 22, "name": "Callisto", "rank": 32897, "score": 293 },
{ "id": 23, "name": "Calvar'ion", "rank": 64112, "score": 58 },
{ "id": 24, "name": "Cerberus", "rank": 18492, "score": 1905 },
{ "id": 25, "name": "Chambers of Xeric", "rank": 178941, "score": 34 },
{
"id": 26,
"name": "Chambers of Xeric: Challenge Mode",
"rank": 2379,
"score": 501
},
{ "id": 27, "name": "Chaos Elemental", "rank": 82902, "score": 50 },
{ "id": 28, "name": "Chaos Fanatic", "rank": 147358, "score": 26 },
{ "id": 29, "name": "Commander Zilyana", "rank": 51973, "score": 287 },
{ "id": 30, "name": "Corporeal Beast", "rank": 2377, "score": 2000 },
{ "id": 31, "name": "Crazy Archaeologist", "rank": 147406, "score": 41 },
{ "id": 32, "name": "Dagannoth Prime", "rank": 32225, "score": 707 },
{ "id": 33, "name": "Dagannoth Rex", "rank": 42229, "score": 698 },
{ "id": 34, "name": "Dagannoth Supreme", "rank": 32212, "score": 714 },
{ "id": 35, "name": "Deranged Archaeologist", "rank": 79605, "score": 25 },
{ "id": 36, "name": "Duke Sucellus", "rank": 9517, "score": 688 },
{ "id": 37, "name": "General Graardor", "rank": 19125, "score": 1228 },
{ "id": 38, "name": "Giant Mole", "rank": 65849, "score": 474 },
{ "id": 39, "name": "Grotesque Guardians", "rank": 2080, "score": 2140 },
{ "id": 40, "name": "Hespori", "rank": 16433, "score": 182 },
{ "id": 41, "name": "Kalphite Queen", "rank": 2006, "score": 2046 },
{ "id": 42, "name": "King Black Dragon", "rank": 48575, "score": 580 },
{ "id": 43, "name": "Kraken", "rank": 115056, "score": 1908 },
{ "id": 44, "name": "Kree'Arra", "rank": 10406, "score": 920 },
{ "id": 45, "name": "K'ril Tsutsaroth", "rank": 2196, "score": 1444 },
{ "id": 46, "name": "Mimic", "rank": 1005, "score": 33 },
{ "id": 47, "name": "Nex", "rank": 28413, "score": 620 },
{ "id": 48, "name": "Nightmare", "rank": 4157, "score": 744 },
{ "id": 49, "name": "Phosani's Nightmare", "rank": 4261, "score": 500 },
{ "id": 50, "name": "Obor", "rank": 993, "score": 217 },
{ "id": 51, "name": "Phantom Muspah", "rank": 21848, "score": 251 },
{ "id": 52, "name": "Sarachnis", "rank": 66260, "score": 119 },
{ "id": 53, "name": "Scorpia", "rank": 108898, "score": 26 },
{ "id": 54, "name": "Skotizo", "rank": 373975, "score": 9 },
{ "id": 55, "name": "Spindel", "rank": 2005, "score": 1569 },
{ "id": 56, "name": "Tempoross", "rank": 40001, "score": 273 },
{ "id": 57, "name": "The Gauntlet", "rank": 192608, "score": 7 },
{ "id": 58, "name": "The Corrupted Gauntlet", "rank": 78367, "score": 201 },
{ "id": 59, "name": "The Leviathan", "rank": 5679, "score": 583 },
{ "id": 60, "name": "The Whisperer", "rank": 478, "score": 1760 },
{ "id": 61, "name": "Theatre of Blood", "rank": 22932, "score": 250 },
{
"id": 62,
"name": "Theatre of Blood: Hard Mode",
"rank": 15059,
"score": 7
},
{
"id": 63,
"name": "Thermonuclear Smoke Devil",
"rank": 7161,
"score": 3066
},
{ "id": 64, "name": "Tombs of Amascut", "rank": 101555, "score": 35 },
{
"id": 65,
"name": "Tombs of Amascut: Expert Mode",
"rank": 610,
"score": 933
},
{ "id": 66, "name": "TzKal-Zuk", "rank": 15733, "score": 2 },
{ "id": 67, "name": "TzTok-Jad", "rank": -1, "score": -1 },
{ "id": 68, "name": "Vardorvis", "rank": 2051, "score": 2078 },
{ "id": 69, "name": "Venenatis", "rank": 194, "score": 7096 },
{ "id": 70, "name": "Vet'ion", "rank": 69260, "score": 51 },
{ "id": 71, "name": "Vorkath", "rank": 14039, "score": 2780 },
{ "id": 72, "name": "Wintertodt", "rank": 876367, "score": 62 },
{ "id": 73, "name": "Zalcano", "rank": 165341, "score": 25 },
{ "id": 74, "name": "Zulrah", "rank": 226318, "score": 275 }
]
}

View File

@@ -12,8 +12,10 @@ import {
getSkillPageURL,
getStatsURL,
BOSSES,
INVALID_FORMAT_ERROR,
BH_MODES
InvalidFormatError,
BH_MODES,
parseJsonStats,
HiscoresResponse
} from '../src/index';
const B0ATY_NAME = 'B0ATY';
@@ -25,7 +27,13 @@ const LYNX_TITAN_FORMATTED_NAME = 'Lynx Titan';
const attackTopPage = readFileSync(`${__dirname}/attackTopPage.html`, 'utf8');
const b0atyNamePage = readFileSync(`${__dirname}/b0atyNamePage.html`, 'utf8');
const lynxTitanStats = readFileSync(`${__dirname}/lynxTitanStats.csv`, 'utf8');
const b0atyStatsCsv = readFileSync(`${__dirname}/b0atyStats.csv`, 'utf8');
const b0atyStatsJson: HiscoresResponse = JSON.parse(
readFileSync(`${__dirname}/b0atyStats.json`, 'utf8')
);
const lynxTitanStats = JSON.parse(
readFileSync(`${__dirname}/lynxTitanStats.json`, 'utf8')
);
const lynxTitanNamePage = readFileSync(
`${__dirname}/lynxTitanNamePage.html`,
'utf8'
@@ -46,7 +54,7 @@ jest.spyOn(axios, 'get').mockImplementation((url) => {
if (getSkillPageURL('main', 'attack', 1) === url) {
return Promise.resolve({ data: attackTopPage });
}
if (getStatsURL('main', LYNX_TITAN_FORMATTED_NAME) === url) {
if (getStatsURL('main', LYNX_TITAN_FORMATTED_NAME, true) === url) {
return Promise.resolve({ status: 200, data: lynxTitanStats });
}
throw new Error(`No mock response for URL: ${url}`);
@@ -78,6 +86,7 @@ test('Parse CSV to json', () => {
810,99,37688883
92,99,32005622
23423,478
89554,301
89914,35
99834,25
99831,23
@@ -180,11 +189,12 @@ test('Parse CSV to json', () => {
construction: { rank: 92, level: 99, xp: 32005622 }
},
leaguePoints: { rank: 23423, score: 478 },
deadmanPoints: { rank: 89554, score: 301 },
bountyHunter: {
hunterV2: { rank: 89914, score: 35 },
rogueV2: { rank: 99834, score: 25 },
hunter: { rank: 99831, score: 23 },
rogue: { rank: 89912, score: 37 },
rogue: { rank: 89912, score: 37 }
},
lastManStanding: { rank: 4814, score: 898 },
pvpArena: { rank: 13, score: 4057 },
@@ -268,12 +278,12 @@ test('Parse CSV with unknown activity', () => {
const statsWithUnknownActivity = `${lynxTitanStats}
-1,-1`;
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
INVALID_FORMAT_ERROR
InvalidFormatError
);
});
test('Parse invalid CSV', () => {
expect(() => parseStats('invalid')).toThrow(INVALID_FORMAT_ERROR);
expect(() => parseStats('invalid')).toThrow(InvalidFormatError);
});
describe('Get name format', () => {
@@ -533,13 +543,13 @@ describe('Get stats options', () => {
beforeEach(() => {
axios.get = jest.fn(
(url) =>
new Promise<any>((resolve) =>
new Promise<any>((resolve) => {
resolve(
url === getPlayerTableURL('main', rsn)
? { data: lynxTitanNamePage }
: { status: 200, data: lynxTitanStats }
)
)
);
})
);
axiosMock = axios.get as any;
axiosMock.mockClear();
@@ -547,11 +557,11 @@ describe('Get stats options', () => {
it('fetches all gamemodes and formatted RSN when no options provided', async () => {
await getStats(rsn);
expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([
getStatsURL('main', rsn),
getStatsURL('main', rsn, true),
getPlayerTableURL('main', rsn),
getStatsURL('ironman', rsn),
getStatsURL('hardcore', rsn),
getStatsURL('ultimate', rsn)
getStatsURL('ironman', rsn, true),
getStatsURL('hardcore', rsn, true),
getStatsURL('ultimate', rsn, true)
]);
});
it('skips fetching formatted RSN when option is provided', async () => {
@@ -572,4 +582,16 @@ describe('Get stats options', () => {
)
).toBeFalsy();
});
it('omits excluded gamemodes', async () => {
const response = await getStats(rsn, {
otherGamemodes: ['ironman', 'ultimate']
});
expect(response.hardcore).toBeUndefined();
});
});
test('CSV and JSON parsing outputs identical object', async () => {
const csvOutput = parseStats(b0atyStatsCsv);
const jsonOutput = parseJsonStats(b0atyStatsJson);
expect(csvOutput).toEqual(jsonOutput);
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,98 +0,0 @@
1,2277,4600000000
15,99,200000000
28,99,200000000
18,99,200000000
7,99,200000000
8,99,200000000
11,99,200000000
32,99,200000000
157,99,200000000
15,99,200000000
12,99,200000000
9,99,200000000
48,99,200000000
4,99,200000000
3,99,200000000
25,99,200000000
5,99,200000000
24,99,200000000
12,99,200000000
2,99,200000000
19,99,200000000
7,99,200000000
4,99,200000000
4,99,200000000
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
764013,22
-1,-1
-1,-1
-1,-1
480246,22
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
196,186
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
Can't render this file because it has a wrong number of fields in line 25.

View File

@@ -0,0 +1,148 @@
{
"skills": [
{ "id": 0, "name": "Overall", "rank": 1, "level": 2277, "xp": 4600000000 },
{ "id": 1, "name": "Attack", "rank": 15, "level": 99, "xp": 200000000 },
{ "id": 2, "name": "Defence", "rank": 28, "level": 99, "xp": 200000000 },
{ "id": 3, "name": "Strength", "rank": 18, "level": 99, "xp": 200000000 },
{ "id": 4, "name": "Hitpoints", "rank": 7, "level": 99, "xp": 200000000 },
{ "id": 5, "name": "Ranged", "rank": 8, "level": 99, "xp": 200000000 },
{ "id": 6, "name": "Prayer", "rank": 11, "level": 99, "xp": 200000000 },
{ "id": 7, "name": "Magic", "rank": 31, "level": 99, "xp": 200000000 },
{ "id": 8, "name": "Cooking", "rank": 155, "level": 99, "xp": 200000000 },
{
"id": 9,
"name": "Woodcutting",
"rank": 15,
"level": 99,
"xp": 200000000
},
{ "id": 10, "name": "Fletching", "rank": 12, "level": 99, "xp": 200000000 },
{ "id": 11, "name": "Fishing", "rank": 9, "level": 99, "xp": 200000000 },
{
"id": 12,
"name": "Firemaking",
"rank": 48,
"level": 99,
"xp": 200000000
},
{ "id": 13, "name": "Crafting", "rank": 4, "level": 99, "xp": 200000000 },
{ "id": 14, "name": "Smithing", "rank": 3, "level": 99, "xp": 200000000 },
{ "id": 15, "name": "Mining", "rank": 24, "level": 99, "xp": 200000000 },
{ "id": 16, "name": "Herblore", "rank": 5, "level": 99, "xp": 200000000 },
{ "id": 17, "name": "Agility", "rank": 24, "level": 99, "xp": 200000000 },
{ "id": 18, "name": "Thieving", "rank": 12, "level": 99, "xp": 200000000 },
{ "id": 19, "name": "Slayer", "rank": 2, "level": 99, "xp": 200000000 },
{ "id": 20, "name": "Farming", "rank": 19, "level": 99, "xp": 200000000 },
{ "id": 21, "name": "Runecraft", "rank": 6, "level": 99, "xp": 200000000 },
{ "id": 22, "name": "Hunter", "rank": 4, "level": 99, "xp": 200000000 },
{
"id": 23,
"name": "Construction",
"rank": 4,
"level": 99,
"xp": 200000000
}
],
"activities": [
{ "id": 0, "name": "League Points", "rank": -1, "score": -1 },
{ "id": 1, "name": "Deadman Points", "rank": -1, "score": -1 },
{ "id": 2, "name": "Bounty Hunter - Hunter", "rank": -1, "score": -1 },
{ "id": 3, "name": "Bounty Hunter - Rogue", "rank": -1, "score": -1 },
{
"id": 4,
"name": "Bounty Hunter (Legacy) - Hunter",
"rank": -1,
"score": -1
},
{
"id": 5,
"name": "Bounty Hunter (Legacy) - Rogue",
"rank": -1,
"score": -1
},
{ "id": 6, "name": "Clue Scrolls (all)", "rank": 805212, "score": 22 },
{ "id": 7, "name": "Clue Scrolls (beginner)", "rank": -1, "score": -1 },
{ "id": 8, "name": "Clue Scrolls (easy)", "rank": -1, "score": -1 },
{ "id": 9, "name": "Clue Scrolls (medium)", "rank": -1, "score": -1 },
{ "id": 10, "name": "Clue Scrolls (hard)", "rank": 502504, "score": 22 },
{ "id": 11, "name": "Clue Scrolls (elite)", "rank": -1, "score": -1 },
{ "id": 12, "name": "Clue Scrolls (master)", "rank": -1, "score": -1 },
{ "id": 13, "name": "LMS - Rank", "rank": -1, "score": -1 },
{ "id": 14, "name": "PvP Arena - Rank", "rank": -1, "score": -1 },
{ "id": 15, "name": "Soul Wars Zeal", "rank": -1, "score": -1 },
{ "id": 16, "name": "Rifts closed", "rank": -1, "score": -1 },
{ "id": 17, "name": "Abyssal Sire", "rank": -1, "score": -1 },
{ "id": 18, "name": "Alchemical Hydra", "rank": -1, "score": -1 },
{ "id": 19, "name": "Artio", "rank": -1, "score": -1 },
{ "id": 20, "name": "Barrows Chests", "rank": -1, "score": -1 },
{ "id": 21, "name": "Bryophyta", "rank": -1, "score": -1 },
{ "id": 22, "name": "Callisto", "rank": -1, "score": -1 },
{ "id": 23, "name": "Calvar'ion", "rank": -1, "score": -1 },
{ "id": 24, "name": "Cerberus", "rank": -1, "score": -1 },
{ "id": 25, "name": "Chambers of Xeric", "rank": -1, "score": -1 },
{
"id": 26,
"name": "Chambers of Xeric: Challenge Mode",
"rank": -1,
"score": -1
},
{ "id": 27, "name": "Chaos Elemental", "rank": -1, "score": -1 },
{ "id": 28, "name": "Chaos Fanatic", "rank": -1, "score": -1 },
{ "id": 29, "name": "Commander Zilyana", "rank": -1, "score": -1 },
{ "id": 30, "name": "Corporeal Beast", "rank": -1, "score": -1 },
{ "id": 31, "name": "Crazy Archaeologist", "rank": -1, "score": -1 },
{ "id": 32, "name": "Dagannoth Prime", "rank": -1, "score": -1 },
{ "id": 33, "name": "Dagannoth Rex", "rank": -1, "score": -1 },
{ "id": 34, "name": "Dagannoth Supreme", "rank": -1, "score": -1 },
{ "id": 35, "name": "Deranged Archaeologist", "rank": -1, "score": -1 },
{ "id": 36, "name": "Duke Sucellus", "rank": -1, "score": -1 },
{ "id": 37, "name": "General Graardor", "rank": -1, "score": -1 },
{ "id": 38, "name": "Giant Mole", "rank": -1, "score": -1 },
{ "id": 39, "name": "Grotesque Guardians", "rank": -1, "score": -1 },
{ "id": 40, "name": "Hespori", "rank": -1, "score": -1 },
{ "id": 41, "name": "Kalphite Queen", "rank": -1, "score": -1 },
{ "id": 42, "name": "King Black Dragon", "rank": -1, "score": -1 },
{ "id": 43, "name": "Kraken", "rank": -1, "score": -1 },
{ "id": 44, "name": "Kree'Arra", "rank": -1, "score": -1 },
{ "id": 45, "name": "K'ril Tsutsaroth", "rank": -1, "score": -1 },
{ "id": 46, "name": "Mimic", "rank": -1, "score": -1 },
{ "id": 47, "name": "Nex", "rank": -1, "score": -1 },
{ "id": 48, "name": "Nightmare", "rank": -1, "score": -1 },
{ "id": 49, "name": "Phosani's Nightmare", "rank": -1, "score": -1 },
{ "id": 50, "name": "Obor", "rank": -1, "score": -1 },
{ "id": 51, "name": "Phantom Muspah", "rank": -1, "score": -1 },
{ "id": 52, "name": "Sarachnis", "rank": -1, "score": -1 },
{ "id": 53, "name": "Scorpia", "rank": -1, "score": -1 },
{ "id": 54, "name": "Skotizo", "rank": -1, "score": -1 },
{ "id": 55, "name": "Spindel", "rank": -1, "score": -1 },
{ "id": 56, "name": "Tempoross", "rank": -1, "score": -1 },
{ "id": 57, "name": "The Gauntlet", "rank": -1, "score": -1 },
{ "id": 58, "name": "The Corrupted Gauntlet", "rank": -1, "score": -1 },
{ "id": 59, "name": "The Leviathan", "rank": -1, "score": -1 },
{ "id": 60, "name": "The Whisperer", "rank": -1, "score": -1 },
{ "id": 61, "name": "Theatre of Blood", "rank": -1, "score": -1 },
{
"id": 62,
"name": "Theatre of Blood: Hard Mode",
"rank": -1,
"score": -1
},
{ "id": 63, "name": "Thermonuclear Smoke Devil", "rank": -1, "score": -1 },
{ "id": 64, "name": "Tombs of Amascut", "rank": -1, "score": -1 },
{
"id": 65,
"name": "Tombs of Amascut: Expert Mode",
"rank": -1,
"score": -1
},
{ "id": 66, "name": "TzKal-Zuk", "rank": -1, "score": -1 },
{ "id": 67, "name": "TzTok-Jad", "rank": 225, "score": 186 },
{ "id": 68, "name": "Vardorvis", "rank": -1, "score": -1 },
{ "id": 69, "name": "Venenatis", "rank": -1, "score": -1 },
{ "id": 70, "name": "Vet'ion", "rank": -1, "score": -1 },
{ "id": 71, "name": "Vorkath", "rank": -1, "score": -1 },
{ "id": 72, "name": "Wintertodt", "rank": -1, "score": -1 },
{ "id": 73, "name": "Zalcano", "rank": -1, "score": -1 },
{ "id": 74, "name": "Zulrah", "rank": -1, "score": -1 }
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "osrs-json-hiscores",
"version": "2.15.0",
"version": "2.16.2",
"description": "The Old School Runescape API wrapper that does more!",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -67,7 +67,10 @@
],
"ignorePatterns": [
"**/@types/*"
]
],
"rules": {
"max-classes-per-file": "off"
}
},
"prettier": {
"trailingComma": "none",
@@ -92,7 +95,7 @@
"testEnvironment": "node"
},
"dependencies": {
"axios": "^0.21.1",
"axios": "^1.6.2",
"jsdom": "^22.1.0",
"useragent-generator": "^1.1.0"
},

View File

@@ -1,4 +1,4 @@
import { AxiosRequestConfig, AxiosResponse } from 'axios';
import axios, { AxiosRequestConfig } from 'axios';
import { BinaryData, JSDOM } from 'jsdom';
import {
Player,
@@ -14,7 +14,8 @@ import {
ActivityName,
PlayerActivityRow,
Bosses,
GetStatsOptions
GetStatsOptions,
HiscoresResponse
} from './types';
import {
getStatsURL,
@@ -30,10 +31,50 @@ import {
getActivityPageURL,
httpGet,
BOSSES,
INVALID_FORMAT_ERROR,
validateRSN
InvalidFormatError,
PlayerNotFoundError,
HiScoresError,
validateRSN,
FORMATTED_SKILL_NAMES,
FORMATTED_BH_NAMES,
FORMATTED_CLUE_NAMES,
FORMATTED_BOSS_NAMES,
FORMATTED_LEAGUE_POINTS,
FORMATTED_LMS,
FORMATTED_PVP_ARENA,
FORMATTED_SOUL_WARS,
FORMATTED_RIFTS_CLOSED,
FORMATTED_DEADMAN_POINTS
} from './utils';
/**
* Gets a player's stats from the official OSRS JSON endpoint.
*
* @param rsn Username of the player.
* @param mode Gamemode to fetch ranks for.
* @param config Optional axios request config object.
* @returns Official JSON stats object.
*/
export async function getOfficialStats(
rsn: string,
mode: Gamemode = 'main',
config?: AxiosRequestConfig
): Promise<HiscoresResponse> {
validateRSN(rsn);
const url = getStatsURL(mode, rsn, true);
try {
const response = await httpGet<HiscoresResponse>(url, config);
return response.data;
} catch (err) {
if (!axios.isAxiosError(err)) throw err;
if (err.response?.status === 404) throw new PlayerNotFoundError();
throw new HiScoresError();
}
}
/**
* Screen scrapes the hiscores to get the formatted rsn of a player.
*
@@ -60,12 +101,84 @@ export async function getRSNFormat(
if (anchor) {
return rsnFromElement(anchor);
}
throw Error('Player not found');
throw new PlayerNotFoundError();
} catch {
throw Error('Player not found');
throw new HiScoresError();
}
}
/**
* Parses official JSON object of raw stats and returns a stats object.
*
* @param csv Raw JSON from the official OSRS API.
* @returns Parsed stats object.
*/
export function parseJsonStats(json: HiscoresResponse): Stats {
const getActivity = (formattedName: string): Activity => {
const hiscoresActivity = json.activities.find(
// We must match on name here since id is not guaranteed to be the same between updates
({ name }) => name.toLowerCase() === formattedName.toLowerCase()
);
return {
rank: hiscoresActivity?.rank ?? -1,
score: hiscoresActivity?.score ?? -1
};
};
const reduceActivity = <Key extends string, Reduced = Record<Key, Activity>>(
keys: Key[],
formattedNames: Record<Key, string>
): Reduced =>
keys.reduce<Reduced>(
(reducer, key) => ({
...reducer,
[key]: getActivity(formattedNames[key])
}),
{} as Reduced
);
const skills = SKILLS.reduce<Skills>((skillsObject, skillName) => {
const hiscoresSkill = json.skills.find(
// We must match on name here since id is not guaranteed to be the same between updates
({ name }) =>
name.toLowerCase() === FORMATTED_SKILL_NAMES[skillName].toLowerCase()
);
return {
...skillsObject,
[skillName]: {
rank: hiscoresSkill?.rank ?? -1,
level: hiscoresSkill?.level ?? -1,
xp: hiscoresSkill?.xp ?? -1
}
};
}, {} as Skills);
const bountyHunter = reduceActivity(BH_MODES, FORMATTED_BH_NAMES);
const clues = reduceActivity(CLUES, FORMATTED_CLUE_NAMES);
const bosses = reduceActivity(BOSSES, FORMATTED_BOSS_NAMES);
const leaguePoints = getActivity(FORMATTED_LEAGUE_POINTS);
const deadmanPoints = getActivity(FORMATTED_DEADMAN_POINTS);
const lastManStanding = getActivity(FORMATTED_LMS);
const pvpArena = getActivity(FORMATTED_PVP_ARENA);
const soulWarsZeal = getActivity(FORMATTED_SOUL_WARS);
const riftsClosed = getActivity(FORMATTED_RIFTS_CLOSED);
const stats: Stats = {
skills,
leaguePoints,
deadmanPoints,
bountyHunter,
lastManStanding,
pvpArena,
soulWarsZeal,
riftsClosed,
clues,
bosses
};
return stats;
}
/**
* Parses CSV string of raw stats and returns a stats object.
*
@@ -78,11 +191,8 @@ export function parseStats(csv: string): Stats {
.filter((entry) => !!entry)
.map((stat) => stat.split(','));
if (
splitCSV.length !==
SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 5
) {
throw Error(INVALID_FORMAT_ERROR);
if (splitCSV.length !== SKILLS.length + ACTIVITIES.length) {
throw new InvalidFormatError();
}
const skillObjects: Skill[] = splitCSV
@@ -108,7 +218,7 @@ export function parseStats(csv: string): Stats {
return activity;
});
const [leaguePoints] = activityObjects.splice(0, 1);
const [leaguePoints, deadmanPoints] = activityObjects.splice(0, 2);
const bhObjects = activityObjects.splice(0, BH_MODES.length);
const clueObjects = activityObjects.splice(0, CLUES.length);
const [lastManStanding, pvpArena, soulWarsZeal, riftsClosed] =
@@ -142,6 +252,7 @@ export function parseStats(csv: string): Stats {
const stats: Stats = {
skills,
leaguePoints,
deadmanPoints,
bountyHunter,
lastManStanding,
pvpArena,
@@ -175,32 +286,19 @@ export async function getStats(
];
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
const mainRes = await httpGet<string>(
getStatsURL('main', rsn),
options?.axiosConfigs?.main
);
if (mainRes.status === 200) {
const emptyResponse: AxiosResponse<string> = {
status: 404,
data: '',
statusText: '',
headers: {},
config: {}
};
const main = await getOfficialStats(rsn, 'main', options?.axiosConfigs?.main);
const getModeStats = async (
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
): Promise<AxiosResponse<string>> =>
): Promise<HiscoresResponse | undefined> =>
otherGamemodes.includes(mode)
? httpGet<string>(
getStatsURL(mode, rsn),
options?.axiosConfigs?.[mode]
).catch((err) => err)
: emptyResponse;
const formattedName = shouldGetFormattedRsn
? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch(
? getOfficialStats(rsn, mode, options?.axiosConfigs?.[mode]).catch(
() => undefined
)
: undefined;
const formattedName = shouldGetFormattedRsn
? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch(() => undefined)
: undefined;
const player: Player = {
name: formattedName ?? rsn,
@@ -209,48 +307,42 @@ export async function getStats(
deulted: false,
deironed: false
};
player.main = parseStats(mainRes.data);
player.main = parseJsonStats(main);
const ironRes = await getModeStats('ironman');
if (ironRes.status === 200) {
player.ironman = parseStats(ironRes.data);
const hcRes = await getModeStats('hardcore');
const ultRes = await getModeStats('ultimate');
if (hcRes.status === 200) {
const iron = await getModeStats('ironman');
if (iron) {
player.ironman = parseJsonStats(iron);
const hc = await getModeStats('hardcore');
const ult = await getModeStats('ultimate');
if (hc) {
player.mode = 'hardcore';
player.hardcore = parseStats(hcRes.data);
player.hardcore = parseJsonStats(hc);
if (
player.ironman.skills.overall.xp !== player.hardcore.skills.overall.xp
) {
player.dead = true;
player.mode = 'ironman';
}
if (
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
) {
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) {
player.deironed = true;
player.mode = 'main';
}
} else if (ultRes.status === 200) {
} else if (ult) {
player.mode = 'ultimate';
player.ultimate = parseStats(ultRes.data);
player.ultimate = parseJsonStats(ult);
if (
player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp
) {
player.deulted = true;
player.mode = 'ironman';
}
if (
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
) {
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) {
player.deironed = true;
player.mode = 'main';
}
} else {
player.mode = 'ironman';
if (
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
) {
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) {
player.deironed = true;
player.mode = 'main';
}
@@ -259,8 +351,6 @@ export async function getStats(
return player;
}
throw Error('Player not found');
}
/**
* Fetches stats from the OSRS API and returns them as an object.
@@ -279,11 +369,8 @@ export async function getStatsByGamemode(
if (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode');
}
const response = await httpGet<string>(getStatsURL(mode, rsn), config);
if (response.status !== 200) {
throw Error('Player not found');
}
const stats = parseStats(response.data);
const response = await getOfficialStats(rsn, mode, config);
const stats = parseJsonStats(response);
return stats;
}

View File

@@ -130,6 +130,7 @@ export type Bosses = { [Type in Boss]: Activity };
export type ActivityName =
| 'leaguePoints'
| 'deadmanPoints'
| 'hunterBHV2'
| 'rogueBHV2'
| 'hunterBH'
@@ -150,7 +151,14 @@ export type ActivityName =
export interface Stats {
skills: Skills;
clues: Clues;
/**
* Will only contain rank and score data for the `seasonal` gamemode.
*/
leaguePoints: Activity;
/**
* Will only contain rank and score data for the `tournament` gamemode.
*/
deadmanPoints: Activity;
bountyHunter: BH;
lastManStanding: Activity;
pvpArena: Activity;
@@ -199,3 +207,41 @@ export interface GetStatsOptions {
rsn?: AxiosRequestConfig;
};
}
export interface HiscoresCommon {
/**
* This field behaves more like an index than a true unique ID.
*/
id: number;
/**
* The display name of this skill / activity.
*/
name: string;
/**
* The player's official hiscores rank in this skill / activity.
*/
rank: number;
}
export interface HiscoresSkill extends HiscoresCommon {
/**
* The player's current level in this skill.
*/
level: number;
/**
* The player's current experience in this skill.
*/
xp: number;
}
export interface HiscoresActivity extends HiscoresCommon {
/**
* The player's current score in this activity.
*/
score: number;
}
export interface HiscoresResponse {
skills: HiscoresSkill[];
activities: HiscoresActivity[];
}

View File

@@ -9,6 +9,7 @@ import {
export const BASE_URL = 'https://secure.runescape.com/m=hiscore_oldschool';
export const STATS_URL = 'index_lite.ws?player=';
export const JSON_STATS_URL = 'index_lite.json?player=';
export const SCORES_URL = 'overall.ws?';
export type GamemodeUrl = {
@@ -134,6 +135,7 @@ export const BOSSES: Boss[] = [
];
export const ACTIVITIES: ActivityName[] = [
'leaguePoints',
'deadmanPoints',
'hunterBHV2',
'rogueBHV2',
'hunterBH',
@@ -188,7 +190,7 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
krilTsutsaroth: "K'ril Tsutsaroth",
mimic: 'Mimic',
nex: 'Nex',
nightmare: 'The Nightmare of Ashihama',
nightmare: 'Nightmare',
phosanisNightmare: "Phosani's Nightmare",
obor: 'Obor',
phantomMuspah: 'Phantom Muspah',
@@ -273,10 +275,49 @@ export const FORMATTED_BH_NAMES: FormattedBHNames = {
hunterV2: 'Bounty Hunter - Hunter'
};
export const FORMATTED_LMS = 'Last Man Standing';
export const FORMATTED_PVP_ARENA = 'PvP Arena';
export const FORMATTED_LMS = 'LMS - Rank';
export const FORMATTED_PVP_ARENA = 'PvP Arena - Rank';
export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal';
export const FORMATTED_LEAGUE_POINTS = 'League Points';
export const FORMATTED_RIFTS_CLOSED = 'Rifts Closed';
export const FORMATTED_DEADMAN_POINTS = 'Deadman Points';
export const FORMATTED_RIFTS_CLOSED = 'Rifts closed';
export const INVALID_FORMAT_ERROR = 'Invalid hiscores format';
export const PLAYER_NOT_FOUND_ERROR = 'Player not found';
export const HISCORES_ERROR = 'HiScores not responding';
export class InvalidFormatError extends Error {
__proto__ = Error;
constructor() {
super(INVALID_FORMAT_ERROR);
Object.setPrototypeOf(this, InvalidFormatError.prototype);
}
}
export class InvalidRSNError extends Error {
__proto__ = Error;
constructor(message: string) {
super(message);
Object.setPrototypeOf(this, InvalidRSNError.prototype);
}
}
export class PlayerNotFoundError extends Error {
__proto__ = Error;
constructor() {
super(PLAYER_NOT_FOUND_ERROR);
Object.setPrototypeOf(this, PlayerNotFoundError.prototype);
}
}
export class HiScoresError extends Error {
__proto__ = Error;
constructor() {
super(HISCORES_ERROR);
Object.setPrototypeOf(this, HiScoresError.prototype);
}
}

View File

@@ -6,7 +6,9 @@ import {
STATS_URL,
SCORES_URL,
SKILLS,
ACTIVITIES
ACTIVITIES,
JSON_STATS_URL,
InvalidRSNError
} from './constants';
/**
@@ -14,10 +16,13 @@ import {
*
* @param gamemode Gamemode to fetch ranks for.
* @param rsn Username of the player.
* @param json If the JSON endpoint is desired instead of CSV.
* @returns Encoded stats URL.
*/
export const getStatsURL = (gamemode: Gamemode, rsn: string) =>
`${GAMEMODE_URL[gamemode]}${STATS_URL}${encodeURIComponent(rsn)}`;
export const getStatsURL = (gamemode: Gamemode, rsn: string, json = false) =>
`${GAMEMODE_URL[gamemode]}${
json ? JSON_STATS_URL : STATS_URL
}${encodeURIComponent(rsn)}`;
/**
* Will generate a player table URL for the official OSRS hiscores website.
@@ -115,10 +120,10 @@ export const httpGet = <Response>(
*/
export const validateRSN = (rsn: string) => {
if (typeof rsn !== 'string') {
throw Error('RSN must be a string');
throw new InvalidRSNError('RSN must be a string');
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
throw Error('RSN contains invalid character');
throw new InvalidRSNError('RSN contains invalid character');
} else if (rsn.length > 12 || rsn.length < 1) {
throw Error('RSN must be between 1 and 12 characters');
throw new InvalidRSNError('RSN must be between 1 and 12 characters');
}
};

117
yarn.lock
View File

@@ -29,6 +29,14 @@
dependencies:
"@babel/highlight" "^7.22.5"
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies:
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/compat-data@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730"
@@ -55,7 +63,7 @@
json5 "^2.2.2"
semver "^6.3.1"
"@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2":
"@babel/generator@^7.22.9", "@babel/generator@^7.7.2":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d"
integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==
@@ -65,6 +73,16 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-compilation-targets@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892"
@@ -76,18 +94,23 @@
lru-cache "^5.1.1"
semver "^6.3.1"
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-environment-visitor@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98"
integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==
"@babel/helper-function-name@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be"
integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
"@babel/template" "^7.22.5"
"@babel/types" "^7.22.5"
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
@@ -148,6 +171,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-identifier@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193"
@@ -176,6 +204,15 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/highlight@^7.22.13":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies:
"@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/highlight@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031"
@@ -195,6 +232,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae"
integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -293,6 +335,15 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/template@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
@@ -312,18 +363,18 @@
"@babel/types" "^7.12.7"
"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8":
version "7.22.8"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e"
integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies:
"@babel/code-frame" "^7.22.5"
"@babel/generator" "^7.22.7"
"@babel/helper-environment-visitor" "^7.22.5"
"@babel/helper-function-name" "^7.22.5"
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.22.7"
"@babel/types" "^7.22.5"
"@babel/parser" "^7.23.0"
"@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
@@ -345,6 +396,15 @@
"@babel/helper-validator-identifier" "^7.22.5"
to-fast-properties "^2.0.0"
"@babel/types@^7.22.15", "@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -1227,12 +1287,14 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
axios@^0.21.1:
version "0.21.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.2.tgz#21297d5084b2aeeb422f5d38e7be4fbb82239017"
integrity sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==
axios@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
dependencies:
follow-redirects "^1.14.0"
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-jest@^29.6.1:
version "29.6.1"
@@ -2366,10 +2428,10 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
follow-redirects@^1.14.0:
version "1.14.8"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
for-each@^0.3.3:
version "0.3.3"
@@ -4671,6 +4733,11 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
psl@^1.1.33:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"