@my-swu/simulator-client 0.1.2 → 0.1.3

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.
Files changed (75) hide show
  1. package/README.md +1 -1
  2. package/dist/connection.js +2 -3
  3. package/dist/generated/index.d.ts +10 -0
  4. package/dist/generated/schemas.d.ts +1 -1
  5. package/dist/generated/schemas.js +466 -59
  6. package/dist/generated/types/admin-match-debug-bundle.d.ts +1 -0
  7. package/dist/generated/types/admin-match-debug-bundle.js +2 -0
  8. package/dist/generated/types/admin-match-list-response.d.ts +1 -0
  9. package/dist/generated/types/admin-match-list-response.js +2 -0
  10. package/dist/generated/types/admin-match-replay-response.d.ts +1 -0
  11. package/dist/generated/types/admin-match-replay-response.js +2 -0
  12. package/dist/generated/types/common.d.ts +143 -15
  13. package/dist/generated/types/create-match-history-replay-share-request.d.ts +1 -0
  14. package/dist/generated/types/create-match-history-replay-share-request.js +2 -0
  15. package/dist/generated/types/create-match-history-replay-share-response.d.ts +1 -0
  16. package/dist/generated/types/create-match-history-replay-share-response.js +2 -0
  17. package/dist/generated/types/inactive-match-recovery-request.d.ts +1 -0
  18. package/dist/generated/types/inactive-match-recovery-request.js +2 -0
  19. package/dist/generated/types/inactive-match-recovery-response.d.ts +1 -0
  20. package/dist/generated/types/inactive-match-recovery-response.js +2 -0
  21. package/dist/generated/types/match-access-recovery-request.d.ts +1 -0
  22. package/dist/generated/types/match-access-recovery-request.js +2 -0
  23. package/dist/generated/types/match-access-recovery-response.d.ts +1 -0
  24. package/dist/generated/types/match-access-recovery-response.js +2 -0
  25. package/dist/generated/types/match-recovery-request.d.ts +1 -0
  26. package/dist/generated/types/match-recovery-request.js +2 -0
  27. package/dist/generated/types/match-recovery-response.d.ts +1 -0
  28. package/dist/generated/types/match-recovery-response.js +2 -0
  29. package/dist/generated/types/shared-match-history-replay-response.d.ts +1 -0
  30. package/dist/generated/types/shared-match-history-replay-response.js +2 -0
  31. package/dist/generated/version.d.ts +1 -1
  32. package/dist/generated/version.js +1 -1
  33. package/dist/index.d.ts +3 -3
  34. package/dist/internal/match-history.d.ts +5 -0
  35. package/dist/internal/match-history.js +8 -0
  36. package/dist/local/engine.d.ts +2 -0
  37. package/dist/local/server/http-router.js +10 -0
  38. package/dist/local/wasm-engine.d.ts +1 -0
  39. package/dist/local/wasm-engine.js +1 -0
  40. package/dist/match-session/controller.d.ts +1 -0
  41. package/dist/match-session/controller.js +56 -17
  42. package/dist/match-session/methods.d.ts +8 -0
  43. package/dist/match-session/session-state.d.ts +5 -1
  44. package/dist/match-session/types.d.ts +1 -1
  45. package/dist/match-session.d.ts +1 -1
  46. package/dist/resources/admin.d.ts +14 -1
  47. package/dist/resources/admin.js +21 -0
  48. package/dist/resources/lobby-types.d.ts +4 -4
  49. package/dist/resources/match-history.d.ts +14 -1
  50. package/dist/resources/match-history.js +16 -0
  51. package/dist/resources/match-types.d.ts +11 -2
  52. package/dist/resources/matches.js +10 -0
  53. package/dist/validators.d.ts +11 -1
  54. package/package.json +1 -1
  55. package/schema/add-lobby-ai-player-request.schema.json +7 -0
  56. package/schema/admin-match-debug-bundle.schema.json +48 -0
  57. package/schema/admin-match-list-response.schema.json +23 -0
  58. package/schema/admin-match-replay-response.schema.json +14 -0
  59. package/schema/common.schema.json +176 -27
  60. package/schema/create-limited-event-request.schema.json +2 -4
  61. package/schema/create-lobby-request.schema.json +4 -8
  62. package/schema/create-match-history-replay-share-request.schema.json +22 -0
  63. package/schema/create-match-history-replay-share-response.schema.json +15 -0
  64. package/schema/create-match-request.schema.json +2 -3
  65. package/schema/create-tournament-room-request.schema.json +4 -8
  66. package/schema/limited-event-preflight-response.schema.json +1 -3
  67. package/schema/manifest.json +11 -1
  68. package/schema/match-access-recovery-request.schema.json +17 -0
  69. package/schema/match-access-recovery-response.schema.json +36 -0
  70. package/schema/match-access-response.schema.json +1 -3
  71. package/schema/match-history-entry.schema.json +1 -3
  72. package/schema/match-recovery-request.schema.json +33 -0
  73. package/schema/match-recovery-response.schema.json +36 -0
  74. package/schema/route-manifest.json +92 -0
  75. package/schema/shared-match-history-replay-response.schema.json +14 -0
@@ -2,6 +2,7 @@ import { MatchConnection } from '../connection.js';
2
2
  import { TypedEmitter } from '../internal/emitter.js';
3
3
  import { formatErrorMessages } from '../internal/errors.js';
