chore: flatten the app workspace
Some checks are pending
Checks / lint (push) Waiting to run
Checks / test (push) Waiting to run
Checks / content (push) Waiting to run

This commit is contained in:
ayo 2026-07-18 21:12:24 +02:00
parent 5b19bcd324
commit f6b71023ec
31 changed files with 373 additions and 475 deletions

11
.gitignore vendored
View file

@ -1,18 +1,18 @@
node_modules/ node_modules/
dist/ dist/
# generated at publish time from apps/mnswpr/README.md (see scripts/publish-lib.js)
packages/mnswpr/README.md
.claude .claude
# Production / local Firebase config values are not committed — set them as # Production / local Firebase config values are not committed — set them as
# Netlify env vars, or keep them in a local, gitignored env file. Dev values # Netlify env vars, or keep them in a local, gitignored env file. Dev values
# live in the committed apps/mnswpr/.env.development (public, non-secret keys). # live in the committed .env.development (public, non-secret keys).
.env.production .env.production
.env.local .env.local
.env.*.local .env.*.local
# Netlify CLI site link (written by `netlify link`)
.netlify/
# Firebase emulator artifacts # Firebase emulator artifacts
firebase-debug.log firebase-debug.log
firestore-debug.log firestore-debug.log
@ -22,6 +22,3 @@ ui-debug.log
*~ *~
*swo *swo
*swp *swp
# TEMPORARY - or unnecesary if done
apps/mnswpr/MIGRATION.md

View file

