feat(chess): mirror consent-flow wire types on client side

Mirrors the 5 new message shapes added to server/src/protocol.ts
(T2-ADR-2): propose, proposal-pending, consent, rejected,
consent-received. Kept as independent interfaces to avoid
importing server types (direction: chess \u2190 server is forbidden).
Structural parity maintained by hand \u2014 any drift surfaces as
a typecheck error in net/client.ts when it starts emitting the
new messages.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-19 09:19:49 -06:00
commit ca9072ce48
No known key found for this signature in database

View file

@ -217,6 +217,45 @@ export interface ModifierProfileQueuedPayload {
pendingVersion: number;
}
// ---------------------------------------------------------------------------
// T2-ADR-2: Two-player consent flow (mirrors server/src/protocol.ts)
// ---------------------------------------------------------------------------
/**
* Server → opponent only: a profile swap has been proposed and
* awaits consent. `expiresAt` is a unix-ms deadline after which
* the server auto-rejects with reason `"timeout"`. The proposing
* player does NOT receive this message — they get a
* `modifier-profile.queued` ack (shape reused from T2).
*/
export interface ModifierProfileProposalPendingPayload {
roomCode: string;
profile: ModifierProfileWire;
expiresAt: number;
proposer: Color;
}
/** Why a pending proposal was cancelled without being promoted
* to the turn-boundary queue. */
export type ModifierProfileRejectReason =
| "rejected"
| "timeout"
| "superseded";
/** Server → both: the current proposal was NOT applied. UIs
* clear any "proposal-pending" banner on receipt. */
export interface ModifierProfileRejectedPayload {
roomCode: string;
reason: ModifierProfileRejectReason;
}
/** Server → proposer only: opponent approved; the proposal is now
* in the T2 pending queue and will apply at the next turn
* boundary via `modifier-profile.updated`. */
export interface ModifierProfileConsentReceivedPayload {
roomCode: string;
}
export interface ErrorPayload {
code: string;
message: string;
@ -249,6 +288,25 @@ export interface ModifierProfileUpdatePayload {
version: number;
}
/**
* Client → server: propose a profile swap for opponent consent
* (T2-ADR-2). Multiplayer only; solo clients use
* `modifier-profile.update` directly. Same staleness gate as
* `update`.
*/
export interface ModifierProfileProposePayload {
roomCode: string;
candidate: ModifierProfileWire;
version: number;
}
/** Client → server (opponent of the proposer only): approve
* or reject the pending proposal. Self-consent is rejected. */
export interface ModifierProfileConsentPayload {
roomCode: string;
decision: "approve" | "reject";
}
export interface RoomJoinPayload {
code: string;
}
@ -281,6 +339,15 @@ export type ServerMessage =
| MessageEnvelope<"room.joined", RoomJoinedPayload>
| MessageEnvelope<"modifier-profile.updated", ModifierProfileUpdatedPayload>
| MessageEnvelope<"modifier-profile.queued", ModifierProfileQueuedPayload>
| MessageEnvelope<
"modifier-profile.proposal-pending",
ModifierProfileProposalPendingPayload
>
| MessageEnvelope<"modifier-profile.rejected", ModifierProfileRejectedPayload>
| MessageEnvelope<
"modifier-profile.consent-received",
ModifierProfileConsentReceivedPayload
>
| MessageEnvelope<"error", ErrorPayload>;
export type ClientMessage =
@ -289,4 +356,6 @@ export type ClientMessage =
| MessageEnvelope<"room.leave", Record<string, never>>
| MessageEnvelope<"game.move", GameMovePayload>
| MessageEnvelope<"room.setPresets", RoomSetPresetsPayload>
| MessageEnvelope<"modifier-profile.update", ModifierProfileUpdatePayload>;
| MessageEnvelope<"modifier-profile.update", ModifierProfileUpdatePayload>
| MessageEnvelope<"modifier-profile.propose", ModifierProfileProposePayload>
| MessageEnvelope<"modifier-profile.consent", ModifierProfileConsentPayload>;