
Add a src/scripts/types.ts file defining TypeScript interfaces for the P2P message shapes and the card-info domain object. This gives tsc --noEmit something to validate and provides a single source of truth for data structures used across modules.
Context
Every module in src/scripts/ constructs and consumes P2P messages and card-info objects using ad-hoc object literals. The message shape — { messageType, entityType, entityId, content, perspective } — is repeated across sendCreateMessage, sendGrabMessage, sendMoveMessage, sendUngrabMessage, and receiveMessage with no shared definition. The card-info shape is similarly duplicated: createCard reads cardInfo.title, .side_code, .faction_code, .type_code, .image, while cardElementToCardInfo in deck.js reconstructs the same shape from DOM attributes. A types.ts file would centralize these definitions, making npm run typecheck catch field name typos (the codebase already has cardIfo at game.js:35 and stactedToken at token.js:122), and serves as the foundation for enabling checkJs (#170) and noImplicitAny (#193).
Affected Files
src/scripts/types.ts — New file — define interfaces: P2PMessage (with discriminated union on messageType), CardInfo, TokenName (string literal union)
src/scripts/p2p.js — Add JSDoc @typedef {import('./types.ts').P2PMessage} or @type annotations on send functions and receiveMessage parameter
src/scripts/card.js — Add JSDoc @param {import('./types.ts').CardInfo} cardInfo on createCard
src/scripts/deck.js:117 — Add JSDoc return type @returns {import('./types.ts').CardInfo} on cardElementToCardInfo
Requirements

Verification
- npm run typecheck
- npm run lint
- test -f src/scripts/types.ts && echo 'types.ts exists'
Not In Scope
Evidence
src/scripts/p2p.js:9-17 — sendCreateMessage builds { perspective, messageType, entityType, entityId, content } as an untyped object literal — no interface enforces field names or value types
src/scripts/card.js:8-11 — createCard reads cardInfo.title, .side_code, .faction_code, .type_code from an untyped parameter
src/scripts/deck.js:117-124 — cardElementToCardInfo reconstructs the card-info shape from DOM attributes — must match what createCard expects, but nothing enforces this
src/scripts/game.js:35 — Variable named cardIfo (typo for cardInfo) — a typed parameter would surface this during type checking
Arasaka Queue Planning Division.

Add a
src/scripts/types.tsfile defining TypeScript interfaces for the P2P message shapes and the card-info domain object. This givestsc --noEmitsomething to validate and provides a single source of truth for data structures used across modules.Context
Every module in
src/scripts/constructs and consumes P2P messages and card-info objects using ad-hoc object literals. The message shape —{ messageType, entityType, entityId, content, perspective }— is repeated acrosssendCreateMessage,sendGrabMessage,sendMoveMessage,sendUngrabMessage, andreceiveMessagewith no shared definition. The card-info shape is similarly duplicated:createCardreadscardInfo.title,.side_code,.faction_code,.type_code,.image, whilecardElementToCardInfoindeck.jsreconstructs the same shape from DOM attributes. Atypes.tsfile would centralize these definitions, makingnpm run typecheckcatch field name typos (the codebase already hascardIfoatgame.js:35andstactedTokenattoken.js:122), and serves as the foundation for enablingcheckJs(#170) andnoImplicitAny(#193).Affected Files
src/scripts/types.ts— New file — define interfaces:P2PMessage(with discriminated union onmessageType),CardInfo,TokenName(string literal union)src/scripts/p2p.js— Add JSDoc@typedef {import('./types.ts').P2PMessage}or@typeannotations on send functions andreceiveMessageparametersrc/scripts/card.js— Add JSDoc@param {import('./types.ts').CardInfo} cardInfooncreateCardsrc/scripts/deck.js:117— Add JSDoc return type@returns {import('./types.ts').CardInfo}oncardElementToCardInfoRequirements
src/scripts/types.tsexists and exports at leastP2PMessage(or a discriminated union of message subtypes) andCardInfointerfacesCardInfoincludes fieldstitle,side_code,faction_code,type_code,image, and optionallycodeP2PMessagecaptures themessageType,entityType,entityId,content, andperspectivefields with appropriate typesnpm run typecheckpasses with the new file in placecreateCardincard.jsandreceiveMessageinp2p.jsreference the new types via JSDoc annotationsVerification
Not In Scope
checkJsintsconfig.json(covered by EnablecheckJsintsconfig.jsonto activate type checking on.jssource files #170).jsfiles to.tscreateCard,receiveMessage, andcardElementToCardInfoEvidence
src/scripts/p2p.js:9-17—sendCreateMessagebuilds{ perspective, messageType, entityType, entityId, content }as an untyped object literal — no interface enforces field names or value typessrc/scripts/card.js:8-11—createCardreadscardInfo.title,.side_code,.faction_code,.type_codefrom an untyped parametersrc/scripts/deck.js:117-124—cardElementToCardInforeconstructs the card-info shape from DOM attributes — must match whatcreateCardexpects, but nothing enforces thissrc/scripts/game.js:35— Variable namedcardIfo(typo forcardInfo) — a typed parameter would surface this during type checkingArasaka Queue Planning Division.