@lgriffin/esi.ts 10.2.1 → 11.0.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.
Files changed (428) hide show
  1. package/CHANGELOG.md +399 -33
  2. package/LICENSE +667 -19
  3. package/NOTICE +19 -0
  4. package/README.md +149 -902
  5. package/dist/EsiClient.d.mts +5 -36
  6. package/dist/EsiClient.d.ts +5 -36
  7. package/dist/EsiClient.d.ts.map +1 -1
  8. package/dist/EsiClientBuilder.d.mts +128 -1
  9. package/dist/EsiClientBuilder.d.ts +128 -1
  10. package/dist/EsiClientBuilder.d.ts.map +1 -1
  11. package/dist/adapters/PipelineTransport.d.ts +16 -0
  12. package/dist/adapters/PipelineTransport.d.ts.map +1 -0
  13. package/dist/auth/EsiTokenManager.d.mts +62 -21
  14. package/dist/auth/EsiTokenManager.d.ts +62 -21
  15. package/dist/auth/EsiTokenManager.d.ts.map +1 -1
  16. package/dist/auth/EveSsoClient.d.mts +9 -9
  17. package/dist/auth/EveSsoClient.d.ts +9 -9
  18. package/dist/auth/EveSsoClient.d.ts.map +1 -1
  19. package/dist/auth/errors.d.mts +2 -2
  20. package/dist/auth/errors.d.ts +2 -2
  21. package/dist/auth/errors.d.ts.map +1 -1
  22. package/dist/auth/jwt.d.mts +7 -7
  23. package/dist/auth/jwt.d.ts +7 -7
  24. package/dist/auth/jwt.d.ts.map +1 -1
  25. package/dist/auth/storage/FileTokenStorage.d.mts +1 -1
  26. package/dist/auth/storage/FileTokenStorage.d.ts +1 -1
  27. package/dist/auth/storage/FileTokenStorage.d.ts.map +1 -1
  28. package/dist/{chunk-BBSHKVPI.js → chunk-CM63VGGF.js} +1 -1
  29. package/dist/chunk-CM63VGGF.js.map +1 -0
  30. package/dist/chunk-DFRTIZK4.js +275 -0
  31. package/dist/chunk-DFRTIZK4.js.map +1 -0
  32. package/dist/{chunk-VZS32UQ3.mjs → chunk-HXS7ZVEL.mjs} +129 -28
  33. package/dist/chunk-HXS7ZVEL.mjs.map +1 -0
  34. package/dist/chunk-JNFQXEWZ.mjs +275 -0
  35. package/dist/chunk-JNFQXEWZ.mjs.map +1 -0
  36. package/dist/chunk-KIY3XA5Q.mjs +2980 -0
  37. package/dist/chunk-KIY3XA5Q.mjs.map +1 -0
  38. package/dist/{chunk-R7D7M2LQ.mjs → chunk-MGOE2YNH.mjs} +1 -1
  39. package/dist/chunk-MGOE2YNH.mjs.map +1 -0
  40. package/dist/{chunk-SAQBXXTM.js → chunk-MRKZ55XI.js} +130 -29
  41. package/dist/chunk-MRKZ55XI.js.map +1 -0
  42. package/dist/chunk-NKFIHGC6.js +2980 -0
  43. package/dist/chunk-NKFIHGC6.js.map +1 -0
  44. package/dist/{chunk-MSJHIJXA.js → chunk-S73PKM2Y.js} +20 -5
  45. package/dist/chunk-S73PKM2Y.js.map +1 -0
  46. package/dist/{chunk-3ZI5A37L.mjs → chunk-YUHGSPQ7.mjs} +19 -4
  47. package/dist/chunk-YUHGSPQ7.mjs.map +1 -0
  48. package/dist/client/identity.d.mts +20 -0
  49. package/dist/client/identity.d.ts +20 -0
  50. package/dist/client/identity.d.ts.map +1 -0
  51. package/dist/client/index.d.mts +11 -0
  52. package/dist/client/index.d.ts +11 -0
  53. package/dist/client/index.d.ts.map +1 -0
  54. package/dist/client/index.js +6607 -0
  55. package/dist/client/index.js.map +1 -0
  56. package/dist/client/index.mjs +6607 -0
  57. package/dist/client/index.mjs.map +1 -0
  58. package/dist/client/runtime.d.mts +67 -0
  59. package/dist/client/runtime.d.ts +67 -0
  60. package/dist/client/runtime.d.ts.map +1 -0
  61. package/dist/clients/BaseEsiClient.d.ts.map +1 -1
  62. package/dist/clients/ClientRegistry.d.mts +45 -0
  63. package/dist/clients/ClientRegistry.d.ts +45 -0
  64. package/dist/clients/ClientRegistry.d.ts.map +1 -0
  65. package/dist/clients/CorporationsClient.d.mts +3 -3
  66. package/dist/clients/CorporationsClient.d.ts +3 -3
  67. package/dist/clients/CorporationsClient.d.ts.map +1 -1
  68. package/dist/clients/MailClient.d.mts +2 -2
  69. package/dist/clients/MailClient.d.ts +2 -2
  70. package/dist/clients/MailClient.d.ts.map +1 -1
  71. package/dist/clients/MetaClient.d.mts +3 -2
  72. package/dist/clients/MetaClient.d.ts +3 -2
  73. package/dist/clients/MetaClient.d.ts.map +1 -1
  74. package/dist/clients/MilitaryCampaignsClient.d.mts +14 -7
  75. package/dist/clients/MilitaryCampaignsClient.d.ts +14 -7
  76. package/dist/clients/MilitaryCampaignsClient.d.ts.map +1 -1
  77. package/dist/clients/RouteClient.d.mts +4 -4
  78. package/dist/clients/RouteClient.d.ts +4 -4
  79. package/dist/clients/RouteClient.d.ts.map +1 -1
  80. package/dist/clients/SkillsClient.d.mts +1 -1
  81. package/dist/clients/SkillsClient.d.ts +1 -1
  82. package/dist/clients/SkillsClient.d.ts.map +1 -1
  83. package/dist/clients/SkyhooksClient.d.mts +4 -3
  84. package/dist/clients/SkyhooksClient.d.ts +4 -3
  85. package/dist/clients/SkyhooksClient.d.ts.map +1 -1
  86. package/dist/core/ApiClient.d.mts +11 -0
  87. package/dist/core/ApiClient.d.ts +11 -0
  88. package/dist/core/ApiClient.d.ts.map +1 -1
  89. package/dist/core/ApiClientBuilder.d.ts.map +1 -1
  90. package/dist/core/ApiRequestHandler.d.ts +2 -1
  91. package/dist/core/ApiRequestHandler.d.ts.map +1 -1
  92. package/dist/core/BatchRequestHandler.d.mts +13 -2
  93. package/dist/core/BatchRequestHandler.d.ts +13 -2
  94. package/dist/core/BatchRequestHandler.d.ts.map +1 -1
  95. package/dist/core/EsiClientConfig.d.mts +60 -0
  96. package/dist/core/EsiClientConfig.d.ts +60 -0
  97. package/dist/core/EsiClientConfig.d.ts.map +1 -0
  98. package/dist/core/RetryStrategy.d.mts +3 -3
  99. package/dist/core/RetryStrategy.d.ts +3 -3
  100. package/dist/core/RetryStrategy.d.ts.map +1 -1
  101. package/dist/core/cache/ETagCacheManager.d.mts +9 -4
  102. package/dist/core/cache/ETagCacheManager.d.ts +9 -4
  103. package/dist/core/cache/ETagCacheManager.d.ts.map +1 -1
  104. package/dist/core/cache/cacheKey.d.ts +31 -6
  105. package/dist/core/cache/cacheKey.d.ts.map +1 -1
  106. package/dist/core/circuitBreaker/CircuitBreaker.d.mts +8 -12
  107. package/dist/core/circuitBreaker/CircuitBreaker.d.ts +8 -12
  108. package/dist/core/circuitBreaker/CircuitBreaker.d.ts.map +1 -1
  109. package/dist/core/clock.d.ts +8 -0
  110. package/dist/core/clock.d.ts.map +1 -0
  111. package/dist/core/configureApiClient.d.mts +1 -1
  112. package/dist/core/configureApiClient.d.ts +1 -1
  113. package/dist/core/configureApiClient.d.ts.map +1 -1
  114. package/dist/core/constants.d.ts +3 -3
  115. package/dist/core/constants.d.ts.map +1 -1
  116. package/dist/core/endpoints/EndpointDefinition.d.mts +17 -0
  117. package/dist/core/endpoints/EndpointDefinition.d.ts +17 -0
  118. package/dist/core/endpoints/EndpointDefinition.d.ts.map +1 -1
  119. package/dist/core/endpoints/assetEndpoints.d.mts +12 -0
  120. package/dist/core/endpoints/assetEndpoints.d.ts +12 -0
  121. package/dist/core/endpoints/assetEndpoints.d.ts.map +1 -1
  122. package/dist/core/endpoints/characterEndpoints.d.mts +10 -0
  123. package/dist/core/endpoints/characterEndpoints.d.ts +10 -0
  124. package/dist/core/endpoints/characterEndpoints.d.ts.map +1 -1
  125. package/dist/core/endpoints/contactEndpoints.d.mts +1 -0
  126. package/dist/core/endpoints/contactEndpoints.d.ts +1 -0
  127. package/dist/core/endpoints/contactEndpoints.d.ts.map +1 -1
  128. package/dist/core/endpoints/contractEndpoints.d.mts +2 -0
  129. package/dist/core/endpoints/contractEndpoints.d.ts +2 -0
  130. package/dist/core/endpoints/contractEndpoints.d.ts.map +1 -1
  131. package/dist/core/endpoints/corporationEndpoints.d.mts +16 -3
  132. package/dist/core/endpoints/corporationEndpoints.d.ts +16 -3
  133. package/dist/core/endpoints/corporationEndpoints.d.ts.map +1 -1
  134. package/dist/core/endpoints/createClient.d.mts +2 -2
  135. package/dist/core/endpoints/createClient.d.ts +2 -2
  136. package/dist/core/endpoints/createClient.d.ts.map +1 -1
  137. package/dist/core/endpoints/esi-cache-ttls.generated.d.ts.map +1 -1
  138. package/dist/core/endpoints/esi-rate-limit-groups.generated.d.ts.map +1 -1
  139. package/dist/core/endpoints/esi-scopes.generated.d.ts.map +1 -1
  140. package/dist/core/endpoints/fittingEndpoints.d.mts +3 -0
  141. package/dist/core/endpoints/fittingEndpoints.d.ts +3 -0
  142. package/dist/core/endpoints/fittingEndpoints.d.ts.map +1 -1
  143. package/dist/core/endpoints/fleetEndpoints.d.mts +6 -0
  144. package/dist/core/endpoints/fleetEndpoints.d.ts +6 -0
  145. package/dist/core/endpoints/fleetEndpoints.d.ts.map +1 -1
  146. package/dist/core/endpoints/industryEndpoints.d.mts +2 -2
  147. package/dist/core/endpoints/industryEndpoints.d.ts +2 -2
  148. package/dist/core/endpoints/mailEndpoints.d.mts +2 -0
  149. package/dist/core/endpoints/mailEndpoints.d.ts +2 -0
  150. package/dist/core/endpoints/mailEndpoints.d.ts.map +1 -1
  151. package/dist/core/endpoints/mercenaryEndpoints.d.mts +1 -1
  152. package/dist/core/endpoints/mercenaryEndpoints.d.ts +1 -1
  153. package/dist/core/endpoints/metaEndpoints.d.mts +23 -8
  154. package/dist/core/endpoints/metaEndpoints.d.ts +23 -8
  155. package/dist/core/endpoints/metaEndpoints.d.ts.map +1 -1
  156. package/dist/core/endpoints/militaryCampaignEndpoints.d.mts +78 -49
  157. package/dist/core/endpoints/militaryCampaignEndpoints.d.ts +78 -49
  158. package/dist/core/endpoints/militaryCampaignEndpoints.d.ts.map +1 -1
  159. package/dist/core/endpoints/paragonHubEndpoints.d.mts +5 -5
  160. package/dist/core/endpoints/paragonHubEndpoints.d.ts +5 -5
  161. package/dist/core/endpoints/skyhookEndpoints.d.mts +10 -8
  162. package/dist/core/endpoints/skyhookEndpoints.d.ts +10 -8
  163. package/dist/core/endpoints/skyhookEndpoints.d.ts.map +1 -1
  164. package/dist/core/logger/DefaultLogger.d.mts +20 -1
  165. package/dist/core/logger/DefaultLogger.d.ts +20 -1
  166. package/dist/core/logger/DefaultLogger.d.ts.map +1 -1
  167. package/dist/core/logger/ILogger.d.mts +9 -0
  168. package/dist/core/logger/ILogger.d.ts +9 -0
  169. package/dist/core/logger/ILogger.d.ts.map +1 -1
  170. package/dist/core/logger/NoopLogger.d.ts.map +1 -1
  171. package/dist/core/logger/clientLog.d.ts.map +1 -1
  172. package/dist/core/logger/loggerUtil.d.ts.map +1 -1
  173. package/dist/core/logger/redactLog.d.ts +23 -0
  174. package/dist/core/logger/redactLog.d.ts.map +1 -0
  175. package/dist/core/pagination/CursorPaginationHandler.d.mts +2 -1
  176. package/dist/core/pagination/CursorPaginationHandler.d.ts +2 -1
  177. package/dist/core/pagination/CursorPaginationHandler.d.ts.map +1 -1
  178. package/dist/core/ports/CacheStore.d.mts +32 -0
  179. package/dist/core/ports/CacheStore.d.ts +32 -0
  180. package/dist/core/ports/CacheStore.d.ts.map +1 -0
  181. package/dist/core/ports/Clock.d.mts +13 -0
  182. package/dist/core/ports/Clock.d.ts +13 -0
  183. package/dist/core/ports/Clock.d.ts.map +1 -0
  184. package/dist/core/ports/HttpTransport.d.mts +7 -0
  185. package/dist/core/ports/HttpTransport.d.ts +7 -0
  186. package/dist/core/ports/HttpTransport.d.ts.map +1 -0
  187. package/dist/core/ports/Identity.d.mts +31 -0
  188. package/dist/core/ports/Identity.d.ts +31 -0
  189. package/dist/core/ports/Identity.d.ts.map +1 -0
  190. package/dist/core/ports/Logger.d.mts +17 -0
  191. package/dist/core/ports/Logger.d.ts +17 -0
  192. package/dist/core/ports/Logger.d.ts.map +1 -0
  193. package/dist/core/ports/OperationTransport.d.mts +42 -0
  194. package/dist/core/ports/OperationTransport.d.ts +42 -0
  195. package/dist/core/ports/OperationTransport.d.ts.map +1 -0
  196. package/dist/core/ports/TokenProvider.d.mts +7 -0
  197. package/dist/core/ports/TokenProvider.d.ts +7 -0
  198. package/dist/core/ports/TokenProvider.d.ts.map +1 -0
  199. package/dist/core/ports/index.d.mts +14 -0
  200. package/dist/core/ports/index.d.ts +14 -0
  201. package/dist/core/ports/index.d.ts.map +1 -0
  202. package/dist/core/rateLimiter/RateLimiter.d.mts +11 -4
  203. package/dist/core/rateLimiter/RateLimiter.d.ts +11 -4
  204. package/dist/core/rateLimiter/RateLimiter.d.ts.map +1 -1
  205. package/dist/core/requestPipeline/cachePolicy.d.ts +5 -3
  206. package/dist/core/requestPipeline/cachePolicy.d.ts.map +1 -1
  207. package/dist/core/requestPipeline/fetchExecution.d.ts +3 -1
  208. package/dist/core/requestPipeline/fetchExecution.d.ts.map +1 -1
  209. package/dist/core/requestPipeline/headers.d.ts +1 -1
  210. package/dist/core/requestPipeline/headers.d.ts.map +1 -1
  211. package/dist/core/requestPipeline/index.d.ts +3 -5
  212. package/dist/core/requestPipeline/index.d.ts.map +1 -1
  213. package/dist/core/requestPipeline/paginationOrchestration.d.ts +1 -1
  214. package/dist/core/requestPipeline/paginationOrchestration.d.ts.map +1 -1
  215. package/dist/core/requestPipeline/statusHandling.d.ts +3 -3
  216. package/dist/core/requestPipeline/statusHandling.d.ts.map +1 -1
  217. package/dist/core/util/callerIdentity.d.ts +10 -0
  218. package/dist/core/util/callerIdentity.d.ts.map +1 -0
  219. package/dist/core/util/concurrency.d.ts +2 -2
  220. package/dist/core/util/concurrency.d.ts.map +1 -1
  221. package/dist/core/util/error.d.mts +92 -3
  222. package/dist/core/util/error.d.ts +92 -3
  223. package/dist/core/util/error.d.ts.map +1 -1
  224. package/dist/core/util/retry.d.mts +4 -4
  225. package/dist/core/util/retry.d.ts +4 -4
  226. package/dist/core/util/retry.d.ts.map +1 -1
  227. package/dist/core/util/validation.d.ts +6 -0
  228. package/dist/core/util/validation.d.ts.map +1 -1
  229. package/dist/errors.d.mts +2 -3
  230. package/dist/errors.d.ts +2 -3
  231. package/dist/errors.d.ts.map +1 -1
  232. package/dist/errors.js +29 -3
  233. package/dist/errors.js.map +1 -1
  234. package/dist/errors.mjs +28 -2
  235. package/dist/generated/operations.generated.d.mts +9379 -0
  236. package/dist/generated/operations.generated.d.ts +9379 -0
  237. package/dist/generated/operations.generated.d.ts.map +1 -0
  238. package/dist/index.d.mts +4 -4
  239. package/dist/index.d.ts +4 -4
  240. package/dist/index.d.ts.map +1 -1
  241. package/dist/index.js +699 -2864
  242. package/dist/index.js.map +1 -1
  243. package/dist/index.mjs +358 -2523
  244. package/dist/index.mjs.map +1 -1
  245. package/dist/schemas/character.d.mts +5 -0
  246. package/dist/schemas/character.d.ts +5 -0
  247. package/dist/schemas/character.d.ts.map +1 -1
  248. package/dist/schemas/contacts.d.mts +2 -0
  249. package/dist/schemas/contacts.d.ts +2 -0
  250. package/dist/schemas/contacts.d.ts.map +1 -1
  251. package/dist/schemas/corporation.d.mts +24 -3
  252. package/dist/schemas/corporation.d.ts +24 -3
  253. package/dist/schemas/corporation.d.ts.map +1 -1
  254. package/dist/schemas/fittings.d.mts +4 -0
  255. package/dist/schemas/fittings.d.ts +4 -0
  256. package/dist/schemas/fittings.d.ts.map +1 -1
  257. package/dist/schemas/fleet.d.mts +8 -0
  258. package/dist/schemas/fleet.d.ts +8 -0
  259. package/dist/schemas/fleet.d.ts.map +1 -1
  260. package/dist/schemas/index.js +24 -2
  261. package/dist/schemas/index.js.map +1 -1
  262. package/dist/schemas/index.mjs +23 -1
  263. package/dist/schemas/industry.d.mts +2 -2
  264. package/dist/schemas/industry.d.ts +2 -2
  265. package/dist/schemas/mail.d.mts +4 -0
  266. package/dist/schemas/mail.d.ts +4 -0
  267. package/dist/schemas/mail.d.ts.map +1 -1
  268. package/dist/schemas/mercenary.d.mts +1 -1
  269. package/dist/schemas/mercenary.d.ts +1 -1
  270. package/dist/schemas/meta.d.mts +18 -9
  271. package/dist/schemas/meta.d.ts +18 -9
  272. package/dist/schemas/meta.d.ts.map +1 -1
  273. package/dist/schemas/military-campaigns.d.mts +54 -10
  274. package/dist/schemas/military-campaigns.d.ts +54 -10
  275. package/dist/schemas/military-campaigns.d.ts.map +1 -1
  276. package/dist/schemas/paragon-hub.d.mts +4 -4
  277. package/dist/schemas/paragon-hub.d.ts +4 -4
  278. package/dist/schemas/skyhooks.d.mts +18 -6
  279. package/dist/schemas/skyhooks.d.ts +18 -6
  280. package/dist/schemas/skyhooks.d.ts.map +1 -1
  281. package/dist/sde/clock.d.ts +11 -0
  282. package/dist/sde/clock.d.ts.map +1 -0
  283. package/dist/sde/domain/characters/schemas.d.ts +149 -0
  284. package/dist/sde/domain/characters/schemas.d.ts.map +1 -0
  285. package/dist/sde/domain/characters/types.d.mts +163 -0
  286. package/dist/sde/domain/characters/types.d.ts +163 -0
  287. package/dist/sde/domain/characters/types.d.ts.map +1 -0
  288. package/dist/sde/domain/content/schemas.d.ts +84 -0
  289. package/dist/sde/domain/content/schemas.d.ts.map +1 -0
  290. package/dist/sde/domain/content/types.d.mts +91 -0
  291. package/dist/sde/domain/content/types.d.ts +91 -0
  292. package/dist/sde/domain/content/types.d.ts.map +1 -0
  293. package/dist/sde/domain/corporations/schemas.d.ts +113 -0
  294. package/dist/sde/domain/corporations/schemas.d.ts.map +1 -0
  295. package/dist/sde/domain/corporations/types.d.mts +118 -0
  296. package/dist/sde/domain/corporations/types.d.ts +118 -0
  297. package/dist/sde/domain/corporations/types.d.ts.map +1 -0
  298. package/dist/sde/domain/dogma/schemas.d.ts +76 -0
  299. package/dist/sde/domain/dogma/schemas.d.ts.map +1 -0
  300. package/dist/sde/domain/dogma/types.d.mts +80 -0
  301. package/dist/sde/domain/dogma/types.d.ts +80 -0
  302. package/dist/sde/domain/dogma/types.d.ts.map +1 -0
  303. package/dist/sde/domain/industry/schemas.d.ts +204 -0
  304. package/dist/sde/domain/industry/schemas.d.ts.map +1 -0
  305. package/dist/sde/domain/industry/types.d.mts +88 -0
  306. package/dist/sde/domain/industry/types.d.ts +88 -0
  307. package/dist/sde/domain/industry/types.d.ts.map +1 -0
  308. package/dist/sde/domain/market/schemas.d.ts +24 -0
  309. package/dist/sde/domain/market/schemas.d.ts.map +1 -0
  310. package/dist/sde/domain/market/types.d.mts +25 -0
  311. package/dist/sde/domain/market/types.d.ts +25 -0
  312. package/dist/sde/domain/market/types.d.ts.map +1 -0
  313. package/dist/sde/domain/schemas.d.ts +15 -0
  314. package/dist/sde/domain/schemas.d.ts.map +1 -0
  315. package/dist/sde/domain/skins/schemas.d.ts +109 -0
  316. package/dist/sde/domain/skins/schemas.d.ts.map +1 -0
  317. package/dist/sde/domain/skins/types.d.mts +121 -0
  318. package/dist/sde/domain/skins/types.d.ts +121 -0
  319. package/dist/sde/domain/skins/types.d.ts.map +1 -0
  320. package/dist/sde/domain/types/schemas.d.ts +163 -0
  321. package/dist/sde/domain/types/schemas.d.ts.map +1 -0
  322. package/dist/sde/domain/types/types.d.mts +182 -0
  323. package/dist/sde/domain/types/types.d.ts +182 -0
  324. package/dist/sde/domain/types/types.d.ts.map +1 -0
  325. package/dist/sde/domain/types.d.mts +16 -0
  326. package/dist/sde/domain/types.d.ts +16 -0
  327. package/dist/sde/domain/types.d.ts.map +1 -0
  328. package/dist/sde/domain/ui/schemas.d.ts +42 -0
  329. package/dist/sde/domain/ui/schemas.d.ts.map +1 -0
  330. package/dist/sde/domain/ui/types.d.mts +45 -0
  331. package/dist/sde/domain/ui/types.d.ts +45 -0
  332. package/dist/sde/domain/ui/types.d.ts.map +1 -0
  333. package/dist/sde/domain/universe/schemas.d.ts +239 -0
  334. package/dist/sde/domain/universe/schemas.d.ts.map +1 -0
  335. package/dist/sde/domain/universe/types.d.mts +206 -0
  336. package/dist/sde/domain/universe/types.d.ts +206 -0
  337. package/dist/sde/domain/universe/types.d.ts.map +1 -0
  338. package/dist/sde/domain/version/schemas.d.ts +11 -0
  339. package/dist/sde/domain/version/schemas.d.ts.map +1 -0
  340. package/dist/sde/errors.d.mts +1 -1
  341. package/dist/sde/errors.d.ts +1 -1
  342. package/dist/sde/errors.d.ts.map +1 -1
  343. package/dist/sde/index.d.mts +7 -6
  344. package/dist/sde/index.d.ts +7 -6
  345. package/dist/sde/index.d.ts.map +1 -1
  346. package/dist/sde/index.js +31 -21
  347. package/dist/sde/index.js.map +1 -1
  348. package/dist/sde/index.mjs +26 -16
  349. package/dist/sde/index.mjs.map +1 -1
  350. package/dist/sde/ingestion/SdeDatabaseBuilder.d.ts +11 -0
  351. package/dist/sde/ingestion/SdeDatabaseBuilder.d.ts.map +1 -1
  352. package/dist/sde/ingestion/SdeDownloader.d.ts +2 -2
  353. package/dist/sde/ingestion/SdeDownloader.d.ts.map +1 -1
  354. package/dist/sde/memory.d.mts +5 -5
  355. package/dist/sde/memory.d.ts +5 -5
  356. package/dist/sde/memory.d.ts.map +1 -1
  357. package/dist/sde/memory.js +2 -2
  358. package/dist/sde/memory.mjs +1 -1
  359. package/dist/sde/{IStaticDataProvider.d.mts → ports/IStaticDataProvider.d.mts} +9 -2
  360. package/dist/sde/{IStaticDataProvider.d.ts → ports/IStaticDataProvider.d.ts} +9 -2
  361. package/dist/sde/ports/IStaticDataProvider.d.ts.map +1 -0
  362. package/dist/sde/{MemorySdeProvider.d.mts → providers/memory/MemorySdeProvider.d.mts} +3 -3
  363. package/dist/sde/{MemorySdeProvider.d.ts → providers/memory/MemorySdeProvider.d.ts} +3 -3
  364. package/dist/sde/providers/memory/MemorySdeProvider.d.ts.map +1 -0
  365. package/dist/sde/providers/order.d.ts +10 -0
  366. package/dist/sde/providers/order.d.ts.map +1 -0
  367. package/dist/sde/{SdeDataProvider.d.mts → providers/yaml/SdeDataProvider.d.mts} +14 -5
  368. package/dist/sde/{SdeDataProvider.d.ts → providers/yaml/SdeDataProvider.d.ts} +14 -5
  369. package/dist/sde/providers/yaml/SdeDataProvider.d.ts.map +1 -0
  370. package/dist/sde/{SdeTestDataFactory.d.mts → testing/SdeTestDataFactory.d.mts} +3 -3
  371. package/dist/sde/{SdeTestDataFactory.d.ts → testing/SdeTestDataFactory.d.ts} +3 -3
  372. package/dist/sde/testing/SdeTestDataFactory.d.ts.map +1 -0
  373. package/dist/sde/version.d.mts +1 -1
  374. package/dist/sde/version.d.ts +1 -1
  375. package/dist/sde/version.d.ts.map +1 -1
  376. package/dist/testing/TestDataFactory.d.mts +21 -3
  377. package/dist/testing/TestDataFactory.d.ts +21 -3
  378. package/dist/testing/TestDataFactory.d.ts.map +1 -1
  379. package/dist/testing/index.d.mts +1 -0
  380. package/dist/testing/index.d.ts +1 -0
  381. package/dist/testing/index.d.ts.map +1 -1
  382. package/dist/testing/index.js +157 -10
  383. package/dist/testing/index.js.map +1 -1
  384. package/dist/testing/index.mjs +155 -8
  385. package/dist/testing/index.mjs.map +1 -1
  386. package/dist/testing/mockTransport.d.mts +87 -0
  387. package/dist/testing/mockTransport.d.ts +87 -0
  388. package/dist/testing/mockTransport.d.ts.map +1 -0
  389. package/dist/types/generated/esi-spec.generated.d.mts +230 -10
  390. package/dist/types/generated/esi-spec.generated.d.ts +230 -10
  391. package/dist/types/generated/esi-spec.generated.d.ts.map +1 -1
  392. package/dist/types/military-campaigns.d.mts +4 -1
  393. package/dist/types/military-campaigns.d.ts +4 -1
  394. package/dist/types/military-campaigns.d.ts.map +1 -1
  395. package/dist/types/skyhooks.d.mts +2 -1
  396. package/dist/types/skyhooks.d.ts +2 -1
  397. package/dist/types/skyhooks.d.ts.map +1 -1
  398. package/package.json +167 -131
  399. package/dist/chunk-3ZI5A37L.mjs.map +0 -1
  400. package/dist/chunk-4W66ONJI.mjs +0 -413
  401. package/dist/chunk-4W66ONJI.mjs.map +0 -1
  402. package/dist/chunk-BBSHKVPI.js.map +0 -1
  403. package/dist/chunk-MSJHIJXA.js.map +0 -1
  404. package/dist/chunk-N3B65VLB.js +0 -413
  405. package/dist/chunk-N3B65VLB.js.map +0 -1
  406. package/dist/chunk-R7D7M2LQ.mjs.map +0 -1
  407. package/dist/chunk-SAQBXXTM.js.map +0 -1
  408. package/dist/chunk-VZS32UQ3.mjs.map +0 -1
  409. package/dist/config/jest/globalSetup.d.ts +0 -2
  410. package/dist/config/jest/globalSetup.d.ts.map +0 -1
  411. package/dist/config/jest/globalTeardown.d.ts +0 -2
  412. package/dist/config/jest/globalTeardown.d.ts.map +0 -1
  413. package/dist/config/jest/jest.setup.d.ts +0 -3
  414. package/dist/config/jest/jest.setup.d.ts.map +0 -1
  415. package/dist/core/ClientRegistry.d.mts +0 -45
  416. package/dist/core/ClientRegistry.d.ts +0 -45
  417. package/dist/core/ClientRegistry.d.ts.map +0 -1
  418. package/dist/core/logger/logger.d.ts +0 -12
  419. package/dist/core/logger/logger.d.ts.map +0 -1
  420. package/dist/sde/IStaticDataProvider.d.ts.map +0 -1
  421. package/dist/sde/MemorySdeProvider.d.ts.map +0 -1
  422. package/dist/sde/SdeDataProvider.d.ts.map +0 -1
  423. package/dist/sde/SdeTestDataFactory.d.ts.map +0 -1
  424. package/dist/sde/schemas.d.ts +0 -1121
  425. package/dist/sde/schemas.d.ts.map +0 -1
  426. package/dist/sde/types.d.mts +0 -1039
  427. package/dist/sde/types.d.ts +0 -1039
  428. package/dist/sde/types.d.ts.map +0 -1
