@spacemolt/lib 9.0.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.
@@ -1,9 +1,13 @@
1
+ import type { FacilityDefinition, Item, Module, Recipe, ShipClass, SkillDefinition } from '../generated/openapi/types.gen.js';
1
2
  /**
2
3
  * Local copy of the bulk game catalog (`GET /api/catalog.json`).
3
4
  *
4
5
  * Reference data — ships, items, recipes, skills, facilities — that changes
5
- * only on a server release. The entry shapes aren't in the v2 OpenAPI spec, so
6
- * they're typed loosely with `id`-keyed lookups.
6
+ * only on a server release. Entry shapes come from the spec's published
7
+ * `CatalogDump` component types (`ShipClass`, `Item`/`Module`, `Recipe`,
8
+ * `SkillDefinition`, `FacilityDefinition`), so they track the server via the
9
+ * spec sync. Entries are structurally validated as JSON objects and then
10
+ * trusted to match the spec — the same trust model as state-cache deltas.
7
11
  *
8
12
  * The payload is large (multiple MB), so the server ships it `public,
9
13
  * max-age=3600` with a content-hash `ETag`. We keep that ETag on the cache and
@@ -12,17 +16,20 @@
12
16
  * catalog after a server release instead of holding the copy it fetched at
13
17
  * startup forever (see `SpacemoltClient.catalog`).
14
18
  */
15
- export interface CatalogEntry {
16
- id?: string;
17
- [key: string]: unknown;
18
- }
19
+ /** A ship entry — the spec's `ShipClass` (the dump omits live prices). */
20
+ export type CatalogShip = ShipClass;
21
+ /** An item entry — regular items and modules share the merged `items` list. */
22
+ export type CatalogItem = Item | Module;
23
+ export type CatalogRecipe = Recipe;
24
+ export type CatalogSkill = SkillDefinition;
25
+ export type CatalogFacility = FacilityDefinition;
19
26
  export interface Catalog {
20
27
  version?: string;
21
- ships: CatalogEntry[];
22
- items: CatalogEntry[];
23
- recipes: CatalogEntry[];
24
- skills: CatalogEntry[];
25
- facilities: CatalogEntry[];
28
+ ships: CatalogShip[];
29
+ items: CatalogItem[];
30
+ recipes: CatalogRecipe[];
31
+ skills: CatalogSkill[];
32
+ facilities: CatalogFacility[];
26
33
  }
27
34
  /** Result of a conditional catalog fetch. */