4
4
  import { DEFAULT_ERROR_HISTORY_LIMIT, DEFAULT_MATCH_HISTORY_LIMIT } from '../internal/limits.js';
5
+ import { upsertMatchHistoryEntry } from '../internal/match-history.js';
5
6
  import { nonNegativeNumber, normalizeReconnectBackoff, reconnectDelayMs, } from '../internal/reconnect-backoff.js';
6
7
  import { createMatchSessionHeartbeat } from './heartbeat.js';
7
8
  import { hasAccessSnapshot, matchReferenceFrom } from './access.js';
@@ -207,6 +208,21 @@ export class MatchSessionController {
207
208
  reconnect(options = {}) {
208
209
  return this.connect(undefined, options);
209
210
  }
211
+ recoverNow() {
212
+ if (this.#manualClose || !this.#autoReconnect.enabled || this.#access == null) {
213
+ return;
214
+ }
215
+ this.#reconnectGeneration += 1;
216
+ if (this.#reconnectTimer != null) {
217
+ clearTimeout(this.#reconnectTimer);
218
+ this.#reconnectTimer = undefined;
219
+ }
220
+ // Detach before close so this intentional foreground replacement cannot
221
+ // schedule a duplicate close-driven retry.
222
+ this.#closeActiveConnection();
223
+ this.#setConnectionState('closed');
224
+ void this.#runAutoReconnectAttempt(1, this.#reconnectGeneration);
225
+ }
210
226
  ready() {
211
227
  this.#activeConnection().ready();
212
228
  }
@@ -333,8 +349,7 @@ export class MatchSessionController {
333
349
  this.#emitChange();
334
350
  }
335
351
  #recordHistoryEntry(entry) {
336
- const withoutDuplicate = this.#history.filter(existing => existing.id !== entry.id);
337
- this.#history = [...withoutDuplicate, entry].slice(-DEFAULT_MATCH_HISTORY_LIMIT);
352
+ this.#history = upsertMatchHistoryEntry(this.#history, entry);
338
353
  this.#emitter.emit('historyEntry', entry);
339
354
  this.#emitChange();
340
355
  }
@@ -368,15 +383,18 @@ export class MatchSessionController {
368
383
  }
369
384
  #scheduleAutoReconnect(attempt, generation) {
370
385
  if (attempt > this.#autoReconnect.maxRetries) {
386
+ const error = {
387
+ code: 'sdk_session_error',
388
+ message: 'automatic reconnect exhausted',
389
+ };
371
390
  this.#setReconnectState({
372
- phase: 'exhausted',
391
+ phase: 'terminal',
373
392
  attempts: this.#autoReconnect.maxRetries,
374
393
  maxAttempts: this.#autoReconnect.maxRetries,
394
+ cause: 'retriesExhausted',
395
+ error,
375
396
  });
376
- this.#recordError({
377
- code: 'sdk_session_error',
378
- message: 'automatic reconnect exhausted',
379
- });
397
+ this.#recordError(error);
380
398
  return;
381
399
  }
382
400
  const delayMs = reconnectDelayMs(this.#autoReconnect, attempt);
@@ -418,17 +436,23 @@ export class MatchSessionController {
418
436
  if (generation !== this.#reconnectGeneration || this.#manualClose) {
419
437
  return;
420
438
  }
421
- if (attempt >= this.#autoReconnect.maxRetries) {
422
- this.#recordError(errorPayloadFromUnknown(error));
423
- this.#recordError({
424
- code: 'sdk_session_error',
425
- message: 'automatic reconnect exhausted',
426
- });
439
+ const payload = errorPayloadFromUnknown(error);
440
+ const cause = terminalCauseForError(payload);
441
+ if (cause != null || attempt >= this.#autoReconnect.maxRetries) {
442
+ const terminalError = cause == null
443
+ ? {
444
+ code: 'sdk_session_error',
445
+ message: 'automatic reconnect exhausted',
446
+ }
447
+ : payload;
427
448
  this.#setReconnectState({
428
- phase: 'exhausted',
449
+ phase: 'terminal',
429
450
  attempts: attempt,
430
451
  maxAttempts: this.#autoReconnect.maxRetries,
452
+ cause: cause ?? 'retriesExhausted',
453
+ error: terminalError,
431
454
  });
455
+ this.#recordError(terminalError);
432
456
  this.#setConnectionState('closed');
433
457
  return;
434
458
  }
@@ -556,9 +580,24 @@ function sameReconnectState(left, right) {
556
580
  return right.phase === 'connecting'
557
581
  && left.attempt === right.attempt
558
582
  && left.maxAttempts === right.maxAttempts;
559
- case 'exhausted':
560
- return right.phase === 'exhausted'
583
+ case 'terminal':
584
+ return right.phase === 'terminal'
561
585
  && left.attempts === right.attempts
562
- && left.maxAttempts === right.maxAttempts;
586
+ && left.maxAttempts === right.maxAttempts
587
+ && left.cause === right.cause
588
+ && left.error.code === right.error.code
589
+ && left.error.message === right.error.message;
590
+ }
591
+ }
592
+ /** Maps definitive server rejections to terminal causes without parsing text. */
593
+ function terminalCauseForError(error) {
594
+ switch (error.code) {
595
+ case 'match_not_found':
596
+ return 'matchNotFound';
597
+ case 'invalid_seat_token':
598
+ case 'invalid_spectator_token':
599
+ return 'invalidCredentials';
600
+ default:
601
+ return undefined;
563
602
  }
564
603
  }
