@lgriffin/esi.ts 5.0.0 → 5.2.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/CHANGELOG.md +39 -0
- package/README.md +174 -7
- package/dist/EsiClient.d.ts +5 -0
- package/dist/EsiClient.d.ts.map +1 -1
- package/dist/EsiClient.js +13 -0
- package/dist/core/ApiClient.d.ts +4 -0
- package/dist/core/ApiClient.d.ts.map +1 -1
- package/dist/core/ApiClient.js +7 -0
- package/dist/core/ApiRequestHandler.d.ts +3 -1
- package/dist/core/ApiRequestHandler.d.ts.map +1 -1
- package/dist/core/ApiRequestHandler.js +107 -41
- package/dist/core/BatchRequestHandler.d.ts +12 -0
- package/dist/core/BatchRequestHandler.d.ts.map +1 -0
- package/dist/core/BatchRequestHandler.js +66 -0
- package/dist/core/constants.d.ts +2 -2
- package/dist/core/constants.js +1 -1
- package/dist/core/endpoints/createClient.d.ts.map +1 -1
- package/dist/core/endpoints/createClient.js +14 -2
- package/dist/core/endpoints/esi-cache-ttls.generated.d.ts +2 -0
- package/dist/core/endpoints/esi-cache-ttls.generated.d.ts.map +1 -0
- package/dist/core/endpoints/esi-cache-ttls.generated.js +126 -0
- package/dist/core/endpoints/esi-rate-limit-groups.generated.d.ts +7 -0
- package/dist/core/endpoints/esi-rate-limit-groups.generated.d.ts.map +1 -0
- package/dist/core/endpoints/esi-rate-limit-groups.generated.js +153 -0
- package/dist/core/pagination/PaginationHandler.d.ts +1 -1
- package/dist/core/pagination/PaginationHandler.d.ts.map +1 -1
- package/dist/core/pagination/PaginationHandler.js +2 -2
- package/dist/core/rateLimiter/IRateLimiter.d.ts +13 -3
- package/dist/core/rateLimiter/IRateLimiter.d.ts.map +1 -1
- package/dist/core/rateLimiter/RateLimiter.d.ts +24 -51
- package/dist/core/rateLimiter/RateLimiter.d.ts.map +1 -1
- package/dist/core/rateLimiter/RateLimiter.js +213 -109
- package/dist/core/util/error.d.ts +5 -1
- package/dist/core/util/error.d.ts.map +1 -1
- package/dist/core/util/error.js +11 -2
- package/dist/core/util/headersUtil.d.ts.map +1 -1
- package/dist/core/util/headersUtil.js +2 -1
- package/dist/core/util/retry.d.ts +8 -0
- package/dist/core/util/retry.d.ts.map +1 -0
- package/dist/core/util/retry.js +9 -0
- package/dist/core/util/stringUtil.d.ts +2 -0
- package/dist/core/util/stringUtil.d.ts.map +1 -0
- package/dist/core/util/stringUtil.js +6 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/types/api-responses.d.ts +1 -0
- package/dist/types/api-responses.d.ts.map +1 -1
- package/dist/types/api-responses.js +24 -0
- package/dist/types/common.d.ts +9 -0
- package/dist/types/common.d.ts.map +1 -1
- package/dist/types/generated/esi-spec.generated.d.ts +1763 -0
- package/dist/types/generated/esi-spec.generated.d.ts.map +1 -0
- package/dist/types/generated/esi-spec.generated.js +6 -0
- package/package.json +4 -2
|
@@ -6,6 +6,10 @@ const loggerUtil_1 = require("../core/logger/loggerUtil");
|
|
|
6
6
|
const headersUtil_1 = require("../core/util/headersUtil");
|
|
7
7
|
const PaginationHandler_1 = require("./pagination/PaginationHandler");
|
|
8
8
|
const constants_1 = require("./constants");
|
|
9
|
+
const esi_cache_ttls_generated_1 = require("./endpoints/esi-cache-ttls.generated");
|
|
10
|
+
const retry_1 = require("./util/retry");
|
|
11
|
+
const sleep_1 = require("./util/sleep");
|
|
12
|
+
const CircuitBreaker_1 = require("./circuitBreaker/CircuitBreaker");
|
|
9
13
|
const STATUS_MESSAGES = {
|
|
10
14
|
201: 'Created',
|
|
11
15
|
204: 'No Content',
|
|
@@ -38,8 +42,41 @@ function resolveCircuitBreaker(client) {
|
|
|
38
42
|
return client.getCircuitBreaker();
|
|
39
43
|
}
|
|
40
44
|
// --- Pure helpers ---
|
|
45
|
+
const stringUtil_1 = require("./util/stringUtil");
|
|
46
|
+
function lookupSpecTtl(method, templatePath) {
|
|
47
|
+
const normalized = templatePath
|
|
48
|
+
.replace(/\/$/, '')
|
|
49
|
+
.replace(/\{(\w+)\}/g, (_, name) => `{${(0, stringUtil_1.camelToSnake)(name)}}`);
|
|
50
|
+
const key = `${method}:${normalized}`;
|
|
51
|
+
const seconds = esi_cache_ttls_generated_1.esiCacheTtls[key];
|
|
52
|
+
return typeof seconds === 'number' ? seconds * 1000 : undefined;
|
|
53
|
+
}
|
|
54
|
+
function trySpecAwareCacheHit(client, url, method, templatePath) {
|
|
55
|
+
if (method !== 'GET' || !templatePath)
|
|
56
|
+
return null;
|
|
57
|
+
const specTtlMs = lookupSpecTtl(method, templatePath);
|
|
58
|
+
if (!specTtlMs)
|
|
59
|
+
return null;
|
|
60
|
+
const cache = resolveCache(client);
|
|
61
|
+
if (!cache)
|
|
62
|
+
return null;
|
|
63
|
+
const entry = cache.get(url);
|
|
64
|
+
if (!entry)
|
|
65
|
+
return null;
|
|
66
|
+
const age = Date.now() - entry.timestamp;
|
|
67
|
+
if (age < specTtlMs) {
|
|
68
|
+
(0, loggerUtil_1.logDebug)(`Spec-aware cache hit for ${url} (age=${Math.round(age / 1000)}s, ttl=${Math.round(specTtlMs / 1000)}s)`);
|
|
69
|
+
return {
|
|
70
|
+
headers: entry.headers,
|
|
71
|
+
body: entry.data,
|
|
72
|
+
fromCache: true,
|
|
73
|
+
cacheHitType: 'spec-ttl',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
41
78
|
const parseCacheControlTtl = (headers) => {
|
|
42
|
-
const cacheControl = headers['cache-control']
|
|
79
|
+
const cacheControl = headers['cache-control'] ?? headers['Cache-Control'];
|
|
43
80
|
if (!cacheControl)
|
|
44
81
|
return undefined;
|
|
45
82
|
const match = /max-age=(\d+)/.exec(cacheControl);
|
|
@@ -89,6 +126,7 @@ function handleEarlyStatus(client, status, url, parsed, useETag) {
|
|
|
89
126
|
headers: { ...cachedEntry.headers, ...parsed.raw },
|
|
90
127
|
body: cachedEntry.data,
|
|
91
128
|
fromCache: true,
|
|
129
|
+
cacheHitType: 'etag-304',
|
|
92
130
|
};
|
|
93
131
|
}
|
|
94
132
|
}
|
|
@@ -108,6 +146,7 @@ function tryStaleCacheResponse(client, url, parsed) {
|
|
|
108
146
|
body: cachedEntry.data,
|
|
109
147
|
fromCache: true,
|
|
110
148
|
stale: true,
|
|
149
|
+
cacheHitType: 'stale-on-error',
|
|
111
150
|
};
|
|
112
151
|
}
|
|
113
152
|
function cacheResponse(client, url, method, endpoint, parsed, data, useETag) {
|
|
@@ -141,7 +180,7 @@ function handleCursorPagination(parsed, data) {
|
|
|
141
180
|
};
|
|
142
181
|
return { headers: parsed.raw, body: data, cursors };
|
|
143
182
|
}
|
|
144
|
-
async function handleOffsetPagination(client, endpoint, method, requiresAuth, parsed, data, body, url, useETag, pageFetch) {
|
|
183
|
+
async function handleOffsetPagination(client, endpoint, method, requiresAuth, parsed, data, body, url, useETag, pageFetch, templatePath) {
|
|
145
184
|
const totalPages = parsed.xPages;
|
|
146
185
|
if (totalPages <= 1) {
|
|
147
186
|
return { headers: parsed.raw, body: data };
|
|
@@ -153,7 +192,7 @@ async function handleOffsetPagination(client, endpoint, method, requiresAuth, pa
|
|
|
153
192
|
maxPages: Math.min(totalPages, 1000),
|
|
154
193
|
stopOnEmptyPage: true,
|
|
155
194
|
maxRetries: 3,
|
|
156
|
-
}, pageFetch);
|
|
195
|
+
}, pageFetch, templatePath);
|
|
157
196
|
cacheResponse(client, url, method, endpoint, parsed, allData, useETag);
|
|
158
197
|
return { headers: parsed.raw, body: allData };
|
|
159
198
|
}
|
|
@@ -222,7 +261,7 @@ function handleErrorResponse(client, response, url, parsed, useETag) {
|
|
|
222
261
|
throw new error_1.EsiError(response.status, errorMessage, url, parsed.requestId ?? undefined);
|
|
223
262
|
}
|
|
224
263
|
function wrapError(error) {
|
|
225
|
-
if (error instanceof error_1.EsiError) {
|
|
264
|
+
if (error instanceof error_1.EsiError || error instanceof CircuitBreaker_1.CircuitOpenError) {
|
|
226
265
|
throw error;
|
|
227
266
|
}
|
|
228
267
|
if (error instanceof Error) {
|
|
@@ -232,7 +271,7 @@ function wrapError(error) {
|
|
|
232
271
|
(0, loggerUtil_1.logError)(`Unexpected error: ${String(error)}`);
|
|
233
272
|
throw (0, error_1.buildError)(String(error), 'ESIJS_ERROR');
|
|
234
273
|
}
|
|
235
|
-
async function executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag) {
|
|
274
|
+
async function executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag, requestTimeout, templatePath) {
|
|
236
275
|
const rawUrl = `${client.getLink()}/${endpoint}`;
|
|
237
276
|
const builtHeaders = buildRequestHeaders(client, rawUrl, method, requiresAuth, useETag, body);
|
|
238
277
|
const req = await applyRequestMiddleware(client, rawUrl, endpoint, method, builtHeaders, body);
|
|
@@ -247,8 +286,8 @@ async function executeSingleFetch(client, endpoint, method, body, requiresAuth,
|
|
|
247
286
|
if (cb)
|
|
248
287
|
cb.checkCircuit(endpoint);
|
|
249
288
|
const rateLimiter = resolveRateLimiter(client);
|
|
250
|
-
await rateLimiter.checkRateLimit();
|
|
251
|
-
const timeoutMs = client.getTimeout();
|
|
289
|
+
await rateLimiter.checkRateLimit(templatePath, method, req.headers);
|
|
290
|
+
const timeoutMs = requestTimeout ?? client.getTimeout();
|
|
252
291
|
const controller = new AbortController();
|
|
253
292
|
const timer = globalThis.setTimeout(() => controller.abort(), timeoutMs);
|
|
254
293
|
options.signal = controller.signal;
|
|
@@ -259,13 +298,13 @@ async function executeSingleFetch(client, endpoint, method, body, requiresAuth,
|
|
|
259
298
|
catch (err) {
|
|
260
299
|
clearTimeout(timer);
|
|
261
300
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
262
|
-
throw new error_1.
|
|
301
|
+
throw new error_1.TimeoutError(timeoutMs, url);
|
|
263
302
|
}
|
|
264
303
|
throw err;
|
|
265
304
|
}
|
|
266
305
|
clearTimeout(timer);
|
|
267
306
|
const parsed = (0, headersUtil_1.parseHeaders)(response.headers);
|
|
268
|
-
rateLimiter.updateFromResponse(parsed.raw, response.status);
|
|
307
|
+
rateLimiter.updateFromResponse(parsed.raw, response.status, templatePath, method, req.headers);
|
|
269
308
|
if (cb) {
|
|
270
309
|
if (response.status >= 500 ||
|
|
271
310
|
response.status === 420 ||
|
|
@@ -281,22 +320,23 @@ async function executeSingleFetch(client, endpoint, method, body, requiresAuth,
|
|
|
281
320
|
}
|
|
282
321
|
return { response, parsed, url };
|
|
283
322
|
}
|
|
284
|
-
async function fetchOnePage(client, endpoint, method, body, requiresAuth = false, useETag = false) {
|
|
285
|
-
const { response, parsed, url } = await executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag);
|
|
323
|
+
async function fetchOnePage(client, endpoint, method, body, requiresAuth = false, useETag = false, requestTimeout, templatePath) {
|
|
324
|
+
const { response, parsed, url } = await executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag, requestTimeout, templatePath);
|
|
286
325
|
if (!response.ok) {
|
|
287
326
|
throw new error_1.EsiError(response.status, STATUS_MESSAGES[response.status] || response.statusText, url, parsed.requestId ?? undefined);
|
|
288
327
|
}
|
|
289
328
|
const data = await parseJsonBody(response, url);
|
|
290
329
|
return { data, parsed, url };
|
|
291
330
|
}
|
|
292
|
-
const executeRequest = async (client, endpoint, method, body, requiresAuth = false, useETag = true) => {
|
|
331
|
+
const executeRequest = async (client, endpoint, method, body, requiresAuth = false, useETag = true, requestTimeout, templatePath) => {
|
|
293
332
|
const startTime = Date.now();
|
|
294
333
|
const finish = (r) => {
|
|
334
|
+
r.responseTimeMs = Date.now() - startTime;
|
|
295
335
|
const rawUrl = `${client.getLink()}/${endpoint}`;
|
|
296
336
|
return applyResponseInterceptors(client, r, rawUrl, endpoint, method, startTime);
|
|
297
337
|
};
|
|
298
338
|
try {
|
|
299
|
-
const { response, parsed, url } = await executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag);
|
|
339
|
+
const { response, parsed, url } = await executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag, requestTimeout, templatePath);
|
|
300
340
|
if (response.status === 201) {
|
|
301
341
|
let data;
|
|
302
342
|
try {
|
|
@@ -320,50 +360,76 @@ const executeRequest = async (client, endpoint, method, body, requiresAuth = fal
|
|
|
320
360
|
if (cursorResult)
|
|
321
361
|
return finish(cursorResult);
|
|
322
362
|
const pageFetch = async (paginatedEndpoint) => {
|
|
323
|
-
const result = await fetchOnePage(client, paginatedEndpoint, method, body, requiresAuth);
|
|
363
|
+
const result = await fetchOnePage(client, paginatedEndpoint, method, body, requiresAuth, false, requestTimeout, templatePath);
|
|
324
364
|
return Array.isArray(result.data)
|
|
325
365
|
? result.data
|
|
326
366
|
: [result.data];
|
|
327
367
|
};
|
|
328
|
-
const paginatedResult = await handleOffsetPagination(client, endpoint, method, requiresAuth, parsed, data, body, url, useETag, pageFetch);
|
|
368
|
+
const paginatedResult = await handleOffsetPagination(client, endpoint, method, requiresAuth, parsed, data, body, url, useETag, pageFetch, templatePath);
|
|
329
369
|
return finish(paginatedResult);
|
|
330
370
|
}
|
|
331
371
|
catch (error) {
|
|
332
372
|
wrapError(error);
|
|
333
373
|
}
|
|
334
374
|
};
|
|
335
|
-
const handleRequest = async (client, endpoint, method, body, requiresAuth = false, useETag = true) => {
|
|
336
|
-
const
|
|
375
|
+
const handleRequest = async (client, endpoint, method, body, requiresAuth = false, useETag = true, templatePath, requestTimeout) => {
|
|
376
|
+
const rawUrl = `${client.getLink()}/${endpoint}`;
|
|
377
|
+
const specHit = trySpecAwareCacheHit(client, rawUrl, method, templatePath);
|
|
378
|
+
if (specHit)
|
|
379
|
+
return specHit;
|
|
380
|
+
const doExecute = () => executeRequest(client, endpoint, method, body, requiresAuth, useETag, requestTimeout, templatePath);
|
|
337
381
|
const dedup = client.getDeduplicator();
|
|
338
382
|
const canDedup = dedup && method === 'GET' && !body;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
383
|
+
const retryConfig = client.getRetryConfig();
|
|
384
|
+
const maxRetries = retryConfig?.maxRetries ?? 0;
|
|
385
|
+
const canRetryMethod = method === 'GET' || retryConfig?.retryMutations === true;
|
|
386
|
+
const baseDelayMs = retryConfig?.baseDelayMs ?? 1000;
|
|
387
|
+
const maxDelayMs = retryConfig?.maxDelayMs ?? 30000;
|
|
388
|
+
let lastError;
|
|
389
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
390
|
+
try {
|
|
391
|
+
return canDedup
|
|
392
|
+
? await dedup.dedupe(endpoint, doExecute)
|
|
393
|
+
: await doExecute();
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
if (error instanceof CircuitBreaker_1.CircuitOpenError) {
|
|
397
|
+
throw error;
|
|
354
398
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
399
|
+
if (error instanceof error_1.EsiError &&
|
|
400
|
+
error.statusCode === 401 &&
|
|
401
|
+
requiresAuth &&
|
|
402
|
+
client.hasTokenProvider()) {
|
|
403
|
+
(0, loggerUtil_1.logInfo)('Received 401, attempting token refresh...');
|
|
404
|
+
try {
|
|
405
|
+
await client.refreshToken();
|
|
406
|
+
(0, loggerUtil_1.logInfo)('Token refreshed, retrying request');
|
|
407
|
+
return await executeRequest(client, endpoint, method, body, requiresAuth, useETag, requestTimeout);
|
|
408
|
+
}
|
|
409
|
+
catch (refreshError) {
|
|
410
|
+
if (refreshError instanceof error_1.EsiError) {
|
|
411
|
+
throw refreshError;
|
|
412
|
+
}
|
|
413
|
+
const msg = refreshError instanceof Error
|
|
414
|
+
? refreshError.message
|
|
415
|
+
: String(refreshError);
|
|
416
|
+
(0, loggerUtil_1.logError)(`Token refresh failed: ${msg}`);
|
|
417
|
+
throw (0, error_1.buildError)(`Token refresh failed: ${msg}`, 'TOKEN_REFRESH_FAILED');
|
|
358
418
|
}
|
|
359
|
-
const msg = refreshError instanceof Error
|
|
360
|
-
? refreshError.message
|
|
361
|
-
: String(refreshError);
|
|
362
|
-
(0, loggerUtil_1.logError)(`Token refresh failed: ${msg}`);
|
|
363
|
-
throw (0, error_1.buildError)(`Token refresh failed: ${msg}`, 'TOKEN_REFRESH_FAILED');
|
|
364
419
|
}
|
|
420
|
+
if (error instanceof error_1.EsiError &&
|
|
421
|
+
error.retryable &&
|
|
422
|
+
canRetryMethod &&
|
|
423
|
+
attempt < maxRetries) {
|
|
424
|
+
const delay = (0, retry_1.retryDelay)(attempt, baseDelayMs, maxDelayMs);
|
|
425
|
+
(0, loggerUtil_1.logWarn)(`Request to ${endpoint} failed (${error.statusCode}), retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
426
|
+
await (0, sleep_1.sleep)(delay);
|
|
427
|
+
lastError = error;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
throw error;
|
|
365
431
|
}
|
|
366
|
-
throw error;
|
|
367
432
|
}
|
|
433
|
+
throw lastError;
|
|
368
434
|
};
|
|
369
435
|
exports.handleRequest = handleRequest;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EsiError } from './util/error';
|
|
2
|
+
export interface BatchOptions {
|
|
3
|
+
concurrency?: number;
|
|
4
|
+
onProgress?: (completed: number, total: number) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface BatchResult<K, T> {
|
|
7
|
+
results: Map<K, T>;
|
|
8
|
+
errors: Map<K, EsiError | Error>;
|
|
9
|
+
}
|
|
10
|
+
export declare function batchFetch<K, T>(keys: K[], fetcher: (key: K) => Promise<T>, options?: BatchOptions): Promise<BatchResult<K, T>>;
|
|
11
|
+
export declare function batchPost<T>(ids: number[], poster: (chunk: number[]) => Promise<T[]>, chunkSize?: number): Promise<T[]>;
|
|
12
|
+
//# sourceMappingURL=BatchRequestHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BatchRequestHandler.d.ts","sourceRoot":"","sources":["../../src/core/BatchRequestHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC;CAClC;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,CAAC,EACnC,IAAI,EAAE,CAAC,EAAE,EACT,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAqD5B;AAED,wBAAsB,SAAS,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EAAE,EACb,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,EACzC,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,CAAC,EAAE,CAAC,CAiBd"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.batchFetch = batchFetch;
|
|
4
|
+
exports.batchPost = batchPost;
|
|
5
|
+
const error_1 = require("./util/error");
|
|
6
|
+
const loggerUtil_1 = require("./logger/loggerUtil");
|
|
7
|
+
async function batchFetch(keys, fetcher, options = {}) {
|
|
8
|
+
const concurrency = options.concurrency ?? 20;
|
|
9
|
+
const results = new Map();
|
|
10
|
+
const errors = new Map();
|
|
11
|
+
let completed = 0;
|
|
12
|
+
let running = 0;
|
|
13
|
+
let index = 0;
|
|
14
|
+
(0, loggerUtil_1.logInfo)(`Batch fetch: ${keys.length} items, concurrency=${concurrency}`);
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
function next() {
|
|
17
|
+
while (running < concurrency && index < keys.length) {
|
|
18
|
+
const currentIndex = index++;
|
|
19
|
+
const key = keys[currentIndex];
|
|
20
|
+
running++;
|
|
21
|
+
fetcher(key)
|
|
22
|
+
.then((result) => {
|
|
23
|
+
results.set(key, result);
|
|
24
|
+
})
|
|
25
|
+
.catch((err) => {
|
|
26
|
+
const error = err instanceof error_1.EsiError || err instanceof Error
|
|
27
|
+
? err
|
|
28
|
+
: new Error(String(err));
|
|
29
|
+
errors.set(key, error);
|
|
30
|
+
})
|
|
31
|
+
.finally(() => {
|
|
32
|
+
running--;
|
|
33
|
+
completed++;
|
|
34
|
+
if (options.onProgress) {
|
|
35
|
+
options.onProgress(completed, keys.length);
|
|
36
|
+
}
|
|
37
|
+
if (completed === keys.length) {
|
|
38
|
+
(0, loggerUtil_1.logDebug)(`Batch complete: ${results.size} succeeded, ${errors.size} failed`);
|
|
39
|
+
resolve({ results, errors });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
next();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (keys.length === 0) {
|
|
48
|
+
resolve({ results, errors });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
next();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function batchPost(ids, poster, chunkSize = 1000) {
|
|
55
|
+
const chunks = [];
|
|
56
|
+
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
57
|
+
chunks.push(ids.slice(i, i + chunkSize));
|
|
58
|
+
}
|
|
59
|
+
(0, loggerUtil_1.logInfo)(`Batch POST: ${ids.length} IDs in ${chunks.length} chunks of ${chunkSize}`);
|
|
60
|
+
const results = [];
|
|
61
|
+
for (const chunk of chunks) {
|
|
62
|
+
const chunkResult = await poster(chunk);
|
|
63
|
+
results.push(...chunkResult);
|
|
64
|
+
}
|
|
65
|
+
return results;
|
|
66
|
+
}
|
package/dist/core/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const PACKAGE_NAME = "esi.ts";
|
|
2
|
-
export declare const PACKAGE_VERSION = "5.
|
|
3
|
-
export declare const USER_AGENT = "esi.ts/5.
|
|
2
|
+
export declare const PACKAGE_VERSION = "5.2.0";
|
|
3
|
+
export declare const USER_AGENT = "esi.ts/5.2.0";
|
|
4
4
|
export declare const COMPATIBILITY_DATE = "2026-05-19";
|
|
5
5
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/core/constants.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.COMPATIBILITY_DATE = exports.USER_AGENT = exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
|
|
4
4
|
exports.PACKAGE_NAME = 'esi.ts';
|
|
5
|
-
exports.PACKAGE_VERSION = '5.
|
|
5
|
+
exports.PACKAGE_VERSION = '5.2.0';
|
|
6
6
|
exports.USER_AGENT = `${exports.PACKAGE_NAME}/${exports.PACKAGE_VERSION}`;
|
|
7
7
|
exports.COMPATIBILITY_DATE = '2026-05-19';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createClient.d.ts","sourceRoot":"","sources":["../../../src/core/endpoints/createClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAmB,MAAM,2BAA2B,CAAC;AAKzE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,KAAK,aAAa,CAAC,CAAC,SAAS,WAAW,IAAI;KACzC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;KAC3B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAC/D,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GACvC,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;
|
|
1
|
+
{"version":3,"file":"createClient.d.ts","sourceRoot":"","sources":["../../../src/core/endpoints/createClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAmB,MAAM,2BAA2B,CAAC;AAKzE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,KAAK,aAAa,CAAC,CAAC,SAAS,WAAW,IAAI;KACzC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;KAC3B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAC/D,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GACvC,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AA8BF,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,EAChD,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,aAAa,CAAC,CAAC,CAAC,CAkIlB;AAGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,KAAK,GAAG,OAAO,EAClE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,EAChE,QAAQ,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,KAAK,EAAE,EAC1C,SAAS,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GACA,OAAO,CAAC,KAAK,EAAE,CAAC,CA0BlB"}
|
|
@@ -12,6 +12,10 @@ function buildMeta(response) {
|
|
|
12
12
|
fromCache: response.fromCache ?? false,
|
|
13
13
|
stale: response.stale ?? false,
|
|
14
14
|
};
|
|
15
|
+
if (response.cacheHitType)
|
|
16
|
+
meta.cacheHitType = response.cacheHitType;
|
|
17
|
+
if (response.responseTimeMs !== undefined)
|
|
18
|
+
meta.responseTimeMs = response.responseTimeMs;
|
|
15
19
|
const w = (0, headersUtil_1.parseWarning)(response.headers['warning']);
|
|
16
20
|
if (w)
|
|
17
21
|
meta.warning = w;
|
|
@@ -21,6 +25,14 @@ function buildMeta(response) {
|
|
|
21
25
|
meta.date = response.headers['date'];
|
|
22
26
|
if (response.headers['content-language'])
|
|
23
27
|
meta.contentLanguage = response.headers['content-language'];
|
|
28
|
+
if (response.headers['x-ratelimit-remaining']) {
|
|
29
|
+
meta.rateLimit = {
|
|
30
|
+
remaining: parseInt(response.headers['x-ratelimit-remaining'], 10),
|
|
31
|
+
limit: parseInt(response.headers['x-ratelimit-limit'] ?? '0', 10),
|
|
32
|
+
used: parseInt(response.headers['x-ratelimit-used'] ?? '0', 10),
|
|
33
|
+
group: response.headers['x-ratelimit-group'] ?? null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
24
36
|
return meta;
|
|
25
37
|
}
|
|
26
38
|
/* eslint-disable sonarjs/cognitive-complexity */
|
|
@@ -96,7 +108,7 @@ function createClient(apiClient, endpoints, options) {
|
|
|
96
108
|
path += (path.includes('?') ? '&' : '?') + parts.join('&');
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
|
-
const response = await (0, ApiRequestHandler_1.handleRequest)(apiClient, path, def.method, body, def.requiresAuth);
|
|
111
|
+
const response = await (0, ApiRequestHandler_1.handleRequest)(apiClient, path, def.method, body, def.requiresAuth, true, def.path);
|
|
100
112
|
const responseBody = response.body;
|
|
101
113
|
let data;
|
|
102
114
|
if (Array.isArray(responseBody)) {
|
|
@@ -117,7 +129,7 @@ function createClient(apiClient, endpoints, options) {
|
|
|
117
129
|
}
|
|
118
130
|
return cursorResult;
|
|
119
131
|
}
|
|
120
|
-
const response = await (0, ApiRequestHandler_1.handleRequest)(apiClient, path, def.method, body, def.requiresAuth);
|
|
132
|
+
const response = await (0, ApiRequestHandler_1.handleRequest)(apiClient, path, def.method, body, def.requiresAuth, true, def.path);
|
|
121
133
|
if (returnMetadata) {
|
|
122
134
|
return { data: response.body, meta: buildMeta(response) };
|
|
123
135
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esi-cache-ttls.generated.d.ts","sourceRoot":"","sources":["../../../src/core/endpoints/esi-cache-ttls.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwH/C,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Auto-generated from ESI Swagger spec — do not edit manually
|
|
3
|
+
// Endpoints with cache TTLs: 119
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.esiCacheTtls = void 0;
|
|
6
|
+
exports.esiCacheTtls = {
|
|
7
|
+
'GET:alliances': 3600,
|
|
8
|
+
'GET:alliances/{alliance_id}': 3600,
|
|
9
|
+
'GET:alliances/{alliance_id}/contacts': 300,
|
|
10
|
+
'GET:alliances/{alliance_id}/contacts/labels': 300,
|
|
11
|
+
'GET:alliances/{alliance_id}/corporations': 3600,
|
|
12
|
+
'GET:characters/{character_id}': 604800,
|
|
13
|
+
'GET:characters/{character_id}/agents_research': 3600,
|
|
14
|
+
'GET:characters/{character_id}/assets': 3600,
|
|
15
|
+
'GET:characters/{character_id}/attributes': 120,
|
|
16
|
+
'GET:characters/{character_id}/blueprints': 3600,
|
|
17
|
+
'GET:characters/{character_id}/calendar': 5,
|
|
18
|
+
'GET:characters/{character_id}/calendar/{event_id}': 5,
|
|
19
|
+
'PUT:characters/{character_id}/calendar/{event_id}': 5,
|
|
20
|
+
'GET:characters/{character_id}/calendar/{event_id}/attendees': 600,
|
|
21
|
+
'GET:characters/{character_id}/clones': 120,
|
|
22
|
+
'GET:characters/{character_id}/contacts': 300,
|
|
23
|
+
'GET:characters/{character_id}/contacts/labels': 300,
|
|
24
|
+
'GET:characters/{character_id}/contracts': 300,
|
|
25
|
+
'GET:characters/{character_id}/contracts/{contract_id}/bids': 300,
|
|
26
|
+
'GET:characters/{character_id}/contracts/{contract_id}/items': 3600,
|
|
27
|
+
'GET:characters/{character_id}/corporationhistory': 86400,
|
|
28
|
+
'GET:characters/{character_id}/fatigue': 300,
|
|
29
|
+
'GET:characters/{character_id}/fittings': 300,
|
|
30
|
+
'GET:characters/{character_id}/fleet': 60,
|
|
31
|
+
'GET:characters/{character_id}/implants': 120,
|
|
32
|
+
'GET:characters/{character_id}/industry/jobs': 300,
|
|
33
|
+
'GET:characters/{character_id}/killmails/recent': 300,
|
|
34
|
+
'GET:characters/{character_id}/location': 5,
|
|
35
|
+
'GET:characters/{character_id}/loyalty/points': 3600,
|
|
36
|
+
'GET:characters/{character_id}/mail': 30,
|
|
37
|
+
'GET:characters/{character_id}/mail/{mail_id}': 30,
|
|
38
|
+
'GET:characters/{character_id}/mail/labels': 30,
|
|
39
|
+
'GET:characters/{character_id}/mail/lists': 120,
|
|
40
|
+
'GET:characters/{character_id}/medals': 3600,
|
|
41
|
+
'GET:characters/{character_id}/mining': 600,
|
|
42
|
+
'GET:characters/{character_id}/notifications': 600,
|
|
43
|
+
'GET:characters/{character_id}/notifications/contacts': 600,
|
|
44
|
+
'GET:characters/{character_id}/online': 60,
|
|
45
|
+
'GET:characters/{character_id}/orders': 1200,
|
|
46
|
+
'GET:characters/{character_id}/orders/history': 3600,
|
|
47
|
+
'GET:characters/{character_id}/planets': 600,
|
|
48
|
+
'GET:characters/{character_id}/roles': 3600,
|
|
49
|
+
'GET:characters/{character_id}/search': 3600,
|
|
50
|
+
'GET:characters/{character_id}/ship': 5,
|
|
51
|
+
'GET:characters/{character_id}/skillqueue': 120,
|
|
52
|
+
'GET:characters/{character_id}/skills': 120,
|
|
53
|
+
'GET:characters/{character_id}/standings': 3600,
|
|
54
|
+
'GET:characters/{character_id}/titles': 3600,
|
|
55
|
+
'GET:characters/{character_id}/wallet': 120,
|
|
56
|
+
'GET:characters/{character_id}/wallet/journal': 3600,
|
|
57
|
+
'GET:characters/{character_id}/wallet/transactions': 3600,
|
|
58
|
+
'POST:characters/affiliation': 3600,
|
|
59
|
+
'GET:contracts/public/{region_id}': 1800,
|
|
60
|
+
'GET:contracts/public/bids/{contract_id}': 300,
|
|
61
|
+
'GET:contracts/public/items/{contract_id}': 3600,
|
|
62
|
+
'GET:corporation/{corporation_id}/mining/extractions': 1800,
|
|
63
|
+
'GET:corporation/{corporation_id}/mining/observers': 3600,
|
|
64
|
+
'GET:corporation/{corporation_id}/mining/observers/{observer_id}': 3600,
|
|
65
|
+
'GET:corporations/{corporation_id}': 3600,
|
|
66
|
+
'GET:corporations/{corporation_id}/alliancehistory': 3600,
|
|
67
|
+
'GET:corporations/{corporation_id}/assets': 3600,
|
|
68
|
+
'GET:corporations/{corporation_id}/blueprints': 3600,
|
|
69
|
+
'GET:corporations/{corporation_id}/contacts': 300,
|
|
70
|
+
'GET:corporations/{corporation_id}/contacts/labels': 300,
|
|
71
|
+
'GET:corporations/{corporation_id}/containers/logs': 600,
|
|
72
|
+
'GET:corporations/{corporation_id}/contracts': 300,
|
|
73
|
+
'GET:corporations/{corporation_id}/contracts/{contract_id}/bids': 3600,
|
|
74
|
+
'GET:corporations/{corporation_id}/contracts/{contract_id}/items': 3600,
|
|
75
|
+
'GET:corporations/{corporation_id}/customs_offices': 3600,
|
|
76
|
+
'GET:corporations/{corporation_id}/divisions': 3600,
|
|
77
|
+
'GET:corporations/{corporation_id}/facilities': 3600,
|
|
78
|
+
'GET:corporations/{corporation_id}/icons': 3600,
|
|
79
|
+
'GET:corporations/{corporation_id}/industry/jobs': 300,
|
|
80
|
+
'GET:corporations/{corporation_id}/killmails/recent': 300,
|
|
81
|
+
'GET:corporations/{corporation_id}/medals': 3600,
|
|
82
|
+
'GET:corporations/{corporation_id}/medals/issued': 3600,
|
|
83
|
+
'GET:corporations/{corporation_id}/members': 3600,
|
|
84
|
+
'GET:corporations/{corporation_id}/members/limit': 3600,
|
|
85
|
+
'GET:corporations/{corporation_id}/members/titles': 3600,
|
|
86
|
+
'GET:corporations/{corporation_id}/membertracking': 3600,
|
|
87
|
+
'GET:corporations/{corporation_id}/orders': 1200,
|
|
88
|
+
'GET:corporations/{corporation_id}/orders/history': 3600,
|
|
89
|
+
'GET:corporations/{corporation_id}/roles': 3600,
|
|
90
|
+
'GET:corporations/{corporation_id}/roles/history': 3600,
|
|
91
|
+
'GET:corporations/{corporation_id}/shareholders': 3600,
|
|
92
|
+
'GET:corporations/{corporation_id}/standings': 3600,
|
|
93
|
+
'GET:corporations/{corporation_id}/starbases': 3600,
|
|
94
|
+
'GET:corporations/{corporation_id}/starbases/{starbase_id}': 3600,
|
|
95
|
+
'GET:corporations/{corporation_id}/structures': 3600,
|
|
96
|
+
'GET:corporations/{corporation_id}/titles': 3600,
|
|
97
|
+
'GET:corporations/{corporation_id}/wallets': 300,
|
|
98
|
+
'GET:corporations/{corporation_id}/wallets/{division}/journal': 3600,
|
|
99
|
+
'GET:corporations/{corporation_id}/wallets/{division}/transactions': 3600,
|
|
100
|
+
'GET:fleets/{fleet_id}': 5,
|
|
101
|
+
'GET:fleets/{fleet_id}/members': 5,
|
|
102
|
+
'GET:fleets/{fleet_id}/wings': 5,
|
|
103
|
+
'GET:fw/systems': 1800,
|
|
104
|
+
'GET:incursions': 300,
|
|
105
|
+
'GET:industry/facilities': 3600,
|
|
106
|
+
'GET:industry/systems': 3600,
|
|
107
|
+
'GET:insurance/prices': 3600,
|
|
108
|
+
'GET:killmails/{killmail_id}/{killmail_hash}': 30758400,
|
|
109
|
+
'GET:markets/{region_id}/orders': 300,
|
|
110
|
+
'GET:markets/{region_id}/types': 600,
|
|
111
|
+
'GET:markets/prices': 3600,
|
|
112
|
+
'GET:markets/structures/{structure_id}': 300,
|
|
113
|
+
'GET:route/{origin}/{destination}': 86400,
|
|
114
|
+
'GET:sovereignty/campaigns': 5,
|
|
115
|
+
'GET:sovereignty/map': 3600,
|
|
116
|
+
'GET:sovereignty/structures': 120,
|
|
117
|
+
'GET:status': 30,
|
|
118
|
+
'GET:universe/schematics/{schematic_id}': 3600,
|
|
119
|
+
'GET:universe/structures': 3600,
|
|
120
|
+
'GET:universe/structures/{structure_id}': 3600,
|
|
121
|
+
'GET:universe/system_jumps': 3600,
|
|
122
|
+
'GET:universe/system_kills': 3600,
|
|
123
|
+
'GET:wars': 3600,
|
|
124
|
+
'GET:wars/{war_id}': 3600,
|
|
125
|
+
'GET:wars/{war_id}/killmails': 3600,
|
|
126
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esi-rate-limit-groups.generated.d.ts","sourceRoot":"","sources":["../../../src/core/endpoints/esi-rate-limit-groups.generated.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAmJjE,CAAC"}
|