scrabble-solver 2.16.6 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bunx.yml +26 -2
- package/.github/workflows/npx.yml +26 -2
- package/.nx/workspace-data/d/server-process.json +3 -3
- package/.nx/workspace-data/file-map.json +441 -465
- package/.nx/workspace-data/lockfile-dependencies.hash +1 -1
- package/.nx/workspace-data/lockfile-nodes.hash +1 -1
- package/.nx/workspace-data/nx_files.nxt +0 -0
- package/.nx/workspace-data/parsed-lock-file.dependencies.json +2671 -2121
- package/.nx/workspace-data/parsed-lock-file.nodes.json +3846 -5277
- package/.nx/workspace-data/project-graph.json +7089 -7924
- package/.nx/workspace-data/source-maps.json +351 -255
- package/.oxlintrc.json +6 -0
- package/CLAUDE.md +25 -20
- package/README.md +9 -0
- package/bun.lock +1054 -2897
- package/bunfig.toml +6 -0
- package/cypress/e2e/app.spec.cy.ts +4 -4
- package/cypress/e2e/features/solver-paths.ts +66 -0
- package/cypress/e2e/features.spec.cy.ts +1 -0
- package/cypress/support/lib/actions.ts +28 -9
- package/cypress.config.ts +7 -0
- package/lerna.json +1 -1
- package/nx.json +3 -1
- package/package.json +18 -18
- package/packages/configs/package.json +4 -4
- package/packages/constants/package.json +2 -2
- package/packages/dictionaries/README.md +1 -1
- package/packages/dictionaries/build/Dictionaries.d.ts +2 -2
- package/packages/dictionaries/build/Dictionaries.js +6 -6
- package/packages/dictionaries/build/lib/DiskCache.d.ts +11 -4
- package/packages/dictionaries/build/lib/DiskCache.js +27 -10
- package/packages/dictionaries/build/lib/LayeredCache.d.ts +4 -4
- package/packages/dictionaries/build/lib/LayeredCache.js +3 -3
- package/packages/dictionaries/build/lib/MemoryCache.d.ts +4 -4
- package/packages/dictionaries/build/lib/MemoryCache.js +2 -2
- package/packages/dictionaries/build/lib/createCacheTimestampComparator.d.ts +2 -2
- package/packages/dictionaries/build/lib/downloadDictionary.d.ts +2 -2
- package/packages/dictionaries/build/lib/downloadDictionary.js +4 -3
- package/packages/dictionaries/build/lib/getDictionaryFilepath.d.ts +3 -1
- package/packages/dictionaries/build/lib/getDictionaryFilepath.js +8 -3
- package/packages/dictionaries/package.json +8 -7
- package/packages/dictionaries/src/Dictionaries.ts +12 -12
- package/packages/dictionaries/src/lib/DiskCache.test.ts +85 -0
- package/packages/dictionaries/src/lib/DiskCache.ts +34 -14
- package/packages/dictionaries/src/lib/LayeredCache.ts +7 -7
- package/packages/dictionaries/src/lib/MemoryCache.ts +6 -6
- package/packages/dictionaries/src/lib/createCacheTimestampComparator.ts +2 -2
- package/packages/dictionaries/src/lib/downloadDictionary.ts +5 -4
- package/packages/dictionaries/src/lib/getDictionaryFilepath.ts +7 -2
- package/packages/gaddag/build/Gaddag.d.ts +39 -0
- package/packages/gaddag/build/Gaddag.js +128 -0
- package/packages/gaddag/build/buildGaddag.d.ts +10 -0
- package/packages/gaddag/build/buildGaddag.js +360 -0
- package/packages/gaddag/build/constants.d.ts +12 -0
- package/packages/gaddag/build/constants.js +15 -0
- package/packages/gaddag/build/index.d.ts +3 -0
- package/packages/gaddag/build/index.js +13 -0
- package/packages/logger/package.json +2 -2
- package/packages/scrabble-solver/.next/BUILD_ID +1 -1
- package/packages/scrabble-solver/.next/build-manifest.json +17 -17
- package/packages/scrabble-solver/.next/cache/.previewinfo +1 -1
- package/packages/scrabble-solver/.next/cache/.rscinfo +1 -1
- package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
- package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/packages/scrabble-solver/.next/diagnostics/framework.json +1 -1
- package/packages/scrabble-solver/.next/next-minimal-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/prerender-manifest.json +7 -4
- package/packages/scrabble-solver/.next/required-server-files.js +29 -10
- package/packages/scrabble-solver/.next/required-server-files.json +29 -10
- package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/chunks/129.js +1 -0
- package/packages/scrabble-solver/.next/server/chunks/30.js +2 -2
- package/packages/scrabble-solver/.next/server/chunks/318.js +3 -3
- package/packages/scrabble-solver/.next/server/chunks/392.js +1 -1
- package/packages/scrabble-solver/.next/server/chunks/671.js +1 -1
- package/packages/scrabble-solver/.next/server/chunks/712.js +1 -1
- package/packages/scrabble-solver/.next/server/chunks/785.js +1 -0
- package/packages/scrabble-solver/.next/server/chunks/791.js +3 -3
- package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/404.html +1 -1
- package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
- package/packages/scrabble-solver/.next/server/pages/_app.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_error.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/solve.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/verify.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/visit.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/visit.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/not-found.html +1 -1
- package/packages/scrabble-solver/.next/server/pages/not-found.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/webpack-runtime.js +1 -1
- package/packages/scrabble-solver/.next/static/{js6-w0PwySs2PyOSeCYfi → OLE8_ZInlGH7ENU0NIOpf}/_buildManifest.js +1 -1
- package/packages/scrabble-solver/.next/static/chunks/583.7a46807be14c377e.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/830.661816bf0de8c2f1.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/framework-3cf15b8041c59dd3.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/main-5b999f3f24ebfa84.js +5 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-7024e7a27ffbb2fa.js +7 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/{index-bdf5e1e44cea16f6.js → index-8b398b0a5d870773.js} +1 -1
- package/packages/scrabble-solver/.next/static/chunks/webpack-3943d624bbb47a64.js +1 -0
- package/packages/scrabble-solver/.next/static/css/aedeaada218d7280.css +2 -0
- package/packages/scrabble-solver/.next/trace +21 -21
- package/packages/scrabble-solver/.next/trace-build +1 -1
- package/packages/scrabble-solver/.next/types/cache-life.d.ts +1 -0
- package/packages/scrabble-solver/.next/types/root-params.d.ts +3 -0
- package/packages/scrabble-solver/next-env.d.ts +1 -0
- package/packages/scrabble-solver/next.config.js +0 -1
- package/packages/scrabble-solver/package.json +19 -19
- package/packages/scrabble-solver/public/service-worker.js +2 -2
- package/packages/scrabble-solver/public/service-worker.js.map +1 -1
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +4 -1
- package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +17 -6
- package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +7 -22
- package/packages/scrabble-solver/src/components/Dictionary/Dictionary.tsx +1 -8
- package/packages/scrabble-solver/src/components/Results/Result.tsx +0 -3
- package/packages/scrabble-solver/src/components/Results/Results.module.scss +0 -1
- package/packages/scrabble-solver/src/components/Results/Results.tsx +10 -2
- package/packages/scrabble-solver/src/components/Results/types.ts +3 -3
- package/packages/scrabble-solver/src/components/Solver/Solver.tsx +4 -3
- package/packages/scrabble-solver/src/components/Tooltip/useTooltip.ts +1 -1
- package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/[word].ts +2 -2
- package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/index.ts +80 -3
- package/packages/scrabble-solver/src/pages/api/solve.ts +2 -2
- package/packages/scrabble-solver/src/pages/api/verify.ts +12 -3
- package/packages/scrabble-solver/src/sdk/solve.ts +13 -7
- package/packages/scrabble-solver/src/sdk/verify.ts +12 -6
- package/packages/scrabble-solver/src/service-worker/appShell.test.ts +86 -0
- package/packages/scrabble-solver/src/service-worker/appShell.ts +39 -0
- package/packages/scrabble-solver/src/service-worker/index.ts +4 -5
- package/packages/scrabble-solver/src/serviceWorkerManager.ts +1 -18
- package/packages/scrabble-solver/src/solver-worker/client.test.ts +151 -0
- package/packages/scrabble-solver/src/solver-worker/client.ts +106 -0
- package/packages/scrabble-solver/src/solver-worker/dictionaries/constants.ts +9 -0
- package/packages/scrabble-solver/src/solver-worker/dictionaries/getDictionary.ts +21 -0
- package/packages/scrabble-solver/src/solver-worker/dictionaries/index.ts +2 -0
- package/packages/scrabble-solver/src/solver-worker/dictionaries/revalidateDictionary.test.ts +199 -0
- package/packages/scrabble-solver/src/solver-worker/dictionaries/revalidateDictionary.ts +86 -0
- package/packages/scrabble-solver/src/solver-worker/dictionaries/revalidatedAt.ts +43 -0
- package/packages/scrabble-solver/src/solver-worker/getGaddag.test.ts +109 -0
- package/packages/scrabble-solver/src/solver-worker/getGaddag.ts +62 -0
- package/packages/scrabble-solver/src/solver-worker/index.ts +1 -0
- package/packages/scrabble-solver/src/solver-worker/index.worker.ts +124 -0
- package/packages/scrabble-solver/src/solver-worker/messages.ts +20 -0
- package/packages/scrabble-solver/src/state/index.ts +1 -0
- package/packages/scrabble-solver/src/state/sagas.ts +19 -9
- package/packages/scrabble-solver/src/state/useTypedStore.ts +6 -0
- package/packages/scrabble-solver/src/types/api.ts +1 -2
- package/packages/scrabble-solver/tsconfig.json +4 -0
- package/packages/scrabble-solver/tsconfig.tsbuildinfo +1 -1
- package/packages/solver/README.md +24 -0
- package/packages/solver/benchmarks/index.ts +15 -0
- package/packages/solver/benchmarks/lib.ts +31 -0
- package/packages/solver/benchmarks/renderChart.ts +165 -0
- package/packages/solver/benchmarks/results/chart.svg +65 -0
- package/packages/solver/benchmarks/runBenchmarks.ts +66 -0
- package/packages/solver/benchmarks/scenarios.ts +64 -0
- package/packages/solver/benchmarks/types.ts +16 -0
- package/packages/solver/benchmarks/updateReadme.ts +53 -0
- package/packages/solver/build/MoveGenerator.d.ts +116 -0
- package/packages/solver/build/MoveGenerator.js +686 -0
- package/packages/solver/build/index.d.ts +0 -13
- package/packages/solver/build/index.js +1 -27
- package/packages/solver/build/solve.d.ts +2 -2
- package/packages/solver/build/solve.js +3 -19
- package/packages/solver/package.json +8 -7
- package/packages/solver/src/MoveGenerator.test.ts +1009 -0
- package/packages/solver/src/MoveGenerator.ts +926 -0
- package/packages/solver/src/ReferenceSolver.ts +401 -0
- package/packages/solver/src/index.ts +0 -13
- package/packages/solver/src/solve.test.ts +21 -21
- package/packages/solver/src/solve.ts +4 -22
- package/packages/solver/tsconfig.json +1 -1
- package/packages/types/build/Cell.d.ts +1 -0
- package/packages/types/build/Cell.js +3 -0
- package/packages/types/build/Result.d.ts +3 -2
- package/packages/types/build/Result.js +11 -6
- package/packages/types/build/ResultJson.d.ts +5 -4
- package/packages/types/build/index.d.ts +0 -6
- package/packages/types/build/index.js +1 -9
- package/packages/types/build/readResultCells.d.ts +5 -0
- package/packages/types/build/readResultCells.js +75 -0
- package/packages/types/package.json +5 -4
- package/packages/types/src/Cell.ts +4 -0
- package/packages/types/src/Result.ts +15 -6
- package/packages/types/src/ResultJson.ts +5 -5
- package/packages/types/src/index.ts +0 -6
- package/packages/types/src/readResultCells.test.ts +140 -0
- package/packages/types/src/readResultCells.ts +90 -0
- package/packages/word-definitions/package.json +3 -3
- package/packages/word-lists/package.json +4 -4
- package/packages/scrabble-solver/.next/static/chunks/framework-a23f7f643b59a2ca.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/main-92448bb08bb6bf1f.js +0 -5
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-4ae86f006f6b9fe7.js +0 -7
- package/packages/scrabble-solver/.next/static/chunks/webpack-1a7a166b34ed8d24.js +0 -1
- package/packages/scrabble-solver/.next/static/css/0af35adb4bab56f0.css +0 -2
- package/packages/scrabble-solver/src/sdk/getDictionary.ts +0 -9
- package/packages/scrabble-solver/src/service-worker/average.ts +0 -7
- package/packages/scrabble-solver/src/service-worker/dictionaries/constants.ts +0 -5
- package/packages/scrabble-solver/src/service-worker/dictionaries/getDictionary.ts +0 -20
- package/packages/scrabble-solver/src/service-worker/dictionaries/index.ts +0 -2
- package/packages/scrabble-solver/src/service-worker/dictionaries/revalidateDictionary.ts +0 -33
- package/packages/scrabble-solver/src/service-worker/getTrie.ts +0 -24
- package/packages/scrabble-solver/src/service-worker/routeSolveRequests.ts +0 -91
- package/packages/scrabble-solver/src/service-worker/routeVerifyRequests.ts +0 -37
- package/packages/solver/build/areDigraphsValid.d.ts +0 -2
- package/packages/solver/build/areDigraphsValid.js +0 -17
- package/packages/solver/build/fillPattern.d.ts +0 -6
- package/packages/solver/build/fillPattern.js +0 -52
- package/packages/solver/build/generateEndIndices.d.ts +0 -2
- package/packages/solver/build/generateEndIndices.js +0 -17
- package/packages/solver/build/generateHorizontalPatterns.d.ts +0 -2
- package/packages/solver/build/generateHorizontalPatterns.js +0 -16
- package/packages/solver/build/generatePattern.d.ts +0 -7
- package/packages/solver/build/generatePattern.js +0 -20
- package/packages/solver/build/generatePatterns.d.ts +0 -2
- package/packages/solver/build/generatePatterns.js +0 -11
- package/packages/solver/build/generateStartIndices.d.ts +0 -2
- package/packages/solver/build/generateStartIndices.js +0 -16
- package/packages/solver/build/generateVectors.d.ts +0 -7
- package/packages/solver/build/generateVectors.js +0 -7
- package/packages/solver/build/generateVerticalPatterns.d.ts +0 -2
- package/packages/solver/build/generateVerticalPatterns.js +0 -16
- package/packages/solver/build/getCellsScore.d.ts +0 -2
- package/packages/solver/build/getCellsScore.js +0 -18
- package/packages/solver/build/getPatternHash.d.ts +0 -2
- package/packages/solver/build/getPatternHash.js +0 -14
- package/packages/solver/build/getPatternScore.d.ts +0 -2
- package/packages/solver/build/getPatternScore.js +0 -22
- package/packages/solver/build/getUniquePatterns.d.ts +0 -2
- package/packages/solver/build/getUniquePatterns.js +0 -17
- package/packages/solver/src/areDigraphsValid.ts +0 -18
- package/packages/solver/src/fillPattern.test.ts +0 -107
- package/packages/solver/src/fillPattern.ts +0 -68
- package/packages/solver/src/generateEndIndices.test.ts +0 -23
- package/packages/solver/src/generateEndIndices.ts +0 -19
- package/packages/solver/src/generateHorizontalPatterns.test.ts +0 -31
- package/packages/solver/src/generateHorizontalPatterns.ts +0 -15
- package/packages/solver/src/generatePattern.ts +0 -33
- package/packages/solver/src/generatePatterns.test.ts +0 -13
- package/packages/solver/src/generatePatterns.ts +0 -11
- package/packages/solver/src/generateStartIndices.test.ts +0 -19
- package/packages/solver/src/generateStartIndices.ts +0 -17
- package/packages/solver/src/generateVectors.test.ts +0 -12
- package/packages/solver/src/generateVectors.ts +0 -10
- package/packages/solver/src/generateVerticalPatterns.test.ts +0 -32
- package/packages/solver/src/generateVerticalPatterns.ts +0 -15
- package/packages/solver/src/getCellsScore.ts +0 -21
- package/packages/solver/src/getPatternHash.test.ts +0 -17
- package/packages/solver/src/getPatternHash.ts +0 -12
- package/packages/solver/src/getPatternScore.test.ts +0 -204
- package/packages/solver/src/getPatternScore.ts +0 -23
- package/packages/solver/src/getUniquePatterns.ts +0 -19
- package/packages/types/build/CollisionJson.d.ts +0 -2
- package/packages/types/build/CollisionJson.js +0 -2
- package/packages/types/build/FinalPattern.d.ts +0 -6
- package/packages/types/build/FinalPattern.js +0 -14
- package/packages/types/build/HorizontalPattern.d.ts +0 -5
- package/packages/types/build/HorizontalPattern.js +0 -34
- package/packages/types/build/Pattern.d.ts +0 -20
- package/packages/types/build/Pattern.js +0 -50
- package/packages/types/build/PatternJson.d.ts +0 -6
- package/packages/types/build/PatternJson.js +0 -2
- package/packages/types/build/VerticalPattern.d.ts +0 -5
- package/packages/types/build/VerticalPattern.js +0 -34
- package/packages/types/src/CollisionJson.ts +0 -3
- package/packages/types/src/FinalPattern.ts +0 -14
- package/packages/types/src/HorizontalPattern.ts +0 -37
- package/packages/types/src/Pattern.ts +0 -71
- package/packages/types/src/PatternJson.ts +0 -7
- package/packages/types/src/VerticalPattern.ts +0 -37
- /package/packages/scrabble-solver/.next/static/{js6-w0PwySs2PyOSeCYfi → OLE8_ZInlGH7ENU0NIOpf}/_ssgManifest.js +0 -0
- /package/packages/scrabble-solver/src/{service-worker → solver-worker}/dictionaries/expirationManager.ts +0 -0
- /package/packages/scrabble-solver/src/{service-worker → solver-worker}/dictionaries/getDictionaryUrl.ts +0 -0
package/.oxlintrc.json
CHANGED
package/CLAUDE.md
CHANGED
|
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
4
4
|
|
|
5
5
|
## Repository layout
|
|
6
6
|
|
|
7
|
-
This is a Bun-workspaces monorepo (managed with Lerna for versioning/publishing and Nx purely for build caching/ordering). `engines` requires **Bun >=1.3** and **Node.js >=24**. The lockfile is `bun.lock` (text JSON, lockfileVersion 1) — `package-lock.json` was removed when the project migrated off npm/Jest in `#421` (Apr 2026). Workspace scripts use `bun run --filter <pattern> <script>` — the `'./packages/*'` glob runs in dependency order (built-in Nx cache), and `'*'` skips workspaces without that script (relevant for `test`).
|
|
7
|
+
This is a Bun-workspaces monorepo (managed with Lerna for versioning/publishing and Nx purely for build caching/ordering). `engines` requires **Bun >=1.3** and **Node.js >=24**. The lockfile is `bun.lock` (text JSON, lockfileVersion 1) — `package-lock.json` was removed when the project migrated off npm/Jest in `#421` (Apr 2026). Workspace scripts use `bun run --filter <pattern> <script>` — the `'./packages/*'` glob runs in dependency order (built-in Nx cache), and `'*'` skips workspaces without that script (relevant for `test`). The root `workspaces` field is an explicit nine-entry list in dependency order, not a glob.
|
|
8
8
|
|
|
9
9
|
The nine packages live under `packages/` and have a strict dependency order. When something breaks "downstream" of where you edited, rebuild the upstream package first:
|
|
10
10
|
|
|
@@ -16,19 +16,21 @@ word-definitions ─────────────┘
|
|
|
16
16
|
logger (independent, used by app + dictionaries)
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
The GADDAG itself lives outside this repo, in [`@kamilmielnik/gaddag`](https://github.com/kamilmielnik/gaddag) — a flat typed-array automaton with `has`/`hasPrefix`/`getArc`, binary `serialize`/`deserialize`, and `Gaddag.fromArray(words)`. It replaced `@kamilmielnik/trie` (#164) and is consumed by `dictionaries`, `solver`, and the app as a regular npm dependency (`^2.0.0`). A local `packages/gaddag/` directory may exist — it holds git-ignored compiled output left over from before the package was extracted and is **not** a workspace.
|
|
20
|
+
|
|
21
|
+
- `solver` — pure word-finding engine. Given a `Gaddag`, `Config`, `Board`, and `Tile[]`, returns scored `ResultJson`s. Has no I/O. `solve.ts` delegates to `MoveGenerator` (a single file holding all the logic) — anchor-based GADDAG move generation with per-cell cross-check masks, run once per direction; results are scored inline and sorted deterministically so UI ties resolve identically. `MoveGenerator` throws on alphabets over 64 tiles or boards over 32×32 because move sort keys pack direction/line/start/end into one integer. `benchmarks/` measures median `solve()` times on fixed mid-game boards (en-US, en-GB, pl-PL; 0–2 blanks): `bun run benchmark` from the root reruns them and rewrites the chart SVG + results table in the package `README.md`; it needs real (downloaded) dictionaries.
|
|
22
|
+
- `dictionaries` — downloads/caches per-locale word lists to `$HOME/.scrabble-solver/dictionaries` and exposes them as `Gaddag`s (binary `<locale>.gaddag` files on disk; building the Polish GADDAG from its 3.2M-word list takes ~25 s, so it happens in `postbuild`/background updates, not per request; the `update-dictionaries` / `remove-dictionaries` bin scripts wrap this, and `update()` refreshes only stale locales unless forced). The `Dictionaries` class layers a `MemoryCache` over a `DiskCache` (`LayeredCache`) and uses a per-locale `createAsyncProxy` to coalesce concurrent downloads. Cache entries older than `CACHE_STALE_THRESHOLD` (1 day) are considered stale, but only `update()` (run from `postbuild` and the packaging workflows) refreshes them — `get()` serves stale entries as-is. `DiskCache.get` treats files it cannot deserialize (corrupted, or written by an app version with an incompatible format — the binary magic doubles as the format version) as cache misses: it deletes them and the dictionary is re-downloaded and re-serialized automatically; `set` also removes the pre-#164 `<locale>.txt` trie cache. The service worker does the same client-side: `getGaddag` returns undefined for undeserializable cached dictionaries, falling back to the server and revalidating. Only this package and `logger` perform filesystem I/O — keep other packages pure so they can run in Edge / browser contexts.
|
|
21
23
|
- `word-lists` — pulls raw word lists from upstream sources (one fetcher per locale in `src/languages/`). Used by `dictionaries` during downloads.
|
|
22
|
-
- `word-definitions` — per-locale `crawl(word) → string` and a parser for each source (Wiktionary, CNRTL, DWDS, SJP, dexonline, vajehyab, etc.). Add a new locale by adding a `crawl` and a `parse` function in `src/languages/` and wiring them into `crawl.ts` / `parse.ts`. `parse.test.ts` is where you add fixture-based parser tests.
|
|
23
|
-
- `types` — domain model classes (`Board`, `Cell`, `Tile`, `
|
|
24
|
+
- `word-definitions` — per-locale `crawl(word) → string` and a parser for each source (Wiktionary, CNRTL, DWDS, SJP, dexonline, vajehyab, etc.). English uses the Wiktionary REST API (#431): `parse` JSON-parses the payload and uses cheerio only to strip HTML from definition strings, so the English fixtures are `.json`. Add a new locale by adding a `crawl` and a `parse` function in `src/languages/` and wiring them into `crawl.ts` / `parse.ts`. `parse.test.ts` is where you add fixture-based parser tests.
|
|
25
|
+
- `types` — domain model classes (`Board`, `Cell`, `Tile`, `Result`, `Config`, `Locale`, …) plus `*Json` shapes. Most of these have a `fromJson` / `toJson` round-trip — use them at the wire boundary instead of hand-rolled serialization.
|
|
24
26
|
- `configs` — split into **games** (`scrabble`, `superScrabble`, `scrabbleDuel`, `letterLeague`, `crossplay`, `literaki`, `kelimelik` — each defines board size, bonuses, rack size, blanks count, bingo bonus) and **languages** (`english`, `french`, …, each spreads a base game config and overrides `locale` + `tiles`). A locale config is one game × one language; e.g. `polishScrabble` = `scrabble` ⊕ Polish tiles + digraphs. Adding a language means a new config here **and** the 15-step checklist under "Add a new language" in `README.md`.
|
|
25
27
|
- `logger` — Winston logger writing JSON to `$HOME/.scrabble-solver/logs/{all,error}.log`. Only `error` goes to console. Used server-side by the app + dictionaries; do **not** import from browser code.
|
|
26
28
|
- `constants` — shared primitives (`BLANK`, `BONUS_CHARACTER`, `BONUS_WORD`, …). No runtime dependencies.
|
|
27
29
|
|
|
28
30
|
### App package (`@scrabble-solver/scrabble-solver`)
|
|
29
31
|
|
|
30
|
-
- **Routing**: Next.js Pages Router (`src/pages/`). API routes: `solve`, `verify`, `visit`, `dictionary/[locale]/[word]
|
|
31
|
-
- **State**: Redux Toolkit + Redux-Saga. Slices in `src/state/{board,cellFilters,dictionary,rack,results,settings,solve,verify}`, each exporting `<name>Slice` (reducer + actions) and selectors. The root saga in `state/sagas.ts` reacts to slice actions: `submit` → call SDK → write results back. `solve`, `verify`, and `dictionary` use `takeLatest` (only the latest in-flight request resolves); cell/rack edits use `takeEvery`. State is intentionally **not** serializable-checked (`serializableCheck: false`) because slices hold class instances (`Board`, `Tile`).
|
|
32
|
+
- **Routing**: Next.js Pages Router (`src/pages/`). API routes: `solve`, `verify`, `visit`, `dictionary/[locale]` (binary GADDAG download), and `dictionary/[locale]/[word]` (definitions). The path alias `@/*` resolves to `src/*` (set in `tsconfig.json`).
|
|
33
|
+
- **State**: Redux Toolkit + Redux-Saga. Slices in `src/state/{board,cellFilters,dictionary,hoveredTile,rack,results,settings,solve,verify}`, each exporting `<name>Slice` (reducer + actions) and selectors. The root saga in `state/sagas.ts` reacts to slice actions: `submit` → call SDK → write results back. `solve`, `verify`, and `dictionary` use `takeLatest` (only the latest in-flight request resolves); cell/rack edits use `takeEvery`. State is intentionally **not** serializable-checked (`serializableCheck: false`) because slices hold class instances (`Board`, `Tile`).
|
|
32
34
|
- **SDK layer (`src/sdk/`)**: thin browser/server clients for the four API routes. `findWordDefinitions` is memoized at the saga level via `lib/memoize`. Always go through SDK — never `fetch` directly from a saga or component.
|
|
33
35
|
- **Persistence**: settings, board, and rack auto-persist to `localStorage` via `store2` under the `scrabble-solver` namespace. The `useLocalStorage` hook (mounted in `pages/index.tsx`) subscribes to the three slices and writes them out on every change — **adding a new field to `SettingsState` is enough; you don't need to touch any save code** (PR #321, Apr 2026). On boot, `settingsInitialState` spreads `localStorage.getSettings()` last so persisted values win over the defaults computed at module-load time. When changing settings shape, add a migration block (see `migrateLegacySettings` for the pattern — dated comment with introduction date and life expectancy).
|
|
34
36
|
- **Service worker**: built by `WorkboxPlugin.InjectManifest` from `src/service-worker/index.ts` to `public/service-worker.js`. It precaches the app shell and intercepts `/api/solve` and `/api/verify` requests so the app can still solve offline once a dictionary is cached. Only generated in production builds (`!isServer && !dev`).
|
|
@@ -48,14 +50,15 @@ All commands run from the repo root unless noted.
|
|
|
48
50
|
| Build all packages | `bun run build` (Nx-cached, respects dep order) |
|
|
49
51
|
| Build one package | `bun run --filter @scrabble-solver/<name> build` |
|
|
50
52
|
| Dev server (port 3000) | `bun run dev` |
|
|
51
|
-
| Production server (port 3333) | `bun start` |
|
|
53
|
+
| Production server (port 3333) | `bun start` (also waits for :3333 and opens the browser; `bun run start:app` is server-only) |
|
|
52
54
|
| Lint | `bun run lint` (oxlint) / `bun run lint:fix` |
|
|
53
55
|
| Format check / fix | `bun run format` / `bun run format:fix` (oxfmt) |
|
|
54
|
-
| Type-check the app | `bun run --filter @scrabble-solver/scrabble-solver type-check` (uses `
|
|
56
|
+
| Type-check the app | `bun run --filter @scrabble-solver/scrabble-solver type-check` (uses `tsc` from stable TypeScript 7, the native Go compiler) |
|
|
55
57
|
| Unit tests (all workspaces) | `bun run test-unit` |
|
|
56
58
|
| Unit tests (one package) | `bun run --filter @scrabble-solver/solver test` |
|
|
57
59
|
| One unit test file | `cd packages/solver && bun test src/solve.test.ts` |
|
|
58
60
|
| One unit test by name | `cd packages/solver && bun test -t "pattern"` |
|
|
61
|
+
| Solver benchmarks | `bun run benchmark` (rewrites the results table + chart in `packages/solver/README.md`) |
|
|
59
62
|
| Cypress (interactive) | `bun run test-cypress` (expects dev server on :3000) |
|
|
60
63
|
| Cypress (headless) | `bun run test-cypress:run` (expects server on :3333) |
|
|
61
64
|
| Full test pipeline | `bun run test` (build → unit → `start-server-and-test` boots the app on :3333 → cypress run) — note: `bun test` invokes Bun's built-in test runner, not this script |
|
|
@@ -65,16 +68,17 @@ Hot reload only works for the `scrabble-solver` package. Edits to any other pack
|
|
|
65
68
|
## Testing notes
|
|
66
69
|
|
|
67
70
|
- Unit tests run on **Bun's test runner**, not Jest. The API is Jest-compatible (`describe`/`it`/`expect`), which is why the oxlint config still loads the `jest` plugin for rules like `no-focused-tests`.
|
|
68
|
-
- Only `solver`, `word-definitions`, and `scrabble-solver` have a `test` script. Tests are auto-discovered under `src/` matching `*.test.ts(x)`. The 180s timeout is needed because some solver tests build a real `
|
|
71
|
+
- Only `dictionaries`, `solver`, `word-definitions`, and `scrabble-solver` have a `test` script. Tests are auto-discovered under `src/` matching `*.test.ts(x)`. The 180s timeout is needed because some solver tests build a real `Gaddag` from a downloaded dictionary.
|
|
69
72
|
- `bunfig.toml` + `bun.test.preload.ts` register a SCSS loader stub (returns a `Proxy` whose keys are their own names) so component tests can import `*.scss` modules without a real compiler. If you add other non-JS imports to test-touched code (images, etc.), extend the preload.
|
|
70
73
|
- Each package's `tsconfig.json` excludes `**/*.test.ts` from the build output. Tests are not part of published packages.
|
|
71
|
-
- Cypress: tests in `cypress/e2e/`. Custom command setup in `cypress/support/commands.ts` (registers `@testing-library/cypress` and `cypress-real-events`). Two base URLs are in play: `cypress.config.ts` defaults to `http://localhost:3000` (matches `bun run dev`); `test-cypress:run` and CI override to `:3333` (matches `bun start`). Pick the script that matches the server you're actually running.
|
|
74
|
+
- Cypress: tests in `cypress/e2e/`. Custom command setup in `cypress/support/commands.ts` (registers `@testing-library/cypress` and `cypress-real-events`). Two base URLs are in play: `cypress.config.ts` defaults to `http://localhost:3000` (matches `bun run dev`); `test-cypress:run` and CI override to `:3333` (matches `bun start`). Pick the script that matches the server you're actually running. `cypress.config.ts` registers `@cypress/webpack-batteries-included-preprocessor` from npm as the `file:preprocessor` — the copy bundled inside Cypress 15.19.0 crashes on TypeScript 7 projects (its `@babel/preset-typescript` ships without a `package.json`); drop the override once a fixed Cypress release lands.
|
|
72
75
|
|
|
73
76
|
## Tooling specifics
|
|
74
77
|
|
|
75
78
|
- **Linting**: `oxlint` (Rust-based ESLint replacement) configured in `.oxlintrc.json`. Type-aware rules require `oxlint-tsgolint`. Adding a new top-level JS config file usually means adding it to `ignorePatterns`. The oxlint config still loads the `jest` plugin and `jest` global because Bun's test runner mirrors the Jest API; do not remove them.
|
|
76
79
|
- **Formatting**: `oxfmt` covers `*.{js,ts,tsx,scss}`.
|
|
77
|
-
- **
|
|
80
|
+
- **Install layout**: `bunfig.toml` pins `[install] linker = "hoisted"`. With Bun's isolated layout, `next build` fails — webpack bundles `unzipper` from `word-lists` and errors on its uninstalled optional `@aws-sdk/client-s3` dependency. Don't remove it.
|
|
81
|
+
- **TypeScript**: stable TypeScript 7 (`typescript@^7.0.2`), whose `tsc` is the native Go compiler. It runs every package `build`, the app's `type-check`, and `next build` — the latter because Next.js runs the project-local `tsc` CLI (TS7 has no JS compiler API). Next 16.3.0 turned that on by default, so the `experimental.useTypeScriptCli` flag that pinned the app to the 16.3 preview line is gone from `next.config.js`; set it to `false` only to opt back into the JS compiler API, which TS7 cannot serve. The interim `@typescript/native-preview` (`tsgo`) setup from PR #422 was removed (Aug 2026). Root `tsconfig.json` sets `types: ["bun"]` for global test-runner types and excludes `cypress` and `cypress.config.ts`; library packages extend it and additionally exclude `**/*.test.ts` from emitted output.
|
|
78
82
|
- **Next.js**: built with `--webpack` flag explicitly (the default Turbopack is intentionally not used). `next.config.js` registers `@svgr/webpack` for SVG-as-component imports and the Workbox `InjectManifest` plugin for the service worker. SCSS load paths are extended to `./src` and `node_modules/include-media/dist`.
|
|
79
83
|
- **Nx**: `nx.json` only defines a `build` target with `dependsOn: ["^build"]` and `cache: true`. It is used purely for dependency-aware build ordering and caching — there are no Nx generators or executors.
|
|
80
84
|
|
|
@@ -83,11 +87,11 @@ Hot reload only works for the `scrabble-solver` package. Edits to any other pack
|
|
|
83
87
|
`.github/workflows/`:
|
|
84
88
|
|
|
85
89
|
- `build.yml` — `bun install --frozen-lockfile && bun run build`.
|
|
86
|
-
- `unit
|
|
87
|
-
- `e2e
|
|
88
|
-
- `oxlint.yml` / `oxfmt.yml` — lint and format-check.
|
|
89
|
-
- `bunx.yml` / `npx.yml` — run daily
|
|
90
|
-
- `deploy.yml` — `workflow_dispatch` only. SSHs into prod, pulls, builds, restarts `scrabble-solver.service`.
|
|
90
|
+
- `unit.yml` — `bun run build && bun run test-unit`.
|
|
91
|
+
- `e2e.yml` — Cypress (`cypress-io/github-action`) against the production server on :3333. Uploads screenshots on failure.
|
|
92
|
+
- `oxlint.yml` / `oxfmt.yml` — lint and format-check. `oxlint.yml` runs `bun run build` first because type-aware rules need built packages.
|
|
93
|
+
- `bunx.yml` / `npx.yml` — run daily, on push to master, and via `workflow_dispatch`. They poll npm (up to 15 min) until the current version of every workspace package is published, download the published `scrabble-solver` tarball with `npm pack`, install the global binary (`bun add --global` / `npm install --global`), pre-warm the dictionaries cache, then run Cypress from the extracted tarball against that binary. The Cypress specs come from the published package, **not** from `master` — if they came from master, any spec added for a feature that's merged but not yet released would run against the older published binary that lacks the feature, and fail (#428). Catches packaging regressions in the `bin/scrabble-solver.js` launcher.
|
|
94
|
+
- `deploy.yml` — `workflow_dispatch` only (branch input, default `master`). SSHs into prod, pulls, builds, restarts `scrabble-solver.service`.
|
|
91
95
|
|
|
92
96
|
When adding a workflow, match the existing pattern: trigger on `push`/`pull_request` to `master`, use `actions/checkout@v6` and `oven-sh/setup-bun@v2`, install with `bun install --frozen-lockfile`.
|
|
93
97
|
|
|
@@ -103,7 +107,7 @@ The `Deploy` GitHub workflow (`workflow_dispatch` only) SSHs into the production
|
|
|
103
107
|
|
|
104
108
|
The app reads/writes user data outside the project directory:
|
|
105
109
|
|
|
106
|
-
- `$HOME/.scrabble-solver/dictionaries/` — cached `
|
|
110
|
+
- `$HOME/.scrabble-solver/dictionaries/` — cached serialized `Gaddag`s (binary), one per locale, refreshed when older than 1 day.
|
|
107
111
|
- `$HOME/.scrabble-solver/logs/{all,error}.log` — Winston JSON logs.
|
|
108
112
|
|
|
109
113
|
The `bunx scrabble-solver@latest` entry point (`bin/scrabble-solver.js`) just `cd`s to the package root and runs `bun start`. The app then serves on http://localhost:3333.
|
|
@@ -112,8 +116,9 @@ The `bunx scrabble-solver@latest` entry point (`bin/scrabble-solver.js`) just `c
|
|
|
112
116
|
|
|
113
117
|
Look here when something seems set up oddly — the reason is usually one of these recent changes. Reference issue numbers, not dates, when grepping git log.
|
|
114
118
|
|
|
115
|
-
- **#
|
|
116
|
-
- **#
|
|
119
|
+
- **#164 — GADDAG solver** (Aug 2026). `@kamilmielnik/trie` was dropped everywhere in favor of the external `@kamilmielnik/gaddag` package; `solve()` was rewritten as anchor-based GADDAG move generation (~40-60× faster). Disk-cached dictionaries changed from serialized-trie `.txt` to binary `.gaddag` files, and `/api/dictionary/[locale]` now serves `application/octet-stream`, gzipped when the client accepts it (compressed once per in-memory dictionary via a `WeakMap` cache — the route parses `Accept-Encoding` q-values so `gzip;q=0` gets identity and `*` counts as accepting, evicts failed compression promises from the cache, and sets `Vary: Accept-Encoding`). Responses are `Cache-Control: no-cache`, so clients revalidate with `If-None-Match` and unchanged re-downloads become 304s — the `ETag` comes from Next's built-in per-payload generation (it overrides any hand-set `ETag` on `send()`, so don't set one). The service worker deserializes with `Gaddag.deserialize` (the browser undoes `Content-Encoding` before the Cache API stores the body) and memoizes the deserialized `Gaddag` per locale, keyed on the cached response's `ETag`/`Date`/`Content-Length`, until `revalidateDictionary` replaces the entry.
|
|
120
|
+
- **#421 — Bun migration** (Apr 2026). npm/Jest → Bun. Top-level scripts now use `bun run --filter`, lockfile is `bun.lock`, unit tests run on `bun test`, the published binary's launcher (`bin/scrabble-solver.js`) shells out to `bun start`, and the packaging workflow now runs in two variants (`bunx.yml` installs via `bun add --global`, `npx.yml` via `npm install --global`). All workflows use `oven-sh/setup-bun@v2`; `npx.yml` is the only one that also uses `setup-node`.
|
|
121
|
+
- **#422 — TypeScript 7** (Apr 2026). Moved build/type-check to `tsgo` (native preview). Superseded in Aug 2026 by stable `typescript@7`, where the native compiler ships as plain `tsc` — scripts call `tsc` again and `@typescript/native-preview` is gone.
|
|
117
122
|
- **#420 — ESLint → Oxlint**. The `eslint-plugin-*` packages still appear in devDeps because oxlint loads them as JS plugins (`jsPlugins` in `.oxlintrc.json`). Don't strip them.
|
|
118
123
|
- **#321 — Auto-persisted settings** (Apr 2026). Settings, board, and rack are written through a single `useLocalStorage` effect. Don't dispatch save actions manually.
|
|
119
124
|
- **#228 — CSS variables in JS** (Apr 2026). JS constants for colors/sizes flow from `_tokens.scss` → `variables.module.scss` `:export` → `parameters/index.ts`. Don't hard-code the same values in TS.
|
package/README.md
CHANGED
|
@@ -188,6 +188,7 @@ bun run --filter @scrabble-solver/word-lists build
|
|
|
188
188
|
- [TypeScript](https://www.typescriptlang.org/)
|
|
189
189
|
- [Bun](https://bun.sh/docs)
|
|
190
190
|
- [Node.js](https://nodejs.org/)
|
|
191
|
+
- [@kamilmielnik/gaddag](https://github.com/kamilmielnik/gaddag)
|
|
191
192
|
- [Next.js](https://nextjs.org/)
|
|
192
193
|
- [Workbox](https://developer.chrome.com/docs/workbox/)
|
|
193
194
|
- [React](https://reactjs.org/)
|
|
@@ -198,12 +199,20 @@ bun run --filter @scrabble-solver/word-lists build
|
|
|
198
199
|
- [CSS Modules](https://github.com/css-modules/css-modules)
|
|
199
200
|
- [include-media](https://eduardoboucas.github.io/include-media/)
|
|
200
201
|
- [Lerna](https://lerna.js.org/)
|
|
202
|
+
- [Nx](https://nx.dev/)
|
|
201
203
|
- [Cypress](https://www.cypress.io/)
|
|
202
204
|
- [Oxlint](https://oxc.rs/docs/guide/usage/linter)
|
|
203
205
|
- [Oxfmt](https://oxc.rs/docs/guide/usage/formatter)
|
|
204
206
|
|
|
205
207
|
## Related projects
|
|
206
208
|
|
|
209
|
+
### @kamilmielnik/gaddag
|
|
210
|
+
|
|
211
|
+
The [GADDAG](https://en.wikipedia.org/wiki/GADDAG) data structure powering the solver. Dictionaries are stored as a GADDAG, which is what makes it possible to generate moves anchored on tiles already placed on the board.
|
|
212
|
+
|
|
213
|
+
- GitHub: https://github.com/kamilmielnik/gaddag
|
|
214
|
+
- npm: https://www.npmjs.com/package/@kamilmielnik/gaddag
|
|
215
|
+
|
|
207
216
|
### Lexatious
|
|
208
217
|
|
|
209
218
|
The user interface and source code for the web front end of Lexatious is based on Scrabble Solver.
|