@my-swu/simulator-client 0.1.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/LICENSE +21 -0
- package/README.md +355 -0
- package/dist/actionable.d.ts +61 -0
- package/dist/actionable.js +91 -0
- package/dist/client.d.ts +157 -0
- package/dist/client.js +92 -0
- package/dist/connection/handshake.d.ts +30 -0
- package/dist/connection/handshake.js +62 -0
- package/dist/connection/options.d.ts +36 -0
- package/dist/connection/options.js +1 -0
- package/dist/connection/url.d.ts +55 -0
- package/dist/connection/url.js +46 -0
- package/dist/connection/wait.d.ts +22 -0
- package/dist/connection/wait.js +10 -0
- package/dist/connection.d.ts +110 -0
- package/dist/connection.js +358 -0
- package/dist/generated/index.d.ts +121 -0
- package/dist/generated/index.js +1 -0
- package/dist/generated/public-types.d.ts +2 -0
- package/dist/generated/public-types.js +1 -0
- package/dist/generated/schemas.d.ts +27 -0
- package/dist/generated/schemas.js +21264 -0
- package/dist/generated/types/abandon-match-request.d.ts +1 -0
- package/dist/generated/types/abandon-match-request.js +2 -0
- package/dist/generated/types/abandon-match-response.d.ts +1 -0
- package/dist/generated/types/abandon-match-response.js +2 -0
- package/dist/generated/types/add-lobby-ai-player-request.d.ts +1 -0
- package/dist/generated/types/add-lobby-ai-player-request.js +2 -0
- package/dist/generated/types/ai-driver-error-report-list-response.d.ts +1 -0
- package/dist/generated/types/ai-driver-error-report-list-response.js +2 -0
- package/dist/generated/types/ai-driver-error-report-response.d.ts +1 -0
- package/dist/generated/types/ai-driver-error-report-response.js +2 -0
- package/dist/generated/types/api-error-response.d.ts +1 -0
- package/dist/generated/types/api-error-response.js +2 -0
- package/dist/generated/types/card-catalog-refresh-response.d.ts +1 -0
- package/dist/generated/types/card-catalog-refresh-response.js +2 -0
- package/dist/generated/types/cards-response.d.ts +1 -0
- package/dist/generated/types/cards-response.js +2 -0
- package/dist/generated/types/client-message.d.ts +1 -0
- package/dist/generated/types/client-message.js +2 -0
- package/dist/generated/types/close-tournament-room-request.d.ts +1 -0
- package/dist/generated/types/close-tournament-room-request.js +2 -0
- package/dist/generated/types/common.d.ts +5361 -0
- package/dist/generated/types/common.js +2 -0
- package/dist/generated/types/covered-cards-response.d.ts +47 -0
- package/dist/generated/types/covered-cards-response.js +2 -0
- package/dist/generated/types/create-limited-event-request.d.ts +1 -0
- package/dist/generated/types/create-limited-event-request.js +2 -0
- package/dist/generated/types/create-lobby-request.d.ts +1 -0
- package/dist/generated/types/create-lobby-request.js +2 -0
- package/dist/generated/types/create-match-request.d.ts +1 -0
- package/dist/generated/types/create-match-request.js +2 -0
- package/dist/generated/types/create-meta-window-request.d.ts +1 -0
- package/dist/generated/types/create-meta-window-request.js +2 -0
- package/dist/generated/types/create-tournament-room-request.d.ts +1 -0
- package/dist/generated/types/create-tournament-room-request.js +2 -0
- package/dist/generated/types/create-tournament-table-request.d.ts +1 -0
- package/dist/generated/types/create-tournament-table-request.js +2 -0
- package/dist/generated/types/error-payload.d.ts +1 -0
- package/dist/generated/types/error-payload.js +2 -0
- package/dist/generated/types/format-event-request.d.ts +1 -0
- package/dist/generated/types/format-event-request.js +2 -0
- package/dist/generated/types/format-event-response.d.ts +1 -0
- package/dist/generated/types/format-event-response.js +2 -0
- package/dist/generated/types/game-command.d.ts +1 -0
- package/dist/generated/types/game-command.js +2 -0
- package/dist/generated/types/game-event.d.ts +1 -0
- package/dist/generated/types/game-event.js +2 -0
- package/dist/generated/types/game-state.d.ts +1 -0
- package/dist/generated/types/game-state.js +2 -0
- package/dist/generated/types/get-match-response.d.ts +1 -0
- package/dist/generated/types/get-match-response.js +2 -0
- package/dist/generated/types/health-response.d.ts +1 -0
- package/dist/generated/types/health-response.js +2 -0
- package/dist/generated/types/join-limited-event-request.d.ts +1 -0
- package/dist/generated/types/join-limited-event-request.js +2 -0
- package/dist/generated/types/join-lobby-request.d.ts +1 -0
- package/dist/generated/types/join-lobby-request.js +2 -0
- package/dist/generated/types/join-match-request.d.ts +1 -0
- package/dist/generated/types/join-match-request.js +2 -0
- package/dist/generated/types/join-tournament-room-request.d.ts +1 -0
- package/dist/generated/types/join-tournament-room-request.js +2 -0
- package/dist/generated/types/join-tournament-table-request.d.ts +1 -0
- package/dist/generated/types/join-tournament-table-request.js +2 -0
- package/dist/generated/types/kick-lobby-player-request.d.ts +1 -0
- package/dist/generated/types/kick-lobby-player-request.js +2 -0
- package/dist/generated/types/kick-lobby-spectator-request.d.ts +1 -0
- package/dist/generated/types/kick-lobby-spectator-request.js +2 -0
- package/dist/generated/types/leave-lobby-request.d.ts +1 -0
- package/dist/generated/types/leave-lobby-request.js +2 -0
- package/dist/generated/types/leave-match-request.d.ts +1 -0
- package/dist/generated/types/leave-match-request.js +2 -0
- package/dist/generated/types/leave-match-response.d.ts +1 -0
- package/dist/generated/types/leave-match-response.js +2 -0
- package/dist/generated/types/leave-tournament-room-request.d.ts +1 -0
- package/dist/generated/types/leave-tournament-room-request.js +2 -0
- package/dist/generated/types/limited-event-access-response.d.ts +1 -0
- package/dist/generated/types/limited-event-access-response.js +2 -0
- package/dist/generated/types/limited-event-command-request.d.ts +1 -0
- package/dist/generated/types/limited-event-command-request.js +2 -0
- package/dist/generated/types/limited-event-preflight-response.d.ts +1 -0
- package/dist/generated/types/limited-event-preflight-response.js +2 -0
- package/dist/generated/types/limited-event-response.d.ts +1 -0
- package/dist/generated/types/limited-event-response.js +2 -0
- package/dist/generated/types/list-active-ai-matches-request.d.ts +1 -0
- package/dist/generated/types/list-active-ai-matches-request.js +2 -0
- package/dist/generated/types/list-active-ai-matches-response.d.ts +1 -0
- package/dist/generated/types/list-active-ai-matches-response.js +2 -0
- package/dist/generated/types/lobby-access-response.d.ts +1 -0
- package/dist/generated/types/lobby-access-response.js +2 -0
- package/dist/generated/types/lobby-chat-request.d.ts +1 -0
- package/dist/generated/types/lobby-chat-request.js +2 -0
- package/dist/generated/types/lobby-chat-response.d.ts +1 -0
- package/dist/generated/types/lobby-chat-response.js +2 -0
- package/dist/generated/types/lobby-list-response.d.ts +1 -0
- package/dist/generated/types/lobby-list-response.js +2 -0
- package/dist/generated/types/lobby-list-server-message.d.ts +1 -0
- package/dist/generated/types/lobby-list-server-message.js +2 -0
- package/dist/generated/types/lobby-matchmaking-decision-request.d.ts +1 -0
- package/dist/generated/types/lobby-matchmaking-decision-request.js +2 -0
- package/dist/generated/types/lobby-matchmaking-request.d.ts +1 -0
- package/dist/generated/types/lobby-matchmaking-request.js +2 -0
- package/dist/generated/types/lobby-response.d.ts +1 -0
- package/dist/generated/types/lobby-response.js +2 -0
- package/dist/generated/types/lobby-server-message.d.ts +1 -0
- package/dist/generated/types/lobby-server-message.js +2 -0
- package/dist/generated/types/lobby-sync-request.d.ts +1 -0
- package/dist/generated/types/lobby-sync-request.js +2 -0
- package/dist/generated/types/match-access-response.d.ts +1 -0
- package/dist/generated/types/match-access-response.js +2 -0
- package/dist/generated/types/match-chat-lookup-response.d.ts +1 -0
- package/dist/generated/types/match-chat-lookup-response.js +2 -0
- package/dist/generated/types/match-connection-role.d.ts +1 -0
- package/dist/generated/types/match-connection-role.js +2 -0
- package/dist/generated/types/match-history-deck-stats-request.d.ts +1 -0
- package/dist/generated/types/match-history-deck-stats-request.js +2 -0
- package/dist/generated/types/match-history-deck-stats-response.d.ts +1 -0
- package/dist/generated/types/match-history-deck-stats-response.js +2 -0
- package/dist/generated/types/match-history-entry.d.ts +1 -0
- package/dist/generated/types/match-history-entry.js +2 -0
- package/dist/generated/types/match-history-list-request.d.ts +1 -0
- package/dist/generated/types/match-history-list-request.js +2 -0
- package/dist/generated/types/match-history-list-response.d.ts +1 -0
- package/dist/generated/types/match-history-list-response.js +2 -0
- package/dist/generated/types/match-history-replay-request.d.ts +1 -0
- package/dist/generated/types/match-history-replay-request.js +2 -0
- package/dist/generated/types/match-history-replay-response.d.ts +1 -0
- package/dist/generated/types/match-history-replay-response.js +2 -0
- package/dist/generated/types/match-history-restore-request.d.ts +1 -0
- package/dist/generated/types/match-history-restore-request.js +2 -0
- package/dist/generated/types/match-save-payload.d.ts +1 -0
- package/dist/generated/types/match-save-payload.js +2 -0
- package/dist/generated/types/match-spectator-access-response.d.ts +1 -0
- package/dist/generated/types/match-spectator-access-response.js +2 -0
- package/dist/generated/types/meta-window.d.ts +1 -0
- package/dist/generated/types/meta-window.js +2 -0
- package/dist/generated/types/meta-windows-response.d.ts +1 -0
- package/dist/generated/types/meta-windows-response.js +2 -0
- package/dist/generated/types/prepare-lobby-limited-ai-decks-request.d.ts +1 -0
- package/dist/generated/types/prepare-lobby-limited-ai-decks-request.js +2 -0
- package/dist/generated/types/random-lobby-matchmaking-request.d.ts +173 -0
- package/dist/generated/types/random-lobby-matchmaking-request.js +2 -0
- package/dist/generated/types/ranked-admin-status.d.ts +1 -0
- package/dist/generated/types/ranked-admin-status.js +2 -0
- package/dist/generated/types/ranked-leaderboard-entry.d.ts +1 -0
- package/dist/generated/types/ranked-leaderboard-entry.js +2 -0
- package/dist/generated/types/ranked-leaderboard-query.d.ts +1 -0
- package/dist/generated/types/ranked-leaderboard-query.js +2 -0
- package/dist/generated/types/ranked-leaderboard-response.d.ts +1 -0
- package/dist/generated/types/ranked-leaderboard-response.js +2 -0
- package/dist/generated/types/ranked-matchmaking-request.d.ts +137 -0
- package/dist/generated/types/ranked-matchmaking-request.js +2 -0
- package/dist/generated/types/ranked-matchmaking-view.d.ts +1 -0
- package/dist/generated/types/ranked-matchmaking-view.js +2 -0
- package/dist/generated/types/ranked-profile-request.d.ts +1 -0
- package/dist/generated/types/ranked-profile-request.js +2 -0
- package/dist/generated/types/ranked-profile-response.d.ts +1 -0
- package/dist/generated/types/ranked-profile-response.js +2 -0
- package/dist/generated/types/ranked-queue.d.ts +1 -0
- package/dist/generated/types/ranked-queue.js +2 -0
- package/dist/generated/types/ready-tournament-table-request.d.ts +7 -0
- package/dist/generated/types/ready-tournament-table-request.js +2 -0
- package/dist/generated/types/rematch-match-request.d.ts +1 -0
- package/dist/generated/types/rematch-match-request.js +2 -0
- package/dist/generated/types/rematch-match-response.d.ts +1 -0
- package/dist/generated/types/rematch-match-response.js +2 -0
- package/dist/generated/types/resolve-card-ids-request.d.ts +1 -0
- package/dist/generated/types/resolve-card-ids-request.js +2 -0
- package/dist/generated/types/resolve-card-ids-response.d.ts +1 -0
- package/dist/generated/types/resolve-card-ids-response.js +2 -0
- package/dist/generated/types/restore-history-response.d.ts +1 -0
- package/dist/generated/types/restore-history-response.js +2 -0
- package/dist/generated/types/restore-match-save-request.d.ts +1 -0
- package/dist/generated/types/restore-match-save-request.js +2 -0
- package/dist/generated/types/restore-match-save-response.d.ts +1 -0
- package/dist/generated/types/restore-match-save-response.js +2 -0
- package/dist/generated/types/return-lobby-match-request.d.ts +1 -0
- package/dist/generated/types/return-lobby-match-request.js +2 -0
- package/dist/generated/types/return-tournament-match-request.d.ts +1 -0
- package/dist/generated/types/return-tournament-match-request.js +2 -0
- package/dist/generated/types/save-match-request.d.ts +1 -0
- package/dist/generated/types/save-match-request.js +2 -0
- package/dist/generated/types/server-message.d.ts +1 -0
- package/dist/generated/types/server-message.js +2 -0
- package/dist/generated/types/set-ai-paused-request.d.ts +1 -0
- package/dist/generated/types/set-ai-paused-request.js +2 -0
- package/dist/generated/types/set-ai-paused-response.d.ts +1 -0
- package/dist/generated/types/set-ai-paused-response.js +2 -0
- package/dist/generated/types/set-ranked-admission-request.d.ts +1 -0
- package/dist/generated/types/set-ranked-admission-request.js +2 -0
- package/dist/generated/types/set-simulator-maintenance-request.d.ts +1 -0
- package/dist/generated/types/set-simulator-maintenance-request.js +2 -0
- package/dist/generated/types/simulator-admin-status-response.d.ts +1 -0
- package/dist/generated/types/simulator-admin-status-response.js +2 -0
- package/dist/generated/types/simulator-availability-response.d.ts +1 -0
- package/dist/generated/types/simulator-availability-response.js +2 -0
- package/dist/generated/types/simulator-report-list-response.d.ts +1 -0
- package/dist/generated/types/simulator-report-list-response.js +2 -0
- package/dist/generated/types/simulator-report-response.d.ts +1 -0
- package/dist/generated/types/simulator-report-response.js +2 -0
- package/dist/generated/types/spectate-lobby-request.d.ts +1 -0
- package/dist/generated/types/spectate-lobby-request.js +2 -0
- package/dist/generated/types/spectate-match-request.d.ts +1 -0
- package/dist/generated/types/spectate-match-request.js +2 -0
- package/dist/generated/types/start-lobby-limited-match-request.d.ts +1 -0
- package/dist/generated/types/start-lobby-limited-match-request.js +2 -0
- package/dist/generated/types/start-lobby-request.d.ts +1 -0
- package/dist/generated/types/start-lobby-request.js +2 -0
- package/dist/generated/types/start-tournament-event-request.d.ts +1 -0
- package/dist/generated/types/start-tournament-event-request.js +2 -0
- package/dist/generated/types/stats-response.d.ts +5 -0
- package/dist/generated/types/stats-response.js +2 -0
- package/dist/generated/types/submit-lobby-limited-deck-request.d.ts +1 -0
- package/dist/generated/types/submit-lobby-limited-deck-request.js +2 -0
- package/dist/generated/types/submit-match-deck-request.d.ts +1 -0
- package/dist/generated/types/submit-match-deck-request.js +2 -0
- package/dist/generated/types/submit-match-deck-response.d.ts +1 -0
- package/dist/generated/types/submit-match-deck-response.js +2 -0
- package/dist/generated/types/submit-tournament-deck-request.d.ts +1 -0
- package/dist/generated/types/submit-tournament-deck-request.js +2 -0
- package/dist/generated/types/tournament-event-command-request.d.ts +1 -0
- package/dist/generated/types/tournament-event-command-request.js +2 -0
- package/dist/generated/types/tournament-room-access-response.d.ts +1 -0
- package/dist/generated/types/tournament-room-access-response.js +2 -0
- package/dist/generated/types/tournament-room-chat-request.d.ts +1 -0
- package/dist/generated/types/tournament-room-chat-request.js +2 -0
- package/dist/generated/types/tournament-room-chat-response.d.ts +1 -0
- package/dist/generated/types/tournament-room-chat-response.js +2 -0
- package/dist/generated/types/tournament-room-list-response.d.ts +1 -0
- package/dist/generated/types/tournament-room-list-response.js +2 -0
- package/dist/generated/types/tournament-room-response.d.ts +1 -0
- package/dist/generated/types/tournament-room-response.js +2 -0
- package/dist/generated/types/tournament-room-server-message.d.ts +1 -0
- package/dist/generated/types/tournament-room-server-message.js +2 -0
- package/dist/generated/types/tournament-room-sync-request.d.ts +1 -0
- package/dist/generated/types/tournament-room-sync-request.js +2 -0
- package/dist/generated/types/undo-match-request.d.ts +1 -0
- package/dist/generated/types/undo-match-request.js +2 -0
- package/dist/generated/types/undo-match-response.d.ts +1 -0
- package/dist/generated/types/undo-match-response.js +2 -0
- package/dist/generated/types/undo-respond-match-request.d.ts +1 -0
- package/dist/generated/types/undo-respond-match-request.js +2 -0
- package/dist/generated/types/update-lobby-ai-player-request.d.ts +1 -0
- package/dist/generated/types/update-lobby-ai-player-request.js +2 -0
- package/dist/generated/types/update-lobby-player-request.d.ts +1 -0
- package/dist/generated/types/update-lobby-player-request.js +2 -0
- package/dist/generated/types/update-lobby-settings-request.d.ts +1 -0
- package/dist/generated/types/update-lobby-settings-request.js +2 -0
- package/dist/generated/types/update-tournament-room-player-request.d.ts +1 -0
- package/dist/generated/types/update-tournament-room-player-request.js +2 -0
- package/dist/generated/types/update-tournament-room-settings-request.d.ts +1 -0
- package/dist/generated/types/update-tournament-room-settings-request.js +2 -0
- package/dist/generated/types/update-tournament-table-deck-request.d.ts +123 -0
- package/dist/generated/types/update-tournament-table-deck-request.js +2 -0
- package/dist/generated/types/welcome-payload.d.ts +1 -0
- package/dist/generated/types/welcome-payload.js +2 -0
- package/dist/generated/version.d.ts +16 -0
- package/dist/generated/version.js +16 -0
- package/dist/http/abort.d.ts +57 -0
- package/dist/http/abort.js +60 -0
- package/dist/http/json.d.ts +13 -0
- package/dist/http/json.js +24 -0
- package/dist/http/options.d.ts +40 -0
- package/dist/http/options.js +1 -0
- package/dist/http/request.d.ts +64 -0
- package/dist/http/request.js +182 -0
- package/dist/http/validated-json.d.ts +9 -0
- package/dist/http/validated-json.js +14 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +10 -0
- package/dist/internal/abort-error.d.ts +91 -0
- package/dist/internal/abort-error.js +62 -0
- package/dist/internal/authenticated-socket-session.d.ts +63 -0
- package/dist/internal/authenticated-socket-session.js +170 -0
- package/dist/internal/emitter.d.ts +18 -0
- package/dist/internal/emitter.js +34 -0
- package/dist/internal/errors.d.ts +233 -0
- package/dist/internal/errors.js +238 -0
- package/dist/internal/limits.d.ts +13 -0
- package/dist/internal/limits.js +13 -0
- package/dist/internal/reconnect-backoff.d.ts +33 -0
- package/dist/internal/reconnect-backoff.js +25 -0
- package/dist/internal/socket-frame.d.ts +43 -0
- package/dist/internal/socket-frame.js +47 -0
- package/dist/internal/transport.d.ts +78 -0
- package/dist/internal/transport.js +18 -0
- package/dist/internal/websocket-event.d.ts +15 -0
- package/dist/internal/websocket-event.js +31 -0
- package/dist/local/assets.d.ts +96 -0
- package/dist/local/assets.js +235 -0
- package/dist/local/client.d.ts +32 -0
- package/dist/local/client.js +211 -0
- package/dist/local/engine.d.ts +79 -0
- package/dist/local/engine.js +6 -0
- package/dist/local/index.d.ts +20 -0
- package/dist/local/index.js +17 -0
- package/dist/local/offline-engine.d.ts +46 -0
- package/dist/local/offline-engine.js +74 -0
- package/dist/local/protocol.d.ts +95 -0
- package/dist/local/protocol.js +10 -0
- package/dist/local/routing.d.ts +52 -0
- package/dist/local/routing.js +193 -0
- package/dist/local/server/ai-scheduler.d.ts +31 -0
- package/dist/local/server/ai-scheduler.js +93 -0
- package/dist/local/server/http-router.d.ts +35 -0
- package/dist/local/server/http-router.js +185 -0
- package/dist/local/server/index.d.ts +16 -0
- package/dist/local/server/index.js +140 -0
- package/dist/local/server/sockets.d.ts +25 -0
- package/dist/local/server/sockets.js +141 -0
- package/dist/local/wasm-engine.d.ts +61 -0
- package/dist/local/wasm-engine.js +63 -0
- package/dist/local/worker-boot.d.ts +17 -0
- package/dist/local/worker-boot.js +76 -0
- package/dist/local/worker.d.ts +6 -0
- package/dist/local/worker.js +9 -0
- package/dist/match-session/access.d.ts +6 -0
- package/dist/match-session/access.js +8 -0
- package/dist/match-session/controller.d.ts +63 -0
- package/dist/match-session/controller.js +513 -0
- package/dist/match-session/errors.d.ts +3 -0
- package/dist/match-session/errors.js +34 -0
- package/dist/match-session/methods.d.ts +433 -0
- package/dist/match-session/methods.js +1 -0
- package/dist/match-session/session-state.d.ts +235 -0
- package/dist/match-session/session-state.js +1 -0
- package/dist/match-session/types.d.ts +56 -0
- package/dist/match-session/types.js +1 -0
- package/dist/match-session.d.ts +3 -0
- package/dist/match-session.js +1 -0
- package/dist/messages.d.ts +195 -0
- package/dist/messages.js +240 -0
- package/dist/resources/admin.d.ts +63 -0
- package/dist/resources/admin.js +75 -0
- package/dist/resources/card-deck-json.d.ts +11 -0
- package/dist/resources/card-deck-json.js +164 -0
- package/dist/resources/card-resolution.d.ts +9 -0
- package/dist/resources/card-resolution.js +21 -0
- package/dist/resources/card-resource.d.ts +157 -0
- package/dist/resources/card-resource.js +28 -0
- package/dist/resources/card-types.d.ts +203 -0
- package/dist/resources/card-types.js +1 -0
- package/dist/resources/cards.d.ts +3 -0
- package/dist/resources/cards.js +1 -0
- package/dist/resources/limited-event-types.d.ts +252 -0
- package/dist/resources/limited-event-types.js +1 -0
- package/dist/resources/limited-events.d.ts +7 -0
- package/dist/resources/limited-events.js +49 -0
- package/dist/resources/lobbies.d.ts +53 -0
- package/dist/resources/lobbies.js +50 -0
- package/dist/resources/lobby-list-session.d.ts +13 -0
- package/dist/resources/lobby-list-session.js +198 -0
- package/dist/resources/lobby-session.d.ts +9 -0
- package/dist/resources/lobby-session.js +59 -0
- package/dist/resources/lobby-types.d.ts +357 -0
- package/dist/resources/lobby-types.js +1 -0
- package/dist/resources/match-history.d.ts +49 -0
- package/dist/resources/match-history.js +34 -0
- package/dist/resources/match-types.d.ts +448 -0
- package/dist/resources/match-types.js +1 -0
- package/dist/resources/matches.d.ts +16 -0
- package/dist/resources/matches.js +109 -0
- package/dist/resources/ranked.d.ts +24 -0
- package/dist/resources/ranked.js +47 -0
- package/dist/resources/session.d.ts +62 -0
- package/dist/resources/session.js +26 -0
- package/dist/resources/system.d.ts +51 -0
- package/dist/resources/system.js +9 -0
- package/dist/resources/tournament-room-session.d.ts +9 -0
- package/dist/resources/tournament-room-session.js +53 -0
- package/dist/resources/tournament-room-types.d.ts +44 -0
- package/dist/resources/tournament-room-types.js +1 -0
- package/dist/resources/tournament-rooms.d.ts +33 -0
- package/dist/resources/tournament-rooms.js +33 -0
- package/dist/session.d.ts +56 -0
- package/dist/session.js +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.js +27 -0
- package/dist/validators/ajv.d.ts +51 -0
- package/dist/validators/ajv.js +121 -0
- package/dist/validators/discriminants.d.ts +36 -0
- package/dist/validators/discriminants.js +89 -0
- package/dist/validators/validator.d.ts +73 -0
- package/dist/validators/validator.js +26 -0
- package/dist/validators/warm-up.d.ts +25 -0
- package/dist/validators/warm-up.js +59 -0
- package/dist/validators.d.ts +173 -0
- package/dist/validators.js +85 -0
- package/package.json +88 -0
- package/schema/abandon-match-request.schema.json +15 -0
- package/schema/abandon-match-response.schema.json +15 -0
- package/schema/add-lobby-ai-player-request.schema.json +53 -0
- package/schema/api-error-response.schema.json +13 -0
- package/schema/card-catalog-refresh-response.schema.json +31 -0
- package/schema/cards-response.schema.json +17 -0
- package/schema/client-message.schema.json +203 -0
- package/schema/close-tournament-room-request.schema.json +18 -0
- package/schema/common.schema.json +14129 -0
- package/schema/create-limited-event-request.schema.json +60 -0
- package/schema/create-lobby-request.schema.json +67 -0
- package/schema/create-match-request.schema.json +124 -0
- package/schema/create-meta-window-request.schema.json +27 -0
- package/schema/create-tournament-room-request.schema.json +104 -0
- package/schema/create-tournament-table-request.schema.json +14 -0
- package/schema/error-payload.schema.json +34 -0
- package/schema/format-event-request.schema.json +20 -0
- package/schema/format-event-response.schema.json +15 -0
- package/schema/game-command.schema.json +775 -0
- package/schema/game-event.schema.json +2012 -0
- package/schema/game-state.schema.json +273 -0
- package/schema/get-match-response.schema.json +13 -0
- package/schema/health-response.schema.json +13 -0
- package/schema/join-limited-event-request.schema.json +6 -0
- package/schema/join-lobby-request.schema.json +40 -0
- package/schema/join-match-request.schema.json +79 -0
- package/schema/join-tournament-room-request.schema.json +39 -0
- package/schema/join-tournament-table-request.schema.json +14 -0
- package/schema/kick-lobby-player-request.schema.json +13 -0
- package/schema/kick-lobby-spectator-request.schema.json +14 -0
- package/schema/leave-lobby-request.schema.json +13 -0
- package/schema/leave-match-request.schema.json +15 -0
- package/schema/leave-match-response.schema.json +15 -0
- package/schema/leave-tournament-room-request.schema.json +14 -0
- package/schema/limited-event-access-response.schema.json +34 -0
- package/schema/limited-event-command-request.schema.json +20 -0
- package/schema/limited-event-preflight-response.schema.json +25 -0
- package/schema/limited-event-response.schema.json +15 -0
- package/schema/list-active-ai-matches-request.schema.json +23 -0
- package/schema/list-active-ai-matches-response.schema.json +17 -0
- package/schema/lobby-access-response.schema.json +31 -0
- package/schema/lobby-chat-request.schema.json +17 -0
- package/schema/lobby-chat-response.schema.json +17 -0
- package/schema/lobby-list-response.schema.json +16 -0
- package/schema/lobby-list-server-message.schema.json +49 -0
- package/schema/lobby-matchmaking-decision-request.schema.json +13 -0
- package/schema/lobby-matchmaking-request.schema.json +80 -0
- package/schema/lobby-response.schema.json +13 -0
- package/schema/lobby-server-message.schema.json +155 -0
- package/schema/lobby-sync-request.schema.json +13 -0
- package/schema/manifest.json +124 -0
- package/schema/match-access-response.schema.json +32 -0
- package/schema/match-chat-lookup-response.schema.json +34 -0
- package/schema/match-connection-role.schema.json +9 -0
- package/schema/match-history-deck-stats-request.schema.json +26 -0
- package/schema/match-history-deck-stats-response.schema.json +17 -0
- package/schema/match-history-entry.schema.json +78 -0
- package/schema/match-history-list-request.schema.json +48 -0
- package/schema/match-history-list-response.schema.json +25 -0
- package/schema/match-history-replay-request.schema.json +17 -0
- package/schema/match-history-replay-response.schema.json +14 -0
- package/schema/match-history-restore-request.schema.json +23 -0
- package/schema/match-save-payload.schema.json +71 -0
- package/schema/match-spectator-access-response.schema.json +22 -0
- package/schema/meta-window.schema.json +37 -0
- package/schema/meta-windows-response.schema.json +24 -0
- package/schema/prepare-lobby-limited-ai-decks-request.schema.json +14 -0
- package/schema/ranked-admin-status.schema.json +43 -0
- package/schema/ranked-leaderboard-entry.schema.json +61 -0
- package/schema/ranked-leaderboard-query.schema.json +34 -0
- package/schema/ranked-leaderboard-response.schema.json +23 -0
- package/schema/ranked-matchmaking-view.schema.json +15 -0
- package/schema/ranked-profile-request.schema.json +21 -0
- package/schema/ranked-profile-response.schema.json +59 -0
- package/schema/ranked-queue.schema.json +12 -0
- package/schema/rematch-match-request.schema.json +20 -0
- package/schema/rematch-match-response.schema.json +61 -0
- package/schema/resolve-card-ids-request.schema.json +17 -0
- package/schema/resolve-card-ids-response.schema.json +16 -0
- package/schema/restore-history-response.schema.json +15 -0
- package/schema/restore-match-save-request.schema.json +20 -0
- package/schema/restore-match-save-response.schema.json +29 -0
- package/schema/return-lobby-match-request.schema.json +13 -0
- package/schema/return-tournament-match-request.schema.json +14 -0
- package/schema/route-manifest.json +1014 -0
- package/schema/save-match-request.schema.json +15 -0
- package/schema/server-message.schema.json +192 -0
- package/schema/set-ai-paused-request.schema.json +19 -0
- package/schema/set-ai-paused-response.schema.json +18 -0
- package/schema/set-ranked-admission-request.schema.json +15 -0
- package/schema/set-simulator-maintenance-request.schema.json +20 -0
- package/schema/simulator-admin-status-response.schema.json +37 -0
- package/schema/simulator-availability-response.schema.json +27 -0
- package/schema/simulator-report-list-response.schema.json +23 -0
- package/schema/simulator-report-response.schema.json +77 -0
- package/schema/spectate-lobby-request.schema.json +32 -0
- package/schema/spectate-match-request.schema.json +32 -0
- package/schema/start-lobby-limited-match-request.schema.json +13 -0
- package/schema/start-lobby-request.schema.json +13 -0
- package/schema/start-tournament-event-request.schema.json +14 -0
- package/schema/submit-lobby-limited-deck-request.schema.json +33 -0
- package/schema/submit-match-deck-request.schema.json +45 -0
- package/schema/submit-match-deck-response.schema.json +14 -0
- package/schema/submit-tournament-deck-request.schema.json +34 -0
- package/schema/tournament-event-command-request.schema.json +18 -0
- package/schema/tournament-room-access-response.schema.json +32 -0
- package/schema/tournament-room-chat-request.schema.json +18 -0
- package/schema/tournament-room-chat-response.schema.json +18 -0
- package/schema/tournament-room-list-response.schema.json +17 -0
- package/schema/tournament-room-response.schema.json +14 -0
- package/schema/tournament-room-server-message.schema.json +127 -0
- package/schema/tournament-room-sync-request.schema.json +14 -0
- package/schema/undo-match-request.schema.json +14 -0
- package/schema/undo-match-response.schema.json +14 -0
- package/schema/undo-respond-match-request.schema.json +19 -0
- package/schema/update-lobby-ai-player-request.schema.json +65 -0
- package/schema/update-lobby-player-request.schema.json +81 -0
- package/schema/update-lobby-settings-request.schema.json +70 -0
- package/schema/update-tournament-room-player-request.schema.json +45 -0
- package/schema/update-tournament-room-settings-request.schema.json +33 -0
- package/schema/welcome-payload.schema.json +38 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing transport: network by default, the in-browser engine for offline
|
|
3
|
+
* matches. Injectable into `createSimulatorClient` as its `fetch` +
|
|
4
|
+
* `webSocketFactory`.
|
|
5
|
+
*
|
|
6
|
+
* Routing rules, in order:
|
|
7
|
+
* 1. `/api/availability` is ALWAYS network — it describes the real backend.
|
|
8
|
+
* 2. Requests inside a `runWithLocalScope` callback go local (how offline
|
|
9
|
+
* match/lobby creation is opted in).
|
|
10
|
+
* 3. Requests whose path references a registered local id go local — covers
|
|
11
|
+
* every post-create call including reconnect after a page reload.
|
|
12
|
+
* 4. `/api/cards*` goes local while the engine is running (offline metadata).
|
|
13
|
+
* 5. Everything else is network.
|
|
14
|
+
*
|
|
15
|
+
* This module must stay light: it lazily receives the heavy transport via
|
|
16
|
+
* `loadLocalTransport`, so the online path never loads the engine chunk.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Creates one routing transport (one per app/client).
|
|
20
|
+
*/
|
|
21
|
+
export function createRoutingTransport(options) {
|
|
22
|
+
const localIds = new Set();
|
|
23
|
+
let engineRunning = false;
|
|
24
|
+
let localScopeDepth = 0;
|
|
25
|
+
const networkFetch = options.networkFetch
|
|
26
|
+
?? ((input, init) => fetch(input, init));
|
|
27
|
+
const createNetworkSocket = options.createNetworkSocket
|
|
28
|
+
?? ((url, protocols) => new WebSocket(url, protocols));
|
|
29
|
+
function shouldRouteHttpToLocal(path) {
|
|
30
|
+
if (path.startsWith('/api/availability')) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (localScopeDepth > 0
|
|
34
|
+
&& (path === '/api/matches'
|
|
35
|
+
|| path === '/api/lobbies'
|
|
36
|
+
|| path === '/api/limited-events/preflight'
|
|
37
|
+
|| path.startsWith('/api/cards'))) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (!engineRunning) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (path.startsWith('/api/cards')) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const resourceId = path.match(/^\/api\/(?:matches|lobbies|limited-events)\/([^/?]+)/)?.[1];
|
|
47
|
+
return Boolean(resourceId && localIds.has(decodeURIComponent(resourceId)));
|
|
48
|
+
}
|
|
49
|
+
function shouldRouteSocketToLocal(url) {
|
|
50
|
+
if (!engineRunning) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const matchId = new URL(url).pathname.split('/').filter(Boolean).pop();
|
|
54
|
+
return Boolean(matchId && localIds.has(decodeURIComponent(matchId)));
|
|
55
|
+
}
|
|
56
|
+
const routingFetch = async (input, init) => {
|
|
57
|
+
const url = typeof input === 'string'
|
|
58
|
+
? input
|
|
59
|
+
: input instanceof URL ? input.toString() : input.url;
|
|
60
|
+
const pathname = new URL(url, 'http://placeholder.local').pathname;
|
|
61
|
+
if (!shouldRouteHttpToLocal(pathname)) {
|
|
62
|
+
return await networkFetch(input, init);
|
|
63
|
+
}
|
|
64
|
+
const local = await options.loadLocalTransport();
|
|
65
|
+
const response = await local.fetch(url, init);
|
|
66
|
+
if ((init?.method ?? 'GET') === 'POST' && response.ok) {
|
|
67
|
+
// Register every id the local engine mints so follow-up requests
|
|
68
|
+
// (snapshots, sockets, reconnect) route local without a scope flag:
|
|
69
|
+
// matches, lobbies, and the limited event + match a lobby starts.
|
|
70
|
+
const payload = await response.clone().json().catch(() => null);
|
|
71
|
+
for (const id of [
|
|
72
|
+
payload?.matchId,
|
|
73
|
+
payload?.lobbyId,
|
|
74
|
+
payload?.private?.startedAccess?.access?.eventId,
|
|
75
|
+
payload?.private?.startedAccess?.access?.matchId,
|
|
76
|
+
]) {
|
|
77
|
+
if (id) {
|
|
78
|
+
localIds.add(id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return response;
|
|
83
|
+
};
|
|
84
|
+
const webSocketFactory = (url, protocols) => {
|
|
85
|
+
if (!shouldRouteSocketToLocal(url)) {
|
|
86
|
+
return createNetworkSocket(url, protocols);
|
|
87
|
+
}
|
|
88
|
+
return createDeferredSocket(async () => {
|
|
89
|
+
const local = await options.loadLocalTransport();
|
|
90
|
+
return local.webSocketFactory(url, protocols);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
fetch: routingFetch,
|
|
95
|
+
webSocketFactory,
|
|
96
|
+
runWithLocalScope: async (operation) => {
|
|
97
|
+
localScopeDepth += 1;
|
|
98
|
+
try {
|
|
99
|
+
return await operation();
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
localScopeDepth -= 1;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
isLocalScopeActive: () => localScopeDepth > 0,
|
|
106
|
+
registerLocalId: id => void localIds.add(id),
|
|
107
|
+
isLocalId: id => id != null && localIds.has(id),
|
|
108
|
+
setEngineRunning: (running) => {
|
|
109
|
+
engineRunning = running;
|
|
110
|
+
},
|
|
111
|
+
isEngineRunning: () => engineRunning,
|
|
112
|
+
reset: () => {
|
|
113
|
+
localIds.clear();
|
|
114
|
+
engineRunning = false;
|
|
115
|
+
localScopeDepth = 0;
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Buffers listener registrations/sends until the inner socket resolves. The
|
|
121
|
+
* SDK expects a socket object synchronously; the local transport may still be
|
|
122
|
+
* lazy-loading. A failed load surfaces as `error` + `close 1006` so the
|
|
123
|
+
* connection layer transitions instead of hanging.
|
|
124
|
+
*/
|
|
125
|
+
function createDeferredSocket(resolve) {
|
|
126
|
+
let inner = null;
|
|
127
|
+
const listeners = [];
|
|
128
|
+
const queued = [];
|
|
129
|
+
let closed = false;
|
|
130
|
+
const emit = (type, event) => {
|
|
131
|
+
for (const [listenerType, listener] of [...listeners]) {
|
|
132
|
+
if (listenerType === type) {
|
|
133
|
+
listener(event);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
void resolve()
|
|
138
|
+
.then((socket) => {
|
|
139
|
+
if (closed) {
|
|
140
|
+
socket.close();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
inner = socket;
|
|
144
|
+
for (const [type, listener] of listeners) {
|
|
145
|
+
socket.addEventListener(type, listener);
|
|
146
|
+
}
|
|
147
|
+
for (const data of queued) {
|
|
148
|
+
socket.send(data);
|
|
149
|
+
}
|
|
150
|
+
queued.length = 0;
|
|
151
|
+
})
|
|
152
|
+
.catch((error) => {
|
|
153
|
+
if (closed) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
closed = true;
|
|
157
|
+
emit('error', { message: String(error) });
|
|
158
|
+
emit('close', { code: 1006, reason: String(error) });
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
addEventListener: (type, listener) => {
|
|
162
|
+
if (inner) {
|
|
163
|
+
inner.addEventListener(type, listener);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
listeners.push([type, listener]);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
removeEventListener: (type, listener) => {
|
|
170
|
+
if (inner) {
|
|
171
|
+
inner.removeEventListener(type, listener);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const index = listeners.findIndex(([t, l]) => t === type && l === listener);
|
|
175
|
+
if (index >= 0) {
|
|
176
|
+
listeners.splice(index, 1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
send: (data) => {
|
|
181
|
+
if (inner) {
|
|
182
|
+
inner.send(data);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
queued.push(data);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
close: (code, reason) => {
|
|
189
|
+
closed = true;
|
|
190
|
+
inner?.close(code, reason);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-lane scheduler for one local match: delivers wire batches in order
|
|
3
|
+
* (honoring `delayBeforeMs` pacing barriers with real timers), applies human
|
|
4
|
+
* messages between AI decisions, and keeps ticking the engine while an AI
|
|
5
|
+
* seat has work. Everything runs on the worker's event loop — the blocking
|
|
6
|
+
* MCTS search inside `tickAi` never overlaps a queued human message because
|
|
7
|
+
* the queue is drained one operation at a time.
|
|
8
|
+
*/
|
|
9
|
+
import type { LocalEngineMatch, LocalWireBatch } from '../engine.js';
|
|
10
|
+
export interface AiSchedulerOptions {
|
|
11
|
+
match: LocalEngineMatch;
|
|
12
|
+
/** Sends one batch's messages to the connected client. */
|
|
13
|
+
deliver: (batch: LocalWireBatch) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Called when an engine call throws (wasm panic, corrupt message). The
|
|
16
|
+
* scheduler stops itself first; the socket layer should fail the channel so
|
|
17
|
+
* the client sees `error` + `close` instead of a silently frozen match.
|
|
18
|
+
*/
|
|
19
|
+
failed: (error: unknown) => void;
|
|
20
|
+
}
|
|
21
|
+
export interface AiScheduler {
|
|
22
|
+
/** Queues one human operation; its batches are delivered in order. */
|
|
23
|
+
enqueueHuman: (operation: () => LocalWireBatch[]) => void;
|
|
24
|
+
/** Starts (or continues) AI ticking if the engine reports work. */
|
|
25
|
+
pump: () => void;
|
|
26
|
+
stop: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates the scheduler for one match socket.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createAiScheduler(options: AiSchedulerOptions): AiScheduler;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-lane scheduler for one local match: delivers wire batches in order
|
|
3
|
+
* (honoring `delayBeforeMs` pacing barriers with real timers), applies human
|
|
4
|
+
* messages between AI decisions, and keeps ticking the engine while an AI
|
|
5
|
+
* seat has work. Everything runs on the worker's event loop — the blocking
|
|
6
|
+
* MCTS search inside `tickAi` never overlaps a queued human message because
|
|
7
|
+
* the queue is drained one operation at a time.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Creates the scheduler for one match socket.
|
|
11
|
+
*/
|
|
12
|
+
export function createAiScheduler(options) {
|
|
13
|
+
const { match, deliver, failed } = options;
|
|
14
|
+
const queue = [];
|
|
15
|
+
let draining = false;
|
|
16
|
+
let stopped = false;
|
|
17
|
+
let aiScheduled = false;
|
|
18
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
19
|
+
async function drain() {
|
|
20
|
+
if (draining || stopped) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
draining = true;
|
|
24
|
+
try {
|
|
25
|
+
while (queue.length > 0 && !stopped) {
|
|
26
|
+
const operation = queue.shift();
|
|
27
|
+
let batches;
|
|
28
|
+
try {
|
|
29
|
+
batches = operation.run();
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// An engine throw would otherwise escape this async loop as an
|
|
33
|
+
// unhandled rejection with the match frozen and no client signal.
|
|
34
|
+
stopped = true;
|
|
35
|
+
queue.length = 0;
|
|
36
|
+
failed(error);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
for (const batch of batches) {
|
|
40
|
+
if (stopped) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (batch.delayBeforeMs > 0) {
|
|
44
|
+
await sleep(batch.delayBeforeMs);
|
|
45
|
+
}
|
|
46
|
+
deliver(batch);
|
|
47
|
+
}
|
|
48
|
+
if (operation.thenPump) {
|
|
49
|
+
scheduleAiTick();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
draining = false;
|
|
55
|
+
}
|
|
56
|
+
if (queue.length > 0) {
|
|
57
|
+
void drain();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function scheduleAiTick() {
|
|
61
|
+
if (stopped || aiScheduled || !match.aiHasWork()) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
aiScheduled = true;
|
|
65
|
+
// Yield to the worker event loop first so freshly posted human messages
|
|
66
|
+
// (undo, pause) can slot in before the next blocking search.
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
aiScheduled = false;
|
|
69
|
+
if (stopped || !match.aiHasWork()) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
queue.push({
|
|
73
|
+
run: () => {
|
|
74
|
+
const tick = match.tickAi(Date.now());
|
|
75
|
+
return tick.batches;
|
|
76
|
+
},
|
|
77
|
+
thenPump: true,
|
|
78
|
+
});
|
|
79
|
+
void drain();
|
|
80
|
+
}, 0);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
enqueueHuman: (operation) => {
|
|
84
|
+
queue.push({ run: operation, thenPump: true });
|
|
85
|
+
void drain();
|
|
86
|
+
},
|
|
87
|
+
pump: () => scheduleAiTick(),
|
|
88
|
+
stop: () => {
|
|
89
|
+
stopped = true;
|
|
90
|
+
queue.length = 0;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP-route emulation for the offline engine. Implements exactly the routes
|
|
3
|
+
* the app's offline flows call; everything else returns a 501-style
|
|
4
|
+
* `ApiErrorResponse` so unexpected traffic fails loudly instead of silently.
|
|
5
|
+
*/
|
|
6
|
+
import type { LocalEngineLobby, LocalEngineMatch, LocalSimulatorEngine, LocalWireBatch } from '../engine.js';
|
|
7
|
+
/** Result of one routed request. */
|
|
8
|
+
export interface RoutedResponse {
|
|
9
|
+
status: number;
|
|
10
|
+
body: string;
|
|
11
|
+
}
|
|
12
|
+
/** Open-socket handle for one match (broadcast + AI re-pump from HTTP routes). */
|
|
13
|
+
export interface LocalMatchChannel {
|
|
14
|
+
/** Enqueues pre-computed batches behind any in-flight paced delivery. */
|
|
15
|
+
deliver: (batches: LocalWireBatch[]) => void;
|
|
16
|
+
}
|
|
17
|
+
/** Mutable match/lobby registry shared with the socket layer. */
|
|
18
|
+
export interface LocalMatchRegistry {
|
|
19
|
+
matches: Map<string, LocalEngineMatch>;
|
|
20
|
+
lobbies: Map<string, LocalEngineLobby>;
|
|
21
|
+
/** Limited event id → owning lobby id (for /api/limited-events routing). */
|
|
22
|
+
eventLobbies: Map<string, string>;
|
|
23
|
+
/** Match id → its open socket, while one is connected. */
|
|
24
|
+
matchChannels: Map<string, LocalMatchChannel>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Maps an engine throw (invalid request payload, wasm-side rejection) to the
|
|
28
|
+
* same 4xx `ApiErrorResponse` shape the online server produces, so app error
|
|
29
|
+
* handling sees an `EngineHttpError` with a code instead of a rejected fetch.
|
|
30
|
+
*/
|
|
31
|
+
export declare function engineFailureResponse(error: unknown): RoutedResponse;
|
|
32
|
+
/**
|
|
33
|
+
* Routes one HTTP request against the engine.
|
|
34
|
+
*/
|
|
35
|
+
export declare function routeHttpRequest(engine: LocalSimulatorEngine, registry: LocalMatchRegistry, method: string, path: string, body: string | undefined, nowMs: number): RoutedResponse;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP-route emulation for the offline engine. Implements exactly the routes
|
|
3
|
+
* the app's offline flows call; everything else returns a 501-style
|
|
4
|
+
* `ApiErrorResponse` so unexpected traffic fails loudly instead of silently.
|
|
5
|
+
*/
|
|
6
|
+
function jsonError(status, code, message) {
|
|
7
|
+
return {
|
|
8
|
+
status,
|
|
9
|
+
body: JSON.stringify({ error: { code, message } }),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Maps an engine throw (invalid request payload, wasm-side rejection) to the
|
|
14
|
+
* same 4xx `ApiErrorResponse` shape the online server produces, so app error
|
|
15
|
+
* handling sees an `EngineHttpError` with a code instead of a rejected fetch.
|
|
16
|
+
*/
|
|
17
|
+
export function engineFailureResponse(error) {
|
|
18
|
+
return jsonError(400, 'localEngineError', String(error));
|
|
19
|
+
}
|
|
20
|
+
/** Drops every hosted match except `kept` — offline flows host one live match. */
|
|
21
|
+
function retainOnlyMatch(registry, kept) {
|
|
22
|
+
for (const [id] of registry.matches) {
|
|
23
|
+
if (id !== kept.matchId) {
|
|
24
|
+
registry.matches.delete(id);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Routes one HTTP request against the engine.
|
|
30
|
+
*/
|
|
31
|
+
export function routeHttpRequest(engine, registry, method, path, body, nowMs) {
|
|
32
|
+
const url = new URL(path, 'http://local.engine');
|
|
33
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
34
|
+
// GET /api/availability is deliberately NOT served here: availability
|
|
35
|
+
// always describes the real Crucible backend.
|
|
36
|
+
if (segments[0] !== 'api') {
|
|
37
|
+
return jsonError(404, 'notFound', `unknown path ${url.pathname}`);
|
|
38
|
+
}
|
|
39
|
+
if (segments[1] === 'cards' && segments.length === 2 && method === 'GET') {
|
|
40
|
+
const ids = (url.searchParams.get('ids') ?? '')
|
|
41
|
+
.split(',')
|
|
42
|
+
.map(value => Number.parseInt(value, 10))
|
|
43
|
+
.filter(value => Number.isFinite(value));
|
|
44
|
+
return { status: 200, body: engine.cardsGet(ids) };
|
|
45
|
+
}
|
|
46
|
+
if (segments[1] === 'lobbies') {
|
|
47
|
+
return routeLobbyRequest(engine, registry, segments, method, body, nowMs);
|
|
48
|
+
}
|
|
49
|
+
if (segments[1] === 'limited-events') {
|
|
50
|
+
if (segments[2] === 'preflight' && segments.length === 3 && method === 'POST') {
|
|
51
|
+
if (body == null) {
|
|
52
|
+
return jsonError(400, 'invalidRequest', 'missing preflight request body');
|
|
53
|
+
}
|
|
54
|
+
return { status: 200, body: engine.preflightLimitedEvent(body) };
|
|
55
|
+
}
|
|
56
|
+
const eventId = segments[2] ? decodeURIComponent(segments[2]) : undefined;
|
|
57
|
+
const lobby = eventId ? registry.lobbies.get(registry.eventLobbies.get(eventId) ?? '') : undefined;
|
|
58
|
+
if (!lobby) {
|
|
59
|
+
return jsonError(404, 'eventNotFound', 'limited event not found');
|
|
60
|
+
}
|
|
61
|
+
if (segments.length === 3 && method === 'GET') {
|
|
62
|
+
const seatToken = url.searchParams.get('seatToken');
|
|
63
|
+
return { status: 200, body: lobby.eventSnapshot(seatToken, nowMs) };
|
|
64
|
+
}
|
|
65
|
+
if (segments[3] === 'commands' && method === 'POST') {
|
|
66
|
+
if (body == null) {
|
|
67
|
+
return jsonError(400, 'invalidRequest', 'missing command body');
|
|
68
|
+
}
|
|
69
|
+
const request = JSON.parse(body);
|
|
70
|
+
return {
|
|
71
|
+
status: 200,
|
|
72
|
+
body: lobby.eventApplyCommand(request.seatToken, JSON.stringify(request.command), nowMs),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return jsonError(501, 'notImplemented', `offline engine does not serve ${method} ${url.pathname}`);
|
|
76
|
+
}
|
|
77
|
+
if (segments[1] === 'matches') {
|
|
78
|
+
if (segments.length === 2 && method === 'POST') {
|
|
79
|
+
if (body == null) {
|
|
80
|
+
return jsonError(400, 'invalidRequest', 'missing create request body');
|
|
81
|
+
}
|
|
82
|
+
const { match, accessJson } = engine.createMatch(body, nowMs);
|
|
83
|
+
registry.matches.set(match.matchId, match);
|
|
84
|
+
retainOnlyMatch(registry, match);
|
|
85
|
+
return { status: 200, body: accessJson };
|
|
86
|
+
}
|
|
87
|
+
const matchId = segments[2] ? decodeURIComponent(segments[2]) : undefined;
|
|
88
|
+
const match = matchId ? registry.matches.get(matchId) : undefined;
|
|
89
|
+
if (!match) {
|
|
90
|
+
return jsonError(404, 'matchNotFound', 'match not found');
|
|
91
|
+
}
|
|
92
|
+
if (segments.length === 3 && method === 'GET') {
|
|
93
|
+
return { status: 200, body: JSON.stringify({ snapshot: JSON.parse(match.snapshot()) }) };
|
|
94
|
+
}
|
|
95
|
+
if (segments[3] === 'ai' && segments[4] === 'pause' && method === 'POST') {
|
|
96
|
+
if (body == null) {
|
|
97
|
+
return jsonError(400, 'invalidRequest', 'missing pause request body');
|
|
98
|
+
}
|
|
99
|
+
const request = JSON.parse(body);
|
|
100
|
+
const batches = match.handleMessage(request.seatToken, JSON.stringify({ type: 'setAiPaused', payload: { paused: request.paused } }), nowMs);
|
|
101
|
+
// The HTTP reply is the refreshed AI status; the full batch set is
|
|
102
|
+
// broadcast to the open socket (which also re-pumps the AI — critical
|
|
103
|
+
// after `paused: false`, or the AI would stall until the next message).
|
|
104
|
+
registry.matchChannels.get(match.matchId)?.deliver(batches);
|
|
105
|
+
const status = batches
|
|
106
|
+
.flatMap(batch => batch.messages)
|
|
107
|
+
.find((message) => message?.type === 'aiStatus');
|
|
108
|
+
if (!status) {
|
|
109
|
+
return jsonError(409, 'invalidRequest', 'AI pause rejected');
|
|
110
|
+
}
|
|
111
|
+
return { status: 200, body: JSON.stringify(status.payload) };
|
|
112
|
+
}
|
|
113
|
+
if (segments[3] === 'save' && method === 'POST') {
|
|
114
|
+
return { status: 200, body: match.save() };
|
|
115
|
+
}
|
|
116
|
+
return jsonError(501, 'notImplemented', `offline engine does not serve ${method} ${url.pathname}`);
|
|
117
|
+
}
|
|
118
|
+
return jsonError(501, 'notImplemented', `offline engine does not serve ${method} ${url.pathname}`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Routes the hidden limited-lobby bootstrap the offline vs-AI pods drive.
|
|
122
|
+
*/
|
|
123
|
+
function routeLobbyRequest(engine, registry, segments, method, body, nowMs) {
|
|
124
|
+
if (segments.length === 2 && method === 'POST') {
|
|
125
|
+
if (body == null) {
|
|
126
|
+
return jsonError(400, 'invalidRequest', 'missing lobby request body');
|
|
127
|
+
}
|
|
128
|
+
const { lobby, accessJson } = engine.createLobby(body);
|
|
129
|
+
registry.lobbies.set(lobby.lobbyId, lobby);
|
|
130
|
+
return { status: 200, body: accessJson };
|
|
131
|
+
}
|
|
132
|
+
const lobbyId = segments[2] ? decodeURIComponent(segments[2]) : undefined;
|
|
133
|
+
const lobby = lobbyId ? registry.lobbies.get(lobbyId) : undefined;
|
|
134
|
+
if (!lobby) {
|
|
135
|
+
return jsonError(404, 'lobbyNotFound', 'lobby not found');
|
|
136
|
+
}
|
|
137
|
+
const action = segments.slice(3).join('/');
|
|
138
|
+
const request = body == null ? {} : JSON.parse(body);
|
|
139
|
+
try {
|
|
140
|
+
lobby.authorize(String(request.playerToken ?? ''));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return jsonError(403, 'invalidToken', 'invalid lobby player token');
|
|
144
|
+
}
|
|
145
|
+
switch (`${method} ${action}`) {
|
|
146
|
+
case 'POST players/ai': {
|
|
147
|
+
return { status: 200, body: lobby.addAiPlayer(String(request.difficulty ?? 'medium')) };
|
|
148
|
+
}
|
|
149
|
+
case 'POST players/me': {
|
|
150
|
+
const ready = typeof request.ready === 'boolean' ? request.ready : true;
|
|
151
|
+
return { status: 200, body: lobby.setHostReady(ready) };
|
|
152
|
+
}
|
|
153
|
+
case 'POST start': {
|
|
154
|
+
const accessJson = lobby.start(nowMs);
|
|
155
|
+
const access = JSON.parse(accessJson);
|
|
156
|
+
const eventId = access.private?.startedAccess?.access?.eventId;
|
|
157
|
+
if (eventId && lobby.lobbyId) {
|
|
158
|
+
registry.eventLobbies.set(eventId, lobby.lobbyId);
|
|
159
|
+
}
|
|
160
|
+
return { status: 200, body: accessJson };
|
|
161
|
+
}
|
|
162
|
+
case 'POST limited-deck': {
|
|
163
|
+
return { status: 200, body: lobby.submitLimitedDeck(body ?? '{}') };
|
|
164
|
+
}
|
|
165
|
+
case 'POST prepare-limited-ai-decks': {
|
|
166
|
+
return { status: 200, body: lobby.prepareLimitedAiDecks() };
|
|
167
|
+
}
|
|
168
|
+
case 'POST start-limited-match': {
|
|
169
|
+
const { match, accessJson } = engine.createMatch(lobby.limitedMatchRequest(), nowMs);
|
|
170
|
+
registry.matches.set(match.matchId, match);
|
|
171
|
+
retainOnlyMatch(registry, match);
|
|
172
|
+
return { status: 200, body: lobby.storeStartedMatch(accessJson) };
|
|
173
|
+
}
|
|
174
|
+
case 'POST sync': {
|
|
175
|
+
return { status: 200, body: lobby.accessJson() };
|
|
176
|
+
}
|
|
177
|
+
case 'POST leave': {
|
|
178
|
+
const response = lobby.accessJson();
|
|
179
|
+
registry.lobbies.delete(lobbyId);
|
|
180
|
+
return { status: 200, body: response };
|
|
181
|
+
}
|
|
182
|
+
default:
|
|
183
|
+
return jsonError(501, 'notImplemented', `offline engine does not serve ${method} /api/lobbies/.../${action}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-worker server: binds the RPC protocol to the engine (HTTP router, virtual
|
|
3
|
+
* sockets, record persistence). The worker entry wires this to the real wasm
|
|
4
|
+
* engine; tests drive it with a scripted fake over an in-process port.
|
|
5
|
+
*/
|
|
6
|
+
import type { LocalSimulatorEngine } from '../engine.js';
|
|
7
|
+
import type { WorkerLike } from '../protocol.js';
|
|
8
|
+
export interface LocalEngineServerOptions {
|
|
9
|
+
engine: LocalSimulatorEngine;
|
|
10
|
+
/** The worker-global port (`self`) or an in-process test double. */
|
|
11
|
+
port: WorkerLike;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Attaches the local engine server to a message port. Returns a disposer.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createLocalEngineServer(options: LocalEngineServerOptions): () => void;
|