* chore(dependencies): update various package versions in package-lock.json
* feat(windbot): playable edison bots and format-scoped random pools
Edison bots join the botlist under short names that fit the utf16[20]
pass field. Botlist entries gain a format tag; random bot selection
resolves a pool from the join tokens (mirroring the room's rule-tier
precedence) so a format room can no longer roll a bot with an illegal
deck. Bot names are validated against the pass-field budget at boot,
and the bot-request failure path delivers its JOINERROR before the
canonical room teardown destroys the sockets.
* feat(ban-list): deterministic alias resolution with load-time format aliases
Format banlists carry an explicit alias captured from their
formats/<dir> path at load time. Alias resolution tries the alias
field, then the exact normalized name, and only then the substring
scan — now tie-broken by shortest normalized name instead of load
order, with a memoized warning on ambiguity. Adds getFirstOCGIndex
so OCG-list tokens stop hardcoding index 0.
* fix(room): rule-mapping clamps, state coherence, and boot-order hardening
The lp clamp compared a NaN (parseInt over the whole token) so lp0
started duels at 0 LP; clamps now act on the extracted number. The
edison rule set is shared between its two tokens. The room's DuelState
label and its state object now always transition together: waiting()
sets both and resets isStart/ready flags, and setDuelFinished disposes
the OCGCore before delegating — the ocgcore-error rewind no longer
leaks the core, lies to the room list, or leaves a stale TRY_START
armed. Missing banlist aliases warn once instead of silently
mislabeling rooms. Windbot bootstrap moved before socket init so a bad
botlist aborts pre-listen, and the never-initialized registry fallback
composes the full base chain.
* feat(room): exact-string pairing joins and (name,password) room identity
Empty-password commands made purely of recognized tokens become
pairing joins: they route to a waiting same-command room with a free
seat, an empty password, and a compatible league — or create a fresh
room. They never land in a dueling or full room. A disconnected
pairing player who re-sends the bare command is first matched as a
legitimate reconnector (same predicate as findReconnectingPlayer)
before the pairing scan runs.
Non-pairing joins now identify a room by the exact (name, password)
pair: a mismatched pair creates its own room instead of rejecting, so
a passwordless pairing room no longer blocks passworded rooms that
share its name. Spectating a room mid-duel with the correct password
is unchanged. docs/join-commands.md is the reference for the whole
command system.
* chore(tooling): replace ESLint + Prettier with Biome
- Add biome.json migrated from the ESLint flat config (preset: none,
drop-in rules) with tab indentation, lineWidth 100, and the
evolution-types submodule excluded.
- Enable unsafeParameterDecoratorsEnabled for DI/worker param decorators.
- Switch lint/lint:fix scripts and lint-staged to Biome; drop ESLint
and Prettier devDependencies and their config files.
- Update .editorconfig to tabs and refresh CONTRIBUTING/testing docs.
No source reformatting in this commit.
* style: reformat codebase with Biome (spaces to tabs)
Mass-apply `biome format --write` across src/ and config files.
Indentation converted from 2 spaces to tabs, lineWidth 100, plus
Biome's default trailing commas. No logic changes — build and the
full test suite (704 tests) pass unchanged.
This is a formatting-only commit; see .git-blame-ignore-revs.
* chore: ignore the Biome reformat commit in git blame
* test: co-locate message tests and dedupe processors
Group B of the legacy tests/ -> co-location migration (docs/testing.md).
Moves to co-location:
- JoinGameMessage, PlayerInfoMessage -> src/edopro/messages/client-to-server/
- JSONMessageProcessor (comprehensive) -> src/edopro/messages/
- MessageProcessor (comprehensive) -> src/shared/messages/
Dedupe the two test files that lived under tests/modules/message-processor/
and tested the same sources as the edopro/messages ones:
- JSONMessageProcessor: dropped (its 3 synthetic-mechanics cases were already
covered by the comprehensive suite).
- MessageProcessor: its 3 cases used UNIQUE real wire-format fixtures
(PLAYER_INFO/CREATE_GAME/RESPONSE/TIME_CONFIRM), merged into the comprehensive
suite with descriptive titles, then dropped. No coverage lost.
Imports switched to same-dir relative. 501 tests green (was 504; -3 redundant).
* test: co-locate ban-list, client and deck tests
* test: co-locate edopro room tests
* test: co-locate mercury (ygopro) client and room tests
* test: co-locate room, duel, match and rps tests
* test: resolve Match.test.ts collision onto the comprehensive suite
The legacy Match suite (~35 cases) and a co-located 2-case suite (from the
draw-score fix 10551e3c) both tested the same Match source; the 2-case suite
was a strict subset. Keep the comprehensive suite co-located in src/, drop the
2-case file, and fold its only extra assertions (isFinished()===false after a
draw and after a win) into the matching cases. Fix a misleading title:
'should increment both scores when draw occurs' already asserted 0-0, renamed
to 'should NOT increment scores when a draw occurs'. No coverage lost.
* chore: drop emptied tests/ from jest roots and tsconfig
All legacy tests are now co-located under src/, so the root tests/ directory
is gone. Remove its references from jest roots, tsconfig.json exclude and
tsconfig.eslint.json include.
* docs: mark legacy test migration complete in testing conventions
Editors apply 2-space indent, LF, UTF-8 and trailing-newline automatically.
Chosen over an eslint indent rule because the CI gates on `npm run lint` and
the eslint config intentionally omits stylistic rules — a global indent rule
would fail CI repo-wide or force a big-bang reformat. .editorconfig guides
without gating. Update docs/testing.md to match and mark completed migration
targets.
* docs: establish co-located testing conventions
Add docs/testing.md as the testing standard: co-located tests in src/,
Object Mother for shared domain entities + inline make* factories for
suite-local stubs, shared Mock classes / jest.mock() / mock<T>() for
mocking, English describe/it naming, 2-space indent.
Reconcile AGENTS.md, which previously mandated the opposite (tests/ folder,
always-Mother, Spanish naming) — Prime Directive #1 and SOP-002/004 now
match the co-located reality the suite already drifted to. Link the doc
from CONTRIBUTING.md. Ignore the local .atl/ skill-registry cache.
* docs: remove stale api and node-cpp design docs