feat(mnswpr): type GameSession.onMove

This commit is contained in:
ayo 2026-07-17 22:56:51 +02:00
parent 310b657e32
commit a26c2c5055
3 changed files with 20 additions and 10 deletions

View file

@ -6,6 +6,7 @@
* Future home: @cozy-games/game-session.
*
* @typedef {{ init: Function, apply: Function, status: Function, project: Function, serialize?: Function, deserialize?: Function, toMoveEvent?: Function }} Rules
* @typedef {import('../minesweeper/rules.js').MoveEvent} MoveEvent
*/
export class GameSession {
/**
@ -56,8 +57,8 @@ export class GameSession {
}>;
_t0: number;
_tEnd: number;
/** @type {Set<(event: object) => void>} */
_moveHandlers: Set<(event: object) => void>;
/** @type {Set<(event: MoveEvent) => void>} */
_moveHandlers: Set<(event: MoveEvent) => void>;
_seq: number;
/**
* Subscribe to typed move-events one per effective move (reveal / flag /
@ -65,10 +66,10 @@ export class GameSession {
* unsubscribe function. Pure in-process pub/sub: no DOM, no rendering. Requires
* the rules to implement `toMoveEvent`.
*
* @param {(event: object) => void} handler
* @param {(event: MoveEvent) => void} handler
* @returns {() => void} unsubscribe
*/
onMove(handler: (event: object) => void): () => void;
onMove(handler: (event: MoveEvent) => void): () => void;
/**
* Apply a move: stamp it, fold it through the rules, and return the projected
* view + events + authoritative elapsed time.
@ -79,8 +80,8 @@ export class GameSession {
view: any;
time: number;
};
/** @param {object} event */
_emitMove(event: object): void;
/** @param {MoveEvent} event */
_emitMove(event: MoveEvent): void;
status(): any;
view(): any;
log(): {
@ -136,3 +137,11 @@ export type Rules = {
deserialize?: Function;
toMoveEvent?: Function;
};
/**
* Layer 1 owns lifecycle, the (injected) clock, and the move log; delegates all
* game meaning to an injected `rules` object. This is where timing authority
* lives: on the client `clock` is `Date.now` (cosmetic); on a server it is the
* server's clock (authoritative). The session never calls a wall clock itself.
* Future home:
*/
export type MoveEvent = import("../minesweeper/rules.js").MoveEvent;

View file

@ -8,6 +8,7 @@
* Future home: @cozy-games/game-session.
*
* @typedef {{ init: Function, apply: Function, status: Function, project: Function, serialize?: Function, deserialize?: Function, toMoveEvent?: Function }} Rules
* @typedef {import('../minesweeper/rules.js').MoveEvent} MoveEvent
*/
export class GameSession {
/**
@ -30,7 +31,7 @@ export class GameSession {
// Move-event emission: subscribers + a monotonic sequence counter (last seq
// assigned; 0 = none yet). Seq is part of the snapshot so it keeps rising
// across a resume rather than restarting.
/** @type {Set<(event: object) => void>} */
/** @type {Set<(event: MoveEvent) => void>} */
this._moveHandlers = new Set()
this._seq = 0
}
@ -41,7 +42,7 @@ export class GameSession {
* unsubscribe function. Pure in-process pub/sub: no DOM, no rendering. Requires
* the rules to implement `toMoveEvent`.
*
* @param {(event: object) => void} handler
* @param {(event: MoveEvent) => void} handler
* @returns {() => void} unsubscribe
*/
onMove(handler) {
@ -75,7 +76,7 @@ export class GameSession {
return { events, view: this.rules.project(state), time: this.elapsed() }
}
/** @param {object} event */
/** @param {MoveEvent} event */
_emitMove(event) {
for (const handler of this._moveHandlers) handler(event)
}

View file

@ -1,6 +1,6 @@
{
"name": "@cozy-games/mnswpr",
"version": "0.5.3",
"version": "0.5.4",
"description": "Classic Minesweeper browser game",
"author": "Ayo",
"type": "module",