@spacemolt/lib 15.1.0 → 15.2.1
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/COMMANDS.md +1 -1
- package/README.md +51 -0
- package/dist/account.d.ts +62 -1
- package/dist/auth/clerk.d.ts +3 -1
- package/dist/client.d.ts +24 -1
- package/dist/data/catalog.d.ts +10 -5
- package/dist/data/http.d.ts +36 -0
- package/dist/data/map.d.ts +3 -2
- package/dist/data/mobile-base.d.ts +3 -2
- package/dist/data/stations.d.ts +3 -2
- package/dist/errors.d.ts +19 -0
- package/dist/generated/actions.gen.d.ts +1 -1
- package/dist/generated/openapi/types.gen.d.ts +10 -11
- package/dist/index.d.ts +3 -2
- package/dist/index.js +257 -70
- package/dist/index.js.map +33 -0
- package/dist/jitter.d.ts +8 -0
- package/dist/node.js +26 -0
- package/dist/node.js.map +12 -0
- package/package.json +7 -9
package/COMMANDS.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AUTO-GENERATED by scripts/generate.ts from openapi.json. DO NOT EDIT. -->
|
|
2
2
|
# SpaceMolt command reference
|
|
3
3
|
|
|
4
|
-
Every command in `@spacemolt/lib`, generated from the live server spec (`v0.
|
|
4
|
+
Every command in `@spacemolt/lib`, generated from the live server spec (`v0.613.4`). **296 commands across 19 tools.**
|
|
5
5
|
|
|
6
6
|
## How to call a command
|
|
7
7
|
|
package/README.md
CHANGED
|
@@ -136,6 +136,23 @@ account.onAny((frame) => console.log('push:', frame.type));
|
|
|
136
136
|
for await (const hit of account.events('battle_damage')) { /* ... */ }
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
`onAny` sees only unsolicited pushes. To log every frame on the wire, including
|
|
140
|
+
your own query/mutation replies, pass `onSend`/`onReceive` (the client variants
|
|
141
|
+
also receive the account id). Credentials are redacted in both directions:
|
|
142
|
+
`onSend` sees a `spacemolt_auth` frame with its `password`, `token`,
|
|
143
|
+
`registration_code`, or `device_code` (from `login_link_poll`) replaced by
|
|
144
|
+
`'[redacted]'`, and `onReceive` sees a `registered` push (which carries the
|
|
145
|
+
account's generated password) with that password redacted, and a `result`
|
|
146
|
+
frame carrying a `device_code` (`login_link`'s response) with that code
|
|
147
|
+
redacted too — the frame actually sent/routed is unaffected either way:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
new SpacemoltClient({
|
|
151
|
+
onSend: (id, frame) => console.log(id, '→', frame.tool, frame.action, frame.request_id),
|
|
152
|
+
onReceive: (id, frame) => console.log(id, '←', frame.type, frame.request_id),
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
139
156
|
### Subscriptions
|
|
140
157
|
|
|
141
158
|
Subscribe to a station's order book or to presence at your location; the
|
|
@@ -303,6 +320,40 @@ const { stations } = await fetchStations('https://game.spacemolt.com');
|
|
|
303
320
|
const { system } = await fetchMobileBase('https://game.spacemolt.com'); // the moving capital
|
|
304
321
|
```
|
|
305
322
|
|
|
323
|
+
Every one of these fetches is bounded by a timeout (30s by default, covering the
|
|
324
|
+
response body, not just the headers; pass `{ timeoutMs }`, or `httpTimeoutMs` on
|
|
325
|
+
the client, to change it — the catalog is several MB), retries a transient `429`/`503` a few times
|
|
326
|
+
(honoring `Retry-After`, jittered), and throws a typed `HttpError`
|
|
327
|
+
(`status`/`url`/`attempts`/`retryAfterMs`) on a retry-exhausted or otherwise
|
|
328
|
+
non-ok response. A timeout or network error throws the native error
|
|
329
|
+
(`DOMException`/`TypeError`) instead — it's not an `HttpError`. The 60s
|
|
330
|
+
retry-wait cap is on the *total* base wait summed across every attempt, not
|
|
331
|
+
each attempt checked in isolation — so three retries of a near-the-cap
|
|
332
|
+
`Retry-After` can't add up to several times the cap.
|
|
333
|
+
|
|
334
|
+
With a client, the same data is `client.catalog()`, `client.map()`,
|
|
335
|
+
`client.stations()` and `client.mobileBase()`. To observe HTTP traffic
|
|
336
|
+
(status/bytes/timing/retries), set `fetchImpl` once on the client — every
|
|
337
|
+
HTTP call it makes, including Clerk, goes through it:
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
const client = new SpacemoltClient({
|
|
341
|
+
fetchImpl: async (input, init) => {
|
|
342
|
+
const started = Date.now();
|
|
343
|
+
const res = await fetch(input, init);
|
|
344
|
+
console.log(res.status, String(input), `${Date.now() - started}ms`);
|
|
345
|
+
return res;
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The per-call timeout is delivered through `init.signal` (an `AbortSignal`), not
|
|
351
|
+
a separate argument — a wrapper must pass `init` through to the real `fetch`
|
|
352
|
+
unchanged, as above, or it silently loses the timeout.
|
|
353
|
+
|
|
354
|
+
The standalone functions above take the same option as a trailing
|
|
355
|
+
`{ fetchImpl }` for use without a client.
|
|
356
|
+
|
|
306
357
|
## Examples
|
|
307
358
|
|
|
308
359
|
Runnable scripts in [`examples/`](./examples), run with `bun run examples/<name>.ts`:
|
package/dist/account.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { FleetPush, OkPush } from './push-frames.js';
|
|
|
18
18
|
import { ConnectionClosedError } from './errors.js';
|
|
19
19
|
import { type MarketBook } from './state/market.js';
|
|
20
20
|
import { type ObservationView } from './state/observation.js';
|
|
21
|
-
import type { GameState, MutationAck, MutationResult, QueryResult, RawFrame, StateSection, WelcomeFrame } from './protocol.js';
|
|
21
|
+
import type { GameState, MutationAck, MutationResult, QueryResult, InboundFrame, RawFrame, StateSection, WelcomeFrame } from './protocol.js';
|
|
22
22
|
import { type WebSocketFactory } from './transport/socket.js';
|
|
23
23
|
/**
|
|
24
24
|
* The `logged_in` frame payload, typed from the server's published
|
|
@@ -43,11 +43,36 @@ export interface ReconnectOptions {
|
|
|
43
43
|
/** Backoff ceiling in ms. Default 30000. */
|
|
44
44
|
maxDelayMs?: number;
|
|
45
45
|
}
|
|
46
|
+
/** Info reported to an `onRateLimited` listener — see `Account.onRateLimited`. */
|
|
47
|
+
export interface RateLimitedInfo {
|
|
48
|
+
/** The call being retried: `tool.action` for `query`/`mutate`, `'authenticate'` for auth. */
|
|
49
|
+
command: string;
|
|
50
|
+
/** 1-based retry attempt number. */
|
|
51
|
+
attempt: number;
|
|
52
|
+
/** How long this retry is waiting before resending, including jitter. */
|
|
53
|
+
delayMs: number;
|
|
54
|
+
}
|
|
46
55
|
export interface AccountOptions {
|
|
47
56
|
/** WebSocket URL of the v2 endpoint. Defaults to the production server. */
|
|
48
57
|
url?: string;
|
|
49
58
|
/** Inject a WebSocket implementation (tests, custom runtimes). */
|
|
50
59
|
webSocketFactory?: WebSocketFactory;
|
|
60
|
+
/**
|
|
61
|
+
* Observe every outbound frame (logging/review). Fires before it is sent.
|
|
62
|
+
* A `spacemolt_auth` frame's `password`, `token`, `registration_code`, or
|
|
63
|
+
* `device_code` (from `login_link_poll`) is replaced with `'[redacted]'`
|
|
64
|
+
* before this fires — the frame actually sent to the server is unaffected.
|
|
65
|
+
*/
|
|
66
|
+
onSend?: (frame: InboundFrame) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Observe every parsed inbound frame (logging/review). Fires before
|
|
69
|
+
* routing. A `registered` push's `password` (the account's generated
|
|
70
|
+
* credential), and a `result` frame's `device_code` (`login_link`'s
|
|
71
|
+
* response), are replaced with `'[redacted]'` before this fires — the
|
|
72
|
+
* frame that reaches `register()`'s/the correlator's own resolution is
|
|
73
|
+
* unaffected.
|
|
74
|
+
*/
|
|
75
|
+
onReceive?: (frame: RawFrame) => void;
|
|
51
76
|
/**
|
|
52
77
|
* After authenticating, issue a `get_status` query to seed the local state
|
|
53
78
|
* cache with the canonical full state. Default `true`. Disable to avoid the
|
|
@@ -136,6 +161,8 @@ export declare class Account {
|
|
|
136
161
|
private readonly seedState;
|
|
137
162
|
private readonly url;
|
|
138
163
|
private readonly webSocketFactory?;
|
|
164
|
+
private readonly onSend?;
|
|
165
|
+
private readonly onReceive?;
|
|
139
166
|
private readonly reconnectConfig;
|
|
140
167
|
private readonly credentialsProvider?;
|
|
141
168
|
private readonly fetchImpl?;
|
|
@@ -148,6 +175,8 @@ export declare class Account {
|
|
|
148
175
|
private requestSeq;
|
|
149
176
|
private _welcome;
|
|
150
177
|
private _authenticated;
|
|
178
|
+
/** How many `reconnectOnce` calls are mid-swap; a count, so overlapping calls can't lift each other's guard. */
|
|
179
|
+
private reconnectsPending;
|
|
151
180
|
private _loginPayload;
|
|
152
181
|
private _currentTick;
|
|
153
182
|
private _commands;
|
|
@@ -169,6 +198,7 @@ export declare class Account {
|
|
|
169
198
|
private readonly reconnectedListeners;
|
|
170
199
|
private readonly reconnectingListeners;
|
|
171
200
|
private readonly disconnectedListeners;
|
|
201
|
+
private readonly rateLimitedListeners;
|
|
172
202
|
constructor(opts?: AccountOptions);
|
|
173
203
|
private makeSocket;
|
|
174
204
|
/** Live view of the cached game state. Treat as read-only. */
|
|
@@ -446,6 +476,13 @@ export declare class Account {
|
|
|
446
476
|
onReconnected(listener: () => void): () => void;
|
|
447
477
|
/** Fired at the start of each reconnect attempt (1-based). Returns an unsubscribe function. */
|
|
448
478
|
onReconnecting(listener: (attempt: number) => void): () => void;
|
|
479
|
+
/**
|
|
480
|
+
* Fired before each rate-limit retry sleep (`query`/`mutate`/`authenticate`
|
|
481
|
+
* auto-retrying a `rate_limited` error) — the delay includes jitter, so
|
|
482
|
+
* accounts rate-limited together don't retry in lockstep; this is what a
|
|
483
|
+
* caller sees per attempt. Returns an unsubscribe function.
|
|
484
|
+
*/
|
|
485
|
+
onRateLimited(listener: (info: RateLimitedInfo) => void): () => void;
|
|
449
486
|
/**
|
|
450
487
|
* Fired when the connection is gone for good (non-reconnectable or retries
|
|
451
488
|
* exhausted). Returns an unsubscribe function.
|
|
@@ -454,8 +491,15 @@ export declare class Account {
|
|
|
454
491
|
private authExchange;
|
|
455
492
|
/** Best-effort seed of the state cache after auth; failures are non-fatal. */
|
|
456
493
|
private maybeSeedState;
|
|
494
|
+
/** Returns whether the auth exchange was accepted — a caller must not send its frame when it wasn't. */
|
|
457
495
|
private beginAuth;
|
|
458
496
|
private sendFrame;
|
|
497
|
+
/**
|
|
498
|
+
* Calls an optional single-callback hook (`onSend`/`onReceive`), swallowing
|
|
499
|
+
* a throw so a broken observer can't stop the frame being sent or routed —
|
|
500
|
+
* same handling as `notifyListeners` for a `Set` of listeners.
|
|
501
|
+
*/
|
|
502
|
+
private callHook;
|
|
459
503
|
private nextRequestId;
|
|
460
504
|
/**
|
|
461
505
|
* Pick the `request_id` for one frame. A caller-supplied id is echoed by the
|
|
@@ -471,6 +515,16 @@ export declare class Account {
|
|
|
471
515
|
private claimRequestId;
|
|
472
516
|
private routeFrame;
|
|
473
517
|
private warnMalformedFrame;
|
|
518
|
+
/**
|
|
519
|
+
* Fails everything waiting on the current socket: in-flight correlator
|
|
520
|
+
* entries (query/mutate), a pending `welcome` wait, and a pending auth
|
|
521
|
+
* exchange (`login`/`register`/`loginToken`). Deliberately excludes
|
|
522
|
+
* `emitter.closeStreams()`: `handleClose` (a real close) still ends the
|
|
523
|
+
* player's event async iterators itself, but a `reconnectOnce` forced on a
|
|
524
|
+
* live socket leaves them running. Shared by `handleClose` and
|
|
525
|
+
* `reconnectOnce`, which both strand work on a socket that's going away.
|
|
526
|
+
*/
|
|
527
|
+
private failPendingWork;
|
|
474
528
|
private handleClose;
|
|
475
529
|
private shouldReconnect;
|
|
476
530
|
private reconnectLoop;
|
|
@@ -490,6 +544,13 @@ export declare class Account {
|
|
|
490
544
|
*/
|
|
491
545
|
reconnectOnce(): Promise<void>;
|
|
492
546
|
private resubscribe;
|
|
547
|
+
/**
|
|
548
|
+
* Runs `fn`, retrying on a `rate_limited` `SpacemoltError` up to
|
|
549
|
+
* `maxRateLimitRetries` times. The wait is jittered (see `jitteredDelayMs`)
|
|
550
|
+
* so accounts rate-limited together don't all retry in lockstep, and fires
|
|
551
|
+
* `onRateLimited` before each sleep. `command` identifies the call for that
|
|
552
|
+
* listener (`tool.action`, or `'authenticate'`).
|
|
553
|
+
*/
|
|
493
554
|
private withRateLimitRetry;
|
|
494
555
|
/** Serialize mutations: each runs only after the previous settles. */
|
|
495
556
|
private enqueueMutation;
|
package/dist/auth/clerk.d.ts
CHANGED
|
@@ -39,13 +39,14 @@ export interface ClerkRegistration {
|
|
|
39
39
|
registrationCode: string;
|
|
40
40
|
players: ClerkPlayer[];
|
|
41
41
|
}
|
|
42
|
+
type HeadersCtorInit = NonNullable<ConstructorParameters<typeof Headers>[0]>;
|
|
42
43
|
/**
|
|
43
44
|
* Headers that authorize calls to the Clerk-gated gameserver endpoints — an
|
|
44
45
|
* alternative to `apiKey` for callers whose credential isn't a static key.
|
|
45
46
|
* A factory is resolved fresh on every request, so short-lived tokens (e.g. a
|
|
46
47
|
* browser Clerk session's `getToken()` JWT) stay valid across reconnects.
|
|
47
48
|
*/
|
|
48
|
-
export type ClerkAuthHeaders =
|
|
49
|
+
export type ClerkAuthHeaders = HeadersCtorInit | (() => HeadersCtorInit | Promise<HeadersCtorInit>);
|
|
49
50
|
/** How Clerk-gated requests are authorized. Provide `apiKey`, `headers`, or both. */
|
|
50
51
|
export interface ClerkAuth {
|
|
51
52
|
/** Clerk API key, sent as `Authorization: Bearer`. Keep it secret. */
|
|
@@ -83,3 +84,4 @@ export declare class ClerkSource {
|
|
|
83
84
|
/** Mint a single-use WS login token for one owned player. */
|
|
84
85
|
mintWsToken(playerId: string): Promise<string>;
|
|
85
86
|
}
|
|
87
|
+
export {};
|
package/dist/client.d.ts
CHANGED
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
import { Account } from './account.js';
|
|
11
11
|
import { type AuthCredentials, type CredentialStore, type StoredAccount } from './auth/credentials.js';
|
|
12
12
|
import type { WebSocketFactory } from './transport/socket.js';
|
|
13
|
+
import type { InboundFrame, RawFrame } from './protocol.js';
|
|
13
14
|
import type { ReconnectOptions, RegisterParams, RegisterResult } from './account.js';
|
|
14
15
|
import { CatalogCache } from './data/catalog.js';
|
|
15
16
|
import { MapCache } from './data/map.js';
|
|
17
|
+
import { type MobileBaseLocation } from './data/mobile-base.js';
|
|
18
|
+
import { type StationList } from './data/stations.js';
|
|
16
19
|
import { type ClerkPlayer } from './auth/clerk.js';
|
|
17
20
|
import { type ConnectionClosedError } from './errors.js';
|
|
18
21
|
export interface SpacemoltClientOptions {
|
|
@@ -22,6 +25,10 @@ export interface SpacemoltClientOptions {
|
|
|
22
25
|
store?: CredentialStore;
|
|
23
26
|
/** Inject a WebSocket implementation (tests, custom runtimes). */
|
|
24
27
|
webSocketFactory?: WebSocketFactory;
|
|
28
|
+
/** Observe every outbound frame on every managed account (see Account). */
|
|
29
|
+
onSend?: (accountId: string, frame: InboundFrame) => void;
|
|
30
|
+
/** Observe every inbound frame on every managed account (see Account). */
|
|
31
|
+
onReceive?: (accountId: string, frame: RawFrame) => void;
|
|
25
32
|
/** Seed each account's state cache after auth (see Account). Default true. */
|
|
26
33
|
seedState?: boolean;
|
|
27
34
|
/**
|
|
@@ -94,8 +101,19 @@ export interface SpacemoltClientOptions {
|
|
|
94
101
|
* Generate one from the website; keep it secret.
|
|
95
102
|
*/
|
|
96
103
|
clerkApiKey?: string;
|
|
97
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Inject a `fetch` implementation (tests, custom runtimes). The per-call
|
|
106
|
+
* timeout is delivered via `init.signal` (an `AbortSignal`), not a
|
|
107
|
+
* separate argument — a wrapper must pass `init` through to the real
|
|
108
|
+
* `fetch` unchanged or it silently loses the timeout.
|
|
109
|
+
*/
|
|
98
110
|
fetchImpl?: typeof fetch;
|
|
111
|
+
/**
|
|
112
|
+
* Abort each HTTP data fetch (catalog, map, stations, mobile base) after this
|
|
113
|
+
* many ms, body included. Default 30000. Raise it on a slow link: the catalog
|
|
114
|
+
* is several MB.
|
|
115
|
+
*/
|
|
116
|
+
httpTimeoutMs?: number;
|
|
99
117
|
/**
|
|
100
118
|
* How long to wait for the server's `welcome` frame (post-WS-upgrade) and
|
|
101
119
|
* for a `logged_in`/error response to an auth attempt, before giving up.
|
|
@@ -196,6 +214,7 @@ export declare class SpacemoltClient {
|
|
|
196
214
|
private notifyAccountDisconnected;
|
|
197
215
|
/** HTTP origin used for bulk data fetches (catalog, map). */
|
|
198
216
|
get httpBaseUrl(): string;
|
|
217
|
+
private get httpOptions();
|
|
199
218
|
/**
|
|
200
219
|
* The bulk catalog. Fetched on first use and cached, then revalidated against
|
|
201
220
|
* the server once the cache is older than `catalogMaxAgeMs` (default 1h) so a
|
|
@@ -207,6 +226,10 @@ export declare class SpacemoltClient {
|
|
|
207
226
|
catalog(force?: boolean): Promise<CatalogCache>;
|
|
208
227
|
/** The static galaxy map, fetched once and cached. Pass `force` to refetch. */
|
|
209
228
|
map(force?: boolean): Promise<MapCache>;
|
|
229
|
+
/** The live station directory. Not cached — it changes as the game runs. */
|
|
230
|
+
stations(): Promise<StationList>;
|
|
231
|
+
/** The mobile base's current system. Not cached — it moves. */
|
|
232
|
+
mobileBase(): Promise<MobileBaseLocation>;
|
|
210
233
|
/** The credential store backing this client. */
|
|
211
234
|
get credentialStore(): CredentialStore;
|
|
212
235
|
/** Persist username/password credentials. Returns the account id (username). */
|
package/dist/data/catalog.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CatalogAchievement, CatalogDump, FacilityDefinition, Item, Module, Recipe, ShipClass, SkillDefinition } from '../generated/openapi/types.gen.js';
|
|
2
|
+
import { type FetchOptions } from './http.js';
|
|
2
3
|
/**
|
|
3
4
|
* Local copy of the bulk game catalog (`GET /api/catalog.json`).
|
|
4
5
|
*
|
|
@@ -52,10 +53,14 @@ export interface CatalogFetchResult {
|
|
|
52
53
|
* `etag` is passed and the server confirms the catalog is unchanged, this
|
|
53
54
|
* returns `{ notModified: true }` after a ~0-byte `304` — no multi-MB download
|
|
54
55
|
* or re-parse. Otherwise it returns the fresh catalog and its new `ETag`.
|
|
56
|
+
*
|
|
57
|
+
* Bounded by a request timeout and retried on a transient `429`/`503`; a
|
|
58
|
+
* failure that isn't a 304 throws `HttpError`. Pass `fetchImpl` to inject a
|
|
59
|
+
* `fetch` (tests, or an observability wrapper — see `httpGet`).
|
|
55
60
|
*/
|
|
56
|
-
export declare function fetchCatalogConditional(httpBaseUrl: string, etag?: string): Promise<CatalogFetchResult>;
|
|
57
|
-
/** Unconditionally fetch and normalize the catalog. */
|
|
58
|
-
export declare function fetchCatalog(httpBaseUrl: string): Promise<Catalog>;
|
|
61
|
+
export declare function fetchCatalogConditional(httpBaseUrl: string, etag?: string, opts?: FetchOptions): Promise<CatalogFetchResult>;
|
|
62
|
+
/** Unconditionally fetch and normalize the catalog. See `fetchCatalogConditional` for `fetchImpl`. */
|
|
63
|
+
export declare function fetchCatalog(httpBaseUrl: string, opts?: FetchOptions): Promise<Catalog>;
|
|
59
64
|
export declare class CatalogCache {
|
|
60
65
|
readonly catalog: Catalog;
|
|
61
66
|
/** The server `ETag` this catalog was fetched with, for conditional reloads. */
|
|
@@ -70,13 +75,13 @@ export declare class CatalogCache {
|
|
|
70
75
|
/** The server `ETag` this catalog was fetched with, for conditional reloads. */
|
|
71
76
|
etag?: string | undefined);
|
|
72
77
|
/** Fetch the catalog and wrap it in a cache. */
|
|
73
|
-
static load(httpBaseUrl: string): Promise<CatalogCache>;
|
|
78
|
+
static load(httpBaseUrl: string, opts?: FetchOptions): Promise<CatalogCache>;
|
|
74
79
|
/**
|
|
75
80
|
* Conditionally refresh via `If-None-Match`. Returns `this` unchanged when the
|
|
76
81
|
* server confirms the catalog is still current (a cheap `304`), or a new
|
|
77
82
|
* `CatalogCache` when the catalog has changed.
|
|
78
83
|
*/
|
|
79
|
-
revalidate(httpBaseUrl: string): Promise<CatalogCache>;
|
|
84
|
+
revalidate(httpBaseUrl: string, opts?: FetchOptions): Promise<CatalogCache>;
|
|
80
85
|
/** Server release this catalog was built for; `''` if the response omitted it. */
|
|
81
86
|
get version(): string;
|
|
82
87
|
ship(id: string): CatalogShip | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Options every public data fetcher accepts. */
|
|
2
|
+
export interface FetchOptions {
|
|
3
|
+
/** Inject a `fetch` implementation (tests, observability). Defaults to global `fetch`. */
|
|
4
|
+
fetchImpl?: typeof fetch;
|
|
5
|
+
/** Abort each request after this many ms, body included. Default 30000. */
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface HttpGetOptions {
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
/** Abort the request after this many ms (`AbortSignal.timeout`). Default 30000. */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Inject a `fetch` implementation — tests, custom runtimes, or an
|
|
14
|
+
* observability wrapper (status/bytes/timing/retries) a consumer supplies
|
|
15
|
+
* via `SpacemoltClientOptions.fetchImpl` / the per-call `fetchImpl` option.
|
|
16
|
+
* Defaults to global `fetch`.
|
|
17
|
+
*/
|
|
18
|
+
fetchImpl?: typeof fetch;
|
|
19
|
+
/** Response statuses besides 2xx to return rather than treat as a failure (e.g. 304). */
|
|
20
|
+
okStatuses?: readonly number[];
|
|
21
|
+
/**
|
|
22
|
+
* Total (un-jittered) base retry wait allowed across every attempt before
|
|
23
|
+
* giving up — not re-checked per attempt in isolation. Default
|
|
24
|
+
* `MAX_RETRY_WAIT_MS` (60000). Mainly for tests that need a small cap to
|
|
25
|
+
* exercise this without waiting real seconds.
|
|
26
|
+
*/
|
|
27
|
+
maxRetryWaitMs?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `GET url`, bounded by `timeoutMs` (headers and body both) and retried up to
|
|
31
|
+
* `MAX_RETRIES` times on `429`/`503` (waiting `Retry-After` when present,
|
|
32
|
+
* else a short backoff — either way, jittered). A timeout or network error is
|
|
33
|
+
* not retried — it throws the native error as-is, not an `HttpError`. Any
|
|
34
|
+
* other non-ok, non-`okStatuses` response throws an `HttpError`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function httpGet(url: string, opts?: HttpGetOptions): Promise<Response>;
|
package/dist/data/map.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MapData, MapDataSystem } from '../generated/openapi/types.gen.js';
|
|
2
|
+
import { type FetchOptions } from './http.js';
|
|
2
3
|
/**
|
|
3
4
|
* Local copy of the static galaxy map (`GET /api/map`).
|
|
4
5
|
*
|
|
@@ -15,13 +16,13 @@ import type { MapData, MapDataSystem } from '../generated/openapi/types.gen.js';
|
|
|
15
16
|
/** A system entry from `GET /api/map`. */
|
|
16
17
|
export type MapSystem = MapDataSystem;
|
|
17
18
|
export type GalaxyMap = MapData;
|
|
18
|
-
export declare function fetchMap(httpBaseUrl: string): Promise<GalaxyMap>;
|
|
19
|
+
export declare function fetchMap(httpBaseUrl: string, opts?: FetchOptions): Promise<GalaxyMap>;
|
|
19
20
|
export declare class MapCache {
|
|
20
21
|
readonly map: GalaxyMap;
|
|
21
22
|
private readonly bySystemId;
|
|
22
23
|
constructor(map: GalaxyMap);
|
|
23
24
|
/** Fetch the galaxy map and wrap it in a cache. */
|
|
24
|
-
static load(httpBaseUrl: string): Promise<MapCache>;
|
|
25
|
+
static load(httpBaseUrl: string, opts?: FetchOptions): Promise<MapCache>;
|
|
25
26
|
system(id: string): MapSystem | undefined;
|
|
26
27
|
get systems(): readonly MapSystem[];
|
|
27
28
|
/** Empire id -> display colour. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MobileBaseLocation } from '../generated/openapi/types.gen.js';
|
|
2
|
+
import { type FetchOptions } from './http.js';
|
|
2
3
|
/**
|
|
3
4
|
* Where the galaxy's single moving capital currently is
|
|
4
5
|
* (`GET /wheres-mobile-base`).
|
|
@@ -8,6 +9,6 @@ import type { MobileBaseLocation } from '../generated/openapi/types.gen.js';
|
|
|
8
9
|
* is the spec's published `MobileBaseLocation` (`{ system }`), a system id that
|
|
9
10
|
* indexes into the galaxy map.
|
|
10
11
|
*/
|
|
11
|
-
/** Fetch the mobile base's current system id. */
|
|
12
|
-
export declare function fetchMobileBase(httpBaseUrl: string): Promise<MobileBaseLocation>;
|
|
12
|
+
/** Fetch the mobile base's current system id. Pass `fetchImpl` to inject a `fetch` (tests, observability). */
|
|
13
|
+
export declare function fetchMobileBase(httpBaseUrl: string, opts?: FetchOptions): Promise<MobileBaseLocation>;
|
|
13
14
|
export type { MobileBaseLocation };
|
package/dist/data/stations.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FetchOptions } from './http.js';
|
|
1
2
|
/**
|
|
2
3
|
* Station directory (`GET /api/stations`).
|
|
3
4
|
*
|
|
@@ -53,5 +54,5 @@ export interface StationList {
|
|
|
53
54
|
stations: StationSummary[];
|
|
54
55
|
empires: StationEmpire[];
|
|
55
56
|
}
|
|
56
|
-
/** Fetch the public station directory. */
|
|
57
|
-
export declare function fetchStations(httpBaseUrl: string): Promise<StationList>;
|
|
57
|
+
/** Fetch the public station directory. Pass `fetchImpl` to inject a `fetch` (tests, observability). */
|
|
58
|
+
export declare function fetchStations(httpBaseUrl: string, opts?: FetchOptions): Promise<StationList>;
|
package/dist/errors.d.ts
CHANGED
|
@@ -24,6 +24,25 @@ export declare class SpacemoltError extends Error {
|
|
|
24
24
|
}
|
|
25
25
|
export declare function errorFromFrame(frame: ErrorFrame): SpacemoltError;
|
|
26
26
|
export declare function errorFromActionFrame(frame: ActionErrorFrame): SpacemoltError;
|
|
27
|
+
/**
|
|
28
|
+
* Raised by a bulk/live HTTP data fetch (catalog, map, stations, mobile-base)
|
|
29
|
+
* that didn't come back ok — after retries, for a retryable status. Message
|
|
30
|
+
* text matches the old bare-`Error` format (`GET <url> -> <status> <text>`).
|
|
31
|
+
*/
|
|
32
|
+
export declare class HttpError extends Error {
|
|
33
|
+
readonly status: number;
|
|
34
|
+
readonly url: string;
|
|
35
|
+
/** How many requests were made in total, including the one that finally failed. */
|
|
36
|
+
readonly attempts: number;
|
|
37
|
+
/** The server's `Retry-After` on the failing response, parsed to ms, if any. */
|
|
38
|
+
readonly retryAfterMs?: number;
|
|
39
|
+
constructor(message: string, opts: {
|
|
40
|
+
status: number;
|
|
41
|
+
url: string;
|
|
42
|
+
attempts: number;
|
|
43
|
+
retryAfterMs?: number;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
27
46
|
/** Raised against every in-flight request when the socket closes. */
|
|
28
47
|
export declare class ConnectionClosedError extends Error {
|
|
29
48
|
readonly code?: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The gameserver spec version this command surface was generated from. */
|
|
2
|
-
export declare const GENERATED_SPEC_VERSION = "v0.
|
|
2
|
+
export declare const GENERATED_SPEC_VERSION = "v0.613.4";
|
|
3
3
|
export type ToolName = "spacemolt" | "spacemolt_arena" | "spacemolt_auth" | "spacemolt_battle" | "spacemolt_citizenship" | "spacemolt_drone" | "spacemolt_facility" | "spacemolt_faction" | "spacemolt_faction_admin" | "spacemolt_faction_commerce" | "spacemolt_fleet" | "spacemolt_intel" | "spacemolt_market" | "spacemolt_salvage" | "spacemolt_ship" | "spacemolt_shipping" | "spacemolt_social" | "spacemolt_storage" | "spacemolt_transfer";
|
|
4
4
|
export type ActionName = "abandon_mission" | "accept" | "accept_ally" | "accept_invite" | "accept_mission" | "accept_peace" | "active" | "advance" | "allow_faction" | "allow_player" | "analyze_market" | "apply" | "attack" | "ban" | "base_cost" | "board" | "browse_for_sale" | "browse_ships" | "build" | "buy" | "buy_listed_ship" | "buy_listing" | "buy_ship_license" | "cancel" | "cancel_commission" | "cancel_listing" | "cancel_mission" | "cancel_order" | "cancel_ship_buy_order" | "cancel_ship_listing" | "captains_log_add" | "captains_log_delete" | "captains_log_get" | "captains_log_list" | "challenge" | "challenges" | "chat" | "claim" | "claim_prize" | "cloak" | "commission_quote" | "commission_ship" | "commission_status" | "complete_mission" | "completed_missions" | "craft" | "create" | "create_buy_order" | "create_note" | "create_role" | "create_sell_order" | "declare_war" | "decline" | "decline_invite" | "decline_mission" | "delete_note" | "delete_role" | "delete_room" | "deliver" | "deploy" | "deploy_outpost" | "deposit" | "disband" | "disembark" | "dismantle" | "dismantle_outpost" | "distress_signal" | "dock" | "edit" | "edit_role" | "engage" | "espionage" | "estimate_purchase" | "facility_set_description" | "faction_build" | "faction_dismantle" | "faction_list" | "faction_owned" | "faction_personnel" | "faction_upgrade" | "fight" | "find_route" | "forum_create_thread" | "forum_delete_reply" | "forum_delete_thread" | "forum_get_thread" | "forum_list" | "forum_reply" | "forum_upvote" | "found_station" | "garages" | "get" | "get_achievements" | "get_action_log" | "get_active_missions" | "get_base" | "get_cargo" | "get_chat_history" | "get_commands" | "get_empire_info" | "get_faction_achievements" | "get_guide" | "get_invites" | "get_location" | "get_map" | "get_missions" | "get_nearby" | "get_notes" | "get_notification_settings" | "get_notifications" | "get_player" | "get_poi" | "get_queue" | "get_ship" | "get_skills" | "get_state" | "get_status" | "get_system" | "get_system_agents" | "get_tax_estimate" | "get_trades" | "get_version" | "hunt" | "info" | "inspect" | "install_mod" | "insure" | "intel_status" | "invite" | "jettison" | "job_add" | "job_cancel" | "job_list" | "job_reorder" | "join" | "jump" | "kick" | "leave" | "list" | "list_for_sale" | "list_missions" | "list_passengers" | "list_ship_for_sale" | "list_ships" | "list_station_passengers" | "load" | "load_passenger" | "log" | "login" | "login_link" | "login_link_poll" | "login_token" | "logout" | "loot" | "mine" | "modify_order" | "mute_notifications" | "name" | "owned" | "pay_bounty" | "pay_debt" | "personal_build" | "personal_decorate" | "personal_visit" | "petition" | "place_ship_buy_order" | "policies" | "post" | "post_mission" | "prepay_tax" | "profile" | "promote" | "propose_ally" | "propose_peace" | "query_intel" | "query_trade_intel" | "quote" | "ranch_set_cull" | "ranch_status" | "read_note" | "recall" | "recruit_personnel" | "recycle" | "refit_ship" | "refuel" | "register" | "release" | "reload" | "remove_ally" | "remove_enemy" | "remove_faction" | "remove_player" | "rename_ship" | "renounce" | "repair" | "retreat" | "return" | "rooms" | "scan" | "scan_poi" | "scrap" | "scrap_ship" | "search_systems" | "self_destruct" | "sell" | "sell_ship_to_order" | "service_prize" | "set_access" | "set_auto_buy_fuel" | "set_build_policy" | "set_colors" | "set_description" | "set_enemy" | "set_home" | "set_market_fee" | "set_name" | "set_output_price" | "set_public" | "set_refuel_price" | "set_repair_price" | "set_service_access" | "set_status" | "stance" | "station_info" | "station_set_name" | "status" | "submit_intel" | "submit_trade_intel" | "subscribe_market" | "subscribe_observation" | "summary" | "supply_commission" | "survey_system" | "switch_ship" | "target" | "tax_estimate" | "tow" | "track" | "trade_accept" | "trade_cancel" | "trade_decline" | "trade_intel_status" | "trade_offer" | "transfer" | "transfer_personnel" | "travel" | "treat_personnel" | "types" | "unban" | "undock" | "uninstall_mod" | "unload" | "unload_passenger" | "unmute_notifications" | "unsubscribe_market" | "unsubscribe_observation" | "upgrade" | "upgrades" | "upload" | "use_item" | "view" | "view_completed_mission" | "view_market" | "view_orders" | "view_ship_buy_orders" | "visit_room" | "withdraw" | "withdraw_invite" | "wrecks" | "write_note" | "write_room";
|
|
5
5
|
export interface ActionParam {
|
|
@@ -2877,7 +2877,7 @@ export type DockResponse = {
|
|
|
2877
2877
|
commissions_active?: Array<CommissionActive>;
|
|
2878
2878
|
facility_note?: string;
|
|
2879
2879
|
fuel_warning?: string;
|
|
2880
|
-
gifts?: Array<
|
|
2880
|
+
gifts?: Array<StorageGift>;
|
|
2881
2881
|
gifts_count?: number;
|
|
2882
2882
|
gifts_note?: string;
|
|
2883
2883
|
gifts_truncated?: boolean;
|
|
@@ -5069,14 +5069,6 @@ export type GetWrecksResponse = {
|
|
|
5069
5069
|
count: number;
|
|
5070
5070
|
wrecks: Array<EnrichedWreck>;
|
|
5071
5071
|
};
|
|
5072
|
-
export type GiftNotification = {
|
|
5073
|
-
credits?: number;
|
|
5074
|
-
items?: Array<ItemQuantity>;
|
|
5075
|
-
message?: string;
|
|
5076
|
-
sender: string;
|
|
5077
|
-
sender_id: string;
|
|
5078
|
-
timestamp: string;
|
|
5079
|
-
};
|
|
5080
5072
|
export type GiftShipResponse = {
|
|
5081
5073
|
action: 'gift_ship';
|
|
5082
5074
|
/**
|
|
@@ -5166,6 +5158,9 @@ export type InspectPackageShipment = {
|
|
|
5166
5158
|
};
|
|
5167
5159
|
export type InspectResponse = {
|
|
5168
5160
|
base?: GetBaseResponse;
|
|
5161
|
+
/**
|
|
5162
|
+
* Present only when source is catalog. kind item or module → type items, entries are Item or Module. kind ship_class → type ships, entries are ShipClass. Other catalog types are not returned here.
|
|
5163
|
+
*/
|
|
5169
5164
|
catalog?: {
|
|
5170
5165
|
analysis?: RecipeAnalysis;
|
|
5171
5166
|
estimated_material_cost?: number;
|
|
@@ -5704,6 +5699,10 @@ export type MarketInsight = {
|
|
|
5704
5699
|
};
|
|
5705
5700
|
export type MarketItemBook = {
|
|
5706
5701
|
buy_orders: Array<MarketLevel>;
|
|
5702
|
+
/**
|
|
5703
|
+
* Item category of this item_id such as ore; refined; component; or module for a ship module. It is the category view_market reports for the same item_id. Omitted when the definition is no longer known.
|
|
5704
|
+
*/
|
|
5705
|
+
category?: string;
|
|
5707
5706
|
item_id: string;
|
|
5708
5707
|
item_name?: string;
|
|
5709
5708
|
sell_orders: Array<MarketLevel>;
|
|
@@ -10908,7 +10907,7 @@ export type V2Response = {
|
|
|
10908
10907
|
*/
|
|
10909
10908
|
error?: {
|
|
10910
10909
|
/**
|
|
10911
|
-
* Machine-readable error code. Session/auth codes: session_required (no X-Session-Id header), session_invalid (session not found or expired — create a new one with POST /api/v2/session then login), not_authenticated (session exists but not logged in). Other codes: rate_limited, command_error, invalid_params, invalid_json, payload_too_large, method_not_allowed, missing_action, unknown_command.
|
|
10910
|
+
* Machine-readable error code. Session/auth codes: session_required (no X-Session-Id header), session_invalid (session not found or expired — create a new one with POST /api/v2/session then login), not_authenticated (session exists but not logged in). Other codes: rate_limited, command_error, invalid_params, invalid_json, payload_too_large, method_not_allowed, missing_action, unknown_command. ip_timed_out is WebSocket-only: over HTTP an IP block returns a flat 429 body without this envelope: {"error": "rate_limited", "message", "retry_after", "limit": "ip_timeout", "scope": "ip"} plus a Retry-After header.
|
|
10912
10911
|
*/
|
|
10913
10912
|
code?: string;
|
|
10914
10913
|
/**
|
|
@@ -10922,7 +10921,7 @@ export type V2Response = {
|
|
|
10922
10921
|
*/
|
|
10923
10922
|
message?: string;
|
|
10924
10923
|
/**
|
|
10925
|
-
* Seconds to wait before retrying. Present on rate_limited errors.
|
|
10924
|
+
* Seconds to wait before retrying. Present on rate_limited errors from a rate-limit bucket (failed-login and bug-report rate_limited errors omit it), with the same value in the Retry-After header. details then carries limit (the bucket), scope (what the bucket is keyed on) and, for capped buckets, limit_per_min and current.
|
|
10926
10925
|
*/
|
|
10927
10926
|
retry_after?: number;
|
|
10928
10927
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -10,17 +10,18 @@
|
|
|
10
10
|
* subsequent milestones.
|
|
11
11
|
*/
|
|
12
12
|
export * from './protocol.js';
|
|
13
|
-
export { Account, type AccountOptions, type ReconnectOptions, type RegisterParams, type RegisterResult, type LoggedInPayload, } from './account.js';
|
|
13
|
+
export { Account, type AccountOptions, type ReconnectOptions, type RegisterParams, type RegisterResult, type LoggedInPayload, type RateLimitedInfo, } from './account.js';
|
|
14
14
|
export { SpacemoltClient, type SpacemoltClientOptions } from './client.js';
|
|
15
15
|
export { ClerkSource, mintWsToken, type ClerkAuth, type ClerkAuthHeaders, type ClerkPlayer, type ClerkRegistration, type ClerkSourceOptions, } from './auth/clerk.js';
|
|
16
16
|
export { type CredentialStore, type StoredAccount, type AuthCredentials, MemoryCredentialStore, } from './auth/credentials.js';
|
|
17
|
-
export { SpacemoltError, ConnectionClosedError, CLOSE_CODE, retryAfterMsFromClose } from './errors.js';
|
|
17
|
+
export { SpacemoltError, ConnectionClosedError, HttpError, CLOSE_CODE, retryAfterMsFromClose } from './errors.js';
|
|
18
18
|
export { StateCache } from './state/cache.js';
|
|
19
19
|
export { MarketCache, type MarketBook, type MarketItem } from './state/market.js';
|
|
20
20
|
export { ObservationCache, type ObservationView, type ObservedPlayer, type CloakedContact, } from './state/observation.js';
|
|
21
21
|
export { TypedEmitter, EventStream } from './events/emitter.js';
|
|
22
22
|
export { type Commands, type CommandDispatch, buildCommands } from './generated/commands.gen.js';
|
|
23
23
|
export { CatalogCache, fetchCatalog, fetchCatalogConditional, type Catalog, type CatalogAchievement, type CatalogFacility, type CatalogFetchResult, type CatalogItem, type CatalogRecipe, type CatalogShip, type CatalogSkill, } from './data/catalog.js';
|
|
24
|
+
export type { FetchOptions } from './data/http.js';
|
|
24
25
|
export { MapCache, fetchMap, httpBaseFromWs, type GalaxyMap, type MapSystem } from './data/map.js';
|
|
25
26
|
export { fetchStations, type StationEmpire, type StationList, type StationSummary } from './data/stations.js';
|
|
26
27
|
export { fetchMobileBase, type MobileBaseLocation } from './data/mobile-base.js';
|