28
35
  export interface CatalogFetchResult {
@@ -46,7 +53,11 @@ export declare class CatalogCache {
46
53
  readonly catalog: Catalog;
47
54
  /** The server `ETag` this catalog was fetched with, for conditional reloads. */
48
55
  readonly etag?: string | undefined;
49
- private readonly indexes;
56
+ private readonly shipsById;
57
+ private readonly itemsById;
58
+ private readonly recipesById;
59
+ private readonly skillsById;
60
+ private readonly facilitiesById;
50
61
  constructor(catalog: Catalog,
51
62
  /** The server `ETag` this catalog was fetched with, for conditional reloads. */
52
63
  etag?: string | undefined);
@@ -59,14 +70,14 @@ export declare class CatalogCache {
59
70
  */
60
71
  revalidate(httpBaseUrl: string): Promise<CatalogCache>;
61
72
  get version(): string | undefined;
62
- ship(id: string): CatalogEntry | undefined;
63
- item(id: string): CatalogEntry | undefined;
64
- recipe(id: string): CatalogEntry | undefined;
65
- skill(id: string): CatalogEntry | undefined;
66
- facility(id: string): CatalogEntry | undefined;
67
- get ships(): readonly CatalogEntry[];
68
- get items(): readonly CatalogEntry[];
69
- get recipes(): readonly CatalogEntry[];
70
- get skills(): readonly CatalogEntry[];
71
- get facilities(): readonly CatalogEntry[];
73
+ ship(id: string): CatalogShip | undefined;
74
+ item(id: string): CatalogItem | undefined;
75
+ recipe(id: string): CatalogRecipe | undefined;
76
+ skill(id: string): CatalogSkill | undefined;
77
+ facility(id: string): CatalogFacility | undefined;
78
+ get ships(): readonly CatalogShip[];
79
+ get items(): readonly CatalogItem[];
80
+ get recipes(): readonly CatalogRecipe[];
81
+ get skills(): readonly CatalogSkill[];
82
+ get facilities(): readonly CatalogFacility[];
72
83
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Station directory (`GET /api/stations`).
3
+ *
4
+ * A public summary of every station in the galaxy — identity, owning
5
+ * empire/faction, system, offered services, condition, and defensive posture —
6
+ * plus the empire display list the server sends alongside it. Live data (the
7
+ * server caches it for ~10s), so fetch on demand rather than caching like the
8
+ * catalog or map.
9
+ *
10
+ * These shapes mirror the server's station list response; they aren't
11
+ * published in the OpenAPI spec yet (see docs/gameserver-todo.md), so they're
12
+ * hand-typed here and kept tolerant of server-side additions via the index
13
+ * signature.
14
+ */
15
+ export interface StationSummary {
16
+ id: string;
17
+ name: string;
18
+ description: string;
19
+ empire?: string;
20
+ empire_name?: string;
21
+ faction_id?: string;
22
+ faction_name?: string;
23
+ faction_tag?: string;
24
+ faction_color?: string;
25
+ system_id: string;
26
+ system_name: string;
27
+ services: string[];
28
+ condition: string;
29
+ condition_text: string;
30
+ satisfaction_pct: number;
31
+ facility_count: number;
32
+ /** Damage per tick the station's armed guns can actually deliver — zero when its batteries are out of shells. */
33
+ weapon_dps: number;
34
+ wrecked: boolean;
35
+ [key: string]: unknown;
36
+ }
37
+ /** An empire display entry from the station list (`{ id, name }`). */
38
+ export interface StationEmpire {
39
+ id: string;
40
+ name: string;
41
+ }
42
+ export interface StationList {
43
+ stations: StationSummary[];
44
+ empires: StationEmpire[];
45
+ }
46
+ /** Fetch the public station directory. */
47
+ export declare function fetchStations(httpBaseUrl: string): Promise<StationList>;
package/dist/index.d.ts CHANGED
@@ -20,8 +20,9 @@ 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
- export { CatalogCache, fetchCatalog, fetchCatalogConditional, type Catalog, type CatalogEntry, type CatalogFetchResult, } from './data/catalog.js';
23
+ export { CatalogCache, fetchCatalog, fetchCatalogConditional, type Catalog, type CatalogFacility, type CatalogFetchResult, type CatalogItem, type CatalogRecipe, type CatalogShip, type CatalogSkill, } from './data/catalog.js';
24
24
  export { MapCache, fetchMap, httpBaseFromWs, type GalaxyMap, type MapSystem } from './data/map.js';
25
+ export { fetchStations, type StationEmpire, type StationList, type StationSummary } from './data/stations.js';
25
26
  export { Socket, type SocketOptions, type WebSocketLike, type WebSocketFactory, type MessageEventLike, type CloseEventLike, } from './transport/socket.js';
26
27
  export { ACTIONS, GENERATED_SPEC_VERSION } from './generated/actions.gen.js';
27
28
  export type { ToolName, ActionName, ActionDef, ActionParam } from './generated/actions.gen.js';
package/dist/index.js CHANGED
@@ -4389,7 +4389,7 @@ class MemoryCredentialStore {
4389
4389
 
4390
4390
  // src/data/catalog.ts
4391
4391
  function catalogEntries(value) {
4392
- return Array.isArray(value) ? value.filter((entry) => isRecord(entry)) : [];
4392
+ return Array.isArray(value) ? value.filter(isRecord) : [];
4393
4393
  }
4394
4394
  function normalizeCatalog(value) {
4395
4395
  const data = requireRecord(value, "catalog response");
@@ -4429,17 +4429,19 @@ async function fetchCatalog(httpBaseUrl) {
4429
4429
  class CatalogCache {
4430
4430
  catalog;
4431
4431
  etag;
4432
- indexes;
4432
+ shipsById;
4433
+ itemsById;
4434
+ recipesById;
4435
+ skillsById;
4436
+ facilitiesById;
4433
4437
  constructor(catalog, etag) {
4434
4438
  this.catalog = catalog;
4435
4439
  this.etag = etag;
4436
- this.indexes = {
4437
- ships: index(catalog.ships),
4438
- items: index(catalog.items),
4439
- recipes: index(catalog.recipes),
4440
- skills: index(catalog.skills),
4441
- facilities: index(catalog.facilities)
4442
- };
4440
+ this.shipsById = index(catalog.ships);
4441
+ this.itemsById = index(catalog.items);
4442
+ this.recipesById = index(catalog.recipes);
4443
+ this.skillsById = index(catalog.skills);
4444
+ this.facilitiesById = index(catalog.facilities);
4443
4445
  }
4444
4446
  static async load(httpBaseUrl) {
4445
4447
  const { catalog, etag } = await fetchCatalogConditional(httpBaseUrl);
@@ -4459,19 +4461,19 @@ class CatalogCache {
4459
4461
  return this.catalog.version;
4460
4462
  }
4461
4463
  ship(id) {
4462
- return this.indexes.ships.get(id);
4464
+ return this.shipsById.get(id);
4463
4465
  }
4464
4466
  item(id) {
4465
- return this.indexes.items.get(id);
4467
+ return this.itemsById.get(id);
4466
4468
  }
4467
4469
  recipe(id) {
4468
- return this.indexes.recipes.get(id);
4470
+ return this.recipesById.get(id);
4469
4471
  }
4470
4472
  skill(id) {
4471
- return this.indexes.skills.get(id);
4473
+ return this.skillsById.get(id);
4472
4474
  }
4473
4475
  facility(id) {
4474
- return this.indexes.facilities.get(id);
4476
+ return this.facilitiesById.get(id);
4475
4477
  }
4476
4478
  get ships() {
4477
4479
  return this.catalog.ships;
@@ -4492,7 +4494,7 @@ class CatalogCache {
4492
4494
  function index(entries) {
4493
4495
  const map = new Map;
4494
4496
  for (const entry of entries) {
4495
- const id = typeof entry.id === "string" ? entry.id : undefined;
4497
+ const id = isRecord(entry) && typeof entry.id === "string" ? entry.id : undefined;
4496
4498
  if (id)
4497
4499
  map.set(id, entry);
4498
4500
  }
@@ -4840,6 +4842,17 @@ class SpacemoltClient {
4840
4842
  }
4841
4843
  }
4842
4844
  }
4845
+ // src/data/stations.ts
4846
+ async function fetchStations(httpBaseUrl) {
4847
+ const url = `${httpBaseUrl.replace(/\/$/, "")}/api/stations`;
4848
+ const res = await fetch(url, { headers: { accept: "application/json" } });
4849
+ if (!res.ok)
4850
+ throw new Error(`GET ${url} -> ${res.status} ${res.statusText}`);
4851
+ const data = requireRecord(await res.json(), "stations response");
4852
+ const stations = Array.isArray(data.stations) ? data.stations.filter(isRecord) : [];
4853
+ const empires = Array.isArray(data.empires) ? data.empires.filter((empire) => isRecord(empire) && typeof empire.id === "string" && typeof empire.name === "string") : [];
4854
+ return { stations, empires };
4855
+ }
4843
4856
  // src/generated/notifications.gen.ts
4844
4857
  var TYPED_NOTIFICATION_TYPES = [
4845
4858
  "achievement_unlocked",
@@ -4888,6 +4901,7 @@ export {
4888
4901
  isActionResultFrame,
4889
4902
  isActionErrorFrame,
4890
4903
  httpBaseFromWs,
4904
+ fetchStations,
4891
4905
  fetchMap,
4892
4906
  fetchCatalogConditional,
4893
4907
  fetchCatalog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spacemolt/lib",
3
- "version": "9.0.0",
3
+ "version": "10.0.0",
4
4
  "description": "The definitive TypeScript library for SpaceMolt. WebSocket-v2-first, multi-account, with local state caches. Internals are regenerated from the server's OpenAPI spec.",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",