@lgriffin/esi.ts 9.6.0 → 10.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.
- package/CHANGELOG.md +787 -388
- package/LICENSE +26 -26
- package/README.md +974 -1030
- package/dist/EsiClient.d.ts +4 -0
- package/dist/EsiClient.d.ts.map +1 -1
- package/dist/EsiClientBuilder.d.ts.map +1 -1
- package/dist/auth/EsiTokenManager.d.ts +200 -0
- package/dist/auth/EsiTokenManager.d.ts.map +1 -0
- package/dist/auth/EveSsoClient.d.ts +88 -0
- package/dist/auth/EveSsoClient.d.ts.map +1 -0
- package/dist/auth/errors.d.ts +42 -0
- package/dist/auth/errors.d.ts.map +1 -0
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/jwt.d.ts +43 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/pkce.d.ts +21 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/storage/FileTokenStorage.d.ts +44 -0
- package/dist/auth/storage/FileTokenStorage.d.ts.map +1 -0
- package/dist/auth/storage/MemoryTokenStorage.d.ts +18 -0
- package/dist/auth/storage/MemoryTokenStorage.d.ts.map +1 -0
- package/dist/auth/types.d.ts +42 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/chunk-3ZI5A37L.mjs +1065 -0
- package/dist/chunk-3ZI5A37L.mjs.map +1 -0
- package/dist/chunk-7A72QFDF.mjs +416 -0
- package/dist/chunk-7A72QFDF.mjs.map +1 -0
- package/dist/chunk-BBSHKVPI.js +71 -0
- package/dist/chunk-BBSHKVPI.js.map +1 -0
- package/dist/chunk-HPSACXX3.js +416 -0
- package/dist/chunk-HPSACXX3.js.map +1 -0
- package/dist/chunk-JIW4BXLP.mjs +17 -0
- package/dist/chunk-JIW4BXLP.mjs.map +1 -0
- package/dist/chunk-MSJHIJXA.js +1065 -0
- package/dist/chunk-MSJHIJXA.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-R7D7M2LQ.mjs +71 -0
- package/dist/chunk-R7D7M2LQ.mjs.map +1 -0
- package/dist/chunk-SAQBXXTM.js +2614 -0
- package/dist/chunk-SAQBXXTM.js.map +1 -0
- package/dist/chunk-VZS32UQ3.mjs +2614 -0
- package/dist/chunk-VZS32UQ3.mjs.map +1 -0
- package/dist/clients/AllianceClient.d.ts +2 -0
- package/dist/clients/AllianceClient.d.ts.map +1 -1
- package/dist/clients/AssetsClient.d.ts +2 -0
- package/dist/clients/AssetsClient.d.ts.map +1 -1
- package/dist/clients/BaseEsiClient.d.ts +1 -0
- package/dist/clients/BaseEsiClient.d.ts.map +1 -1
- package/dist/clients/CalendarClient.d.ts +2 -0
- package/dist/clients/CalendarClient.d.ts.map +1 -1
- package/dist/clients/CharacterClient.d.ts +8 -0
- package/dist/clients/CharacterClient.d.ts.map +1 -1
- package/dist/clients/ClonesClient.d.ts +1 -0
- package/dist/clients/ClonesClient.d.ts.map +1 -1
- package/dist/clients/ContactsClient.d.ts +6 -0
- package/dist/clients/ContactsClient.d.ts.map +1 -1
- package/dist/clients/ContractsClient.d.ts +14 -8
- package/dist/clients/ContractsClient.d.ts.map +1 -1
- package/dist/clients/CorporationProjectsClient.d.ts +22 -12
- package/dist/clients/CorporationProjectsClient.d.ts.map +1 -1
- package/dist/clients/CorporationsClient.d.ts +17 -0
- package/dist/clients/CorporationsClient.d.ts.map +1 -1
- package/dist/clients/FactionClient.d.ts +4 -4
- package/dist/clients/FactionClient.d.ts.map +1 -1
- package/dist/clients/FittingsClient.d.ts +1 -0
- package/dist/clients/FittingsClient.d.ts.map +1 -1
- package/dist/clients/FleetClient.d.ts +2 -0
- package/dist/clients/FleetClient.d.ts.map +1 -1
- package/dist/clients/FreelanceJobsClient.d.ts +3 -3
- package/dist/clients/FreelanceJobsClient.d.ts.map +1 -1
- package/dist/clients/IndustryClient.d.ts +11 -3
- package/dist/clients/IndustryClient.d.ts.map +1 -1
- package/dist/clients/KillmailsClient.d.ts +2 -0
- package/dist/clients/KillmailsClient.d.ts.map +1 -1
- package/dist/clients/LoyaltyClient.d.ts +2 -0
- package/dist/clients/LoyaltyClient.d.ts.map +1 -1
- package/dist/clients/MailClient.d.ts +8 -3
- package/dist/clients/MailClient.d.ts.map +1 -1
- package/dist/clients/MarketClient.d.ts +6 -0
- package/dist/clients/MarketClient.d.ts.map +1 -1
- package/dist/clients/MetaClient.d.ts +3 -2
- package/dist/clients/MetaClient.d.ts.map +1 -1
- package/dist/clients/PiClient.d.ts +2 -0
- package/dist/clients/PiClient.d.ts.map +1 -1
- package/dist/clients/SkillsClient.d.ts +1 -0
- package/dist/clients/SkillsClient.d.ts.map +1 -1
- package/dist/clients/WalletClient.d.ts +7 -3
- package/dist/clients/WalletClient.d.ts.map +1 -1
- package/dist/clients/WarsClient.d.ts +2 -0
- package/dist/clients/WarsClient.d.ts.map +1 -1
- package/dist/core/ApiClient.d.ts +8 -0
- package/dist/core/ApiClient.d.ts.map +1 -1
- package/dist/core/ApiClientBuilder.d.ts +3 -1
- package/dist/core/ApiClientBuilder.d.ts.map +1 -1
- package/dist/core/ApiRequestHandler.d.ts.map +1 -1
- package/dist/core/BatchRequestHandler.d.ts.map +1 -1
- package/dist/core/RequestDeduplicator.d.ts +2 -0
- package/dist/core/RequestDeduplicator.d.ts.map +1 -1
- package/dist/core/RetryStrategy.d.ts +2 -0
- package/dist/core/RetryStrategy.d.ts.map +1 -1
- package/dist/core/cache/ETagCacheManager.d.ts +4 -0
- package/dist/core/cache/ETagCacheManager.d.ts.map +1 -1
- package/dist/core/circuitBreaker/CircuitBreaker.d.ts +3 -0
- package/dist/core/circuitBreaker/CircuitBreaker.d.ts.map +1 -1
- package/dist/core/configureApiClient.d.ts.map +1 -1
- package/dist/core/constants.d.ts +2 -2
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/endpoints/EndpointDefinition.d.ts +2 -2
- package/dist/core/endpoints/EndpointDefinition.d.ts.map +1 -1
- package/dist/core/endpoints/calendarEndpoints.d.ts +7 -7
- package/dist/core/endpoints/characterEndpoints.d.ts +2 -2
- package/dist/core/endpoints/cloneEndpoints.d.ts +2 -2
- package/dist/core/endpoints/contractEndpoints.d.ts +15 -22
- package/dist/core/endpoints/contractEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/corporationEndpoints.d.ts +19 -22
- package/dist/core/endpoints/corporationEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/corporationProjectEndpoints.d.ts +75 -23
- package/dist/core/endpoints/corporationProjectEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/createClient.d.ts +2 -2
- package/dist/core/endpoints/createClient.d.ts.map +1 -1
- package/dist/core/endpoints/dogmaEndpoints.d.ts +2 -2
- package/dist/core/endpoints/factionEndpoints.d.ts +36 -36
- package/dist/core/endpoints/factionEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/fleetEndpoints.d.ts +1 -1
- package/dist/core/endpoints/freelanceJobsEndpoints.d.ts +131 -122
- package/dist/core/endpoints/freelanceJobsEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/industryEndpoints.d.ts +6 -6
- package/dist/core/endpoints/industryEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/mailEndpoints.d.ts +3 -5
- package/dist/core/endpoints/mailEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/mercenaryEndpoints.d.ts +1 -1
- package/dist/core/endpoints/metaEndpoints.d.ts +5 -1
- package/dist/core/endpoints/metaEndpoints.d.ts.map +1 -1
- package/dist/core/endpoints/piEndpoints.d.ts +2 -2
- package/dist/core/endpoints/statusEndpoints.d.ts +1 -1
- package/dist/core/endpoints/universeEndpoints.d.ts +22 -22
- package/dist/core/endpoints/walletEndpoints.d.ts +2 -3
- package/dist/core/endpoints/walletEndpoints.d.ts.map +1 -1
- package/dist/core/logger/DefaultLogger.d.ts +31 -0
- package/dist/core/logger/DefaultLogger.d.ts.map +1 -0
- package/dist/core/logger/ILogger.d.ts +14 -4
- package/dist/core/logger/ILogger.d.ts.map +1 -1
- package/dist/core/logger/NoopLogger.d.ts +7 -0
- package/dist/core/logger/NoopLogger.d.ts.map +1 -0
- package/dist/core/logger/clientLog.d.ts +11 -0
- package/dist/core/logger/clientLog.d.ts.map +1 -0
- package/dist/core/logger/logger.d.ts +11 -3
- package/dist/core/logger/logger.d.ts.map +1 -1
- package/dist/core/logger/loggerUtil.d.ts +8 -6
- package/dist/core/logger/loggerUtil.d.ts.map +1 -1
- package/dist/core/logger/resolveLogger.d.ts +10 -0
- package/dist/core/logger/resolveLogger.d.ts.map +1 -0
- package/dist/core/pagination/AsyncPaginationIterator.d.ts +1 -0
- package/dist/core/pagination/AsyncPaginationIterator.d.ts.map +1 -1
- package/dist/core/pagination/CursorPaginationHandler.d.ts.map +1 -1
- package/dist/core/pagination/PaginationHandler.d.ts.map +1 -1
- package/dist/core/rateLimiter/RateLimiter.d.ts +6 -0
- package/dist/core/rateLimiter/RateLimiter.d.ts.map +1 -1
- package/dist/core/requestPipeline/cachePolicy.d.ts +14 -0
- package/dist/core/requestPipeline/cachePolicy.d.ts.map +1 -1
- package/dist/core/requestPipeline/fetchExecution.d.ts +1 -1
- package/dist/core/requestPipeline/fetchExecution.d.ts.map +1 -1
- package/dist/core/requestPipeline/headers.d.ts.map +1 -1
- package/dist/core/requestPipeline/index.d.ts +2 -2
- package/dist/core/requestPipeline/index.d.ts.map +1 -1
- package/dist/core/requestPipeline/paginationOrchestration.d.ts.map +1 -1
- package/dist/core/requestPipeline/statusHandling.d.ts +8 -2
- package/dist/core/requestPipeline/statusHandling.d.ts.map +1 -1
- package/dist/core/util/concurrency.d.ts +29 -0
- package/dist/core/util/concurrency.d.ts.map +1 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +53 -195
- package/dist/errors.js.map +1 -1
- package/dist/errors.mjs +37 -129
- package/dist/errors.mjs.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2597 -3480
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2250 -3024
- package/dist/index.mjs.map +1 -1
- package/dist/schemas/calendar.d.ts +9 -7
- package/dist/schemas/calendar.d.ts.map +1 -1
- package/dist/schemas/character.d.ts +2 -2
- package/dist/schemas/clones.d.ts +2 -2
- package/dist/schemas/common.d.ts +11 -1
- package/dist/schemas/common.d.ts.map +1 -1
- package/dist/schemas/contracts.d.ts +69 -6
- package/dist/schemas/contracts.d.ts.map +1 -1
- package/dist/schemas/corporation-projects.d.ts +96 -9
- package/dist/schemas/corporation-projects.d.ts.map +1 -1
- package/dist/schemas/corporation.d.ts +34 -22
- package/dist/schemas/corporation.d.ts.map +1 -1
- package/dist/schemas/dogma.d.ts +2 -2
- package/dist/schemas/faction-warfare.d.ts +125 -12
- package/dist/schemas/faction-warfare.d.ts.map +1 -1
- package/dist/schemas/fleet.d.ts +1 -1
- package/dist/schemas/freelance-jobs.d.ts +52 -22
- package/dist/schemas/freelance-jobs.d.ts.map +1 -1
- package/dist/schemas/index.js +420 -2474
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/index.mjs +225 -2060
- package/dist/schemas/index.mjs.map +1 -1
- package/dist/schemas/industry.d.ts +33 -0
- package/dist/schemas/industry.d.ts.map +1 -1
- package/dist/schemas/mail.d.ts +24 -5
- package/dist/schemas/mail.d.ts.map +1 -1
- package/dist/schemas/mercenary.d.ts +1 -1
- package/dist/schemas/meta.d.ts +12 -1
- package/dist/schemas/meta.d.ts.map +1 -1
- package/dist/schemas/pi.d.ts +2 -2
- package/dist/schemas/status.d.ts +1 -1
- package/dist/schemas/universe.d.ts +22 -22
- package/dist/schemas/universe.d.ts.map +1 -1
- package/dist/schemas/wallet.d.ts +16 -0
- package/dist/schemas/wallet.d.ts.map +1 -1
- package/dist/sde/SdeDataProvider.d.ts.map +1 -1
- package/dist/sde/index.js +95 -1162
- package/dist/sde/index.js.map +1 -1
- package/dist/sde/index.mjs +73 -1091
- package/dist/sde/index.mjs.map +1 -1
- package/dist/sde/ingestion/SdeExtractor.d.ts.map +1 -1
- package/dist/sde/ingestion/metadata.d.ts +13 -0
- package/dist/sde/ingestion/metadata.d.ts.map +1 -0
- package/dist/sde/memory.js +21 -1095
- package/dist/sde/memory.js.map +1 -1
- package/dist/sde/memory.mjs +13 -1051
- package/dist/sde/memory.mjs.map +1 -1
- package/dist/sde/optionalPeers.d.ts +25 -0
- package/dist/sde/optionalPeers.d.ts.map +1 -0
- package/dist/testing/TestDataFactory.d.ts +28 -1
- package/dist/testing/TestDataFactory.d.ts.map +1 -1
- package/dist/testing/index.js +116 -125
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/index.mjs +110 -82
- package/dist/testing/index.mjs.map +1 -1
- package/dist/types/contracts.d.ts +4 -1
- package/dist/types/contracts.d.ts.map +1 -1
- package/dist/types/corporation-projects.d.ts +5 -1
- package/dist/types/corporation-projects.d.ts.map +1 -1
- package/dist/types/faction-warfare.d.ts +9 -1
- package/dist/types/faction-warfare.d.ts.map +1 -1
- package/dist/types/freelance-jobs.d.ts +2 -1
- package/dist/types/freelance-jobs.d.ts.map +1 -1
- package/dist/types/generated/esi-spec.generated.d.ts +12 -12
- package/dist/types/generated/esi-spec.generated.d.ts.map +1 -1
- package/dist/types/generated/spec-alignment.check.d.ts +2 -2
- package/dist/types/industry.d.ts +2 -1
- package/dist/types/industry.d.ts.map +1 -1
- package/dist/types/mail.d.ts +2 -1
- package/dist/types/mail.d.ts.map +1 -1
- package/dist/types/meta.d.ts +2 -1
- package/dist/types/meta.d.ts.map +1 -1
- package/dist/types/wallet.d.ts +2 -1
- package/dist/types/wallet.d.ts.map +1 -1
- package/package.json +326 -289
package/README.md
CHANGED
|
@@ -1,1030 +1,974 @@
|
|
|
1
|
-
# ESI.ts
|
|
2
|
-
|
|
3
|
-
[](https://badge.fury.io/js/%40lgriffin%2Fesi.ts)
|
|
4
|
-
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
5
|
-
[](https://www.typescriptlang.org/)
|
|
6
|
-
[](https://github.com/lgriffin/ESI.ts/actions/workflows/ci.yml)
|
|
7
|
-
[](https://www.npmjs.com/package/@lgriffin/esi.ts)
|
|
9
|
-
[](https://scorecard.dev/viewer/?uri=github.com/lgriffin/ESI.ts)
|
|
10
|
-
|
|
11
|
-
A production-grade TypeScript client for the [EVE Online ESI API](https://esi.evetech.net/), built on the **OpenAPI 3.1 spec**, with runtime validation, intelligent caching, and full endpoint coverage.
|
|
12
|
-
|
|
13
|
-
**
|
|
14
|
-
|
|
15
|
-
**v9.5.
|
|
16
|
-
|
|
17
|
-
**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
| **
|
|
36
|
-
| **
|
|
37
|
-
| **
|
|
38
|
-
| **
|
|
39
|
-
| **
|
|
40
|
-
| **
|
|
41
|
-
| **
|
|
42
|
-
| **
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
The
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
sde
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
await client.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
await
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const client = new EsiClient();
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
npm run example:
|
|
784
|
-
npm run example:
|
|
785
|
-
npm run example:
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
npm run example:
|
|
792
|
-
npm run example:
|
|
793
|
-
npm run example:
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
```
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
```
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
|
883
|
-
|
|
|
884
|
-
|
|
|
885
|
-
|
|
|
886
|
-
|
|
|
887
|
-
|
|
|
888
|
-
|
|
|
889
|
-
|
|
|
890
|
-
|
|
|
891
|
-
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
npm run
|
|
898
|
-
npm run
|
|
899
|
-
npm run
|
|
900
|
-
npm run
|
|
901
|
-
npm run
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
npm run
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
npm run
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
npm run validate:esi
|
|
976
|
-
```
|
|
977
|
-
|
|
978
|
-
This fetches the ESI OpenAPI spec and reports:
|
|
979
|
-
|
|
980
|
-
- Endpoints in the codebase that are no longer in the ESI spec
|
|
981
|
-
- Endpoints in the ESI spec that the codebase doesn't cover
|
|
982
|
-
- HTTP method mismatches between codebase and spec
|
|
983
|
-
|
|
984
|
-
### Pre-commit Hooks
|
|
985
|
-
|
|
986
|
-
The project uses husky with lint-staged to run ESLint and Prettier on staged files before each commit. This is set up automatically when you run `npm install`.
|
|
987
|
-
|
|
988
|
-
### CI/CD
|
|
989
|
-
|
|
990
|
-
Every pull request runs the full validation suite:
|
|
991
|
-
|
|
992
|
-
- ESLint (with security and sonarjs plugins)
|
|
993
|
-
- Prettier formatting check
|
|
994
|
-
- TypeScript compilation
|
|
995
|
-
- Generated types staleness check (regenerates from live ESI OpenAPI spec and verifies no diff)
|
|
996
|
-
- Unit tests across Node.js 18, 20, and 22
|
|
997
|
-
- BDD scenario tests
|
|
998
|
-
- Coverage threshold enforcement (branches: 80%, functions: 75%, lines: 90%, statements: 90%)
|
|
999
|
-
- Auth/scopes cross-validation
|
|
1000
|
-
- Spec-alignment type assertions
|
|
1001
|
-
- Schema drift detection
|
|
1002
|
-
- Mutation testing (Stryker)
|
|
1003
|
-
- Dead code detection via knip
|
|
1004
|
-
- npm security audit
|
|
1005
|
-
|
|
1006
|
-
**Supply chain security:**
|
|
1007
|
-
|
|
1008
|
-
- All GitHub Actions pinned by SHA hash (not mutable tags) to prevent supply chain attacks
|
|
1009
|
-
- Least-privilege `permissions:` on all workflows and jobs
|
|
1010
|
-
- Script injection prevention (user-controlled inputs passed via `env:`, never interpolated in `run:`)
|
|
1011
|
-
- npm publish with `--provenance` for SLSA attestations (verifiable build origin)
|
|
1012
|
-
- OpenSSF Scorecard runs weekly via the `scorecard.yml` workflow
|
|
1013
|
-
|
|
1014
|
-
See [.github/workflows/README.md](.github/workflows/README.md) for full workflow details.
|
|
1015
|
-
|
|
1016
|
-
## Contributing
|
|
1017
|
-
|
|
1018
|
-
1. Fork the repository
|
|
1019
|
-
2. Create a feature branch
|
|
1020
|
-
3. Write tests for your changes
|
|
1021
|
-
4. Run `npm run validate` to check everything passes
|
|
1022
|
-
5. Open a Pull Request
|
|
1023
|
-
|
|
1024
|
-
## License
|
|
1025
|
-
|
|
1026
|
-
GPL-3.0-or-later - see the [LICENSE](LICENSE) file for details.
|
|
1027
|
-
|
|
1028
|
-
---
|
|
1029
|
-
|
|
1030
|
-
**o7**
|
|
1
|
+
# ESI.ts
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/%40lgriffin%2Fesi.ts)
|
|
4
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://github.com/lgriffin/ESI.ts/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/lgriffin/ESI.ts)
|
|
8
|
+
[](https://www.npmjs.com/package/@lgriffin/esi.ts)
|
|
9
|
+
[](https://scorecard.dev/viewer/?uri=github.com/lgriffin/ESI.ts)
|
|
10
|
+
|
|
11
|
+
A production-grade TypeScript client for the [EVE Online ESI API](https://esi.evetech.net/), built on the **OpenAPI 3.1 spec**, with runtime validation, intelligent caching, and full endpoint coverage.
|
|
12
|
+
|
|
13
|
+
**[Documentation Site](https://lgriffin.github.io/ESI.ts/)** — guides, API reference, interactive endpoint explorer, and runnable examples.
|
|
14
|
+
|
|
15
|
+
**v9.5.2** — Supply chain security hardening: all GitHub Actions pinned by SHA, npm publish with SLSA provenance attestations, least-privilege workflow permissions, script injection prevention, and ETag cache cross-tenant isolation.
|
|
16
|
+
|
|
17
|
+
**v9.5.0** — Adds 12 new ESI endpoints: CosmeticsClient (SKINR licenses, components, design lookup), ParagonHubClient (marketplace listings with cursor pagination), plus detail endpoints for Mercenary Dens, Tactical Operations, Skyhooks, and Sovereignty Hubs.
|
|
18
|
+
|
|
19
|
+
**235 endpoint definitions — 206 from the public ESI OpenAPI spec, plus 29 for newer EVE features (Equinox sovereignty, orbital skyhooks, mercenary dens, access lists, freelance jobs, military campaigns, corporation projects, SKINR cosmetics, Paragon Hub marketplace). All exercisable endpoints validated against live Tranquility.**
|
|
20
|
+
|
|
21
|
+
## Why ESI.ts vs. OpenAPI-Generated Clients?
|
|
22
|
+
|
|
23
|
+
Tools like `openapi-typescript` or `openapi-generator` can produce a typed client from the ESI OpenAPI spec in minutes. They're a reasonable starting point — but they stop at type generation. ESI.ts is a purpose-built SDK that handles the problems you hit _after_ the types compile.
|
|
24
|
+
|
|
25
|
+
### What generators give you
|
|
26
|
+
|
|
27
|
+
- TypeScript interfaces from the OpenAPI spec
|
|
28
|
+
- Basic request/response typing
|
|
29
|
+
- A thin HTTP wrapper
|
|
30
|
+
|
|
31
|
+
### What ESI.ts gives you on top of that
|
|
32
|
+
|
|
33
|
+
| Capability | openapi-typescript | ESI.ts |
|
|
34
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| **Runtime response validation** | None — types are erased at compile time. If CCP changes a field, you get silent data corruption. | Every GET response is validated at runtime via [Zod](https://zod.dev/) schemas — all 200 GET endpoints have schemas. Schema mismatches throw `EsiValidationError` immediately. |
|
|
36
|
+
| **Intelligent caching** | None — you build your own. | Three-tier: spec-aware TTL (zero HTTP calls within ESI's `x-cached-seconds` window), ETag conditional GETs, stale-on-error fallback on 5xx. Write operations auto-invalidate related GET caches. |
|
|
37
|
+
| **Rate limiting** | None — you build your own. | 36 per-group token buckets extracted from the ESI spec at build time. Market requests can't starve wallet requests. Optional per-user bucketing for multi-character apps. |
|
|
38
|
+
| **Pagination** | Manual — you write the page loop. | Automatic offset pagination, cursor-based pagination (Equinox-era endpoints), and streaming `AsyncGenerator` pagination for memory-efficient processing of large datasets. |
|
|
39
|
+
| **Retry & resilience** | None. | Exponential backoff with jitter, circuit breaker (closed/open/half-open), automatic 401 token refresh with concurrent coalescing. |
|
|
40
|
+
| **Wire format correctness** | Generates from spec, but ESI's spec has inconsistencies (query params documented as body, missing required fields). | Every endpoint tested against live ESI. Wire format bugs (query params vs. body, field naming) are caught and fixed — see the contacts and UI endpoint fixes in v6.1.0. |
|
|
41
|
+
| **Batch operations** | None. | `batch()` with bounded concurrency for GET fan-out, `batchPost()` with auto-chunking for large POST payloads. |
|
|
42
|
+
| **Domain knowledge** | None — generic HTTP client. | 39 domain clients with typed methods, JSDoc documentation, and input validation (e.g., fleet wing/squad names are capped at 10 characters before hitting the API). |
|
|
43
|
+
| **Streaming pagination** | None. | 21 domain clients with 73+ `stream*` methods via `AsyncGenerator` — process large datasets page-by-page without loading everything into memory. |
|
|
44
|
+
| **Testing** | Whatever you write. | 171 test suites, 4,957 tests across 9 tiers including property-based fuzzing (fast-check), mutation testing (Stryker), deep contract tests against live OpenAPI spec, and consumer type tests (tsd). 52 runnable example scripts. |
|
|
45
|
+
|
|
46
|
+
### The real problem with generated clients
|
|
47
|
+
|
|
48
|
+
The ESI OpenAPI spec is not a perfect source of truth. During live endpoint validation against the OpenAPI 3.1 spec, we discovered:
|
|
49
|
+
|
|
50
|
+
- `addContacts`, `editContacts`, and 4 UI endpoints document parameters as request body when ESI actually expects query parameters
|
|
51
|
+
- `deleteCharacterContacts` expects comma-separated contact IDs as a query param, not a JSON body
|
|
52
|
+
- Fleet wing/squad names have a 10-character limit not documented in the spec
|
|
53
|
+
- The `updateMailMetadata` endpoint uses the field name `read`, not `is_read`
|
|
54
|
+
|
|
55
|
+
A generated client faithfully reproduces these spec bugs. ESI.ts fixes them.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install @lgriffin/esi.ts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Building from Source
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git clone https://github.com/lgriffin/ESI.ts.git
|
|
67
|
+
cd ESI.ts
|
|
68
|
+
npm install # installs dependencies and compiles (via the prepare script)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If you've already installed and just need to recompile:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm run build
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Verify everything works:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run example:status # quick smoke test — checks ESI is reachable
|
|
81
|
+
npm test # run the full test suite (171 suites, 4,957 tests)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Sub-path Exports
|
|
85
|
+
|
|
86
|
+
ESI.ts provides sub-path exports for targeted imports, reducing bundle size when you only need specific parts of the library:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Zod schemas for runtime validation
|
|
90
|
+
import { MarketOrderSchema } from '@lgriffin/esi.ts/schemas';
|
|
91
|
+
|
|
92
|
+
// Error classes and type guards
|
|
93
|
+
import { EsiError, isRetryable } from '@lgriffin/esi.ts/errors';
|
|
94
|
+
|
|
95
|
+
// Test utilities
|
|
96
|
+
import { TestDataFactory } from '@lgriffin/esi.ts/testing';
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Static Data Export (SDE) Module
|
|
100
|
+
|
|
101
|
+
ESI.ts includes a standalone module for querying CCP's EVE Online Static Data Export — 102 YAML files loaded into in-memory Maps with 109 typed interfaces, Zod validation, and ~97 query methods. No database, no external services.
|
|
102
|
+
|
|
103
|
+
Reading SDE files needs two optional peer dependencies, which `npm install @lgriffin/esi.ts` does not install:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install js-yaml # SdeDataProvider.fromDirectory and fromZip (parses the YAML)
|
|
107
|
+
npm install adm-zip # SdeDataProvider.fromZip (reads the ZIP archive)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`@lgriffin/esi.ts/sde` loads without them, and `MemorySdeProvider` never needs them. A method that needs one that is missing throws an `SdeError` naming the package and the install command.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { SdeDataProvider } from '@lgriffin/esi.ts/sde';
|
|
114
|
+
|
|
115
|
+
const sde = SdeDataProvider.fromDirectory('./sde-data');
|
|
116
|
+
|
|
117
|
+
const tritanium = sde.getType(34);
|
|
118
|
+
console.log(tritanium?.name); // "Tritanium"
|
|
119
|
+
|
|
120
|
+
const jita = sde.getSolarSystem(30000142);
|
|
121
|
+
const minerals = sde.getTypesByGroup(18);
|
|
122
|
+
const caldari = sde.getFaction(500001);
|
|
123
|
+
|
|
124
|
+
sde.close();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Download SDE data with: `npx ts-node scripts/sde-ingest.ts --output sde-data`
|
|
128
|
+
|
|
129
|
+
| Document | Description |
|
|
130
|
+
| -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
131
|
+
| [SDE README](src/sde/README.md) | Module overview, quick start, full API reference (~97 methods), entity coverage table |
|
|
132
|
+
| [Architecture](src/sde/docs/ARCHITECTURE.md) | C4 diagrams (context, container, component), data flow sequence, ER diagram, design decisions |
|
|
133
|
+
| [Usage Guide](src/sde/docs/USAGE.md) | Provider patterns, query examples, error handling |
|
|
134
|
+
| [Developer Guide](src/sde/docs/DEVELOPER_GUIDE.md) | Project structure, new entity checklist, field normalization, testing patterns |
|
|
135
|
+
| [API Contracts](src/sde/docs/API_CONTRACTS.md) | Complete method reference for all IStaticDataProvider methods |
|
|
136
|
+
|
|
137
|
+
## Quick Start
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { EsiClient } from '@lgriffin/esi.ts';
|
|
141
|
+
|
|
142
|
+
const client = new EsiClient();
|
|
143
|
+
|
|
144
|
+
// Public data — no auth required
|
|
145
|
+
const alliances = await client.alliance.getAlliances();
|
|
146
|
+
const character = await client.characters.getCharacterPublicInfo(1689391488);
|
|
147
|
+
const system = await client.universe.getSystemById(30000142);
|
|
148
|
+
const prices = await client.market.getMarketPrices();
|
|
149
|
+
|
|
150
|
+
// Authenticated data — token read from ESI_ACCESS_TOKEN env var
|
|
151
|
+
const authedClient = new EsiClient();
|
|
152
|
+
const assets = await authedClient.assets.getCharacterAssets(characterId);
|
|
153
|
+
const wallet = await authedClient.wallet.getCharacterWallet(characterId);
|
|
154
|
+
|
|
155
|
+
// Clean up when done
|
|
156
|
+
await client.shutdown();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Guides
|
|
160
|
+
|
|
161
|
+
The README orients; the guides are canonical. Each one opens with the [engineering charter](guides/CHARTER.md) requirements it implements.
|
|
162
|
+
|
|
163
|
+
| Guide | Covers |
|
|
164
|
+
| -------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
165
|
+
| [Architecture](guides/ARCHITECTURE.md) | Layers, request path, caching, retry, rate limiting, circuit breaker, interceptors |
|
|
166
|
+
| [Design rules](guides/DESIGN-RULES.md) | Naming and schema conventions, adding an endpoint, adding a client, generated files |
|
|
167
|
+
| [Errors](guides/ERRORS.md) | Error classes, type guards, retryability, token refresh, safe mode |
|
|
168
|
+
| [Logging](guides/LOGGING.md) | `ILogger`, per-client loggers, pino, `ESI_LOG_LEVEL`, silencing in tests |
|
|
169
|
+
| [Pagination](guides/PAGINATION.md) | Offset and cursor pagination, `stream*`, `fetchAll*`, batch helpers |
|
|
170
|
+
| [Runtime validation](guides/RUNTIME-VALIDATION.md) | Zod response and request validation |
|
|
171
|
+
| [Security](guides/SECURITY.md) | Runtime defences and supply-chain controls ([policy](SECURITY.md)) |
|
|
172
|
+
| [Testing](guides/TESTING.md) | Test tiers, coverage, EARS specification |
|
|
173
|
+
| [Mutation testing](guides/MUTATION-TESTING.md) | Stryker configuration and scores |
|
|
174
|
+
| [Quality gates](guides/QUALITY-GATES.md) | What runs at commit, push, PR, nightly and release; every workflow and script |
|
|
175
|
+
| [Release](guides/RELEASE.md) | Cutting a release, changelog, provenance, signatures, supported versions |
|
|
176
|
+
| [Semantic versioning](guides/SEMVER.md) | What is public, major/minor/patch decisions, breaking-change commits, merge buttons |
|
|
177
|
+
| [OKF bundle](guides/OKF.md) | The generated Open Knowledge Format catalogue of ESI |
|
|
178
|
+
| [Documentation](guides/DOCUMENTATION.md) | Documentation surfaces and the TypeDoc reference |
|
|
179
|
+
| [Beads](guides/BEADS.md) | Issue tracking workflow |
|
|
180
|
+
|
|
181
|
+
## Configuration
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const client = new EsiClient({
|
|
185
|
+
clientId: 'my-app', // User-Agent identifier (default: 'esi-client')
|
|
186
|
+
accessToken: 'your-token', // EVE SSO token for authenticated endpoints
|
|
187
|
+
baseUrl: 'https://esi.evetech.net', // ESI base URL (default)
|
|
188
|
+
onTokenRefresh: async () => newToken, // Auto-refresh on 401 (optional)
|
|
189
|
+
language: 'en', // Accept-Language header: en, de, fr, ja, ru, zh, ko, es (default: none)
|
|
190
|
+
timeout: 30000, // Request timeout in ms (default: 30000)
|
|
191
|
+
retryConfig: {
|
|
192
|
+
maxRetries: 3, // Max retry attempts for transient errors (default: 3)
|
|
193
|
+
baseDelayMs: 1000, // Initial backoff delay (default: 1000)
|
|
194
|
+
maxDelayMs: 30000, // Maximum backoff delay (default: 30000)
|
|
195
|
+
retryMutations: false, // Retry POST/PUT/DELETE (default: false, GET only)
|
|
196
|
+
},
|
|
197
|
+
enableETagCache: true, // ETag caching (default: true)
|
|
198
|
+
etagCacheConfig: {
|
|
199
|
+
maxEntries: 1000, // Max cached responses (default: 1000)
|
|
200
|
+
defaultTtl: 300000, // Fallback TTL in ms (default: 5 min)
|
|
201
|
+
cleanupInterval: 60000, // Expired entry cleanup interval (default: 1 min)
|
|
202
|
+
},
|
|
203
|
+
validateResponse: true, // Runtime Zod validation of ESI responses (default: true)
|
|
204
|
+
validateRequest: false, // Opt-in request body Zod validation for POST/PUT/DELETE (default: false)
|
|
205
|
+
retryStrategy: customRetryStrategy, // Injectable IRetryStrategy (default: built-in exponential backoff)
|
|
206
|
+
enableCircuitBreaker: false, // Opt-in circuit breaker (default: false); circuitBreakerConfig is ignored unless true
|
|
207
|
+
circuitBreakerConfig: {
|
|
208
|
+
keyStrategy: 'resolved', // CB keying: 'resolved' (per-URL) or 'template' (per-route) (default: 'resolved')
|
|
209
|
+
cleanupIntervalMs: 3600000, // Stale circuit cleanup interval (default: disabled)
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Retry is enabled by default (`maxRetries: 3`). Transient errors (502, 503, 504, timeout, rate limit) are retried with exponential backoff and jitter. The circuit breaker is respected — requests are not retried when the circuit is open. Set `maxRetries: 0` to disable retry.
|
|
215
|
+
|
|
216
|
+
The access token can be updated at runtime:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
client.setAccessToken('new-token');
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Authentication
|
|
223
|
+
|
|
224
|
+
Many ESI endpoints require an EVE SSO access token. There are three ways to provide one:
|
|
225
|
+
|
|
226
|
+
### 1. Environment variable (recommended)
|
|
227
|
+
|
|
228
|
+
Set `ESI_ACCESS_TOKEN` in your environment or a `.env` file. The client reads it automatically — no token in source code.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Copy the example and fill in your token
|
|
232
|
+
cp .env.example .env
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```env
|
|
236
|
+
ESI_ACCESS_TOKEN=your-eve-sso-access-token
|
|
237
|
+
ESI_CLIENT_ID=my-app-name
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
If you use a `.env` loader like [dotenv](https://www.npmjs.com/package/dotenv), load it before creating the client:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import 'dotenv/config';
|
|
244
|
+
import { EsiClient } from '@lgriffin/esi.ts';
|
|
245
|
+
|
|
246
|
+
const client = new EsiClient();
|
|
247
|
+
// Token is picked up from process.env.ESI_ACCESS_TOKEN
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 2. Constructor parameter
|
|
251
|
+
|
|
252
|
+
Pass the token directly (useful for apps that manage tokens themselves):
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const client = new EsiClient({ accessToken: token });
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 3. Runtime update
|
|
259
|
+
|
|
260
|
+
Set or refresh the token after construction:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
client.setAccessToken(newToken);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Getting an EVE SSO token
|
|
267
|
+
|
|
268
|
+
1. Register an application at [EVE Developers](https://developers.eveonline.com/)
|
|
269
|
+
2. Set a callback URL and select the ESI scopes your app needs
|
|
270
|
+
3. Implement the [OAuth2 flow](https://docs.esi.evetech.net/docs/sso/) to obtain an access token
|
|
271
|
+
4. Access tokens expire — use the refresh token to get new ones
|
|
272
|
+
|
|
273
|
+
### Automatic Token Refresh
|
|
274
|
+
|
|
275
|
+
EVE SSO access tokens expire after 20 minutes. Instead of manually tracking expiry, you can provide a refresh callback — the client will automatically call it on 401, update the token, and retry the request:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
const client = new EsiClient({
|
|
279
|
+
accessToken: initialToken,
|
|
280
|
+
onTokenRefresh: async () => {
|
|
281
|
+
const response = await fetch('https://login.eveonline.com/v2/oauth/token', {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
284
|
+
body: new URLSearchParams({
|
|
285
|
+
grant_type: 'refresh_token',
|
|
286
|
+
refresh_token: myRefreshToken,
|
|
287
|
+
client_id: myClientId,
|
|
288
|
+
}),
|
|
289
|
+
});
|
|
290
|
+
const { access_token } = await response.json();
|
|
291
|
+
return access_token;
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Requests now auto-refresh on 401 — no manual token management needed
|
|
296
|
+
const location = await client.location.getCharacterLocation(characterId);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The token provider can also be set or changed at runtime:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
client.setTokenProvider(myRefreshFunction);
|
|
303
|
+
client.setTokenProvider(undefined); // disable auto-refresh
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Key behaviors:
|
|
307
|
+
|
|
308
|
+
- Only retries **once** per request — if the refreshed token also gets a 401, the error is thrown
|
|
309
|
+
- **Concurrent coalescing** — if multiple requests hit 401 simultaneously, only one refresh call is made
|
|
310
|
+
- If the refresh callback throws (e.g., refresh token revoked), a `TOKEN_REFRESH_FAILED` error is raised
|
|
311
|
+
- Without a token provider, 401 errors throw immediately as before
|
|
312
|
+
|
|
313
|
+
### Token Manager (multi-character, persistent)
|
|
314
|
+
|
|
315
|
+
The refresh callback above is the low-level hook. For applications that hold tokens for one or many characters, `EsiTokenManager` does the whole lifecycle: the SSO code exchange, persistence through a pluggable storage adapter, proactive refresh ahead of expiry, coalescing of concurrent refreshes, persistence of the rotated refresh token, revocation tracking, and bulk refresh with a concurrency cap.
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import {
|
|
319
|
+
EsiTokenManager,
|
|
320
|
+
FileTokenStorage,
|
|
321
|
+
generateState,
|
|
322
|
+
} from '@lgriffin/esi.ts';
|
|
323
|
+
|
|
324
|
+
const tokens = new EsiTokenManager({
|
|
325
|
+
clientId: process.env.ESI_SSO_CLIENT_ID!,
|
|
326
|
+
clientSecret: process.env.ESI_SSO_CLIENT_SECRET, // omit for a public (PKCE) client
|
|
327
|
+
callbackUrl: 'https://my-app.example/callback',
|
|
328
|
+
storage: new FileTokenStorage('./tokens.json'), // or MemoryTokenStorage, or your own
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// 1. Send the player to SSO
|
|
332
|
+
const state = generateState();
|
|
333
|
+
const loginUrl = tokens.getAuthorizationUrl({
|
|
334
|
+
scopes: ['esi-wallet.read_character_wallet.v1'],
|
|
335
|
+
state,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// 2. On the callback, exchange the code. The character id, name and scopes
|
|
339
|
+
// are decoded from the token; you never have to say who just logged in.
|
|
340
|
+
const stored = await tokens.addCharacter(codeFromCallback);
|
|
341
|
+
console.log(`Added ${stored.characterName} (${stored.characterId})`);
|
|
342
|
+
|
|
343
|
+
// 3. Get a client bound to that character. Its token is refreshed before
|
|
344
|
+
// expiry, and again on a 401, through the manager.
|
|
345
|
+
const client = await tokens.createClient(stored.characterId);
|
|
346
|
+
const wallet = await client.wallet.getCharacterWallet(stored.characterId);
|
|
347
|
+
|
|
348
|
+
// Or just the access token, for use elsewhere
|
|
349
|
+
const accessToken = await tokens.getToken(stored.characterId);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Public clients (desktop and CLI tools that cannot keep a secret) use PKCE:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
import { generatePkcePair } from '@lgriffin/esi.ts';
|
|
356
|
+
|
|
357
|
+
const pkce = generatePkcePair();
|
|
358
|
+
const loginUrl = tokens.getAuthorizationUrl({
|
|
359
|
+
scopes,
|
|
360
|
+
state,
|
|
361
|
+
codeChallenge: pkce.codeChallenge,
|
|
362
|
+
});
|
|
363
|
+
// ...later, on the callback:
|
|
364
|
+
await tokens.addCharacter(code, { codeVerifier: pkce.codeVerifier });
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### Bulk refresh
|
|
368
|
+
|
|
369
|
+
Applications holding many characters (corporation tools, alliance services) refresh in bulk. Per-character failures never reject the call; each character gets its own result. The one exception is a storage adapter that cannot list tokens, which rejects with the storage error.
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const results = await tokens.refreshAll({
|
|
373
|
+
concurrency: 5, // simultaneous SSO requests (default 5)
|
|
374
|
+
expiringWithinMs: 5 * 60_000, // only tokens expiring in the next 5 minutes; omit for all
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
for (const r of results) {
|
|
378
|
+
switch (r.status) {
|
|
379
|
+
case 'refreshed':
|
|
380
|
+
break;
|
|
381
|
+
case 'skipped':
|
|
382
|
+
break; // not stale, or the run was aborted
|
|
383
|
+
case 'revoked':
|
|
384
|
+
console.log(`${r.characterId} must log in again`);
|
|
385
|
+
break;
|
|
386
|
+
case 'failed':
|
|
387
|
+
if (r.retryable) scheduleRetry(r.characterId);
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### Storage adapters
|
|
394
|
+
|
|
395
|
+
`ITokenStorage` is four async methods keyed by character id: `get`, `set`, `delete`, `list`. Two adapters ship with the library:
|
|
396
|
+
|
|
397
|
+
| Adapter | Use for |
|
|
398
|
+
| -------------------- | ---------------------------------------------------------------------- |
|
|
399
|
+
| `MemoryTokenStorage` | Tests, CLIs that log in every run, a cache in front of a durable store |
|
|
400
|
+
| `FileTokenStorage` | Single-process apps; atomic temp-file-and-rename writes, `0600` mode |
|
|
401
|
+
|
|
402
|
+
Implement the interface over Redis, Postgres, or a keychain for anything else. One rule matters: `set` must be durable before it resolves, because the manager persists the rotated refresh token before returning the new access token, and SSO invalidates the previous one.
|
|
403
|
+
|
|
404
|
+
Key behaviors:
|
|
405
|
+
|
|
406
|
+
- **One token per character** — re-authorizing replaces the stored token rather than accumulating a second one; a warning is logged if the new consent drops scopes
|
|
407
|
+
- **Proactive refresh** — `getToken` refreshes when the token is inside `refreshSkewMs` of expiry (default 60 s), so requests are never sent with a token about to fail
|
|
408
|
+
- **Coalescing** — concurrent refreshes for the same character share one SSO call, which matters because SSO rotates the refresh token on every use
|
|
409
|
+
- **Revocation tracking** — an `invalid_grant` from SSO marks the character revoked; later calls throw `TokenRevokedError` locally instead of hitting SSO again
|
|
410
|
+
- **Hooks** — `onRefresh`, `onRefreshError`, and `onRevoked` for logging, metrics, or prompting a re-login
|
|
411
|
+
- **No JWT signature verification** — tokens are trusted because they arrive directly from SSO over TLS; do not use `decodeAccessToken` to authenticate tokens presented by third parties
|
|
412
|
+
- **Single process per store** — two processes sharing one `FileTokenStorage` would each rotate refresh tokens the other cannot see
|
|
413
|
+
|
|
414
|
+
### Environment variables reference
|
|
415
|
+
|
|
416
|
+
| Variable | Description | Default |
|
|
417
|
+
| ------------------ | -------------------------------------------- | ------------------------- |
|
|
418
|
+
| `ESI_ACCESS_TOKEN` | EVE SSO access token | none |
|
|
419
|
+
| `ESI_CLIENT_ID` | User-Agent identifier | `esi-client` |
|
|
420
|
+
| `ESI_BASE_URL` | ESI API base URL | `https://esi.evetech.net` |
|
|
421
|
+
| `ESI_LOG_LEVEL` | Log level (`error`, `warn`, `info`, `debug`) | `warn` |
|
|
422
|
+
|
|
423
|
+
## Available APIs
|
|
424
|
+
|
|
425
|
+
All clients are accessed as properties on the `EsiClient` instance. Authenticated endpoints require an access token.
|
|
426
|
+
|
|
427
|
+
| Client | Property | Auth | Examples |
|
|
428
|
+
| ------------------ | ---------------------------- | ---- | ------------------------------------------------------------------------------------- |
|
|
429
|
+
| Alliance | `client.alliance` | Some | `getAlliances()`, `getAllianceById(id)` |
|
|
430
|
+
| Assets | `client.assets` | Yes | `getCharacterAssets(id)` |
|
|
431
|
+
| Calendar | `client.calendar` | Yes | `getCalendarEvents(id)` |
|
|
432
|
+
| Characters | `client.characters` | Some | `getCharacterPublicInfo(id)`, `getCharacterPortrait(id)` |
|
|
433
|
+
| Clones | `client.clones` | Yes | `getCharacterClones(id)` |
|
|
434
|
+
| Contacts | `client.contacts` | Yes | `getCharacterContacts(id)`, `postCharacterContacts(id, standing, contactIds)` |
|
|
435
|
+
| Contracts | `client.contracts` | Yes | `getCharacterContracts(id)` |
|
|
436
|
+
| Corp Projects | `client.corporationProjects` | Yes | `getCorporationProjects(corpId)`, `getCorporationProject(corpId, projectId)` |
|
|
437
|
+
| Corporations | `client.corporations` | Some | `getCorporationInfo(id)`, `getCorporationMembers(id)` |
|
|
438
|
+
| Dogma | `client.dogma` | No | `getDogmaAttributes()`, `getDynamicItemInfo(typeId, itemId)` |
|
|
439
|
+
| Factions | `client.factions` | Some | `getFactionWarStats()` |
|
|
440
|
+
| Fittings | `client.fittings` | Yes | `getFittings(id)`, `createFitting(id, body)` |
|
|
441
|
+
| Fleets | `client.fleets` | Yes | `getFleetInformation(id)`, `getFleetMembers(id)` |
|
|
442
|
+
| Incursions | `client.incursions` | No | `getIncursions()` |
|
|
443
|
+
| Industry | `client.industry` | Some | `getCharacterIndustryJobs(id)` |
|
|
444
|
+
| Insurance | `client.insurance` | No | `getInsurancePrices()` |
|
|
445
|
+
| Killmails | `client.killmails` | Some | `getKillmail(id, hash)` |
|
|
446
|
+
| Location | `client.location` | Yes | `getCharacterLocation(id)` |
|
|
447
|
+
| Loyalty | `client.loyalty` | Yes | `getCharacterLoyaltyPoints(id)` |
|
|
448
|
+
| Mail | `client.mail` | Yes | `getCharacterMail(id)`, `sendMail(id, body)` |
|
|
449
|
+
| Market | `client.market` | Some | `getMarketPrices()`, `getMarketOrders(regionId)` |
|
|
450
|
+
| Military Campaigns | `client.militaryCampaigns` | Some | `getMilitaryCampaigns()`, `getMilitaryCampaignById(id)` |
|
|
451
|
+
| PI | `client.pi` | Yes | `getCharacterPlanets(id)` |
|
|
452
|
+
| Route | `client.route` | No | `getRoute(origin, destination)` |
|
|
453
|
+
| Search | `client.search` | Some | `search(characterId, query)` |
|
|
454
|
+
| Skills | `client.skills` | Yes | `getCharacterSkills(id)` |
|
|
455
|
+
| Sovereignty | `client.sovereignty` | No | `getSovereigntySystems()`, `getSovereigntyMap()` |
|
|
456
|
+
| Skyhooks | `client.skyhooks` | Some | `getSovereigntyHubs(corpId)`, `getSkyhookDetail(corpId, id)`, `getRaidableSkyhooks()` |
|
|
457
|
+
| Mercenary | `client.mercenary` | Yes | `getMercenaryDens(charId)`, `getMercenaryDenDetail(charId, denId)` |
|
|
458
|
+
| Cosmetics | `client.cosmetics` | Some | `getSkinr(id)`, `getCharacterSkinr(charId)`, `getCharacterSkinrComponents(charId)` |
|
|
459
|
+
| Paragon Hub | `client.paragonHub` | Some | `getPublicListings()`, `getCharacterListings(charId)`, `getAllianceListings(id)` |
|
|
460
|
+
| Access Lists | `client.accessLists` | Yes | `getAccessList(id)` |
|
|
461
|
+
| Status | `client.status` | No | `getStatus()` |
|
|
462
|
+
| UI | `client.ui` | Yes | `setAutopilotWaypoint(destId, addToBeginning, clear)`, `openNewMailWindow(body)` |
|
|
463
|
+
| Universe | `client.universe` | Some | `getSystemById(id)`, `getTypeById(id)` |
|
|
464
|
+
| Wallet | `client.wallet` | Yes | `getCharacterWallet(id)` |
|
|
465
|
+
| Wars | `client.wars` | No | `getWars()`, `getWarById(id)` |
|
|
466
|
+
| Freelance Jobs | `client.freelanceJobs` | Some | `getFreelanceJobs()`, `getFreelanceJobById(id)` |
|
|
467
|
+
| Meta | `client.meta` | No | `getOpenApiJson()`, `getOpenApiYaml()` |
|
|
468
|
+
|
|
469
|
+
## Runtime Response Validation
|
|
470
|
+
|
|
471
|
+
ESI.ts validates API responses at runtime using [Zod](https://zod.dev/) schemas. All GET endpoints have schemas — these are the endpoints that return data your application consumes, where a silent shape change from CCP would cause bugs. POST/PUT/DELETE mutations typically return `204 No Content` (no body to validate) or simple confirmation values, so schemas are omitted where there is nothing meaningful to validate.
|
|
472
|
+
|
|
473
|
+
Validation is **on by default**. Extra fields from ESI are preserved via `z.looseObject()` passthrough mode, so new fields added by CCP won't break your application — they flow through to your code untouched.
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
import {
|
|
477
|
+
EsiClient,
|
|
478
|
+
EsiValidationError,
|
|
479
|
+
isValidationError,
|
|
480
|
+
schemas,
|
|
481
|
+
} from '@lgriffin/esi.ts';
|
|
482
|
+
|
|
483
|
+
const client = new EsiClient();
|
|
484
|
+
|
|
485
|
+
// Validation happens automatically on every request
|
|
486
|
+
const character = await client.characters.getCharacterPublicInfo(12345);
|
|
487
|
+
|
|
488
|
+
// Disable validation globally if needed
|
|
489
|
+
const rawClient = new EsiClient({ validateResponse: false });
|
|
490
|
+
|
|
491
|
+
// Use schemas directly for your own validation
|
|
492
|
+
const result = schemas.CharacterInfoSchema.safeParse(someData);
|
|
493
|
+
if (result.success) {
|
|
494
|
+
console.log(result.data.name);
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Request Body Validation
|
|
499
|
+
|
|
500
|
+
For POST/PUT/DELETE endpoints, opt-in request body validation ensures outgoing payloads match the endpoint's `requestSchema` before the request is sent:
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// Opt-in request body validation for POST/PUT/DELETE
|
|
504
|
+
const client = new EsiClient({ validateRequest: true });
|
|
505
|
+
|
|
506
|
+
// Throws EsiValidationError if the request body doesn't match the endpoint's requestSchema
|
|
507
|
+
await client.mail.sendMail(characterId, {
|
|
508
|
+
recipients: [{ recipient_id: 12345, recipient_type: 'character' }],
|
|
509
|
+
subject: 'Hello',
|
|
510
|
+
body: 'Message body',
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
See [guides/RUNTIME-VALIDATION.md](guides/RUNTIME-VALIDATION.md) for the full guide on schemas, error handling, and extending schemas.
|
|
515
|
+
|
|
516
|
+
## Caching
|
|
517
|
+
|
|
518
|
+
ETag caching is on by default and works in three tiers: a GET inside the spec-defined TTL is answered from cache with no HTTP call, an older entry is revalidated with `If-None-Match`, and a 5xx with a cached copy serves the stale body instead of throwing. Authenticated cache entries are isolated per token.
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
const client = new EsiClient({ etagCacheConfig: { maxEntries: 2000 } });
|
|
522
|
+
client.getCacheStats();
|
|
523
|
+
client.clearCache();
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
See [Caching in the architecture guide](guides/ARCHITECTURE.md#4-caching) for TTL precedence, invalidation, keys and configuration.
|
|
527
|
+
|
|
528
|
+
## Batch Requests
|
|
529
|
+
|
|
530
|
+
Fetch data for multiple IDs with bounded concurrency using `batch()`, or chunk large POST payloads with `batchPost()`:
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
import { EsiClient } from '@lgriffin/esi.ts';
|
|
534
|
+
|
|
535
|
+
const client = new EsiClient();
|
|
536
|
+
|
|
537
|
+
// Fetch 500 type details with at most 10 concurrent requests (default 20)
|
|
538
|
+
const result = await client.batch(
|
|
539
|
+
typeIds,
|
|
540
|
+
(id) => client.universe.getTypeById(id),
|
|
541
|
+
{
|
|
542
|
+
concurrency: 10,
|
|
543
|
+
onProgress: (done, total) => console.log(`${done}/${total}`),
|
|
544
|
+
},
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
// result.results: Map<number, T> — successful responses
|
|
548
|
+
// result.errors: Map<number, Error> — failed requests
|
|
549
|
+
console.log(`${result.results.size} succeeded, ${result.errors.size} failed`);
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
For POST endpoints that accept arrays (e.g., `postNamesAndCategories` with a 1000-ID limit), `batchPost` auto-chunks and concatenates:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
const allNames = await client.batchPost(
|
|
556
|
+
largeIdArray,
|
|
557
|
+
(chunk) => client.universe.postNamesAndCategories(chunk),
|
|
558
|
+
1000, // chunk size
|
|
559
|
+
);
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## Streaming Pagination
|
|
563
|
+
|
|
564
|
+
Paginated endpoints can be consumed three ways: the plain method fetches every page and returns one array, `stream*` methods yield one validated page at a time, and `fetchAll*` methods fetch the remaining pages concurrently.
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
for await (const page of client.market.streamMarketOrders(10000002)) {
|
|
568
|
+
console.log(
|
|
569
|
+
`Page ${page.page}/${page.totalPages}: ${page.data.length} orders`,
|
|
570
|
+
);
|
|
571
|
+
if (page.page >= 3) break; // stops fetching the remaining pages
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Try it: `npm run example:streaming`. See [guides/PAGINATION.md](guides/PAGINATION.md) for the full method list, concurrency defaults and failure behaviour.
|
|
576
|
+
|
|
577
|
+
## Cursor-based Pagination
|
|
578
|
+
|
|
579
|
+
Newer ESI routes such as Freelance Jobs page with opaque `before` / `after` cursor tokens instead of page numbers. `fetchAllCursorPages` follows them to the end of the dataset, and a saved `after` token can be polled later for changed records.
|
|
580
|
+
|
|
581
|
+
See [guides/PAGINATION.md](guides/PAGINATION.md) for cursor semantics and examples.
|
|
582
|
+
|
|
583
|
+
## Generated Types
|
|
584
|
+
|
|
585
|
+
The library includes TypeScript interfaces generated directly from the ESI OpenAPI 3.1 spec, available as the `EsiSpec` namespace. These are guaranteed to match the live spec and complement the hand-written types:
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
import { EsiSpec } from '@lgriffin/esi.ts';
|
|
589
|
+
|
|
590
|
+
// Generated type — uses OpenAPI schema names (v7.0.0+)
|
|
591
|
+
const order: EsiSpec.MarketsRegionIdOrdersGet = {
|
|
592
|
+
order_id: 123,
|
|
593
|
+
type_id: 34,
|
|
594
|
+
price: 5.5,
|
|
595
|
+
volume_remain: 1000,
|
|
596
|
+
volume_total: 5000,
|
|
597
|
+
is_buy_order: false,
|
|
598
|
+
// ...
|
|
599
|
+
};
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
To regenerate types from the latest ESI spec:
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
npm run generate:types # fetches OpenAPI spec, generates 161 interfaces + cache TTL map + rate limit groups + scope map
|
|
606
|
+
npm run validate:esi # reports type drift between hand-written and generated types
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## ESI Scopes
|
|
610
|
+
|
|
611
|
+
The library includes a generated scope-to-endpoint mapping extracted from the ESI OpenAPI spec. Use it to check which OAuth scopes an endpoint requires before making a request:
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
import { esiEndpointScopes, EsiScope } from '@lgriffin/esi.ts';
|
|
615
|
+
|
|
616
|
+
// Look up scopes for a specific endpoint
|
|
617
|
+
const walletScopes = esiEndpointScopes['GET:characters/{character_id}/wallet'];
|
|
618
|
+
// → ['esi-wallet.read_character_wallet.v1']
|
|
619
|
+
|
|
620
|
+
// Check if an endpoint requires auth
|
|
621
|
+
const isPublic = !esiEndpointScopes['GET:universe/types/{type_id}'];
|
|
622
|
+
// → true (public endpoint, no scopes needed)
|
|
623
|
+
|
|
624
|
+
// Type-safe scope values
|
|
625
|
+
const scope: EsiScope = 'esi-assets.read_assets.v1';
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
## Error Handling
|
|
629
|
+
|
|
630
|
+
Failed calls throw `EsiError` (with `statusCode`, a sanitised `url` and `retryable`) or one of its subclasses, `TimeoutError` and `EsiValidationError`. An open circuit throws `CircuitOpenError`. Type guards such as `isRetryable`, `isTimeout`, `isValidationError` and `isCircuitOpen` narrow them, and `withSafeMode()` returns a result envelope instead of throwing.
|
|
631
|
+
|
|
632
|
+
```typescript
|
|
633
|
+
import { EsiError, isCircuitOpen } from '@lgriffin/esi.ts';
|
|
634
|
+
|
|
635
|
+
try {
|
|
636
|
+
await client.alliance.getAllianceById(99999999);
|
|
637
|
+
} catch (err) {
|
|
638
|
+
if (isCircuitOpen(err)) console.log(`Retry in ${err.retryAfterMs} ms`);
|
|
639
|
+
else if (err instanceof EsiError) console.log(err.statusCode, err.retryable);
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
See [guides/ERRORS.md](guides/ERRORS.md) for the class hierarchy, retryability rules and safe mode.
|
|
644
|
+
|
|
645
|
+
## Response Metadata
|
|
646
|
+
|
|
647
|
+
Use `withMetadata()` to get response headers, cache status, rate limit info, and timing alongside the data:
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
const metaClient = client.alliance.withMetadata();
|
|
651
|
+
const result = await metaClient.getAllianceById(99000001);
|
|
652
|
+
|
|
653
|
+
console.log(result.data.name); // "Goonswarm Federation"
|
|
654
|
+
console.log(result.meta.fromCache); // true if served from cache
|
|
655
|
+
console.log(result.meta.cacheHitType); // 'spec-ttl' | 'etag-304' | 'stale-on-error'
|
|
656
|
+
console.log(result.meta.responseTimeMs); // milliseconds
|
|
657
|
+
console.log(result.meta.rateLimit); // { remaining, limit, used, group }
|
|
658
|
+
console.log(result.meta.requestId); // ESI request ID for debugging
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
The `meta` object includes:
|
|
662
|
+
|
|
663
|
+
| Field | Type | Description |
|
|
664
|
+
| ---------------- | ------------------------ | ------------------------------------------------- |
|
|
665
|
+
| `headers` | `Record<string, string>` | Raw response headers |
|
|
666
|
+
| `fromCache` | `boolean` | Whether data was served from cache |
|
|
667
|
+
| `stale` | `boolean` | Whether cached data is stale (5xx fallback) |
|
|
668
|
+
| `cacheHitType` | `string?` | `'spec-ttl'`, `'etag-304'`, or `'stale-on-error'` |
|
|
669
|
+
| `rateLimit` | `RateLimitMeta?` | Rate limit status from ESI headers |
|
|
670
|
+
| `responseTimeMs` | `number?` | Request duration in milliseconds |
|
|
671
|
+
| `requestId` | `string?` | ESI request ID |
|
|
672
|
+
| `warning` | `object?` | ESI deprecation warning |
|
|
673
|
+
|
|
674
|
+
## Rate Limiting
|
|
675
|
+
|
|
676
|
+
Rate limiting is always on and needs no configuration. Each ESI rate-limit group from the OpenAPI spec gets its own bucket, the limiter learns remaining tokens from ESI's response headers, and a 420 or 429 blocks only the affected group. Multi-character applications can give each token its own buckets:
|
|
677
|
+
|
|
678
|
+
```typescript
|
|
679
|
+
const client = new EsiClient({
|
|
680
|
+
rateLimiterConfig: {
|
|
681
|
+
userKeyExtractor: (headers) => headers['Authorization'] ?? 'anon',
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
See [Rate limiting in the architecture guide](guides/ARCHITECTURE.md#6-rate-limiting) for the throttling rules, per-endpoint overrides and monitoring. Retry, deduplication, the opt-in [circuit breaker](guides/ARCHITECTURE.md#7-circuit-breaker) and [request/response interceptors](guides/ARCHITECTURE.md#8-interceptors) are documented alongside it.
|
|
687
|
+
|
|
688
|
+
## Lightweight Clients
|
|
689
|
+
|
|
690
|
+
All three client creation patterns (`EsiClient`, `CustomEsiClient`, `EsiApiFactory`) now get identical middleware defaults (cache, request deduplication, rate limiter) thanks to `configureApiClient()`. Previously `CustomEsiClient` and `EsiApiFactory` only configured the rate limiter.
|
|
691
|
+
|
|
692
|
+
If you only need a subset of APIs, use `CustomEsiClient` or `EsiClientBuilder` to load only what you need:
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
import { EsiClientBuilder } from '@lgriffin/esi.ts';
|
|
696
|
+
|
|
697
|
+
const client = new EsiClientBuilder()
|
|
698
|
+
.addClients(['market', 'universe', 'characters'])
|
|
699
|
+
.withClientId('my-trading-bot')
|
|
700
|
+
.withAccessToken('your-token')
|
|
701
|
+
.build();
|
|
702
|
+
|
|
703
|
+
const prices = await client.market?.getMarketPrices();
|
|
704
|
+
const system = await client.universe?.getSystemById(30000142);
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Or create standalone single-API clients:
|
|
708
|
+
|
|
709
|
+
```typescript
|
|
710
|
+
import { EsiApiFactory } from '@lgriffin/esi.ts';
|
|
711
|
+
|
|
712
|
+
const marketClient = EsiApiFactory.createMarketClient({
|
|
713
|
+
clientId: 'price-checker',
|
|
714
|
+
});
|
|
715
|
+
const prices = await marketClient.getMarketPrices();
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
## Endpoint Coverage
|
|
719
|
+
|
|
720
|
+
All 235 endpoint definitions have been validated against live Tranquility using the **OpenAPI 3.1 spec** — 206 from the public ESI spec plus 29 for newer EVE features. Full output is captured in [`openapi.output.md`](openapi.output.md).
|
|
721
|
+
|
|
722
|
+
| Category | Endpoints | Method |
|
|
723
|
+
| --------------------------- | --------- | -------------------------------------------------- |
|
|
724
|
+
| Public GETs | 86 | 52 runnable example scripts with captured output |
|
|
725
|
+
| Authenticated GETs | 114 | Example scripts + live testing with EVE SSO tokens |
|
|
726
|
+
| Contacts (POST/PUT/DELETE) | 3 | Live create/edit/delete lifecycle |
|
|
727
|
+
| Fittings (POST/DELETE) | 2 | Live create/delete lifecycle |
|
|
728
|
+
| Mail (POST/PUT/DELETE) | 5 | Live send/label/metadata/delete lifecycle |
|
|
729
|
+
| UI (POST) | 5 | Live testing with EVE client running |
|
|
730
|
+
| Calendar (PUT) | 1 | Live RSVP to event |
|
|
731
|
+
| Fleet (GET/POST/PUT/DELETE) | 14 | Live fleet with fleet commander + squad members |
|
|
732
|
+
| Assets POST | 3 | Live asset location/name queries |
|
|
733
|
+
| CSPA (POST) | 1 | Live charge cost calculation |
|
|
734
|
+
| Dogma dynamic (GET) | 1 | Live mutaplasmid (Abyssal) item query |
|
|
735
|
+
| Universe POST helpers | 3 | Live name resolution and affiliation |
|
|
736
|
+
| Freelance Jobs (GET) | 4 | Live queries (graceful 404 for no active jobs) |
|
|
737
|
+
|
|
738
|
+
## Examples
|
|
739
|
+
|
|
740
|
+
52 runnable examples are in the `examples/` directory.
|
|
741
|
+
|
|
742
|
+
### Public Endpoints (no auth needed)
|
|
743
|
+
|
|
744
|
+
```bash
|
|
745
|
+
npm run example:status # Server status — quickest smoke test
|
|
746
|
+
npm run example:character # Character public info, portrait, corporation
|
|
747
|
+
npm run example:universe # Solar system, constellation, region, station
|
|
748
|
+
npm run example:market # Average prices + Tritanium price history
|
|
749
|
+
npm run example:alliance # Alliance info + member corporations
|
|
750
|
+
npm run example:route # Jita-to-Amarr route with system names
|
|
751
|
+
npm run example:wars # Recent wars with aggressor/defender details
|
|
752
|
+
npm run example:sovereignty # Nullsec sovereignty map + active campaigns
|
|
753
|
+
npm run example:industry # Industry facilities, cost indices, insurance
|
|
754
|
+
npm run example:incursions # Active incursions + faction warfare stats
|
|
755
|
+
npm run example:dogma # Item type details + dogma attributes
|
|
756
|
+
npm run example:contracts # Public region contracts + auction bids/items
|
|
757
|
+
npm run example:rate-limiting # Rate limiter & pagination demonstration
|
|
758
|
+
npm run example:cursor-pagination # Freelance Jobs with cursor pagination
|
|
759
|
+
npm run example:streaming # Streaming pagination for large datasets
|
|
760
|
+
npm run example:token-refresh # Automatic token refresh on 401
|
|
761
|
+
npm run example:universe-encyclopedia # Ancestries, bloodlines, races, celestials
|
|
762
|
+
npm run example:dogma-meta-sov # Dogma effects, sovereignty, meta endpoint
|
|
763
|
+
npm run example:faction-details # Faction warfare leaderboards and stats
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### Authenticated Endpoints (require ESI_ACCESS_TOKEN)
|
|
767
|
+
|
|
768
|
+
```bash
|
|
769
|
+
npm run example # Full character profile assembly
|
|
770
|
+
npm run example:wallet # Wallet balance, journal, transactions
|
|
771
|
+
npm run example:skills # Trained skills, queue, attributes
|
|
772
|
+
npm run example:assets # Asset inventory with bulk name lookup
|
|
773
|
+
npm run example:killmails # Recent killmails + full details
|
|
774
|
+
npm run example:fleet # Fleet info, members, wing/squad structure
|
|
775
|
+
npm run example:mail # Inbox headers, labels, mailing lists
|
|
776
|
+
npm run example:location # Current system, online status, ship
|
|
777
|
+
npm run example:fittings # Saved fittings + clone state + implants
|
|
778
|
+
npm run example:contacts # Contact list with standings + labels
|
|
779
|
+
npm run example:character-details # Blueprints, roles, standings, medals
|
|
780
|
+
npm run example:corporation-details # Corp members, divisions, structures
|
|
781
|
+
npm run example:calendar-search # Calendar events + character search
|
|
782
|
+
npm run example:loyalty-pi # Loyalty points + planetary interaction
|
|
783
|
+
npm run example:industry-mining # Industry jobs + mining ledger
|
|
784
|
+
npm run example:market-orders # Character/corp market orders
|
|
785
|
+
npm run example:corp-contracts-wallet # Corp contracts, contacts, wallets
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### Write Operations (require specific scopes + caution)
|
|
789
|
+
|
|
790
|
+
```bash
|
|
791
|
+
npm run example:write-ops # Contacts, fittings, mail, UI lifecycle tests
|
|
792
|
+
npm run example:universe-posts # Name resolution + character affiliation (public)
|
|
793
|
+
npm run example:freelance-jobs # Freelance job queries
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### Parallel Requests
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
const [character, portrait, corp] = await Promise.all([
|
|
800
|
+
client.characters.getCharacterPublicInfo(characterId),
|
|
801
|
+
client.characters.getCharacterPortrait(characterId),
|
|
802
|
+
client.corporations.getCorporationInfo(corporationId),
|
|
803
|
+
]);
|
|
804
|
+
|
|
805
|
+
console.log(`${character.name} [${corp.ticker}]`);
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### Market Analysis
|
|
809
|
+
|
|
810
|
+
```typescript
|
|
811
|
+
const [orders, history] = await Promise.all([
|
|
812
|
+
client.market.getMarketOrders(regionId),
|
|
813
|
+
client.market.getMarketHistory(regionId, typeId),
|
|
814
|
+
]);
|
|
815
|
+
|
|
816
|
+
const buyOrders = orders.filter((o) => o.is_buy_order);
|
|
817
|
+
const sellOrders = orders.filter((o) => !o.is_buy_order);
|
|
818
|
+
|
|
819
|
+
console.log(`Best buy: ${Math.max(...buyOrders.map((o) => o.price))}`);
|
|
820
|
+
console.log(`Best sell: ${Math.min(...sellOrders.map((o) => o.price))}`);
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
## Resource Management
|
|
824
|
+
|
|
825
|
+
Always call `shutdown()` when you're done to clean up cache timers:
|
|
826
|
+
|
|
827
|
+
```typescript
|
|
828
|
+
const client = new EsiClient();
|
|
829
|
+
try {
|
|
830
|
+
const status = await client.status.getStatus();
|
|
831
|
+
console.log(status.server_version);
|
|
832
|
+
} finally {
|
|
833
|
+
await client.shutdown();
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
## Testing
|
|
838
|
+
|
|
839
|
+
ESI.ts has a comprehensive multi-tier testing strategy with 171 suites and 4,957 tests:
|
|
840
|
+
|
|
841
|
+
| Tier | Tests | Purpose |
|
|
842
|
+
| -------------------------- | ---------------- | ------------------------------------------------------------------ |
|
|
843
|
+
| **TDD unit tests** | 130 files | Every client method, endpoint path, query param, and body format |
|
|
844
|
+
| **BDD scenario tests** | 41 feature files | Behavioral specifications in Gherkin (Given/When/Then) |
|
|
845
|
+
| **Mocked integration** | Full suite | Cross-layer request flow with jest-fetch-mock |
|
|
846
|
+
| **Live smoke tests** | 46 examples | Every endpoint against live Tranquility |
|
|
847
|
+
| **ESI spec contract** | 15 tests | Endpoint definitions validated against live OpenAPI spec |
|
|
848
|
+
| **Deep contract tests** | 8 categories | Path params, query params, body, auth, schemas, pagination vs spec |
|
|
849
|
+
| **Property-based fuzzing** | 601 tests | fast-check fuzzing of validation, URL construction, Zod schemas |
|
|
850
|
+
| **Mutation testing** | Stryker | Validates test suite kills code mutants |
|
|
851
|
+
| **Type-level tests** | tsd | Consumer API type correctness via tsd |
|
|
852
|
+
| **Gated auth tests** | 33 tests | Authenticated endpoints with real tokens |
|
|
853
|
+
| **Construction parity** | Per-surface | Verifies all client surfaces get identical middleware defaults |
|
|
854
|
+
| **Spec-alignment** | Type assertions | Ensures hand-written types align with generated OpenAPI types |
|
|
855
|
+
|
|
856
|
+
```bash
|
|
857
|
+
npm test # Unit + BDD tests (171 suites, 4,957 tests)
|
|
858
|
+
npm run coverage # Tests with coverage report (thresholds enforced)
|
|
859
|
+
npm run bdd # BDD scenario tests only
|
|
860
|
+
npm run contract # Contract tests (skipped without ESI_LIVE_TESTS=true)
|
|
861
|
+
npm run fuzz # Property-based fuzz tests (601 tests)
|
|
862
|
+
npm run mutation # Mutation testing (Stryker)
|
|
863
|
+
npm run benchmark # Performance benchmark tests
|
|
864
|
+
npm run test:types # tsd consumer type tests
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
Coverage: statements 98.37%, branches 95.14%, functions 96.09%, lines 98.17%. Thresholds enforced in CI: branches 80%, functions 75%, lines 90%, statements 90%.
|
|
868
|
+
|
|
869
|
+
See [guides/TESTING.md](guides/TESTING.md) for the full testing guide, and [guides/ARCHITECTURE.md](guides/ARCHITECTURE.md) for architecture diagrams.
|
|
870
|
+
|
|
871
|
+
## Development
|
|
872
|
+
|
|
873
|
+
### Prerequisites
|
|
874
|
+
|
|
875
|
+
- Node.js 18+
|
|
876
|
+
- npm
|
|
877
|
+
|
|
878
|
+
### Code Quality Tools
|
|
879
|
+
|
|
880
|
+
The project uses a comprehensive suite of static analysis and code quality tools:
|
|
881
|
+
|
|
882
|
+
| Tool | Purpose | Command |
|
|
883
|
+
| ------------------------------------------------------------------------------------ | ------------------------------------------------------- | ------------------------------ |
|
|
884
|
+
| [ESLint](https://eslint.org/) | Linting with TypeScript, security, and code smell rules | `npm run lint` |
|
|
885
|
+
| [Prettier](https://prettier.io/) | Code formatting | `npm run format:check` |
|
|
886
|
+
| [knip](https://knip.dev/) | Dead code and unused export detection | `npm run knip` |
|
|
887
|
+
| [eslint-plugin-security](https://github.com/eslint-community/eslint-plugin-security) | Security anti-pattern detection | Integrated into `npm run lint` |
|
|
888
|
+
| [eslint-plugin-sonarjs](https://github.com/SonarSource/eslint-plugin-sonarjs) | Cognitive complexity and code smell detection | Integrated into `npm run lint` |
|
|
889
|
+
| [husky](https://typicode.github.io/husky/) | Git pre-commit hooks | Automatic on commit |
|
|
890
|
+
| [lint-staged](https://github.com/lint-staged/lint-staged) | Run linters on staged files only | Automatic on commit |
|
|
891
|
+
| [Redocly CLI](https://redocly.com/docs/cli/) | OpenAPI spec validation and linting | `npm run validate:spec` |
|
|
892
|
+
|
|
893
|
+
### Available Scripts
|
|
894
|
+
|
|
895
|
+
```bash
|
|
896
|
+
# Development
|
|
897
|
+
npm run build # Compile TypeScript
|
|
898
|
+
npm run lint # Run ESLint
|
|
899
|
+
npm run lint:fix # Run ESLint with auto-fix
|
|
900
|
+
npm run format # Format code with Prettier
|
|
901
|
+
npm run format:check # Check formatting without modifying
|
|
902
|
+
|
|
903
|
+
# Testing
|
|
904
|
+
npm test # Unit tests (171 suites, 4,957 tests)
|
|
905
|
+
npm run test:all # Unit + BDD + integration + fuzz + type tests
|
|
906
|
+
npm run coverage # Tests with coverage report (thresholds enforced)
|
|
907
|
+
npm run bdd # BDD scenario tests
|
|
908
|
+
ESI_LIVE_TESTS=true npm run contract:live # Deep contract tests against live ESI spec (fails without the variable)
|
|
909
|
+
npm run fuzz # Property-based fuzz tests (fast-check)
|
|
910
|
+
npm run mutation # Mutation testing (Stryker)
|
|
911
|
+
npm run benchmark # Performance benchmark tests
|
|
912
|
+
npm run test:types # Consumer type tests (tsd)
|
|
913
|
+
npm run mock:esi # Start Prism mock ESI server on port 4010
|
|
914
|
+
|
|
915
|
+
# Static Analysis
|
|
916
|
+
npm run knip # Detect dead code and unused exports
|
|
917
|
+
npm run validate:esi # Validate endpoints against live ESI OpenAPI spec
|
|
918
|
+
npm run validate:spec # Lint ESI OpenAPI spec with Redocly (structural + best practices)
|
|
919
|
+
npm run validate:auth-scopes # Auth/scope cross-validation
|
|
920
|
+
npm run schema:drift # Schema drift detection (hand-written vs OpenAPI spec)
|
|
921
|
+
npm run validate # Run all checks: lint, format, build, coverage, knip
|
|
922
|
+
npm run generate:types # Regenerate TypeScript interfaces from ESI OpenAPI spec
|
|
923
|
+
npm run generate:endpoints # Regenerate endpoint definitions from ESI OpenAPI spec
|
|
924
|
+
npm run generate:all # Run all generators (types + endpoints + OKF)
|
|
925
|
+
npm run generate:okf # Generate OKF knowledge bundle from ESI OpenAPI spec
|
|
926
|
+
|
|
927
|
+
# Documentation
|
|
928
|
+
npm run docs # Generate TypeDoc API documentation
|
|
929
|
+
npm run docs:serve # Serve docs locally on port 8080
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
### ESI Endpoint Validation
|
|
933
|
+
|
|
934
|
+
To verify that the codebase endpoint definitions match the live ESI OpenAPI spec:
|
|
935
|
+
|
|
936
|
+
```bash
|
|
937
|
+
npm run validate:esi
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
This fetches the ESI OpenAPI spec and reports:
|
|
941
|
+
|
|
942
|
+
- Endpoints in the codebase that are no longer in the ESI spec
|
|
943
|
+
- Endpoints in the ESI spec that the codebase doesn't cover
|
|
944
|
+
- HTTP method mismatches between codebase and spec
|
|
945
|
+
|
|
946
|
+
### Pre-commit Hooks
|
|
947
|
+
|
|
948
|
+
The project uses husky with lint-staged to run ESLint and Prettier on staged files before each commit. This is set up automatically when you run `npm install`.
|
|
949
|
+
|
|
950
|
+
### CI/CD
|
|
951
|
+
|
|
952
|
+
Every push runs lint, format, build, typecheck and unit tests; pull requests to `master` run the full matrix behind a single Quality Gate check. Actions are SHA-pinned, packages publish with npm provenance, and release assets are cosign-signed.
|
|
953
|
+
|
|
954
|
+
See [guides/QUALITY-GATES.md](guides/QUALITY-GATES.md) for the gate matrix and every workflow, and [guides/SECURITY.md](guides/SECURITY.md) for the supply-chain controls.
|
|
955
|
+
|
|
956
|
+
## Contributing
|
|
957
|
+
|
|
958
|
+
1. Fork the repository
|
|
959
|
+
2. Create a feature branch
|
|
960
|
+
3. Write tests for your changes
|
|
961
|
+
4. Run `npm run validate` to check everything passes
|
|
962
|
+
5. Open a Pull Request
|
|
963
|
+
|
|
964
|
+
Work is tracked with [beads](https://github.com/gastownhall/beads) (`bd`). Run
|
|
965
|
+
`bd ready` to see available work — see [guides/BEADS.md](guides/BEADS.md) for the
|
|
966
|
+
full workflow.
|
|
967
|
+
|
|
968
|
+
## License
|
|
969
|
+
|
|
970
|
+
GPL-3.0-or-later - see the [LICENSE](LICENSE) file for details.
|
|
971
|
+
|
|
972
|
+
---
|
|
973
|
+
|
|
974
|
+
**o7**
|