@timmsy/riftjs 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ name: Publish package
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ packages: write
14
+
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Setup Node
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: 20
23
+ registry-url: https://registry.npmjs.org
24
+
25
+ - name: Install dependencies
26
+ run: npm ci
27
+
28
+ - name: Run tests
29
+ run: npm test
30
+
31
+ - name: Validate tag matches package version
32
+ run: |
33
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
34
+ PKG_VERSION="$(node -p "require('./package.json').version")"
35
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
36
+ echo "Tag version v$TAG_VERSION does not match package.json version $PKG_VERSION"
37
+ exit 1
38
+ fi
39
+
40
+ - name: Publish to npm
41
+ run: npm publish --access public
42
+ env:
43
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
44
+
45
+ - name: Publish to GitHub Packages
46
+ run: |
47
+ npm config set @timmsy:registry https://npm.pkg.github.com
48
+ npm publish --registry=https://npm.pkg.github.com
49
+ env:
50
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 James Timms
3
+ Copyright (c) 2026 James Timms
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # RiftJS
2
+
3
+ A lightweight Node.js wrapper for the Riot Games API, providing easy access to League of Legends game data.
4
+
5
+ [![npm version](https://badge.fury.io/js/@timmsy%2Friftjs.svg)](https://www.npmjs.com/package/@timmsy/riftjs)
6
+ ![GitHub license](https://img.shields.io/github/license/timmsy1998/RiftJS)
7
+
8
+ ## Overview
9
+
10
+ RiftJS simplifies interaction with the Riot Games API and DataDragon static data. It supports fetching account details, summoner info, match history, and game data using a modular endpoint structure. Built with `axios` and `dotenv`, it’s designed for developers building League of Legends tools or applications.
11
+
12
+ ## Features
13
+
14
+ - **RiotAPI**: Fetch account data by Riot ID, summoner data by PUUID, match history, and match details.
15
+ - **DataDragon**: Access static game data like champions and items.
16
+ - **Region Support**: Handles platform (e.g., `EUW1`) and shard (e.g., `europe`) routing.
17
+ - **Modular Design**: Endpoints are organized in an `/endpoints/` directory for easy extension.
18
+
19
+ ## Installation
20
+
21
+ Install RiftJS via npm:
22
+
23
+ ```bash
24
+ npm install @timmsy/riftjs
25
+ ```
26
+
27
+ You’ll also need a Riot Games API key from [developer.riotgames.com](https://developer.riotgames.com/).
28
+
29
+ ## Setup
30
+
31
+ 1. **Install Dependencies**:
32
+ Ensure you have Node.js installed, then run the install command above.
33
+
34
+ 2. **Configure Environment**:
35
+ Create a `.env` file in your project root with your API key and region:
36
+
37
+ ```env
38
+ RIOT_API_KEY=RGAPI-your-api-key-here
39
+ REGION=EUW1
40
+ TEST_RIOT_ID=YourRiotName
41
+ TEST_TAG_LINE=EUW
42
+ ```
43
+
44
+ - Replace `RGAPI-your-api-key-here` with your API key.
45
+ - Use a short region code (e.g., `EUW1`, `NA1`). See [Region Mapping](#region-mapping) for details.
46
+ - `TEST_RIOT_ID` and `TEST_TAG_LINE` are used by the endpoint test script.
47
+
48
+ ## Usage
49
+
50
+ Here’s a basic example to get started:
51
+
52
+ ```js
53
+ const { RiotAPI, DataDragon } = require('@timmsy/riftjs');
54
+
55
+ // Initialize RiotAPI
56
+ const riot = new RiotAPI();
57
+
58
+ // Fetch account, summoner, and match data
59
+ async function fetchPlayerData() {
60
+ try {
61
+ const account = await riot.getAccountByRiotId('Timmsy#BRUV');
62
+ console.log('Account:', account);
63
+
64
+ const summoner = await riot.getSummonerByPuuid(account.puuid);
65
+ console.log('Summoner:', summoner);
66
+
67
+ const matchlist = await riot.getMatchlistByPuuid(account.puuid, { start: 0, count: 5 });
68
+ console.log('Matchlist:', matchlist);
69
+
70
+ const match = await riot.getMatchById(matchlist[0]);
71
+ console.log('Match:', match);
72
+ } catch (error) {
73
+ console.error('Error:', error.message);
74
+ }
75
+ }
76
+
77
+ // Initialize DataDragon
78
+ const dd = new DataDragon();
79
+
80
+ async function fetchStaticData() {
81
+ try {
82
+ const champions = await dd.getChampions();
83
+ console.log('Champions:', champions.data);
84
+
85
+ const items = await dd.getItems();
86
+ console.log('Items:', items.data);
87
+ } catch (error) {
88
+ console.error('Error:', error.message);
89
+ }
90
+ }
91
+
92
+ // Run the examples
93
+ fetchPlayerData();
94
+ fetchStaticData();
95
+ ```
96
+
97
+ ## API Reference
98
+
99
+ ### RiotAPI
100
+
101
+ - `getAccountByRiotId(riotId, [tagLine], [region])`: Fetch account by Riot ID (e.g., `Timmsy#BRUV`).
102
+ - `getSummonerByPuuid(puuid, [region])`: Get summoner data by PUUID.
103
+ - `getMatchlistByPuuid(puuid, [options], [region])`: Get match history (options: `{ start, count }`).
104
+ - `getMatchById(matchId, [region])`: Get full match payload (`metadata` + `info`) by match ID.
105
+ - `getMatchTimelineById(matchId, [region])`: Get timeline payload by match ID.
106
+ - `getMatchlistByPuuidAll(puuid, [options], [region], [pacing])`: Fetch all match IDs in pages of 100.
107
+ - `getMatchesWithDetailsByPuuid(puuid, [options], [region], [pacing])`: Fetch all match IDs and their match payloads.
108
+
109
+ `options` supports Riot Match-V5 query params:
110
+ - `startTime` (epoch seconds)
111
+ - `endTime` (epoch seconds)
112
+ - `queue` (int)
113
+ - `type` (string)
114
+ - `start` (int, default `0`)
115
+ - `count` (int, `0-100`, single-page method only)
116
+
117
+ `pacing` helps respect rate limits on multi-request methods:
118
+ - `getMatchlistByPuuidAll`: `{ delayMs, maxMatches }`
119
+ - `getMatchesWithDetailsByPuuid`: `{ pageDelayMs, detailDelayMs, maxMatches }`
120
+
121
+ ### DataDragon
122
+
123
+ - `getChampions()`: Fetch all champion data.
124
+ - `getItems()`: Fetch all item data.
125
+ - `new DataDragon()` resolves the latest Data Dragon patch automatically.
126
+ - `new DataDragon('x.y.z')` pins requests to an explicit patch version.
127
+
128
+ ## Region Mapping
129
+
130
+ RiftJS uses a region map to route requests correctly:
131
+
132
+ - **Platform Routing** (e.g., Summoner V4):
133
+
134
+ - `EUW1` → `euw1.api.riotgames.com`
135
+ - `NA1` → `na1.api.riotgames.com`
136
+
137
+ - **Shard Routing** (e.g., Account V1, Match V5):
138
+
139
+ - `EUW1` → `europe.api.riotgames.com`
140
+ - `NA1` → `americas.api.riotgames.com`
141
+
142
+ Supported regions: `BR1`, `EUN1`, `EUW1`, `JP1`, `KR`, `LA1`, `LA2`, `NA1`, `OC1`, `TR1`, `RU`, `PH2`, `SG2`, `TH2`, `TW2`, `VN2`.
143
+
144
+ ## Keywords
145
+
146
+ - riot-api
147
+ - league-of-legends
148
+ - lol-api
149
+ - datadragon
150
+ - summoner
151
+ - match-history
152
+ - game-data
153
+ - node-js
154
+ - javascript
155
+ - api-wrapper
156
+
157
+ ## Development
158
+
159
+ To contribute or run locally:
160
+
161
+ 1. Clone the repo:
162
+
163
+ ```bash
164
+ git clone https://github.com/timmsy1998/RiftJS.git
165
+ cd RiftJS
166
+ ```
167
+
168
+ 2. Install dependencies:
169
+
170
+ ```bash
171
+ npm install
172
+ ```
173
+
174
+ 3. Create a `.env` file (see [Setup](#setup)).
175
+ 4. Run endpoint checks:
176
+
177
+ ```bash
178
+ npm test
179
+ ```
180
+
181
+ - Riot API calls run when `TEST_RIOT_ID` is set.
182
+ - Data Dragon calls always run.
183
+
184
+ ## License
185
+
186
+ MIT License © 2025 James Timms. See [LICENSE](LICENSE) for details.
187
+
188
+ ## Links
189
+
190
+ - **npm Registry**: [https://www.npmjs.com/package/@timmsy/riftjs](https://www.npmjs.com/package/@timmsy/riftjs)
191
+ - **GitHub Repository**: [https://github.com/timmsy1998/RiftJS](https://github.com/timmsy1998/RiftJS)
192
+ - **Riot Developer Portal**: [https://developer.riotgames.com/](https://developer.riotgames.com/)
@@ -0,0 +1,40 @@
1
+ const axios = require('axios');
2
+
3
+ module.exports = (baseURLOrResolver) => {
4
+ const resolveBaseURL = async () => {
5
+ if (typeof baseURLOrResolver === 'function') {
6
+ return baseURLOrResolver();
7
+ }
8
+ return baseURLOrResolver;
9
+ };
10
+
11
+ return {
12
+ /**
13
+ * Fetch champion metadata for the configured Data Dragon version/locale.
14
+ * @returns {Promise<object>} Champion data.
15
+ */
16
+ async getChampions() {
17
+ try {
18
+ const baseURL = await resolveBaseURL();
19
+ const response = await axios.get(`${baseURL}/champion.json`);
20
+ return response.data;
21
+ } catch (error) {
22
+ throw new Error(`DataDragon error: ${error.message}`);
23
+ }
24
+ },
25
+
26
+ /**
27
+ * Fetch item metadata for the configured Data Dragon version/locale.
28
+ * @returns {Promise<object>} Item data.
29
+ */
30
+ async getItems() {
31
+ try {
32
+ const baseURL = await resolveBaseURL();
33
+ const response = await axios.get(`${baseURL}/item.json`);
34
+ return response.data;
35
+ } catch (error) {
36
+ throw new Error(`DataDragon error: ${error.message}`);
37
+ }
38
+ },
39
+ };
40
+ };
@@ -0,0 +1,176 @@
1
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2
+
3
+ module.exports = (client, defaultRegion, regionMap) => ({
4
+ /**
5
+ * Fetch account data by Riot ID.
6
+ * @param {string} riotId - Riot ID (e.g., "Timmsy#BRUV" or "Timmsy", "BRUV").
7
+ * @param {string} [tagLine] - Optional tagLine if not included in riotId.
8
+ * @param {string} [region] - Region code used to resolve shard routing.
9
+ * @returns {Promise<object>} Account payload including `puuid`.
10
+ */
11
+ async getAccountByRiotId(riotId, tagLine = null, region = defaultRegion) {
12
+ let gameName, tag;
13
+ if (riotId.includes('#')) {
14
+ [gameName, tag] = riotId.split('#');
15
+ } else {
16
+ gameName = riotId;
17
+ tag = tagLine || '';
18
+ }
19
+ if (!tag) throw new Error('TagLine is required for getAccountByRiotId');
20
+ const shard = regionMap[region].shard;
21
+ try {
22
+ const response = await client.get(`/riot/account/v1/accounts/by-riot-id/${encodeURIComponent(gameName)}/${encodeURIComponent(tag)}`, {
23
+ baseURL: `https://${shard}`,
24
+ });
25
+ return response.data;
26
+ } catch (error) {
27
+ throw this._handleError(error);
28
+ }
29
+ },
30
+
31
+ /**
32
+ * Fetch Summoner-V4 data for a player's PUUID.
33
+ * @param {string} puuid - Encrypted PUUID.
34
+ * @param {string} [region] - Region code used to resolve platform routing.
35
+ * @returns {Promise<object>} Summoner payload.
36
+ */
37
+ async getSummonerByPuuid(puuid, region = defaultRegion) {
38
+ const platform = regionMap[region].platform;
39
+ try {
40
+ const response = await client.get(`/lol/summoner/v4/summoners/by-puuid/${encodeURIComponent(puuid)}`, {
41
+ baseURL: `https://${platform}`,
42
+ });
43
+ return response.data;
44
+ } catch (error) {
45
+ throw this._handleError(error);
46
+ }
47
+ },
48
+
49
+ /**
50
+ * Fetch match IDs for a PUUID from Match-V5.
51
+ * @param {string} puuid - Player PUUID.
52
+ * @param {object} [options] - Query params such as `start` and `count`.
53
+ * @param {string} [region] - Region code used to resolve shard routing.
54
+ * @returns {Promise<string[]>} Array of match IDs.
55
+ */
56
+ async getMatchlistByPuuid(puuid, options = {}, region = defaultRegion) {
57
+ const shard = regionMap[region].shard;
58
+ try {
59
+ const response = await client.get(`/lol/match/v5/matches/by-puuid/${encodeURIComponent(puuid)}/ids`, {
60
+ baseURL: `https://${shard}`,
61
+ params: options,
62
+ });
63
+ return response.data;
64
+ } catch (error) {
65
+ throw this._handleError(error);
66
+ }
67
+ },
68
+
69
+ /**
70
+ * Fetch full match details by match ID.
71
+ * @param {string} matchId - Match ID (e.g., "EUW1_1234567890").
72
+ * @param {string} [region] - Region code used to resolve shard routing.
73
+ * @returns {Promise<object>} Match payload.
74
+ */
75
+ async getMatchById(matchId, region = defaultRegion) {
76
+ const shard = regionMap[region].shard;
77
+ try {
78
+ const response = await client.get(`/lol/match/v5/matches/${matchId}`, {
79
+ baseURL: `https://${shard}`,
80
+ });
81
+ return response.data;
82
+ } catch (error) {
83
+ throw this._handleError(error);
84
+ }
85
+ },
86
+
87
+ /**
88
+ * Fetch match timeline data by match ID.
89
+ * @param {string} matchId - Match ID (e.g., "EUW1_1234567890").
90
+ * @param {string} [region] - Region code used to resolve shard routing.
91
+ * @returns {Promise<object>} Match timeline payload.
92
+ */
93
+ async getMatchTimelineById(matchId, region = defaultRegion) {
94
+ const shard = regionMap[region].shard;
95
+ try {
96
+ const response = await client.get(`/lol/match/v5/matches/${matchId}/timeline`, {
97
+ baseURL: `https://${shard}`,
98
+ });
99
+ return response.data;
100
+ } catch (error) {
101
+ throw this._handleError(error);
102
+ }
103
+ },
104
+
105
+ /**
106
+ * Fetch all match IDs for a PUUID using Riot's max page size (100).
107
+ * @param {string} puuid - Player PUUID.
108
+ * @param {object} [options] - Riot filters: startTime, endTime, queue, type, start.
109
+ * @param {string} [region] - Region code used to resolve shard routing.
110
+ * @param {object} [pacing] - Pacing controls.
111
+ * @param {number} [pacing.delayMs=1250] - Delay between page requests.
112
+ * @param {number|null} [pacing.maxMatches=null] - Optional cap on total IDs returned.
113
+ * @returns {Promise<string[]>} All matched IDs.
114
+ */
115
+ async getMatchlistByPuuidAll(puuid, options = {}, region = defaultRegion, pacing = {}) {
116
+ const baseStart = Number.isInteger(options.start) ? options.start : 0;
117
+ const pageDelayMs = Number.isInteger(pacing.delayMs) ? pacing.delayMs : 1250;
118
+ const maxMatches = Number.isInteger(pacing.maxMatches) && pacing.maxMatches >= 0 ? pacing.maxMatches : null;
119
+ const filters = {
120
+ startTime: options.startTime,
121
+ endTime: options.endTime,
122
+ queue: options.queue,
123
+ type: options.type,
124
+ };
125
+
126
+ let start = baseStart;
127
+ const allMatchIds = [];
128
+
129
+ while (true) {
130
+ const remaining = maxMatches === null ? 100 : Math.min(100, maxMatches - allMatchIds.length);
131
+ if (remaining <= 0) break;
132
+
133
+ const page = await this.getMatchlistByPuuid(puuid, { ...filters, start, count: remaining }, region);
134
+ allMatchIds.push(...page);
135
+
136
+ if (page.length < remaining) break;
137
+ start += page.length;
138
+ if (pageDelayMs > 0) await sleep(pageDelayMs);
139
+ }
140
+
141
+ return allMatchIds;
142
+ },
143
+
144
+ /**
145
+ * Fetch match IDs and full match payloads for each ID.
146
+ * @param {string} puuid - Player PUUID.
147
+ * @param {object} [options] - Riot filters: startTime, endTime, queue, type, start.
148
+ * @param {string} [region] - Region code used to resolve shard routing.
149
+ * @param {object} [pacing] - Pacing controls.
150
+ * @param {number} [pacing.pageDelayMs=1250] - Delay between matchlist page requests.
151
+ * @param {number} [pacing.detailDelayMs=1250] - Delay between match detail requests.
152
+ * @param {number|null} [pacing.maxMatches=null] - Optional cap on total matches fetched.
153
+ * @returns {Promise<{matchIds: string[], matches: object[]}>} IDs and full match payloads.
154
+ */
155
+ async getMatchesWithDetailsByPuuid(puuid, options = {}, region = defaultRegion, pacing = {}) {
156
+ const pageDelayMs = Number.isInteger(pacing.pageDelayMs) ? pacing.pageDelayMs : 1250;
157
+ const detailDelayMs = Number.isInteger(pacing.detailDelayMs) ? pacing.detailDelayMs : 1250;
158
+ const maxMatches = Number.isInteger(pacing.maxMatches) && pacing.maxMatches >= 0 ? pacing.maxMatches : null;
159
+ const matchIds = await this.getMatchlistByPuuidAll(
160
+ puuid,
161
+ options,
162
+ region,
163
+ { delayMs: pageDelayMs, maxMatches }
164
+ );
165
+ const matches = [];
166
+
167
+ for (let i = 0; i < matchIds.length; i += 1) {
168
+ matches.push(await this.getMatchById(matchIds[i], region));
169
+ if (detailDelayMs > 0 && i < matchIds.length - 1) {
170
+ await sleep(detailDelayMs);
171
+ }
172
+ }
173
+
174
+ return { matchIds, matches };
175
+ },
176
+ });
package/index.js CHANGED
@@ -1,41 +1,42 @@
1
1
  require('dotenv').config();
2
2
  const axios = require('axios');
3
+ const riotEndpoints = require('./endpoints/riot');
4
+ const dataDragonEndpoints = require('./endpoints/datadragon');
5
+
6
+ // Maps region codes to the platform and routing shard hosts required by Riot APIs.
7
+ const regionMap = {
8
+ BR1: { platform: 'br1.api.riotgames.com', shard: 'americas.api.riotgames.com' },
9
+ EUN1: { platform: 'eun1.api.riotgames.com', shard: 'europe.api.riotgames.com' },
10
+ EUW1: { platform: 'euw1.api.riotgames.com', shard: 'europe.api.riotgames.com' },
11
+ JP1: { platform: 'jp1.api.riotgames.com', shard: 'asia.api.riotgames.com' },
12
+ KR: { platform: 'kr.api.riotgames.com', shard: 'asia.api.riotgames.com' },
13
+ LA1: { platform: 'la1.api.riotgames.com', shard: 'americas.api.riotgames.com' },
14
+ LA2: { platform: 'la2.api.riotgames.com', shard: 'americas.api.riotgames.com' },
15
+ NA1: { platform: 'na1.api.riotgames.com', shard: 'americas.api.riotgames.com' },
16
+ OC1: { platform: 'oc1.api.riotgames.com', shard: 'sea.api.riotgames.com' },
17
+ TR1: { platform: 'tr1.api.riotgames.com', shard: 'europe.api.riotgames.com' },
18
+ RU: { platform: 'ru.api.riotgames.com', shard: 'europe.api.riotgames.com' },
19
+ PH2: { platform: 'ph2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
20
+ SG2: { platform: 'sg2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
21
+ TH2: { platform: 'th2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
22
+ TW2: { platform: 'tw2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
23
+ VN2: { platform: 'vn2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
24
+ };
3
25
 
4
- /**
5
- * RiotAPI class for interacting with Riot Games API endpoints.
6
- */
7
26
  class RiotAPI {
8
27
  constructor() {
9
28
  this.apiKey = process.env.RIOT_API_KEY;
10
29
  if (!this.apiKey) throw new Error('RIOT_API_KEY is required in .env');
11
- this.region = process.env.REGION || 'euw1';
30
+ this.region = (process.env.REGION || 'EUW1').toUpperCase();
31
+ if (!regionMap[this.region]) throw new Error(`Invalid region: ${this.region}`);
12
32
  this.client = axios.create({
13
- baseURL: `https://${this.region}.api.riotgames.com`,
14
33
  headers: { 'X-Riot-Token': this.apiKey },
15
34
  });
35
+ // Attach endpoint methods with a shared axios client and region configuration.
36
+ Object.assign(this, riotEndpoints(this.client, this.region, regionMap));
16
37
  }
17
38
 
18
- /**
19
- * Get summoner data by name.
20
- * @param {string} name - Summoner name.
21
- * @param {string} [region] - Region (defaults to constructor region).
22
- * @returns {Promise<object>} Summoner data.
23
- */
24
- async getSummonerByName(name, region = this.region) {
25
- try {
26
- const response = await this.client.get(`/lol/summoner/v4/summoners/by-name/${encodeURIComponent(name)}`, {
27
- baseURL: `https://${region}.api.riotgames.com`,
28
- });
29
- return response.data;
30
- } catch (error) {
31
- throw this._handleError(error);
32
- }
33
- }
34
-
35
- /**
36
- * Handle axios errors and provide meaningful messages.
37
- * @private
38
- */
39
+ // Normalize axios errors to stable Error messages for consumers.
39
40
  _handleError(error) {
40
41
  if (error.response) {
41
42
  const { status, data } = error.response;
@@ -47,28 +48,41 @@ class RiotAPI {
47
48
  }
48
49
  }
49
50
 
50
- /**
51
- * DataDragon class for accessing static game data.
52
- */
53
51
  class DataDragon {
54
- constructor(version = '14.19.1', locale = 'en_US') {
52
+ constructor(version = null, locale = 'en_US') {
55
53
  this.version = version;
56
54
  this.locale = locale;
57
- this.baseURL = `https://ddragon.leagueoflegends.com/cdn/${this.version}/data/${this.locale}`;
55
+ this.baseURL = null;
56
+ this._baseURLPromise = null;
57
+
58
+ // Attach static-data endpoint methods that resolve the latest version when needed.
59
+ Object.assign(this, dataDragonEndpoints(() => this._resolveBaseURL()));
58
60
  }
59
61
 
60
- /**
61
- * Get all champion data.
62
- * @returns {Promise<object>} Champion data.
63
- */
64
- async getChampions() {
62
+ async _resolveBaseURL() {
63
+ if (this.baseURL) return this.baseURL;
64
+ if (this._baseURLPromise) return this._baseURLPromise;
65
+
66
+ this._baseURLPromise = (async () => {
67
+ if (!this.version) {
68
+ const response = await axios.get('https://ddragon.leagueoflegends.com/api/versions.json');
69
+ const latestVersion = Array.isArray(response.data) ? response.data[0] : null;
70
+ if (!latestVersion) {
71
+ throw new Error('Could not resolve latest Data Dragon version');
72
+ }
73
+ this.version = latestVersion;
74
+ }
75
+
76
+ this.baseURL = `https://ddragon.leagueoflegends.com/cdn/${this.version}/data/${this.locale}`;
77
+ return this.baseURL;
78
+ })();
79
+
65
80
  try {
66
- const response = await axios.get(`${this.baseURL}/champion.json`);
67
- return response.data;
68
- } catch (error) {
69
- throw new Error(`DataDragon error: ${error.message}`);
81
+ return await this._baseURLPromise;
82
+ } finally {
83
+ this._baseURLPromise = null;
70
84
  }
71
85
  }
72
86
  }
73
87
 
74
- module.exports = { RiotAPI, DataDragon };
88
+ module.exports = { RiotAPI, DataDragon };
package/package.json CHANGED
@@ -1,14 +1,27 @@
1
1
  {
2
2
  "name": "@timmsy/riftjs",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1"
6
+ "test": "npm run test:endpoints",
7
+ "test:endpoints": "node test-endpoints.js"
7
8
  },
8
9
  "dependencies": {
9
10
  "axios": "^1.7.7",
10
11
  "dotenv": "^16.4.5"
11
12
  },
13
+ "keywords": [
14
+ "riot-api",
15
+ "league-of-legends",
16
+ "lol-api",
17
+ "datadragon",
18
+ "summoner",
19
+ "match-history",
20
+ "game-data",
21
+ "node-js",
22
+ "javascript",
23
+ "api-wrapper"
24
+ ],
12
25
  "author": "James Timms",
13
26
  "description": "A lightweight Node.js wrapper for the Riot Games API, providing easy access to League of Legends game data.",
14
27
  "license": "MIT",
@@ -0,0 +1,65 @@
1
+ require('dotenv').config();
2
+ const { RiotAPI, DataDragon } = require('./index');
3
+
4
+ async function run() {
5
+ const riotId = process.env.TEST_RIOT_ID;
6
+ const tagLine = process.env.TEST_TAG_LINE;
7
+ const dd = new DataDragon();
8
+
9
+ if (process.env.RIOT_API_KEY && riotId) {
10
+ const riot = new RiotAPI();
11
+ const account = await riot.getAccountByRiotId(riotId, tagLine);
12
+ console.log('[PASS] getAccountByRiotId:', account.puuid);
13
+
14
+ const summoner = await riot.getSummonerByPuuid(account.puuid);
15
+ console.log('[PASS] getSummonerByPuuid:', {
16
+ puuid: summoner.puuid,
17
+ summonerLevel: summoner.summonerLevel,
18
+ });
19
+
20
+ const matchIds = await riot.getMatchlistByPuuid(account.puuid, { start: 0, count: 3 });
21
+ console.log('[PASS] getMatchlistByPuuid:', matchIds.length, 'match ids');
22
+
23
+ if (matchIds.length > 0) {
24
+ const match = await riot.getMatchById(matchIds[0]);
25
+ console.log('[PASS] getMatchById:', match.metadata.matchId);
26
+
27
+ const timeline = await riot.getMatchTimelineById(matchIds[0]);
28
+ console.log('[PASS] getMatchTimelineById:', timeline.metadata.matchId);
29
+
30
+ const allMatchIds = await riot.getMatchlistByPuuidAll(
31
+ account.puuid,
32
+ { start: 0 },
33
+ undefined,
34
+ { maxMatches: 2, delayMs: 0 }
35
+ );
36
+ console.log('[PASS] getMatchlistByPuuidAll:', allMatchIds.length, 'match ids');
37
+
38
+ const withDetails = await riot.getMatchesWithDetailsByPuuid(
39
+ account.puuid,
40
+ { start: 0 },
41
+ undefined,
42
+ { maxMatches: 2, pageDelayMs: 0, detailDelayMs: 0 }
43
+ );
44
+ console.log('[PASS] getMatchesWithDetailsByPuuid:', withDetails.matches.length, 'matches');
45
+ } else {
46
+ console.log('[SKIP] Match-by-id/timeline/all/details checks: no matches returned');
47
+ }
48
+ } else if (process.env.RIOT_API_KEY) {
49
+ console.log('[SKIP] Riot endpoints: set TEST_RIOT_ID (and TEST_TAG_LINE if needed)');
50
+ } else {
51
+ console.log('[SKIP] Riot endpoints: set RIOT_API_KEY and TEST_RIOT_ID');
52
+ }
53
+
54
+ const champions = await dd.getChampions();
55
+ console.log('[PASS] getChampions:', Object.keys(champions.data || {}).length, 'champions');
56
+ console.log('[INFO] DataDragon version:', dd.version);
57
+
58
+ const items = await dd.getItems();
59
+ console.log('[PASS] getItems:', Object.keys(items.data || {}).length, 'items');
60
+ }
61
+
62
+ run().catch((error) => {
63
+ console.error('[FAIL]', error.message);
64
+ process.exitCode = 1;
65
+ });