irdata_js 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # irdata_js
2
2
 
3
- JavaScript library to interact with the iRacing /data API.
3
+ A JavaScript library to interact with the iRacing /data API.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,6 +8,20 @@ JavaScript library to interact with the iRacing /data API.
8
8
  npm install irdata_js
9
9
  ```
10
10
 
11
+ ## Compatibility
12
+
13
+ - **Node.js**: v20.0.0 or newer.
14
+ - **Browsers**: Modern browsers supporting ES2022 (Chrome 100+, Firefox 100+, Safari 15.4+).
15
+
16
+ ## Client Registration
17
+
18
+ Before using the library, you must register your application with iRacing to obtain a Client ID and configure your Redirect URI.
19
+
20
+ Please refer to the [official iRacing Client Registration documentation](https://oauth.iracing.com/oauth2/book/client_registration.html).
21
+
22
+ > [!NOTE]
23
+ > It may take up to **10 business days** for registration requests to be processed.
24
+
11
25
  ## CDN Usage
12
26
 
13
27
  For direct usage in the browser without a build step, you can load the library via a CDN. The library is exposed as the global `irdata` variable.
@@ -16,7 +30,8 @@ For direct usage in the browser without a build step, you can load the library v
16
30
  <script src="https://unpkg.com/irdata_js/dist/index.global.js"></script>
17
31
  <script>
18
32
  const client = new irdata.IRacingClient({
19
- // ...
33
+ clientId: 'YOUR_CLIENT_ID',
34
+ redirectUri: 'YOUR_REDIRECT_URI',
20
35
  });
21
36
  </script>
22
37
  ```
@@ -31,18 +46,54 @@ The library supports OAuth 2.0 authentication.
31
46
  import { IRacingClient } from 'irdata_js';
32
47
 
33
48
  const client = new IRacingClient({
34
- auth: {
35
- clientId: 'YOUR_CLIENT_ID', // Required for OAuth
36
- redirectUri: 'YOUR_REDIRECT_URI', // Required for OAuth
37
- },
49
+ clientId: 'YOUR_CLIENT_ID', // Required for OAuth
50
+ redirectUri: 'YOUR_REDIRECT_URI', // Required for OAuth
38
51
  });
39
52
  ```
40
53
 
54
+ ### Configuration
55
+
56
+ The `IRacingClient` constructor accepts two optional configuration objects: `AuthConfig` and `ProxyConfig`.
57
+
58
+ ```javascript
59
+ const authConfig = {
60
+ clientId: 'YOUR_CLIENT_ID',
61
+ redirectUri: 'YOUR_REDIRECT_URI',
62
+ };
63
+
64
+ const proxyConfig = {
65
+ apiUrl: 'https://your-proxy.com/data',
66
+ fileProxyUrl: 'https://your-proxy.com/passthrough',
67
+ authBaseUrl: 'https://your-proxy.com/oauth2',
68
+ tokenEndpoint: 'https://your-proxy.com/token',
69
+ };
70
+
71
+ const client = new IRacingClient(authConfig, proxyConfig);
72
+ ```
73
+
74
+ #### AuthConfig
75
+
76
+ | Property | Type | Required | Description |
77
+ | :------------ | :------- | :------- | :---------------------------------------------------------------- |
78
+ | `clientId` | `string` | **Yes** | Your iRacing OAuth client ID. |
79
+ | `redirectUri` | `string` | **Yes** | The URI iRacing will redirect to after successful authentication. |
80
+
81
+ #### ProxyConfig
82
+
83
+ Since the iRacing API and S3 buckets do not support CORS, you need to use a proxy for browser-based applications. If you provide a `ProxyConfig` object, the following fields are mandatory (except `authBaseUrl`).
84
+
85
+ | Property | Type | Required | Description |
86
+ | :-------------- | :------- | :------- | :-------------------------------------------------------------------------------------------------- |
87
+ | `apiUrl` | `string` | **Yes** | The base URL for API requests. |
88
+ | `fileProxyUrl` | `string` | **Yes** | A proxy URL for fetching S3 files. The original S3 URL will be appended as a `url` query parameter. |
89
+ | `tokenEndpoint` | `string` | **Yes** | The specific endpoint for token exchange. |
90
+ | `authBaseUrl` | `string` | No | The base URL for OAuth authorization. Defaults to `https://oauth.iracing.com/oauth2`. |
91
+
41
92
  ### 2. Authentication
42
93
 
43
94
  #### Web / Browser (OAuth 2.0 PKCE)
44
95
 
45
- To authenticate in the browser, you need to generate an authorization URL, redirect the user, and then handle the callback.
96
+ To authenticate in the browser, you need to generate an authorization URL, redirect the user, and then handle the return.
46
97
 
47
98
  **Step 1: Generate Auth URL and Redirect**
48
99
 
@@ -51,18 +102,26 @@ const url = await client.auth.generateAuthUrl();
51
102
  window.location.href = url;
52
103
  ```
53
104
 
54
- **Step 2: Handle Callback**
105
+ **Step 2: Handle Return & Restore Session**
55
106
 
56
- On your redirect page, capture the `code` from the URL:
107
+ Simply call `handleAuthentication()` on every page that uses the library. This single method handles:
108
+ - Exchanging the authorization code (when returning from the iRacing login page).
109
+ - Refreshing the access token (if a refresh token is stored).
110
+ - Verifying an existing session.
57
111
 
58
112
  ```javascript
59
- const params = new URLSearchParams(window.location.search);
60
- const code = params.get('code');
113
+ // This should run on every page load of your application,
114
+ // including the redirectUri page.
115
+ const isAuthenticated = await client.auth.handleAuthentication();
116
+ ```
61
117
 
62
- if (code) {
63
- await client.auth.handleCallback(code);
64
- // Success! The client is now authenticated with an access token.
65
- }
118
+ #### Manual Session Management
119
+
120
+ If you have obtained an access token (and refresh token) through other means (e.g., server-side authentication), you can manually set the session on the client.
121
+
122
+ ```javascript
123
+ // Set the access token (and optional refresh token)
124
+ client.auth.setSession('YOUR_ACCESS_TOKEN', 'YOUR_REFRESH_TOKEN');
66
125
  ```
67
126
 
68
127
  ### 3. Fetch Data
@@ -73,7 +132,7 @@ Once authenticated, you can call any endpoint using `getData`. This method handl
73
132
  try {
74
133
  // Call an endpoint directly
75
134
  const { data, metadata } = await client.getData('/member/info');
76
-
135
+
77
136
  console.log(data); // The actual API response
78
137
  console.log(metadata.contentType); // Response content type (e.g. 'application/json')
79
138
  console.log(metadata.sizeBytes); // Response size in bytes
@@ -108,7 +167,7 @@ For extremely large datasets, you might want to fetch chunks one by one:
108
167
  ```javascript