@ -6,27 +6,27 @@ Guidance for AI coding agents working in this repository.
Classic Minesweeper as a vanilla web game — no framework, no TypeScript (JSDoc + `// @ts-check` only). Deployed at [mnswpr.com](https://mnswpr.com) (Netlify), with a Firestore-backed leaderboard. Classic Minesweeper as a vanilla web game — no framework, no TypeScript (JSDoc + `// @ts-check` only). Deployed at [mnswpr.com](https://mnswpr.com) (Netlify), with a Firestore-backed leaderboard.
**This repo is only the app.** The engine, leaderboard, and shared services (`@cozy-games/mnswpr`, `@cozy-games/leaderboard`, `@cozy-games/utils`) live in [ayo-run/cozy-games](https://github.com/ayo-run/cozy-games) and are consumed here **from npm** — there is no local `packages/` to edit. A change to game mechanics, leaderboard internals, or the shared services belongs in that repo and arrives here as a version bump in `apps/mnswpr/package.json`. **This repo is only the app.** The engine, leaderboard, and shared services (`@cozy-games/mnswpr`, `@cozy-games/leaderboard`, `@cozy-games/utils`) live in [ayo-run/cozy-games](https://github.com/ayo-run/cozy-games) and are consumed here **from npm** — there is no local `packages/` to edit. A change to game mechanics, leaderboard internals, or the shared services belongs in that repo and arrives here as a version bump in `package.json`.
Structurally it is still a pnpm workspace (`pnpm-workspace.yaml` → `apps/*`) with a single member, `apps/mnswpr`, so app scripts are addressed with pnpm's `-F` filter. Structurally it is a single package at the repo root — no workspace, no `apps/` nesting. Every script runs from the root with plain `pnpm run <script>`.
## Commands ## Commands
Workspace-wide commands run from the root; app commands target the app with pnpm's `-F` filter. `pnpm dev` and `pnpm build` are root aliases for the two most common ones. All commands run from the repo root.
```bash ```bash
pnpm i # install (pnpm is required — this is a pnpm workspace) pnpm i # install (pnpm is required)
pnpm test # run the Vitest suite once (jsdom) pnpm test # run the Vitest suite once (jsdom)
pnpm test:watch # run Vitest in watch mode pnpm test:watch # run Vitest in watch mode
pnpm lint # eslint . (JS + CSS); runs automatically on pre-commit pnpm lint # eslint . (JS + CSS); runs automatically on pre-commit
pnpm lint:fix # eslint --fix pnpm lint:fix # eslint --fix
pnpm scan:secrets # secretlint over the tree pnpm scan:secrets # secretlint over the tree
pnpm -F mnswpr run dev # Firestore emulator + auto-seed + dev server (emulators:exec) — most common; needs JDK 21+ pnpm run dev # Firestore emulator + auto-seed + dev server (emulators:exec) — most common; needs JDK 21+
pnpm -F mnswpr run dev:no-db # plain vite, no emulator (UI-only work / no JDK) pnpm run dev:no-db # plain vite, no emulator (UI-only work / no JDK)
pnpm -F mnswpr run build # build the website -> apps/mnswpr/dist pnpm run build # build the website -> dist
pnpm -F mnswpr run preview # serve the production build pnpm run preview # serve the production build
pnpm -F mnswpr run build:preview # build the app and serve the production preview pnpm run build:preview # build the app and serve the production preview
``` ```
Run a single test file or name: `pnpm vitest run scripts/test/check-content.test.js` · `pnpm vitest run -t 'partial name'`. Run a single test file or name: `pnpm vitest run scripts/test/check-content.test.js` · `pnpm vitest run -t 'partial name'`.
@ -35,7 +35,7 @@ Run a single test file or name: `pnpm vitest run scripts/test/check-content.test
**Every infra operation — provision, deploy hosting, deploy DB, manage env — is doable from the CLI, and every configuration/schema is codified in-repo.** Nothing lives only in a web dashboard. There are two distinct layers, both owned by the app: **Every infra operation — provision, deploy hosting, deploy DB, manage env — is doable from the CLI, and every configuration/schema is codified in-repo.** Nothing lives only in a web dashboard. There are two distinct layers, both owned by the app:
**1. App infra *config* — declarative, committed, deployed state.** These files ARE the source of truth; deploying just pushes them up. For `mnswpr`, all under `apps/mnswpr/`: **1. App infra *config* — declarative, committed, deployed state.** These files ARE the source of truth; deploying just pushes them up. All at the repo root:
| File | Codifies | | File | Codifies |
| --- | --- | | --- | --- |
@ -46,50 +46,52 @@ Run a single test file or name: `pnpm vitest run scripts/test/check-content.test
| `netlify.toml` | Netlify hosting: build command, publish dir, redirects, headers, build env | | `netlify.toml` | Netlify hosting: build command, publish dir, redirects, headers, build env |
| `.env.example` | The full env-var contract; real prod values are set as Netlify env vars via CLI, never committed | | `.env.example` | The full env-var contract; real prod values are set as Netlify env vars via CLI, never committed |
**2. App infra *tools* — the CLIs that act on that config.** They are versioned **devDependencies** of the app (not `npx`-on-demand, not global installs), so `pnpm install` pins them and every machine gets the same version. `mnswpr` depends on `firebase-tools` and `netlify-cli`; its scripts call the `firebase`/`netlify` binaries directly (pnpm puts the app's `node_modules/.bin` on `PATH`). A future app using a different stack (Postgres, a different host) declares *its* CLIs as *its* devDependencies and backs the same generic script names — so `pnpm -F <name> run deploy:db` stays uniform. **2. App infra *tools* — the CLIs that act on that config.** They are versioned **devDependencies** (not `npx`-on-demand, not global installs), so `pnpm install` pins them and every machine gets the same version. The app depends on `firebase-tools` and `netlify-cli`; its scripts call the `firebase`/`netlify` binaries directly (pnpm puts `node_modules/.bin` on `PATH`).
Each app **owns its infra scripts** in its own `package.json` under generic, tech-agnostic names (`deploy:db`, not `deploy:firestore`) — run them by targeting the app with pnpm's `-F` filter (no root wrapper scripts): Infra scripts live in `package.json` under generic, tech-agnostic names (`deploy:db`, not `deploy:firestore`), so swapping the underlying stack doesn't change the command you type:
```bash ```bash
pnpm -F mnswpr run db:start # local DB emulator (mnswpr -> Firestore), standalone pnpm run db:start # local DB emulator (mnswpr -> Firestore), standalone
pnpm -F mnswpr run db:seed # seed the running local emulator pnpm run db:seed # seed the running local emulator
pnpm -F mnswpr run db:stop # kill a stray/orphaned Firestore emulator holding :8080 pnpm run db:stop # kill a stray/orphaned Firestore emulator holding :8080
pnpm -F mnswpr run deploy:db # deploy DB rules/indexes (-> firebase deploy --only firestore) pnpm run deploy:db # deploy DB rules/indexes (-> firebase deploy --only firestore)
pnpm -F mnswpr run deploy:site # build + deploy hosting (-> netlify deploy --prod --dir=dist) pnpm run deploy:site # build + deploy hosting (-> netlify deploy --prod --dir=dist)
``` ```
**One-time per app / per machine (all CLI, no dashboard):** **One-time per app / per machine (all CLI, no dashboard):**
```bash ```bash
pnpm -F mnswpr exec firebase login # auth the Firebase CLI pnpm exec firebase login # auth the Firebase CLI
pnpm -F mnswpr exec netlify login # auth the Netlify CLI pnpm exec netlify login # auth the Netlify CLI
pnpm -F mnswpr exec netlify link # bind the app dir to its Netlify site (writes .netlify/, gitignored) pnpm exec netlify link # bind the app dir to its Netlify site (writes .netlify/, gitignored)
``` ```
**Managing hosting env vars via CLI** (keeps prod Firebase keys + `VITE_LB_NAMESPACE=mw` out of git while still reproducible): **Managing hosting env vars via CLI** (keeps prod Firebase keys + `VITE_LB_NAMESPACE=mw` out of git while still reproducible):
```bash ```bash
pnpm -F mnswpr exec netlify env:set VITE_LB_NAMESPACE mw # set one var pnpm exec netlify env:set VITE_LB_NAMESPACE mw # set one var
pnpm -F mnswpr exec netlify env:import .env.production # bulk-import from a local (gitignored) env file pnpm exec netlify env:import .env.production # bulk-import from a local (gitignored) env file
pnpm -F mnswpr exec netlify env:list # inspect what's set pnpm exec netlify env:list # inspect what's set
``` ```
**Non-npm tools get a setup script instead of a devDependency.** The Firestore emulator needs **Java** (it's a JVM program), which isn't an npm package — so `pnpm install` runs a root `postinstall` (`scripts/ensure-java.mjs`) that installs a user-local Temurin JRE 21 into `~/.local` without `sudo` when `java` is missing — idempotent, non-fatal, and auto-skipped on `CI` / `SKIP_JRE_SETUP` / unsupported platforms. Any future infra tool that isn't on npm follows the same pattern (a checked-in setup script), never a manual install step. **Non-npm tools get a setup script instead of a devDependency.** The Firestore emulator needs **Java** (it's a JVM program), which isn't an npm package — so `pnpm install` runs a root `postinstall` (`scripts/ensure-java.mjs`) that installs a user-local Temurin JRE 21 into `~/.local` without `sudo` when `java` is missing — idempotent, non-fatal, and auto-skipped on `CI` / `SKIP_JRE_SETUP` / unsupported platforms. Any future infra tool that isn't on npm follows the same pattern (a checked-in setup script), never a manual install step.
Tests run under **Vitest** with a jsdom environment (root config in `vitest.config.js`), which collects `apps/**/test/**/*.test.js` and `scripts/test/**/*.test.js`. Today only `scripts/test/` exists (the content scanner); app-level tests go in `apps/mnswpr/test/`. Engine and shared-package tests live in the cozy-games repo, not here. For anything visual or input-timing related, verify by running `pnpm -F mnswpr run dev` and playing. Tests run under **Vitest** with a jsdom environment (config in `vitest.config.js`), which collects `test/**/*.test.js` and `scripts/test/**/*.test.js`. Today only `scripts/test/` exists (the content scanner); app-level tests go in `test/`. Engine and shared-package tests live in the cozy-games repo, not here. For anything visual or input-timing related, verify by running `pnpm run dev` and playing.
Node version: `.nvmrc` pins `lts/*`. Node version: `.nvmrc` pins `lts/*`.
## Repository layout ## Repository layout
`pnpm-workspace.yaml` declares `apps/*`; `apps/mnswpr/` (package name `mnswpr`) is the only member. A single package (`mnswpr`) rooted at the repo root.
- **`apps/mnswpr/`** — the mnswpr.com website. `main.js` composes the npm packages; `index.html` + `main.css` are the shell; `modules/` holds the two app-owned services; `scripts/` holds app scripts (dev seeding, legends export); `docs/` documents the backend. Owns its infra config (`firebase.json`, `firestore.rules`, `.firebaserc`, `netlify.toml`). - **root** — the mnswpr.com website. `main.js` composes the npm packages; `index.html` + `main.css` are the shell. Infra config lives here too (`firebase.json`, `firestore.rules`, `.firebaserc`, `netlify.toml`, `vite.config.js`).
- **`scripts/`** — repo-level tooling: `check-content.mjs` (content policy scanner, tested in `scripts/test/`) and `ensure-java.mjs` (postinstall JRE bootstrap). - **`modules/`** — the two app-owned services (`user/`, `nickname/`).
- **`docs/`** — backend documentation (Firestore data model, env migration).
- **`scripts/`** — tooling: `seed-dev-scores.js` and `export-legends.js` (app scripts), plus `check-content.mjs` (content policy scanner, tested in `scripts/test/`) and `ensure-java.mjs` (postinstall JRE bootstrap).
## Architecture ## Architecture
`apps/mnswpr/main.js` is ~75 lines and is the whole app: it wires three npm packages together and owns nothing else. `main.js` is ~75 lines and is the whole app: it wires three npm packages together and owns nothing else.
**The engine is decoupled from the app via two hooks.** `new mnswpr(appId, version, hooks)` is a constructor function that imperatively builds a `<table>` grid in the DOM. It knows nothing about Firebase or leaderboards. The app injects behavior through: **The engine is decoupled from the app via two hooks.** `new mnswpr(appId, version, hooks)` is a constructor function that imperatively builds a `<table>` grid in the DOM. It knows nothing about Firebase or leaderboards. The app injects behavior through:
@ -112,18 +114,18 @@ The engine-internal notes below describe code that lives in the cozy-games repo
## Leaderboard / Firebase ## Leaderboard / Firebase
Storage goes through `FirebaseAdapter` from `@cozy-games/leaderboard`; this repo supplies only configuration. Full data model, security rules, environments, and deployment: `apps/mnswpr/docs/firebase-leaderboards.md`. Storage goes through `FirebaseAdapter` from `@cozy-games/leaderboard`; this repo supplies only configuration. Full data model, security rules, environments, and deployment: `docs/firebase-leaderboards.md`.
**Firebase config comes from `VITE_FIREBASE_*` env vars** — dev values are committed in `apps/mnswpr/.env.development`, production values are Netlify env vars. For a client-only Firebase app the API key is **not a secret** (access is governed by `firestore.rules`), so don't treat the committed dev config as a leaked credential or try to hide it. **Firebase config comes from `VITE_FIREBASE_*` env vars** — dev values are committed in `.env.development`, production values are Netlify env vars. For a client-only Firebase app the API key is **not a secret** (access is governed by `firestore.rules`), so don't treat the committed dev config as a leaked credential or try to hide it.
**Namespace guards production.** `VITE_LB_NAMESPACE` selects the Firestore collection prefix and defaults to `mw-test`, so a missing env var can never write into the production board (`mw`). `VITE_FIRESTORE_EMULATOR=1` (set in `.env.development`) points dev at the local emulator; production builds always use real Firestore. **Namespace guards production.** `VITE_LB_NAMESPACE` selects the Firestore collection prefix and defaults to `mw-test`, so a missing env var can never write into the production board (`mw`). `VITE_FIRESTORE_EMULATOR=1` (set in `.env.development`) points dev at the local emulator; production builds always use real Firestore.
App-owned modules in `apps/mnswpr/modules/`: `UserService` (`user/user.js`) derives a non-cryptographic `browserId` fingerprint from navigator/screen properties to attribute scores without accounts; `NicknameService` (`nickname/nickname.js`) prompts for a display name on first visit and renders the greeting bar. App-owned modules in `modules/`: `UserService` (`user/user.js`) derives a non-cryptographic `browserId` fingerprint from navigator/screen properties to attribute scores without accounts; `NicknameService` (`nickname/nickname.js`) prompts for a display name on first visit and renders the greeting bar.
## Conventions ## Conventions
- **Code style is enforced by ESLint Stylistic**, not Prettier: 2-space indent, single quotes, **no semicolons**, no trailing commas, spaces inside `{ braces }` but not `[brackets]`. Run `pnpm lint:fix` before committing. Both `**/*.js` and `**/*.css` are linted (CSS via `@eslint/css`). - **Code style is enforced by ESLint Stylistic**, not Prettier: 2-space indent, single quotes, **no semicolons**, no trailing commas, spaces inside `{ braces }` but not `[brackets]`. Run `pnpm lint:fix` before committing. Both `**/*.js` and `**/*.css` are linted (CSS via `@eslint/css`).
- `apps/mnswpr/modules/` uses ES classes; `scripts/` uses plain functions. Match the surrounding style of the file you edit. - `modules/` uses ES classes; `scripts/` uses plain functions. Match the surrounding style of the file you edit.
- **Content policy.** Commit messages, branch names, PR text, and contributed lines are checked by `scripts/check-content.mjs` (hooks + the `Checks` workflow) against `.repo-policy.json`. Write commit messages in plain project voice; no tool-attribution trailers or footers, no `Co-Authored-By:` line for anyone outside the policy's `allowedCoAuthors`, no session links. - **Content policy.** Commit messages, branch names, PR text, and contributed lines are checked by `scripts/check-content.mjs` (hooks + the `Checks` workflow) against `.repo-policy.json`. Write commit messages in plain project voice; no tool-attribution trailers or footers, no `Co-Authored-By:` line for anyone outside the policy's `allowedCoAuthors`, no session links.
- The same scanner matches text against a maintainer-managed reserved-terms list. Findings report a location and a masked preview, never the term. If one flags your change, reword it or ask a maintainer — don't edit `.repo-policy.json`. - The same scanner matches text against a maintainer-managed reserved-terms list. Findings report a location and a masked preview, never the term. If one flags your change, reword it or ask a maintainer — don't edit `.repo-policy.json`.
- **Source stays JS + JSDoc (`// @ts-check`)** — no TypeScript. Nothing is published from this repo, so there is no type-generation step here; the `.d.ts` files shipped by `@cozy-games/*` are generated in the cozy-games repo. - **Source stays JS + JSDoc (`// @ts-check`)** — no TypeScript. Nothing is published from this repo, so there is no type-generation step here; the `.d.ts` files shipped by `@cozy-games/*` are generated in the cozy-games repo.

View file

@ -10,8 +10,8 @@ develop, test, and submit changes.
- **Node.js** — the version pinned in [`.nvmrc`](.nvmrc) (`lts/*`). With - **Node.js** — the version pinned in [`.nvmrc`](.nvmrc) (`lts/*`). With
[nvm](https://github.com/nvm-sh/nvm): `nvm use`. [nvm](https://github.com/nvm-sh/nvm): `nvm use`.
- **pnpm**this is a [pnpm](https://pnpm.io) workspace; **pnpm is required** - **pnpm****pnpm is required** (npm/yarn will not work). The repo pins its
(npm/yarn will not work). The repo pins its pnpm version via the pnpm version via the
`packageManager` field, so the simplest way to get the right one is Corepack: `packageManager` field, so the simplest way to get the right one is Corepack:
`corepack enable`. `corepack enable`.
- **Java (JDK 11+, 21 recommended)** — only needed to run the local Firestore - **Java (JDK 11+, 21 recommended)** — only needed to run the local Firestore
@ -22,46 +22,43 @@ develop, test, and submit changes.
## Project Structure ## Project Structure
- `apps/` - Playable games (each deploys independently) This repo is a single package at the root — the mnswpr.com web app. `main.js`
- `packages/` - Shared, publishable libraries and `index.html` are the app itself, `modules/` holds the app-owned services,
- `sites/` - Docs (Astro Starlight) and UI demos — placeholders for now `scripts/` holds tooling, and `docs/` documents the backend. The game engine and
leaderboard come from npm ([ayo-run/cozy-games](https://github.com/ayo-run/cozy-games));
there is no local `packages/` to edit.
Each app owns its own backend config (e.g. mnswpr's Firestore rules live in Backend config (Firestore rules, Firebase project aliases, Netlify hosting) is
`apps/mnswpr/`); the shared packages stay backend-agnostic. committed at the root — see [AGENTS.md](AGENTS.md) "Infra".
## Setup ## Setup
```bash ```bash
pnpm i # install all workspace dependencies pnpm i # install dependencies
``` ```
## Workspace commands (run from the repo root) ## Commands (all run from the repo root)
```bash ```bash
pnpm test # run the whole test suite once (Vitest, jsdom) pnpm test # run the whole test suite once (Vitest, jsdom)
pnpm test:watch # tests in watch mode pnpm test:watch # tests in watch mode
pnpm lint # eslint (JS + CSS) pnpm lint # eslint (JS + CSS)
pnpm lint:fix # eslint --fix pnpm lint:fix # eslint --fix
pnpm build:lib # build the publishable engine -> packages/mnswpr/dist
``` ```
## Running a game locally ## Running the game locally
Apps aren't run from the root — target one by name with pnpm's `-F` filter. Apps
are named `<name>` (e.g. `mnswpr`), so every app runs the same way
(`pnpm -F <name> run <script>`):
```bash ```bash
pnpm -F mnswpr run dev # Vite dev server + Firestore emulator (auto-seeded) — needs Java pnpm run dev # Vite dev server + Firestore emulator (auto-seeded) — needs Java
pnpm -F mnswpr run dev:no-db # Vite only, no emulator (UI work, or no Java) pnpm run dev:no-db # Vite only, no emulator (UI work, or no Java)
pnpm -F mnswpr run build # build the app -> apps/mnswpr/dist pnpm run build # build the app -> dist
pnpm -F mnswpr run preview # preview the production build pnpm run preview # preview the production build
``` ```
## Tests ## Tests
Tests run under **Vitest** with a **jsdom** environment and live next to the code Tests run under **Vitest** with a **jsdom** environment and live next to the code
they exercise (`packages/*/test/`). They drive real behavior — e.g. mounting the they exercise (`test/`, `scripts/test/`). They drive real behavior — e.g. mounting the
game and dispatching DOM events — not just isolated unit calls. Run `pnpm test` game and dispatching DOM events — not just isolated unit calls. Run `pnpm test`
(or `pnpm test:watch`) before opening a PR, and add tests for new behavior. (or `pnpm test:watch`) before opening a PR, and add tests for new behavior.
@ -75,8 +72,7 @@ Style is enforced by **ESLint (Stylistic)**, not Prettier:
Run `pnpm lint:fix` before committing. The codebase is **plain JavaScript with Run `pnpm lint:fix` before committing. The codebase is **plain JavaScript with
JSDoc + `// @ts-check`** — no TypeScript. Match the style and patterns of the file JSDoc + `// @ts-check`** — no TypeScript. Match the style and patterns of the file
you're editing (the game engine uses plain functions and closures; `packages/utils` you're editing (`modules/` uses ES classes; `scripts/` uses plain functions).
and the app modules use ES classes).
A **pre-commit hook** runs the linter and a secret scan automatically — commits A **pre-commit hook** runs the linter and a secret scan automatically — commits
fail if either does. Keep credentials and any `.env.production` out of commits. fail if either does. Keep credentials and any `.env.production` out of commits.
@ -89,22 +85,19 @@ devDependencies) — no web dashboards. For mnswpr (Firestore + Netlify), the lo
DB emulator is all a contributor needs: DB emulator is all a contributor needs:
```bash ```bash
pnpm -F mnswpr run db:start # start the local Firestore emulator (standalone) — needs Java pnpm run db:start # start the local Firestore emulator (standalone) — needs Java
pnpm -F mnswpr run db:seed # seed the running emulator with sample data pnpm run db:seed # seed the running emulator with sample data
pnpm -F mnswpr run db:stop # stop a stray emulator holding :8080 pnpm run db:stop # stop a stray emulator holding :8080
``` ```
Deploy scripts (`deploy:db`, `deploy:site`) also exist but require project Deploy scripts (`deploy:db`, `deploy:site`) also exist but require project
credentials, so they're for maintainers. See credentials, so they're for maintainers. See [README.md](README.md) and
[apps/mnswpr/README.md](apps/mnswpr/README.md) and that app's `docs/` for the full [`docs/`](docs/) for the full backend reference.
backend reference.
## Project structure & decisions ## Project structure
The repo layout is in the [README](README.md#layout). Significant architecture The repo layout and architecture are documented in [AGENTS.md](AGENTS.md) —
choices are recorded in [`docs/decisions/`](docs/decisions/) — worth a read before worth a read before a large change.
a large change. Shared packages stay backend-agnostic; each app owns its own
backend config.
## Submitting changes ## Submitting changes

163
README.md
View file

@ -1,29 +1,156 @@
# mnswpr # Play Minesweeper Online for Free
[![Netlify Status](https://api.netlify.com/api/v1/badges/172478bd-afc5-4e47-95ba-d9ab814248fb/deploy-status)](https://app.netlify.com/sites/mnswpr/deploys)
The [mnswpr.com](https://mnswpr.com) web app — classic Minesweeper in the browser. Play it here: [mnswpr.com](https://mnswpr.com). This is the classic game **Minesweeper** built with vanilla web technologies (i.e., no framework dependency).
![screenshot](screenshot.png) ![mnswpr gameplay](https://git.ayo.run/ayo/mnswpr/raw/branch/main/screenshot.png)
This repo is the game app. The shared, reusable modules it builds on ## How to Play
(`@cozy-games/mnswpr`, `@cozy-games/leaderboard`, `@cozy-games/utils`, and the
rest of the `@cozy-games/*` family) live in their own repo: The goal is to reveal every safe cell without detonating a mine. Your **first click is always safe**.
[ayo-run/cozy-games](https://github.com/ayo-run/cozy-games), and are consumed
here from npm. - **Left click** — reveal a cell
- **Right click** — flag / unflag a suspected mine
- **Left + right click together** (chording) — reveal the neighbors of a satisfied number
- **Touch** — tap to reveal, long-press to flag
## Ways to Use
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/@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 '@cozy-games/mnswpr/mnswpr.css'
import mnswpr from '@cozy-games/mnswpr'
const game = new mnswpr('app')
game.initialize()
```
## Tooling
The project has gone through years of existence. It started from 2019 when tooling was massively different. I have [modernized it](https://elk.zone/social.ayco.io/@ayo/116333804543330938) since and have witnessed how much easier and faster it is to build now - even without web frameworks or LLMs!
As of now the tooling I use are:
- [Vite](https://vite.dev/) for bundling and development server
- [Eslint](https://eslint.org) for JS linting & [CSS linting](https://eslint.org/blog/2025/02/eslint-css-support/)
- [ESLint Stylistic](https://eslint.style) for JS formatting
- [Husky](https://typicode.github.io/husky/) for git hooks
- [PNPM](https://pnpm.io/installation) for dependency management
- and a bunch of automation using scripts and Continuous Integration actions
Because a big part of this project's purpose is to track how the software development industry evolves — and because it has come a long way in modernizing along the way — I now also use it as a **playground for coding agents**. It's a small, framework-free, well-scoped codebase, which makes it a great sandbox to see how AI agents read, reason about, and change real code. To help them get their bearings quickly, the repo ships an [`AGENTS.md`](./AGENTS.md) describing the architecture and conventions.
## Development
Technology Stack: HTML, JS, and CSS; [Google Firebase](https://firebase.google.com) for leader board store; [Netlify](https://netlify.com) for hosting
To start development, you need [`node`](https://nodejs.org/en/download). I highly recommend [`pnpm`](https://pnpm.io/installation) to be used as well. Once you know you have this, you can do the following:
1. Install dependencies: `pnpm i`
2. Start the dev server: `pnpm run dev`
The rest of the everyday commands:
```bash
pnpm test # run the Vitest suite
pnpm lint # ESLint (JS + CSS)
pnpm lint:fix # ESLint with autofix
pnpm run build # build the website
pnpm run preview # serve the production build
```
The game engine and leaderboard are consumed from npm — they live in
[ayo-run/cozy-games](https://github.com/ayo-run/cozy-games), not here.
### Leaderboard (local Firestore emulator)
The leader board is backed by [Google Firestore](https://firebase.google.com). For local development the app talks to the **Firestore emulator** by default — fully local, no cloud, no deploy. The flag `VITE_FIRESTORE_EMULATOR=1` is already set in `.env.development`.
`dev` is the whole loop in one command — it wraps Vite in `firebase emulators:exec`, so the Firestore emulator (on :8080, + UI) comes up, gets **seeded with sample scores automatically**, and the app dev server starts against it; everything shuts down when you stop it. (Each `dev` starts a fresh in-memory emulator, so the auto-seed writes exactly one clean set every time — no accumulation.) `firebase-tools` is a pinned **devDependency** of this app (installed by `pnpm install`, invoked as the `firebase` binary), so the only extra prerequisite is **Java** — the Firestore emulator is a Java program (`java -jar cloud-firestore-emulator-*.jar`), and firebase-tools does not bundle a JRE.
**Usually automatic.** `pnpm install` runs a root `postinstall` ([`scripts/ensure-java.mjs`](../../scripts/ensure-java.mjs)) that installs a user-local Temurin JRE 21 into `~/.local` (no `sudo`) when `java` isn't already on your PATH. It's idempotent and never fails the install, and it skips when `CI` or `SKIP_JRE_SETUP=1` is set, or on unsupported platforms.
If that skipped and you need Java (or prefer a system-wide install), do it manually — **install a JRE (Java 11+; 21 recommended):**
```bash
# Debian / Ubuntu / Pop!_OS
sudo apt update && sudo apt install -y openjdk-21-jre-headless
# Fedora
sudo dnf install -y java-21-openjdk-headless
# macOS (Homebrew)
brew install openjdk@21
java -version # verify: should print "openjdk 21.x" (or 11+)
```
No `sudo`? Install a JRE into your home directory instead (no root needed):
```bash
curl -fsSL -o /tmp/jre21.tgz "https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jre/hotspot/normal/eclipse"
mkdir -p ~/.local/lib && tar xzf /tmp/jre21.tgz -C ~/.local/lib
ln -sf ~/.local/lib/jdk-21*-jre/bin/java ~/.local/bin/java # ~/.local/bin is already on PATH
java -version
```
Without Java, `dev` and `db:start` fail with `Could not spawn 'java -version'`. Install it to a permanent location — a JRE unpacked under `/tmp` disappears when the OS cleans temp files. Then just:
```bash
pnpm run dev # emulator (:8080 + UI) + auto-seed + app dev server — one command
```
That's the everyday loop. The other DB scripts are for when you want to run pieces separately:
```bash
pnpm run db:start # emulator only (stays up across app restarts); pair with dev:no-db
pnpm run db:seed # seed a separately-running emulator (what dev does for you)
pnpm run db:stop # kill a stray/orphaned emulator holding :8080
```
To skip the emulator entirely — for quick UI-only work, or if you don't have a JDK — run `pnpm run dev:no-db` (plain Vite) and set `VITE_FIRESTORE_EMULATOR=` (empty) in a local, gitignored `.env.local`; the app then uses the cloud `mw-test` namespace instead.
See [`docs/firebase-leaderboards.md`](./docs/firebase-leaderboards.md) for the full data model, security rules, environments, and deployment.
## Contributing ## Contributing
Setup, running the game locally, testing, code style, and local infra all live Contributions are welcome! See [`AGENTS.md`](./AGENTS.md) for the architecture, conventions, and release workflow before opening a pull request.
in **[CONTRIBUTING.md](CONTRIBUTING.md)**. In short — this is a
[pnpm](https://pnpm.io) workspace: ## You just want to play?
*👉 The live site is here: [mnswpr.com](https://mnswpr.com)*
## Background
One day, while working in my home office, I heard loud and fast mouse clicks coming from our bedroom. It's my wife, playing her favorite game (Minesweeper) on a crappy website full of advertisements.
I can't allow this, it's a security issue. 🤣
But it is also an opportunity.
I wanted to give her the same game, with a similar leader board she can dominate. And this is also a chance for me to dig deeper into vanilla JS.
Can I make a page with complex interactions (more on this later) without any library dependency?
## What I have learned:
1. JS is awesome ✨
1. We don't always *need* JS frameworks (or TS) ✨
1. Even subtle UI changes *can improve* user gameplay experience ✨
1. There's more ways to break your app than you are initially aware of ✨
1. Competition motivates users to use your app more ✨
1. Hash in bundled filenames helps avoid issues with browser caching (when shipping versions fast) ✨
```bash
pnpm i # install
pnpm test # run the test suite
pnpm -F mnswpr run dev # run the Minesweeper app locally
```
See [apps/mnswpr/README.md](apps/mnswpr/README.md) for the game itself.
## License ## License
MIT © Ayo Ayco [MIT](../../LICENSE)
---
_Just keep building._<br>
_A project by [Ayo](https://ayo.ayco.io)_

View file

@ -1,153 +0,0 @@
# Play Minesweeper Online for Free
[![Netlify Status](https://api.netlify.com/api/v1/badges/172478bd-afc5-4e47-95ba-d9ab814248fb/deploy-status)](https://app.netlify.com/sites/mnswpr/deploys)
Play it here: [mnswpr.com](https://mnswpr.com). This is the classic game **Minesweeper** built with vanilla web technologies (i.e., no framework dependency).
![mnswpr gameplay](https://git.ayo.run/ayo/mnswpr/raw/branch/main/screenshot.png)
## How to Play
The goal is to reveal every safe cell without detonating a mine. Your **first click is always safe**.
- **Left click** — reveal a cell
- **Right click** — flag / unflag a suspected mine
- **Left + right click together** (chording) — reveal the neighbors of a satisfied number
- **Touch** — tap to reveal, long-press to flag
## Ways to Use
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/@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 '@cozy-games/mnswpr/mnswpr.css'
import mnswpr from '@cozy-games/mnswpr'
const game = new mnswpr('app')
game.initialize()
```
## Tooling
The project has gone through years of existence. It started from 2019 when tooling was massively different. I have [modernized it](https://elk.zone/social.ayco.io/@ayo/116333804543330938) since and have witnessed how much easier and faster it is to build now - even without web frameworks or LLMs!
As of now the tooling I use are:
- [Vite](https://vite.dev/) for bundling and development server
- [Eslint](https://eslint.org) for JS linting & [CSS linting](https://eslint.org/blog/2025/02/eslint-css-support/)
- [ESLint Stylistic](https://eslint.style) for JS formatting
- [Husky](https://typicode.github.io/husky/) for git hooks
- [PNPM](https://pnpm.io/installation) for dependency & workspace management
- and a bunch of automation using scripts and Continuous Integration actions
Because a big part of this project's purpose is to track how the software development industry evolves — and because it has come a long way in modernizing along the way — I now also use it as a **playground for coding agents**. It's a small, framework-free, well-scoped codebase, which makes it a great sandbox to see how AI agents read, reason about, and change real code. To help them get their bearings quickly, the repo ships an [`AGENTS.md`](./AGENTS.md) describing the architecture and conventions.
## Development
Technology Stack: HTML, JS, and CSS; [Google Firebase](https://firebase.google.com) for leader board store; [Netlify](https://netlify.com) for hosting
To start development, you need [`node`](https://nodejs.org/en/download). I highly recommend [`pnpm`](https://pnpm.io/installation) to be used as well. Once you know you have this, you can do the following:
1. Install dependencies: `pnpm i`
2. Start the dev server: `pnpm -F mnswpr run dev`
The rest of the everyday commands:
```bash
pnpm test # run the Vitest suite (workspace-wide)
pnpm lint # ESLint (JS + CSS)
pnpm lint:fix # ESLint with autofix
pnpm -F mnswpr run build # build the website
pnpm build:lib # build the publishable library
```
### Leaderboard (local Firestore emulator)
The leader board is backed by [Google Firestore](https://firebase.google.com). For local development the app talks to the **Firestore emulator** by default — fully local, no cloud, no deploy. The flag `VITE_FIRESTORE_EMULATOR=1` is already set in `app/.env.development`.
`dev` is the whole loop in one command — it wraps Vite in `firebase emulators:exec`, so the Firestore emulator (on :8080, + UI) comes up, gets **seeded with sample scores automatically**, and the app dev server starts against it; everything shuts down when you stop it. (Each `dev` starts a fresh in-memory emulator, so the auto-seed writes exactly one clean set every time — no accumulation.) `firebase-tools` is a pinned **devDependency** of this app (installed by `pnpm install`, invoked as the `firebase` binary), so the only extra prerequisite is **Java** — the Firestore emulator is a Java program (`java -jar cloud-firestore-emulator-*.jar`), and firebase-tools does not bundle a JRE.
**Usually automatic.** `pnpm install` runs a root `postinstall` ([`scripts/ensure-java.mjs`](../../scripts/ensure-java.mjs)) that installs a user-local Temurin JRE 21 into `~/.local` (no `sudo`) when `java` isn't already on your PATH. It's idempotent and never fails the install, and it skips when `CI` or `SKIP_JRE_SETUP=1` is set, or on unsupported platforms.
If that skipped and you need Java (or prefer a system-wide install), do it manually — **install a JRE (Java 11+; 21 recommended):**
```bash
# Debian / Ubuntu / Pop!_OS
sudo apt update && sudo apt install -y openjdk-21-jre-headless
# Fedora
sudo dnf install -y java-21-openjdk-headless
# macOS (Homebrew)
brew install openjdk@21
java -version # verify: should print "openjdk 21.x" (or 11+)
```
No `sudo`? Install a JRE into your home directory instead (no root needed):
```bash
curl -fsSL -o /tmp/jre21.tgz "https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jre/hotspot/normal/eclipse"
mkdir -p ~/.local/lib && tar xzf /tmp/jre21.tgz -C ~/.local/lib
ln -sf ~/.local/lib/jdk-21*-jre/bin/java ~/.local/bin/java # ~/.local/bin is already on PATH
java -version
```
Without Java, `dev` and `db:start` fail with `Could not spawn 'java -version'`. Install it to a permanent location — a JRE unpacked under `/tmp` disappears when the OS cleans temp files. Then just:
```bash
pnpm -F mnswpr run dev # emulator (:8080 + UI) + auto-seed + app dev server — one command
```
That's the everyday loop. The other DB scripts are for when you want to run pieces separately:
```bash
pnpm -F mnswpr run db:start # emulator only (stays up across app restarts); pair with dev:no-db
pnpm -F mnswpr run db:seed # seed a separately-running emulator (what dev does for you)
pnpm -F mnswpr run db:stop # kill a stray/orphaned emulator holding :8080
```
To skip the emulator entirely — for quick UI-only work, or if you don't have a JDK — run `pnpm -F mnswpr run dev:no-db` (plain Vite) and set `VITE_FIRESTORE_EMULATOR=` (empty) in a local, gitignored `app/.env.local`; the app then uses the cloud `mw-test` namespace instead.
See [`docs/firebase-leaderboards.md`](./docs/firebase-leaderboards.md) for the full data model, security rules, environments, and deployment.
## Contributing
Contributions are welcome! See [`AGENTS.md`](./AGENTS.md) for the architecture, conventions, and release workflow before opening a pull request.
## You just want to play?
*👉 The live site is here: [mnswpr.com](https://mnswpr.com)*
## Background
One day, while working in my home office, I heard loud and fast mouse clicks coming from our bedroom. It's my wife, playing her favorite game (Minesweeper) on a crappy website full of advertisements.
I can't allow this, it's a security issue. 🤣
But it is also an opportunity.
I wanted to give her the same game, with a similar leader board she can dominate. And this is also a chance for me to dig deeper into vanilla JS.
Can I make a page with complex interactions (more on this later) without any library dependency?
## What I have learned:
1. JS is awesome ✨
1. We don't always *need* JS frameworks (or TS) ✨
1. Even subtle UI changes *can improve* user gameplay experience ✨
1. There's more ways to break your app than you are initially aware of ✨
1. Competition motivates users to use your app more ✨
1. Hash in bundled filenames helps avoid issues with browser caching (when shipping versions fast) ✨
## License
[MIT](../../LICENSE)
---
_Just keep building._<br>
_A project by [Ayo](https://ayo.ayco.io)_

View file

@ -1,30 +0,0 @@
{
"name": "mnswpr",
"version": "0.0.1",
"description": "the mnswpr.com web app",
"private": true,
"type": "module",
"main": "main.js",
"scripts": {
"dev": "firebase emulators:exec --only firestore --ui \"node scripts/seed-dev-scores.js; vite\"",
"dev:no-db": "vite",
"build": "vite build",
"preview": "vite preview",
"build:preview": "npm run build && npm run preview",
"deploy:db": "firebase deploy --only firestore",
"deploy:site": "npm run build && netlify deploy --prod --dir=dist",
"db:start": "firebase emulators:start --only firestore",
"db:seed": "FIRESTORE_EMULATOR_HOST=127.0.0.1:8080 node scripts/seed-dev-scores.js",
"db:stop": "pkill -f '[c]loud-firestore-emulator'; pkill -f '[f]irebase.* emulators:'; true"
},
"devDependencies": {
"@cozy-games/mnswpr": "^0.5.4",
"@cozy-games/leaderboard": "^0.1.1",
"@cozy-games/utils": "^0.0.3",
"firebase": "^12.11.0",
"firebase-tools": "^15.22.4",
"netlify-cli": "^26.1.0",
"web-component-base": "^5.0.0"
},
"author": "Ayo Ayco"
}

View file

@ -103,21 +103,21 @@ so run it as the `firebase` binary via pnpm — no global install, no `npx`. The
Firebase project already exists — no need to create it. Firebase project already exists — no need to create it.
```bash ```bash
pnpm -F mnswpr exec firebase login pnpm exec firebase login
``` ```
### Deploy rules + indexes ### Deploy rules + indexes
The `deploy:db` script (`pnpm -F mnswpr run deploy:db`) deploys everything under The `deploy:db` script (`pnpm run deploy:db`) deploys everything under
`firestore` to the **default** project. To target a specific project or a subset, `firestore` to the **default** project. To target a specific project or a subset,
call the CLI directly: call the CLI directly:
```bash ```bash
# production (default alias 'prod') # production (default alias 'prod')
pnpm -F mnswpr exec firebase deploy --only firestore:rules,firestore:indexes --project prod pnpm exec firebase deploy --only firestore:rules,firestore:indexes --project prod
# development database # development database
pnpm -F mnswpr exec firebase deploy --only firestore:rules,firestore:indexes --project dev pnpm exec firebase deploy --only firestore:rules,firestore:indexes --project dev
``` ```
> ⚠️ Deploying **replaces** whatever rules currently live in the Console. The > ⚠️ Deploying **replaces** whatever rules currently live in the Console. The
@ -142,7 +142,7 @@ locally (so you validate them before deploying). The flag
Everyday dev loop: Everyday dev loop:
```bash ```bash
pnpm -F mnswpr run dev # emulator (+ UI) on :8080, auto-seeded with sample scores, + app dev server pnpm run dev # emulator (+ UI) on :8080, auto-seeded with sample scores, + app dev server
``` ```
`dev` seeds the fresh emulator for you (via `emulators:exec "node scripts/seed-dev-scores.js; vite"`). Use the standalone `db:start` / `db:seed` scripts only when running the emulator separately from the app. `dev` seeds the fresh emulator for you (via `emulators:exec "node scripts/seed-dev-scores.js; vite"`). Use the standalone `db:start` / `db:seed` scripts only when running the emulator separately from the app.
@ -175,17 +175,16 @@ Everything above is doable via the CLI. If you can't use it:
## Legends (frozen hall of fame) ## Legends (frozen hall of fame)
The old all-time leaders are preserved as a **fully-rendered static page** The old all-time leaders are preserved as a **fully-rendered static page**
[`app/legends.html`](../app/legends.html). The records are baked straight into the [`legends.html`](../legends.html). The records are baked straight into the
HTML with times pre-formatted; there is **no JavaScript and no Firebase** at page HTML with times pre-formatted; there is **no JavaScript and no Firebase** at page
load. The data never changes. load. The data never changes.
The page is generated by [`scripts/export-legends.js`](../scripts/export-legends.js), The page is generated by [`scripts/export-legends.js`](../scripts/export-legends.js),
which snapshots `mw-leaders/{level}/games` and writes the whole `legends.html`. which snapshots `mw-leaders/{level}/games` and writes the whole `legends.html`.
It only needs re-running if you ever want to regenerate. Because `firebase` is an It only needs re-running if you ever want to regenerate. Run it from the repo root:
app-workspace dependency, run it so Node can resolve it:
```bash ```bash
(cd app && node ../scripts/export-legends.js) node scripts/export-legends.js
``` ```
The Firebase config for this one-off is hardcoded in the script (public keys). The Firebase config for this one-off is hardcoded in the script (public keys).

View file

@ -139,8 +139,8 @@ Both aliases target the one project we actually have:
### 7. Deploy & verify ### 7. Deploy & verify
1. `pnpm -F mnswpr exec firebase deploy --only firestore:rules,firestore:indexes --project prod` 1. `pnpm exec firebase deploy --only firestore:rules,firestore:indexes --project prod`
2. Set the Netlify env vars via CLI: `pnpm -F mnswpr exec netlify env:import .env.production` (or `netlify env:set VITE_LB_NAMESPACE mw` + each `VITE_FIREBASE_*`). 2. Set the Netlify env vars via CLI: `pnpm exec netlify env:import .env.production` (or `netlify env:set VITE_LB_NAMESPACE mw` + each `VITE_FIREBASE_*`).
3. `pnpm dev`, win a game → confirm the write lands in **`mw-test-scores`**, and 3. `pnpm dev`, win a game → confirm the write lands in **`mw-test-scores`**, and
the prod `mw-scores` is untouched (Firestore console). the prod `mw-scores` is untouched (Firestore console).
4. Confirm reads on both `mw-scores` and `mw-test-scores` succeed under the new 4. Confirm reads on both `mw-scores` and `mw-test-scores` succeed under the new

View file

@ -16,7 +16,7 @@ rules_version = '2'
// block is guarded by a regex on the captured collection name. // block is guarded by a regex on the captured collection name.
// //
// Deploy with (see docs/firebase-leaderboards.md): // Deploy with (see docs/firebase-leaderboards.md):
// pnpm -F mnswpr exec firebase deploy --only firestore:rules --project prod // pnpm exec firebase deploy --only firestore:rules --project prod
service cloud.firestore { service cloud.firestore {
match /databases/{database}/documents { match /databases/{database}/documents {

View file

@ -1,13 +1,12 @@
# Netlify hosting config for mnswpr.com — the app-level, declarative hosting # Netlify hosting config for mnswpr.com — the app-level, declarative hosting
# state, codified here so hosting is reproducible from the repo and deployed via # state, codified here so hosting is reproducible from the repo and deployed via
# CLI (`pnpm -F mnswpr run deploy:site`), never the web dashboard. This file is # CLI (`pnpm run deploy:site`), never the web dashboard. This file is
# app infra *config*; the CLI that reads it (`netlify-cli`) is app infra *tooling* # app infra *config*; the CLI that reads it (`netlify-cli`) is app infra *tooling*
# and lives in this app's devDependencies. See AGENTS.md "Infra". # and lives in this app's devDependencies. See AGENTS.md "Infra".
# The `deploy:site` script pre-builds locally and uploads `dist` via the CLI, so # The `deploy:site` script pre-builds locally and uploads `dist` via the CLI, so
# this [build] block is only exercised when Netlify builds the site from git # this [build] block is only exercised when Netlify builds the site from git
# (dashboard "base directory" = apps/mnswpr). It is a pnpm workspace, so # (dashboard "base directory" = the repo root — no longer `apps/mnswpr`).
# `pnpm install` from here installs the whole monorepo before building.
[build] [build]
command = "pnpm install --frozen-lockfile && pnpm run build" command = "pnpm install --frozen-lockfile && pnpm run build"
publish = "dist" publish = "dist"
@ -18,7 +17,7 @@
# git-triggered builds; local CLI builds read app/.env.production instead. # git-triggered builds; local CLI builds read app/.env.production instead.
# Firebase VITE_* keys are public but are set as Netlify env vars (via # Firebase VITE_* keys are public but are set as Netlify env vars (via
# `netlify env:set` / `netlify env:import`, not the dashboard) to keep prod # `netlify env:set` / `netlify env:import`, not the dashboard) to keep prod
# values out of git — see apps/mnswpr/.env.example. # values out of git — see .env.example.
VITE_LB_NAMESPACE = "mw" VITE_LB_NAMESPACE = "mw"
# Pretty URL for the frozen Legends page (built as /legends.html). # Pretty URL for the frozen Legends page (built as /legends.html).

View file

@ -1,20 +1,29 @@
{ {
"name": "cozy-games", "name": "mnswpr",
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"description": "mnswpr the mnswpr.com web app", "description": "mnswpr \u2014 the mnswpr.com web app",
"author": "Ayo Ayco", "author": "Ayo Ayco",
"type": "module", "type": "module",
"main": "main.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/ayo-run/mnswpr" "url": "https://github.com/ayo-run/mnswpr"
}, },
"homepage": "https://mnswpr.com", "homepage": "https://mnswpr.com",
"scripts": { "scripts": {
"dev": "firebase emulators:exec --only firestore --ui \"node scripts/seed-dev-scores.js; vite\"",
"dev:no-db": "vite",
"build": "vite build",
"preview": "vite preview",
"build:preview": "pnpm run build && pnpm run preview",
"test": "vitest run", "test": "vitest run",
"dev": "pnpm -F mnswpr dev",
"test:watch": "vitest", "test:watch": "vitest",
"build": "pnpm -F mnswpr build", "deploy:db": "firebase deploy --only firestore",
"deploy:site": "pnpm run build && netlify deploy --prod --dir=dist",
"db:start": "firebase emulators:start --only firestore",
"db:seed": "FIRESTORE_EMULATOR_HOST=127.0.0.1:8080 node scripts/seed-dev-scores.js",
"db:stop": "pkill -f '[c]loud-firestore-emulator'; pkill -f '[f]irebase.* emulators:'; true",
"postinstall": "node scripts/ensure-java.mjs", "postinstall": "node scripts/ensure-java.mjs",
"prepare": "husky", "prepare": "husky",
"lint": "eslint .", "lint": "eslint .",
@ -22,20 +31,27 @@
"scan:secrets": "secretlint --secretlintignore .secretlintignore \"**/*\"" "scan:secrets": "secretlint --secretlintignore .secretlintignore \"**/*\""
}, },
"devDependencies": { "devDependencies": {
"@cozy-games/leaderboard": "^0.1.1",
"@cozy-games/mnswpr": "^0.5.4",
"@cozy-games/utils": "^0.0.3",
"@eslint/css": "^1.1.0", "@eslint/css": "^1.1.0",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@secretlint/secretlint-rule-preset-recommend": "^13.0.2", "@secretlint/secretlint-rule-preset-recommend": "^13.0.2",
"@stylistic/eslint-plugin": "^5.10.0", "@stylistic/eslint-plugin": "^5.10.0",
"bumpp": "^11.0.1", "bumpp": "^11.0.1",
"eslint": "^10.1.0", "eslint": "^10.1.0",
"firebase": "^12.11.0",
"firebase-tools": "^15.22.4",
"globals": "^17.4.0", "globals": "^17.4.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
"netlify-cli": "^26.1.0",
"secretlint": "^13.0.2", "secretlint": "^13.0.2",
"simple-git": "^3.33.0", "simple-git": "^3.33.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^8.0.16", "vite": "^8.0.16",
"vitest": "^4.1.9" "vitest": "^4.1.9",
"web-component-base": "^5.0.0"
}, },
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b" "packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
} }

View file

@ -7,51 +7,6 @@ settings:
importers: importers:
.: .:
devDependencies:
'@eslint/css':
specifier: ^1.1.0
version: 1.1.0
'@eslint/js':
specifier: ^10.0.1
version: 10.0.1(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2))
'@secretlint/secretlint-rule-preset-recommend':
specifier: ^13.0.2
version: 13.0.2
'@stylistic/eslint-plugin':
specifier: ^5.10.0
version: 5.10.0(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2))
bumpp:
specifier: ^11.0.1
version: 11.0.1
eslint:
specifier: ^10.1.0
version: 10.1.0(jiti@2.6.1)(supports-color@10.2.2)
globals:
specifier: ^17.4.0
version: 17.4.0
husky:
specifier: ^9.1.7
version: 9.1.7
jsdom:
specifier: ^29.1.1
version: 29.1.1
secretlint:
specifier: ^13.0.2
version: 13.0.2(supports-color@10.2.2)
simple-git:
specifier: ^3.33.0
version: 3.33.0(supports-color@10.2.2)
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^8.0.16
version: 8.0.16(@types/node@25.5.2)(esbuild@0.28.0)(jiti@2.6.1)(yaml@2.8.3)
vitest:
specifier: ^4.1.9
version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.1.1)(vite@8.0.16(@types/node@25.5.2)(esbuild@0.28.0)(jiti@2.6.1)(yaml@2.8.3))
apps/mnswpr:
devDependencies: devDependencies:
'@cozy-games/leaderboard': '@cozy-games/leaderboard':
specifier: ^0.1.1 specifier: ^0.1.1
@ -62,15 +17,57 @@ importers:
'@cozy-games/utils': '@cozy-games/utils':
specifier: ^0.0.3 specifier: ^0.0.3
version: 0.0.3 version: 0.0.3
'@eslint/css':
specifier: ^1.1.0
version: 1.1.0
'@eslint/js':
specifier: ^10.0.1
version: 10.0.1(eslint@10.1.0(jiti@2.6.1))
'@secretlint/secretlint-rule-preset-recommend':
specifier: ^13.0.2
version: 13.0.2
'@stylistic/eslint-plugin':
specifier: ^5.10.0
version: 5.10.0(eslint@10.1.0(jiti@2.6.1))
bumpp:
specifier: ^11.0.1
version: 11.0.1
eslint:
specifier: ^10.1.0
version: 10.1.0(jiti@2.6.1)
firebase: firebase:
specifier: ^12.11.0 specifier: ^12.11.0
version: 12.11.0 version: 12.11.0
firebase-tools: firebase-tools:
specifier: ^15.22.4 specifier: ^15.22.4
version: 15.22.4(@types/node@25.5.2)(supports-color@10.2.2) version: 15.22.4(@types/node@25.5.2)
globals:
specifier: ^17.4.0
version: 17.4.0
husky:
specifier: ^9.1.7
version: 9.1.7
jsdom:
specifier: ^29.1.1
version: 29.1.1
netlify-cli: netlify-cli:
specifier: ^26.1.0 specifier: ^26.1.0
version: 26.1.0(@types/node@25.5.2)(picomatch@4.0.4)(supports-color@10.2.2) version: 26.1.0(@types/node@25.5.2)(picomatch@4.0.5)
secretlint:
specifier: ^13.0.2
version: 13.0.2
simple-git:
specifier: ^3.33.0
version: 3.33.0
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^8.0.16
version: 8.0.16(@types/node@25.5.2)(esbuild@0.28.0)(jiti@2.6.1)(yaml@2.8.3)
vitest:
specifier: ^4.1.9
version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.1.1)(vite@8.0.16(@types/node@25.5.2)(esbuild@0.28.0)(jiti@2.6.1)(yaml@2.8.3))
web-component-base: web-component-base:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0 version: 5.0.0
@ -1607,39 +1604,24 @@ packages:
'@protobufjs/base64@1.1.2': '@protobufjs/base64@1.1.2':
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
'@protobufjs/codegen@2.0.4':
resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
'@protobufjs/codegen@2.0.5': '@protobufjs/codegen@2.0.5':
resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==}
'@protobufjs/eventemitter@1.1.0':
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
'@protobufjs/eventemitter@1.1.1': '@protobufjs/eventemitter@1.1.1':
resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==}
'@protobufjs/fetch@1.1.0':
resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
'@protobufjs/fetch@1.1.1': '@protobufjs/fetch@1.1.1':
resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==}
'@protobufjs/float@1.0.2': '@protobufjs/float@1.0.2':
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
'@protobufjs/inquire@1.1.0':
resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
'@protobufjs/path@1.1.2': '@protobufjs/path@1.1.2':
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
'@protobufjs/pool@1.1.0': '@protobufjs/pool@1.1.0':
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
'@protobufjs/utf8@1.1.0':
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
'@protobufjs/utf8@1.1.1': '@protobufjs/utf8@1.1.1':
resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==}
@ -5273,10 +5255,6 @@ packages:
resolution: {integrity: sha512-E1sbAYg3aEbXrq0n1ojJkRHQJGE1kaE/O6GLA94y8rnJBfgvOPTOd1b9hOceQK1FFZI9qMh1vBERCyO2ifubcw==} resolution: {integrity: sha512-E1sbAYg3aEbXrq0n1ojJkRHQJGE1kaE/O6GLA94y8rnJBfgvOPTOd1b9hOceQK1FFZI9qMh1vBERCyO2ifubcw==}
engines: {node: '>=18'} engines: {node: '>=18'}
protobufjs@7.5.4:
resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
engines: {node: '>=12.0.0'}
protobufjs@7.6.4: protobufjs@7.6.4:
resolution: {integrity: sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==} resolution: {integrity: sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
@ -6910,14 +6888,14 @@ snapshots:
'@esbuild/win32-x64@0.28.0': '@esbuild/win32-x64@0.28.0':
optional: true optional: true
'@eslint-community/eslint-utils@4.9.1(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2))': '@eslint-community/eslint-utils@4.9.1(eslint@10.1.0(jiti@2.6.1))':
dependencies: dependencies:
eslint: 10.1.0(jiti@2.6.1)(supports-color@10.2.2) eslint: 10.1.0(jiti@2.6.1)
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {} '@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.23.3(supports-color@10.2.2)': '@eslint/config-array@0.23.3':
dependencies: dependencies:
'@eslint/object-schema': 3.0.3 '@eslint/object-schema': 3.0.3
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
@ -6944,9 +6922,9 @@ snapshots:
'@eslint/css-tree': 3.6.9 '@eslint/css-tree': 3.6.9
'@eslint/plugin-kit': 0.6.1 '@eslint/plugin-kit': 0.6.1
'@eslint/js@10.0.1(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2))': '@eslint/js@10.0.1(eslint@10.1.0(jiti@2.6.1))':
optionalDependencies: optionalDependencies:
eslint: 10.1.0(jiti@2.6.1)(supports-color@10.2.2) eslint: 10.1.0(jiti@2.6.1)
'@eslint/object-schema@3.0.3': {} '@eslint/object-schema@3.0.3': {}
@ -7380,7 +7358,7 @@ snapshots:
dependencies: dependencies:
lodash.camelcase: 4.3.0 lodash.camelcase: 4.3.0
long: 5.3.2 long: 5.3.2
protobufjs: 7.5.4 protobufjs: 7.6.4
yargs: 17.7.2 yargs: 17.7.2
'@grpc/proto-loader@0.8.1': '@grpc/proto-loader@0.8.1':
@ -7658,7 +7636,7 @@ snapshots:
'@jsdevtools/ono@7.1.3': {} '@jsdevtools/ono@7.1.3': {}
'@kwsites/file-exists@1.1.1(supports-color@10.2.2)': '@kwsites/file-exists@1.1.1':
dependencies: dependencies:
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies: transitivePeerDependencies:
@ -7743,7 +7721,7 @@ snapshots:
yaml: 2.8.3 yaml: 2.8.3
yargs: 17.7.2 yargs: 17.7.2
'@netlify/build@35.15.0(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(picomatch@4.0.4)': '@netlify/build@35.15.0(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(picomatch@4.0.5)':
dependencies: dependencies:
'@bugsnag/js': 8.9.0 '@bugsnag/js': 8.9.0
'@netlify/blobs': 10.7.9(supports-color@10.2.2) '@netlify/blobs': 10.7.9(supports-color@10.2.2)
@ -7762,7 +7740,7 @@ snapshots:
ansis: 4.3.1 ansis: 4.3.1
clean-stack: 5.3.0 clean-stack: 5.3.0
execa: 8.0.1 execa: 8.0.1
fdir: 6.5.0(picomatch@4.0.4) fdir: 6.5.0(picomatch@4.0.5)
figures: 6.1.0 figures: 6.1.0
filter-obj: 6.1.0 filter-obj: 6.1.0
hot-shots: 11.4.0 hot-shots: 11.4.0
@ -7872,7 +7850,7 @@ snapshots:
semver: 7.7.4 semver: 7.7.4
tmp-promise: 3.0.3 tmp-promise: 3.0.3
'@netlify/dev@4.18.9(supports-color@10.2.2)': '@netlify/dev@4.18.9':
dependencies: dependencies:
'@netlify/ai': 0.4.2 '@netlify/ai': 0.4.2
'@netlify/blobs': 10.7.9(supports-color@10.2.2) '@netlify/blobs': 10.7.9(supports-color@10.2.2)
@ -7880,9 +7858,9 @@ snapshots:
'@netlify/database-dev': 0.10.1 '@netlify/database-dev': 0.10.1
'@netlify/dev-utils': 4.4.6 '@netlify/dev-utils': 4.4.6
'@netlify/edge-functions-dev': 1.0.22 '@netlify/edge-functions-dev': 1.0.22
'@netlify/functions-dev': 1.3.1(supports-color@10.2.2) '@netlify/functions-dev': 1.3.1
'@netlify/headers': 2.1.11 '@netlify/headers': 2.1.11
'@netlify/images': 1.3.10(@netlify/blobs@10.7.9(supports-color@10.2.2)) '@netlify/images': 1.3.10(@netlify/blobs@10.7.9)
'@netlify/redirects': 3.1.13 '@netlify/redirects': 3.1.13
'@netlify/runtime': 4.1.25 '@netlify/runtime': 4.1.25
'@netlify/static': 3.1.10 '@netlify/static': 3.1.10
@ -7955,7 +7933,7 @@ snapshots:
dependencies: dependencies:
'@netlify/types': 2.8.0 '@netlify/types': 2.8.0
'@netlify/functions-dev@1.3.1(supports-color@10.2.2)': '@netlify/functions-dev@1.3.1':
dependencies: dependencies:
'@netlify/blobs': 10.7.9(supports-color@10.2.2) '@netlify/blobs': 10.7.9(supports-color@10.2.2)
'@netlify/dev-utils': 4.4.6 '@netlify/dev-utils': 4.4.6
@ -7963,7 +7941,7 @@ snapshots:
'@netlify/zip-it-and-ship-it': 14.7.1(supports-color@10.2.2) '@netlify/zip-it-and-ship-it': 14.7.1(supports-color@10.2.2)
cron-parser: 4.9.0 cron-parser: 4.9.0
decache: 4.6.2 decache: 4.6.2
extract-zip: 2.0.1(supports-color@10.2.2) extract-zip: 2.0.1
is-stream: 4.0.1 is-stream: 4.0.1
jwt-decode: 4.0.0 jwt-decode: 4.0.0
lambda-local: 2.2.0 lambda-local: 2.2.0
@ -8015,9 +7993,9 @@ snapshots:
dependencies: dependencies:
'@netlify/headers-parser': 9.0.3 '@netlify/headers-parser': 9.0.3
'@netlify/images@1.3.10(@netlify/blobs@10.7.9(supports-color@10.2.2))': '@netlify/images@1.3.10(@netlify/blobs@10.7.9)':
dependencies: dependencies:
ipx: 3.1.1(@netlify/blobs@10.7.9(supports-color@10.2.2)) ipx: 3.1.1(@netlify/blobs@10.7.9)
transitivePeerDependencies: transitivePeerDependencies:
- '@azure/app-configuration' - '@azure/app-configuration'
- '@azure/cosmos' - '@azure/cosmos'
@ -8407,7 +8385,7 @@ snapshots:
'@pnpm/network.ca-file': 1.0.2 '@pnpm/network.ca-file': 1.0.2
config-chain: 1.1.13 config-chain: 1.1.13
'@pnpm/tabtab@0.5.4(supports-color@10.2.2)': '@pnpm/tabtab@0.5.4':
dependencies: dependencies:
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
enquirer: 2.4.1 enquirer: 2.4.1
@ -8420,33 +8398,20 @@ snapshots:
'@protobufjs/base64@1.1.2': {} '@protobufjs/base64@1.1.2': {}
'@protobufjs/codegen@2.0.4': {}
'@protobufjs/codegen@2.0.5': {} '@protobufjs/codegen@2.0.5': {}
'@protobufjs/eventemitter@1.1.0': {}
'@protobufjs/eventemitter@1.1.1': {} '@protobufjs/eventemitter@1.1.1': {}
'@protobufjs/fetch@1.1.0':
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/fetch@1.1.1': '@protobufjs/fetch@1.1.1':
dependencies: dependencies:
'@protobufjs/aspromise': 1.1.2 '@protobufjs/aspromise': 1.1.2
'@protobufjs/float@1.0.2': {} '@protobufjs/float@1.0.2': {}
'@protobufjs/inquire@1.1.0': {}
'@protobufjs/path@1.1.2': {} '@protobufjs/path@1.1.2': {}
'@protobufjs/pool@1.1.0': {} '@protobufjs/pool@1.1.0': {}
'@protobufjs/utf8@1.1.0': {}
'@protobufjs/utf8@1.1.1': {} '@protobufjs/utf8@1.1.1': {}
'@quansync/fs@1.0.0': '@quansync/fs@1.0.0':
@ -8516,18 +8481,18 @@ snapshots:
dependencies: dependencies:
'@secretlint/types': 13.0.2 '@secretlint/types': 13.0.2
'@secretlint/config-loader@13.0.2(supports-color@10.2.2)': '@secretlint/config-loader@13.0.2':
dependencies: dependencies:
'@secretlint/profiler': 13.0.2 '@secretlint/profiler': 13.0.2
'@secretlint/resolver': 13.0.2 '@secretlint/resolver': 13.0.2
'@secretlint/types': 13.0.2 '@secretlint/types': 13.0.2
ajv: 8.20.0 ajv: 8.20.0
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
rc-config-loader: 4.1.4(supports-color@10.2.2) rc-config-loader: 4.1.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@secretlint/core@13.0.2(supports-color@10.2.2)': '@secretlint/core@13.0.2':
dependencies: dependencies:
'@secretlint/profiler': 13.0.2 '@secretlint/profiler': 13.0.2
'@secretlint/types': 13.0.2 '@secretlint/types': 13.0.2
@ -8552,10 +8517,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@secretlint/node@13.0.2(supports-color@10.2.2)': '@secretlint/node@13.0.2':
dependencies: dependencies:
'@secretlint/config-loader': 13.0.2(supports-color@10.2.2) '@secretlint/config-loader': 13.0.2
'@secretlint/core': 13.0.2(supports-color@10.2.2) '@secretlint/core': 13.0.2
'@secretlint/formatter': 13.0.2 '@secretlint/formatter': 13.0.2
'@secretlint/profiler': 13.0.2 '@secretlint/profiler': 13.0.2
'@secretlint/source-creator': 13.0.2 '@secretlint/source-creator': 13.0.2
@ -8603,11 +8568,11 @@ snapshots:
'@standard-schema/spec@1.1.0': {} '@standard-schema/spec@1.1.0': {}
'@stylistic/eslint-plugin@5.10.0(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2))': '@stylistic/eslint-plugin@5.10.0(eslint@10.1.0(jiti@2.6.1))':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2)) '@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
'@typescript-eslint/types': 8.58.0 '@typescript-eslint/types': 8.58.0
eslint: 10.1.0(jiti@2.6.1)(supports-color@10.2.2) eslint: 10.1.0(jiti@2.6.1)
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
espree: 10.4.0 espree: 10.4.0
estraverse: 5.3.0 estraverse: 5.3.0
@ -10031,11 +9996,11 @@ snapshots:
eslint-visitor-keys@5.0.1: {} eslint-visitor-keys@5.0.1: {}
eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2): eslint@10.1.0(jiti@2.6.1):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1)(supports-color@10.2.2)) '@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.3(supports-color@10.2.2) '@eslint/config-array': 0.23.3
'@eslint/config-helpers': 0.5.3 '@eslint/config-helpers': 0.5.3
'@eslint/core': 1.1.1 '@eslint/core': 1.1.1
'@eslint/plugin-kit': 0.6.1 '@eslint/plugin-kit': 0.6.1
@ -10258,7 +10223,7 @@ snapshots:
extend@3.0.2: {} extend@3.0.2: {}
extract-zip@2.0.1(supports-color@10.2.2): extract-zip@2.0.1:
dependencies: dependencies:
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
get-stream: 5.2.0 get-stream: 5.2.0
@ -10435,7 +10400,7 @@ snapshots:
locate-path: 8.0.0 locate-path: 8.0.0
unicorn-magic: 0.3.0 unicorn-magic: 0.3.0
firebase-tools@15.22.4(@types/node@25.5.2)(supports-color@10.2.2): firebase-tools@15.22.4(@types/node@25.5.2):
dependencies: dependencies:
'@apphosting/common': 0.0.8 '@apphosting/common': 0.0.8
'@electric-sql/pglite': 0.3.16 '@electric-sql/pglite': 0.3.16
@ -10466,7 +10431,7 @@ snapshots:
form-data: 4.0.6 form-data: 4.0.6
fs-extra: 10.1.0 fs-extra: 10.1.0
fuzzy: 0.1.3 fuzzy: 0.1.3
gaxios: 6.7.1(supports-color@10.2.2) gaxios: 6.7.1
glob: 10.5.0 glob: 10.5.0
google-auth-library: 9.15.1 google-auth-library: 9.15.1
ignore: 7.0.5 ignore: 7.0.5
@ -10486,9 +10451,9 @@ snapshots:
pg: 8.22.0 pg: 8.22.0
pg-gateway: 0.3.0-beta.4 pg-gateway: 0.3.0-beta.4
pglite-2: '@electric-sql/pglite@0.2.17' pglite-2: '@electric-sql/pglite@0.2.17'
portfinder: 1.0.38(supports-color@10.2.2) portfinder: 1.0.38
progress: 2.0.3 progress: 2.0.3
proxy-agent: 6.5.0(supports-color@10.2.2) proxy-agent: 6.5.0
retry: 0.13.1 retry: 0.13.1
semver: 7.7.4 semver: 7.7.4
sql-formatter: 15.8.2 sql-formatter: 15.8.2
@ -10499,7 +10464,7 @@ snapshots:
tcp-port-used: 1.0.3 tcp-port-used: 1.0.3
tmp: 0.2.7 tmp: 0.2.7
triple-beam: 1.4.1 triple-beam: 1.4.1
universal-analytics: 0.5.4(supports-color@10.2.2) universal-analytics: 0.5.4
update-notifier-cjs: 5.1.7 update-notifier-cjs: 5.1.7
winston: 3.19.0 winston: 3.19.0
winston-transport: 4.9.0 winston-transport: 4.9.0
@ -10564,7 +10529,7 @@ snapshots:
dependencies: dependencies:
from2: 2.3.0 from2: 2.3.0
follow-redirects@1.16.0(debug@4.4.3(supports-color@10.2.2)): follow-redirects@1.16.0(debug@4.4.3):
optionalDependencies: optionalDependencies:
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
@ -10627,7 +10592,7 @@ snapshots:
fuzzy@0.1.3: {} fuzzy@0.1.3: {}
gaxios@6.7.1(supports-color@10.2.2): gaxios@6.7.1:
dependencies: dependencies:
extend: 3.0.2 extend: 3.0.2
https-proxy-agent: 7.0.6(supports-color@10.2.2) https-proxy-agent: 7.0.6(supports-color@10.2.2)
@ -10657,7 +10622,7 @@ snapshots:
gcp-metadata@6.1.1: gcp-metadata@6.1.1:
dependencies: dependencies:
gaxios: 6.7.1(supports-color@10.2.2) gaxios: 6.7.1
google-logging-utils: 0.0.2 google-logging-utils: 0.0.2
json-bigint: 1.0.0 json-bigint: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
@ -10667,7 +10632,7 @@ snapshots:
gcp-metadata@8.1.2: gcp-metadata@8.1.2:
dependencies: dependencies:
gaxios: 7.1.5 gaxios: 7.1.5
google-logging-utils: 1.1.3 google-logging-utils: 1.1.4
json-bigint: 1.0.0 json-bigint: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -10734,7 +10699,7 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
get-intrinsic: 1.3.0 get-intrinsic: 1.3.0
get-uri@6.0.5(supports-color@10.2.2): get-uri@6.0.5:
dependencies: dependencies:
basic-ftp: 5.3.1 basic-ftp: 5.3.1
data-uri-to-buffer: 6.0.2 data-uri-to-buffer: 6.0.2
@ -10834,7 +10799,7 @@ snapshots:
dependencies: dependencies:
base64-js: 1.5.1 base64-js: 1.5.1
ecdsa-sig-formatter: 1.0.11 ecdsa-sig-formatter: 1.0.11
gaxios: 6.7.1(supports-color@10.2.2) gaxios: 6.7.1
gcp-metadata: 6.1.1 gcp-metadata: 6.1.1
gtoken: 7.1.0 gtoken: 7.1.0
jws: 4.0.1 jws: 4.0.1
@ -10852,7 +10817,7 @@ snapshots:
node-fetch: 3.3.2 node-fetch: 3.3.2
object-hash: 3.0.0 object-hash: 3.0.0
proto3-json-serializer: 3.0.4 proto3-json-serializer: 3.0.4
protobufjs: 7.5.4 protobufjs: 7.6.4
retry-request: 8.0.3 retry-request: 8.0.3
rimraf: 5.0.10 rimraf: 5.0.10
transitivePeerDependencies: transitivePeerDependencies:
@ -10883,7 +10848,7 @@ snapshots:
gtoken@7.1.0: gtoken@7.1.0:
dependencies: dependencies:
gaxios: 6.7.1(supports-color@10.2.2) gaxios: 6.7.1
jws: 4.0.1 jws: 4.0.1
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
@ -10900,7 +10865,7 @@ snapshots:
dependencies: dependencies:
cookie-es: 1.2.3 cookie-es: 1.2.3
crossws: 0.3.5 crossws: 0.3.5
defu: 6.1.6 defu: 6.1.7
destr: 2.0.5 destr: 2.0.5
iron-webcrypto: 1.2.1 iron-webcrypto: 1.2.1
node-mock-http: 1.0.4 node-mock-http: 1.0.4
@ -10987,21 +10952,21 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
http-proxy-middleware@3.0.7(supports-color@10.2.2): http-proxy-middleware@3.0.7:
dependencies: dependencies:
'@types/http-proxy': 1.17.17 '@types/http-proxy': 1.17.17
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
http-proxy: 1.18.1(debug@4.4.3(supports-color@10.2.2)) http-proxy: 1.18.1(debug@4.4.3)
is-glob: 4.0.3 is-glob: 4.0.3
is-plain-object: 5.0.0 is-plain-object: 5.0.0
micromatch: 4.0.8 micromatch: 4.0.8
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
http-proxy@1.18.1(debug@4.4.3(supports-color@10.2.2)): http-proxy@1.18.1(debug@4.4.3):
dependencies: dependencies:
eventemitter3: 4.0.7 eventemitter3: 4.0.7
follow-redirects: 1.16.0(debug@4.4.3(supports-color@10.2.2)) follow-redirects: 1.16.0(debug@4.4.3)
requires-port: 1.0.0 requires-port: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
@ -11015,7 +10980,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
https-proxy-agent@8.0.0(supports-color@10.2.2): https-proxy-agent@8.0.0:
dependencies: dependencies:
agent-base: 8.0.0 agent-base: 8.0.0
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
@ -11117,12 +11082,12 @@ snapshots:
ipaddr.js@2.4.0: {} ipaddr.js@2.4.0: {}
ipx@3.1.1(@netlify/blobs@10.7.9(supports-color@10.2.2)): ipx@3.1.1(@netlify/blobs@10.7.9):
dependencies: dependencies:
'@fastify/accept-negotiator': 2.0.1 '@fastify/accept-negotiator': 2.0.1
citty: 0.1.6 citty: 0.1.6
consola: 3.4.2 consola: 3.4.2
defu: 6.1.6 defu: 6.1.7
destr: 2.0.5 destr: 2.0.5
etag: 1.8.1 etag: 1.8.1
h3: 1.15.11 h3: 1.15.11
@ -11133,7 +11098,7 @@ snapshots:
sharp: 0.34.5 sharp: 0.34.5
svgo: 4.0.1 svgo: 4.0.1
ufo: 1.6.4 ufo: 1.6.4
unstorage: 1.17.5(@netlify/blobs@10.7.9(supports-color@10.2.2)) unstorage: 1.17.5(@netlify/blobs@10.7.9)
xss: 1.0.15 xss: 1.0.15
transitivePeerDependencies: transitivePeerDependencies:
- '@azure/app-configuration' - '@azure/app-configuration'
@ -11920,28 +11885,28 @@ snapshots:
negotiator@1.0.0: {} negotiator@1.0.0: {}
netlify-cli@26.1.0(@types/node@25.5.2)(picomatch@4.0.4)(supports-color@10.2.2): netlify-cli@26.1.0(@types/node@25.5.2)(picomatch@4.0.5):
dependencies: dependencies:
'@fastify/static': 9.1.3 '@fastify/static': 9.1.3
'@netlify/ai': 0.4.2 '@netlify/ai': 0.4.2
'@netlify/api': 14.0.19 '@netlify/api': 14.0.19
'@netlify/blobs': 10.7.9(supports-color@10.2.2) '@netlify/blobs': 10.7.9(supports-color@10.2.2)
'@netlify/build': 35.15.0(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(picomatch@4.0.4) '@netlify/build': 35.15.0(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(picomatch@4.0.5)
'@netlify/build-info': 10.5.1 '@netlify/build-info': 10.5.1
'@netlify/config': 24.6.0 '@netlify/config': 24.6.0
'@netlify/dev': 4.18.9(supports-color@10.2.2) '@netlify/dev': 4.18.9
'@netlify/dev-utils': 4.4.6 '@netlify/dev-utils': 4.4.6
'@netlify/edge-bundler': 14.10.3 '@netlify/edge-bundler': 14.10.3
'@netlify/edge-functions': 3.0.8 '@netlify/edge-functions': 3.0.8
'@netlify/edge-functions-bootstrap': 2.17.1 '@netlify/edge-functions-bootstrap': 2.17.1
'@netlify/headers-parser': 9.0.3 '@netlify/headers-parser': 9.0.3
'@netlify/images': 1.3.10(@netlify/blobs@10.7.9(supports-color@10.2.2)) '@netlify/images': 1.3.10(@netlify/blobs@10.7.9)
'@netlify/local-functions-proxy': 2.0.3 '@netlify/local-functions-proxy': 2.0.3
'@netlify/redirect-parser': 15.0.4 '@netlify/redirect-parser': 15.0.4
'@netlify/zip-it-and-ship-it': 14.7.1(supports-color@10.2.2) '@netlify/zip-it-and-ship-it': 14.7.1(supports-color@10.2.2)
'@octokit/rest': 22.0.1 '@octokit/rest': 22.0.1
'@opentelemetry/api': 1.9.1 '@opentelemetry/api': 1.9.1
'@pnpm/tabtab': 0.5.4(supports-color@10.2.2) '@pnpm/tabtab': 0.5.4
ansi-escapes: 7.3.0 ansi-escapes: 7.3.0
ansi-to-html: 0.7.2 ansi-to-html: 0.7.2
ascii-table: 0.0.9 ascii-table: 0.0.9
@ -11974,9 +11939,9 @@ snapshots:
get-port: 5.1.1 get-port: 5.1.1
git-repo-info: 2.1.1 git-repo-info: 2.1.1
gitconfiglocal: 2.1.0 gitconfiglocal: 2.1.0
http-proxy: 1.18.1(debug@4.4.3(supports-color@10.2.2)) http-proxy: 1.18.1(debug@4.4.3)
http-proxy-middleware: 3.0.7(supports-color@10.2.2) http-proxy-middleware: 3.0.7
https-proxy-agent: 8.0.0(supports-color@10.2.2) https-proxy-agent: 8.0.0
inquirer: 8.2.7(@types/node@25.5.2) inquirer: 8.2.7(@types/node@25.5.2)
inquirer-autocomplete-prompt: 1.4.0(inquirer@8.2.7(@types/node@25.5.2)) inquirer-autocomplete-prompt: 1.4.0(inquirer@8.2.7(@types/node@25.5.2))
is-docker: 4.0.0 is-docker: 4.0.0
@ -12325,12 +12290,12 @@ snapshots:
p-wait-for@6.0.0: {} p-wait-for@6.0.0: {}
pac-proxy-agent@7.2.0(supports-color@10.2.2): pac-proxy-agent@7.2.0:
dependencies: dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0 '@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.4 agent-base: 7.1.4
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
get-uri: 6.0.5(supports-color@10.2.2) get-uri: 6.0.5
http-proxy-agent: 7.0.2 http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@10.2.2) https-proxy-agent: 7.0.6(supports-color@10.2.2)
pac-resolver: 7.0.1 pac-resolver: 7.0.1
@ -12512,7 +12477,7 @@ snapshots:
pluralize@8.0.0: {} pluralize@8.0.0: {}
portfinder@1.0.38(supports-color@10.2.2): portfinder@1.0.38:
dependencies: dependencies:
async: 3.2.6 async: 3.2.6
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
@ -12598,22 +12563,7 @@ snapshots:
proto3-json-serializer@3.0.4: proto3-json-serializer@3.0.4:
dependencies: dependencies:
protobufjs: 7.5.4 protobufjs: 7.6.4
protobufjs@7.5.4:
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/base64': 1.1.2
'@protobufjs/codegen': 2.0.4
'@protobufjs/eventemitter': 1.1.0
'@protobufjs/fetch': 1.1.0
'@protobufjs/float': 1.0.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
'@types/node': 25.5.2
long: 5.3.2
protobufjs@7.6.4: protobufjs@7.6.4:
dependencies: dependencies:
@ -12634,14 +12584,14 @@ snapshots:
forwarded: 0.2.0 forwarded: 0.2.0
ipaddr.js: 1.9.1 ipaddr.js: 1.9.1
proxy-agent@6.5.0(supports-color@10.2.2): proxy-agent@6.5.0:
dependencies: dependencies:
agent-base: 7.1.4 agent-base: 7.1.4
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
http-proxy-agent: 7.0.2 http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@10.2.2) https-proxy-agent: 7.0.6(supports-color@10.2.2)
lru-cache: 7.18.3 lru-cache: 7.18.3
pac-proxy-agent: 7.2.0(supports-color@10.2.2) pac-proxy-agent: 7.2.0
proxy-from-env: 1.1.0 proxy-from-env: 1.1.0
socks-proxy-agent: 8.0.5 socks-proxy-agent: 8.0.5
transitivePeerDependencies: transitivePeerDependencies:
@ -12713,7 +12663,7 @@ snapshots:
iconv-lite: 0.7.2 iconv-lite: 0.7.2
unpipe: 1.0.0 unpipe: 1.0.0
rc-config-loader@4.1.4(supports-color@10.2.2): rc-config-loader@4.1.4:
dependencies: dependencies:
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
js-yaml: 4.3.0 js-yaml: 4.3.0
@ -12981,11 +12931,11 @@ snapshots:
dependencies: dependencies:
xmlchars: 2.2.0 xmlchars: 2.2.0
secretlint@13.0.2(supports-color@10.2.2): secretlint@13.0.2:
dependencies: dependencies:
'@secretlint/config-creator': 13.0.2 '@secretlint/config-creator': 13.0.2
'@secretlint/formatter': 13.0.2 '@secretlint/formatter': 13.0.2
'@secretlint/node': 13.0.2(supports-color@10.2.2) '@secretlint/node': 13.0.2
'@secretlint/profiler': 13.0.2 '@secretlint/profiler': 13.0.2
'@secretlint/resolver': 13.0.2 '@secretlint/resolver': 13.0.2
'@secretlint/walker': 13.0.2 '@secretlint/walker': 13.0.2
@ -13157,9 +13107,9 @@ snapshots:
signal-exit@4.1.0: {} signal-exit@4.1.0: {}
simple-git@3.33.0(supports-color@10.2.2): simple-git@3.33.0:
dependencies: dependencies:
'@kwsites/file-exists': 1.1.1(supports-color@10.2.2) '@kwsites/file-exists': 1.1.1
'@kwsites/promise-deferred': 1.1.1 '@kwsites/promise-deferred': 1.1.1
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies: transitivePeerDependencies:
@ -13711,7 +13661,7 @@ snapshots:
dependencies: dependencies:
crypto-random-string: 2.0.0 crypto-random-string: 2.0.0
universal-analytics@0.5.4(supports-color@10.2.2): universal-analytics@0.5.4:
dependencies: dependencies:
debug: 4.4.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2)
uuid: 14.0.1 uuid: 14.0.1
@ -13734,7 +13684,7 @@ snapshots:
unpipe@1.0.0: {} unpipe@1.0.0: {}
unstorage@1.17.5(@netlify/blobs@10.7.9(supports-color@10.2.2)): unstorage@1.17.5(@netlify/blobs@10.7.9):
dependencies: dependencies:
anymatch: 3.1.3 anymatch: 3.1.3
chokidar: 5.0.0 chokidar: 5.0.0

View file

@ -1,5 +1,5 @@
packages: # Not a workspace — this repo is a single package at the root. pnpm 11 reads its
- "apps/*" # settings from this file, so it stays for `allowBuilds` / `minimumReleaseAgeExclude`.
allowBuilds: allowBuilds:
'@firebase/util': false '@firebase/util': false
'@parcel/watcher': false '@parcel/watcher': false

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,6 +1,6 @@
/** /**
* Post-install convenience: make a Java runtime available for the Firebase * Post-install convenience: make a Java runtime available for the Firebase
* Firestore emulator (used by `pnpm -F mnswpr dev` / `db:start`). The emulator is * Firestore emulator (used by `pnpm run dev` / `db:start`). The emulator is
* a Java program and firebase-tools does not bundle a JRE. * a Java program and firebase-tools does not bundle a JRE.
* *
* Installs a Temurin JRE 21 into the user's home (~/.local) WITHOUT sudo and * Installs a Temurin JRE 21 into the user's home (~/.local) WITHOUT sudo and
@ -9,7 +9,7 @@
* - non-fatal: never fails `pnpm install` on any problem it warns, exits 0 * - non-fatal: never fails `pnpm install` on any problem it warns, exits 0
* - opt-out: set SKIP_JRE_SETUP=1 (also auto-skips when CI is set) * - opt-out: set SKIP_JRE_SETUP=1 (also auto-skips when CI is set)
* Only Linux/macOS on x64/arm64 are auto-handled; anything else prints manual * Only Linux/macOS on x64/arm64 are auto-handled; anything else prints manual
* instructions (see apps/mnswpr/README.md). * instructions (see README.md).
*/ */
import { spawnSync } from 'node:child_process' import { spawnSync } from 'node:child_process'
import { existsSync, mkdirSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' import { existsSync, mkdirSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
@ -24,7 +24,7 @@ try {
await main() await main()
} catch (err) { } catch (err) {
warn(`skipped (${err?.message || err}).`) warn(`skipped (${err?.message || err}).`)
warn('The Firestore emulator needs Java 11+ — install it manually, see apps/mnswpr/README.md.') warn('The Firestore emulator needs Java 11+ — install it manually, see README.md.')
} }
process.exit(0) process.exit(0)
@ -36,11 +36,11 @@ async function main() {
const adoptOs = { linux: 'linux', darwin: 'mac' }[platform()] const adoptOs = { linux: 'linux', darwin: 'mac' }[platform()]
const adoptArch = { x64: 'x64', arm64: 'aarch64' }[arch()] const adoptArch = { x64: 'x64', arm64: 'aarch64' }[arch()]
if (!adoptOs || !adoptArch) { if (!adoptOs || !adoptArch) {
warn(`no auto-install for ${platform()}/${arch()} — install a JRE 21 manually (apps/mnswpr/README.md).`) warn(`no auto-install for ${platform()}/${arch()} — install a JRE 21 manually (README.md).`)
return return
} }
if (!hasTool('tar')) { if (!hasTool('tar')) {
warn('`tar` not found — cannot unpack the JRE. Install Java manually (apps/mnswpr/README.md).') warn('`tar` not found — cannot unpack the JRE. Install Java manually (README.md).')
return return
} }

View file

@ -4,9 +4,8 @@
* a FULLY-RENDERED, static page no runtime JS, no Firebase at page load. * a FULLY-RENDERED, static page no runtime JS, no Firebase at page load.
* *
* The data never changes, so the page is a frozen artifact. Re-run only if you * The data never changes, so the page is a frozen artifact. Re-run only if you
* ever need to regenerate it. Because `firebase` is an app-workspace dependency, * ever need to regenerate it. Run it from the repo root:
* run it so Node can resolve it, e.g. from the app directory: * node scripts/export-legends.js
* (cd apps/mnswpr && node scripts/export-legends.js)
*/ */
import { writeFileSync } from 'node:fs' import { writeFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path' import { resolve, dirname } from 'node:path'

View file

@ -13,10 +13,9 @@
* firestore.rules to be deployed first (they permit writes to * firestore.rules to be deployed first (they permit writes to
* `mw-test-scores`), else every write returns permission-denied: * `mw-test-scores`), else every write returns permission-denied:
* npx firebase deploy --only firestore:rules --project dev * npx firebase deploy --only firestore:rules --project dev
* (cd apps/mnswpr && node scripts/seed-dev-scores.js) * node scripts/seed-dev-scores.js
* *
* `firebase` is an app-workspace dependency, so run it from the app directory so * Run from the repo root. Optional namespace override: LB_NAMESPACE=mw-test.
* Node can resolve it. Optional namespace override: LB_NAMESPACE=mw-test.
*/ */
import { initializeApp } from 'firebase/app' import { initializeApp } from 'firebase/app'
import { getFirestore, connectFirestoreEmulator, doc, collection, setDoc } from 'firebase/firestore/lite' import { getFirestore, connectFirestoreEmulator, doc, collection, setDoc } from 'firebase/firestore/lite'

View file

@ -3,6 +3,6 @@ import { defineConfig } from 'vitest/config'
export default defineConfig({ export default defineConfig({
test: { test: {
environment: 'jsdom', environment: 'jsdom',
include: ['apps/**/test/**/*.test.js', 'scripts/test/**/*.test.js'] include: ['test/**/*.test.js', 'scripts/test/**/*.test.js']
} }
}) })