chore: move to @cozy-games/mnswpr

This commit is contained in:
ayo 2026-07-06 00:11:22 +02:00
parent 2d7f7d0ebb
commit 3418be3d7b
13 changed files with 31 additions and 31 deletions

View file

@ -4,7 +4,7 @@ Guidance for AI coding agents working in this repository.
## What this is
Classic Minesweeper as a vanilla web game — no framework, no TypeScript (JSDoc + `// @ts-check` only). Deployed at [mnswpr.com](https://mnswpr.com) (Netlify) and published to npm as `@ayo-run/mnswpr`. The game engine has **zero runtime dependencies**; only the website adds Firebase.
Classic Minesweeper as a vanilla web game — no framework, no TypeScript (JSDoc + `// @ts-check` only). Deployed at [mnswpr.com](https://mnswpr.com) (Netlify) and published to npm as `@cozy-games/mnswpr`. The game engine has **zero runtime dependencies**; only the website adds Firebase.
**`mnswpr` is the main test app.** It's the reference app for the monorepo and the default target for local runs — `.claude/launch.json` launches it (`dev` on :5173, `preview` on :4173), and it's what you should build/run/preview when verifying changes to the shared packages or tooling.
@ -79,8 +79,8 @@ Node version: `.nvmrc` pins `lts/*`.
This is the **Cozy Games** monorepo. Workspaces are declared in `pnpm-workspace.yaml` as `apps/*`, `packages/*`, and `sites/*`. `utils/` is now a real workspace package (`@cozy-games/utils`), imported by name — no more `../utils` relative paths.
- **`apps/mnswpr/`** — package `mnswpr`, `@ayo-run/mnswpr`'s host, the mnswpr.com website. Consumes the engine and leaderboard via `workspace:*` (`import mnswpr from '@ayo-run/mnswpr/mnswpr.js'`) and wires them together in `apps/mnswpr/main.js`. Owns its Firebase config (`firebase.json`, `firestore.rules`, `.firebaserc`) and app-specific scripts (`apps/mnswpr/scripts/`). A future app (e.g. sudoku) gets its own `apps/<name>/` and its `package.json` `name` is just the app name (`<name>`, unscoped) so it's addressable directly by name (`pnpm -F <name> run <script>`).
- **`packages/mnswpr/`** — `@ayo-run/mnswpr`, the standalone, framework-free game engine published to npm. `packages/mnswpr/mnswpr.js` is the whole engine; `levels.js` defines the four difficulty presets. Depends only on `@cozy-games/utils`.
- **`apps/mnswpr/`** — package `mnswpr`, `@cozy-games/mnswpr`'s host, the mnswpr.com website. Consumes the engine and leaderboard via `workspace:*` (`import mnswpr from '@cozy-games/mnswpr/mnswpr.js'`) and wires them together in `apps/mnswpr/main.js`. Owns its Firebase config (`firebase.json`, `firestore.rules`, `.firebaserc`) and app-specific scripts (`apps/mnswpr/scripts/`). A future app (e.g. sudoku) gets its own `apps/<name>/` and its `package.json` `name` is just the app name (`<name>`, unscoped) so it's addressable directly by name (`pnpm -F <name> run <script>`).
- **`packages/mnswpr/`** — `@cozy-games/mnswpr`, the standalone, framework-free game engine published to npm. `packages/mnswpr/mnswpr.js` is the whole engine; `levels.js` defines the four difficulty presets. Depends only on `@cozy-games/utils`.
- **`packages/leaderboard/`** — `@cozy-games/leaderboard`, a backend-agnostic, time-windowed leaderboard (adapter-injected storage).
- **`packages/utils/`** — `@cozy-games/utils`, shared services with no dependencies, re-exported from `index.js`: `StorageService`, `TimerService` (`pretty()` time formatting used by both engine and leaderboard), `LoggerService`, `LoadingService`, and date-bucket helpers.
- **`sites/`** — docs (Astro Starlight) and UI demos. Placeholders for now.

View file

@ -20,14 +20,14 @@ The goal is to reveal every safe cell without detonating a mine. Your **first cl
The web is a wonderful, free, and open platform to create and distribute value. You can use **mnswpr** in different ways:
- as a deployed [web app](https://mnswpr.com)
- as a [library](https://npmx.dev/package/@ayo-run/mnswpr) with `npm i @ayo-run/mnswpr`
- as a [library](https://npmx.dev/package/@cozy-games/mnswpr) with `npm i @cozy-games/mnswpr`
- as a `web component` (coming soon).
Using it as a library takes only a few lines — mount it onto any element by `id`:
```js
import '@ayo-run/mnswpr/mnswpr.css'
import mnswpr from '@ayo-run/mnswpr'
import '@cozy-games/mnswpr/mnswpr.css'
import mnswpr from '@cozy-games/mnswpr'
const game = new mnswpr('app')
game.initialize()

View file

@ -1,7 +1,7 @@
import mnswpr from '@ayo-run/mnswpr/mnswpr.js'
import '@ayo-run/mnswpr/mnswpr.css'
import mnswpr from '@cozy-games/mnswpr/mnswpr.js'
import '@cozy-games/mnswpr/mnswpr.css'
import '@cozy-games/utils/loading/loading.css'
import * as pkg from '@ayo-run/mnswpr/package.json'
import * as pkg from '@cozy-games/mnswpr/package.json'
import { configureLeaderboard } from '@cozy-games/leaderboard/leaderboard-element.js'
import { FirebaseAdapter } from '@cozy-games/leaderboard/adapters/firebase.js'
import { NicknameService } from './modules/nickname/nickname.js'

View file

@ -18,7 +18,7 @@
"db:stop": "pkill -f '[c]loud-firestore-emulator'; pkill -f '[f]irebase.* emulators:'; true"
},
"devDependencies": {
"@ayo-run/mnswpr": "workspace:*",
"@cozy-games/mnswpr": "workspace:*",
"@cozy-games/leaderboard": "workspace:*",
"@cozy-games/utils": "workspace:*",
"firebase": "^12.11.0",

View file

@ -17,7 +17,7 @@ import {
getFirestore, getDocs, collection, query, orderBy, limit
} from 'firebase/firestore/lite'
import { levels } from '@ayo-run/mnswpr/levels.js'
import { levels } from '@cozy-games/mnswpr/levels.js'
// Mirror of TimerService.pretty() (@cozy-games/utils timer) — inlined so this
// generator has no cross-module import chain to resolve under raw Node.

View file

@ -3,7 +3,7 @@
**Status:** Accepted · **Repo:** cozy-games · **Date:** 2026-07
## Context
cozy-games is a collection of reusable game modules (`@ayo-run/mnswpr`, the
cozy-games is a collection of reusable game modules (`@cozy-games/mnswpr`, the
leaderboard package, future extractions). Applications built on these packages
differ in how they store data, authenticate users, and enforce rules.

View file

@ -17,7 +17,7 @@
"build": "pnpm -r --filter \"./packages/*\" run build",
"build:lib": "vite build packages/mnswpr",
"publish:lib": "node scripts/publish-lib.js",
"release": "pnpm build:lib && pnpm -F @ayo-run/mnswpr run release && pnpm publish:lib",
"release": "pnpm build:lib && pnpm -F @cozy-games/mnswpr run release && pnpm publish:lib",
"postinstall": "node scripts/ensure-java.mjs",
"prepare": "husky",
"lint": "eslint .",

View file

@ -61,7 +61,7 @@ npm i -D vite
Now that the JS project is initialized and we have a development environment with `vite`, we will install **mnswpr** as a dependency:
```bash
npm i @ayo-run/mnswpr
npm i @cozy-games/mnswpr
```
Finally, you can run the installed `vite` dev server by running the following:
@ -141,8 +141,8 @@ Create a new file named `main.js` with the following content:
/**
* main.js
*/
import '@ayo-run/mnswpr/mnswpr.css'
import mnswpr from '@ayo-run/mnswpr'
import '@cozy-games/mnswpr/mnswpr.css'
import mnswpr from '@cozy-games/mnswpr'
const game = new mnswpr('app')
game.initialize()

View file

@ -1,7 +1,7 @@
// @ts-check
/**
* `@ayo-run/mnswpr/core` the headless, isomorphic Minesweeper core. No DOM, no
* `@cozy-games/mnswpr/core` the headless, isomorphic Minesweeper core. No DOM, no
* wall clock. Runs identically in a browser (offline play) or on a server
* (authoritative timing / replay verification). See
* docs/headless-core-and-client-design.md.

View file

@ -146,7 +146,7 @@ export function validateLayout(layout) {
/**
* Pure, Node-runnable board generation: given a size, a mine count, and an
* injected RNG, produce a plain layout object no DOM, no I/O, no `Grid` class
* leaking out. This is the headless entry point behind `@ayo-run/mnswpr/core`;
* leaking out. This is the headless entry point behind `@cozy-games/mnswpr/core`;
* the DOM client reaches the same generator lazily through `MinesweeperRules`.
*
* The injected `rng` is the determinism seam: the same rng sequence always

View file

@ -1,4 +1,4 @@
# Headless core + client design — `@ayo-run/mnswpr/core` and the client that consumes it
# Headless core + client design — `@cozy-games/mnswpr/core` and the client that consumes it
Design for splitting the Minesweeper engine into a **headless, isomorphic core**
(runs identically in a browser or on a server) and a **thin client** that renders
@ -23,12 +23,12 @@ Goals, in priority order:
## 1. Package layout & the seam
**Decision (settled): one published package, core exposed as a sub-path.** Open-
source consumers keep installing a single `@ayo-run/mnswpr` and get the headless
core for free at `@ayo-run/mnswpr/core` — no second package to publish, version,
source consumers keep installing a single `@cozy-games/mnswpr` and get the headless
core for free at `@cozy-games/mnswpr/core` — no second package to publish, version,
or document. The DOM client stays the default entry (`.`).
```
packages/mnswpr/ # @ayo-run/mnswpr — ONE published package
packages/mnswpr/ # @cozy-games/mnswpr — ONE published package
mnswpr.js # "." → DOM client (browser entry; today's default)
levels.js # shared by client + core (level presets)
core/ # "./core" → headless, isomorphic, ZERO DOM, ZERO wall-clock
@ -66,8 +66,8 @@ packages/mnswpr/ # @ayo-run/mnswpr — ONE published package
}
```
- `import mnswpr from '@ayo-run/mnswpr'` → DOM client, exactly as today.
- `import { GameSession, MinesweeperRules } from '@ayo-run/mnswpr/core'` → headless.
- `import mnswpr from '@cozy-games/mnswpr'` → DOM client, exactly as today.
- `import { GameSession, MinesweeperRules } from '@cozy-games/mnswpr/core'` → headless.
The core entry pulls in **zero DOM**, so a server (later) imports it without
dragging in browser code, and the browser bundle for `.` never includes the
core's server-only helpers.
@ -85,7 +85,7 @@ layering are orthogonal. Rules of the seam:
When Sudoku lands, `core/grid/` and `core/session/` move out verbatim into
`@cozy-games/grid` + `@cozy-games/game-session`; `core/minesweeper/` (and a new
`sudoku/`) depend on them, and `@ayo-run/mnswpr/core` re-exports from them so the
`sudoku/`) depend on them, and `@cozy-games/mnswpr/core` re-exports from them so the
consumer-facing sub-path is unchanged.
---
@ -346,7 +346,7 @@ Minesweeper(appId, version, {
| Needs a host tier | no | yes (Function/Worker + session store) |
| Use | offline play, published npm engine | host-owned sessions |
The published `@ayo-run/mnswpr` stays fully functional standalone (Local mode).
The published `@cozy-games/mnswpr` stays fully functional standalone (Local mode).
Running on a host opts into Remote. Same renderer, same input, same rules.
---
@ -425,7 +425,7 @@ appear in `core/` outside the injected `clock`/`rng` seams.
## 11. Open decisions
- **Package boundary:***resolved* — one `@ayo-run/mnswpr` package, core at the
- **Package boundary:***resolved* — one `@cozy-games/mnswpr` package, core at the
`./core` sub-path (§1). Extraction to `@cozy-games/grid` + `@cozy-games/game-session`
deferred to when Sudoku lands; the sub-path stays stable across that move.
- **Host mode (replay-validation vs live authority):** *deferred.* Ship

View file

@ -50,12 +50,12 @@ importers:
apps/mnswpr:
devDependencies:
'@ayo-run/mnswpr':
specifier: workspace:*
version: link:../../packages/mnswpr
'@cozy-games/leaderboard':
specifier: workspace:*
version: link:../../packages/leaderboard
'@cozy-games/mnswpr':
specifier: workspace:*
version: link:../../packages/mnswpr
'@cozy-games/utils':
specifier: workspace:*
version: link:../../packages/utils

View file

@ -1,4 +1,4 @@
// Publishes @ayo-run/mnswpr to npm using the mnswpr app README.md as the
// Publishes @cozy-games/mnswpr to npm using the mnswpr app README.md as the
// package's README (what npmjs.com displays).
//
// packages/mnswpr/README.md is a generated copy (gitignored) — the source of