word-waltz / commit
missing files
README.md | 30 +++++++++++----------- cspell.json | 44 ++++++++++++++++---------------- package.json | 2 +- scripts/cleanup-old-games.js | 58 +++++++++++++++++++++---------------------- src/components/Keyboard.ts | 2 +- src/components/Scoreboard.ts | 2 +- src/components/ShareDialog.ts | 2 +- src/components/WordPicker.ts | 2 +- src/components/WordleBoard.ts | 2 +- src/firebase/gameService.ts | 2 +- src/index.html | 13 ++++++---- src/styles/main.css | 2 +- src/types.ts | 32 ++++++++++++------------ tsconfig.json | 43 +++++++++++++++++--------------- 14 files changed, 121 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index d86988c..8bb6eaa 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ A two-player, asynchronous Wordle duel. You challenge your opponent with a secre ## Tech stack -| Concern | Tool | -| --- | --- | -| Build | [Vite](https://vitejs.dev/) (static output) | -| Hosting | [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) | +| Concern | Tool | +| ------------ | ---------------------------------------------------------------- | +| Build | [Vite](https://vitejs.dev/) (static output) | +| Hosting | [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) | | Game storage | [Firebase Firestore](https://firebase.google.com/docs/firestore) | -| Encryption | Web Crypto API (AES-GCM, 256-bit) | -| QR codes | [qrcode](https://www.npmjs.com/package/qrcode) | -| Language | TypeScript (strict mode, ES modules) | +| Encryption | Web Crypto API (AES-GCM, 256-bit) | +| QR codes | [qrcode](https://www.npmjs.com/package/qrcode) | +| Language | TypeScript (strict mode, ES modules) | ## Security model — URL-keyed encryption @@ -48,14 +48,14 @@ https://your-app.gitlab.io/game/#gameId=xxxx&key=base64encodedkey ## Scoring | Guesses taken | Points | -| --- | --- | -| 1 | 6 | -| 2 | 5 | -| 3 | 4 | -| 4 | 3 | -| 5 | 2 | -| 6 | 1 | -| Failed (> 6) | 0 | +| ------------- | ------ | +| 1 | 6 | +| 2 | 5 | +| 3 | 4 | +| 4 | 3 | +| 5 | 2 | +| 6 | 1 | +| Failed (> 6) | 0 | Scores accumulate across rounds. There is no set game length — keep playing as long as you like. diff --git a/cspell.json b/cspell.json index ed76067..705bdef 100644 --- a/cspell.json +++ b/cspell.json @@ -1,23 +1,23 @@ { - "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/packages/cspell-types/cspell.schema.json", - "version": "0.2", - "language": "en", - "words": [ - "ciphertext", - "Firestore", - "firestore", - "firebaseapp", - "firebasestorage", - "idempotent", - "initialisation", - "qrcode", - "serialisation", - "unencrypted", - "Wordle" - ], - "ignorePaths": [ - "node_modules", - "dist", - "package-lock.json" - ] -} + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/packages/cspell-types/cspell.schema.json", + "version": "0.2", + "language": "en", + "words": [ + "ciphertext", + "Firestore", + "firestore", + "firebaseapp", + "firebasestorage", + "idempotent", + "initialisation", + "qrcode", + "serialisation", + "unencrypted", + "Wordle" + ], + "ignorePaths": [ + "node_modules", + "dist", + "package-lock.json" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 94c6103..60398f2 100644 --- a/package.json +++ b/package.json @@ -22,4 +22,4 @@ "typescript": "^6.0.2", "vite": "^5.2.0" } -} +} \ No newline at end of file diff --git a/scripts/cleanup-old-games.js b/scripts/cleanup-old-games.js index 0e9714a..5dfe179 100644 --- a/scripts/cleanup-old-games.js +++ b/scripts/cleanup-old-games.js @@ -22,12 +22,12 @@ const RETENTION_DAYS = 30; const BATCH_SIZE = 500; // Firestore max batch-write size function requireEnv(name) { - const value = process.env[name]; - if (!value) { - console.error(`Error: required environment variable "${name}" is not set.`); - process.exit(1); - } - return value; + const value = process.env[name]; + if (!value) { + console.error(`Error: required environment variable "${name}" is not set.`); + process.exit(1); + } + return value; } const serviceAccount = JSON.parse(requireEnv('FIREBASE_SERVICE_ACCOUNT_JSON')); @@ -39,35 +39,35 @@ const db = getFirestore(); const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1000; async function deleteOldGames() { - console.log( - `Querying for games with lastPlayed < ${new Date(cutoffMs).toISOString()} ` + - `(older than ${RETENTION_DAYS} days)…` - ); + console.log( + `Querying for games with lastPlayed < ${new Date(cutoffMs).toISOString()} ` + + `(older than ${RETENTION_DAYS} days)…` + ); - const snapshot = await db - .collection('games') - .where('lastPlayed', '<', cutoffMs) - .get(); + const snapshot = await db + .collection('games') + .where('lastPlayed', '<', cutoffMs) + .get(); - if (snapshot.empty) { - console.log('No stale games found. Nothing to delete.'); - return; - } + if (snapshot.empty) { + console.log('No stale games found. Nothing to delete.'); + return; + } - console.log(`Found ${snapshot.size} stale game(s). Deleting in batches…`); + console.log(`Found ${snapshot.size} stale game(s). Deleting in batches…`); - const docs = snapshot.docs; - for (let i = 0; i < docs.length; i += BATCH_SIZE) { - const batch = db.batch(); - docs.slice(i, i + BATCH_SIZE).forEach((doc) => batch.delete(doc.ref)); - await batch.commit(); - console.log(` Deleted ${Math.min(i + BATCH_SIZE, docs.length)} / ${docs.length}`); - } + const docs = snapshot.docs; + for (let i = 0; i < docs.length; i += BATCH_SIZE) { + const batch = db.batch(); + docs.slice(i, i + BATCH_SIZE).forEach((doc) => batch.delete(doc.ref)); + await batch.commit(); + console.log(` Deleted ${Math.min(i + BATCH_SIZE, docs.length)} / ${docs.length}`); + } - console.log('Cleanup complete.'); + console.log('Cleanup complete.'); } deleteOldGames().catch((err) => { - console.error('Cleanup failed:', err); - process.exit(1); + console.error('Cleanup failed:', err); + process.exit(1); }); diff --git a/src/components/Keyboard.ts b/src/components/Keyboard.ts index 11f6b08..8bcfa70 100644 --- a/src/components/Keyboard.ts +++ b/src/components/Keyboard.ts @@ -16,5 +16,5 @@ interface KeyboardProps { // TODO: implement keyboard layout and coloring export function mountKeyboard(container: HTMLElement, props: KeyboardProps): () => void { // TODO - return function cleanup() {}; + return function cleanup() { }; } diff --git a/src/components/Scoreboard.ts b/src/components/Scoreboard.ts index d5428e1..45eadaa 100644 --- a/src/components/Scoreboard.ts +++ b/src/components/Scoreboard.ts @@ -14,5 +14,5 @@ interface ScoreboardProps { // TODO: implement scoreboard UI export function mountScoreboard(container: HTMLElement, props: ScoreboardProps): () => void { // TODO - return function cleanup() {}; + return function cleanup() { }; } diff --git a/src/components/ShareDialog.ts b/src/components/ShareDialog.ts index 93b5285..ed90541 100644 --- a/src/components/ShareDialog.ts +++ b/src/components/ShareDialog.ts @@ -12,5 +12,5 @@ interface ShareDialogProps { // TODO: implement share dialog with qrcode rendering export function mountShareDialog(container: HTMLElement, props: ShareDialogProps): () => void { // TODO: use the `qrcode` package to render a QR code canvas - return function cleanup() {}; + return function cleanup() { }; } diff --git a/src/components/WordPicker.ts b/src/components/WordPicker.ts index 54d2472..4752bd3 100644 --- a/src/components/WordPicker.ts +++ b/src/components/WordPicker.ts @@ -12,5 +12,5 @@ interface WordPickerProps { // TODO: implement word picker UI export function mountWordPicker(container: HTMLElement, props: WordPickerProps): () => void { // TODO - return function cleanup() {}; + return function cleanup() { }; } diff --git a/src/components/WordleBoard.ts b/src/components/WordleBoard.ts index 9a6b98e..2cd9e08 100644 --- a/src/components/WordleBoard.ts +++ b/src/components/WordleBoard.ts @@ -19,5 +19,5 @@ interface WordleBoardProps { // TODO: implement board rendering and guess logic export function mountWordleBoard(container: HTMLElement, props: WordleBoardProps): () => void { // TODO - return function cleanup() {}; + return function cleanup() { }; } diff --git a/src/firebase/gameService.ts b/src/firebase/gameService.ts index 5343a17..21c80de 100644 --- a/src/firebase/gameService.ts +++ b/src/firebase/gameService.ts @@ -42,5 +42,5 @@ export function subscribeToGame( onUpdate: (envelope: EncryptedEnvelope) => void, ): () => void { // TODO - return function unsubscribe() {}; + return function unsubscribe() { }; } diff --git a/src/index.html b/src/index.html index 7c6f7b3..7329f5c 100644 --- a/src/index.html +++ b/src/index.html @@ -1,13 +1,16 @@ <!DOCTYPE html> <html lang="en"> - <head> + +<head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Word Waltz</title> <link rel="stylesheet" href="./styles/main.css" /> - </head> - <body> +</head> + +<body> <div id="app"></div> <script type="module" src="./main.ts"></script> - </body> -</html> +</body> + +</html> \ No newline at end of file diff --git a/src/styles/main.css b/src/styles/main.css index 7afa655..5f912da 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -1,3 +1,3 @@ /* Word Waltz — global styles */ -/* TODO: design system, tile colors, keyboard layout, responsive layout */ +/* TODO: design system, tile colors, keyboard layout, responsive layout */ \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 4b20d37..37d3ff9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,32 +9,32 @@ export type PlayerId = 'a' | 'b'; export interface Player { - name: string; - score: number; + name: string; + score: number; } export interface Round { - challenger: PlayerId; - word: string; - guesses: string[]; - solved: boolean; - score: number; + challenger: PlayerId; + word: string; + guesses: string[]; + solved: boolean; + score: number; } export interface GameState { - players: Record<PlayerId, Player>; - rounds: Round[]; - currentRound: number; - turn: 'pick' | 'solve'; - createdAt: number; - updatedAt: number; + players: Record<PlayerId, Player>; + rounds: Round[]; + currentRound: number; + turn: 'pick' | 'solve'; + createdAt: number; + updatedAt: number; } /** The shape stored in Firestore (encrypted envelope + one plaintext field). */ export interface EncryptedEnvelope { - iv: string; - ciphertext: string; - lastPlayed: number; + iv: string; + ciphertext: string; + lastPlayed: number; } /** The encrypted part only — passed in by callers of createGame / updateGame. */ diff --git a/tsconfig.json b/tsconfig.json index ad79e54..0b70058 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,21 +1,24 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - /* Vite-recommended module resolution */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - - /* tsc is type-check only; Vite/esbuild handles emit */ - "noEmit": true, - - "strict": true - }, - "include": ["src"] -} + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + /* Vite-recommended module resolution */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + /* tsc is type-check only; Vite/esbuild handles emit */ + "noEmit": true, + "strict": true + }, + "include": [ + "src" + ] +} \ No newline at end of file