Initial Commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user