59 lines
3.2 KiB
Markdown
59 lines
3.2 KiB
Markdown
# ESI operations
|
|
|
|
The server ESI adapter uses the fixed trusted base `https://esi.evetech.net` and pins
|
|
`X-Compatibility-Date: 2026-08-18` (`ESI_COMPATIBILITY_DATE`). It exposes only the
|
|
character asset GET and global market-price GET needed by the application; callers
|
|
cannot provide an arbitrary ESI URL.
|
|
|
|
## Asset synchronization
|
|
|
|
`EsiClient.getAssets(characterId, accessToken, authorizationKey)` fetches page one,
|
|
reads `X-Pages`, fetches all remaining pages, de-duplicates by `item_id` while
|
|
checking for duplicates, and revalidates page one after the other pages. A changed
|
|
page count, duplicate ID, malformed page, failed request, or changed first page
|
|
fails the operation. The caller must not persist a returned partial result: the
|
|
method returns only a complete `{ assets, syncedAt, expiresAt }` snapshot. Asset
|
|
IDs are retained as decimal strings, and the response is parsed with
|
|
`lossless-json` so an int64 cannot be rounded by JavaScript.
|
|
|
|
This is a stability check, not a transaction. ESI's paginated endpoint does not
|
|
promise a true multi-page snapshot while its cache expires between page requests.
|
|
A caller should stage pages and replace the persisted Convex snapshot only after
|
|
`getAssets` resolves. Convex persistence is intentionally outside this adapter.
|
|
|
|
The cache is private to an `EsiClient` and keyed by user/authorization context,
|
|
character, and scopes. `authorizationKey` should therefore identify the linked
|
|
application user and include the character/scopes when available:
|
|
|
|
```ts
|
|
{ userId, characterId, scopes }
|
|
```
|
|
|
|
A string key is supported for callers that have no scope object, but is treated as
|
|
an isolated user key. Tokens are never used as cache keys or emitted in metrics.
|
|
|
|
Responses retain their raw text, ETag, Last-Modified, and cache metadata. Fresh
|
|
responses use the server TTL; stale responses send `If-None-Match` and
|
|
`If-Modified-Since`, and a 304 reuses the cached body. `Cache-Control`, `Expires`,
|
|
`Date`, and `Age` are honored. `no-store` responses retain neither body nor
|
|
validators. Requests have one deadline covering response-body reads and bounded
|
|
retries for transient network/5xx/420/429 responses. `Retry-After` (seconds or
|
|
HTTP date) and the legacy ESI error-limit reset are respected. Exhausted new
|
|
limiter buckets are cooled down per authorization context and group; the legacy
|
|
error limiter is cooled down globally. A server delay that exceeds the remaining
|
|
request budget returns a typed rate-limit error rather than retrying early. 401
|
|
and 403 are never blindly retried.
|
|
|
|
## Market estimates
|
|
|
|
`EsiMarketEstimates` reads public `GET /markets/prices`. It uses only
|
|
`average_price`; a missing average price is omitted and `adjusted_price` is never
|
|
a fallback. The returned `PriceSnapshot.source` includes the pinned compatibility
|
|
date, `asOf` is the retrieval time (ESI supplies no source timestamp), and prices
|
|
are integer ISK cents represented as validated decimal strings. The route is a
|
|
cached global reference estimate, not a regional or structure order book.
|
|
|
|
Metrics supplied through `EsiClient`'s `observe` callback include endpoint, status,
|
|
retry/cache state, and redacted rate-limit headers. They do not include access
|
|
tokens, authorization keys, or response bodies.
|