@@ -0,0 +1,2980 @@
1
+ import {
2
+ CircuitOpenError,
3
+ EsiConfigurationError,
4
+ EsiError,
5
+ EsiNetworkError,
6
+ EsiPaginationError,
7
+ EsiParseError,
8
+ EsiTokenRefreshError,
9
+ EsiValidationError,
10
+ SENSITIVE_QUERY_PATTERN,
11
+ TimeoutError,
12
+ isSensitiveQueryParam,
13
+ toEsiError
14
+ } from "./chunk-JNFQXEWZ.mjs";
15
+
16
+ // src/core/middleware/Middleware.ts
17
+ var MiddlewareManager = class {
18
+ requestInterceptors = [];
19
+ responseInterceptors = [];
20
+ addRequestInterceptor(fn) {
21
+ this.requestInterceptors.push(fn);
22
+ return () => {
23
+ this.requestInterceptors = this.requestInterceptors.filter(
24
+ (i) => i !== fn
25
+ );
26
+ };
27
+ }
28
+ addResponseInterceptor(fn) {
29
+ this.responseInterceptors.push(fn);
30
+ return () => {
31
+ this.responseInterceptors = this.responseInterceptors.filter(
32
+ (i) => i !== fn
33
+ );
34
+ };
35
+ }
36
+ async applyRequestInterceptors(context) {
37
+ let ctx = context;
38
+ for (const interceptor of this.requestInterceptors) {
39
+ ctx = await interceptor(ctx);
40
+ }
41
+ return ctx;
42
+ }
43
+ async applyResponseInterceptors(context) {
44
+ let ctx = context;
45
+ for (const interceptor of this.responseInterceptors) {
46
+ ctx = await interceptor(ctx);
47
+ }
48
+ return ctx;
49
+ }
50
+ hasInterceptors() {
51
+ return this.requestInterceptors.length > 0 || this.responseInterceptors.length > 0;
52
+ }
53
+ clear() {
54
+ this.requestInterceptors = [];
55
+ this.responseInterceptors = [];
56
+ }
57
+ };
58
+
59
+ // src/core/util/validation.ts
60
+ var UNSAFE_PATH_CHARS = /[/\\?#@!$&'()*+,;=<>{}|^`]/;
61
+ function validatePathParam(paramName, value) {
62
+ const str = value === null || value === void 0 ? "" : `${value}`;
63
+ if (str === "") {
64
+ throw new EsiConfigurationError(
65
+ "VALIDATION_ERROR",
66
+ `Path parameter '${paramName}' must not be empty`
67
+ );
68
+ }
69
+ if (UNSAFE_PATH_CHARS.test(str)) {
70
+ throw new EsiConfigurationError(
71
+ "VALIDATION_ERROR",
72
+ `Path parameter '${paramName}' contains invalid characters`
73
+ );
74
+ }
75
+ if (str === "." || str === "..") {
76
+ throw new EsiConfigurationError(
77
+ "VALIDATION_ERROR",
78
+ `Path parameter '${paramName}' must not be a dot segment`
79
+ );
80
+ }
81
+ if (typeof value === "number" && !Number.isFinite(value)) {
82
+ throw new EsiConfigurationError(
83
+ "VALIDATION_ERROR",
84
+ `Path parameter '${paramName}' must be a finite number`
85
+ );
86
+ }
87
+ return str;
88
+ }
89
+ var ALLOWED_ESI_HOSTS = ["esi.evetech.net"];
90
+ function validateBaseUrl(url, allowCustomHost) {
91
+ let parsed;
92
+ try {
93
+ parsed = new URL(url);
94
+ } catch {
95
+ throw new EsiConfigurationError(
96
+ "VALIDATION_ERROR",
97
+ `Invalid base URL: ${url}`
98
+ );
99
+ }
100
+ if (parsed.protocol !== "https:") {
101
+ throw new EsiConfigurationError(
102
+ "VALIDATION_ERROR",
103
+ "Base URL must use HTTPS protocol"
104
+ );
105
+ }
106
+ if (!allowCustomHost && !ALLOWED_ESI_HOSTS.includes(parsed.hostname)) {
107
+ throw new EsiConfigurationError(
108
+ "VALIDATION_ERROR",
109
+ `Base URL host '${parsed.hostname}' is not in the allowlist. Allowed hosts: ${ALLOWED_ESI_HOSTS.join(", ")}. Set unsafeAllowCustomHost to bypass this check.`
110
+ );
111
+ }
112
+ return url.replace(/\/$/, "");
113
+ }
114
+ function validateQueryParam(paramName, value) {
115
+ if (value === null || value === void 0) {
116
+ throw new EsiConfigurationError(
117
+ "VALIDATION_ERROR",
118
+ `Query parameter '${paramName}' must not be null or undefined`
119
+ );
120
+ }
121
+ const str = `${value}`;
122
+ if (typeof value === "number" && !Number.isFinite(value)) {
123
+ throw new EsiConfigurationError(
124
+ "VALIDATION_ERROR",
125
+ `Query parameter '${paramName}' must be a finite number`
126
+ );
127
+ }
128
+ if (str.length > 2e3) {
129
+ throw new EsiConfigurationError(
130
+ "VALIDATION_ERROR",
131
+ `Query parameter '${paramName}' exceeds maximum length`
132
+ );
133
+ }
134
+ return str;
135
+ }
136
+ var HEADER_VALUE = /^[\t\x20-\x7e]+$/;
137
+ function validateHeaderOption(option, value) {
138
+ if (!HEADER_VALUE.test(value)) {
139
+ throw new EsiConfigurationError(
140
+ "VALIDATION_ERROR",
141
+ `${option} must be printable ASCII with no line breaks or control characters`
142
+ );
143
+ }
144
+ return value;
145
+ }
146
+
147
+ // src/core/ApiClient.ts
148
+ var ApiClient = class {
149
+ constructor(clientId, link, accessToken) {
150
+ this.clientId = clientId;
151
+ this.link = link;
152
+ this.accessToken = accessToken;
153
+ this.link = this.link.replace(/\/$/, "");
154
+ }
155
+ clientId;
156
+ link;
157
+ accessToken;
158
+ datasource;
159
+ tokenProvider;
160
+ refreshInFlight;
161
+ middleware = new MiddlewareManager();
162
+ cache = null;
163
+ rateLimiter = null;
164
+ circuitBreaker = null;
165
+ timeout = 3e4;
166
+ deduplicator = null;
167
+ retryConfig = null;
168
+ retryStrategy = null;
169
+ validateResponse = true;
170
+ validateRequest = false;
171
+ language;
172
+ tenant;
173
+ userAgent;
174
+ compatibilityDate;
175
+ fetchFn = null;
176
+ logger = null;
177
+ getTimeout() {
178
+ return this.timeout;
179
+ }
180
+ setTimeout(timeout) {
181
+ this.timeout = timeout;
182
+ }
183
+ getCache() {
184
+ return this.cache;
185
+ }
186
+ setCache(cache) {
187
+ this.cache = cache;
188
+ }
189
+ getRateLimiter() {
190
+ return this.rateLimiter;
191
+ }
192
+ setRateLimiter(limiter) {
193
+ this.rateLimiter = limiter;
194
+ }
195
+ getCircuitBreaker() {
196
+ return this.circuitBreaker;
197
+ }
198
+ setCircuitBreaker(cb) {
199
+ this.circuitBreaker = cb;
200
+ }
201
+ getDeduplicator() {
202
+ return this.deduplicator;
203
+ }
204
+ setDeduplicator(dedup) {
205
+ this.deduplicator = dedup;
206
+ }
207
+ getRetryConfig() {
208
+ return this.retryConfig;
209
+ }
210
+ setRetryConfig(config) {
211
+ this.retryConfig = config;
212
+ }
213
+ getRetryStrategy() {
214
+ return this.retryStrategy;
215
+ }
216
+ setRetryStrategy(strategy) {
217
+ this.retryStrategy = strategy;
218
+ }
219
+ getValidateResponse() {
220
+ return this.validateResponse;
221
+ }
222
+ setValidateResponse(validate) {
223
+ this.validateResponse = validate;
224
+ }
225
+ getValidateRequest() {
226
+ return this.validateRequest;
227
+ }
228
+ setValidateRequest(validate) {
229
+ this.validateRequest = validate;
230
+ }
231
+ getFetch() {
232
+ return this.fetchFn ?? globalThis.fetch;
233
+ }
234
+ setFetch(fn) {
235
+ this.fetchFn = fn;
236
+ }
237
+ getMiddleware() {
238
+ return this.middleware;
239
+ }
240
+ addRequestInterceptor(fn) {
241
+ return this.middleware.addRequestInterceptor(fn);
242
+ }
243
+ addResponseInterceptor(fn) {
244
+ return this.middleware.addResponseInterceptor(fn);
245
+ }
246
+ getAuthorizationHeader() {
247
+ return this.accessToken ? `Bearer ${this.accessToken}` : void 0;
248
+ }
249
+ getLink() {
250
+ return this.link;
251
+ }
252
+ getClientId() {
253
+ return this.clientId;
254
+ }
255
+ getDatasource() {
256
+ return this.datasource;
257
+ }
258
+ setDatasource(datasource) {
259
+ this.datasource = datasource;
260
+ }
261
+ getLanguage() {
262
+ return this.language;
263
+ }
264
+ setLanguage(language) {
265
+ this.language = language;
266
+ }
267
+ /** The `X-Tenant` every request names, or undefined for ESI's default. */
268
+ getTenant() {
269
+ return this.tenant;
270
+ }
271
+ /** @throws VALIDATION_ERROR when the value is not printable ASCII. */
272
+ setTenant(tenant) {
273
+ this.tenant = tenant === void 0 ? void 0 : validateHeaderOption("tenant", tenant);
274
+ }
275
+ /** The application's own user agent, sent ahead of the library's. */
276
+ getUserAgent() {
277
+ return this.userAgent;
278
+ }
279
+ /** @throws VALIDATION_ERROR when the value is not printable ASCII. */
280
+ setUserAgent(userAgent) {
281
+ this.userAgent = userAgent === void 0 ? void 0 : validateHeaderOption("userAgent", userAgent);
282
+ }
283
+ getCompatibilityDate() {
284
+ return this.compatibilityDate;
285
+ }
286
+ setCompatibilityDate(date) {
287
+ this.compatibilityDate = date;
288
+ }
289
+ setAccessToken(token) {
290
+ this.accessToken = token;
291
+ }
292
+ setTokenProvider(provider) {
293
+ this.tokenProvider = provider;
294
+ }
295
+ getLogger() {
296
+ return this.logger;
297
+ }
298
+ setLogger(logger) {
299
+ this.logger = logger;
300
+ }
301
+ hasTokenProvider() {
302
+ return this.tokenProvider !== void 0;
303
+ }
304
+ toJSON() {
305
+ return {
306
+ link: this.link,
307
+ datasource: this.datasource,
308
+ timeout: this.timeout,
309
+ hasCache: this.cache !== null,
310
+ hasRateLimiter: this.rateLimiter !== null,
311
+ hasCircuitBreaker: this.circuitBreaker !== null,
312
+ hasDeduplicator: this.deduplicator !== null,
313
+ hasTokenProvider: this.tokenProvider !== void 0,
314
+ validateResponse: this.validateResponse,
315
+ validateRequest: this.validateRequest,
316
+ language: this.language,
317
+ compatibilityDate: this.compatibilityDate
318
+ };
319
+ }
320
+ async refreshToken() {
321
+ if (!this.tokenProvider) {
322
+ throw new Error("No token provider configured");
323
+ }
324
+ if (this.refreshInFlight) {
325
+ return this.refreshInFlight;
326
+ }
327
+ this.refreshInFlight = this.tokenProvider().then(
328
+ (token) => {
329
+ this.accessToken = token;
330
+ this.refreshInFlight = void 0;
331
+ return token;
332
+ },
333
+ (err) => {
334
+ this.refreshInFlight = void 0;
335
+ throw err;
336
+ }
337
+ );
338
+ return this.refreshInFlight;
339
+ }
340
+ };
341
+
342
+ // src/core/logger/DefaultLogger.ts
343
+ import pino from "pino";
344
+ function createDefaultLogger(level) {
345
+ return buildDefaultLogger(level);
346
+ }
347
+ function buildDefaultLogger(level) {
348
+ const resolved = level || process.env.ESI_LOG_LEVEL || "warn";
349
+ return adaptPino(pino({ level: resolved }));
350
+ }
351
+ function toPinoLogger(p) {
352
+ return adaptPino(p);
353
+ }
354
+ function adaptPino(p) {
355
+ const emit3 = (level) => (message, context) => context ? p[level](context, message) : p[level](message);
356
+ const logger = {
357
+ fatal: emit3("fatal"),
358
+ error: emit3("error"),
359
+ warn: emit3("warn"),
360
+ info: emit3("info"),
361
+ debug: emit3("debug"),
362
+ trace: emit3("trace")
363
+ };
364
+ const values = p.levels?.values;
365
+ if (values && typeof p.levelVal === "number") {
366
+ logger.isLevelEnabled = (level) => (values[level] ?? Infinity) >= (p.levelVal ?? -Infinity);
367
+ } else if (typeof p.isLevelEnabled === "function") {
368
+ logger.isLevelEnabled = (level) => p.isLevelEnabled?.(level) ?? true;
369
+ }
370
+ return logger;
371
+ }
372
+ var LEVELS = [
373
+ "fatal",
374
+ "error",
375
+ "warn",
376
+ "info",
377
+ "debug",
378
+ "trace"
379
+ ];
380
+ function createLazyDefaultLogger() {
381
+ const lazy = {};
382
+ let built;
383
+ const materialise = () => {
384
+ if (built) return built;
385
+ const real = buildDefaultLogger();
386
+ built = real;
387
+ for (const level of LEVELS) lazy[level] = real[level];
388
+ lazy.isLevelEnabled = real.isLevelEnabled ?? (() => true);
389
+ return real;
390
+ };
391
+ for (const level of LEVELS) {
392
+ lazy[level] = (message, context) => materialise()[level](message, context);
393
+ }
394
+ lazy.isLevelEnabled = (level) => materialise().isLevelEnabled?.(level) ?? true;
395
+ return lazy;
396
+ }
397
+ var defaultLogger = createLazyDefaultLogger();
398
+
399
+ // src/core/logger/redactLog.ts
400
+ var QUERY_PAIR = /([?&])([^=&#?\s]+)=([^&#\s,;"'`<>()[\]{}]*)/g;
401
+ var REDACTED = "%5BREDACTED%5D";
402
+ function redactLogText(text) {
403
+ if (!text.includes("?") || !SENSITIVE_QUERY_PATTERN.test(text)) return text;
404
+ return text.replace(
405
+ QUERY_PAIR,
406
+ (pair, separator, name, value) => value !== "" && isSensitiveQueryParam(name) ? `${separator}${name}=${REDACTED}` : pair
407
+ );
408
+ }
409
+ function redactLogContext(context) {
410
+ if (!context) return void 0;
411
+ const keys = Object.keys(context);
412
+ if (keys.length === 0) return void 0;
413
+ for (const key of keys) {
414
+ if (mayCarrySecret(context[key])) return redactValues(context, keys);
415
+ }
416
+ return context;
417
+ }
418
+ function mayCarrySecret(value) {
419
+ return typeof value === "string" && value.includes("?") && SENSITIVE_QUERY_PATTERN.test(value);
420
+ }
421
+ function redactValues(context, keys) {
422
+ const redacted = {};
423
+ for (const key of keys) {
424
+ const value = context[key];
425
+ redacted[key] = mayCarrySecret(value) ? redactLogText(value) : value;
426
+ }
427
+ return redacted;
428
+ }
429
+ function redactingLogger(logger) {
430
+ const at = (level) => (message, context) => {
431
+ if (logger.isLevelEnabled?.(level) === false) return;
432
+ const safe = redactLogContext(context);
433
+ if (safe === void 0) logger[level](redactLogText(message));
434
+ else logger[level](redactLogText(message), safe);
435
+ };
436
+ return {
437
+ fatal: at("fatal"),
438
+ error: at("error"),
439
+ warn: at("warn"),
440
+ info: at("info"),
441
+ debug: at("debug"),
442
+ trace: at("trace")
443
+ };
444
+ }
445
+
446
+ // src/core/logger/loggerUtil.ts
447
+ var activeLogger = defaultLogger;
448
+ var setLogger = (customLogger) => {
449
+ activeLogger = customLogger;
450
+ };
451
+ var getLogger = () => activeLogger;
452
+ var emit = (level) => (message, context) => {
453
+ if (activeLogger.isLevelEnabled?.(level) === false) return;
454
+ activeLogger[level](redactLogText(message), redactLogContext(context));
455
+ };
456
+ var logFatal = emit("fatal");
457
+ var logError = emit("error");
458
+ var logWarn = emit("warn");
459
+ var logInfo = emit("info");
460
+ var logDebug = emit("debug");
461
+ var logTrace = emit("trace");
462
+
463
+ // src/core/logger/resolveLogger.ts
464
+ function resolveLogger(client) {
465
+ return client?.getLogger?.() ?? getLogger();
466
+ }
467
+
468
+ // src/core/logger/clientLog.ts
469
+ var emit2 = (level) => (client, message, context) => {
470
+ const logger = resolveLogger(client);
471
+ if (logger.isLevelEnabled?.(level) === false) return;
472
+ logger[level](redactLogText(message), redactLogContext(context));
473
+ };
474
+ var logFatal2 = emit2("fatal");
475
+ var logError2 = emit2("error");
476
+ var logWarn2 = emit2("warn");
477
+ var logInfo2 = emit2("info");
478
+ var logDebug2 = emit2("debug");
479
+ var logTrace2 = emit2("trace");
480
+
481
+ // src/core/circuitBreaker/CircuitBreaker.ts
482
+ var CircuitBreaker = class {
483
+ circuits = /* @__PURE__ */ new Map();
484
+ failureThreshold;
485
+ resetTimeoutMs;
486
+ halfOpenMaxAttempts;
487
+ staleThresholdMs;
488
+ keyStrategy;
489
+ cleanupTimer = null;
490
+ client = null;
491
+ setClient(client) {
492
+ this.client = client;
493
+ }
494
+ constructor(config = {}) {
495
+ this.failureThreshold = config.failureThreshold ?? 5;
496
+ this.resetTimeoutMs = config.resetTimeoutMs ?? 3e4;
497
+ this.halfOpenMaxAttempts = config.halfOpenMaxAttempts ?? 1;
498
+ this.staleThresholdMs = config.staleThresholdMs ?? 36e5;
499
+ this.keyStrategy = config.keyStrategy ?? "resolved";
500
+ const cleanupIntervalMs = config.cleanupIntervalMs;
501
+ if (cleanupIntervalMs !== void 0 && cleanupIntervalMs > 0) {
502
+ this.startCleanupTimer(cleanupIntervalMs);
503
+ }
504
+ }
505
+ getKey(endpoint) {
506
+ return endpoint.split("?")[0];
507
+ }
508
+ getOrCreate(key) {
509
+ let record = this.circuits.get(key);
510
+ if (!record) {
511
+ record = {
512
+ state: "closed",
513
+ failures: 0,
514
+ lastFailureTime: 0,
515
+ halfOpenAttempts: 0
516
+ };
517
+ this.circuits.set(key, record);
518
+ }
519
+ return record;
520
+ }
521
+ checkCircuit(endpoint) {
522
+ const key = this.getKey(endpoint);
523
+ const record = this.getOrCreate(key);
524
+ if (record.state === "closed") return;
525
+ if (record.state === "open") {
526
+ const elapsed = Date.now() - record.lastFailureTime;
527
+ if (elapsed >= this.resetTimeoutMs) {
528
+ record.state = "half-open";
529
+ record.halfOpenAttempts = 1;
530
+ logInfo2(
531
+ this.client,
532
+ `Circuit half-open for ${key}, allowing probe request`,
533
+ { key }
534
+ );
535
+ return;
536
+ }
537
+ const remainingMs = this.resetTimeoutMs - elapsed;
538
+ throw new CircuitOpenError(key, record.failures, remainingMs);
539
+ }
540
+ if (record.state === "half-open") {
541
+ if (record.halfOpenAttempts >= this.halfOpenMaxAttempts) {
542
+ throw new CircuitOpenError(key, record.failures, this.resetTimeoutMs);
543
+ }
544
+ record.halfOpenAttempts++;
545
+ }
546
+ }
547
+ recordSuccess(endpoint) {
548
+ const key = this.getKey(endpoint);
549
+ const record = this.circuits.get(key);
550
+ if (!record) return;
551
+ if (record.state === "open") return;
552
+ if (record.state === "half-open") {
553
+ logInfo2(this.client, `Circuit closed for ${key} after successful probe`, {
554
+ key
555
+ });
556
+ }
557
+ record.state = "closed";
558
+ record.failures = 0;
559
+ record.halfOpenAttempts = 0;
560
+ }
561
+ recordFailure(endpoint, statusCode) {
562
+ if (statusCode !== 0 && statusCode < 500 && statusCode !== 420 && statusCode !== 429) {
563
+ return;
564
+ }
565
+ const key = this.getKey(endpoint);
566
+ const record = this.getOrCreate(key);
567
+ record.failures++;
568
+ record.lastFailureTime = Date.now();
569
+ if (record.state === "half-open") {
570
+ record.state = "open";
571
+ logWarn2(
572
+ this.client,
573
+ `Circuit re-opened for ${key} after failed probe (${record.failures} failures)`,
574
+ { key, failures: record.failures }
575
+ );
576
+ return;
577
+ }
578
+ if (record.failures >= this.failureThreshold) {
579
+ record.state = "open";
580
+ logWarn2(
581
+ this.client,
582
+ `Circuit opened for ${key} after ${record.failures} consecutive failures`,
583
+ { key, failures: record.failures }
584
+ );
585
+ }
586
+ }
587
+ getState(endpoint) {
588
+ const key = this.getKey(endpoint);
589
+ const record = this.circuits.get(key);
590
+ if (!record) return "closed";
591
+ if (record.state === "open") {
592
+ const elapsed = Date.now() - record.lastFailureTime;
593
+ if (elapsed >= this.resetTimeoutMs) {
594
+ return "half-open";
595
+ }
596
+ }
597
+ return record.state;
598
+ }
599
+ getStats() {
600
+ const circuits = {};
601
+ let openCircuits = 0;
602
+ for (const [key, record] of this.circuits.entries()) {
603
+ const state = this.getState(key);
604
+ circuits[key] = { state, failures: record.failures };
605
+ if (state === "open") openCircuits++;
606
+ }
607
+ return {
608
+ totalCircuits: this.circuits.size,
609
+ openCircuits,
610
+ circuits
611
+ };
612
+ }
613
+ reset(endpoint) {
614
+ if (endpoint) {
615
+ this.circuits.delete(this.getKey(endpoint));
616
+ } else {
617
+ this.circuits.clear();
618
+ }
619
+ }
620
+ cleanup() {
621
+ const now = Date.now();
622
+ let cleaned = 0;
623
+ for (const [key, record] of this.circuits.entries()) {
624
+ if (record.state === "closed" && record.failures === 0 && record.lastFailureTime > 0 && now - record.lastFailureTime > this.staleThresholdMs) {
625
+ this.circuits.delete(key);
626
+ cleaned++;
627
+ }
628
+ }
629
+ if (cleaned > 0) {
630
+ logInfo2(
631
+ this.client,
632
+ `Circuit breaker cleanup: removed ${cleaned} stale circuits`,
633
+ {
634
+ cleaned
635
+ }
636
+ );
637
+ }
638
+ return cleaned;
639
+ }
640
+ getKeyStrategy() {
641
+ return this.keyStrategy;
642
+ }
643
+ destroy() {
644
+ if (this.cleanupTimer) {
645
+ clearInterval(this.cleanupTimer);
646
+ this.cleanupTimer = null;
647
+ }
648
+ this.circuits.clear();
649
+ }
650
+ shutdown() {
651
+ this.destroy();
652
+ }
653
+ startCleanupTimer(intervalMs) {
654
+ this.cleanupTimer = setInterval(() => {
655
+ this.cleanup();
656
+ }, intervalMs);
657
+ this.cleanupTimer.unref();
658
+ }
659
+ };
660
+
661
+ // src/core/util/retry.ts
662
+ function retryDelay(attempt, baseMs, maxMs) {
663
+ const exponential = baseMs === 0 ? 0 : baseMs * Math.pow(2, attempt);
664
+ const jitter = exponential * (0.75 + Math.random() * 0.5);
665
+ return Math.min(jitter, maxMs);
666
+ }
667
+
668
+ // src/core/util/sleep.ts
669
+ function sleep(ms) {
670
+ return new Promise((resolve) => setTimeout(resolve, ms));
671
+ }
672
+
673
+ // src/core/RetryStrategy.ts
674
+ var RetryStrategy = class {
675
+ maxRetries;
676
+ baseDelayMs;
677
+ maxDelayMs;
678
+ retryMutations;
679
+ constructor(config) {
680
+ this.maxRetries = config?.maxRetries ?? 0;
681
+ this.baseDelayMs = config?.baseDelayMs ?? 1e3;
682
+ this.maxDelayMs = config?.maxDelayMs ?? 3e4;
683
+ this.retryMutations = config?.retryMutations ?? false;
684
+ }
685
+ async execute(operation, context) {
686
+ const canRetryMethod = context.method === "GET" || this.retryMutations;
687
+ let lastError;
688
+ let refreshAttempted = false;
689
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
690
+ try {
691
+ return await operation();
692
+ } catch (error) {
693
+ if (error instanceof CircuitOpenError) {
694
+ throw error;
695
+ }
696
+ if (!refreshAttempted && error instanceof EsiError && error.statusCode === 401 && context.requiresAuth && context.refreshToken) {
697
+ refreshAttempted = true;
698
+ logInfo2(context.client, "Received 401, attempting token refresh...", {
699
+ endpoint: context.endpoint
700
+ });
701
+ try {
702
+ await context.refreshToken();
703
+ } catch (refreshError) {
704
+ if (refreshError instanceof EsiError) {
705
+ throw refreshError;
706
+ }
707
+ const msg = refreshError instanceof Error ? refreshError.message : String(refreshError);
708
+ logError2(context.client, `Token refresh failed: ${msg}`, {
709
+ endpoint: context.endpoint
710
+ });
711
+ throw new EsiTokenRefreshError(refreshError);
712
+ }
713
+ logInfo2(context.client, "Token refreshed, retrying request", {
714
+ endpoint: context.endpoint
715
+ });
716
+ attempt--;
717
+ continue;
718
+ }
719
+ if (error instanceof EsiError && error.retryable && canRetryMethod && attempt < this.maxRetries) {
720
+ const delay = retryDelay(attempt, this.baseDelayMs, this.maxDelayMs);
721
+ logWarn2(
722
+ context.client,
723
+ `Request to ${context.endpoint} failed (${error.statusCode}), retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${this.maxRetries})`,
724
+ { method: context.method, statusCode: error.statusCode }
725
+ );
726
+ await sleep(delay);
727
+ lastError = error;
728
+ continue;
729
+ }
730
+ throw error;
731
+ }
732
+ }
733
+ throw lastError;
734
+ }
735
+ };
736
+
737
+ // src/core/util/callerIdentity.ts
738
+ import { createHash } from "crypto";
739
+ var CHARACTER_SUBJECT = /^CHARACTER:EVE:(\d+)$/;
740
+ function characterIdOfToken(token) {
741
+ const parts = token.split(".");
742
+ if (parts.length !== 3 || !parts[1]) return null;
743
+ let claims;
744
+ try {
745
+ claims = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
746
+ } catch {
747
+ return null;
748
+ }
749
+ if (typeof claims !== "object" || claims === null) return null;
750
+ const sub = claims.sub;
751
+ const match = typeof sub === "string" ? CHARACTER_SUBJECT.exec(sub) : null;
752
+ return match ? Number(match[1]) : null;
753
+ }
754
+ function hashedIdentity(authorizationHeader) {
755
+ return createHash("sha256").update(authorizationHeader).digest("hex").slice(0, 16);
756
+ }
757
+ function callerIdentity(authorizationHeader) {
758
+ const token = authorizationHeader.startsWith("Bearer ") ? authorizationHeader.slice("Bearer ".length) : authorizationHeader;
759
+ const characterId = characterIdOfToken(token);
760
+ if (characterId !== null) return `character:${characterId}`;
761
+ return hashedIdentity(authorizationHeader);
762
+ }
763
+
764
+ // src/core/cache/cacheKey.ts
765
+ var identities = /* @__PURE__ */ new WeakMap();
766
+ var accepted = /* @__PURE__ */ new WeakMap();
767
+ var ACCEPTED_LIMIT = 64;
768
+ function identityOf(client, header) {
769
+ const known = identities.get(client);
770
+ if (known && known.header === header) return known;
771
+ const identity = {
772
+ header,
773
+ claimed: callerIdentity(header),
774
+ hashed: hashedIdentity(header)
775
+ };
776
+ identities.set(client, identity);
777
+ return identity;
778
+ }
779
+ function markTokenAccepted(client, authorizationHeader) {
780
+ let set = accepted.get(client);
781
+ if (!set) {
782
+ set = /* @__PURE__ */ new Set();
783
+ accepted.set(client, set);
784
+ }
785
+ const hashed = hashedIdentity(authorizationHeader);
786
+ if (set.has(hashed)) return;
787
+ if (set.size >= ACCEPTED_LIMIT) {
788
+ set.delete(set.values().next().value);
789
+ }
790
+ set.add(hashed);
791
+ }
792
+ function isAccepted(client, identity) {
793
+ return accepted.get(client)?.has(identity.hashed) ?? false;
794
+ }
795
+ function scope(key, identity) {
796
+ return identity === null ? key : `${identity}:${key}`;
797
+ }
798
+ function trustedIdentity(client, requiresAuth, authorizationHeader) {
799
+ if (!requiresAuth) return null;
800
+ const header = authorizationHeader ?? client.getAuthorizationHeader();
801
+ if (!header) return null;
802
+ const identity = identityOf(client, header);
803
+ return isAccepted(client, identity) ? identity.claimed : identity.hashed;
804
+ }
805
+ function claimedIdentity(client, requiresAuth) {
806
+ if (!requiresAuth) return null;
807
+ const header = client.getAuthorizationHeader();
808
+ if (!header) return null;
809
+ return identityOf(client, header).claimed;
810
+ }
811
+ function buildCacheKey(url, client, requiresAuth = false, authorizationHeader) {
812
+ return scope(url, trustedIdentity(client, requiresAuth, authorizationHeader));
813
+ }
814
+ function buildConditionalCacheKey(url, client, requiresAuth = false) {
815
+ return scope(url, claimedIdentity(client, requiresAuth));
816
+ }
817
+ function buildDedupeKey(endpoint, client, requiresAuth = false) {
818
+ return scope(endpoint, trustedIdentity(client, requiresAuth));
819
+ }
820
+
821
+ // src/core/util/stringUtil.ts
822
+ function camelToSnake(s) {
823
+ return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
824
+ }
825
+
826
+ // src/core/endpoints/esi-cache-ttls.generated.ts
827
+ var esiCacheTtls = {
828
+ "GET:alliances": 3600,
829
+ "GET:alliances/{alliance_id}": 3600,
830
+ "GET:alliances/{alliance_id}/contacts": 300,
831
+ "GET:alliances/{alliance_id}/contacts/labels": 300,
832
+ "GET:alliances/{alliance_id}/corporations": 3600,
833
+ "GET:characters/{character_id}": 86400,
834
+ "GET:characters/{character_id}/access-lists": 300,
835
+ "GET:characters/{character_id}/access-lists/{access_list_id}": 300,
836
+ "GET:characters/{character_id}/agents_research": 3600,
837
+ "GET:characters/{character_id}/assets": 3600,
838
+ "GET:characters/{character_id}/attributes": 120,
839
+ "GET:characters/{character_id}/blueprints": 3600,
840
+ "GET:characters/{character_id}/calendar": 5,
841
+ "GET:characters/{character_id}/calendar/{event_id}": 5,
842
+ "PUT:characters/{character_id}/calendar/{event_id}": 5,
843
+ "GET:characters/{character_id}/calendar/{event_id}/attendees": 600,
844
+ "GET:characters/{character_id}/clones": 120,
845
+ "GET:characters/{character_id}/contacts": 300,
846
+ "GET:characters/{character_id}/contacts/labels": 300,
847
+ "GET:characters/{character_id}/contracts": 300,
848
+ "GET:characters/{character_id}/contracts/{contract_id}/bids": 300,
849
+ "GET:characters/{character_id}/contracts/{contract_id}/items": 3600,
850
+ "GET:characters/{character_id}/corporationhistory": 86400,
851
+ "GET:characters/{character_id}/cosmetics/skinr": 3600,
852
+ "GET:characters/{character_id}/cosmetics/skinr/components": 3600,
853
+ "GET:characters/{character_id}/fatigue": 300,
854
+ "GET:characters/{character_id}/fittings": 300,
855
+ "GET:characters/{character_id}/fleet": 60,
856
+ "GET:characters/{character_id}/freelance-jobs": 60,
857
+ "GET:characters/{character_id}/freelance-jobs/{job_id}/participation": 60,
858
+ "GET:characters/{character_id}/implants": 120,
859
+ "GET:characters/{character_id}/industry/jobs": 300,
860
+ "GET:characters/{character_id}/killmails/recent": 300,
861
+ "GET:characters/{character_id}/location": 5,
862
+ "GET:characters/{character_id}/loyalty/points": 3600,
863
+ "GET:characters/{character_id}/mail": 30,
864
+ "GET:characters/{character_id}/mail/{mail_id}": 30,
865
+ "GET:characters/{character_id}/mail/labels": 30,
866
+ "GET:characters/{character_id}/mail/lists": 120,
867
+ "GET:characters/{character_id}/medals": 3600,
868
+ "GET:characters/{character_id}/mercenary-tactical-operations": 300,
869
+ "GET:characters/{character_id}/mercenary-tactical-operations/{operation_id}": 300,
870
+ "GET:characters/{character_id}/military-campaigns/objectives": 60,
871
+ "GET:characters/{character_id}/military-campaigns/objectives/{objective_id}": 60,
872
+ "GET:characters/{character_id}/mining": 600,
873
+ "GET:characters/{character_id}/notifications": 600,
874
+ "GET:characters/{character_id}/notifications/contacts": 600,
875
+ "GET:characters/{character_id}/online": 60,
876
+ "GET:characters/{character_id}/orders": 1200,
877
+ "GET:characters/{character_id}/orders/history": 3600,
878
+ "GET:characters/{character_id}/planets": 600,
879
+ "GET:characters/{character_id}/planets/{planet_id}": 600,
880
+ "GET:characters/{character_id}/roles": 3600,
881
+ "GET:characters/{character_id}/search": 3600,
882
+ "GET:characters/{character_id}/ship": 5,
883
+ "GET:characters/{character_id}/skillqueue": 60,
884
+ "GET:characters/{character_id}/skills": 60,
885
+ "GET:characters/{character_id}/standings": 3600,
886
+ "GET:characters/{character_id}/structures/mercenary-dens": 3600,
887
+ "GET:characters/{character_id}/structures/mercenary-dens/{mercenary_den_id}": 3600,
888
+ "GET:characters/{character_id}/titles": 3600,
889
+ "GET:characters/{character_id}/wallet": 120,
890
+ "GET:characters/{character_id}/wallet/journal": 3600,
891
+ "GET:characters/{character_id}/wallet/transactions": 3600,
892
+ "POST:characters/affiliation": 3600,
893
+ "GET:contracts/public/{region_id}": 1800,
894
+ "GET:contracts/public/bids/{contract_id}": 300,
895
+ "GET:contracts/public/items/{contract_id}": 3600,
896
+ "GET:corporation/{corporation_id}/mining/extractions": 1800,
897
+ "GET:corporation/{corporation_id}/mining/observers": 3600,
898
+ "GET:corporation/{corporation_id}/mining/observers/{observer_id}": 3600,
899
+ "GET:corporations/{corporation_id}": 3600,
900
+ "GET:corporations/{corporation_id}/alliancehistory": 3600,
901
+ "GET:corporations/{corporation_id}/assets": 3600,
902
+ "GET:corporations/{corporation_id}/blueprints": 3600,
903
+ "GET:corporations/{corporation_id}/contacts": 300,
904
+ "GET:corporations/{corporation_id}/contacts/labels": 300,
905
+ "GET:corporations/{corporation_id}/containers/logs": 600,
906
+ "GET:corporations/{corporation_id}/contracts": 300,
907
+ "GET:corporations/{corporation_id}/contracts/{contract_id}/bids": 3600,
908
+ "GET:corporations/{corporation_id}/contracts/{contract_id}/items": 3600,
909
+ "GET:corporations/{corporation_id}/customs_offices": 3600,
910
+ "GET:corporations/{corporation_id}/divisions": 3600,
911
+ "GET:corporations/{corporation_id}/facilities": 3600,
912
+ "GET:corporations/{corporation_id}/icons": 3600,
913
+ "GET:corporations/{corporation_id}/industry/jobs": 300,
914
+ "GET:corporations/{corporation_id}/killmails/recent": 300,
915
+ "GET:corporations/{corporation_id}/medals": 3600,
916
+ "GET:corporations/{corporation_id}/medals/issued": 3600,
917
+ "GET:corporations/{corporation_id}/members": 3600,
918
+ "GET:corporations/{corporation_id}/members/limit": 3600,
919
+ "GET:corporations/{corporation_id}/members/titles": 3600,
920
+ "GET:corporations/{corporation_id}/membertracking": 3600,
921
+ "GET:corporations/{corporation_id}/orders": 1200,
922
+ "GET:corporations/{corporation_id}/orders/history": 3600,
923
+ "GET:corporations/{corporation_id}/projects/{project_id}": 60,
924
+ "GET:corporations/{corporation_id}/projects/{project_id}/contribution/{character_id}": 60,
925
+ "GET:corporations/{corporation_id}/roles": 3600,
926
+ "GET:corporations/{corporation_id}/roles/history": 3600,
927
+ "GET:corporations/{corporation_id}/shareholders": 3600,
928
+ "GET:corporations/{corporation_id}/standings": 3600,
929
+ "GET:corporations/{corporation_id}/starbases": 3600,
930
+ "GET:corporations/{corporation_id}/starbases/{starbase_id}": 3600,
931
+ "GET:corporations/{corporation_id}/structures": 3600,
932
+ "GET:corporations/{corporation_id}/structures/skyhooks": 3600,
933
+ "GET:corporations/{corporation_id}/structures/skyhooks/{skyhook_id}": 3600,
934
+ "GET:corporations/{corporation_id}/structures/sovereignty-hubs": 3600,
935
+ "GET:corporations/{corporation_id}/structures/sovereignty-hubs/{sovereignty_hub_id}": 3600,
936
+ "GET:corporations/{corporation_id}/titles": 3600,
937
+ "GET:corporations/{corporation_id}/wallets": 300,
938
+ "GET:corporations/{corporation_id}/wallets/{division}/journal": 3600,
939
+ "GET:corporations/{corporation_id}/wallets/{division}/transactions": 3600,
940
+ "GET:cosmetics/skinr/{skinr_id}": 31536e3,
941
+ "GET:fleets/{fleet_id}": 5,
942
+ "GET:fleets/{fleet_id}/members": 5,
943
+ "GET:fleets/{fleet_id}/wings": 5,
944
+ "GET:freelance-jobs/{job_id}": 60,
945
+ "GET:fw/systems": 1800,
946
+ "GET:incursions": 300,
947
+ "GET:industry/facilities": 3600,
948
+ "GET:industry/systems": 3600,
949
+ "GET:insurance/prices": 3600,
950
+ "GET:killmails/{killmail_id}/{killmail_hash}": 2592e3,
951
+ "GET:markets/{region_id}/orders": 300,
952
+ "GET:markets/{region_id}/types": 600,
953
+ "GET:markets/prices": 3600,
954
+ "GET:markets/structures/{structure_id}": 300,
955
+ "GET:meta/changelog": 600,
956
+ "GET:meta/compatibility-dates": 600,
957
+ "GET:meta/name": 600,
958
+ "GET:military-campaigns": 60,
959
+ "GET:military-campaigns/{campaign_id}": 60,
960
+ "GET:military-campaigns/{campaign_id}/objectives": 60,
961
+ "GET:military-campaigns/{campaign_id}/objectives/{objective_id}": 60,
962
+ "GET:skyhooks/raidable": 300,
963
+ "GET:sovereignty/campaigns": 5,
964
+ "GET:sovereignty/systems": 300,
965
+ "GET:status": 30,
966
+ "GET:universe/schematics/{schematic_id}": 3600,
967
+ "GET:universe/structures": 3600,
968
+ "GET:universe/structures/{structure_id}": 3600,
969
+ "GET:universe/system_jumps": 3600,
970
+ "GET:universe/system_kills": 3600,
971
+ "GET:wars": 3600,
972
+ "GET:wars/{war_id}": 3600,
973
+ "GET:wars/{war_id}/killmails": 3600
974
+ };
975
+
976
+ // src/core/constants.ts
977
+ var PACKAGE_NAME = "esi.ts";
978
+ var PACKAGE_VERSION = "11.0.0";
979
+ var USER_AGENT = `${PACKAGE_NAME}/${PACKAGE_VERSION}`;
980
+ var COMPATIBILITY_DATE = "2026-08-18";
981
+
982
+ // src/core/requestPipeline/headers.ts
983
+ var parseCacheControlTtl = (headers) => {
984
+ const cacheControl = headers["cache-control"] ?? headers["Cache-Control"];
985
+ if (!cacheControl) return void 0;
986
+ const match = /max-age=(\d+)/.exec(cacheControl);
987
+ return match ? parseInt(match[1], 10) * 1e3 : void 0;
988
+ };
989
+ var HEADER_VALUE2 = /^[\t\x20-\x7e]+$/;
990
+ function buildRequestHeaders(client, url, method, requiresAuth, useETag, body, resolveCache2, accept = "application/json") {
991
+ const userAgent = client.getUserAgent();
992
+ const headers = {
993
+ Accept: accept,
994
+ "Accept-Encoding": "gzip, deflate, br",
995
+ "User-Agent": userAgent ? `${userAgent} ${USER_AGENT}` : USER_AGENT,
996
+ "X-Compatibility-Date": client.getCompatibilityDate() ?? COMPATIBILITY_DATE
997
+ };
998
+ const clientId = client.getClientId();
999
+ if (userAgent) {
1000
+ headers["X-User-Agent"] = userAgent;
1001
+ } else if (HEADER_VALUE2.test(clientId)) {
1002
+ headers["X-User-Agent"] = clientId;
1003
+ }
1004
+ const tenant = client.getTenant();
1005
+ if (tenant) {
1006
+ headers["X-Tenant"] = tenant;
1007
+ }
1008
+ const language = client.getLanguage();
1009
+ if (language) {
1010
+ headers["Accept-Language"] = language;
1011
+ }
1012
+ if (requiresAuth) {
1013
+ const authHeader = client.getAuthorizationHeader();
1014
+ if (!authHeader) {
1015
+ throw new EsiConfigurationError(
1016
+ "NO_AUTH_TOKEN",
1017
+ [
1018
+ "Authorization header is required for this endpoint but no access token is configured",
1019
+ "Fix: set ESI_ACCESS_TOKEN in your environment (see .env.example), or call client.setAccessToken(token), or pass accessToken to the EsiClient constructor"
1020
+ ].join(" \u2014 ")
1021
+ );
1022
+ }
1023
+ headers["Authorization"] = authHeader;
1024
+ }
1025
+ const cache = resolveCache2(client);
1026
+ if (useETag && method === "GET" && cache) {
1027
+ const key = buildConditionalCacheKey(url, client, requiresAuth);
1028
+ const cachedETag = cache.getETag(key);
1029
+ if (cachedETag) {
1030
+ headers["If-None-Match"] = cachedETag;
1031
+ logDebug2(client, `Adding If-None-Match header: ${cachedETag}`, {
1032
+ etag: cachedETag
1033
+ });
1034
+ }
1035
+ }
1036
+ if (body) {
1037
+ headers["Content-Type"] = "application/json";
1038
+ }
1039
+ return headers;
1040
+ }
1041
+
1042
+ // src/core/requestPipeline/cachePolicy.ts
1043
+ var STALE_RETENTION_MS = 60 * 60 * 1e3;
1044
+ function retentionTtl(freshnessTtlMs) {
1045
+ return freshnessTtlMs === void 0 ? void 0 : freshnessTtlMs + STALE_RETENTION_MS;
1046
+ }
1047
+ function lookupSpecTtl(method, templatePath) {
1048
+ const normalized = templatePath.replace(/\/$/, "").replace(/\{(\w+)\}/g, (_, name) => `{${camelToSnake(name)}}`);
1049
+ const key = `${method}:${normalized}`;
1050
+ const seconds = esiCacheTtls[key];
1051
+ return typeof seconds === "number" ? seconds * 1e3 : void 0;
1052
+ }
1053
+ function trySpecAwareCacheHit(client, url, method, templatePath, resolveCache2, requiresAuth = false) {
1054
+ if (method !== "GET" || !templatePath) return null;
1055
+ const specTtlMs = lookupSpecTtl(method, templatePath);
1056
+ if (!specTtlMs) return null;
1057
+ const cache = resolveCache2(client);
1058
+ if (!cache) return null;
1059
+ const key = buildCacheKey(url, client, requiresAuth);
1060
+ const entry = cache.get(key);
1061
+ if (!entry) return null;
1062
+ const age = Date.now() - entry.timestamp;
1063
+ if (age < specTtlMs) {
1064
+ logDebug2(
1065
+ client,
1066
+ `Spec-aware cache hit for ${url} (age=${Math.round(age / 1e3)}s, ttl=${Math.round(specTtlMs / 1e3)}s)`,
1067
+ { method, templatePath }
1068
+ );
1069
+ return {
1070
+ headers: entry.headers,
1071
+ body: entry.data,
1072
+ status: 200,
1073
+ fromCache: true,
1074
+ cacheHitType: "spec-ttl"
1075
+ };
1076
+ }
1077
+ return null;
1078
+ }
1079
+ function hasCachedEntry(client, url, resolveCache2, requiresAuth = false, authorization) {
1080
+ const cache = resolveCache2(client);
1081
+ return !!cache && cache.has(buildCacheKey(url, client, requiresAuth, authorization));
1082
+ }
1083
+ function tryStaleCacheResponse(client, url, parsed, resolveCache2, requiresAuth = false, authorization) {
1084
+ const cache = resolveCache2(client);
1085
+ if (!cache) return null;
1086
+ const key = buildCacheKey(url, client, requiresAuth, authorization);
1087
+ const cachedEntry = cache.get(key);
1088
+ if (!cachedEntry) return null;
1089
+ return {
1090
+ headers: { ...cachedEntry.headers, ...parsed.raw },
1091
+ body: cachedEntry.data,
1092
+ status: 200,
1093
+ fromCache: true,
1094
+ stale: true,
1095
+ cacheHitType: "stale-on-error"
1096
+ };
1097
+ }
1098
+ var RECENT_WRITES = 64;
1099
+ var writeLogs = /* @__PURE__ */ new WeakMap();
1100
+ function writeLogFor(cache) {
1101
+ let log = writeLogs.get(cache);
1102
+ if (!log) {
1103
+ log = { generation: 0, recent: [] };
1104
+ writeLogs.set(cache, log);
1105
+ }
1106
+ return log;
1107
+ }
1108
+ function currentWriteGeneration(client, resolveCache2) {
1109
+ const cache = resolveCache2(client);
1110
+ return cache ? writeLogFor(cache).generation : 0;
1111
+ }
1112
+ function invalidatedSince(cache, generation, key) {
1113
+ const log = writeLogFor(cache);
1114
+ if (log.generation === generation) return false;
1115
+ if (log.generation - generation > log.recent.length) return true;
1116
+ return log.recent.some(
1117
+ (write) => write.generation > generation && key.includes(write.path)
1118
+ );
1119
+ }
1120
+ function cacheResponse(client, url, method, endpoint, parsed, data, useETag, resolveCache2, templatePath, requiresAuth = false, sentAtWriteGeneration, authorization) {
1121
+ const cache = resolveCache2(client);
1122
+ if (useETag && method === "GET" && cache && parsed.etag) {
1123
+ const key = buildCacheKey(url, client, requiresAuth, authorization);
1124
+ if (sentAtWriteGeneration !== void 0 && invalidatedSince(cache, sentAtWriteGeneration, key)) {
1125
+ logDebug2(
1126
+ client,
1127
+ `Not caching ${url}: a write invalidated it while the request was in flight`,
1128
+ { method }
1129
+ );
1130
+ return;
1131
+ }
1132
+ const headerTtl = parseCacheControlTtl(parsed.raw);
1133
+ const specTtlMs = templatePath ? lookupSpecTtl(method, templatePath) : void 0;
1134
+ const freshnessTtl = specTtlMs ?? headerTtl;
1135
+ const ttl = retentionTtl(freshnessTtl);
1136
+ cache.set(key, parsed.etag, data, parsed.raw, ttl);
1137
+ const ttlInfo = freshnessTtl !== void 0 ? ` (ttl=${freshnessTtl}ms, kept for ${ttl}ms)` : "";
1138
+ logDebug2(
1139
+ client,
1140
+ `Cached response for ${url} with ETag ${parsed.etag}${ttlInfo}`,
1141
+ {
1142
+ method,
1143
+ etag: parsed.etag
1144
+ }
1145
+ );
1146
+ }
1147
+ invalidateAfterWrite(client, method, endpoint, resolveCache2);
1148
+ }
1149
+ function evictRejectedResponse(client, endpoint, requiresAuth, resolveCache2) {
1150
+ const cache = resolveCache2(client);
1151
+ if (!cache) return;
1152
+ const url = `${client.getLink()}/${endpoint}`;
1153
+ if (cache.delete(buildCacheKey(url, client, requiresAuth))) {
1154
+ logDebug2(
1155
+ client,
1156
+ `Evicted cached response for ${url} after it failed validation`
1157
+ );
1158
+ }
1159
+ }
1160
+ function invalidateAfterWrite(client, method, endpoint, resolveCache2) {
1161
+ if (method === "GET") return;
1162
+ const path = endpoint.split("?")[0];
1163
+ client.getDeduplicator()?.detachByPath?.(path);
1164
+ const cache = resolveCache2(client);
1165
+ if (cache) {
1166
+ cache.deleteByPath(path);
1167
+ const log = writeLogFor(cache);
1168
+ log.generation += 1;
1169
+ log.recent.push({ generation: log.generation, path });
1170
+ if (log.recent.length > RECENT_WRITES) log.recent.shift();
1171
+ }
1172
+ }
1173
+
1174
+ // src/core/requestPipeline/statusHandling.ts
1175
+ var STATUS_MESSAGES = {
1176
+ 201: "Created",
1177
+ 204: "No Content",
1178
+ 304: "Not Modified",
1179
+ 400: "Bad Request",
1180
+ 401: "Unauthorized",
1181
+ 403: "Forbidden",
1182
+ 404: "Resource not found",
1183
+ 420: "Error Limited",
1184
+ 422: "Unprocessable Entity",
1185
+ 429: "Too many requests",
1186
+ 500: "Internal server error",
1187
+ 502: "Bad Gateway",
1188
+ 503: "Service Unavailable",
1189
+ 504: "Gateway Timeout",
1190
+ 520: "Internal server error, did the request terminate too soon?"
1191
+ };
1192
+ function statusMessage(response) {
1193
+ return STATUS_MESSAGES[response.status] || response.statusText || `HTTP ${response.status}`;
1194
+ }
1195
+ function handleEarlyStatus(client, status, url, parsed, useETag, resolveCache2, requiresAuth = false, authorization) {
1196
+ if (status === 201) {
1197
+ return { headers: parsed.raw, body: void 0, status: 201 };
1198
+ }
1199
+ if (status === 204) {
1200
+ logInfo2(client, `No Content for endpoint: ${url}`, { status });
1201
+ return { headers: parsed.raw, body: void 0, status: 204 };
1202
+ }
1203
+ if (status === 304) {
1204
+ const cache = resolveCache2(client);
1205
+ if (useETag && cache) {
1206
+ const key = buildCacheKey(url, client, requiresAuth, authorization);
1207
+ const cachedEntry = cache.get(key);
1208
+ if (cachedEntry) {
1209
+ logInfo2(client, `Cache hit (304) for endpoint: ${url}`, {
1210
+ status,
1211
+ cacheHitType: "etag-304"
1212
+ });
1213
+ const headers = {
1214
+ ...cachedEntry.headers,
1215
+ ...parsed.raw
1216
+ };
1217
+ if ("etag" in parsed.raw) headers["etag"] = cachedEntry.etag;
1218
+ cache.set(
1219
+ key,
1220
+ cachedEntry.etag,
1221
+ cachedEntry.data,
1222
+ headers,
1223
+ cachedEntry.ttl
1224
+ );
1225
+ return {
1226
+ headers,
1227
+ body: cachedEntry.data,
1228
+ status: 304,
1229
+ fromCache: true,
1230
+ cacheHitType: "etag-304"
1231
+ };
1232
+ }
1233
+ }
1234
+ throw new EsiError(
1235
+ 304,
1236
+ "Not Modified \u2014 no cached data available",
1237
+ url,
1238
+ parsed.requestId ?? void 0
1239
+ );
1240
+ }
1241
+ return null;
1242
+ }
1243
+ var MAX_REASON_LENGTH = 200;
1244
+ async function readEsiErrorReason(response) {
1245
+ try {
1246
+ const parsed = JSON.parse(await response.text());
1247
+ const reason = typeof parsed === "object" && parsed !== null ? parsed.error : void 0;
1248
+ if (typeof reason !== "string") return void 0;
1249
+ const trimmed = reason.trim();
1250
+ return trimmed ? trimmed.slice(0, MAX_REASON_LENGTH) : void 0;
1251
+ } catch {
1252
+ return void 0;
1253
+ }
1254
+ }
1255
+ function handleErrorResponse(client, response, url, parsed, useETag, resolveCache2, requiresAuth = false, esiReason, authorization) {
1256
+ const text = statusMessage(response);
1257
+ const errorMessage = esiReason ? `${text}: ${esiReason}` : text;
1258
+ if (response.status >= 500 && useETag) {
1259
+ const staleResult = tryStaleCacheResponse(
1260
+ client,
1261
+ url,
1262
+ parsed,
1263
+ resolveCache2,
1264
+ requiresAuth,
1265
+ authorization
1266
+ );
1267
+ if (staleResult) {
1268
+ logWarn2(client, `${errorMessage} for ${url} \u2014 serving stale cache`, {
1269
+ status: response.status
1270
+ });
1271
+ return { ...staleResult, status: response.status };
1272
+ }
1273
+ }
1274
+ if (response.status === 420 || response.status === 429) {
1275
+ logWarn2(client, `Rate limited (${response.status}) on ${url}`, {
1276
+ status: response.status
1277
+ });
1278
+ }
1279
+ let message = errorMessage;
1280
+ if (response.status === 401) {
1281
+ message = `${errorMessage} \u2014 the access token was missing, expired, or lacks the required ESI scope. Fix: verify ESI_ACCESS_TOKEN, or configure onTokenRefresh for automatic refresh on 401`;
1282
+ } else if (response.status === 403) {
1283
+ message = `${errorMessage} \u2014 your access token does not have the OAuth scopes required for this endpoint. Check the scopes on your EVE SSO application`;
1284
+ }
1285
+ throw new EsiError(
1286
+ response.status,
1287
+ message,
1288
+ url,
1289
+ parsed.requestId ?? void 0
1290
+ );
1291
+ }
1292
+ function wrapError(error, client) {
1293
+ if (error instanceof EsiError) {
1294
+ throw error;
1295
+ }
1296
+ const message = error instanceof Error ? error.message : String(error);
1297
+ logError2(client, `Unexpected error: ${message}`);
1298
+ throw toEsiError(error);
1299
+ }
1300
+
1301
+ // src/core/requestPipeline/dependencies.ts
1302
+ function resolveCache(client) {
1303
+ return client.getCache();
1304
+ }
1305
+ function resolveRateLimiter(client) {
1306
+ const limiter = client.getRateLimiter();
1307
+ if (!limiter) {
1308
+ throw new EsiConfigurationError(
1309
+ "CONFIGURATION_ERROR",
1310
+ "No rate limiter configured on ApiClient. Set one via apiClient.setRateLimiter(new RateLimiter())."
1311
+ );
1312
+ }
1313
+ return limiter;
1314
+ }
1315
+ function resolveCircuitBreaker(client) {
1316
+ return client.getCircuitBreaker();
1317
+ }
1318
+ function resolveRetryStrategy(client) {
1319
+ return client.getRetryStrategy() ?? new RetryStrategy(client.getRetryConfig() ?? void 0);
1320
+ }
1321
+
1322
+ // src/core/pagination/PaginationHandler.ts
1323
+ var PaginationHandler = class {
1324
+ static DEFAULT_OPTIONS = {
1325
+ maxPages: 1e3,
1326
+ stopOnEmptyPage: true
1327
+ };
1328
+ /**
1329
+ * Fetch remaining pages (2..totalPages) and combine with first page data.
1330
+ * The caller has already fetched page 1 and knows the total page count.
1331
+ */
1332
+ static async fetchRemainingPages(client, endpoint, method, requiresAuth, firstPageData, totalPages, body, options, pageFetch, templatePath) {
1333
+ const opts = { ...this.DEFAULT_OPTIONS, ...options };
1334
+ const rateLimiter = client.getRateLimiter();
1335
+ const allData = [...firstPageData];
1336
+ const effectiveMaxPage = Math.min(totalPages, opts.maxPages);
1337
+ if (effectiveMaxPage <= 1) {
1338
+ return allData;
1339
+ }
1340
+ logInfo2(client, `Fetching pages 2-${effectiveMaxPage} for ${endpoint}...`, {
1341
+ method,
1342
+ totalPages
1343
+ });
1344
+ for (let page = 2; page <= effectiveMaxPage; page++) {
1345
+ try {
1346
+ if (rateLimiter) await rateLimiter.checkRateLimit(templatePath, method);
1347
+ const pageData = await this.fetchPageWithRetry(
1348
+ client,
1349
+ endpoint,
1350
+ method,
1351
+ requiresAuth,
1352
+ page,
1353
+ pageFetch
1354
+ );
1355
+ if (opts.stopOnEmptyPage && (!pageData || pageData.length === 0)) {
1356
+ logWarn2(client, `Page ${page} is empty. Stopping pagination.`, {
1357
+ page
1358
+ });
1359
+ break;
1360
+ }
1361
+ allData.push(...pageData);
1362
+ logInfo2(
1363
+ client,
1364
+ `Fetched page ${page}/${effectiveMaxPage} (${pageData.length} items)`,
1365
+ { page, items: pageData.length }
1366
+ );
1367
+ } catch (error) {
1368
+ logError2(
1369
+ client,
1370
+ `Failed to fetch page ${page}: ${error instanceof Error ? error.message : String(error)}`,
1371
+ { page }
1372
+ );
1373
+ throw error instanceof Error ? error : new Error(`Failed to fetch page ${page}: ${String(error)}`);
1374
+ }
1375
+ }
1376
+ logInfo2(
1377
+ client,
1378
+ `Pagination complete. Fetched ${allData.length} total items from up to ${effectiveMaxPage} pages.`,
1379
+ { totalItems: allData.length }
1380
+ );
1381
+ return allData;
1382
+ }
1383
+ /**
1384
+ * Fetch a single page with retry logic.
1385
+ *
1386
+ * Delegates to the configured `IRetryStrategy` (exponential backoff
1387
+ * with jitter) instead of implementing a custom retry loop.
1388
+ */
1389
+ static async fetchPageWithRetry(client, endpoint, method, requiresAuth, page, pageFetch) {
1390
+ const retryStrategy = resolveRetryStrategy(client);
1391
+ const paginatedEndpoint = this.buildPaginatedEndpoint(endpoint, page);
1392
+ return retryStrategy.execute(
1393
+ () => {
1394
+ logInfo2(
1395
+ client,
1396
+ `Fetching page ${page} via pipeline: ${paginatedEndpoint}`,
1397
+ { page }
1398
+ );
1399
+ return pageFetch(paginatedEndpoint);
1400
+ },
1401
+ {
1402
+ client,
1403
+ endpoint: paginatedEndpoint,
1404
+ method,
1405
+ requiresAuth,
1406
+ refreshToken: client.hasTokenProvider() ? () => client.refreshToken().then(() => {
1407
+ }) : void 0
1408
+ }
1409
+ );
1410
+ }
1411
+ /**
1412
+ * Build a paginated endpoint path.
1413
+ * Preserves any existing query params on the endpoint and appends page=N.
1414
+ */
1415
+ static buildPaginatedEndpoint(endpoint, page) {
1416
+ const separator = endpoint.includes("?") ? "&" : "?";
1417
+ return `${endpoint}${separator}page=${page}`;
1418
+ }
1419
+ };
1420
+
1421
+ // src/core/requestPipeline/paginationOrchestration.ts
1422
+ function handleCursorPagination(parsed, data) {
1423
+ if (!parsed.hasCursorPagination) return null;
1424
+ const cursors = {
1425
+ before: parsed.cursorBefore,
1426
+ after: parsed.cursorAfter
1427
+ };
1428
+ return { headers: parsed.raw, body: data, status: 200, cursors };
1429
+ }
1430
+ async function handleOffsetPagination(client, endpoint, method, requiresAuth, parsed, data, body, url, useETag, pageFetch, resolveCache2, templatePath, sentAtWriteGeneration, authorization) {
1431
+ const totalPages = parsed.xPages;
1432
+ if (totalPages <= 1) {
1433
+ return { headers: parsed.raw, body: data, status: 200 };
1434
+ }
1435
+ logInfo2(
1436
+ client,
1437
+ `Found ${totalPages} pages, fetching additional pages with rate limiting...`,
1438
+ { endpoint, totalPages }
1439
+ );
1440
+ try {
1441
+ const firstPageData = Array.isArray(data) ? data : [data];
1442
+ const allData = await PaginationHandler.fetchRemainingPages(
1443
+ client,
1444
+ endpoint,
1445
+ method,
1446
+ requiresAuth,
1447
+ firstPageData,
1448
+ totalPages,
1449
+ body,
1450
+ {
1451
+ maxPages: Math.min(totalPages, 1e3),
1452
+ stopOnEmptyPage: true
1453
+ },
1454
+ pageFetch,
1455
+ templatePath
1456
+ );
1457
+ cacheResponse(
1458
+ client,
1459
+ url,
1460
+ method,
1461
+ endpoint,
1462
+ parsed,
1463
+ allData,
1464
+ useETag,
1465
+ resolveCache2,
1466
+ templatePath,
1467
+ requiresAuth,
1468
+ sentAtWriteGeneration,
1469
+ authorization
1470
+ );
1471
+ return { headers: parsed.raw, body: allData, status: 200 };
1472
+ } catch (paginationError) {
1473
+ const msg = paginationError instanceof Error ? paginationError.message : String(paginationError);
1474
+ logWarn2(client, `Pagination failed for ${url}: ${msg}`, {
1475
+ url,
1476
+ endpoint,
1477
+ error: msg
1478
+ });
1479
+ if (paginationError instanceof EsiError) {
1480
+ throw paginationError;
1481
+ }
1482
+ throw new EsiPaginationError(url, paginationError);
1483
+ }
1484
+ }
1485
+
1486
+ // src/core/requestPipeline/middlewareBridge.ts
1487
+ async function applyRequestMiddleware(client, url, endpoint, method, headers, body) {
1488
+ const middleware = client.getMiddleware();
1489
+ if (!middleware.hasInterceptors()) {
1490
+ return { url, headers, body };
1491
+ }
1492
+ const reqCtx = {
1493
+ url,
1494
+ endpoint,
1495
+ method,
1496
+ headers: { ...headers },
1497
+ body
1498
+ };
1499
+ const modified = await middleware.applyRequestInterceptors(reqCtx);
1500
+ return {
1501
+ url: modified.url,
1502
+ headers: modified.headers,
1503
+ body: modified.body
1504
+ };
1505
+ }
1506
+ async function applyResponseInterceptors(client, result, url, endpoint, method, startTime) {
1507
+ const middleware = client.getMiddleware();
1508
+ if (!middleware.hasInterceptors()) return result;
1509
+ const responseCtx = {
1510
+ url,
1511
+ endpoint,
1512
+ method,
1513
+ status: result.status ?? 200,
1514
+ headers: result.headers,
1515
+ body: result.body,
1516
+ durationMs: Date.now() - startTime,
1517
+ fromCache: result.fromCache ?? false
1518
+ };
1519
+ const modified = await middleware.applyResponseInterceptors(responseCtx);
1520
+ return {
1521
+ ...result,
1522
+ headers: modified.headers,
1523
+ body: modified.body
1524
+ };
1525
+ }
1526
+
1527
+ // src/core/util/headersUtil.ts
1528
+ function parseWarning(value) {
1529
+ if (!value) return null;
1530
+ const match = /^(\d{3}) - "([^"]+)"$/.exec(value) ?? /^(\d{3}) - ([^ ].+[^ ])$/.exec(value);
1531
+ if (!match) return null;
1532
+ const [, code, message] = match;
1533
+ return { code: parseInt(code, 10), message };
1534
+ }
1535
+ function parseHeaders(fetchHeaders) {
1536
+ const raw = {};
1537
+ fetchHeaders.forEach((value, key) => {
1538
+ raw[key.toLowerCase()] = value;
1539
+ });
1540
+ const cursorBefore = raw["x-cursor-before"] ?? null;
1541
+ const cursorAfter = raw["x-cursor-after"] ?? null;
1542
+ return {
1543
+ raw,
1544
+ xPages: parseInt(raw["x-pages"] ?? "1", 10),
1545
+ etag: raw["etag"] ?? null,
1546
+ cacheControl: raw["cache-control"] ?? null,
1547
+ expires: raw["expires"] ?? null,
1548
+ lastModified: raw["last-modified"] ?? null,
1549
+ cursorBefore,
1550
+ cursorAfter,
1551
+ hasCursorPagination: "x-cursor-before" in raw || "x-cursor-after" in raw,
1552
+ warning: parseWarning(raw["warning"]),
1553
+ requestId: raw["x-esi-request-id"] ?? null,
1554
+ date: raw["date"] ?? null,
1555
+ contentLanguage: raw["content-language"] ?? null
1556
+ };
1557
+ }
1558
+
1559
+ // src/core/requestPipeline/fetchExecution.ts
1560
+ function isAbortError(err) {
1561
+ return typeof err === "object" && err !== null && err.name === "AbortError";
1562
+ }
1563
+ function networkError(err, url) {
1564
+ const reason = err instanceof Error ? err.message : String(err);
1565
+ return new EsiNetworkError(reason, url, err);
1566
+ }
1567
+ async function bufferBody(response) {
1568
+ if (typeof response.text !== "function") return;
1569
+ const text = await response.text();
1570
+ Object.defineProperties(response, {
1571
+ text: { value: () => Promise.resolve(text), configurable: true },
1572
+ json: {
1573
+ value: () => Promise.resolve(text).then((t) => JSON.parse(t)),
1574
+ configurable: true
1575
+ }
1576
+ });
1577
+ }
1578
+ async function executeSingleFetch(client, endpoint, method, body, requiresAuth, useETag, resolveCache2, resolveRateLimiter2, resolveCircuitBreaker2, requestTimeout, templatePath, accept) {
1579
+ const rawUrl = `${client.getLink()}/${endpoint}`;
1580
+ const builtHeaders = buildRequestHeaders(
1581
+ client,
1582
+ rawUrl,
1583
+ method,
1584
+ requiresAuth,
1585
+ useETag,
1586
+ body,
1587
+ resolveCache2,
1588
+ accept
1589
+ );
1590
+ const req = await applyRequestMiddleware(
1591
+ client,
1592
+ rawUrl,
1593
+ endpoint,
1594
+ method,
1595
+ builtHeaders,
1596
+ body
1597
+ );
1598
+ const options = {
1599
+ method,
1600
+ headers: req.headers,
1601
+ ...req.body ? { body: JSON.stringify(req.body) } : {}
1602
+ };
1603
+ const url = req.url;
1604
+ logInfo2(client, `Hitting endpoint: ${url}`, { method, endpoint });
1605
+ const cb = resolveCircuitBreaker2(client);
1606
+ const cbKey = cb && templatePath && cb.getKeyStrategy?.() === "template" ? templatePath : endpoint;
1607
+ if (cb) cb.checkCircuit(cbKey);
1608
+ let cbRecorded = false;
1609
+ try {
1610
+ const rateLimiter = resolveRateLimiter2(client);
1611
+ await rateLimiter.checkRateLimit(templatePath, method, req.headers);
1612
+ const timeoutMs = requestTimeout ?? client.getTimeout();
1613
+ const controller = new AbortController();
1614
+ const timer = globalThis.setTimeout(() => controller.abort(), timeoutMs);
1615
+ options.signal = controller.signal;
1616
+ let response;
1617
+ try {
1618
+ response = await client.getFetch()(url, options);
1619
+ await bufferBody(response);
1620
+ } catch (err) {
1621
+ clearTimeout(timer);
1622
+ if (cb) {
1623
+ cb.recordFailure(cbKey, 0);
1624
+ cbRecorded = true;
1625
+ }
1626
+ if (err instanceof EsiError) throw err;
1627
+ if (isAbortError(err)) {
1628
+ throw new TimeoutError(timeoutMs, url);
1629
+ }
1630
+ throw networkError(err, url);
1631
+ }
1632
+ clearTimeout(timer);
1633
+ const parsed = parseHeaders(response.headers);
1634
+ if (requiresAuth && (response.ok || response.status === 304)) {
1635
+ const sent = req.headers["Authorization"];
1636
+ if (sent) markTokenAccepted(client, sent);
1637
+ }
1638
+ rateLimiter.updateFromResponse(
1639
+ parsed.raw,
1640
+ response.status,
1641
+ templatePath,
1642
+ method,
1643
+ req.headers
1644
+ );
1645
+ if (cb) {
1646
+ if (response.status >= 500 || response.status === 420 || response.status === 429) {
1647
+ cb.recordFailure(cbKey, response.status);
1648
+ } else {
1649
+ cb.recordSuccess(cbKey);
1650
+ }
1651
+ cbRecorded = true;
1652
+ }
1653
+ if (parsed.warning) {
1654
+ logWarn2(
1655
+ client,
1656
+ `ESI Warning ${parsed.warning.code}: ${parsed.warning.message}`,
1657
+ {
1658
+ url,
1659
+ warningCode: parsed.warning.code
1660
+ }
1661
+ );
1662
+ }
1663
+ return {
1664
+ response,
1665
+ parsed,
1666
+ url,
1667
+ authorization: req.headers["Authorization"]
1668
+ };
1669
+ } finally {
1670
+ if (cb && !cbRecorded) {
1671
+ cb.recordFailure(cbKey, 0);
1672
+ }
1673
+ }
1674
+ }
1675
+ async function parseJsonBody(client, response, _url) {
1676
+ try {
1677
+ return await response.json();
1678
+ } catch (jsonError) {
1679
+ const msg = jsonError instanceof Error ? jsonError.message : String(jsonError);
1680
+ logError2(client, `Failed to parse JSON response: ${msg}`, { url: _url });
1681
+ throw new EsiParseError(`Invalid JSON response: ${msg}`, _url, jsonError);
1682
+ }
1683
+ }
1684
+ async function fetchOnePage(client, endpoint, method, body, requiresAuth, useETag, resolveCache2, resolveRateLimiter2, resolveCircuitBreaker2, requestTimeout, templatePath) {
1685
+ const { response, parsed, url } = await executeSingleFetch(
1686
+ client,
1687
+ endpoint,
1688
+ method,
1689
+ body,
1690
+ requiresAuth,
1691
+ useETag,
1692
+ resolveCache2,
1693
+ resolveRateLimiter2,
1694
+ resolveCircuitBreaker2,
1695
+ requestTimeout,
1696
+ templatePath
1697
+ );
1698
+ if (!response.ok) {
1699
+ const text = statusMessage(response);
1700
+ const reason = await readEsiErrorReason(response);
1701
+ throw new EsiError(
1702
+ response.status,
1703
+ reason ? `${text}: ${reason}` : text,
1704
+ url,
1705
+ parsed.requestId ?? void 0
1706
+ );
1707
+ }
1708
+ const data = await parseJsonBody(client, response, url);
1709
+ return { data, parsed, url };
1710
+ }
1711
+
1712
+ // src/core/ApiRequestHandler.ts
1713
+ var executeRequest = async (client, endpoint, method, body, requiresAuth = false, useETag = true, requestTimeout, templatePath, textResponse, emptyWhenNoContent) => {
1714
+ const startTime = Date.now();
1715
+ const finish = (r) => {
1716
+ r.responseTimeMs = Date.now() - startTime;
1717
+ const rawUrl = `${client.getLink()}/${endpoint}`;
1718
+ return applyResponseInterceptors(
1719
+ client,
1720
+ r,
1721
+ rawUrl,
1722
+ endpoint,
1723
+ method,
1724
+ startTime
1725
+ );
1726
+ };
1727
+ try {
1728
+ const writeGeneration = currentWriteGeneration(client, resolveCache);
1729
+ const revalidating = useETag && method === "GET" && hasCachedEntry(
1730
+ client,
1731
+ `${client.getLink()}/${endpoint}`,
1732
+ resolveCache,
1733
+ requiresAuth
1734
+ );
1735
+ const { response, parsed, url, authorization } = await executeSingleFetch(
1736
+ client,
1737
+ endpoint,
1738
+ method,
1739
+ body,
1740
+ requiresAuth,
1741
+ useETag,
1742
+ resolveCache,
1743
+ resolveRateLimiter,
1744
+ resolveCircuitBreaker,
1745
+ requestTimeout,
1746
+ templatePath,
1747
+ textResponse?.accept
1748
+ );
1749
+ if (response.status === 201 || response.status === 204) {
1750
+ invalidateAfterWrite(client, method, endpoint, resolveCache);
1751
+ }
1752
+ if (response.status === 201) {
1753
+ let data2;
1754
+ try {
1755
+ data2 = await response.json();
1756
+ } catch {
1757
+ data2 = void 0;
1758
+ }
1759
+ return finish({ headers: parsed.raw, body: data2, status: 201 });
1760
+ }
1761
+ if (emptyWhenNoContent && response.status === 200 && response.headers.get("content-length") === "0") {
1762
+ logInfo2(client, `No Content for endpoint: ${url}`, { status: 200 });
1763
+ return finish({ headers: parsed.raw, body: void 0, status: 200 });
1764
+ }
1765
+ if (response.status === 304 && revalidating && !hasCachedEntry(client, url, resolveCache, requiresAuth, authorization)) {
1766
+ return await executeRequest(
1767
+ client,
1768
+ endpoint,
1769
+ method,
1770
+ body,
1771
+ requiresAuth,
1772
+ useETag,
1773
+ requestTimeout,
1774
+ templatePath,
1775
+ textResponse,
1776
+ emptyWhenNoContent
1777
+ );
1778
+ }
1779
+ const earlyResult = handleEarlyStatus(
1780
+ client,
1781
+ response.status,
1782
+ url,
1783
+ parsed,
1784
+ useETag,
1785
+ resolveCache,
1786
+ requiresAuth,
1787
+ authorization
1788
+ );
1789
+ if (earlyResult) return finish(earlyResult);
1790
+ if (!response.ok) {
1791
+ const staleOrThrow = handleErrorResponse(
1792
+ client,
1793
+ response,
1794
+ url,
1795
+ parsed,
1796
+ useETag,
1797
+ resolveCache,
1798
+ requiresAuth,
1799
+ await readEsiErrorReason(response),
1800
+ authorization
1801
+ );
1802
+ return finish(staleOrThrow);
1803
+ }
1804
+ const data = textResponse ? await response.text() : await parseJsonBody(client, response, url);
1805
+ if (parsed.xPages <= 1 || parsed.hasCursorPagination) {
1806
+ cacheResponse(
1807
+ client,
1808
+ url,
1809
+ method,
1810
+ endpoint,
1811
+ parsed,
1812
+ data,
1813
+ useETag,
1814
+ resolveCache,
1815
+ templatePath,
1816
+ requiresAuth,
1817
+ writeGeneration,
1818
+ authorization
1819
+ );
1820
+ }
1821
+ const cursorResult = handleCursorPagination(parsed, data);
1822
+ if (cursorResult) return finish(cursorResult);
1823
+ const pageFetch = async (paginatedEndpoint) => {
1824
+ const result = await fetchOnePage(
1825
+ client,
1826
+ paginatedEndpoint,
1827
+ method,
1828
+ body,
1829
+ requiresAuth,
1830
+ false,
1831
+ resolveCache,
1832
+ resolveRateLimiter,
1833
+ resolveCircuitBreaker,
1834
+ requestTimeout,
1835
+ templatePath
1836
+ );
1837
+ return Array.isArray(result.data) ? result.data : [result.data];
1838
+ };
1839
+ const paginatedResult = await handleOffsetPagination(
1840
+ client,
1841
+ endpoint,
1842
+ method,
1843
+ requiresAuth,
1844
+ parsed,
1845
+ data,
1846
+ body,
1847
+ url,
1848
+ useETag,
1849
+ pageFetch,
1850
+ resolveCache,
1851
+ templatePath,
1852
+ writeGeneration,
1853
+ authorization
1854
+ );
1855
+ return finish(paginatedResult);
1856
+ } catch (error) {
1857
+ wrapError(error, client);
1858
+ }
1859
+ };
1860
+ var handleSinglePageRequest = async (client, endpoint, method, body, requiresAuth = false, templatePath, requestTimeout) => {
1861
+ const doExecute = () => fetchOnePage(
1862
+ client,
1863
+ endpoint,
1864
+ method,
1865
+ body,
1866
+ requiresAuth,
1867
+ false,
1868
+ resolveCache,
1869
+ resolveRateLimiter,
1870
+ resolveCircuitBreaker,
1871
+ requestTimeout,
1872
+ templatePath
1873
+ ).then(({ data, parsed }) => ({
1874
+ headers: parsed.raw,
1875
+ body: data
1876
+ }));
1877
+ const retryStrategy = resolveRetryStrategy(client);
1878
+ return retryStrategy.execute(doExecute, {
1879
+ client,
1880
+ endpoint,
1881
+ method,
1882
+ requiresAuth,
1883
+ refreshToken: client.hasTokenProvider() ? () => client.refreshToken().then(() => {
1884
+ }) : void 0
1885
+ });
1886
+ };
1887
+ var handleRequest = async (client, endpoint, method, body, requiresAuth = false, useETag = true, templatePath, requestTimeout, textResponse, emptyWhenNoContent = false) => {
1888
+ const rawUrl = `${client.getLink()}/${endpoint}`;
1889
+ const startTime = Date.now();
1890
+ const specCacheHit = () => {
1891
+ const hit = trySpecAwareCacheHit(
1892
+ client,
1893
+ rawUrl,
1894
+ method,
1895
+ templatePath,
1896
+ resolveCache,
1897
+ requiresAuth
1898
+ );
1899
+ return hit ? applyResponseInterceptors(
1900
+ client,
1901
+ hit,
1902
+ rawUrl,
1903
+ endpoint,
1904
+ method,
1905
+ startTime
1906
+ ) : null;
1907
+ };
1908
+ const specHit = specCacheHit();
1909
+ if (specHit) return specHit;
1910
+ const doExecute = () => executeRequest(
1911
+ client,
1912
+ endpoint,
1913
+ method,
1914
+ body,
1915
+ requiresAuth,
1916
+ useETag,
1917
+ requestTimeout,
1918
+ templatePath,
1919
+ textResponse,
1920
+ emptyWhenNoContent
1921
+ );
1922
+ const dedup = client.getDeduplicator();
1923
+ const canDedup = dedup && method === "GET" && !body;
1924
+ let attempted = false;
1925
+ const operation = () => {
1926
+ const retryHit = attempted ? specCacheHit() : null;
1927
+ attempted = true;
1928
+ if (retryHit) return retryHit;
1929
+ return canDedup ? dedup.dedupe(
1930
+ buildDedupeKey(endpoint, client, requiresAuth),
1931
+ doExecute
1932
+ ) : doExecute();
1933
+ };
1934
+ const retryStrategy = resolveRetryStrategy(client);
1935
+ return retryStrategy.execute(operation, {
1936
+ client,
1937
+ endpoint,
1938
+ method,
1939
+ requiresAuth,
1940
+ refreshToken: client.hasTokenProvider() ? () => client.refreshToken().then(() => {
1941
+ }) : void 0
1942
+ });
1943
+ };
1944
+
1945
+ // src/core/pagination/AsyncPaginationIterator.ts
1946
+ function extractArray(body) {
1947
+ if (Array.isArray(body)) return body;
1948
+ if (body !== void 0 && body !== null) return [body];
1949
+ return [];
1950
+ }
1951
+ function validatePageBody(body, schema, url, validate) {
1952
+ if (!schema || !validate) return body;
1953
+ const result = schema.safeParse(body);
1954
+ if (!result.success) {
1955
+ throw new EsiValidationError(url, result.error);
1956
+ }
1957
+ return result.data;
1958
+ }
1959
+ async function fetchAllPages(client, endpoint, method, requiresAuth = false, body, templatePath, responseSchema, concurrency = 8) {
1960
+ const validate = client.getValidateResponse();
1961
+ const firstResponse = await handleSinglePageRequest(
1962
+ client,
1963
+ endpoint,
1964
+ method,
1965
+ body,
1966
+ requiresAuth,
1967
+ templatePath
1968
+ );
1969
+ const firstBody = validatePageBody(
1970
+ firstResponse.body,
1971
+ responseSchema,
1972
+ `${client.getLink()}/${endpoint}`,
1973
+ validate
1974
+ );
1975
+ const totalPages = parseInt(firstResponse.headers["x-pages"] || "1", 10);
1976
+ const result = extractArray(firstBody);
1977
+ const remaining = Array.from({ length: totalPages - 1 }, (_, i) => i + 2);
1978
+ for (let i = 0; i < remaining.length; i += concurrency) {
1979
+ const batch = remaining.slice(i, i + concurrency);
1980
+ const pages = await Promise.all(
1981
+ batch.map(async (page) => {
1982
+ const sep = endpoint.includes("?") ? "&" : "?";
1983
+ const pagedEndpoint = `${endpoint}${sep}page=${page}`;
1984
+ logInfo2(
1985
+ client,
1986
+ `Fetching page ${page}/${totalPages}: ${pagedEndpoint}`,
1987
+ { page, totalPages }
1988
+ );
1989
+ const response = await handleSinglePageRequest(
1990
+ client,
1991
+ pagedEndpoint,
1992
+ method,
1993
+ body,
1994
+ requiresAuth,
1995
+ templatePath
1996
+ );
1997
+ return validatePageBody(
1998
+ response.body,
1999
+ responseSchema,
2000
+ `${client.getLink()}/${pagedEndpoint}`,
2001
+ validate
2002
+ );
2003
+ })
2004
+ );
2005
+ for (const pageBody of pages) {
2006
+ result.push(...extractArray(pageBody));
2007
+ }
2008
+ }
2009
+ return result;
2010
+ }
2011
+ async function* fetchPages(client, endpoint, method, requiresAuth = false, body, templatePath, responseSchema) {
2012
+ const validate = client.getValidateResponse();
2013
+ const firstResponse = await handleSinglePageRequest(
2014
+ client,
2015
+ endpoint,
2016
+ method,
2017
+ body,
2018
+ requiresAuth,
2019
+ templatePath
2020
+ );
2021
+ const firstBody = validatePageBody(
2022
+ firstResponse.body,
2023
+ responseSchema,
2024
+ `${client.getLink()}/${endpoint}`,
2025
+ validate
2026
+ );
2027
+ const totalPages = parseInt(firstResponse.headers["x-pages"] || "1", 10);
2028
+ yield { data: extractArray(firstBody), page: 1, totalPages };
2029
+ for (let page = 2; page <= totalPages; page++) {
2030
+ const sep = endpoint.includes("?") ? "&" : "?";
2031
+ const pagedEndpoint = `${endpoint}${sep}page=${page}`;
2032
+ logInfo2(client, `Fetching page ${page}/${totalPages}: ${pagedEndpoint}`, {
2033
+ page,
2034
+ totalPages
2035
+ });
2036
+ const response = await handleSinglePageRequest(
2037
+ client,
2038
+ pagedEndpoint,
2039
+ method,
2040
+ body,
2041
+ requiresAuth,
2042
+ templatePath
2043
+ );
2044
+ const pageBody = validatePageBody(
2045
+ response.body,
2046
+ responseSchema,
2047
+ `${client.getLink()}/${pagedEndpoint}`,
2048
+ validate
2049
+ );
2050
+ const pageData = extractArray(pageBody);
2051
+ if (pageData.length === 0) break;
2052
+ yield { data: pageData, page, totalPages };
2053
+ }
2054
+ }
2055
+
2056
+ // src/core/cache/ETagCacheManager.ts
2057
+ var ETagCacheManager = class {
2058
+ cache = /* @__PURE__ */ new Map();
2059
+ config;
2060
+ cleanupTimer;
2061
+ hits = 0;
2062
+ misses = 0;
2063
+ client = null;
2064
+ setClient(client) {
2065
+ this.client = client;
2066
+ }
2067
+ log(level, message, context) {
2068
+ const ctx = context && Object.keys(context).length > 0 ? context : void 0;
2069
+ const emit3 = level === "info" ? logInfo2 : logDebug2;
2070
+ emit3(this.client, message, ctx);
2071
+ }
2072
+ /**
2073
+ * @param client - the client whose logger this cache logs to, from the
2074
+ * first line (the startup log) on. `setClient` can attach or change it
2075
+ * later; without one the cache logs to the global logger.
2076
+ */
2077
+ constructor(config = {}, client) {
2078
+ this.client = client ?? null;
2079
+ this.config = {
2080
+ maxEntries: config.maxEntries ?? 1e3,
2081
+ defaultTtl: config.defaultTtl ?? 5 * 60 * 1e3,
2082
+ // 5 minutes default
2083
+ cleanupInterval: config.cleanupInterval ?? 60 * 1e3
2084
+ // 1 minute cleanup
2085
+ };
2086
+ this.startCleanupTimer();
2087
+ this.log(
2088
+ "info",
2089
+ `ETag cache manager initialized with ${this.config.maxEntries} max entries`,
2090
+ { maxEntries: this.config.maxEntries }
2091
+ );
2092
+ }
2093
+ /**
2094
+ * Get cached data if ETag matches
2095
+ */
2096
+ get(url) {
2097
+ const entry = this.cache.get(url);
2098
+ if (!entry) {
2099
+ this.misses++;
2100
+ return null;
2101
+ }
2102
+ if (this.isExpired(entry)) {
2103
+ this.cache.delete(url);
2104
+ this.misses++;
2105
+ this.log("debug", `Cache entry expired for ${url}`, { url });
2106
+ return null;
2107
+ }
2108
+ this.hits++;
2109
+ this.log("debug", `Cache hit for ${url} with ETag ${entry.etag}`, { url });
2110
+ return entry;
2111
+ }
2112
+ /**
2113
+ * Get ETag for a URL (for If-None-Match header)
2114
+ */
2115
+ getETag(url) {
2116
+ const entry = this.get(url);
2117
+ return entry ? entry.etag : null;
2118
+ }
2119
+ /**
2120
+ * Set cache entry
2121
+ */
2122
+ set(url, etag, data, headers, customTtl) {
2123
+ if (!this.cache.has(url) && this.cache.size >= this.config.maxEntries) {
2124
+ this.evictOldest();
2125
+ }
2126
+ const entry = {
2127
+ etag,
2128
+ data,
2129
+ headers,
2130
+ timestamp: Date.now(),
2131
+ ttl: customTtl ?? this.config.defaultTtl
2132
+ };
2133
+ this.cache.set(url, entry);
2134
+ this.log("debug", `Cached response for ${url} with ETag ${etag}`, { url });
2135
+ }
2136
+ /**
2137
+ * Check if cached data exists and is valid
2138
+ */
2139
+ has(url) {
2140
+ const entry = this.cache.get(url);
2141
+ return entry !== void 0 && !this.isExpired(entry);
2142
+ }
2143
+ /**
2144
+ * Clear specific cache entry
2145
+ */
2146
+ delete(url) {
2147
+ return this.cache.delete(url);
2148
+ }
2149
+ /**
2150
+ * Delete all cache entries whose URL contains the given path segment
2151
+ */
2152
+ deleteByPath(pathSegment) {
2153
+ let count = 0;
2154
+ for (const key of this.cache.keys()) {
2155
+ if (key.includes(pathSegment)) {
2156
+ this.cache.delete(key);
2157
+ count++;
2158
+ }
2159
+ }
2160
+ if (count > 0) {
2161
+ this.log(
2162
+ "debug",
2163
+ `Invalidated ${count} cache entries matching ${pathSegment}`,
2164
+ {
2165
+ count
2166
+ }
2167
+ );
2168
+ }
2169
+ return count;
2170
+ }
2171
+ /**
2172
+ * Clear all cache entries
2173
+ */
2174
+ clear() {
2175
+ this.cache.clear();
2176
+ this.hits = 0;
2177
+ this.misses = 0;
2178
+ this.log("info", "ETag cache cleared");
2179
+ }
2180
+ /**
2181
+ * Get cache statistics
2182
+ */
2183
+ getStats() {
2184
+ let oldest = Infinity;
2185
+ let newest = -Infinity;
2186
+ for (const entry of this.cache.values()) {
2187
+ if (entry.timestamp < oldest) oldest = entry.timestamp;
2188
+ if (entry.timestamp > newest) newest = entry.timestamp;
2189
+ }
2190
+ const total = this.hits + this.misses;
2191
+ return {
2192
+ totalEntries: this.cache.size,
2193
+ maxEntries: this.config.maxEntries,
2194
+ hits: this.hits,
2195
+ misses: this.misses,
2196
+ hitRate: total > 0 ? this.hits / total : 0,
2197
+ oldestEntry: this.cache.size > 0 ? oldest : null,
2198
+ newestEntry: this.cache.size > 0 ? newest : null
2199
+ };
2200
+ }
2201
+ /**
2202
+ * Update cache configuration
2203
+ */
2204
+ updateConfig(newConfig) {
2205
+ this.config = {
2206
+ maxEntries: newConfig.maxEntries ?? this.config.maxEntries,
2207
+ defaultTtl: newConfig.defaultTtl ?? this.config.defaultTtl,
2208
+ cleanupInterval: newConfig.cleanupInterval ?? this.config.cleanupInterval
2209
+ };
2210
+ this.log("info", "ETag cache configuration updated", {
2211
+ maxEntries: this.config.maxEntries
2212
+ });
2213
+ }
2214
+ /**
2215
+ * Cleanup expired entries
2216
+ */
2217
+ cleanup() {
2218
+ const beforeSize = this.cache.size;
2219
+ for (const [url, entry] of this.cache.entries()) {
2220
+ if (this.isExpired(entry)) {
2221
+ this.cache.delete(url);
2222
+ }
2223
+ }
2224
+ const cleanedCount = beforeSize - this.cache.size;
2225
+ if (cleanedCount > 0) {
2226
+ this.log("debug", `Cleaned up ${cleanedCount} expired cache entries`, {
2227
+ cleanedCount
2228
+ });
2229
+ }
2230
+ return cleanedCount;
2231
+ }
2232
+ /**
2233
+ * Shutdown cache manager
2234
+ */
2235
+ shutdown() {
2236
+ if (this.cleanupTimer) {
2237
+ clearInterval(this.cleanupTimer);
2238
+ }
2239
+ this.log("info", "ETag cache manager shut down");
2240
+ }
2241
+ isExpired(entry) {
2242
+ if (!entry.ttl) return false;
2243
+ return Date.now() - entry.timestamp > entry.ttl;
2244
+ }
2245
+ evictOldest() {
2246
+ let oldestKey = null;
2247
+ let oldestTime = Infinity;
2248
+ for (const [key, entry] of this.cache.entries()) {
2249
+ if (entry.timestamp < oldestTime) {
2250
+ oldestTime = entry.timestamp;
2251
+ oldestKey = key;
2252
+ }
2253
+ }
2254
+ if (oldestKey) {
2255
+ this.cache.delete(oldestKey);
2256
+ this.log("debug", `Evicted oldest cache entry: ${oldestKey}`, {
2257
+ url: oldestKey
2258
+ });
2259
+ }
2260
+ }
2261
+ startCleanupTimer() {
2262
+ this.cleanupTimer = setInterval(() => {
2263
+ this.cleanup();
2264
+ }, this.config.cleanupInterval);
2265
+ this.cleanupTimer.unref();
2266
+ }
2267
+ };
2268
+
2269
+ // src/core/endpoints/esi-rate-limit-groups.generated.ts
2270
+ var esiRateLimitGroups = {
2271
+ "GET:alliances/{alliance_id}/contacts": { group: "alliance-social", maxTokens: 300, windowSizeMs: 9e5 },
2272
+ "GET:alliances/{alliance_id}/contacts/labels": { group: "alliance-social", maxTokens: 300, windowSizeMs: 9e5 },
2273
+ "GET:characters/{character_id}/access-lists": { group: "char-access", maxTokens: 600, windowSizeMs: 9e5 },
2274
+ "GET:characters/{character_id}/access-lists/{access_list_id}": { group: "char-access", maxTokens: 600, windowSizeMs: 9e5 },
2275
+ "GET:characters/{character_id}/agents_research": { group: "char-industry", maxTokens: 600, windowSizeMs: 9e5 },
2276
+ "GET:characters/{character_id}/assets": { group: "char-asset", maxTokens: 1800, windowSizeMs: 9e5 },
2277
+ "POST:characters/{character_id}/assets/locations": { group: "char-asset", maxTokens: 1800, windowSizeMs: 9e5 },
2278
+ "POST:characters/{character_id}/assets/names": { group: "char-asset", maxTokens: 1800, windowSizeMs: 9e5 },
2279
+ "GET:characters/{character_id}/attributes": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2280
+ "GET:characters/{character_id}/blueprints": { group: "char-industry", maxTokens: 600, windowSizeMs: 9e5 },
2281
+ "GET:characters/{character_id}/calendar": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2282
+ "GET:characters/{character_id}/calendar/{event_id}": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2283
+ "PUT:characters/{character_id}/calendar/{event_id}": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2284
+ "GET:characters/{character_id}/calendar/{event_id}/attendees": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2285
+ "GET:characters/{character_id}/clones": { group: "char-location", maxTokens: 1200, windowSizeMs: 9e5 },
2286
+ "GET:characters/{character_id}/contacts": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2287
+ "POST:characters/{character_id}/contacts": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2288
+ "PUT:characters/{character_id}/contacts": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2289
+ "DELETE:characters/{character_id}/contacts": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2290
+ "GET:characters/{character_id}/contacts/labels": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2291
+ "GET:characters/{character_id}/contracts": { group: "char-contract", maxTokens: 600, windowSizeMs: 9e5 },
2292
+ "GET:characters/{character_id}/contracts/{contract_id}/bids": { group: "char-contract", maxTokens: 600, windowSizeMs: 9e5 },
2293
+ "GET:characters/{character_id}/contracts/{contract_id}/items": { group: "char-contract", maxTokens: 600, windowSizeMs: 9e5 },
2294
+ "GET:characters/{character_id}/cosmetics/skinr": { group: "char-skinr", maxTokens: 30, windowSizeMs: 9e5 },
2295
+ "GET:characters/{character_id}/cosmetics/skinr/components": { group: "char-skinr", maxTokens: 30, windowSizeMs: 9e5 },
2296
+ "POST:characters/{character_id}/cspa": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2297
+ "GET:characters/{character_id}/fatigue": { group: "char-location", maxTokens: 1200, windowSizeMs: 9e5 },
2298
+ "GET:characters/{character_id}/fittings": { group: "fitting", maxTokens: 150, windowSizeMs: 9e5 },
2299
+ "POST:characters/{character_id}/fittings": { group: "fitting", maxTokens: 150, windowSizeMs: 9e5 },
2300
+ "DELETE:characters/{character_id}/fittings/{fitting_id}": { group: "fitting", maxTokens: 150, windowSizeMs: 9e5 },
2301
+ "GET:characters/{character_id}/fleet": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2302
+ "GET:characters/{character_id}/freelance-jobs": { group: "char-freelance-job", maxTokens: 300, windowSizeMs: 9e5 },
2303
+ "GET:characters/{character_id}/freelance-jobs/{job_id}/participation": { group: "char-freelance-job", maxTokens: 300, windowSizeMs: 9e5 },
2304
+ "GET:characters/{character_id}/fw/stats": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2305
+ "GET:characters/{character_id}/implants": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2306
+ "GET:characters/{character_id}/industry/jobs": { group: "char-industry", maxTokens: 600, windowSizeMs: 9e5 },
2307
+ "GET:characters/{character_id}/killmails/recent": { group: "char-killmail", maxTokens: 30, windowSizeMs: 9e5 },
2308
+ "GET:characters/{character_id}/location": { group: "char-location", maxTokens: 1200, windowSizeMs: 9e5 },
2309
+ "GET:characters/{character_id}/loyalty/points": { group: "char-wallet", maxTokens: 150, windowSizeMs: 9e5 },
2310
+ "GET:characters/{character_id}/mail": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2311
+ "POST:characters/{character_id}/mail": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2312
+ "GET:characters/{character_id}/mail/{mail_id}": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2313
+ "PUT:characters/{character_id}/mail/{mail_id}": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2314
+ "DELETE:characters/{character_id}/mail/{mail_id}": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2315
+ "GET:characters/{character_id}/mail/labels": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2316
+ "POST:characters/{character_id}/mail/labels": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2317
+ "DELETE:characters/{character_id}/mail/labels/{label_id}": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2318
+ "GET:characters/{character_id}/mail/lists": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2319
+ "GET:characters/{character_id}/medals": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2320
+ "GET:characters/{character_id}/mercenary-tactical-operations": { group: "char-activity", maxTokens: 150, windowSizeMs: 9e5 },
2321
+ "GET:characters/{character_id}/mercenary-tactical-operations/{operation_id}": { group: "char-activity", maxTokens: 150, windowSizeMs: 9e5 },
2322
+ "GET:characters/{character_id}/military-campaigns/objectives": { group: "char-military-campaign", maxTokens: 150, windowSizeMs: 9e5 },
2323
+ "GET:characters/{character_id}/military-campaigns/objectives/{objective_id}": { group: "char-military-campaign", maxTokens: 150, windowSizeMs: 9e5 },
2324
+ "GET:characters/{character_id}/mining": { group: "char-industry", maxTokens: 600, windowSizeMs: 9e5 },
2325
+ "GET:characters/{character_id}/notifications": { group: "char-notification", maxTokens: 15, windowSizeMs: 9e5 },
2326
+ "GET:characters/{character_id}/notifications/contacts": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2327
+ "GET:characters/{character_id}/online": { group: "char-location", maxTokens: 1200, windowSizeMs: 9e5 },
2328
+ "GET:characters/{character_id}/paragon-hub/skinr": { group: "char-paragon-hub", maxTokens: 150, windowSizeMs: 9e5 },
2329
+ "GET:characters/{character_id}/planets": { group: "char-industry", maxTokens: 600, windowSizeMs: 9e5 },
2330
+ "GET:characters/{character_id}/planets/{planet_id}": { group: "char-industry", maxTokens: 600, windowSizeMs: 9e5 },
2331
+ "GET:characters/{character_id}/portrait": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2332
+ "GET:characters/{character_id}/roles": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2333
+ "GET:characters/{character_id}/ship": { group: "char-location", maxTokens: 1200, windowSizeMs: 9e5 },
2334
+ "GET:characters/{character_id}/skillqueue": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2335
+ "GET:characters/{character_id}/skills": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2336
+ "GET:characters/{character_id}/standings": { group: "char-social", maxTokens: 600, windowSizeMs: 9e5 },
2337
+ "GET:characters/{character_id}/structures/mercenary-dens": { group: "char-structure", maxTokens: 30, windowSizeMs: 9e5 },
2338
+ "GET:characters/{character_id}/structures/mercenary-dens/{mercenary_den_id}": { group: "char-structure", maxTokens: 30, windowSizeMs: 9e5 },
2339
+ "GET:characters/{character_id}/titles": { group: "char-detail", maxTokens: 600, windowSizeMs: 9e5 },
2340
+ "GET:characters/{character_id}/wallet": { group: "char-wallet", maxTokens: 150, windowSizeMs: 9e5 },
2341
+ "GET:characters/{character_id}/wallet/journal": { group: "char-wallet", maxTokens: 150, windowSizeMs: 9e5 },
2342
+ "GET:characters/{character_id}/wallet/transactions": { group: "char-wallet", maxTokens: 150, windowSizeMs: 9e5 },
2343
+ "GET:corporation/{corporation_id}/mining/extractions": { group: "corp-industry", maxTokens: 600, windowSizeMs: 9e5 },
2344
+ "GET:corporation/{corporation_id}/mining/observers": { group: "corp-industry", maxTokens: 600, windowSizeMs: 9e5 },
2345
+ "GET:corporation/{corporation_id}/mining/observers/{observer_id}": { group: "corp-industry", maxTokens: 600, windowSizeMs: 9e5 },
2346
+ "GET:corporations/{corporation_id}/assets": { group: "corp-asset", maxTokens: 1800, windowSizeMs: 9e5 },
2347
+ "POST:corporations/{corporation_id}/assets/locations": { group: "corp-asset", maxTokens: 1800, windowSizeMs: 9e5 },
2348
+ "POST:corporations/{corporation_id}/assets/names": { group: "corp-asset", maxTokens: 1800, windowSizeMs: 9e5 },
2349
+ "GET:corporations/{corporation_id}/blueprints": { group: "corp-industry", maxTokens: 600, windowSizeMs: 9e5 },
2350
+ "GET:corporations/{corporation_id}/contacts": { group: "corp-social", maxTokens: 300, windowSizeMs: 9e5 },
2351
+ "GET:corporations/{corporation_id}/contacts/labels": { group: "corp-social", maxTokens: 300, windowSizeMs: 9e5 },
2352
+ "GET:corporations/{corporation_id}/containers/logs": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2353
+ "GET:corporations/{corporation_id}/contracts": { group: "corp-contract", maxTokens: 600, windowSizeMs: 9e5 },
2354
+ "GET:corporations/{corporation_id}/contracts/{contract_id}/bids": { group: "corp-contract", maxTokens: 600, windowSizeMs: 9e5 },
2355
+ "GET:corporations/{corporation_id}/contracts/{contract_id}/items": { group: "corp-contract", maxTokens: 600, windowSizeMs: 9e5 },
2356
+ "GET:corporations/{corporation_id}/customs_offices": { group: "corp-industry", maxTokens: 600, windowSizeMs: 9e5 },
2357
+ "GET:corporations/{corporation_id}/divisions": { group: "corp-wallet", maxTokens: 300, windowSizeMs: 9e5 },
2358
+ "GET:corporations/{corporation_id}/facilities": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2359
+ "GET:corporations/{corporation_id}/freelance-jobs": { group: "corp-freelance-job", maxTokens: 300, windowSizeMs: 9e5 },
2360
+ "GET:corporations/{corporation_id}/freelance-jobs/{job_id}/participants": { group: "corp-freelance-job", maxTokens: 300, windowSizeMs: 9e5 },
2361
+ "GET:corporations/{corporation_id}/fw/stats": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2362
+ "GET:corporations/{corporation_id}/industry/jobs": { group: "corp-industry", maxTokens: 600, windowSizeMs: 9e5 },
2363
+ "GET:corporations/{corporation_id}/killmails/recent": { group: "corp-killmail", maxTokens: 30, windowSizeMs: 9e5 },
2364
+ "GET:corporations/{corporation_id}/medals": { group: "corp-detail", maxTokens: 300, windowSizeMs: 9e5 },
2365
+ "GET:corporations/{corporation_id}/medals/issued": { group: "corp-detail", maxTokens: 300, windowSizeMs: 9e5 },
2366
+ "GET:corporations/{corporation_id}/members": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2367
+ "GET:corporations/{corporation_id}/members/limit": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2368
+ "GET:corporations/{corporation_id}/members/titles": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2369
+ "GET:corporations/{corporation_id}/membertracking": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2370
+ "GET:corporations/{corporation_id}/projects": { group: "corp-project", maxTokens: 600, windowSizeMs: 9e5 },
2371
+ "GET:corporations/{corporation_id}/projects/{project_id}": { group: "corp-project", maxTokens: 600, windowSizeMs: 9e5 },
2372
+ "GET:corporations/{corporation_id}/projects/{project_id}/contribution/{character_id}": { group: "corp-project", maxTokens: 600, windowSizeMs: 9e5 },
2373
+ "GET:corporations/{corporation_id}/projects/{project_id}/contributors": { group: "corp-project", maxTokens: 600, windowSizeMs: 9e5 },
2374
+ "GET:corporations/{corporation_id}/roles": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2375
+ "GET:corporations/{corporation_id}/roles/history": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2376
+ "GET:corporations/{corporation_id}/shareholders": { group: "corp-detail", maxTokens: 300, windowSizeMs: 9e5 },
2377
+ "GET:corporations/{corporation_id}/standings": { group: "corp-member", maxTokens: 300, windowSizeMs: 9e5 },
2378
+ "GET:corporations/{corporation_id}/starbases": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2379
+ "GET:corporations/{corporation_id}/starbases/{starbase_id}": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2380
+ "GET:corporations/{corporation_id}/structures": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2381
+ "GET:corporations/{corporation_id}/structures/skyhooks": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2382
+ "GET:corporations/{corporation_id}/structures/skyhooks/{skyhook_id}": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2383
+ "GET:corporations/{corporation_id}/structures/sovereignty-hubs": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2384
+ "GET:corporations/{corporation_id}/structures/sovereignty-hubs/{sovereignty_hub_id}": { group: "corp-structure", maxTokens: 300, windowSizeMs: 9e5 },
2385
+ "GET:corporations/{corporation_id}/titles": { group: "corp-detail", maxTokens: 300, windowSizeMs: 9e5 },
2386
+ "GET:corporations/{corporation_id}/wallets": { group: "corp-wallet", maxTokens: 300, windowSizeMs: 9e5 },
2387
+ "GET:corporations/{corporation_id}/wallets/{division}/journal": { group: "corp-wallet", maxTokens: 300, windowSizeMs: 9e5 },
2388
+ "GET:corporations/{corporation_id}/wallets/{division}/transactions": { group: "corp-wallet", maxTokens: 300, windowSizeMs: 9e5 },
2389
+ "GET:cosmetics/skinr/{skinr_id}": { group: "skinr", maxTokens: 12e3, windowSizeMs: 9e5 },
2390
+ "GET:fleets/{fleet_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2391
+ "PUT:fleets/{fleet_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2392
+ "GET:fleets/{fleet_id}/members": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2393
+ "POST:fleets/{fleet_id}/members": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2394
+ "PUT:fleets/{fleet_id}/members/{member_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2395
+ "DELETE:fleets/{fleet_id}/members/{member_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2396
+ "PUT:fleets/{fleet_id}/squads/{squad_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2397
+ "DELETE:fleets/{fleet_id}/squads/{squad_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2398
+ "GET:fleets/{fleet_id}/wings": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2399
+ "POST:fleets/{fleet_id}/wings": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2400
+ "PUT:fleets/{fleet_id}/wings/{wing_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2401
+ "DELETE:fleets/{fleet_id}/wings/{wing_id}": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2402
+ "POST:fleets/{fleet_id}/wings/{wing_id}/squads": { group: "fleet", maxTokens: 1800, windowSizeMs: 9e5 },
2403
+ "GET:freelance-jobs": { group: "freelance-job", maxTokens: 12e3, windowSizeMs: 9e5 },
2404
+ "GET:freelance-jobs/{job_id}": { group: "freelance-job", maxTokens: 12e3, windowSizeMs: 9e5 },
2405
+ "GET:fw/leaderboards": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2406
+ "GET:fw/leaderboards/characters": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2407
+ "GET:fw/leaderboards/corporations": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2408
+ "GET:fw/stats": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2409
+ "GET:fw/systems": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2410
+ "GET:fw/wars": { group: "factional-warfare", maxTokens: 150, windowSizeMs: 9e5 },
2411
+ "GET:incursions": { group: "incursion", maxTokens: 150, windowSizeMs: 9e5 },
2412
+ "GET:industry/facilities": { group: "industry", maxTokens: 150, windowSizeMs: 9e5 },
2413
+ "GET:industry/systems": { group: "industry", maxTokens: 150, windowSizeMs: 9e5 },
2414
+ "GET:insurance/prices": { group: "insurance", maxTokens: 150, windowSizeMs: 9e5 },
2415
+ "GET:killmails/{killmail_id}/{killmail_hash}": { group: "killmail", maxTokens: 3600, windowSizeMs: 9e5 },
2416
+ "GET:markets/{region_id}/orders": { group: "market-order", maxTokens: 12e3, windowSizeMs: 9e5 },
2417
+ "GET:meta/changelog": { group: "meta", maxTokens: 150, windowSizeMs: 9e5 },
2418
+ "GET:meta/compatibility-dates": { group: "meta", maxTokens: 150, windowSizeMs: 9e5 },
2419
+ "GET:meta/name": { group: "meta", maxTokens: 150, windowSizeMs: 9e5 },
2420
+ "GET:meta/status": { group: "meta", maxTokens: 150, windowSizeMs: 9e5 },
2421
+ "GET:military-campaigns": { group: "military-campaign", maxTokens: 300, windowSizeMs: 9e5 },
2422
+ "GET:military-campaigns/{campaign_id}": { group: "military-campaign", maxTokens: 300, windowSizeMs: 9e5 },
2423
+ "GET:military-campaigns/{campaign_id}/objectives": { group: "military-campaign", maxTokens: 300, windowSizeMs: 9e5 },
2424
+ "GET:military-campaigns/{campaign_id}/objectives/{objective_id}": { group: "military-campaign", maxTokens: 300, windowSizeMs: 9e5 },
2425
+ "GET:paragon-hub/skinr": { group: "paragon-hub", maxTokens: 150, windowSizeMs: 9e5 },
2426
+ "GET:paragon-hub/skinr/alliances/{alliance_id}": { group: "char-paragon-hub", maxTokens: 150, windowSizeMs: 9e5 },
2427
+ "GET:paragon-hub/skinr/characters/{character_id}": { group: "char-paragon-hub", maxTokens: 150, windowSizeMs: 9e5 },
2428
+ "GET:paragon-hub/skinr/corporations/{corporation_id}": { group: "char-paragon-hub", maxTokens: 150, windowSizeMs: 9e5 },
2429
+ "POST:route/{origin_system_id}/{destination_system_id}": { group: "routes", maxTokens: 3600, windowSizeMs: 9e5 },
2430
+ "GET:skyhooks/raidable": { group: "activity", maxTokens: 30, windowSizeMs: 9e5 },
2431
+ "GET:sovereignty/campaigns": { group: "sovereignty", maxTokens: 600, windowSizeMs: 9e5 },
2432
+ "GET:sovereignty/systems": { group: "sovereignty", maxTokens: 600, windowSizeMs: 9e5 },
2433
+ "GET:status": { group: "status", maxTokens: 600, windowSizeMs: 9e5 },
2434
+ "POST:ui/autopilot/waypoint": { group: "ui", maxTokens: 900, windowSizeMs: 9e5 },
2435
+ "POST:ui/openwindow/contract": { group: "ui", maxTokens: 900, windowSizeMs: 9e5 },
2436
+ "POST:ui/openwindow/information": { group: "ui", maxTokens: 900, windowSizeMs: 9e5 },
2437
+ "POST:ui/openwindow/marketdetails": { group: "ui", maxTokens: 900, windowSizeMs: 9e5 },
2438
+ "POST:ui/openwindow/newmail": { group: "ui", maxTokens: 900, windowSizeMs: 9e5 },
2439
+ "GET:wars": { group: "killmail", maxTokens: 3600, windowSizeMs: 9e5 },
2440
+ "GET:wars/{war_id}": { group: "killmail", maxTokens: 3600, windowSizeMs: 9e5 },
2441
+ "GET:wars/{war_id}/killmails": { group: "killmail", maxTokens: 3600, windowSizeMs: 9e5 }
2442
+ };
2443
+
2444
+ // src/core/rateLimiter/RateLimiter.ts
2445
+ function getTokenCost(statusCode) {
2446
+ if (statusCode >= 200 && statusCode < 300) return 2;
2447
+ if (statusCode >= 300 && statusCode < 400) return 1;
2448
+ if (statusCode >= 400 && statusCode < 500) return 5;
2449
+ if (statusCode >= 500 && statusCode < 600) return 0;
2450
+ return 2;
2451
+ }
2452
+ function retryAfterDeadline(value, now) {
2453
+ const trimmed = value.trim();
2454
+ if (/^\d+$/.test(trimmed)) return now + parseInt(trimmed, 10) * 1e3;
2455
+ if (!/[A-Za-z]/.test(trimmed)) return void 0;
2456
+ const date = Date.parse(trimmed);
2457
+ return Number.isNaN(date) ? void 0 : date;
2458
+ }
2459
+ var DEFAULT_WINDOW_MS = 9e5;
2460
+ var DEFAULT_ERROR_LIMIT = {
2461
+ remain: 100,
2462
+ reset: 0,
2463
+ exhaustedUntil: 0
2464
+ };
2465
+ var RateLimiter = class _RateLimiter {
2466
+ minDelayMs;
2467
+ decelerationThreshold;
2468
+ userKeyExtractor;
2469
+ endpointOverrides;
2470
+ lastRequestTime = 0;
2471
+ minDelayChain = Promise.resolve();
2472
+ isTestMode = false;
2473
+ globalErrorLimit = { ...DEFAULT_ERROR_LIMIT };
2474
+ defaultBuckets = /* @__PURE__ */ new Map();
2475
+ userBuckets = null;
2476
+ fallbackBucket;
2477
+ lastCleanup = 0;
2478
+ static CLEANUP_INTERVAL_MS = 6e4;
2479
+ client = null;
2480
+ /**
2481
+ * Attach the client whose logger this limiter's warnings go to. Without
2482
+ * one they go to the global logger (`setLogger`), then the pino default.
2483
+ */
2484
+ setClient(client) {
2485
+ this.client = client;
2486
+ }
2487
+ constructor(config) {
2488
+ this.minDelayMs = config?.minDelayMs ?? 50;
2489
+ this.decelerationThreshold = config?.decelerationThreshold ?? 0.2;
2490
+ this.userKeyExtractor = config?.userKeyExtractor;
2491
+ this.endpointOverrides = config?.endpointOverrides ?? {};
2492
+ if (this.userKeyExtractor) {
2493
+ this.userBuckets = /* @__PURE__ */ new Map();
2494
+ }
2495
+ this.fallbackBucket = this.createFallbackBucket();
2496
+ }
2497
+ createFallbackBucket() {
2498
+ return {
2499
+ group: "__fallback__",
2500
+ remaining: -1,
2501
+ limit: 0,
2502
+ used: 0,
2503
+ windowSizeMs: DEFAULT_WINDOW_MS,
2504
+ blockedUntil: 0,
2505
+ lastUpdated: 0
2506
+ };
2507
+ }
2508
+ createBucketFromSpec(spec) {
2509
+ return {
2510
+ group: spec.group,
2511
+ remaining: spec.maxTokens,
2512
+ limit: spec.maxTokens,
2513
+ used: 0,
2514
+ windowSizeMs: spec.windowSizeMs,
2515
+ blockedUntil: 0,
2516
+ lastUpdated: 0
2517
+ };
2518
+ }
2519
+ createBucketForGroup(groupName) {
2520
+ return {
2521
+ group: groupName,
2522
+ remaining: -1,
2523
+ limit: 0,
2524
+ used: 0,
2525
+ windowSizeMs: DEFAULT_WINDOW_MS,
2526
+ blockedUntil: 0,
2527
+ lastUpdated: 0
2528
+ };
2529
+ }
2530
+ lookupGroupSpec(method, templatePath) {
2531
+ if (!templatePath || !method) return void 0;
2532
+ const normalized = templatePath.replace(/\/$/, "").replace(/\{(\w+)\}/g, (_, name) => `{${camelToSnake(name)}}`);
2533
+ const key = `${method}:${normalized}`;
2534
+ const override = this.endpointOverrides[key];
2535
+ if (override) {
2536
+ return {
2537
+ group: `endpoint:${key}`,
2538
+ maxTokens: override.maxTokens,
2539
+ windowSizeMs: override.windowSizeMs
2540
+ };
2541
+ }
2542
+ return esiRateLimitGroups[key];
2543
+ }
2544
+ resolveUserSet(requestHeaders) {
2545
+ if (!this.userBuckets || !this.userKeyExtractor || !requestHeaders) {
2546
+ return null;
2547
+ }
2548
+ const userKey = this.userKeyExtractor(requestHeaders);
2549
+ let userSet = this.userBuckets.get(userKey);
2550
+ if (!userSet) {
2551
+ userSet = {
2552
+ groups: /* @__PURE__ */ new Map(),
2553
+ errorLimit: { ...DEFAULT_ERROR_LIMIT },
2554
+ lastAccessed: Date.now()
2555
+ };
2556
+ this.userBuckets.set(userKey, userSet);
2557
+ }
2558
+ userSet.lastAccessed = Date.now();
2559
+ return userSet;
2560
+ }
2561
+ resolveBucketMap(requestHeaders) {
2562
+ const userSet = this.resolveUserSet(requestHeaders);
2563
+ return userSet ? userSet.groups : this.defaultBuckets;
2564
+ }
2565
+ resolveErrorLimit(requestHeaders) {
2566
+ const userSet = this.resolveUserSet(requestHeaders);
2567
+ return userSet ? userSet.errorLimit : this.globalErrorLimit;
2568
+ }
2569
+ getBucket(templatePath, method, requestHeaders, groupNameOverride) {
2570
+ const bucketMap = this.resolveBucketMap(requestHeaders);
2571
+ const spec = this.lookupGroupSpec(method, templatePath);
2572
+ const groupName = groupNameOverride ?? spec?.group;
2573
+ if (!groupName) return this.fallbackBucket;
2574
+ let bucket = bucketMap.get(groupName);
2575
+ if (!bucket) {
2576
+ bucket = spec ? this.createBucketFromSpec(spec) : this.createBucketForGroup(groupName);
2577
+ bucketMap.set(groupName, bucket);
2578
+ }
2579
+ return bucket;
2580
+ }
2581
+ cleanupStaleUsers() {
2582
+ if (!this.userBuckets) return;
2583
+ const now = Date.now();
2584
+ if (now - this.lastCleanup < _RateLimiter.CLEANUP_INTERVAL_MS) return;
2585
+ this.lastCleanup = now;
2586
+ for (const [userKey, userSet] of this.userBuckets) {
2587
+ if (now - userSet.lastAccessed > DEFAULT_WINDOW_MS) {
2588
+ this.userBuckets.delete(userKey);
2589
+ }
2590
+ }
2591
+ }
2592
+ updateFromResponse(headers, statusCode, templatePath, method, requestHeaders) {
2593
+ if (this.isTestMode) return;
2594
+ const now = Date.now();
2595
+ const responseGroup = headers["x-ratelimit-group"] || void 0;
2596
+ const bucket = this.getBucket(
2597
+ templatePath,
2598
+ method,
2599
+ requestHeaders,
2600
+ responseGroup
2601
+ );
2602
+ if ("x-ratelimit-remaining" in headers) {
2603
+ bucket.remaining = parseInt(headers["x-ratelimit-remaining"], 10);
2604
+ }
2605
+ if ("x-ratelimit-limit" in headers) {
2606
+ bucket.limit = parseInt(headers["x-ratelimit-limit"], 10);
2607
+ }
2608
+ if ("x-ratelimit-used" in headers) {
2609
+ bucket.used = parseInt(headers["x-ratelimit-used"], 10);
2610
+ }
2611
+ bucket.lastUpdated = now;
2612
+ if ("retry-after" in headers) {
2613
+ const until = retryAfterDeadline(headers["retry-after"], now);
2614
+ if (until !== void 0) bucket.blockedUntil = until;
2615
+ }
2616
+ if (statusCode === 420 || statusCode === 429) {
2617
+ if (bucket.blockedUntil <= now) {
2618
+ bucket.blockedUntil = now + 6e4;
2619
+ }
2620
+ }
2621
+ const errorLimit = this.resolveErrorLimit(requestHeaders);
2622
+ const reportedRemain = "x-esi-error-limit-remain" in headers ? parseInt(headers["x-esi-error-limit-remain"], 10) : void 0;
2623
+ if (reportedRemain !== void 0 && reportedRemain > 0 && errorLimit.remain <= 0 && now < errorLimit.exhaustedUntil) {
2624
+ return;
2625
+ }
2626
+ if (reportedRemain !== void 0) {
2627
+ errorLimit.remain = reportedRemain;
2628
+ }
2629
+ if ("x-esi-error-limit-reset" in headers) {
2630
+ errorLimit.reset = parseInt(headers["x-esi-error-limit-reset"], 10);
2631
+ }
2632
+ if (reportedRemain !== void 0 && reportedRemain <= 0) {
2633
+ errorLimit.exhaustedUntil = now + errorLimit.reset * 1e3;
2634
+ }
2635
+ }
2636
+ updateRateLimitInfo(headers) {
2637
+ this.updateFromResponse(headers, 200);
2638
+ }
2639
+ async checkRateLimit(templatePath, method, requestHeaders) {
2640
+ if (this.isTestMode) return;
2641
+ this.cleanupStaleUsers();
2642
+ let stillBlockedBucket;
2643
+ for (let i = 0; i < 10; i++) {
2644
+ const bucket2 = this.getBucket(templatePath, method, requestHeaders);
2645
+ const now = Date.now();
2646
+ if (bucket2.blockedUntil <= now) {
2647
+ stillBlockedBucket = void 0;
2648
+ break;
2649
+ }
2650
+ stillBlockedBucket = bucket2;
2651
+ const waitedUntil = bucket2.blockedUntil;
2652
+ const waitTime = waitedUntil - now;
2653
+ logWarn2(
2654
+ this.client,
2655
+ `[ESI Rate Limit] Group '${bucket2.group}' blocked for ${Math.ceil(waitTime / 1e3)}s (420/429 received)`,
2656
+ { group: bucket2.group, waitMs: waitTime }
2657
+ );
2658
+ await sleep(waitTime);
2659
+ if (bucket2.blockedUntil <= waitedUntil) {
2660
+ bucket2.blockedUntil = 0;
2661
+ }
2662
+ }
2663
+ if (stillBlockedBucket && stillBlockedBucket.blockedUntil > Date.now()) {
2664
+ const waitTime = stillBlockedBucket.blockedUntil - Date.now();
2665
+ logWarn2(
2666
+ this.client,
2667
+ `[ESI Rate Limit] Group '${stillBlockedBucket.group}' still blocked after retry budget exhausted; aborting request instead of sending while blocked`,
2668
+ { group: stillBlockedBucket.group }
2669
+ );
2670
+ throw new EsiError(
2671
+ 429,
2672
+ `Rate limit group '${stillBlockedBucket.group}' still blocked for ${Math.ceil(waitTime / 1e3)}s`
2673
+ );
2674
+ }
2675
+ const errorLimit = this.resolveErrorLimit(requestHeaders);
2676
+ if (errorLimit.remain <= 10 && errorLimit.remain > 0) {
2677
+ const resetMs = errorLimit.reset * 1e3;
2678
+ const delay = Math.min(resetMs, 5e3);
2679
+ if (delay > 0) {
2680
+ logWarn2(
2681
+ this.client,
2682
+ `[ESI Rate Limit] Legacy error limit low (${errorLimit.remain}), waiting ${delay}ms`,
2683
+ { errorLimitRemain: errorLimit.remain }
2684
+ );
2685
+ await sleep(delay);
2686
+ }
2687
+ } else if (errorLimit.remain <= 0 && errorLimit.reset > 0) {
2688
+ const waitTime = errorLimit.reset * 1e3;
2689
+ logWarn2(
2690
+ this.client,
2691
+ `[ESI Rate Limit] Legacy error limit exhausted, waiting ${Math.ceil(waitTime / 1e3)}s`,
2692
+ { errorLimitReset: errorLimit.reset }
2693
+ );
2694
+ await sleep(waitTime);
2695
+ }
2696
+ const bucket = this.getBucket(templatePath, method, requestHeaders);
2697
+ if (bucket.remaining >= 0 && bucket.limit > 0) {
2698
+ const ratio = bucket.remaining / bucket.limit;
2699
+ if (bucket.remaining <= 0) {
2700
+ logWarn2(
2701
+ this.client,
2702
+ `[ESI Rate Limit] Group '${bucket.group}' token bucket empty, waiting 1s`,
2703
+ { group: bucket.group }
2704
+ );
2705
+ await sleep(1e3);
2706
+ } else if (ratio <= this.decelerationThreshold) {
2707
+ const extraDelay = Math.ceil(
2708
+ (1 - ratio / this.decelerationThreshold) * 1e3
2709
+ );
2710
+ await sleep(extraDelay);
2711
+ }
2712
+ }
2713
+ await this.enforceMinDelay();
2714
+ }
2715
+ /**
2716
+ * Serializes min-delay waits so concurrent callers cannot all read the same
2717
+ * lastRequestTime and fire together after sleeping.
2718
+ */
2719
+ async enforceMinDelay() {
2720
+ if (this.isTestMode) {
2721
+ this.lastRequestTime = Date.now();
2722
+ return;
2723
+ }
2724
+ const run = async () => {
2725
+ const now = Date.now();
2726
+ const timeSinceLastRequest = now - this.lastRequestTime;
2727
+ if (timeSinceLastRequest < this.minDelayMs) {
2728
+ await sleep(this.minDelayMs - timeSinceLastRequest);
2729
+ }
2730
+ this.lastRequestTime = Date.now();
2731
+ };
2732
+ const scheduled = this.minDelayChain.then(run, run);
2733
+ this.minDelayChain = scheduled.then(
2734
+ () => void 0,
2735
+ () => void 0
2736
+ );
2737
+ await scheduled;
2738
+ }
2739
+ getStatus() {
2740
+ let worstBucket = this.fallbackBucket;
2741
+ let worstRatio = Infinity;
2742
+ const checkBucket = (bucket) => {
2743
+ if (bucket.limit > 0) {
2744
+ const ratio = bucket.remaining / bucket.limit;
2745
+ if (ratio < worstRatio) {
2746
+ worstRatio = ratio;
2747
+ worstBucket = bucket;
2748
+ }
2749
+ }
2750
+ };
2751
+ for (const bucket of this.defaultBuckets.values()) {
2752
+ checkBucket(bucket);
2753
+ }
2754
+ if (this.userBuckets) {
2755
+ for (const userSet of this.userBuckets.values()) {
2756
+ for (const bucket of userSet.groups.values()) {
2757
+ checkBucket(bucket);
2758
+ }
2759
+ }
2760
+ }
2761
+ return {
2762
+ remaining: worstBucket.remaining,
2763
+ limit: worstBucket.limit,
2764
+ used: worstBucket.used,
2765
+ group: worstBucket.group === "__fallback__" ? null : worstBucket.group,
2766
+ errorLimitRemain: this.globalErrorLimit.remain,
2767
+ errorLimitReset: this.globalErrorLimit.reset,
2768
+ retryAfter: worstBucket.blockedUntil > Date.now() ? Math.ceil((worstBucket.blockedUntil - Date.now()) / 1e3) : null,
2769
+ blockedUntil: worstBucket.blockedUntil
2770
+ };
2771
+ }
2772
+ getGroupStatus(group) {
2773
+ const bucket = this.defaultBuckets.get(group);
2774
+ if (!bucket) return void 0;
2775
+ return {
2776
+ group: bucket.group,
2777
+ remaining: bucket.remaining,
2778
+ limit: bucket.limit,
2779
+ used: bucket.used,
2780
+ windowSizeMs: bucket.windowSizeMs,
2781
+ blockedUntil: bucket.blockedUntil
2782
+ };
2783
+ }
2784
+ getAllGroupStatuses() {
2785
+ const result = /* @__PURE__ */ new Map();
2786
+ for (const [name, bucket] of this.defaultBuckets) {
2787
+ result.set(name, {
2788
+ group: bucket.group,
2789
+ remaining: bucket.remaining,
2790
+ limit: bucket.limit,
2791
+ used: bucket.used,
2792
+ windowSizeMs: bucket.windowSizeMs,
2793
+ blockedUntil: bucket.blockedUntil
2794
+ });
2795
+ }
2796
+ return result;
2797
+ }
2798
+ static getTokenCost(statusCode) {
2799
+ return getTokenCost(statusCode);
2800
+ }
2801
+ isBlocked(group) {
2802
+ const now = Date.now();
2803
+ if (group) {
2804
+ const bucket = this.defaultBuckets.get(group);
2805
+ return bucket ? bucket.blockedUntil > now : false;
2806
+ }
2807
+ if (this.fallbackBucket.blockedUntil > now) return true;
2808
+ for (const bucket of this.defaultBuckets.values()) {
2809
+ if (bucket.blockedUntil > now) return true;
2810
+ }
2811
+ return false;
2812
+ }
2813
+ setTestMode(enabled) {
2814
+ this.isTestMode = enabled;
2815
+ }
2816
+ reset() {
2817
+ this.defaultBuckets.clear();
2818
+ if (this.userBuckets) this.userBuckets.clear();
2819
+ this.fallbackBucket = this.createFallbackBucket();
2820
+ this.globalErrorLimit = { ...DEFAULT_ERROR_LIMIT };
2821
+ this.lastRequestTime = 0;
2822
+ this.minDelayChain = Promise.resolve();
2823
+ this.lastCleanup = 0;
2824
+ }
2825
+ };
2826
+
2827
+ // src/core/RequestDeduplicator.ts
2828
+ var RequestDeduplicator = class {
2829
+ inflight = /* @__PURE__ */ new Map();
2830
+ client = null;
2831
+ setClient(client) {
2832
+ this.client = client;
2833
+ }
2834
+ async dedupe(key, execute) {
2835
+ const existing = this.inflight.get(key);
2836
+ if (existing) {
2837
+ if (this.client) {
2838
+ logDebug2(this.client, `[Dedup] Coalescing request: ${key}`);
2839
+ }
2840
+ return existing;
2841
+ }
2842
+ const promise = execute().finally(() => {
2843
+ this.inflight.delete(key);
2844
+ });
2845
+ this.inflight.set(key, promise);
2846
+ return promise;
2847
+ }
2848
+ /**
2849
+ * Drop the in-flight entries whose key contains `pathSegment`, so a call
2850
+ * made after a write cannot join a request sent before it.
2851
+ *
2852
+ * The requests themselves keep running and whoever already joined them still
2853
+ * gets their answer. Only the map entry goes, which is what new callers look
2854
+ * in. Matching is `includes`, the same rule ETagCacheManager.deleteByPath
2855
+ * uses, so one written path invalidates the cache and detaches the reads of
2856
+ * it by the same test.
2857
+ */
2858
+ detachByPath(pathSegment) {
2859
+ let count = 0;
2860
+ for (const key of this.inflight.keys()) {
2861
+ if (key.includes(pathSegment)) {
2862
+ this.inflight.delete(key);
2863
+ count += 1;
2864
+ }
2865
+ }
2866
+ if (count > 0 && this.client) {
2867
+ logDebug2(
2868
+ this.client,
2869
+ `[Dedup] Detached ${count} in-flight request(s) matching ${pathSegment} after a write`
2870
+ );
2871
+ }
2872
+ return count;
2873
+ }
2874
+ get pending() {
2875
+ return this.inflight.size;
2876
+ }
2877
+ clear() {
2878
+ this.inflight.clear();
2879
+ }
2880
+ };
2881
+
2882
+ // src/core/configureApiClient.ts
2883
+ function configureApiClient(client, config) {
2884
+ let deduplicator = null;
2885
+ if (config?.logger) {
2886
+ client.setLogger(config.logger);
2887
+ } else if (config?.logLevel) {
2888
+ client.setLogger(createDefaultLogger(config.logLevel));
2889
+ }
2890
+ if (config?.tenant !== void 0) client.setTenant(config.tenant);
2891
+ if (config?.userAgent !== void 0) client.setUserAgent(config.userAgent);
2892
+ const rateLimiter = new RateLimiter(config?.rateLimiterConfig);
2893
+ rateLimiter.setClient(client);
2894
+ client.setRateLimiter(rateLimiter);
2895
+ if (config?.enableRequestDeduplication !== false) {
2896
+ deduplicator = new RequestDeduplicator();
2897
+ deduplicator.setClient(client);
2898
+ client.setDeduplicator(deduplicator);
2899
+ }
2900
+ if (config?.enableETagCache !== false) {
2901
+ const cache = new ETagCacheManager(config?.etagCacheConfig, client);
2902
+ client.setCache(cache);
2903
+ }
2904
+ if (config?.enableCircuitBreaker) {
2905
+ const cb = new CircuitBreaker(config.circuitBreakerConfig);
2906
+ cb.setClient(client);
2907
+ client.setCircuitBreaker(cb);
2908
+ }
2909
+ if (config?.retryConfig) {
2910
+ client.setRetryConfig(config.retryConfig);
2911
+ } else if (config?.retryAttempts !== void 0) {
2912
+ client.setRetryConfig({ maxRetries: config.retryAttempts });
2913
+ } else {
2914
+ client.setRetryConfig({
2915
+ maxRetries: 3,
2916
+ baseDelayMs: 1e3,
2917
+ maxDelayMs: 3e4
2918
+ });
2919
+ }
2920
+ if (config?.retryStrategy) {
2921
+ client.setRetryStrategy(config.retryStrategy);
2922
+ }
2923
+ if (config?.requestInterceptors) {
2924
+ for (const interceptor of config.requestInterceptors) {
2925
+ client.addRequestInterceptor(interceptor);
2926
+ }
2927
+ }
2928
+ if (config?.responseInterceptors) {
2929
+ for (const interceptor of config.responseInterceptors) {
2930
+ client.addResponseInterceptor(interceptor);
2931
+ }
2932
+ }
2933
+ if (config?.validateResponse !== void 0) {
2934
+ client.setValidateResponse(config.validateResponse);
2935
+ }
2936
+ if (config?.validateRequest !== void 0) {
2937
+ client.setValidateRequest(config.validateRequest);
2938
+ }
2939
+ if (config?.timeout !== void 0) {
2940
+ client.setTimeout(config.timeout);
2941
+ }
2942
+ return { deduplicator };
2943
+ }
2944
+
2945
+ export {
2946
+ validatePathParam,
2947
+ validateBaseUrl,
2948
+ validateQueryParam,
2949
+ validateHeaderOption,
2950
+ ApiClient,
2951
+ characterIdOfToken,
2952
+ redactingLogger,
2953
+ createDefaultLogger,
2954
+ toPinoLogger,
2955
+ setLogger,
2956
+ getLogger,
2957
+ logFatal,
2958
+ logError,
2959
+ logWarn,
2960
+ logInfo,
2961
+ logDebug,
2962
+ logTrace,
2963
+ logWarn2,
2964
+ logInfo2,
2965
+ logDebug2,
2966
+ USER_AGENT,
2967
+ evictRejectedResponse,
2968
+ CircuitBreaker,
2969
+ RetryStrategy,
2970
+ resolveCache,
2971
+ parseWarning,
2972
+ handleRequest,
2973
+ fetchAllPages,
2974
+ fetchPages,
2975
+ ETagCacheManager,
2976
+ RateLimiter,
2977
+ RequestDeduplicator,
2978
+ configureApiClient
2979
+ };
2980
+ //# sourceMappingURL=chunk-KIY3XA5Q.mjs.map