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 ## 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. **`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. 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>`). - **`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/`** — `@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`. - **`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/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. - **`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. - **`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: 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 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). - as a `web component` (coming soon).
Using it as a library takes only a few lines — mount it onto any element by `id`: Using it as a library takes only a few lines — mount it onto any element by `id`:
```js ```js
import '@ayo-run/mnswpr/mnswpr.css' import '@cozy-games/mnswpr/mnswpr.css'
import mnswpr from '@ayo-run/mnswpr' import mnswpr from '@cozy-games/mnswpr'
const game = new mnswpr('app') const game = new mnswpr('app')
game.initialize() game.initialize()

View file

@ -1,7 +1,7 @@
import mnswpr from '@ayo-run/mnswpr/mnswpr.js' import mnswpr from '@cozy-games/mnswpr/mnswpr.js'
import '@ayo-run/mnswpr/mnswpr.css' import '@cozy-games/mnswpr/mnswpr.css'
import '@cozy-games/utils/loading/loading.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 { configureLeaderboard } from '@cozy-games/leaderboard/leaderboard-element.js'
import { FirebaseAdapter } from '@cozy-games/leaderboard/adapters/firebase.js' import { FirebaseAdapter } from '@cozy-games/leaderboard/adapters/firebase.js'
import { NicknameService } from './modules/nickname/nickname.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" "db:stop": "pkill -f '[c]loud-firestore-emulator'; pkill -f '[f]irebase.* emulators:'; true"
}, },
"devDependencies": { "devDependencies": {
"@ayo-run/mnswpr": "workspace:*", "@cozy-games/mnswpr": "workspace:*",
"@cozy-games/leaderboard": "workspace:*", "@cozy-games/leaderboard": "workspace:*",
"@cozy-games/utils": "workspace:*", "@cozy-games/utils": "workspace:*",
"firebase": "^12.11.0", "firebase": "^12.11.0",

View file

@ -17,7 +17,7 @@ import {
getFirestore, getDocs, collection, query, orderBy, limit getFirestore, getDocs, collection, query, orderBy, limit
} from 'firebase/firestore/lite' } 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 // Mirror of TimerService.pretty() (@cozy-games/utils timer) — inlined so this
// generator has no cross-module import chain to resolve under raw Node. // 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 **Status:** Accepted · **Repo:** cozy-games · **Date:** 2026-07
## Context ## 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 leaderboard package, future extractions). Applications built on these packages
differ in how they store data, authenticate users, and enforce rules. 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": "pnpm -r --filter \"./packages/*\" run build",
"build:lib": "vite build packages/mnswpr", "build:lib": "vite build packages/mnswpr",
"publish:lib": "node scripts/publish-lib.js", "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", "postinstall": "node scripts/ensure-java.mjs",
"prepare": "husky", "prepare": "husky",
"lint": "eslint .", "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: Now that the JS project is initialized and we have a development environment with `vite`, we will install **mnswpr** as a dependency:
```bash ```bash
npm i @ayo-run/mnswpr npm i @cozy-games/mnswpr
``` ```
Finally, you can run the installed `vite` dev server by running the following: 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 * main.js
*/ */
import '@ayo-run/mnswpr/mnswpr.css' import '@cozy-games/mnswpr/mnswpr.css'
import mnswpr from '@ayo-run/mnswpr' import mnswpr from '@cozy-games/mnswpr'
const game = new mnswpr('app') const game = new mnswpr('app')
game.initialize() game.initialize()

View file

@ -1,7 +1,7 @@
// @ts-check // @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 * wall clock. Runs identically in a browser (offline play) or on a server
* (authoritative timing / replay verification). See * (authoritative timing / replay verification). See
* docs/headless-core-and-client-design.md. * 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 * 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 * 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 DOM client reaches the same generator lazily through `MinesweeperRules`.
* *
* The injected `rng` is the determinism seam: the same rng sequence always * 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** 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 (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 ## 1. Package layout & the seam
**Decision (settled): one published package, core exposed as a sub-path.** Open- **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 source consumers keep installing a single `@cozy-games/mnswpr` and get the headless
core for free at `@ayo-run/mnswpr/core` — no second package to publish, version, core for free at `@cozy-games/mnswpr/core` — no second package to publish, version,
or document. The DOM client stays the default entry (`.`). 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) mnswpr.js # "." → DOM client (browser entry; today's default)
levels.js # shared by client + core (level presets) levels.js # shared by client + core (level presets)
core/ # "./core" → headless, isomorphic, ZERO DOM, ZERO wall-clock 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 mnswpr from '@cozy-games/mnswpr'` → DOM client, exactly as today.
- `import { GameSession, MinesweeperRules } from '@ayo-run/mnswpr/core'` → headless. - `import { GameSession, MinesweeperRules } from '@cozy-games/mnswpr/core'` → headless.
The core entry pulls in **zero DOM**, so a server (later) imports it without 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 dragging in browser code, and the browser bundle for `.` never includes the
core's server-only helpers. 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 When Sudoku lands, `core/grid/` and `core/session/` move out verbatim into
`@cozy-games/grid` + `@cozy-games/game-session`; `core/minesweeper/` (and a new `@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. consumer-facing sub-path is unchanged.
--- ---
@ -346,7 +346,7 @@ Minesweeper(appId, version, {
| Needs a host tier | no | yes (Function/Worker + session store) | | Needs a host tier | no | yes (Function/Worker + session store) |
| Use | offline play, published npm engine | host-owned sessions | | 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. 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 ## 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` `./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. deferred to when Sudoku lands; the sub-path stays stable across that move.
- **Host mode (replay-validation vs live authority):** *deferred.* Ship - **Host mode (replay-validation vs live authority):** *deferred.* Ship

View file

@ -50,12 +50,12 @@ importers:
apps/mnswpr: apps/mnswpr:
devDependencies: devDependencies:
'@ayo-run/mnswpr':
specifier: workspace:*
version: link:../../packages/mnswpr
'@cozy-games/leaderboard': '@cozy-games/leaderboard':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/leaderboard version: link:../../packages/leaderboard
'@cozy-games/mnswpr':
specifier: workspace:*
version: link:../../packages/mnswpr
'@cozy-games/utils': '@cozy-games/utils':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/utils 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). // package's README (what npmjs.com displays).
// //
// packages/mnswpr/README.md is a generated copy (gitignored) — the source of // packages/mnswpr/README.md is a generated copy (gitignored) — the source of