screeps-connectivity 0.6.0 → 0.8.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fb4ab0a: Add a read-only Market section — all orders, my orders, and history — matching the vanilla client.
8
+
9
+ ### Patch Changes
10
+
11
+ - 620f551: Add Power Creeps pages — list, create, and per-creep power upgrades — from the Overview page.
12
+
13
+ ## 0.7.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 69d132d: Add an account Overview page (GCL/GPL rings, lifetime stats, per-room minimap previews) and a public Profile page with routing between them; optional dashboard endpoints (`user/overview`, `user/rooms`) now fail silently on servers that don't implement them.
18
+
3
19
  ## 0.6.0
4
20
 
5
21
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screeps-connectivity",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "repository": {
@@ -16,6 +16,14 @@ export interface RateLimitInfo {
16
16
  reset: number
17
17
  }
18
18
 
19
+ export interface RequestOptions {
20
+ /** Internal: marks the post-auth retry so a second 401 doesn't loop. */
21
+ isRetry?: boolean
22
+ /** Suppress user-facing surfacing of failures (the http:error event carries the
23
+ * flag through). Use for optional endpoints the caller handles gracefully. */
24
+ silent?: boolean
25
+ }
26
+
19
27
  export class HttpClient extends EventTarget {
20
28
  readonly baseUrl: string
21
29
  private readonly authStrategy: AuthStrategy
@@ -79,7 +87,7 @@ export class HttpClient extends EventTarget {
79
87
  this.token = token
80
88
  }
81
89
 
82
- async request<T>(method: string, path: string, body?: Record<string, unknown>, isRetry = false): Promise<T> {
90
+ async request<T>(method: string, path: string, body?: Record<string, unknown>, opts: RequestOptions = {}): Promise<T> {
83
91
  this.logger.log(method, path)
84
92
  const url = new URL(path.startsWith('/') ? path.slice(1) : path, this.baseUrl)
85
93
  const headers: Record<string, string> = {}
@@ -115,9 +123,9 @@ export class HttpClient extends EventTarget {
115
123
 
116
124
  this.updateRateLimit(path, res)
117
125
 
118
- if (res.status === 401 && !isRetry && !this.authenticating) {
126
+ if (res.status === 401 && !opts.isRetry && !this.authenticating) {
119
127
  await this.authenticate()
120
- return this.request<T>(method, path, body, true)
128
+ return this.request<T>(method, path, body, { ...opts, isRetry: true })
121
129
  }
122
130
 
123
131
  // 304: some servers (e.g. private Screeps) send a body with 304 — treat it as success
@@ -125,7 +133,7 @@ export class HttpClient extends EventTarget {
125
133
  let body = ''
126
134
  try { body = await res.text() } catch { /* ignore */ }
127
135
  const error = new Error(`HTTP ${res.status}: ${body}`)
128
- this.emit('http:error', { method, path, status: res.status, error })
136
+ this.emit('http:error', { method, path, status: res.status, error, silent: opts.silent })
129
137
  throw error
130
138
  }
131
139
 
@@ -135,7 +143,7 @@ export class HttpClient extends EventTarget {
135
143
 
136
144
  if (typeof data['error'] === 'string') {
137
145
  const error = new Error(`Screeps API error: ${data['error']}`)
138
- this.emit('http:error', { method, path, status: res.status, error })
146
+ this.emit('http:error', { method, path, status: res.status, error, silent: opts.silent })
139
147
  throw error
140
148
  }
141
149
 
@@ -15,6 +15,10 @@ import type {
15
15
  ApiGameTickResponse,
16
16
  RoomHistoryChunk,
17
17
  ApiRoomDecorationsResponse,
18
+ ApiMarketOrdersIndexResponse,
19
+ ApiMarketOrdersResponse,
20
+ ApiMarketMyOrdersResponse,
21
+ ApiMarketStatsResponse,
18
22
  } from '../../types/api.js'
19
23
  import { createPowerCreepsEndpoints, type PowerCreepsEndpoints } from './power-creeps.js'
20
24
 
@@ -49,10 +53,10 @@ export interface GameEndpoints {
49
53
  removeInvader(id: string): Promise<{ ok: number }>
50
54
  powerCreeps: PowerCreepsEndpoints
51
55
  market: {
52
- ordersIndex(shard?: string | null): Promise<unknown>
53
- myOrders(): Promise<unknown>
54
- orders(resourceType: string, shard?: string | null): Promise<unknown>
55
- stats(resourceType: string, shard?: string | null): Promise<unknown>
56
+ ordersIndex(shard?: string | null): Promise<ApiMarketOrdersIndexResponse>
57
+ myOrders(): Promise<ApiMarketMyOrdersResponse>
58
+ orders(resourceType: string, shard?: string | null): Promise<ApiMarketOrdersResponse>
59
+ stats(resourceType: string, shard?: string | null): Promise<ApiMarketStatsResponse>
56
60
  }
57
61
  shards: {
58
62
  info(): Promise<ApiShardsInfoResponse>
@@ -3,6 +3,8 @@ import type {
3
3
  ApiUserBranchesResponse,
4
4
  ApiUserFindResponse,
5
5
  ApiUserMoneyHistoryResponse,
6
+ ApiUserOverviewResponse,
7
+ ApiUserRoomsResponse,
6
8
  } from '../../types/api.js'
7
9
  import { createUserMessagesEndpoints, type UserMessagesEndpoints } from './user-messages.js'
8
10
 
@@ -30,8 +32,8 @@ export interface UserEndpoints {
30
32
  }
31
33
  console(expression: string, shard?: string | null): Promise<unknown>
32
34
  stats(interval: number): Promise<unknown>
33
- rooms(id: string): Promise<unknown>
34
- overview(interval: number, statName: string): Promise<unknown>
35
+ rooms(id: string): Promise<ApiUserRoomsResponse>
36
+ overview(interval: number, statName: string): Promise<ApiUserOverviewResponse>
35
37
  worldStatus(): Promise<{ ok: number; status: 'normal' | 'lost' | 'empty' }>
36
38
  worldStartRoom(shard?: string | null): Promise<unknown>
37
39
  find(query: { username: string } | { id: string }): Promise<ApiUserFindResponse>
@@ -73,8 +75,11 @@ export function createUserEndpoints(http: HttpClient): UserEndpoints {
73
75
  },
74
76
  console: (expression, shard) => http.request('POST', '/api/user/console', withShard({ expression }, shard)),
75
77
  stats: (interval) => http.request('GET', '/api/user/stats', { interval }),
76
- rooms: (id) => http.request('GET', '/api/user/rooms', { id }),
77
- overview: (interval, statName) => http.request('GET', '/api/user/overview', { interval, statName }),
78
+ // Best-effort dashboard data: not every server implements these, and the
79
+ // Overview page degrades gracefully (zeros / no tiles), so a failure here
80
+ // shouldn't raise a user-facing error toast — mark them silent.
81
+ rooms: (id) => http.request('GET', '/api/user/rooms', { id }, { silent: true }),
82
+ overview: (interval, statName) => http.request('GET', '/api/user/overview', { interval, statName }, { silent: true }),
78
83
  worldStatus: () => http.request('GET', '/api/user/world-status'),
79
84
  worldStartRoom: (shard) => http.request('GET', '/api/user/world-start-room', withShard({}, shard)),
80
85
  find: (query) => http.request('GET', '/api/user/find', query as Record<string, unknown>),
package/src/index.ts CHANGED
@@ -40,7 +40,7 @@ export type {
40
40
 
41
41
  export { fetchServerVersion, fetchAuthModInfo, checkUsername, checkEmail, registerUser, getServerFeature, getScreepsmodAuth } from './http/fetchServerVersion.js'
42
42
  export type { ApiAuthModInfoResponse } from './http/fetchServerVersion.js'
43
- export type { RoomHistoryChunk, ApiRoomDecorationsResponse, ApiRoomDecorationItem, ApiRoomDecorationDef, ApiRoomDecorationGraphic, ApiRoomDecorationActive } from './types/api.js'
43
+ export type { RoomHistoryChunk, ApiRoomDecorationsResponse, ApiRoomDecorationItem, ApiRoomDecorationDef, ApiRoomDecorationGraphic, ApiRoomDecorationActive, ApiUserOverviewResponse, ApiUserOverviewTotals, ApiUserRoomsResponse, ApiUserMoneyHistoryResponse, ApiMarketOrder, ApiMarketOrdersIndexResponse, ApiMarketOrdersResponse, ApiMarketMyOrdersResponse, ApiMarketStat, ApiMarketStatsResponse, ApiPowerCreep, ApiPowerCreepsListResponse } from './types/api.js'
44
44
  export { ROOM_DECORATIONS_MOCK } from './mocks/roomDecorations.js'
45
45
 
46
46
  export { badgeToSvg } from './badge/index.js'
package/src/types/api.ts CHANGED
@@ -21,11 +21,38 @@ export interface ApiAuthMeResponse {
21
21
  username: string
22
22
  cpu: number
23
23
  gcl: number
24
+ /** Raw accumulated power points; Global Power Level derives from this. Absent on servers without the power system. */
25
+ power?: number
24
26
  credits: number
25
27
  badge: import('./game.js').Badge
26
28
  password: boolean
27
29
  }
28
30
 
31
+ /** Per-stat lifetime totals over the requested interval — the values behind the Overview stat tiles. Individual fields may be absent on servers that don't track that stat. */
32
+ export interface ApiUserOverviewTotals {
33
+ energyControl?: number
34
+ energyHarvested?: number
35
+ energyConstruction?: number
36
+ energyCreeps?: number
37
+ creepsProduced?: number
38
+ creepsLost?: number
39
+ powerProcessed?: number
40
+ }
41
+
42
+ /** Response of GET /api/user/overview. Only `totals` is consumed by the Overview tiles; per-room time series (shards/stats) are omitted until the room band is built. */
43
+ export interface ApiUserOverviewResponse {
44
+ ok: number
45
+ totals?: ApiUserOverviewTotals
46
+ statsMax?: number
47
+ }
48
+
49
+ /** Response of GET /api/user/rooms?id=<userId> — the rooms a user owns. Multishard servers key by shard; single-shard servers may return a flat list. */
50
+ export interface ApiUserRoomsResponse {
51
+ ok: number
52
+ shards?: Record<string, string[]>
53
+ rooms?: string[]
54
+ }
55
+
29
56
  export interface ApiAuthQueryTokenResponse {
30
57
  ok: number
31
58
  token: { full: boolean }
@@ -218,6 +245,8 @@ export interface ApiUserFindResponse {
218
245
  export interface ApiUserMoneyHistoryResponse {
219
246
  ok: number
220
247
  page: number
248
+ // True when an older page exists; drives the History "Older" pager.
249
+ hasMore?: boolean
221
250
  list: Array<{
222
251
  _id: string
223
252
  date: string
@@ -225,10 +254,64 @@ export interface ApiUserMoneyHistoryResponse {
225
254
  type: string
226
255
  balance: number
227
256
  change: number
257
+ // Shard the transaction occurred on (official multi-shard servers only).
258
+ shard?: string
228
259
  market?: unknown
229
260
  }>
230
261
  }
231
262
 
263
+ // Market — a single order from game/market/orders (public) or my-orders (own).
264
+ // `active`/`totalAmount` are returned only for your own orders. `range` is not
265
+ // part of the API response — the client computes it as the room distance to a
266
+ // chosen target room.
267
+ export interface ApiMarketOrder {
268
+ _id: string
269
+ type: 'sell' | 'buy'
270
+ resourceType: string
271
+ price: number
272
+ amount: number
273
+ remainingAmount: number
274
+ /** Original order size — own orders only. */
275
+ totalAmount?: number
276
+ /** Whether the order is currently funded/stocked — own orders only. */
277
+ active?: boolean
278
+ /** Source/target room; absent for token and other roomless orders. */
279
+ roomName?: string
280
+ created?: number
281
+ }
282
+
283
+ export interface ApiMarketOrdersIndexResponse {
284
+ ok: number
285
+ // One entry per resource type that has open orders; `_id` is the resource type.
286
+ list: Array<{ _id: string; count: number }>
287
+ }
288
+
289
+ export interface ApiMarketOrdersResponse {
290
+ ok: number
291
+ list: ApiMarketOrder[]
292
+ }
293
+
294
+ // Own orders: multi-shard servers return `shards` keyed by shard name;
295
+ // single-shard servers return a flat `list` instead.
296
+ export interface ApiMarketMyOrdersResponse {
297
+ ok: number
298
+ shards?: Record<string, ApiMarketOrder[]>
299
+ list?: ApiMarketOrder[]
300
+ }
301
+
302
+ export interface ApiMarketStat {
303
+ date: string
304
+ transactions: number
305
+ volume: number
306
+ avgPrice: number
307
+ stddevPrice: number
308
+ }
309
+
310
+ export interface ApiMarketStatsResponse {
311
+ ok: number
312
+ stats: ApiMarketStat[]
313
+ }
314
+
232
315
  export interface ApiUserMessage {
233
316
  _id: string
234
317
  date: string
@@ -68,6 +68,9 @@ export interface HttpClientEvents {
68
68
  path: string
69
69
  status: number
70
70
  error: Error
71
+ // Set when the request opted out of user-facing error surfacing (e.g. an
72
+ // optional endpoint whose failure the caller handles gracefully).
73
+ silent?: boolean
71
74
  }
72
75
  'http:tokenRefresh': {
73
76
  token: string
package/src/types/game.ts CHANGED
@@ -66,6 +66,8 @@ export interface UserInfo {
66
66
  email: string
67
67
  cpu: number
68
68
  gcl: number
69
+ /** Raw accumulated power points; Global Power Level derives from this. Absent on servers without the power system. */
70
+ power?: number
69
71
  credits: number
70
72
  badge: Badge
71
73
  /** True when the account has a password set. Absent for password-less accounts (e.g. Steam-only logins). Only present for the authenticated user's own info. */