From 7ab49cfca098c7fe23504683f9df9c017677baf9 Mon Sep 17 00:00:00 2001 From: Natalie Rose Date: Sun, 27 Sep 2026 12:32:03 +1000 Subject: [PATCH] Schema updates (empire, inbox + other catchup) --- CLAUDE.md | 34 ++-- README.md | 46 ++--- lib/__utils__/validate-schema.ts | 8 +- lib/endpoints/alliance.test.ts | 2 +- lib/endpoints/body.test.ts | 2 +- lib/endpoints/buildings/entertainment.test.ts | 2 +- .../buildings/oracle-of-anid.test.ts | 3 +- .../buildings/waste-recycling.test.ts | 2 +- lib/endpoints/empire.test.ts | 21 ++- lib/endpoints/inbox.test.ts | 12 ++ lib/endpoints/map.test.ts | 12 +- lib/types/empire.ts | 7 +- lib/types/inbox.ts | 17 +- lib/types/schema.ts | 171 +++++++++++------- lib/types/status.ts | 3 + package-lock.json | 14 +- package.json | 6 +- 17 files changed, 212 insertions(+), 150 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2f6fced..d40a26b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,8 @@ The repo serves two purposes at once: - `npx jest lib/endpoints/captcha.test.ts` — run a single test file - `npx jest -t "drain"` — run tests matching a name - `npm run build` — library build via `vite build` (also emits `.d.ts` via vite-plugin-dts) -- `npm run lint` / `npm run lint:fix` — Prettier check/write (there is no ESLint in this repo) +- `npm run format` / `npm run format:fix` — Prettier check/write over the whole repo (there is no ESLint in this repo) +- `npm run generate:types` — regenerate `lib/types/schema.ts` from the `@tlecommunity/api-spec` devDependency - `npm run prepare` — full pre-publish pipeline (`types` → `test` → `build`), runs automatically on `npm publish` - Requires Node >=24, npm >=10 (`.nvmrc` pins 24) @@ -52,11 +53,14 @@ broader than a single file. - `lib/index.ts` is the package entry point, exporting `Lacuna` (default), `util`, and `types`. - `Lacuna` (`lib/lacuna.ts`) is the root object. Its constructor builds the core - infra — `Log`, `Config`, `Server`, `Session` — and then one instance per API - module (`body`, `empire`, `captcha`, `shipyard`, `spacePort`, `stats`, - `essentiaVein`, plus `planetaryCommand`/`university` via the generic `Building` - endpoint). Every endpoint receives the parent `Lacuna` instance in its - constructor and reaches back through it (`this.lacuna.server`, `.session`, `.log`). + infra — `Log`, `Config`, `Responses`, `Server`, `Session` — and then one + instance per API module (`alliance`, `body`, `captcha`, `empire`, `inbox`, + `map`, `stats`, and one per building type, e.g. `shipyard`, `spacePort`, + `planetaryCommand`; `university` has no extra methods so it's a plain + `Building`). `Lacuna#buildingFromUrl` maps a building's module slug to its + subclass via `BUILDING_SUBCLASSES`, falling back to `Building`. Every + endpoint receives the parent `Lacuna` instance in its constructor and + reaches back through it (`this.lacuna.server`, `.session`, `.log`). - `lib/core/endpoint.ts` — abstract `Endpoint` base class every API module extends; provides `callWithSession` / `callWithoutSession`, which delegate to `Server#call`. - `lib/core/server.ts` — the JSON-RPC transport. Builds the request body @@ -78,19 +82,25 @@ broader than a single file. about errors on its own; that's what subscribers are for. - `lib/core/session.ts` — holds the session id set by `Lacuna#authenticate`. - `lib/core/config.ts` — holds `serverUrl` / `apiKey` / `fingerprintToken`. -- `lib/endpoints/*.ts` — one file per top-level API module (`empire`, `body`, - `captcha`, `stats`, `building`). Each is a thin class extending `Endpoint` whose +- `lib/endpoints/*.ts` — one file per top-level API module (`alliance`, `body`, + `captcha`, `empire`, `inbox`, `map`, `stats`, `building`). Each is a thin class extending `Endpoint` whose methods just forward typed params to `callWithSession`/`callWithoutSession`, in the exact positional-argument order the server RPC expects. - `lib/endpoints/buildings/*.ts` — building-specific modules (`essentia-vein`, - `shipyard`, `space-port`) extend the generic `Building` endpoint + `shipyard`, `space-port`, ...) extend the generic `Building` endpoint (`lib/endpoints/building.ts`) rather than `Endpoint` directly, inheriting `view`/`upgrade`/`downgrade`/`demolish`. - `lib/types/*.ts` — one file per endpoint module with `*Params`/`*Response` interfaces, mirroring the `lib/endpoints/` structure 1:1 (imported as - `import * as B from '../types/building'`). `lib/types/index.ts` re-exports all - of them plus shared aliases (`ServerDate`, `EmpireName`, `IntBool`, - `MapLocation`, `LacunaConfig`). + `import * as B from '../types/building'`), plus `status.ts` for the shared + status block. `lib/types/index.ts` re-exports all of them plus shared aliases + (`ServerDate`, `EmpireName`, `IntBool`, `MapLocation`, `LacunaConfig`). +- `lib/types/schema.ts` is **generated** from `@tlecommunity/api-spec` (`npm run +generate:types`) — don't hand-edit it. The hand-written types are the public + API; where they deliberately differ from the spec, the divergence is listed in + README "Spec Discrepancies". After bumping the spec, diff `schema.ts` and carry + relevant changes into the hand-written types. Tests check live responses + against the spec via `expectMatchesApiSchema` (`lib/__utils__/validate-schema.ts`). ### Adding a new endpoint method diff --git a/README.md b/README.md index ce9c0eb..58d6cad 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ for the `/v2` Rails API) via `openapi-typescript` (`npm run generate:types` → `lib/types/schema.ts`). Where the spec conflicts with this client's existing, tested behavior, the tested behavior wins. Differences found so far, for whoever owns the spec/backend. Everything below was re-verified against both -the spec and the live server as of `@tlecommunity/api-spec` 1.4.0; entries -resolved by that release (and the accompanying backend work) have been -removed. 1.4.0 normalised every id field to `integer`; the client already +the spec and the live server as of `@tlecommunity/api-spec` 1.4.0, with +later releases checked entry-by-entry as they land; entries resolved by a +release (and the accompanying backend work) have been removed. 1.4.0 normalised every id field to `integer`; the client already sent/typed ids as `number`, and `fixNumbers` coerces the ids the legacy server still stringifies on the wire, so that change was a no-op here. @@ -75,70 +75,64 @@ desc,population desc" via package Lacuna::DB::Result::Log::Alliance`; subscribers (see "Error handling" below) along with a retry callback, but it ships no captcha solver, so the test still skips rather than fails. -13. **A named-only method's session id is passed where its first real - argument belongs.** The v2 layer hands methods it can't map positionally - their whole request as one hash, session id folded in, but the legacy - backend still reads the session id as the first _positional_ argument - - so `empire/authorize_sitters`, `empire/deauthorize_sitters` and - `empire/redefine_species` reject a perfectly valid session with 1006, - and `empire/edit_profile` dies before it can format an RPC error at all - (the client sees its own "could not communicate", code `-1`, because - what comes back is an HTML 500 rather than JSON). Asserted as-is in - `empire.test.ts` so the suite flips when the backend is fixed. -14. **`/v2/empire/update_species` is documented as requiring a session but +13. **`/v2/empire/update_species` is documented as requiring a session but must be called _without_ one.** It identifies the empire by `empire_id` (there is no session to be had until the empire is founded), and the v2 layer prepends the session id to the positional argument list whenever one is sent, which shifts every argument along by one. `updateSpecies` therefore uses `callWithoutSession`. -15. **`/v2/empire/redefine_species_limits` is documented as _not_ requiring +14. **`/v2/empire/redefine_species_limits` is documented as _not_ requiring a session but does** - the limits are computed from the current empire. Its `reason` is `null` rather than absent when a redefinition is allowed, which is why the schema validator now widens `reason` nodes to accept `null` alongside array/number/string. -16. **`/v2/empire/set_status_message` returns the status block itself** +15. **`/v2/empire/set_status_message` returns the status block itself** (`{empire, server}`, exactly like `get_status`), not the `{status}` wrapper the spec declares. -17. **`/v2/empire/authorize_sitters` returns `sitters`, not `auths`** - it's +16. **`/v2/empire/authorize_sitters` returns `sitters`, not `auths`** - it's `view_authorized_sitters`' response plus `rejected_ids`, and those rejected ids are the bare ids/names as passed, not the - `{id, reason}` objects the spec declares. `deauthorize_sitters` likewise - returns the sitter list rather than a bare `{status}`, and accepts an - undocumented `deauthorize_all` flag as an alternative to `empires`. -18. **The empire profile methods send fields the spec doesn't document, and + `{id, reason}` objects the spec declares. (Last verified before the + endpoint started demanding a captcha, which the test suite can't solve.) +17. **The empire profile methods send fields the spec doesn't document, and document fields they don't always send.** `view_profile` adds `skip_excavator_destroyed`, `skip_excavator_artifact`, `skip_excavator_replace_msg` and `dont_replace_excavator`; `view_public_profile` adds `alliance` and omits the spec-required `public` on each medal (the spec reuses `view_profile`'s medal schema, but every medal in a _public_ profile is public by definition). -19. **`map` star `influence` is spec-required `number` but arrives `null`** +18. **`map` star `influence` is spec-required `number` but arrives `null`** for stars outside any station's jurisdiction - `get_star_map`'s `stars[].influence` and `get_star` / `get_star_by_name` / `get_star_by_xy`'s `star.influence`. New in 1.4.0 (the field was added that release). The client types it `number | null`. -20. **`map/view_laws` law items still carry the misspelled key +19. **`map/view_laws` law items still carry the misspelled key `descripition`.** 1.4.0 renamed it to `description` and made it required in the spec, but the live server hasn't been updated. (`parliament`'s own `view_laws` already sends `description`.) -21. **`wasterecycling` / `wasteexchanger` `view`'s `recycle` block is +20. **`wasterecycling` / `wasteexchanger` `view`'s `recycle` block is spec-too-strict.** `seconds_remaining`, `water`, `energy` and `ore` are only present while a recycling run is in progress, but the spec marks them required; and `seconds_per_resource` is fractional (e.g. `1.86`) while 1.4.0 types it `integer` (should be `number`). The client types the four conditional fields optional and `seconds_per_resource` as `number`. -22. **`body/get_buildable` diverges from its spec on both sides.** The `tag` +21. **`body/get_buildable` diverges from its spec on both sides.** The `tag` param is spec-optional but the `/v2` layer rejects the call without it (pass `''` for "no filter"), and the response omits the spec-required top-level `max_items_in_build_queue`, sending `build_queue: {max, current}` instead. -23. **Building-list rows omit the spec-required per-row `id`.** 1.4.0 added a +22. **Building-list rows omit the spec-required per-row `id`.** 1.4.0 added a required `id` to each `buildings_list` / `buildings_resources_list` entry, but the server still only carries the id as the map key. `body.getBuildings`, `body.getBuildingsResources` and the `Building` `build`/`upgrade`/ `downgrade`/`demolish`/`repair` methods splice the key back onto each row (`util.spliceBuildingIds`), so callers see a self-describing row and the schema check passes. +23. **Status-block body lists carry an undocumented `type`.** Every entry of + `get_status`'s `empire.bodies.colonies` / `mystations` / `ourstations` / + `babies.*.planets` (the `bodies_list` component, new in 1.6.10) includes + the body's image type (e.g. `p35`), which the spec doesn't declare. The + client types it on `BodiesList`. Most of #1, #3, #4 and #5 are handled automatically by the schema validator behind the tests (`lib/__utils__/validate-schema.ts`) rather than by one-off diff --git a/lib/__utils__/validate-schema.ts b/lib/__utils__/validate-schema.ts index e4c2880..773eeec 100644 --- a/lib/__utils__/validate-schema.ts +++ b/lib/__utils__/validate-schema.ts @@ -17,15 +17,15 @@ function widenTypeList(types: string[]) { * genuinely the wrong shape, only these specific, documented divergences * (see README "Spec Discrepancies"): * - * - #3: numeric-looking *string* values get turned into JS `number`s + * - #1: numeric-looking *string* values get turned into JS `number`s * client-side by `fixNumbers()` (lib/core/util.ts), regardless of what the * spec says the field's type is. Every `"type": "string"` node is widened * to also accept `number`. - * - #15: boolean-flag fields (e.g. `buildable.*.can`) are typed `boolean` in + * - #3: boolean-flag fields (e.g. `buildable.*.can`) are typed `boolean` in * the spec but the server actually sends the IntBool convention (`0`/`1`) * used pervasively elsewhere in this same API. Every `"type": "boolean"` * node is widened to also accept `number`. - * - #16: `reason` fields (e.g. `building.upgrade.reason`, + * - #4: `reason` fields (e.g. `building.upgrade.reason`, * `building.downgrade.reason`, `buildable.*.reason`) hold either a * `[code, message]` pair or the literal number `0` for "no reason" - * inconsistently typed `array` in some places and `string` in others by @@ -34,7 +34,7 @@ function widenTypeList(types: string[]) { * `null` to the pile. Every schema node *named* `reason` is widened to * accept `array`, `number`, `string`, and `null` regardless of its * declared type. - * - #17: fields of any shape (arrays, objects, and plain strings - e.g. + * - #5: fields of any shape (arrays, objects, and plain strings - e.g. * `building.pending_build`, `spaceport/get_fleet_for`'s `ships`, * `alliance/view_profile`'s `profile.description`) are sent as `null` * instead of `[]`/`{}`/`""` when empty/absent - a common Rails-ism the diff --git a/lib/endpoints/alliance.test.ts b/lib/endpoints/alliance.test.ts index af3b060..02bb234 100644 --- a/lib/endpoints/alliance.test.ts +++ b/lib/endpoints/alliance.test.ts @@ -30,6 +30,6 @@ test('viewProfile', async () => { expect(result?.profile).toBeDefined(); // Live server sends `profile.description: null` for an alliance with no // description set; tolerated by validate-schema.ts's blanket - // nullable-string relaxation (README "Spec Discrepancies" #17). + // nullable-string relaxation (README "Spec Discrepancies" #5). expectMatchesApiSchema('alliance', 'view_profile', result); }); diff --git a/lib/endpoints/body.test.ts b/lib/endpoints/body.test.ts index 5a6046c..3fdf502 100644 --- a/lib/endpoints/body.test.ts +++ b/lib/endpoints/body.test.ts @@ -58,7 +58,7 @@ test('getBuildable', async () => { expect(entry.production).toBeDefined(); // Spec bug: the spec's response requires a top-level // `max_items_in_build_queue`, but the server sends `build_queue: {max, - // current}` instead. See README #22. + // current}` instead. See README #21. expectMatchesApiSchema('body', 'get_buildable', result, [ { keyword: 'required', instancePath: '' }, ]); diff --git a/lib/endpoints/buildings/entertainment.test.ts b/lib/endpoints/buildings/entertainment.test.ts index 738678a..e2a5f13 100644 --- a/lib/endpoints/buildings/entertainment.test.ts +++ b/lib/endpoints/buildings/entertainment.test.ts @@ -55,7 +55,7 @@ test('getLotteryVotingOptions', async () => { // 1016). The client surfaces that to subscribers via Lacuna#onResponse // with a retry callback, but ships no captcha solver, so nothing here // can answer the challenge. Skip rather than fail. See README "Spec - // Discrepancies" #25. + // Discrepancies" #12. console.warn('Skipping schema check: get_lottery_voting_options requires a captcha', error); return; } diff --git a/lib/endpoints/buildings/oracle-of-anid.test.ts b/lib/endpoints/buildings/oracle-of-anid.test.ts index a3d524c..ccb3add 100644 --- a/lib/endpoints/buildings/oracle-of-anid.test.ts +++ b/lib/endpoints/buildings/oracle-of-anid.test.ts @@ -53,8 +53,7 @@ test('getStar', async () => { } = await lacuna.empire.getStatus(); const bodyId = empire.home_planet_id; - // @ts-expect-error missing star_id type from colonies list - const starId = empire.bodies.colonies.find(({ id }) => id === bodyId).star_id; + const starId = empire.bodies.colonies.find(({ id }) => id === bodyId)!.star_id; const { result } = await lacuna.oracleOfAnid.getStar({ building_id: buildingId, diff --git a/lib/endpoints/buildings/waste-recycling.test.ts b/lib/endpoints/buildings/waste-recycling.test.ts index dbc082d..a19940c 100644 --- a/lib/endpoints/buildings/waste-recycling.test.ts +++ b/lib/endpoints/buildings/waste-recycling.test.ts @@ -29,7 +29,7 @@ test('view', async () => { expect(result?.building).toBeDefined(); expect(result?.building.name).toBe('Waste Recycling Center'); - // Spec bugs (see README #23): + // Spec bugs (see README #20): // - the `recycle` block only carries `seconds_remaining` / `water` / // `energy` / `ore` while a run is in progress, but the spec marks them // required; diff --git a/lib/endpoints/empire.test.ts b/lib/endpoints/empire.test.ts index 1249ff3..fe295ac 100644 --- a/lib/endpoints/empire.test.ts +++ b/lib/endpoints/empire.test.ts @@ -312,29 +312,30 @@ describe('methods checked without being carried out', () => { expect(error?.code).toBe(1002); expect(error?.message).toBe('The key you provided is invalid. Password not reset.'); }); -}); -// The v2 API hands a named-only method its whole request as a single hash, -// session id included, but the legacy backend still expects the session id as -// the first *positional* argument - so these reject a perfectly good session. -// A backend bug rather than a client one; see README "Spec Discrepancies". -describe('named-only methods the v2 API misroutes', () => { test('authorizeSitters', async () => { const lacuna = await getLacuna(); - // A name nobody owns, so there's nothing to authorize even if it lands. + // An id nobody owns, so there's nothing to authorize even if it lands. const { error } = await lacuna.empire.authorizeSitters({ - empires: ['A Name Nobody Has Taken'], + empires: [-99], }); + if (error?.code === 1016) { + console.warn('Skipping schema check: authorize_sitters requires a captcha', error); + return; + } + expect(error?.code).toBe(1006); }); test('deauthorizeSitters', async () => { const lacuna = await getLacuna(); - const { error } = await lacuna.empire.deauthorizeSitters({ empires: [999999999] }); + // An id nobody owns, so no real sitter is removed. + const { result } = await lacuna.empire.deauthorizeSitters({ empires: [999999999] }); - expect(error?.code).toBe(1006); + expect(Array.isArray(result?.sitters)).toBe(true); + expectMatchesApiSchema('empire', 'deauthorize_sitters', result); }); }); diff --git a/lib/endpoints/inbox.test.ts b/lib/endpoints/inbox.test.ts index fca54dc..1c09c47 100644 --- a/lib/endpoints/inbox.test.ts +++ b/lib/endpoints/inbox.test.ts @@ -47,3 +47,15 @@ test('viewUnread', async () => { expect(result?.message_count).toBeDefined(); expectMatchesApiSchema('inbox', 'view_unread', result); }); + +test('trashMessagesWhere', async () => { + // A subject no message has, so nothing is actually trashed. + const { result } = await lacuna.inbox.trashMessagesWhere({ + spec: [{ subject: 'A Subject Nobody Has Used%' }], + save_ids: true, + }); + + expect(result?.deleted_count).toBe(0); + expect(result?.deleted).toEqual([]); + expectMatchesApiSchema('inbox', 'trash_messages_where', result); +}); diff --git a/lib/endpoints/map.test.ts b/lib/endpoints/map.test.ts index 749459d..bc1865f 100644 --- a/lib/endpoints/map.test.ts +++ b/lib/endpoints/map.test.ts @@ -30,9 +30,9 @@ test('getStarMap', async () => { expect(result?.stars).toBeDefined(); // Spec bug: live star items with no bodies omit the spec-required // `bodies` field entirely rather than sending `[]`. See README "Spec - // Discrepancies" #20. + // Discrepancies" #19. // Spec bug: `influence` is spec-required `number` but the server sends - // `null` for stars outside any station's jurisdiction. See README #19. + // `null` for stars outside any station's jurisdiction. See README #18. expectMatchesApiSchema('map', 'get_star_map', result, [ { keyword: 'required', instancePath: '/stars/*' }, { keyword: 'type', instancePath: '/stars/*/influence' }, @@ -60,7 +60,7 @@ test('getStar', async () => { expect(result?.star).toBeDefined(); // Spec bug: `influence` is spec-required `number` but arrives `null` for - // stars outside any station's jurisdiction. See README #19. + // stars outside any station's jurisdiction. See README #18. expectMatchesApiSchema('map', 'get_star', result, [ { keyword: 'type', instancePath: '/star/influence' }, ]); @@ -76,7 +76,7 @@ test('getStarByName', async () => { expect(result?.star).toBeDefined(); // Spec bug: `influence` is spec-required `number` but arrives `null` for - // stars outside any station's jurisdiction. See README #19. + // stars outside any station's jurisdiction. See README #18. expectMatchesApiSchema('map', 'get_star_by_name', result, [ { keyword: 'type', instancePath: '/star/influence' }, ]); @@ -92,7 +92,7 @@ test('getStarByXy', async () => { expect(result?.star).toBeDefined(); // Spec bug: `influence` is spec-required `number` but arrives `null` for - // stars outside any station's jurisdiction. See README #19. + // stars outside any station's jurisdiction. See README #18. expectMatchesApiSchema('map', 'get_star_by_xy', result, [ { keyword: 'type', instancePath: '/star/influence' }, ]); @@ -127,7 +127,7 @@ test('viewLaws', async () => { expect(result?.laws).toBeDefined(); // Spec bug: 1.4.0 renamed the law key `descripition` -> `description` and // made it required, but the live server still sends the misspelled key. - // See README #20. + // See README #19. expectMatchesApiSchema('map', 'view_laws', result, [ { keyword: 'required', instancePath: '/laws/*' }, ]); diff --git a/lib/types/empire.ts b/lib/types/empire.ts index f42c46b..4947fc3 100644 --- a/lib/types/empire.ts +++ b/lib/types/empire.ts @@ -464,8 +464,11 @@ export interface AuthorizeSittersResponse extends ViewAuthorizedSittersResponse export interface DeauthorizeSittersParams { /** The ids (not names) of specific empires being removed. */ empires?: number[]; - /** Removes every current sitter. Undocumented in the spec. */ - deauthorize_all?: IntBool; + /** + * Removes every current sitter. Ignored if `empires` is given. The server + * treats any value (even 0) as true, so omit it rather than sending 0. + */ + deauthorize_all?: 1; } /** `view_authorized_sitters`' response, minus whoever was just removed. */ diff --git a/lib/types/inbox.ts b/lib/types/inbox.ts index e8f7c65..05633b4 100644 --- a/lib/types/inbox.ts +++ b/lib/types/inbox.ts @@ -1,20 +1,21 @@ import type { components } from './schema'; export type MessageTag = - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' - | 'Trade'; + | 'Trade' + | 'Tutorial'; export interface ViewInboxParams { options?: { @@ -234,11 +235,13 @@ export interface TrashMessagesWhereParams { subject?: string | Array; from?: Array; }>; - return_ids?: boolean; + /** Gather the trashed message ids into `deleted`. Off by default. */ + save_ids?: boolean; } export interface TrashMessagesWhereResponse { - deleted?: Array; + /** Only present when `save_ids` is true. */ + deleted?: Array; deleted_count: number; status: { server: components['schemas']['server_status']; diff --git a/lib/types/schema.ts b/lib/types/schema.ts index 6d3074b..e3818cb 100644 --- a/lib/types/schema.ts +++ b/lib/types/schema.ts @@ -28182,13 +28182,18 @@ export interface paths { requestBody?: { content: { 'application/json': { - /** @description The ids (not names) of specific empires being removed. */ - empires: string[]; + /** @description The ids (not names) of specific empires being removed. Takes precedence over deauthorize_all. */ + empires?: string[]; + /** + * @description Removes every authorized sitter. Ignored if empires is given. Any value (even 0) is treated as true, so omit it rather than sending 0. + * @enum {integer} + */ + deauthorize_all?: 1; }; }; }; responses: { - /** @description Removes sitters from being permitted to sit this account. */ + /** @description Removes sitters from being permitted to sit this account. Returns the same list as view_authorized_sitters. */ 200: { headers: { [name: string]: unknown; @@ -28198,6 +28203,11 @@ export interface paths { id?: number; jsonrpc?: string; result?: { + sitters: { + id: number; + name: string; + expiry: string; + }[]; status: { server: components['schemas']['server_status']; empire?: components['schemas']['empire_status']; @@ -29288,7 +29298,7 @@ export interface paths { empire?: components['schemas']['empire_status']; body?: components['schemas']['body_status']; }; - ducks_quacked: number; + ducks_quacked: number | null; }; }; }; @@ -38145,20 +38155,21 @@ export interface paths { page_number?: number; /** @description Only messages carrying at least one of these tags are returned. */ tags?: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description The name or id of a baby empire whose inbox to view. */ empire?: string; @@ -38191,20 +38202,21 @@ export interface paths { has_replied: 1 | 0; body_preview: string; tags: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; }[]; message_count: number; @@ -38263,20 +38275,21 @@ export interface paths { page_number?: number; /** @description Only messages carrying at least one of these tags are returned. */ tags?: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description The name or id of a baby empire whose inbox to view. */ empire?: string; @@ -38309,20 +38322,21 @@ export interface paths { has_replied: 1 | 0; body_preview: string; tags: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; }[]; message_count: number; @@ -38381,20 +38395,21 @@ export interface paths { page_number?: number; /** @description Only messages carrying at least one of these tags are returned. */ tags?: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description The name or id of a baby empire whose inbox to view. */ empire?: string; @@ -38427,20 +38442,21 @@ export interface paths { has_replied: 1 | 0; body_preview: string; tags: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; }[]; message_count: number; @@ -38499,20 +38515,21 @@ export interface paths { page_number?: number; /** @description Only messages carrying at least one of these tags are returned. */ tags?: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description The name or id of a baby empire whose inbox to view. */ empire?: string; @@ -38545,20 +38562,21 @@ export interface paths { has_replied: 1 | 0; body_preview: string; tags: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; }[]; message_count: number; @@ -38617,20 +38635,21 @@ export interface paths { page_number?: number; /** @description Only messages carrying at least one of these tags are returned. */ tags?: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description The name or id of a baby empire whose inbox to view. */ empire?: string; @@ -38663,20 +38682,21 @@ export interface paths { has_replied: 1 | 0; body_preview: string; tags: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; }[]; message_count: number; @@ -38766,20 +38786,21 @@ export interface paths { /** @description The complete list of empires who received the message. `to`/`to_id` above is just the empire that owns this particular copy of the message. */ recipients: number[]; tags: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description At most one of each attachment type per message. */ attachments?: { @@ -38989,20 +39010,21 @@ export interface paths { spec: { /** @description A message carrying any of these tags is eligible. Defaults to all tags. */ tags?: ( - | 'Tutorial' - | 'Correspondence' - | 'Medal' - | 'Intelligence' | 'Alert' + | 'Alliance' | 'Attack' | 'Colonization' | 'Complaint' + | 'Correspondence' | 'Excavator' + | 'Intelligence' + | 'Medal' | 'Mission' | 'Parliament' | 'Probe' | 'Spies' | 'Trade' + | 'Tutorial' )[]; /** @description A subject to match. Use % as a wildcard, e.g. "Pass:%". An array is treated as a set of exact subjects, OR-ed together. Defaults to any subject. */ subject?: string | string[]; @@ -39010,7 +39032,7 @@ export interface paths { from?: string[]; }[]; /** @description When true, the trashed message ids are gathered and returned in `deleted`. Off by default, since gathering them adds server work and response size; `deleted_count` is returned either way. */ - return_ids?: boolean; + save_ids?: boolean; }; }; }; @@ -39025,7 +39047,7 @@ export interface paths { id?: number; jsonrpc?: string; result?: { - /** @description Only present when return_ids is true. */ + /** @description Only present when save_ids is true. */ deleted?: number[]; deleted_count: number; status: { @@ -99579,25 +99601,40 @@ export interface components { latest_message_id: number; /** @description The bodies known to the empire, grouped by relationship. */ bodies: { - /** @description The colonies owned by the empire. */ - colonies: { - empire_name: string; - x: number; - name: string; - empire_id: number; - star_id: number; - star_name: string; - y: number; - id: number; - orbit: number; - zone: string; - }[]; + colonies: components['schemas']['bodies_list']; + mystations?: components['schemas']['bodies_list']; + ourstations?: components['schemas']['bodies_list']; + /** @description Empires that can be managed by this empire, keyed by id */ + babies?: { + [key: string]: { + /** @description The id of the baby empire */ + id: number; + /** @description The id of the alliance the baby empire is a member of */ + alliance_id?: number; + /** @description The date whereupon this baby will case to be managed by the empire */ + sitter_expiry: string; + planets: components['schemas']['bodies_list']; + }; + }; }; /** @description The resource cost of the next space station. */ next_station_cost: number; /** @description The highest level the University has reached. */ tech_level: number; }; + /** @description A list of colonies or stations. */ + bodies_list: { + empire_name: string; + x: number; + name: string; + empire_id: number; + star_id: number; + star_name: string; + y: number; + id: number; + orbit: number; + zone: string; + }[]; /** @description The resource and time cost of a build or upgrade. */ building_cost: { /** @description Food spent from storage. */ diff --git a/lib/types/status.ts b/lib/types/status.ts index 3268208..df36dbd 100644 --- a/lib/types/status.ts +++ b/lib/types/status.ts @@ -7,9 +7,12 @@ export interface BodiesList { id: number; name: string; orbit: number; + star_id: number; + star_name: string; x: number; y: number; zone: string; + /** Sent by the server but undocumented in the spec. See README "Spec Discrepancies". */ type: string; } diff --git a/package-lock.json b/package-lock.json index f3d53c7..ea9f9a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "openapi-fetch": "^0.17.0" }, "devDependencies": { - "@tlecommunity/api-spec": "^1.6.8", + "@tlecommunity/api-spec": "^1.6.10", "@types/jest": "^30.0.0", "ajv": "^8.20.0", "dotenv": "^17.4.2", @@ -1960,9 +1960,9 @@ } }, "node_modules/@tlecommunity/api-spec": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@tlecommunity/api-spec/-/api-spec-1.6.8.tgz", - "integrity": "sha512-ar2rkqT683s4BylG4GIMLp9/H3b/52tRCg3v22Zs3ipe/sLoGxnSGiSQM96+2qQxWKQGoHL4+omC3j8md2dgUg==", + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@tlecommunity/api-spec/-/api-spec-1.6.10.tgz", + "integrity": "sha512-d1FWzeoM6P8AGhNST1l4EJMnsZwdnv7yLXYAsij5/AhNR3ZkTx/oKFez0VVQ3hABwzk9mHp54pAdKXaUzDyfbA==", "dev": true, "license": "GPL-3.0-only", "engines": { @@ -7469,9 +7469,9 @@ } }, "@tlecommunity/api-spec": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@tlecommunity/api-spec/-/api-spec-1.6.8.tgz", - "integrity": "sha512-ar2rkqT683s4BylG4GIMLp9/H3b/52tRCg3v22Zs3ipe/sLoGxnSGiSQM96+2qQxWKQGoHL4+omC3j8md2dgUg==", + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@tlecommunity/api-spec/-/api-spec-1.6.10.tgz", + "integrity": "sha512-d1FWzeoM6P8AGhNST1l4EJMnsZwdnv7yLXYAsij5/AhNR3ZkTx/oKFez0VVQ3hABwzk9mHp54pAdKXaUzDyfbA==", "dev": true }, "@tybys/wasm-util": { diff --git a/package.json b/package.json index 1e88cd1..04b76d5 100644 --- a/package.json +++ b/package.json @@ -31,13 +31,13 @@ "test": "jest --coverage --runInBand", "types": "tsc --noEmit", "generate:types": "openapi-typescript node_modules/@tlecommunity/api-spec/build/spec.json -o lib/types/schema.ts", - "lint": "prettier --check **/*.{ts,tsx,md,js,jsx,css,scss,yml}", - "lint:fix": "prettier --write **/*.{ts,tsx,md,js,jsx,css,scss,yml}" + "format": "prettier --check .", + "format:fix": "prettier --write ." }, "author": "Natalie Rose (https://nataliethistime.com/)", "license": "GPL-3.0-only", "devDependencies": { - "@tlecommunity/api-spec": "^1.6.8", + "@tlecommunity/api-spec": "^1.6.10", "@types/jest": "^30.0.0", "ajv": "^8.20.0", "dotenv": "^17.4.2", -- 2.51.2