109
168
  if (result.metadata.chunkCount > 0) {
110
169
  const totalChunks = result.metadata.chunkCount;
111
-
170
+
112
171
  for (let i = 0; i < totalChunks; i++) {
113
172
  const { data: chunk } = await client.getChunk(result.data, i);
114
173
  console.log(`Processing chunk ${i + 1}/${totalChunks}`);
@@ -118,6 +177,16 @@ if (result.metadata.chunkCount > 0) {
118
177
 
119
178
  > **Note:** iRacing's API incorrectly returns `application/octet-stream` as the `Content-Type` for JSON chunks. This library automatically detects and parses these as JSON.
120
179
 
180
+ ## The Proxy Requirement (CORS)
181
+
182
+ The iRacing API (`members-ng.iracing.com`) and its associated S3 data links do not provide CORS (`Cross-Origin Resource Sharing`) headers for third-party domains. This means that direct requests from a web browser to the API will be blocked by the browser's security policies.
183
+
184
+ This behavior is intentional by iRacing to better protect their business and operations and is unlikely to change (see [this message by their head of operations](https://forums.iracing.com/discussion/comment/772334/#Comment_772334)).
185
+
186
+ To use this library in a web application, you must route your requests through a proxy server that adds the necessary CORS headers or resides on the same domain as your application.
187
+
188
+ For development and as a reference implementation, this repository includes a `proxy_server.js` that demonstrates how to implement such a workaround. See the [Development](#development) section for more details on how to use it.
189
+
121
190
  ## Development
122
191
 
123
192
  ### Build
@@ -130,7 +199,9 @@ npm run build
130
199
 
131
200
  This repository includes a local development proxy server and a demo application to test the OAuth flow and API interaction, avoiding CORS issues during development.
132
201
 
133
- 1. Create a file named `config.json` in the `demo/` directory (ignored by git) with your configuration:
202
+ 1. Create a file named `config.json` in the `demo/` directory (ignored by git) with your configuration. See the [Configuration](#configuration) section for details on the `ProxyConfig` and `AuthConfig` structures which map to this JSON file.
203
+
204
+ **Example `demo/config.json`:**
134
205
 
135
206
  ```json
136
207
  {
@@ -145,23 +216,17 @@ This repository includes a local development proxy server and a demo application
145
216
  }
146
217
  ```
147
218
 
148
- * `port`: The port the proxy server will listen on.
149
- * `basePath`: The path prefix where the static files and proxy endpoints are served from.
150
- * `redirectPath`: The path the proxy server intercepts for OAuth callbacks.
151
- * `auth`: Your iRacing API credentials. Note that `tokenEndpoint` should include the `basePath`.
152
-
153
219
  2. Start the proxy server:
154
220
 
155
221
  ```bash
156
222
  npm run dev
157
223
  ```
158
224
 
159
- *This command automatically generates the `demo/index.html` from the template using your configuration and starts the proxy server.*
160
- *Depending on your system configuration, you might need elevated privileges (e.g., `sudo`) to listen on port 80.*
225
+ _This command automatically generates the `demo/index.html` from the template using your configuration and starts the proxy server._
226
+ _Depending on your system configuration, you might need elevated privileges (e.g., `sudo`) to listen on port 80._
161
227
 
162
228
  3. Open `http://127.0.0.1/irdata_js/` (or your configured `basePath`) in your browser.
163
- * The demo app is configured to use the local proxy endpoints (e.g., `/irdata_js/token`, `/irdata_js/data`, `/irdata_js/passthrough`) to bypass CORS restrictions.
164
-
229
+ - The demo app is configured to use the local proxy endpoints (e.g., `/irdata_js/token`, `/irdata_js/data`, `/irdata_js/passthrough`) to bypass CORS restrictions.
165
230
 
166
231
  ## License
167
232
 
package/dist/index.cjs CHANGED
@@ -268,10 +268,13 @@ var LocalStorageTokenStore = class {
268
268
  var AuthManager = class {
269
269
  tokenStore;
270
270
  config;
271
- baseUrl = "https://oauth.iracing.com/oauth2";
272
- constructor(config = {}) {
271
+ authBaseUrl;
272
+ tokenEndpoint;
273
+ pkceVerifier = null;
274
+ constructor(config, proxySettings = {}) {
273
275
  this.config = config;
274
- this.baseUrl = config.authBaseUrl || "https://oauth.iracing.com/oauth2";
276
+ this.authBaseUrl = proxySettings.authBaseUrl || "https://oauth.iracing.com/oauth2";
277
+ this.tokenEndpoint = proxySettings.tokenEndpoint;
275
278
  if (typeof window !== "undefined" && window.localStorage) {
276
279
  this.tokenStore = new LocalStorageTokenStore();
277
280
  } else {
@@ -281,6 +284,54 @@ var AuthManager = class {
281
284
  get accessToken() {
282
285
  return this.tokenStore.getAccessToken();
283
286
  }
287
+ get refreshToken() {
288
+ return this.tokenStore.getRefreshToken();
289
+ }
290
+ /**
291
+ * Manually sets the session tokens.
292
+ * Useful for loading a saved session from external storage.
293
+ */
294
+ setSession(accessToken, refreshToken) {
295
+ this.tokenStore.setAccessToken(accessToken);
296
+ if (refreshToken) {
297
+ this.tokenStore.setRefreshToken(refreshToken);
298
+ }
299
+ }
300
+ /**
301
+ * Returns true if an access token is present.
302
+ */
303
+ get isLoggedIn() {
304
+ return !!this.accessToken;
305
+ }
306
+ /**
307
+ * Comprehensive method to establish a session.
308
+ *
309
+ * Automatically handles:
310
+ * 1. Existing valid sessions (returns true immediately).
311
+ * 2. OAuth Callback handling (exchanges code for token).
312
+ * 3. Session restoration (uses Refresh Token).
313
+ *
314
+ * @returns Promise<boolean> - true if authenticated, false otherwise.
315
+ */
316
+ async handleAuthentication() {
317
+ if (this.isLoggedIn) {
318
+ return true;
319
+ }
320
+ try {
321
+ await this.handleCallback();
322
+ if (this.isLoggedIn) {
323
+ if (typeof window !== "undefined" && window.history && window.history.replaceState) {
324
+ const url = new URL(window.location.href);
325
+ url.searchParams.delete("code");
326
+ window.history.replaceState({}, document.title, url.toString());
327
+ }
328
+ return true;
329
+ }
330
+ } catch (error) {
331
+ console.warn("Authentication: Callback exchange failed", error);
332
+ }
333
+ return await this.refreshAccessToken();
334
+ }
284
335
  getAuthHeaders() {
285
336
  const headers = {};
286
337
  const token = this.tokenStore.getAccessToken();
@@ -298,6 +349,8 @@ var AuthManager = class {
298
349
  const challenge = await PKCEHelper.generateChallenge(verifier);
299
350
  if (typeof window !== "undefined" && window.sessionStorage) {
300
351
  window.sessionStorage.setItem("irdata_pkce_verifier", verifier);
352
+ } else {
353
+ this.pkceVerifier = verifier;
301
354
  }
302
355
  const params = new URLSearchParams({
303
356
  response_type: "code",
@@ -308,14 +361,42 @@ var AuthManager = class {
308
361
  code_challenge: challenge,
309
362
  code_challenge_method: "S256"
310
363
  });
311
- return `${this.baseUrl}/authorize?${params.toString()}`;
364
+ return `${this.authBaseUrl}/authorize?${params.toString()}`;
312
365
  }
313
- async handleCallback(code) {
366
+ async handleCallback(codeOrUrl) {
367
+ let input = codeOrUrl;
368
+ if (!input && typeof window !== "undefined") {
369
+ input = window.location.href;
370
+ }
371
+ if (!input) {
372
+ return;
373
+ }
374
+ let code = input;
375
+ if (input.includes("code=") || input.startsWith("http")) {
376
+ try {
377
+ const url = new URL(input);
378
+ const extractedCode = url.searchParams.get("code");
379
+ if (extractedCode) {
380
+ code = extractedCode;
381
+ } else if (input.startsWith("http")) {
382
+ return;
383
+ }
384
+ } catch {
385
+ const match = input.match(/[?&]code=([\w-]+)/);
386
+ if (match) {
387
+ code = match[1];
388
+ }
389
+ }
390
+ }
314
391
  let verifier = "";
315
392
  if (typeof window !== "undefined" && window.sessionStorage) {
316
393
  verifier = window.sessionStorage.getItem("irdata_pkce_verifier") || "";
317
394
  window.sessionStorage.removeItem("irdata_pkce_verifier");
318
395
  }
396
+ if (!verifier && this.pkceVerifier) {
397
+ verifier = this.pkceVerifier;
398
+ this.pkceVerifier = null;
399
+ }
319
400
  if (!verifier) {
320
401
  throw new Error("No PKCE verifier found");
321
402
  }
@@ -326,7 +407,7 @@ var AuthManager = class {
326
407
  code,
327
408
  code_verifier: verifier
328
409
  });
329
- const tokenUrl = this.config.tokenEndpoint || `${this.baseUrl}/token`;
410
+ const tokenUrl = this.tokenEndpoint || `${this.authBaseUrl}/token`;
330
411
  const response = await fetch(tokenUrl, {
331
412
  method: "POST",
332
413
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -368,7 +449,7 @@ var AuthManager = class {
368
449
  client_id: this.config.clientId,
369
450
  refresh_token: refreshToken
370
451
  });
371
- const tokenUrl = this.config.tokenEndpoint || `${this.baseUrl}/token`;
452
+ const tokenUrl = this.tokenEndpoint || `${this.authBaseUrl}/token`;
372
453
  try {
373
454
  const response = await fetch(tokenUrl, {
374
455
  method: "POST",
@@ -400,10 +481,10 @@ var IRacingClient = class {
400
481
  auth;
401
482
  apiUrl;
402
483
  fileProxyUrl;
403
- constructor(config = {}) {
404
- this.apiUrl = config.apiUrl || "https://members-ng.iracing.com/data";
405
- this.fileProxyUrl = config.fileProxyUrl;
406
- this.auth = new AuthManager(config.auth);
484
+ constructor(authConfig, proxyConfig) {
485
+ this.apiUrl = proxyConfig?.apiUrl || "https://members-ng.iracing.com/data";
486
+ this.fileProxyUrl = proxyConfig?.fileProxyUrl;
487
+ this.auth = new AuthManager(authConfig, proxyConfig);
407
488
  }
408
489
  calculateSize(response, data) {
409
490
  const cl = response.headers.get("content-length");
@@ -666,7 +747,7 @@ var IRacingClient = class {
666
747
  };
667
748
 
668
749
  // src/version.ts
669
- var VERSION = "0.3.0";
750
+ var VERSION = "0.4.0";
670
751
  // Annotate the CommonJS export names for ESM import in node:
671
752
  0 && (module.exports = {
672
753
  AuthManager,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/auth/PKCEHelper.ts","../src/errors.ts","../src/auth/AuthManager.ts","../src/client.ts","../src/version.ts"],"sourcesContent":["export * from './client.js';\nexport * from './auth/AuthManager.js';\nexport * from './auth/PKCEHelper.js';\nexport * from './errors.js';\nexport * from './version.js';\n","export class PKCEHelper {\n /**\n * Generates a random string for the code verifier.\n * @param length Length of the string (43-128 characters recommended)\n */\n static generateVerifier(length: number = 128): string {\n const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n let result = '';\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const values = new Uint8Array(length);\n crypto.getRandomValues(values);\n for (let i = 0; i < length; i++) {\n result += charset[values[i] % charset.length];\n }\n } else {\n // Fallback for environments without crypto.getRandomValues (though Node 18+ and browsers have it)\n for (let i = 0; i < length; i++) {\n result += charset.charAt(Math.floor(Math.random() * charset.length));\n }\n }\n return result;\n }\n\n /**\n * Generates the code challenge from the verifier using SHA-256.\n * @param verifier The code verifier string\n */\n static async generateChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n return this.base64URLEncode(hashBuffer);\n } else {\n // Fallback for insecure contexts or environments without crypto.subtle\n const hashBuffer = this.sha256(data);\n return this.base64URLEncode(hashBuffer);\n }\n }\n\n private static base64URLEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n }\n\n private static sha256(data: Uint8Array): ArrayBuffer {\n const K = [\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,\n 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,\n 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,\n 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,\n 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,\n 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,\n 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,\n 0xc67178f2,\n ];\n\n function rotr(n: number, x: number) {\n return (x >>> n) | (x << (32 - n));\n }\n function ch(x: number, y: number, z: number) {\n return (x & y) ^ (~x & z);\n }\n function maj(x: number, y: number, z: number) {\n return (x & y) ^ (x & z) ^ (y & z);\n }\n function sigma0(x: number) {\n return rotr(2, x) ^ rotr(13, x) ^ rotr(22, x);\n }\n function sigma1(x: number) {\n return rotr(6, x) ^ rotr(11, x) ^ rotr(25, x);\n }\n function gamma0(x: number) {\n return rotr(7, x) ^ rotr(18, x) ^ (x >>> 3);\n }\n function gamma1(x: number) {\n return rotr(17, x) ^ rotr(19, x) ^ (x >>> 10);\n }\n\n const bytes = new Uint8Array(data);\n const len = bytes.length * 8;\n\n // Padding\n const paddingLen = (((len + 64) >>> 9) << 4) + 16;\n const words = new Uint32Array(paddingLen);\n for (let i = 0; i < bytes.length; i++) words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);\n words[bytes.length >>> 2] |= 0x80 << (24 - (bytes.length % 4) * 8);\n words[paddingLen - 1] = len;\n\n const H = [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,\n 0x5be0cd19,\n ];\n\n const W = new Uint32Array(64);\n for (let i = 0; i < words.length; i += 16) {\n W.fill(0);\n for (let j = 0; j < 16; j++) W[j] = words[i + j];\n for (let j = 16; j < 64; j++)\n W[j] = (gamma1(W[j - 2]) + W[j - 7] + gamma0(W[j - 15]) + W[j - 16]) | 0;\n\n let [a, b, c, d, e, f, g, h] = H;\n\n for (let j = 0; j < 64; j++) {\n const T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0;\n const T2 = (sigma0(a) + maj(a, b, c)) | 0;\n h = g;\n g = f;\n f = e;\n e = (d + T1) | 0;\n d = c;\n c = b;\n b = a;\n a = (T1 + T2) | 0;\n }\n\n H[0] = (H[0] + a) | 0;\n H[1] = (H[1] + b) | 0;\n H[2] = (H[2] + c) | 0;\n H[3] = (H[3] + d) | 0;\n H[4] = (H[4] + e) | 0;\n H[5] = (H[5] + f) | 0;\n H[6] = (H[6] + g) | 0;\n H[7] = (H[7] + h) | 0;\n }\n\n const buffer = new ArrayBuffer(32);\n const view = new DataView(buffer);\n H.forEach((h, i) => view.setUint32(i * 4, h, false));\n return buffer;\n }\n}\n","export class IRacingAPIError extends Error {\n constructor(\n message: string,\n public status: number,\n public statusText: string,\n public body?: unknown,\n ) {\n super(message);\n this.name = 'IRacingAPIError';\n }\n}\n","import { PKCEHelper } from './PKCEHelper.js';\nimport { IRacingAPIError } from '../errors.js';\n\ninterface TokenResponse {\n access_token: string;\n expires_in: number; // seconds\n token_type: string;\n refresh_token?: string; // Not always returned?\n // Add other fields if necessary\n}\n\nexport interface AuthConfig {\n clientId?: string; // For OAuth\n redirectUri?: string; // For OAuth\n authBaseUrl?: string;\n tokenEndpoint?: string;\n}\n\nexport interface TokenStore {\n getAccessToken(): string | null;\n setAccessToken(token: string): void;\n getRefreshToken(): string | null;\n setRefreshToken(token: string): void;\n clear(): void;\n}\n\nclass InMemoryTokenStore implements TokenStore {\n private accessToken: string | null = null;\n private refreshToken: string | null = null;\n\n getAccessToken() {\n return this.accessToken;\n }\n setAccessToken(token: string) {\n this.accessToken = token;\n }\n getRefreshToken() {\n return this.refreshToken;\n }\n setRefreshToken(token: string) {\n this.refreshToken = token;\n }\n clear() {\n this.accessToken = null;\n this.refreshToken = null;\n }\n}\n\nclass LocalStorageTokenStore implements TokenStore {\n private prefix = 'irdata_';\n getAccessToken() {\n return localStorage.getItem(this.prefix + 'access_token');\n }\n setAccessToken(token: string) {\n localStorage.setItem(this.prefix + 'access_token', token);\n }\n getRefreshToken() {\n return localStorage.getItem(this.prefix + 'refresh_token');\n }\n setRefreshToken(token: string) {\n localStorage.setItem(this.prefix + 'refresh_token', token);\n }\n clear() {\n localStorage.removeItem(this.prefix + 'access_token');\n localStorage.removeItem(this.prefix + 'refresh_token');\n }\n}\n\nexport class AuthManager {\n private tokenStore: TokenStore;\n private config: AuthConfig;\n private baseUrl = 'https://oauth.iracing.com/oauth2';\n\n constructor(config: AuthConfig = {}) {\n this.config = config;\n this.baseUrl = config.authBaseUrl || 'https://oauth.iracing.com/oauth2';\n if (typeof window !== 'undefined' && window.localStorage) {\n this.tokenStore = new LocalStorageTokenStore();\n } else {\n this.tokenStore = new InMemoryTokenStore();\n }\n }\n\n get accessToken(): string | null {\n return this.tokenStore.getAccessToken();\n }\n\n getAuthHeaders(): HeadersInit {\n const headers: HeadersInit = {};\n const token = this.tokenStore.getAccessToken();\n\n if (token) {\n // OAuth2 Bearer Token\n headers['Authorization'] = `Bearer ${token}`;\n }\n return headers;\n }\n\n // --- Browser OAuth2 PKCE ---\n\n async generateAuthUrl(): Promise<string> {\n if (!this.config.clientId || !this.config.redirectUri) {\n throw new Error('clientId and redirectUri required for OAuth');\n }\n\n const verifier = PKCEHelper.generateVerifier();\n const challenge = await PKCEHelper.generateChallenge(verifier);\n\n // Store verifier for the callback\n if (typeof window !== 'undefined' && window.sessionStorage) {\n window.sessionStorage.setItem('irdata_pkce_verifier', verifier);\n }\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n scope: 'iracing.auth', // Required based iRacing docs\n code_challenge: challenge,\n code_challenge_method: 'S256',\n });\n\n return `${this.baseUrl}/authorize?${params.toString()}`;\n }\n\n async handleCallback(code: string): Promise<void> {\n let verifier = '';\n if (typeof window !== 'undefined' && window.sessionStorage) {\n verifier = window.sessionStorage.getItem('irdata_pkce_verifier') || '';\n window.sessionStorage.removeItem('irdata_pkce_verifier');\n }\n\n if (!verifier) {\n throw new Error('No PKCE verifier found');\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.config.clientId!,\n redirect_uri: this.config.redirectUri!,\n code: code,\n code_verifier: verifier,\n });\n\n const tokenUrl = this.config.tokenEndpoint || `${this.baseUrl}/token`;\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n let errorBody: unknown;\n try {\n errorBody = await response.json();\n } catch (_e) {\n try {\n errorBody = await response.text();\n } catch (_e2) {\n /* ignore */\n }\n }\n throw new IRacingAPIError(\n `Failed to exchange code for token: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n errorBody,\n );\n }\n\n const tokens: TokenResponse = await response.json();\n this.tokenStore.setAccessToken(tokens.access_token);\n if (tokens.refresh_token) {\n this.tokenStore.setRefreshToken(tokens.refresh_token);\n }\n }\n\n async refreshAccessToken(): Promise<boolean> {\n const refreshToken = this.tokenStore.getRefreshToken();\n if (!refreshToken) {\n return false;\n }\n\n if (!this.config.clientId) {\n throw new Error('clientId required for token refresh');\n }\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.config.clientId,\n refresh_token: refreshToken,\n });\n\n const tokenUrl = this.config.tokenEndpoint || `${this.baseUrl}/token`;\n\n try {\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n // If refresh fails (e.g. token expired), clear tokens to force re-login\n this.tokenStore.clear();\n return false;\n }\n\n const tokens: TokenResponse = await response.json();\n this.tokenStore.setAccessToken(tokens.access_token);\n // Update refresh token if a new one is returned\n if (tokens.refresh_token) {\n this.tokenStore.setRefreshToken(tokens.refresh_token);\n }\n return true;\n } catch (error) {\n console.error('Error refreshing token:', error);\n return false;\n }\n }\n\n logout() {\n this.tokenStore.clear();\n }\n}\n","import { AuthManager } from './auth/AuthManager.js';\nimport { IRacingAPIError } from './errors.js';\n\nexport interface ClientConfig {\n apiUrl?: string;\n fileProxyUrl?: string;\n auth?: {\n clientId?: string;\n redirectUri?: string;\n authBaseUrl?: string;\n tokenEndpoint?: string;\n };\n}\n\nexport interface ChunkInfo {\n chunk_size: number;\n num_chunks: number;\n rows: number;\n base_download_url: string;\n chunk_file_names: string[];\n}\n\ninterface DataWithChunkInfo {\n chunk_info?: ChunkInfo;\n}\n\nexport interface DataResult<T> {\n data: T;\n metadata: {\n s3LinkFollowed: boolean;\n chunkCount: number;\n chunkRows?: number;\n sizeBytes: number;\n fetchTimeMs: number;\n contentType?: string;\n };\n}\n\nexport interface ChunkResult<T> {\n data: T;\n metadata: {\n sizeBytes: number;\n fetchTimeMs: number;\n contentType?: string;\n };\n}\n\nexport class IRacingClient {\n public auth: AuthManager;\n private apiUrl: string;\n private fileProxyUrl?: string;\n\n constructor(config: ClientConfig = {}) {\n this.apiUrl = config.apiUrl || 'https://members-ng.iracing.com/data';\n this.fileProxyUrl = config.fileProxyUrl;\n this.auth = new AuthManager(config.auth);\n }\n\n private calculateSize(response: Response, data: unknown): number {\n const cl = response.headers.get('content-length');\n if (cl) return parseInt(cl, 10);\n\n if (typeof data === 'string') {\n return new TextEncoder().encode(data).length;\n }\n\n try {\n return new TextEncoder().encode(JSON.stringify(data)).length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Processes a fetch response based on its Content-Type.\n */\n private async processResponse<T>(\n response: Response,\n ): Promise<{ data: T; contentType: string | null }> {\n let contentType = response.headers.get('content-type');\n let data: T;\n\n if (contentType) {\n const lowerContentType = contentType.toLowerCase();\n // NOTE: iRacing's chunks return with 'application/octet-stream' as Content Type\n if (\n lowerContentType.includes('application/json') ||\n lowerContentType.includes('application/octet-stream')\n ) {\n data = await response.json();\n if (lowerContentType.includes('application/octet-stream')) {\n contentType = contentType.replace(/application\\/octet-stream/i, 'application/json');\n }\n } else {\n data = (await response.text()) as unknown as T;\n }\n } else {\n // If no content type, try JSON, fall back to text\n const clone = response.clone();\n try {\n data = await response.json();\n contentType = 'application/json';\n } catch {\n data = (await clone.text()) as unknown as T;\n contentType = 'text/plain';\n }\n }\n\n return { data, contentType };\n }\n\n /**\n * Internal method to perform request and return data + size.\n */\n private async requestInternal<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<{ data: T; sizeBytes: number; duration: number; contentType: string | null }> {\n const startTime = Date.now();\n // Remove leading slash from endpoint if present\n const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;\n const cleanApiUrl = this.apiUrl.endsWith('/') ? this.apiUrl : `${this.apiUrl}/`;\n const url = `${cleanApiUrl}${cleanEndpoint}`;\n\n const headers = this.auth.getAuthHeaders();\n\n const mergedOptions: RequestInit = {\n ...options,\n headers: {\n ...headers,\n ...options.headers,\n 'Content-Type': 'application/json',\n },\n };\n\n let response = await fetch(url, mergedOptions);\n\n if (!response.ok) {\n if (response.status === 401) {\n // Try to refresh token\n try {\n const refreshed = await this.auth.refreshAccessToken();\n if (refreshed) {\n // Retry request with new token\n const newHeaders = this.auth.getAuthHeaders();\n const retryOptions: RequestInit = {\n ...options,\n headers: {\n ...newHeaders,\n ...options.headers,\n 'Content-Type': 'application/json',\n },\n };\n\n response = await fetch(url, retryOptions);\n }\n } catch (refreshError) {\n console.error('Token refresh failed during request retry:', refreshError);\n }\n }\n\n if (!response.ok) {\n return this.handleErrorResponse(response);\n }\n }\n\n const { data, contentType } = await this.processResponse<T>(response);\n const duration = Date.now() - startTime;\n const sizeBytes = this.calculateSize(response, data);\n return { data, sizeBytes, duration, contentType };\n }\n\n /**\n * Performs a fetch request with authentication headers.\n * Does NOT automatically follow \"link\" responses.\n */\n async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const { data } = await this.requestInternal<T>(endpoint, options);\n return data;\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n let body: unknown;\n try {\n const { data } = await this.processResponse<unknown>(response);\n body = data;\n } catch (_e) {\n // ignore\n }\n\n throw new IRacingAPIError(\n `API Request failed: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n body,\n );\n }\n\n /**\n * Helper to fetch data from an external URL (e.g. S3), handling the file proxy if configured.\n */\n private async fetchExternal<T>(\n url: string,\n ): Promise<{ data: T; sizeBytes: number; duration: number; contentType: string | null }> {\n const startTime = Date.now();\n let fetchUrl = url;\n if (this.fileProxyUrl) {\n // If a file proxy is configured, use it\n const separator = this.fileProxyUrl.includes('?') ? '&' : '?';\n fetchUrl = `${this.fileProxyUrl}${separator}url=${encodeURIComponent(url)}`;\n }\n\n const response = await fetch(fetchUrl);\n if (!response.ok) {\n return this.handleErrorResponse(response);\n }\n\n const { data, contentType } = await this.processResponse<T>(response);\n const duration = Date.now() - startTime;\n const sizeBytes = this.calculateSize(response, data);\n return { data, sizeBytes, duration, contentType };\n }\n\n /**\n * Fetches data from an endpoint, automatically following any S3 links returned.\n * Returns metadata about the operation.\n */\n async getData<T>(endpoint: string): Promise<DataResult<T>> {\n const {\n data: initialData,\n sizeBytes: initialSize,\n duration: initialDuration,\n contentType: initialContentType,\n } = await this.requestInternal<T>(endpoint);\n\n // Check if the response contains a generic link to S3 and follow it\n if (\n initialData &&\n typeof initialData === 'object' &&\n 'link' in initialData &&\n typeof (initialData as { link?: unknown }).link === 'string'\n ) {\n const s3Link = (initialData as { link: string }).link;\n if (s3Link.startsWith('http')) {\n const {\n data: externalData,\n sizeBytes: externalSize,\n duration: externalDuration,\n contentType: externalContentType,\n } = await this.fetchExternal<T>(s3Link);\n\n const chunkMetadata = this.extractChunkMetadata(externalData);\n\n return {\n data: externalData,\n metadata: {\n s3LinkFollowed: true,\n chunkCount: chunkMetadata.chunkCount,\n chunkRows: chunkMetadata.chunkRows,\n sizeBytes: externalSize,\n fetchTimeMs: initialDuration + externalDuration,\n contentType: externalContentType || undefined,\n },\n };\n }\n }\n\n const chunkMetadata = this.extractChunkMetadata(initialData);\n\n return {\n data: initialData,\n metadata: {\n s3LinkFollowed: false,\n chunkCount: chunkMetadata.chunkCount,\n chunkRows: chunkMetadata.chunkRows,\n sizeBytes: initialSize,\n fetchTimeMs: initialDuration,\n contentType: initialContentType || undefined,\n },\n };\n }\n\n private extractChunkMetadata(data: unknown): { chunkCount: number; chunkRows?: number } {\n if (data && typeof data === 'object' && 'chunk_info' in data) {\n const chunkInfo = (data as DataWithChunkInfo).chunk_info;\n if (chunkInfo) {\n return {\n chunkCount: typeof chunkInfo.num_chunks === 'number' ? chunkInfo.num_chunks : 0,\n chunkRows: typeof chunkInfo.rows === 'number' ? chunkInfo.rows : undefined,\n };\n }\n }\n return { chunkCount: 0 };\n }\n\n /**\n * Fetches a specific chunk from a response containing chunk_info.\n *\n * @param data The response object containing chunk_info\n * @param chunkIndex The index of the chunk to fetch (0-based)\n * @returns The content of the chunk and metadata\n */\n async getChunk<T>(data: unknown, chunkIndex: number): Promise<ChunkResult<T[]>> {\n const dataWithChunks = data as DataWithChunkInfo;\n if (!dataWithChunks || !dataWithChunks.chunk_info) {\n throw new Error('Response does not contain chunk_info');\n }\n\n const chunkInfo = dataWithChunks.chunk_info;\n const { base_download_url, chunk_file_names } = chunkInfo;\n\n if (\n !base_download_url ||\n !Array.isArray(chunk_file_names) ||\n chunkIndex < 0 ||\n chunkIndex >= chunk_file_names.length\n ) {\n throw new Error(\n `Invalid chunk index: ${chunkIndex} (Total chunks: ${chunk_file_names?.length || 0})`,\n );\n }\n\n const chunkUrl = `${base_download_url}${chunk_file_names[chunkIndex]}`;\n const {\n data: chunkData,\n sizeBytes,\n duration,\n contentType,\n } = await this.fetchExternal<T[]>(chunkUrl);\n return {\n data: chunkData,\n metadata: {\n sizeBytes,\n fetchTimeMs: duration,\n contentType: contentType || undefined,\n },\n };\n }\n\n /**\n * Fetches multiple chunks and merges the results into a single array.\n *\n * @param data The response object containing chunk_info\n * @param options Options for fetching chunks (start index, limit count)\n * @returns A merged array of data from the requested chunks and total size\n */\n async getChunks<T>(\n data: unknown,\n options: { start?: number; limit?: number } = {},\n ): Promise<ChunkResult<T[]>> {\n const dataWithChunks = data as DataWithChunkInfo;\n if (!dataWithChunks || !dataWithChunks.chunk_info) {\n throw new Error('Response does not contain chunk_info');\n }\n\n const chunkInfo = dataWithChunks.chunk_info as ChunkInfo;\n const totalChunks = chunkInfo.chunk_file_names.length;\n const start = options.start || 0;\n const limit = options.limit || totalChunks - start;\n const end = Math.min(start + limit, totalChunks);\n\n if (start < 0 || start >= totalChunks) {\n throw new Error(`Invalid start index: ${start} (Total chunks: ${totalChunks})`);\n }\n\n const chunkPromises: Promise<ChunkResult<T[]>>[] = [];\n for (let i = start; i < end; i++) {\n chunkPromises.push(this.getChunk<T>(data, i));\n }\n\n const results = await Promise.all(chunkPromises);\n const mergedData = results.flatMap((r) => r.data);\n const totalSize = results.reduce((sum, r) => sum + r.metadata.sizeBytes, 0);\n const totalDuration = results.reduce((sum, r) => sum + r.metadata.fetchTimeMs, 0);\n\n return {\n data: mergedData,\n metadata: {\n sizeBytes: totalSize,\n fetchTimeMs: totalDuration,\n },\n };\n }\n}\n","declare const __VERSION__: string;\nexport const VERSION = __VERSION__;\n"],"mappings":"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","names":["chunkMetadata"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/auth/PKCEHelper.ts","../src/errors.ts","../src/auth/AuthManager.ts","../src/client.ts","../src/version.ts"],"sourcesContent":["export * from './client.js';\nexport * from './auth/AuthManager.js';\nexport * from './auth/PKCEHelper.js';\nexport * from './errors.js';\nexport * from './version.js';\n","export class PKCEHelper {\n /**\n * Generates a random string for the code verifier.\n * @param length Length of the string (43-128 characters recommended)\n */\n static generateVerifier(length: number = 128): string {\n const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n let result = '';\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const values = new Uint8Array(length);\n crypto.getRandomValues(values);\n for (let i = 0; i < length; i++) {\n result += charset[values[i] % charset.length];\n }\n } else {\n // Fallback for environments without crypto.getRandomValues (though Node 18+ and browsers have it)\n for (let i = 0; i < length; i++) {\n result += charset.charAt(Math.floor(Math.random() * charset.length));\n }\n }\n return result;\n }\n\n /**\n * Generates the code challenge from the verifier using SHA-256.\n * @param verifier The code verifier string\n */\n static async generateChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n return this.base64URLEncode(hashBuffer);\n } else {\n // Fallback for insecure contexts or environments without crypto.subtle\n const hashBuffer = this.sha256(data);\n return this.base64URLEncode(hashBuffer);\n }\n }\n\n private static base64URLEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n }\n\n private static sha256(data: Uint8Array): ArrayBuffer {\n const K = [\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,\n 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,\n 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,\n 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,\n 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,\n 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,\n 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,\n 0xc67178f2,\n ];\n\n function rotr(n: number, x: number) {\n return (x >>> n) | (x << (32 - n));\n }\n function ch(x: number, y: number, z: number) {\n return (x & y) ^ (~x & z);\n }\n function maj(x: number, y: number, z: number) {\n return (x & y) ^ (x & z) ^ (y & z);\n }\n function sigma0(x: number) {\n return rotr(2, x) ^ rotr(13, x) ^ rotr(22, x);\n }\n function sigma1(x: number) {\n return rotr(6, x) ^ rotr(11, x) ^ rotr(25, x);\n }\n function gamma0(x: number) {\n return rotr(7, x) ^ rotr(18, x) ^ (x >>> 3);\n }\n function gamma1(x: number) {\n return rotr(17, x) ^ rotr(19, x) ^ (x >>> 10);\n }\n\n const bytes = new Uint8Array(data);\n const len = bytes.length * 8;\n\n // Padding\n const paddingLen = (((len + 64) >>> 9) << 4) + 16;\n const words = new Uint32Array(paddingLen);\n for (let i = 0; i < bytes.length; i++) words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);\n words[bytes.length >>> 2] |= 0x80 << (24 - (bytes.length % 4) * 8);\n words[paddingLen - 1] = len;\n\n const H = [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,\n 0x5be0cd19,\n ];\n\n const W = new Uint32Array(64);\n for (let i = 0; i < words.length; i += 16) {\n W.fill(0);\n for (let j = 0; j < 16; j++) W[j] = words[i + j];\n for (let j = 16; j < 64; j++)\n W[j] = (gamma1(W[j - 2]) + W[j - 7] + gamma0(W[j - 15]) + W[j - 16]) | 0;\n\n let [a, b, c, d, e, f, g, h] = H;\n\n for (let j = 0; j < 64; j++) {\n const T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0;\n const T2 = (sigma0(a) + maj(a, b, c)) | 0;\n h = g;\n g = f;\n f = e;\n e = (d + T1) | 0;\n d = c;\n c = b;\n b = a;\n a = (T1 + T2) | 0;\n }\n\n H[0] = (H[0] + a) | 0;\n H[1] = (H[1] + b) | 0;\n H[2] = (H[2] + c) | 0;\n H[3] = (H[3] + d) | 0;\n H[4] = (H[4] + e) | 0;\n H[5] = (H[5] + f) | 0;\n H[6] = (H[6] + g) | 0;\n H[7] = (H[7] + h) | 0;\n }\n\n const buffer = new ArrayBuffer(32);\n const view = new DataView(buffer);\n H.forEach((h, i) => view.setUint32(i * 4, h, false));\n return buffer;\n }\n}\n","export class IRacingAPIError extends Error {\n constructor(\n message: string,\n public status: number,\n public statusText: string,\n public body?: unknown,\n ) {\n super(message);\n this.name = 'IRacingAPIError';\n }\n}\n","import { PKCEHelper } from './PKCEHelper.js';\nimport { IRacingAPIError } from '../errors.js';\n\ninterface TokenResponse {\n access_token: string;\n expires_in: number; // seconds\n token_type: string;\n refresh_token?: string; // Not always returned?\n // Add other fields if necessary\n}\n\nexport interface AuthConfig {\n clientId: string; // For OAuth\n redirectUri: string; // For OAuth\n}\n\nexport interface TokenStore {\n getAccessToken(): string | null;\n setAccessToken(token: string): void;\n getRefreshToken(): string | null;\n setRefreshToken(token: string): void;\n clear(): void;\n}\n\nclass InMemoryTokenStore implements TokenStore {\n private accessToken: string | null = null;\n private refreshToken: string | null = null;\n\n getAccessToken() {\n return this.accessToken;\n }\n setAccessToken(token: string) {\n this.accessToken = token;\n }\n getRefreshToken() {\n return this.refreshToken;\n }\n setRefreshToken(token: string) {\n this.refreshToken = token;\n }\n clear() {\n this.accessToken = null;\n this.refreshToken = null;\n }\n}\n\nclass LocalStorageTokenStore implements TokenStore {\n private prefix = 'irdata_';\n getAccessToken() {\n return localStorage.getItem(this.prefix + 'access_token');\n }\n setAccessToken(token: string) {\n localStorage.setItem(this.prefix + 'access_token', token);\n }\n getRefreshToken() {\n return localStorage.getItem(this.prefix + 'refresh_token');\n }\n setRefreshToken(token: string) {\n localStorage.setItem(this.prefix + 'refresh_token', token);\n }\n clear() {\n localStorage.removeItem(this.prefix + 'access_token');\n localStorage.removeItem(this.prefix + 'refresh_token');\n }\n}\n\nexport class AuthManager {\n private tokenStore: TokenStore;\n private config: AuthConfig;\n private authBaseUrl: string;\n private tokenEndpoint?: string;\n private pkceVerifier: string | null = null;\n\n constructor(\n config: AuthConfig,\n proxySettings: { authBaseUrl?: string; tokenEndpoint?: string } = {},\n ) {\n this.config = config;\n this.authBaseUrl = proxySettings.authBaseUrl || 'https://oauth.iracing.com/oauth2';\n this.tokenEndpoint = proxySettings.tokenEndpoint;\n\n if (typeof window !== 'undefined' && window.localStorage) {\n this.tokenStore = new LocalStorageTokenStore();\n } else {\n this.tokenStore = new InMemoryTokenStore();\n }\n }\n\n get accessToken(): string | null {\n return this.tokenStore.getAccessToken();\n }\n\n get refreshToken(): string | null {\n return this.tokenStore.getRefreshToken();\n }\n\n /**\n * Manually sets the session tokens.\n * Useful for loading a saved session from external storage.\n */\n setSession(accessToken: string, refreshToken?: string) {\n this.tokenStore.setAccessToken(accessToken);\n if (refreshToken) {\n this.tokenStore.setRefreshToken(refreshToken);\n }\n }\n\n /**\n * Returns true if an access token is present.\n */\n get isLoggedIn(): boolean {\n return !!this.accessToken;\n }\n\n /**\n * Comprehensive method to establish a session.\n *\n * Automatically handles:\n * 1. Existing valid sessions (returns true immediately).\n * 2. OAuth Callback handling (exchanges code for token).\n * 3. Session restoration (uses Refresh Token).\n *\n * @returns Promise<boolean> - true if authenticated, false otherwise.\n */\n async handleAuthentication(): Promise<boolean> {\n // 1. Check if we already have a token\n if (this.isLoggedIn) {\n return true;\n }\n\n // 2. Check for OAuth callback (code in URL)\n try {\n await this.handleCallback();\n if (this.isLoggedIn) {\n // success! clean up the URL to avoid re-submitting the code on refresh\n if (typeof window !== 'undefined' && window.history && window.history.replaceState) {\n const url = new URL(window.location.href);\n url.searchParams.delete('code');\n // We also remove 'iss' or 'state' if they exist usually, but 'code' is the critical one.\n window.history.replaceState({}, document.title, url.toString());\n }\n return true;\n }\n } catch (error) {\n console.warn('Authentication: Callback exchange failed', error);\n // Continue to try refresh token...\n }\n\n // 3. Try to refresh the session using a stored refresh token\n return await this.refreshAccessToken();\n }\n\n getAuthHeaders(): HeadersInit {\n const headers: HeadersInit = {};\n const token = this.tokenStore.getAccessToken();\n\n if (token) {\n // OAuth2 Bearer Token\n headers['Authorization'] = `Bearer ${token}`;\n }\n return headers;\n }\n\n // --- Browser OAuth2 PKCE ---\n\n async generateAuthUrl(): Promise<string> {\n if (!this.config.clientId || !this.config.redirectUri) {\n throw new Error('clientId and redirectUri required for OAuth');\n }\n\n const verifier = PKCEHelper.generateVerifier();\n const challenge = await PKCEHelper.generateChallenge(verifier);\n\n // Store verifier for the callback\n if (typeof window !== 'undefined' && window.sessionStorage) {\n window.sessionStorage.setItem('irdata_pkce_verifier', verifier);\n } else {\n this.pkceVerifier = verifier;\n }\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n scope: 'iracing.auth', // Required based iRacing docs\n code_challenge: challenge,\n code_challenge_method: 'S256',\n });\n\n return `${this.authBaseUrl}/authorize?${params.toString()}`;\n }\n\n async handleCallback(codeOrUrl?: string): Promise<void> {\n let input = codeOrUrl;\n\n // In a browser, default to the current URL if no input is provided\n if (!input && typeof window !== 'undefined') {\n input = window.location.href;\n }\n\n if (!input) {\n return;\n }\n\n let code = input;\n\n // if the user passes a full URL, extract the code\n if (input.includes('code=') || input.startsWith('http')) {\n try {\n const url = new URL(input);\n const extractedCode = url.searchParams.get('code');\n if (extractedCode) {\n code = extractedCode;\n } else if (input.startsWith('http')) {\n // It's a URL but no code found.\n return;\n }\n } catch {\n // Not a valid URL, assume it's the code itself or handled below\n // Fallback: try to extract code via regex if URL parsing failed\n const match = input.match(/[?&]code=([\\w-]+)/);\n if (match) {\n code = match[1];\n }\n }\n }\n\n let verifier = '';\n if (typeof window !== 'undefined' && window.sessionStorage) {\n verifier = window.sessionStorage.getItem('irdata_pkce_verifier') || '';\n window.sessionStorage.removeItem('irdata_pkce_verifier');\n }\n\n if (!verifier && this.pkceVerifier) {\n verifier = this.pkceVerifier;\n this.pkceVerifier = null;\n }\n\n if (!verifier) {\n throw new Error('No PKCE verifier found');\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.config.clientId!,\n redirect_uri: this.config.redirectUri!,\n code: code,\n code_verifier: verifier,\n });\n\n const tokenUrl = this.tokenEndpoint || `${this.authBaseUrl}/token`;\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n let errorBody: unknown;\n try {\n errorBody = await response.json();\n } catch (_e) {\n try {\n errorBody = await response.text();\n } catch (_e2) {\n /* ignore */\n }\n }\n throw new IRacingAPIError(\n `Failed to exchange code for token: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n errorBody,\n );\n }\n\n const tokens: TokenResponse = await response.json();\n this.tokenStore.setAccessToken(tokens.access_token);\n if (tokens.refresh_token) {\n this.tokenStore.setRefreshToken(tokens.refresh_token);\n }\n }\n\n async refreshAccessToken(): Promise<boolean> {\n const refreshToken = this.tokenStore.getRefreshToken();\n if (!refreshToken) {\n return false;\n }\n\n if (!this.config.clientId) {\n throw new Error('clientId required for token refresh');\n }\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.config.clientId,\n refresh_token: refreshToken,\n });\n\n const tokenUrl = this.tokenEndpoint || `${this.authBaseUrl}/token`;\n\n try {\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n // If refresh fails (e.g. token expired), clear tokens to force re-login\n this.tokenStore.clear();\n return false;\n }\n\n const tokens: TokenResponse = await response.json();\n this.tokenStore.setAccessToken(tokens.access_token);\n // Update refresh token if a new one is returned\n if (tokens.refresh_token) {\n this.tokenStore.setRefreshToken(tokens.refresh_token);\n }\n return true;\n } catch (error) {\n console.error('Error refreshing token:', error);\n return false;\n }\n }\n\n logout() {\n this.tokenStore.clear();\n }\n}\n","import { AuthManager, AuthConfig } from './auth/AuthManager.js';\nimport { IRacingAPIError } from './errors.js';\n\nexport interface ProxyConfig {\n /**\n * The base URL for API requests.\n * **Required** when using a proxy.\n */\n apiUrl: string;\n /**\n * A proxy URL for fetching S3 files.\n * **Required** when using a proxy.\n */\n fileProxyUrl: string;\n /**\n * The specific endpoint for token exchange.\n * **Required** when using a proxy.\n */\n tokenEndpoint: string;\n /**\n * Optional base URL for OAuth authorization.\n * Defaults to 'https://oauth.iracing.com/oauth2' if not provided.\n */\n authBaseUrl?: string;\n}\n\nexport interface ChunkInfo {\n chunk_size: number;\n num_chunks: number;\n rows: number;\n base_download_url: string;\n chunk_file_names: string[];\n}\n\ninterface DataWithChunkInfo {\n chunk_info?: ChunkInfo;\n}\n\nexport interface DataResult<T> {\n data: T;\n metadata: {\n s3LinkFollowed: boolean;\n chunkCount: number;\n chunkRows?: number;\n sizeBytes: number;\n fetchTimeMs: number;\n contentType?: string;\n };\n}\n\nexport interface ChunkResult<T> {\n data: T;\n metadata: {\n sizeBytes: number;\n fetchTimeMs: number;\n contentType?: string;\n };\n}\n\nexport class IRacingClient {\n public auth: AuthManager;\n private apiUrl: string;\n private fileProxyUrl?: string;\n\n constructor(authConfig: AuthConfig, proxyConfig?: ProxyConfig) {\n this.apiUrl = proxyConfig?.apiUrl || 'https://members-ng.iracing.com/data';\n this.fileProxyUrl = proxyConfig?.fileProxyUrl;\n this.auth = new AuthManager(authConfig, proxyConfig);\n }\n\n private calculateSize(response: Response, data: unknown): number {\n const cl = response.headers.get('content-length');\n if (cl) return parseInt(cl, 10);\n\n if (typeof data === 'string') {\n return new TextEncoder().encode(data).length;\n }\n\n try {\n return new TextEncoder().encode(JSON.stringify(data)).length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Processes a fetch response based on its Content-Type.\n */\n private async processResponse<T>(\n response: Response,\n ): Promise<{ data: T; contentType: string | null }> {\n let contentType = response.headers.get('content-type');\n let data: T;\n\n if (contentType) {\n const lowerContentType = contentType.toLowerCase();\n // NOTE: iRacing's chunks return with 'application/octet-stream' as Content Type\n if (\n lowerContentType.includes('application/json') ||\n lowerContentType.includes('application/octet-stream')\n ) {\n data = await response.json();\n if (lowerContentType.includes('application/octet-stream')) {\n contentType = contentType.replace(/application\\/octet-stream/i, 'application/json');\n }\n } else {\n data = (await response.text()) as unknown as T;\n }\n } else {\n // If no content type, try JSON, fall back to text\n const clone = response.clone();\n try {\n data = await response.json();\n contentType = 'application/json';\n } catch {\n data = (await clone.text()) as unknown as T;\n contentType = 'text/plain';\n }\n }\n\n return { data, contentType };\n }\n\n /**\n * Internal method to perform request and return data + size.\n */\n private async requestInternal<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<{ data: T; sizeBytes: number; duration: number; contentType: string | null }> {\n const startTime = Date.now();\n // Remove leading slash from endpoint if present\n const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;\n const cleanApiUrl = this.apiUrl.endsWith('/') ? this.apiUrl : `${this.apiUrl}/`;\n const url = `${cleanApiUrl}${cleanEndpoint}`;\n\n const headers = this.auth.getAuthHeaders();\n\n const mergedOptions: RequestInit = {\n ...options,\n headers: {\n ...headers,\n ...options.headers,\n 'Content-Type': 'application/json',\n },\n };\n\n let response = await fetch(url, mergedOptions);\n\n if (!response.ok) {\n if (response.status === 401) {\n // Try to refresh token\n try {\n const refreshed = await this.auth.refreshAccessToken();\n if (refreshed) {\n // Retry request with new token\n const newHeaders = this.auth.getAuthHeaders();\n const retryOptions: RequestInit = {\n ...options,\n headers: {\n ...newHeaders,\n ...options.headers,\n 'Content-Type': 'application/json',\n },\n };\n\n response = await fetch(url, retryOptions);\n }\n } catch (refreshError) {\n console.error('Token refresh failed during request retry:', refreshError);\n }\n }\n\n if (!response.ok) {\n return this.handleErrorResponse(response);\n }\n }\n\n const { data, contentType } = await this.processResponse<T>(response);\n const duration = Date.now() - startTime;\n const sizeBytes = this.calculateSize(response, data);\n return { data, sizeBytes, duration, contentType };\n }\n\n /**\n * Performs a fetch request with authentication headers.\n * Does NOT automatically follow \"link\" responses.\n */\n async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const { data } = await this.requestInternal<T>(endpoint, options);\n return data;\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n let body: unknown;\n try {\n const { data } = await this.processResponse<unknown>(response);\n body = data;\n } catch (_e) {\n // ignore\n }\n\n throw new IRacingAPIError(\n `API Request failed: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n body,\n );\n }\n\n /**\n * Helper to fetch data from an external URL (e.g. S3), handling the file proxy if configured.\n */\n private async fetchExternal<T>(\n url: string,\n ): Promise<{ data: T; sizeBytes: number; duration: number; contentType: string | null }> {\n const startTime = Date.now();\n let fetchUrl = url;\n if (this.fileProxyUrl) {\n // If a file proxy is configured, use it\n const separator = this.fileProxyUrl.includes('?') ? '&' : '?';\n fetchUrl = `${this.fileProxyUrl}${separator}url=${encodeURIComponent(url)}`;\n }\n\n const response = await fetch(fetchUrl);\n if (!response.ok) {\n return this.handleErrorResponse(response);\n }\n\n const { data, contentType } = await this.processResponse<T>(response);\n const duration = Date.now() - startTime;\n const sizeBytes = this.calculateSize(response, data);\n return { data, sizeBytes, duration, contentType };\n }\n\n /**\n * Fetches data from an endpoint, automatically following any S3 links returned.\n * Returns metadata about the operation.\n */\n async getData<T>(endpoint: string): Promise<DataResult<T>> {\n const {\n data: initialData,\n sizeBytes: initialSize,\n duration: initialDuration,\n contentType: initialContentType,\n } = await this.requestInternal<T>(endpoint);\n\n // Check if the response contains a generic link to S3 and follow it\n if (\n initialData &&\n typeof initialData === 'object' &&\n 'link' in initialData &&\n typeof (initialData as { link?: unknown }).link === 'string'\n ) {\n const s3Link = (initialData as { link: string }).link;\n if (s3Link.startsWith('http')) {\n const {\n data: externalData,\n sizeBytes: externalSize,\n duration: externalDuration,\n contentType: externalContentType,\n } = await this.fetchExternal<T>(s3Link);\n\n const chunkMetadata = this.extractChunkMetadata(externalData);\n\n return {\n data: externalData,\n metadata: {\n s3LinkFollowed: true,\n chunkCount: chunkMetadata.chunkCount,\n chunkRows: chunkMetadata.chunkRows,\n sizeBytes: externalSize,\n fetchTimeMs: initialDuration + externalDuration,\n contentType: externalContentType || undefined,\n },\n };\n }\n }\n\n const chunkMetadata = this.extractChunkMetadata(initialData);\n\n return {\n data: initialData,\n metadata: {\n s3LinkFollowed: false,\n chunkCount: chunkMetadata.chunkCount,\n chunkRows: chunkMetadata.chunkRows,\n sizeBytes: initialSize,\n fetchTimeMs: initialDuration,\n contentType: initialContentType || undefined,\n },\n };\n }\n\n private extractChunkMetadata(data: unknown): { chunkCount: number; chunkRows?: number } {\n if (data && typeof data === 'object' && 'chunk_info' in data) {\n const chunkInfo = (data as DataWithChunkInfo).chunk_info;\n if (chunkInfo) {\n return {\n chunkCount: typeof chunkInfo.num_chunks === 'number' ? chunkInfo.num_chunks : 0,\n chunkRows: typeof chunkInfo.rows === 'number' ? chunkInfo.rows : undefined,\n };\n }\n }\n return { chunkCount: 0 };\n }\n\n /**\n * Fetches a specific chunk from a response containing chunk_info.\n *\n * @param data The response object containing chunk_info\n * @param chunkIndex The index of the chunk to fetch (0-based)\n * @returns The content of the chunk and metadata\n */\n async getChunk<T>(data: unknown, chunkIndex: number): Promise<ChunkResult<T[]>> {\n const dataWithChunks = data as DataWithChunkInfo;\n if (!dataWithChunks || !dataWithChunks.chunk_info) {\n throw new Error('Response does not contain chunk_info');\n }\n\n const chunkInfo = dataWithChunks.chunk_info;\n const { base_download_url, chunk_file_names } = chunkInfo;\n\n if (\n !base_download_url ||\n !Array.isArray(chunk_file_names) ||\n chunkIndex < 0 ||\n chunkIndex >= chunk_file_names.length\n ) {\n throw new Error(\n `Invalid chunk index: ${chunkIndex} (Total chunks: ${chunk_file_names?.length || 0})`,\n );\n }\n\n const chunkUrl = `${base_download_url}${chunk_file_names[chunkIndex]}`;\n const {\n data: chunkData,\n sizeBytes,\n duration,\n contentType,\n } = await this.fetchExternal<T[]>(chunkUrl);\n return {\n data: chunkData,\n metadata: {\n sizeBytes,\n fetchTimeMs: duration,\n contentType: contentType || undefined,\n },\n };\n }\n\n /**\n * Fetches multiple chunks and merges the results into a single array.\n *\n * @param data The response object containing chunk_info\n * @param options Options for fetching chunks (start index, limit count)\n * @returns A merged array of data from the requested chunks and total size\n */\n async getChunks<T>(\n data: unknown,\n options: { start?: number; limit?: number } = {},\n ): Promise<ChunkResult<T[]>> {\n const dataWithChunks = data as DataWithChunkInfo;\n if (!dataWithChunks || !dataWithChunks.chunk_info) {\n throw new Error('Response does not contain chunk_info');\n }\n\n const chunkInfo = dataWithChunks.chunk_info as ChunkInfo;\n const totalChunks = chunkInfo.chunk_file_names.length;\n const start = options.start || 0;\n const limit = options.limit || totalChunks - start;\n const end = Math.min(start + limit, totalChunks);\n\n if (start < 0 || start >= totalChunks) {\n throw new Error(`Invalid start index: ${start} (Total chunks: ${totalChunks})`);\n }\n\n const chunkPromises: Promise<ChunkResult<T[]>>[] = [];\n for (let i = start; i < end; i++) {\n chunkPromises.push(this.getChunk<T>(data, i));\n }\n\n const results = await Promise.all(chunkPromises);\n const mergedData = results.flatMap((r) => r.data);\n const totalSize = results.reduce((sum, r) => sum + r.metadata.sizeBytes, 0);\n const totalDuration = results.reduce((sum, r) => sum + r.metadata.fetchTimeMs, 0);\n\n return {\n data: mergedData,\n metadata: {\n sizeBytes: totalSize,\n fetchTimeMs: totalDuration,\n },\n };\n }\n}\n","declare const __VERSION__: string;\nexport const VERSION = __VERSION__;\n"],"mappings":"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","names":["chunkMetadata"]}