@@ -159,6 +159,14 @@ export interface MatchSessionMethods {
159
159
  * @see https://simulator-sdk.my-swu.com/guide/reconnect
160
160
  */
161
161
  reconnect(options?: MatchSessionConnectOptions): Promise<MatchSession>;
162
+ /**
163
+ * Immediately starts session-owned automatic recovery using stored access.
164
+ *
165
+ * This is intended for foreground restoration: it preserves automatic
166
+ * backoff after a failed dial instead of turning recovery into a one-shot
167
+ * manual reconnect.
168
+ */
169
+ recoverNow(): void;
162
170
  /**
163
171
  * Sends one ready message for the current seat.
164
172
  *
@@ -132,10 +132,14 @@ export type MatchSessionReconnectState = {
132
132
  attempt: number;
133
133
  maxAttempts: number;
134
134
  } | {
135
- phase: 'exhausted';
135
+ phase: 'terminal';
136
136
  attempts: number;
137
137
  maxAttempts: number;
138
+ cause: MatchSessionTerminalCause;
139
+ error: ErrorPayload;
138
140
  };
141
+ /** Server or transport condition which stopped automatic reconnect retries. */
142
+ export type MatchSessionTerminalCause = 'matchNotFound' | 'invalidCredentials' | 'retriesExhausted';
139
143
  /**
140
144
  * Options for creating one SDK-owned match session.
141
145
  *
@@ -3,7 +3,7 @@ import type { MatchHistoryFeedEntry } from '../generated/types/server-message.js
3
3
  import type { ConnectionState } from '../session.js';
4
4
  import type { MatchSessionMethods } from './methods.js';
5
5
  import type { MatchSeat, MatchSessionReconnectState, MatchSessionState } from './session-state.js';
6
- export type { MatchSeat, MatchSessionAccess, MatchSessionAutoReconnectOptions, MatchSessionAutoReconnectPolicy, MatchSessionConnectOptions, MatchSessionEvents, MatchSessionHeartbeatOptions, MatchSessionHeartbeatPolicy, MatchSessionOptions, MatchSessionReconnectState, MatchSessionSnapshotPredicate, MatchSessionState, MatchSessionWaitOptions, } from './session-state.js';
6
+ export type { MatchSeat, MatchSessionAccess, MatchSessionAutoReconnectOptions, MatchSessionAutoReconnectPolicy, MatchSessionConnectOptions, MatchSessionEvents, MatchSessionHeartbeatOptions, MatchSessionHeartbeatPolicy, MatchSessionOptions, MatchSessionReconnectState, MatchSessionSnapshotPredicate, MatchSessionState, MatchSessionTerminalCause, MatchSessionWaitOptions, } from './session-state.js';
7
7
  /**
8
8
  * Main ergonomic SDK API for interacting with one live match.
9
9
  *
@@ -1,3 +1,3 @@
1
1
  export { MatchSessionController } from './match-session/controller.js';
2
2
  export type { MatchSessionControllerOptions } from './match-session/controller.js';
3
- export type { MatchSeat, MatchSession, MatchSessionAccess, MatchSessionAutoReconnectOptions, MatchSessionAutoReconnectPolicy, MatchSessionConnectOptions, MatchSessionEvents, MatchSessionHeartbeatOptions, MatchSessionHeartbeatPolicy, MatchSessionOptions, MatchSessionReconnectState, MatchSessionSnapshotPredicate, MatchSessionState, MatchSessionWaitOptions, } from './match-session/types.js';
3
+ export type { MatchSeat, MatchSession, MatchSessionAccess, MatchSessionAutoReconnectOptions, MatchSessionAutoReconnectPolicy, MatchSessionConnectOptions, MatchSessionEvents, MatchSessionHeartbeatOptions, MatchSessionHeartbeatPolicy, MatchSessionOptions, MatchSessionReconnectState, MatchSessionTerminalCause, MatchSessionSnapshotPredicate, MatchSessionState, MatchSessionWaitOptions, } from './match-session/types.js';
@@ -1,4 +1,4 @@
1
- import type { SimulatorReportListResponse, SimulatorReportResponse, CardCatalogRefreshResponse, CreateMetaWindowRequest, MatchChatLookupResponse, MetaWindowsResponse, RankedLeaderboardQuery, RankedLeaderboardResponse, RankedProfileRequest, RankedProfileResponse, SetRankedAdmissionRequest, SetSimulatorMaintenanceRequest, SimulatorAdminStatusResponse } from '../generated/index.js';
1
+ import type { AdminMatchDebugBundle, AdminMatchListResponse, AdminMatchReplayResponse, SimulatorReportListResponse, SimulatorReportResponse, CardCatalogRefreshResponse, CreateMetaWindowRequest, MatchChatLookupResponse, MetaWindowsResponse, RankedLeaderboardQuery, RankedLeaderboardResponse, RankedProfileRequest, RankedProfileResponse, SetRankedAdmissionRequest, SetSimulatorMaintenanceRequest, SimulatorAdminStatusResponse } from '../generated/index.js';
2
2
  import type { SimulatorRequestOptions } from '../http/options.js';
3
3
  import type { EngineHttpRequester } from '../http/request.js';
4
4
  /**
@@ -10,6 +10,13 @@ export interface SimulatorReportListOptions {
10
10
  cursor?: string;
11
11
  limit?: number;
12
12
  }
13
+ /** Filters and cursor pagination controls for durable simulator admin games. */
14
+ export interface AdminGameListOptions {
15
+ matchId?: string;
16
+ playerSearch?: string;
17
+ cursor?: string;
18
+ limit?: number;
19
+ }
13
20
  /**
14
21
  * Bearer-token credentials for simulator admin calls.
15
22
  */
@@ -23,6 +30,12 @@ export interface SimulatorAdminAuth {
23
30
  * Simulator administration API methods.
24
31
  */
25
32
  export interface AdminResource {
33
+ /** Lists durable saved and completed matches for simulator support work. */
34
+ listGames(auth: SimulatorAdminAuth, query?: AdminGameListOptions, options?: SimulatorRequestOptions): Promise<AdminMatchListResponse>;
35
+ /** Returns all durable diagnostics for one match with restore credentials removed. */
36
+ getGameDebugBundle(auth: SimulatorAdminAuth, historyId: string, options?: SimulatorRequestOptions): Promise<AdminMatchDebugBundle>;
37
+ /** Returns a completed replay archive for one match. */
38
+ getGameReplay(auth: SimulatorAdminAuth, historyId: string, options?: SimulatorRequestOptions): Promise<AdminMatchReplayResponse>;
26
39
  /** Lists safe metadata for retained simulator diagnostic reports. */
27
40
  listSimulatorReports(auth: SimulatorAdminAuth, query?: SimulatorReportListOptions, options?: SimulatorRequestOptions): Promise<SimulatorReportListResponse>;
28
41
  /** Returns one full sanitized simulator support bundle. */
@@ -4,6 +4,9 @@ import { jsonBody, jsonHeaders } from '../http/json.js';
4
4
  */
5
5
  export function createAdminResource(requester) {
6
6
  return {
7
+ listGames: (auth, query, options) => requester.request('admin-match-list-response', adminGamesPath(query ?? {}), { headers: authHeaders(auth) }, options),
8
+ getGameDebugBundle: (auth, historyId, options) => requester.request('admin-match-debug-bundle', `/api/admin/games/${encodeURIComponent(historyId)}/debug`, { headers: authHeaders(auth) }, options),
9
+ getGameReplay: (auth, historyId, options) => requester.request('admin-match-replay-response', `/api/admin/games/${encodeURIComponent(historyId)}/replay`, { headers: authHeaders(auth) }, options),
7
10
  listSimulatorReports: (auth, query, options) => requester.request('simulator-report-list-response', simulatorReportsPath(query ?? {}), { headers: authHeaders(auth) }, options),
8
11
  getSimulatorReport: (auth, reportId, options) => requester.request('simulator-report-response', `/api/admin/reports/${encodeURIComponent(reportId)}`, { headers: authHeaders(auth) }, options),
9
12
  refreshCardCatalog: (auth, options) => requester.request('card-catalog-refresh-response', '/api/admin/card-catalog/refresh', { headers: authHeaders(auth), method: 'POST' }, options),
@@ -34,6 +37,24 @@ export function createAdminResource(requester) {
34
37
  rankedLeaderboard: (auth, windowId, query, options) => requester.request('ranked-leaderboard-response', leaderboardPath(requester, windowId, query ?? {}), { headers: authHeaders(auth) }, options),
35
38
  };
36
39
  }
40
+ /** Builds an encoded durable-game query without exposing the route shape. */
41
+ function adminGamesPath(options) {
42
+ const parameters = new URLSearchParams();
43
+ if (options.matchId) {
44
+ parameters.set('matchId', options.matchId);
45
+ }
46
+ if (options.playerSearch) {
47
+ parameters.set('playerSearch', options.playerSearch);
48
+ }
49
+ if (options.cursor) {
50
+ parameters.set('cursor', options.cursor);
51
+ }
52
+ if (options.limit != null) {
53
+ parameters.set('limit', String(options.limit));
54
+ }
55
+ const suffix = parameters.size === 0 ? '' : `?${parameters.toString()}`;
56
+ return `/api/admin/games${suffix}`;
57
+ }
37
58
  /** Builds a report-list path without exposing pagination implementation. */
38
59
  function simulatorReportsPath(options) {
39
60
  const parameters = new URLSearchParams();
@@ -62,9 +62,9 @@ export type LobbyConfig = {
62
62
  export interface CreateLobbyRequest {
63
63
  config: LobbyConfig;
64
64
  /** Fixed limited pack/leader seed. */
65
- packSeed?: number | null;
65
+ packSeed?: string | null;
66
66
  /** Fixed match seed reused for limited duel matches. */
67
- matchSeed?: number | null;
67
+ matchSeed?: string | null;
68
68
  /** Whether the lobby should appear in public browsing surfaces. */
69
69
  visibility: LobbyVisibility;
70
70
  hostName: string;
@@ -183,9 +183,9 @@ export interface LobbySnapshot {
183
183
  visibility: LobbyVisibility;
184
184
  config: LobbyConfig;
185
185
  /** Fixed limited pack/leader seed when configured. */
186
- packSeed?: number | null;
186
+ packSeed?: string | null;
187
187
  /** Fixed match seed when configured. */
188
- matchSeed?: number | null;
188
+ matchSeed?: string | null;
189
189
  hostPlayerId: string;
190
190
  requiredPlayerCount: number;
191
191
  /** Absolute server deadline for the ready-lobby auto-start countdown. */
@@ -1,4 +1,4 @@
1
- import type { MatchHistoryDeckStatsRequest, MatchHistoryDeckStatsResponse, MatchHistoryListRequest, MatchHistoryListResponse, MatchHistoryReplayRequest, MatchHistoryReplayResponse, MatchHistoryRestoreRequest, RestoreHistoryResponse } from '../generated/index.js';
1
+ import type { CreateMatchHistoryReplayShareRequest, CreateMatchHistoryReplayShareResponse, MatchRecoveryRequest, MatchRecoveryResponse, MatchHistoryDeckStatsRequest, MatchHistoryDeckStatsResponse, MatchHistoryListRequest, MatchHistoryListResponse, MatchHistoryReplayRequest, MatchHistoryReplayResponse, MatchHistoryRestoreRequest, RestoreHistoryResponse, SharedMatchHistoryReplayResponse } from '../generated/index.js';
2
2
  import type { SimulatorRequestOptions } from '../http/options.js';
3
3
  import type { EngineHttpRequester } from '../http/request.js';
4
4
  /**
@@ -10,11 +10,18 @@ export interface MatchHistoryRestoreInput extends MatchHistoryRestoreRequest {
10
10
  */
11
11
  historyId: string;
12
12
  }
13
+ /** Recovery request for a source runtime that is no longer available. */
14
+ export type MatchRecoveryInput = MatchRecoveryRequest;
13
15
  /** Request used to load a completed server-side replay archive. */
14
16
  export interface MatchHistoryReplayInput extends MatchHistoryReplayRequest {
15
17
  /** Durable history row id returned by {@link MatchHistoryResource.list}. */
16
18
  historyId: string;
17
19
  }
20
+ /** Participant-authorized request that creates one scoped replay-share link. */
21
+ export interface CreateMatchHistoryReplayShareInput extends CreateMatchHistoryReplayShareRequest {
22
+ /** Durable history row id returned by {@link MatchHistoryResource.list}. */
23
+ historyId: string;
24
+ }
18
25
  /**
19
26
  * Server-owned simulator history APIs.
20
27
  */
@@ -29,10 +36,16 @@ export interface MatchHistoryResource {
29
36
  list(request: MatchHistoryListRequest, options?: SimulatorRequestOptions): Promise<MatchHistoryListResponse>;
30
37
  /** Loads public and post-game reveal-all frames for an authorized result. */
31
38
  replay(request: MatchHistoryReplayInput, options?: SimulatorRequestOptions): Promise<MatchHistoryReplayResponse>;
39
+ /** Creates or returns a stable opaque replay link for one disclosure scope. */
40
+ createReplayShare(request: CreateMatchHistoryReplayShareInput, options?: SimulatorRequestOptions): Promise<CreateMatchHistoryReplayShareResponse>;
41
+ /** Loads a replay through an opaque link without participant identities. */
42
+ loadReplayShare(shareToken: string, options?: SimulatorRequestOptions): Promise<SharedMatchHistoryReplayResponse>;
32
43
  /**
33
44
  * Restores one authorized row whose eligibility is `available`.
34
45
  */
35
46
  restore(request: MatchHistoryRestoreInput, options?: SimulatorRequestOptions): Promise<RestoreHistoryResponse>;
47
+ /** Restores an owned Solo-versus-AI source after its runtime is lost. */
48
+ recover(request: MatchRecoveryInput, options?: SimulatorRequestOptions): Promise<MatchRecoveryResponse>;
36
49
  }
37
50
  /**
38
51
  * Dependencies needed by the match-history resource.
@@ -22,6 +22,17 @@ export function createMatchHistoryResource(options) {
22
22
  headers: jsonHeaders(),
23
23
  method: 'POST',
24
24
  }, options),
25
+ createReplayShare: (request, options) => requester.request('create-match-history-replay-share-response', `/api/match-history/${request.historyId}/replay-shares`, {
26
+ body: jsonBody(requester.validate('create-match-history-replay-share-request', {
27
+ identities: request.identities,
28
+ includeHiddenInformation: request.includeHiddenInformation,
29
+ })),
30
+ headers: jsonHeaders(),
31
+ method: 'POST',
32
+ }, options),
33
+ loadReplayShare: (shareToken, options) => requester.request('shared-match-history-replay-response', `/api/match-history/replay-shares/${shareToken}`, {
34
+ method: 'GET',
35
+ }, options),
25
36
  restore: (request, options) => requester.request('restore-history-response', `/api/match-history/${request.historyId}/restore`, {
26
37
  body: jsonBody(requester.validate('match-history-restore-request', {
27
38
  identities: request.identities,
@@ -30,5 +41,10 @@ export function createMatchHistoryResource(options) {
30
41
  headers: jsonHeaders(),
31
42
  method: 'POST',
32
43
  }, options),
44
+ recover: (request, options) => requester.request('match-recovery-response', '/api/match-history/recover', {
45
+ body: jsonBody(requester.validate('match-recovery-request', request)),
46
+ headers: jsonHeaders(),
47
+ method: 'POST',
48
+ }, options),
33
49
  };
34
50
  }
@@ -1,4 +1,4 @@
1
- import type { AiStatusPayload, CreateMatchRequest, GameState, JoinMatchRequest, ListActiveAiMatchesRequest, ListActiveAiMatchesResponse, MatchAccessResponse, MatchSpectatorAccessResponse, MatchSavePayload, RematchMatchResponse, RestoreMatchSaveResponse, SpectateMatchRequest, SubmitMatchDeckRequest } from '../generated/index.js';
1
+ import type { AiStatusPayload, CreateMatchRequest, GameState, JoinMatchRequest, ListActiveAiMatchesRequest, ListActiveAiMatchesResponse, MatchAccessRecoveryRequest, MatchAccessRecoveryResponse, MatchAccessResponse, MatchSpectatorAccessResponse, MatchSavePayload, RematchMatchResponse, RestoreMatchSaveResponse, SpectateMatchRequest, SubmitMatchDeckRequest } from '../generated/index.js';
2
2
  import type { FormatEventCommand } from '../generated/types/format-event-request.js';
3
3
  import type { SimulatorRequestOptions } from '../http/options.js';
4
4
  /**
@@ -87,7 +87,7 @@ export interface MatchAccessInput extends MatchReference {
87
87
  /**
88
88
  * Resolved seed for a private solo-vs-AI match, retained across reconnects.
89
89
  */
90
- replaySeed?: number;
90
+ replaySeed?: string;
91
91
  }
92
92
  export interface MatchSpectatorAccessInput extends MatchReference {
93
93
  /**
@@ -134,6 +134,15 @@ export type MatchConnectionAccessInput = MatchAccessInput | MatchSpectatorAccess
134
134
  * @see https://simulator-sdk.my-swu.com/guide/match-session
135
135
  */
136
136
  export interface MatchesResource {
137
+ /** Refreshes private seat access after validating its existing seat token. */
138
+ refreshAccess(access: MatchAccessInput, options?: SimulatorRequestOptions): Promise<MatchAccess>;
139
+ /**
140
+ * Recovers current host access for an owned remote Solo-versus-AI match.
141
+ *
142
+ * `unavailable` intentionally hides whether the match is absent, no longer
143
+ * resumable, or not owned by the supplied history identities.
144
+ */
145
+ recoverAccess(match: MatchReference, request: MatchAccessRecoveryRequest, options?: SimulatorRequestOptions): Promise<MatchAccessRecoveryResponse>;
137
146
  /** Records an intentional player departure without conceding the match. */
138
147
  leave(access: MatchAccessInput, options?: SimulatorRequestOptions): Promise<void>;
139
148
  /**
@@ -5,6 +5,16 @@ import { jsonBody, jsonHeaders } from '../http/json.js';
5
5
  export function createMatchesResource(options) {
6
6
  const { requester } = options;
7
7
  return {
8
+ refreshAccess: (access, requestOptions) => requester.request('match-access-response', `/api/matches/${matchIdFrom(access)}/access`, {
9
+ body: jsonBody(requester.validate('save-match-request', { seatToken: access.seatToken })),
10
+ headers: jsonHeaders(),
11
+ method: 'POST',
12
+ }, requestOptions),
13
+ recoverAccess: (match, request, options) => requester.request('match-access-recovery-response', `/api/matches/${matchIdFrom(match)}/recover-access`, {
14
+ body: jsonBody(requester.validate('match-access-recovery-request', request)),
15
+ headers: jsonHeaders(),
16
+ method: 'POST',
17
+ }, options),
8
18
  leave: (access, options) => requester.request('leave-match-response', `/api/matches/${matchIdFrom(access)}/leave`, {
9
19
  body: jsonBody(requester.validate('leave-match-request', { seatToken: access.seatToken })),
10
20
  headers: jsonHeaders(),
@@ -1,4 +1,4 @@
1
- import type { SimulatorReportListResponse, SimulatorReportResponse, CloseTournamentRoomRequest, AbandonMatchRequest, AbandonMatchResponse, AddLobbyAiPlayerRequest, ApiErrorResponse, CardCatalogRefreshResponse, CardsResponse, ClientMessage, CreateLimitedEventRequest, CreateLobbyRequest, CreateMetaWindowRequest, CreateMatchRequest, CreateTournamentRoomRequest, CreateTournamentTableRequest, ErrorPayload, FormatEventRequest, FormatEventResponse, GameCommand, GameEvent, GameState, GetMatchResponse, HealthResponse, JoinLimitedEventRequest, JoinLobbyRequest, JoinMatchRequest, JoinTournamentRoomRequest, JoinTournamentTableRequest, KickLobbyPlayerRequest, KickLobbySpectatorRequest, LeaveLobbyRequest, LeaveMatchRequest, LeaveMatchResponse, LeaveTournamentRoomRequest, LimitedEventAccessResponse, LimitedEventCommandRequest, LimitedEventPreflightResponse, LimitedEventResponse, LobbyAccessResponse, LobbyChatRequest, LobbyChatResponse, LobbyListResponse, LobbyListServerMessage, LobbyMatchmakingDecisionRequest, PrepareLobbyLimitedAiDecksRequest, LobbyResponse, LobbyServerMessage, LobbySyncRequest, ListActiveAiMatchesRequest, ListActiveAiMatchesResponse, MatchAccessResponse, MatchChatLookupResponse, MatchConnectionRole, MatchHistoryDeckStatsRequest, MatchHistoryDeckStatsResponse, MatchHistoryEntry, MatchHistoryListRequest, MatchHistoryListResponse, MatchHistoryReplayRequest, MatchHistoryReplayResponse, MatchHistoryRestoreRequest, RestoreHistoryResponse, MatchSavePayload, MatchSpectatorAccessResponse, MetaWindow, MetaWindowsResponse, LobbyMatchmakingRequest, ResolveCardIdsRequest, ResolveCardIdsResponse, RankedAdminStatus, RankedLeaderboardEntry, RankedLeaderboardQuery, RankedLeaderboardResponse, RankedMatchmakingView, RankedProfileRequest, RankedProfileResponse, RankedQueue, RematchMatchRequest, RematchMatchResponse, SaveMatchRequest, ReturnLobbyMatchRequest, ReturnTournamentMatchRequest, RestoreMatchSaveRequest, RestoreMatchSaveResponse, ServerMessage, AiStatusPayload, SetRankedAdmissionRequest, SetSimulatorMaintenanceRequest, SetAiPausedRequest, SimulatorAdminStatusResponse, SimulatorAvailabilityResponse, SpectateLobbyRequest, SpectateMatchRequest, StartLobbyLimitedMatchRequest, StartLobbyRequest, StartTournamentEventRequest, SubmitLobbyLimitedDeckRequest, SubmitMatchDeckRequest, SubmitMatchDeckResponse, SubmitTournamentDeckRequest, TournamentEventCommandRequest, TournamentRoomAccessResponse, TournamentRoomChatRequest, TournamentRoomChatResponse, TournamentRoomListResponse, TournamentRoomResponse, TournamentRoomServerMessage, TournamentRoomSyncRequest, UndoMatchRequest, UndoMatchResponse, UndoRespondMatchRequest, UpdateLobbyAiPlayerRequest, UpdateLobbyPlayerRequest, UpdateLobbySettingsRequest, UpdateTournamentRoomPlayerRequest, UpdateTournamentRoomSettingsRequest, WelcomePayload } from './generated/index.js';
1
+ import type { AdminMatchDebugBundle, AdminMatchListResponse, AdminMatchReplayResponse, SimulatorReportListResponse, SimulatorReportResponse, CloseTournamentRoomRequest, AbandonMatchRequest, AbandonMatchResponse, AddLobbyAiPlayerRequest, ApiErrorResponse, CardCatalogRefreshResponse, CardsResponse, ClientMessage, CreateMatchHistoryReplayShareRequest, CreateMatchHistoryReplayShareResponse, CreateLimitedEventRequest, CreateLobbyRequest, CreateMetaWindowRequest, CreateMatchRequest, CreateTournamentRoomRequest, CreateTournamentTableRequest, ErrorPayload, FormatEventRequest, FormatEventResponse, GameCommand, GameEvent, GameState, GetMatchResponse, HealthResponse, JoinLimitedEventRequest, JoinLobbyRequest, JoinMatchRequest, JoinTournamentRoomRequest, JoinTournamentTableRequest, KickLobbyPlayerRequest, KickLobbySpectatorRequest, LeaveLobbyRequest, LeaveMatchRequest, LeaveMatchResponse, LeaveTournamentRoomRequest, LimitedEventAccessResponse, LimitedEventCommandRequest, LimitedEventPreflightResponse, LimitedEventResponse, LobbyAccessResponse, LobbyChatRequest, LobbyChatResponse, LobbyListResponse, LobbyListServerMessage, LobbyMatchmakingDecisionRequest, PrepareLobbyLimitedAiDecksRequest, LobbyResponse, LobbyServerMessage, LobbySyncRequest, ListActiveAiMatchesRequest, ListActiveAiMatchesResponse, MatchAccessRecoveryRequest, MatchAccessRecoveryResponse, MatchAccessResponse, MatchChatLookupResponse, MatchConnectionRole, MatchHistoryDeckStatsRequest, MatchHistoryDeckStatsResponse, MatchHistoryEntry, MatchHistoryListRequest, MatchHistoryListResponse, MatchHistoryReplayRequest, MatchHistoryReplayResponse, MatchHistoryRestoreRequest, MatchRecoveryRequest, MatchRecoveryResponse, RestoreHistoryResponse, MatchSavePayload, MatchSpectatorAccessResponse, MetaWindow, MetaWindowsResponse, LobbyMatchmakingRequest, ResolveCardIdsRequest, ResolveCardIdsResponse, RankedAdminStatus, RankedLeaderboardEntry, RankedLeaderboardQuery, RankedLeaderboardResponse, RankedMatchmakingView, RankedProfileRequest, RankedProfileResponse, RankedQueue, RematchMatchRequest, RematchMatchResponse, SaveMatchRequest, ReturnLobbyMatchRequest, ReturnTournamentMatchRequest, RestoreMatchSaveRequest, RestoreMatchSaveResponse, ServerMessage, AiStatusPayload, SetRankedAdmissionRequest, SetSimulatorMaintenanceRequest, SetAiPausedRequest, SharedMatchHistoryReplayResponse, SimulatorAdminStatusResponse, SimulatorAvailabilityResponse, SpectateLobbyRequest, SpectateMatchRequest, StartLobbyLimitedMatchRequest, StartLobbyRequest, StartTournamentEventRequest, SubmitLobbyLimitedDeckRequest, SubmitMatchDeckRequest, SubmitMatchDeckResponse, SubmitTournamentDeckRequest, TournamentEventCommandRequest, TournamentRoomAccessResponse, TournamentRoomChatRequest, TournamentRoomChatResponse, TournamentRoomListResponse, TournamentRoomResponse, TournamentRoomServerMessage, TournamentRoomSyncRequest, UndoMatchRequest, UndoMatchResponse, UndoRespondMatchRequest, UpdateLobbyAiPlayerRequest, UpdateLobbyPlayerRequest, UpdateLobbySettingsRequest, UpdateTournamentRoomPlayerRequest, UpdateTournamentRoomSettingsRequest, WelcomePayload } from './generated/index.js';
2
2
  import type { SchemaValidator } from './validators/validator.js';
3
3
  /**
4
4
  * Exact schema-to-type map generated from Rust public wire contracts.
@@ -9,6 +9,9 @@ import type { SchemaValidator } from './validators/validator.js';
9
9
  * @see https://simulator-sdk.my-swu.com/guide/versioning
10
10
  */
11
11
  export interface SchemaTypeMap {
12
+ 'admin-match-list-response': AdminMatchListResponse;
13
+ 'admin-match-debug-bundle': AdminMatchDebugBundle;
14
+ 'admin-match-replay-response': AdminMatchReplayResponse;
12
15
  'simulator-report-list-response': SimulatorReportListResponse;
13
16
  'simulator-report-response': SimulatorReportResponse;
14
17
  'close-tournament-room-request': CloseTournamentRoomRequest;
@@ -69,8 +72,15 @@ export interface SchemaTypeMap {
69
72
  'match-history-list-response': MatchHistoryListResponse;
70
73
  'match-history-replay-request': MatchHistoryReplayRequest;
71
74
  'match-history-replay-response': MatchHistoryReplayResponse;
75
+ 'create-match-history-replay-share-request': CreateMatchHistoryReplayShareRequest;
76
+ 'create-match-history-replay-share-response': CreateMatchHistoryReplayShareResponse;
77
+ 'shared-match-history-replay-response': SharedMatchHistoryReplayResponse;
72
78
  'match-history-restore-request': MatchHistoryRestoreRequest;
73
79
  'restore-history-response': RestoreHistoryResponse;
80
+ 'match-recovery-request': MatchRecoveryRequest;
81
+ 'match-recovery-response': MatchRecoveryResponse;
82
+ 'match-access-recovery-request': MatchAccessRecoveryRequest;
83
+ 'match-access-recovery-response': MatchAccessRecoveryResponse;
74
84
  'match-save-payload': MatchSavePayload;
75
85
  'save-match-request': SaveMatchRequest;
76
86
  'match-spectator-access-response': MatchSpectatorAccessResponse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@my-swu/simulator-client",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Typed TypeScript client for the swu-simulator HTTP and WebSocket engine.",
5
5
  "keywords": [
6
6
  "swu",
@@ -3,6 +3,13 @@
3
3
  "title": "AddLobbyAiPlayerRequest",
4
4
  "type": "object",
5
5
  "properties": {
6
+ "accountToken": {
7
+ "description": "Signed app account proof required to create a server-controlled AI.",
8
+ "type": [
9
+ "string",
10
+ "null"
11
+ ]
12
+ },
6
13
  "deck": {
7
14
  "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/DeckInput"
8
15
  },
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "AdminMatchDebugBundle",
4
+ "description": "Complete durable diagnostics for one match, excluding restore credentials.",
5
+ "type": "object",
6
+ "properties": {
7
+ "exportVersion": {
8
+ "type": "integer",
9
+ "format": "uint8",
10
+ "maximum": 255,
11
+ "minimum": 0
12
+ },
13
+ "exportedAtMs": {
14
+ "type": "integer",
15
+ "format": "uint64",
16
+ "minimum": 0
17
+ },
18
+ "game": {
19
+ "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/AdminMatchSummary"
20
+ },
21
+ "replay": {
22
+ "anyOf": [
23
+ {
24
+ "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/MatchHistoryReplayArchive"
25
+ },
26
+ {
27
+ "type": "null"
28
+ }
29
+ ]
30
+ },
31
+ "reports": {
32
+ "type": "array",
33
+ "items": {
34
+ "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/SimulatorReportResponse"
35
+ }
36
+ },
37
+ "save": {
38
+ "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/MatchSavePayload"
39
+ }
40
+ },
41
+ "required": [
42
+ "exportVersion",
43
+ "exportedAtMs",
44
+ "game",
45
+ "save",
46
+ "reports"
47
+ ]
48
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "AdminMatchListResponse",
4
+ "description": "Cursor-paginated durable match list for simulator administrators.",
5
+ "type": "object",
6
+ "properties": {
7
+ "games": {
8
+ "type": "array",
9
+ "items": {
10
+ "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/AdminMatchSummary"
11
+ }
12
+ },
13
+ "nextCursor": {
14
+ "type": [
15
+ "string",
16
+ "null"
17
+ ]
18
+ }
19
+ },
20
+ "required": [
21
+ "games"
22
+ ]
23
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "AdminMatchReplayResponse",
4
+ "description": "Completed replay archive returned to an authenticated simulator administrator.",
5
+ "type": "object",
6
+ "properties": {
7
+ "archive": {
8
+ "$ref": "https://simulator-sdk.my-swu.com/schema/common.json#/$defs/MatchHistoryReplayArchive"
9
+ }
10
+ },
11
+ "required": [
12
+ "archive"
13
+ ]
14
+ }