Phases 0-1 shipped in v0.4.0 (seat/identity split, per-player turn state, the Session boundary). This lands Phase 2 (server core, one game, no lobby) and Phase 3 (persistence and resumption) per docs/architecture/multiplayer.md §12. Phases 4-6 (lobby/reconnection, the 22 opponent-directed cards, StartOS packaging) are still ahead. Phase 2: src/server/session.ts hosts a game in pure logic (no sockets) on top of game.ts's existing Game/submit/currentActor/actionMenu; it verifies seat === currentActor(game) itself before calling submit, since submit() trusts its caller and a server can't. src/server/http.ts and index.ts add POST /api/game, GET /api/stream (SSE, per-seat), POST /api/intent, and static serving of dist/. src/sim/frame-delta.ts is a purpose-built per-seat board delta for one live push at a time. Found and fixed along the way: actionMenu(game, seat) only used seat for the hand field, so a server computing every connected seat's Menu would have handed the acting player's legal moves to a waiting seat. Verified with a live end-to-end smoke test (2-player game, two SSE streams, a rejected intent from the wrong seat, an idempotent resend) plus test/server/session.test.ts and test/redaction.test.ts. Not verified: an actual browser (none available in this environment). Phase 3: src/server/persistence.ts writes game.json and turn-timings.json, atomic-rewrite-then- rename. game.ts gained fromMultiplayerSave, fixing a narration-attribution bug found while testing it (fromSave's replay loop drops the actor argument, invisible in solitaire, unreadable the moment there's more than one seat — fromSave itself still has this gap, deliberately untouched). Verified live: server killed and restarted mid-game, both seats reconnected exactly where they left off. Two rules bugs found while building this: the New Train phase never implemented its car-placement round (every car of every train was placed by the Superintendent alone, in every mode, all along — now reads the round position off tray.consist.length); and victory conditions are now one shared, configurable GameConfig set across solitaire/competitive/coop instead of a fixed length lookup and a dead firstToTarget condition. Also folds in the three fixes already released on the patch line as v0.4.9b/c/d: a switching train's crew badge failing to draw once it left the Office square, an unload that always took the westmost car regardless of which was picked, and a legal decision that could render with zero buttons. docs/testing/0.5.0-test-plan.md and three reported-bug save files (docs/station-master-seed*.json) included for reproducibility. tools/jitsi-harness/ deliberately left untracked — unrelated side-project work, not part of this release. 635 tests, 0 failures.
412 lines
13 KiB
JSON
412 lines
13 KiB
JSON
{
|
|
"name": "station-master",
|
|
"version": "0.5.0",
|
|
"lockfileVersion": 3,
|
|
"requires": true,
|
|
"packages": {
|
|
"": {
|
|
"name": "station-master",
|
|
"version": "0.5.0",
|
|
"devDependencies": {
|
|
"@types/node": "^26.1.2",
|
|
"typescript": "^7.0.2"
|
|
},
|
|
"engines": {
|
|
"node": ">=22.18"
|
|
}
|
|
},
|
|
"node_modules/@types/node": {
|
|
"version": "26.1.2",
|
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz",
|
|
"integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==",
|
|
"dev": true,
|
|
"license": "MIT",
|
|
"dependencies": {
|
|
"undici-types": "~8.3.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-aix-ppc64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
|
|
"integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
|
|
"cpu": [
|
|
"ppc64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"aix"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-darwin-arm64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
|
|
"integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
|
|
"cpu": [
|
|
"arm64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"darwin"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-darwin-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
|
|
"integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"darwin"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-freebsd-arm64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
|
|
"integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
|
|
"cpu": [
|
|
"arm64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"freebsd"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-freebsd-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
|
|
"integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"freebsd"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-arm": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
|
|
"integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
|
|
"cpu": [
|
|
"arm"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-arm64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
|
|
"integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
|
|
"cpu": [
|
|
"arm64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-loong64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
|
|
"integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
|
|
"cpu": [
|
|
"loong64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-mips64el": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
|
|
"integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
|
|
"cpu": [
|
|
"mips64el"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-ppc64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
|
|
"integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
|
|
"cpu": [
|
|
"ppc64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-riscv64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
|
|
"integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
|
|
"cpu": [
|
|
"riscv64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-s390x": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
|
|
"integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
|
|
"cpu": [
|
|
"s390x"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-linux-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
|
|
"integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"linux"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-netbsd-arm64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
|
|
"integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
|
|
"cpu": [
|
|
"arm64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"netbsd"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-netbsd-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
|
|
"integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"netbsd"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-openbsd-arm64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
|
|
"integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
|
|
"cpu": [
|
|
"arm64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"openbsd"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-openbsd-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
|
|
"integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"openbsd"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-sunos-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
|
|
"integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"sunos"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-win32-arm64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
|
|
"integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
|
|
"cpu": [
|
|
"arm64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"win32"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/@typescript/typescript-win32-x64": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
|
|
"integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
|
|
"cpu": [
|
|
"x64"
|
|
],
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"optional": true,
|
|
"os": [
|
|
"win32"
|
|
],
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
}
|
|
},
|
|
"node_modules/typescript": {
|
|
"version": "7.0.2",
|
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
|
|
"integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
|
|
"dev": true,
|
|
"license": "Apache-2.0",
|
|
"bin": {
|
|
"tsc": "bin/tsc"
|
|
},
|
|
"engines": {
|
|
"node": ">=16.20.0"
|
|
},
|
|
"optionalDependencies": {
|
|
"@typescript/typescript-aix-ppc64": "7.0.2",
|
|
"@typescript/typescript-darwin-arm64": "7.0.2",
|
|
"@typescript/typescript-darwin-x64": "7.0.2",
|
|
"@typescript/typescript-freebsd-arm64": "7.0.2",
|
|
"@typescript/typescript-freebsd-x64": "7.0.2",
|
|
"@typescript/typescript-linux-arm": "7.0.2",
|
|
"@typescript/typescript-linux-arm64": "7.0.2",
|
|
"@typescript/typescript-linux-loong64": "7.0.2",
|
|
"@typescript/typescript-linux-mips64el": "7.0.2",
|
|
"@typescript/typescript-linux-ppc64": "7.0.2",
|
|
"@typescript/typescript-linux-riscv64": "7.0.2",
|
|
"@typescript/typescript-linux-s390x": "7.0.2",
|
|
"@typescript/typescript-linux-x64": "7.0.2",
|
|
"@typescript/typescript-netbsd-arm64": "7.0.2",
|
|
"@typescript/typescript-netbsd-x64": "7.0.2",
|
|
"@typescript/typescript-openbsd-arm64": "7.0.2",
|
|
"@typescript/typescript-openbsd-x64": "7.0.2",
|
|
"@typescript/typescript-sunos-x64": "7.0.2",
|
|
"@typescript/typescript-win32-arm64": "7.0.2",
|
|
"@typescript/typescript-win32-x64": "7.0.2"
|
|
}
|
|
},
|
|
"node_modules/undici-types": {
|
|
"version": "8.3.0",
|
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
|
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
|
"dev": true,
|
|
"license": "MIT"
|
|
}
|
|
}
|
|
}
|