scrabble-solver 2.15.24 → 2.16.0-alpha.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.
Files changed (143) hide show
  1. package/.claude/settings.local.json +12 -1
  2. package/.github/workflows/build.yml +3 -5
  3. package/.github/workflows/{npx.yml → bunx.yml} +7 -11
  4. package/.github/workflows/deploy.yml +2 -2
  5. package/.github/workflows/{cypress.yml → e2e-tests.yml} +7 -7
  6. package/.github/workflows/oxfmt.yml +4 -6
  7. package/.github/workflows/{eslint.yml → oxlint.yml} +6 -8
  8. package/.github/workflows/{jest.yml → unit-tests.yml} +6 -8
  9. package/.nx/workspace-data/d/server-process.json +5 -0
  10. package/.nx/workspace-data/file-map.json +614 -602
  11. package/.nx/workspace-data/lockfile-dependencies.hash +1 -1
  12. package/.nx/workspace-data/lockfile-nodes.hash +1 -1
  13. package/.nx/workspace-data/nx_files.nxt +0 -0
  14. package/.nx/workspace-data/parsed-lock-file.dependencies.json +5297 -11117
  15. package/.nx/workspace-data/parsed-lock-file.nodes.json +16371 -7676
  16. package/.nx/workspace-data/project-graph.json +25734 -20117
  17. package/.nx/workspace-data/source-maps.json +96 -0
  18. package/.oxlintrc.json +314 -0
  19. package/CLAUDE.md +120 -0
  20. package/README.md +27 -25
  21. package/bin/scrabble-solver.js +1 -1
  22. package/bump-version.js +3 -4
  23. package/bun-test-globals.d.ts +1 -0
  24. package/bun.lock +3984 -0
  25. package/bun.test.preload.ts +21 -0
  26. package/bunfig.toml +2 -0
  27. package/lerna.json +1 -1
  28. package/package.json +28 -38
  29. package/packages/configs/build/games/literaki.d.ts +1 -1
  30. package/packages/configs/package.json +4 -4
  31. package/packages/constants/package.json +2 -2
  32. package/packages/dictionaries/package.json +5 -5
  33. package/packages/logger/package.json +2 -2
  34. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  35. package/packages/scrabble-solver/.next/build-manifest.json +9 -9
  36. package/packages/scrabble-solver/.next/cache/.previewinfo +1 -1
  37. package/packages/scrabble-solver/.next/cache/.rscinfo +1 -1
  38. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  39. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  40. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  41. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack.old +0 -0
  42. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  43. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  44. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  45. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  46. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack.old +0 -0
  47. package/packages/scrabble-solver/.next/diagnostics/framework.json +1 -1
  48. package/packages/scrabble-solver/.next/prerender-manifest.json +4 -4
  49. package/packages/scrabble-solver/.next/required-server-files.js +2 -1
  50. package/packages/scrabble-solver/.next/required-server-files.json +2 -1
  51. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  52. package/packages/scrabble-solver/.next/server/chunks/30.js +1 -1
  53. package/packages/scrabble-solver/.next/server/chunks/712.js +1 -1
  54. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  55. package/packages/scrabble-solver/.next/server/pages/404.html +1 -1
  56. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  57. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  58. package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
  59. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  60. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  61. package/packages/scrabble-solver/.next/server/pages/api/solve.js +1 -1
  62. package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
  63. package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -1
  64. package/packages/scrabble-solver/.next/server/pages/api/visit.js.nft.json +1 -1
  65. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  66. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  67. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  68. package/packages/scrabble-solver/.next/server/pages/not-found.html +1 -1
  69. package/packages/scrabble-solver/.next/server/webpack-api-runtime.js +1 -1
  70. package/packages/scrabble-solver/.next/static/{f_g0t0jBAscXxDrNYOuV0 → HFiEUpdZIGYd_XKb5mCLZ}/_buildManifest.js +1 -1
  71. package/packages/scrabble-solver/.next/static/chunks/{main-ca07941c8a544c9a.js → main-65a5015b46ca9827.js} +1 -1
  72. package/packages/scrabble-solver/.next/static/chunks/pages/{_app-369e5cb907606ad4.js → _app-9c9aaea7b6881765.js} +4 -4
  73. package/packages/scrabble-solver/.next/static/chunks/pages/{index-08b3b0a754095919.js → index-e9ea63e97865edcf.js} +1 -1
  74. package/packages/scrabble-solver/.next/static/css/{500cdc9a24075d91.css → 78726ae7cf7a5497.css} +1 -1
  75. package/packages/scrabble-solver/.next/trace +21 -21
  76. package/packages/scrabble-solver/.next/trace-build +1 -1
  77. package/packages/scrabble-solver/README.md +3 -3
  78. package/packages/scrabble-solver/next.config.js +1 -0
  79. package/packages/scrabble-solver/package.json +11 -10
  80. package/packages/scrabble-solver/public/service-worker.js +1 -1
  81. package/packages/scrabble-solver/src/@types/scss.d.ts +5 -0
  82. package/packages/scrabble-solver/src/components/Board/Board.module.scss +2 -2
  83. package/packages/scrabble-solver/src/components/Board/components/Actions/Actions.module.scss +1 -1
  84. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +26 -6
  85. package/packages/scrabble-solver/src/components/Board/components/InputPrompt/InputPrompt.module.scss +1 -1
  86. package/packages/scrabble-solver/src/components/Button/Button.module.scss +1 -1
  87. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +1 -1
  88. package/packages/scrabble-solver/src/components/DictionaryInput/DictionaryInput.module.scss +1 -1
  89. package/packages/scrabble-solver/src/components/IconButton/IconButton.module.scss +1 -1
  90. package/packages/scrabble-solver/src/components/Loading/Loading.module.scss +1 -1
  91. package/packages/scrabble-solver/src/components/Modal/Modal.module.scss +1 -1
  92. package/packages/scrabble-solver/src/components/Modal/Modal.tsx +0 -1
  93. package/packages/scrabble-solver/src/components/NotFound/NotFound.module.scss +1 -1
  94. package/packages/scrabble-solver/src/components/PlainTiles/PlainTiles.module.scss +1 -1
  95. package/packages/scrabble-solver/src/components/Rack/Rack.module.scss +11 -6
  96. package/packages/scrabble-solver/src/components/Rack/components/InputPrompt/InputPrompt.module.scss +1 -1
  97. package/packages/scrabble-solver/src/components/Rack/components/RackTile/RackTile.module.scss +1 -1
  98. package/packages/scrabble-solver/src/components/Radio/Radio.module.scss +1 -1
  99. package/packages/scrabble-solver/src/components/Results/Results.module.scss +1 -1
  100. package/packages/scrabble-solver/src/components/ResultsInput/ResultsInput.module.scss +1 -1
  101. package/packages/scrabble-solver/src/components/Solver/Solver.module.scss +1 -1
  102. package/packages/scrabble-solver/src/components/Solver/components/InsertButton/InsertButton.module.scss +1 -1
  103. package/packages/scrabble-solver/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +1 -1
  104. package/packages/scrabble-solver/src/components/Spinner/Spinner.module.scss +1 -1
  105. package/packages/scrabble-solver/src/components/Tile/Tile.module.scss +1 -1
  106. package/packages/scrabble-solver/src/hooks/useLocalStorage.ts +4 -52
  107. package/packages/scrabble-solver/src/hooks/useMedia.ts +0 -1
  108. package/packages/scrabble-solver/src/lib/isMac.ts +1 -1
  109. package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.module.scss +1 -1
  110. package/packages/scrabble-solver/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -1
  111. package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.module.scss +1 -1
  112. package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +1 -1
  113. package/packages/scrabble-solver/src/pages/_app.tsx +0 -1
  114. package/packages/scrabble-solver/src/parameters/index.ts +48 -47
  115. package/packages/scrabble-solver/src/sdk/fetchJson.ts +3 -7
  116. package/packages/scrabble-solver/src/service-worker/index.ts +0 -1
  117. package/packages/scrabble-solver/src/service-worker/routeSolveRequests.ts +18 -12
  118. package/packages/scrabble-solver/src/state/board/slice.ts +6 -4
  119. package/packages/scrabble-solver/src/state/localStorage.ts +42 -52
  120. package/packages/scrabble-solver/src/state/sagas.ts +1 -2
  121. package/packages/scrabble-solver/src/state/settings/initialState.ts +7 -7
  122. package/packages/scrabble-solver/src/styles/_tokens.scss +59 -0
  123. package/packages/scrabble-solver/src/styles/global.scss +3 -3
  124. package/packages/scrabble-solver/src/styles/mixins.scss +13 -12
  125. package/packages/scrabble-solver/src/styles/variables.module.scss +53 -0
  126. package/packages/scrabble-solver/src/styles/variables.scss +44 -41
  127. package/packages/scrabble-solver/tsconfig.json +0 -1
  128. package/packages/scrabble-solver/tsconfig.tsbuildinfo +1 -1
  129. package/packages/solver/build/fillPattern.d.ts +1 -3
  130. package/packages/solver/package.json +8 -7
  131. package/packages/types/build/Result.d.ts +1 -1
  132. package/packages/types/package.json +3 -3
  133. package/packages/word-definitions/build/languages/english.js +1 -3
  134. package/packages/word-definitions/package.json +5 -4
  135. package/packages/word-definitions/src/languages/english.ts +0 -1
  136. package/packages/word-definitions/src/parse.test.ts +7 -2
  137. package/packages/word-lists/package.json +3 -3
  138. package/tsconfig.json +2 -1
  139. package/eslint.config.mjs +0 -722
  140. package/jest.config.js +0 -15
  141. package/jest.setup.js +0 -6
  142. package/tsconfig.jest.json +0 -11
  143. /package/packages/scrabble-solver/.next/static/{f_g0t0jBAscXxDrNYOuV0 → HFiEUpdZIGYd_XKb5mCLZ}/_ssgManifest.js +0 -0
@@ -680,6 +680,10 @@
680
680
  "packages/scrabble-solver/package.json",
681
681
  "nx/core/package-json"
682
682
  ],
683
+ "metadata.targetGroups.NPM Scripts.6": [
684
+ "packages/scrabble-solver/package.json",
685
+ "nx/core/package-json"
686
+ ],
683
687
  "metadata.description": [
684
688
  "packages/scrabble-solver/package.json",
685
689
  "nx/core/package-json"
@@ -852,6 +856,34 @@
852
856
  "packages/scrabble-solver/package.json",
853
857
  "nx/core/package-json"
854
858
  ],
859
+ "targets.test": [
860
+ "packages/scrabble-solver/package.json",
861
+ "nx/core/package-json"
862
+ ],
863
+ "targets.test.executor": [
864
+ "packages/scrabble-solver/package.json",
865
+ "nx/core/package-json"
866
+ ],
867
+ "targets.test.options": [
868
+ "packages/scrabble-solver/package.json",
869
+ "nx/core/package-json"
870
+ ],
871
+ "targets.test.metadata": [
872
+ "packages/scrabble-solver/package.json",
873
+ "nx/core/package-json"
874
+ ],
875
+ "targets.test.options.script": [
876
+ "packages/scrabble-solver/package.json",
877
+ "nx/core/package-json"
878
+ ],
879
+ "targets.test.metadata.scriptContent": [
880
+ "packages/scrabble-solver/package.json",
881
+ "nx/core/package-json"
882
+ ],
883
+ "targets.test.metadata.runCommand": [
884
+ "packages/scrabble-solver/package.json",
885
+ "nx/core/package-json"
886
+ ],
855
887
  "targets.type-check": [
856
888
  "packages/scrabble-solver/package.json",
857
889
  "nx/core/package-json"
@@ -926,6 +958,10 @@
926
958
  "packages/solver/package.json",
927
959
  "nx/core/package-json"
928
960
  ],
961
+ "metadata.targetGroups.NPM Scripts.2": [
962
+ "packages/solver/package.json",
963
+ "nx/core/package-json"
964
+ ],
929
965
  "metadata.description": [
930
966
  "packages/solver/package.json",
931
967
  "nx/core/package-json"
@@ -1014,6 +1050,34 @@
1014
1050
  "packages/solver/package.json",
1015
1051
  "nx/core/package-json"
1016
1052
  ],
1053
+ "targets.test": [
1054
+ "packages/solver/package.json",
1055
+ "nx/core/package-json"
1056
+ ],
1057
+ "targets.test.executor": [
1058
+ "packages/solver/package.json",
1059
+ "nx/core/package-json"
1060
+ ],
1061
+ "targets.test.options": [
1062
+ "packages/solver/package.json",
1063
+ "nx/core/package-json"
1064
+ ],
1065
+ "targets.test.metadata": [
1066
+ "packages/solver/package.json",
1067
+ "nx/core/package-json"
1068
+ ],
1069
+ "targets.test.options.script": [
1070
+ "packages/solver/package.json",
1071
+ "nx/core/package-json"
1072
+ ],
1073
+ "targets.test.metadata.scriptContent": [
1074
+ "packages/solver/package.json",
1075
+ "nx/core/package-json"
1076
+ ],
1077
+ "targets.test.metadata.runCommand": [
1078
+ "packages/solver/package.json",
1079
+ "nx/core/package-json"
1080
+ ],
1017
1081
  "targets.build.dependsOn": [
1018
1082
  "nx.json",
1019
1083
  "nx/target-defaults"
@@ -1194,6 +1258,10 @@
1194
1258
  "packages/word-definitions/package.json",
1195
1259
  "nx/core/package-json"
1196
1260
  ],
1261
+ "metadata.targetGroups.NPM Scripts.2": [
1262
+ "packages/word-definitions/package.json",
1263
+ "nx/core/package-json"
1264
+ ],
1197
1265
  "metadata.description": [
1198
1266
  "packages/word-definitions/package.json",
1199
1267
  "nx/core/package-json"
@@ -1282,6 +1350,34 @@
1282
1350
  "packages/word-definitions/package.json",
1283
1351
  "nx/core/package-json"
1284
1352
  ],
1353
+ "targets.test": [
1354
+ "packages/word-definitions/package.json",
1355
+ "nx/core/package-json"
1356
+ ],
1357
+ "targets.test.executor": [
1358
+ "packages/word-definitions/package.json",
1359
+ "nx/core/package-json"
1360
+ ],
1361
+ "targets.test.options": [
1362
+ "packages/word-definitions/package.json",
1363
+ "nx/core/package-json"
1364
+ ],
1365
+ "targets.test.metadata": [
1366
+ "packages/word-definitions/package.json",
1367
+ "nx/core/package-json"
1368
+ ],
1369
+ "targets.test.options.script": [
1370
+ "packages/word-definitions/package.json",
1371
+ "nx/core/package-json"
1372
+ ],
1373
+ "targets.test.metadata.scriptContent": [
1374
+ "packages/word-definitions/package.json",
1375
+ "nx/core/package-json"
1376
+ ],
1377
+ "targets.test.metadata.runCommand": [
1378
+ "packages/word-definitions/package.json",
1379
+ "nx/core/package-json"
1380
+ ],
1285
1381
  "targets.build.dependsOn": [
1286
1382
  "nx.json",
1287
1383
  "nx/target-defaults"
package/.oxlintrc.json ADDED
@@ -0,0 +1,314 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["eslint", "typescript", "react", "import", "unicorn", "oxc", "jest"],
4
+ "jsPlugins": [
5
+ { "name": "import-js", "specifier": "eslint-plugin-import" },
6
+ { "name": "mocha", "specifier": "eslint-plugin-mocha" },
7
+ { "name": "react-js", "specifier": "eslint-plugin-react" }
8
+ ],
9
+ "env": {
10
+ "browser": true,
11
+ "node": true,
12
+ "jest": true,
13
+ "mocha": true,
14
+ "jquery": true,
15
+ "serviceworker": true
16
+ },
17
+ "globals": {
18
+ "RequestInfo": "readonly",
19
+ "RequestInit": "readonly",
20
+ "ServiceWorkerGlobalScope": "readonly",
21
+ "beforeAll": "readonly",
22
+ "cy": "readonly",
23
+ "Cypress": "readonly",
24
+ "define": "readonly",
25
+ "describe": "readonly",
26
+ "expect": "readonly",
27
+ "globalThis": "readonly",
28
+ "it": "readonly",
29
+ "jest": "readonly"
30
+ },
31
+ "options": {
32
+ "typeAware": true
33
+ },
34
+ "settings": {
35
+ "react": {
36
+ "version": "19"
37
+ },
38
+ "import-js/extensions": [".js", ".jsx", ".ts", ".tsx"],
39
+ "import-js/external-module-folders": ["node_modules"],
40
+ "import-js/parsers": {
41
+ "@typescript-eslint/parser": [".ts", ".tsx"]
42
+ },
43
+ "import-js/resolver": {
44
+ "typescript": {
45
+ "alwaysTryTypes": true,
46
+ "project": ["tsconfig.json", "packages/*/tsconfig.json", "cypress/tsconfig.json"]
47
+ },
48
+ "node": true
49
+ }
50
+ },
51
+ "ignorePatterns": [
52
+ "**/bin/*.js",
53
+ "**/build/**/*",
54
+ "**/coverage/**/*",
55
+ "**/dist/**/*",
56
+ "**/node_modules/**/*",
57
+ "**/*.d.ts",
58
+ "**/.eslintrc.js",
59
+ "**/babel.config.js",
60
+ "**/bump-version.js",
61
+ "packages/scrabble-solver/next.config.js",
62
+ "packages/logger/scripts/stats.js",
63
+ "packages/scrabble-solver/public/service-worker.js",
64
+ "**/.next/**/*"
65
+ ],
66
+ "rules": {
67
+ "import/default": "error",
68
+ "import/no-absolute-path": "error",
69
+ "import/no-cycle": "error",
70
+ "import/no-dynamic-require": "error",
71
+
72
+ "import-js/newline-after-import": "error",
73
+ "import-js/order": [
74
+ "error",
75
+ {
76
+ "alphabetize": { "order": "asc", "caseInsensitive": true },
77
+ "groups": [["builtin", "external"], "internal", "parent", "sibling", "index"],
78
+ "newlines-between": "always",
79
+ "pathGroups": [{ "pattern": "@/**", "group": "internal" }],
80
+ "pathGroupsExcludedImportTypes": ["builtin"]
81
+ }
82
+ ],
83
+
84
+ "mocha/no-exclusive-tests": "error",
85
+ "jest/no-focused-tests": "error",
86
+
87
+ "react-js/no-deprecated": "error",
88
+ "react-js/no-unused-prop-types": "error",
89
+ "react-js/sort-comp": [
90
+ "error",
91
+ {
92
+ "order": [
93
+ "static-methods",
94
+ "instance-variables",
95
+ "lifecycle",
96
+ "/^on.+$/",
97
+ "everything-else",
98
+ "render",
99
+ "/^render.+/"
100
+ ]
101
+ }
102
+ ],
103
+ "react-js/sort-prop-types": ["error", { "ignoreCase": true, "callbacksLast": true }],
104
+
105
+ "no-cond-assign": "error",
106
+ "no-console": "warn",
107
+ "no-constant-condition": "error",
108
+ "no-control-regex": "error",
109
+ "no-debugger": "warn",
110
+ "no-dupe-keys": "error",
111
+ "no-duplicate-case": "error",
112
+ "no-empty": "error",
113
+ "no-empty-character-class": "error",
114
+ "no-ex-assign": "error",
115
+ "no-extra-boolean-cast": "error",
116
+ "no-func-assign": "error",
117
+ "no-invalid-regexp": "error",
118
+ "no-irregular-whitespace": "error",
119
+ "no-unsafe-negation": "error",
120
+ "no-obj-calls": "error",
121
+ "no-regex-spaces": "error",
122
+ "no-sparse-arrays": "error",
123
+ "no-unexpected-multiline": "error",
124
+ "no-unreachable": "error",
125
+ "no-unsafe-finally": "error",
126
+ "use-isnan": "error",
127
+ "valid-typeof": "error",
128
+ "accessor-pairs": "error",
129
+ "array-callback-return": "error",
130
+ "block-scoped-var": "error",
131
+ "curly": ["error", "all"],
132
+ "default-case": "error",
133
+ "dot-notation": "error",
134
+ "eqeqeq": "error",
135
+ "guard-for-in": "error",
136
+ "no-alert": "error",
137
+ "no-caller": "error",
138
+ "no-case-declarations": "error",
139
+ "no-div-regex": "error",
140
+ "no-else-return": "error",
141
+ "no-empty-function": "error",
142
+ "no-empty-pattern": "error",
143
+ "no-eq-null": "error",
144
+ "no-eval": "error",
145
+ "no-extend-native": "error",
146
+ "no-extra-bind": "error",
147
+ "no-extra-label": "error",
148
+ "no-fallthrough": "error",
149
+ "no-implicit-coercion": "error",
150
+ "no-iterator": "error",
151
+ "no-labels": "error",
152
+ "no-lone-blocks": "error",
153
+ "no-loop-func": "error",
154
+ "no-multi-str": "error",
155
+ "no-global-assign": "error",
156
+ "no-new": "error",
157
+ "no-new-func": "error",
158
+ "no-new-wrappers": "error",
159
+ "no-param-reassign": "error",
160
+ "no-proto": "error",
161
+ "no-redeclare": ["error", { "builtinGlobals": false }],
162
+ "no-return-assign": "error",
163
+ "no-script-url": "error",
164
+ "no-self-assign": "error",
165
+ "no-self-compare": "error",
166
+ "no-sequences": "error",
167
+ "no-unmodified-loop-condition": "error",
168
+ "no-unused-expressions": "error",
169
+ "no-unused-labels": "error",
170
+ "no-useless-call": "error",
171
+ "no-useless-concat": "error",
172
+ "no-useless-escape": "error",
173
+ "no-void": "error",
174
+ "no-warning-comments": "warn",
175
+ "no-with": "error",
176
+ "radix": "error",
177
+ "vars-on-top": "error",
178
+ "yoda": ["error", "never"],
179
+ "no-delete-var": "error",
180
+ "no-label-var": "error",
181
+ "no-restricted-globals": "error",
182
+ "no-shadow": "error",
183
+ "no-shadow-restricted-names": "error",
184
+ "no-undef": "error",
185
+ "no-unused-vars": "error",
186
+ "unicorn/no-process-exit": "error",
187
+
188
+ "max-depth": ["error", 4],
189
+ "max-lines": ["warn", 200],
190
+ "max-nested-callbacks": ["error", 4],
191
+ "max-params": ["error", 4],
192
+ "max-statements": ["warn", 20],
193
+ "new-cap": ["error", { "newIsCap": true, "capIsNew": false, "properties": true }],
194
+ "no-array-constructor": "error",
195
+ "no-bitwise": "error",
196
+ "no-lonely-if": "error",
197
+ "no-negated-condition": "error",
198
+ "no-nested-ternary": "error",
199
+ "no-object-constructor": "error",
200
+ "no-restricted-exports": [
201
+ "error",
202
+ {
203
+ "restrictDefaultExports": {
204
+ "direct": true,
205
+ "named": true,
206
+ "defaultFrom": true,
207
+ "namedFrom": true,
208
+ "namespaceFrom": true
209
+ }
210
+ }
211
+ ],
212
+ "no-unneeded-ternary": "error",
213
+ "operator-assignment": ["error", "always"],
214
+ "sort-vars": "error",
215
+ "unicode-bom": ["error", "never"],
216
+
217
+ "constructor-super": "error",
218
+ "no-class-assign": "error",
219
+ "no-const-assign": "error",
220
+ "no-dupe-class-members": "error",
221
+ "no-duplicate-imports": "error",
222
+ "no-this-before-super": "error",
223
+ "no-useless-computed-key": "error",
224
+ "no-useless-constructor": "error",
225
+ "no-useless-rename": "error",
226
+ "no-var": "error",
227
+ "object-shorthand": ["error", "always", { "avoidQuotes": true }],
228
+ "prefer-const": "error",
229
+ "prefer-rest-params": "error",
230
+ "prefer-spread": "error",
231
+ "prefer-template": "error",
232
+ "require-yield": "error",
233
+
234
+ "react/display-name": "off",
235
+ "react/jsx-no-comment-textnodes": "error",
236
+ "react/no-danger": "error",
237
+ "react/no-did-mount-set-state": "error",
238
+ "react/no-direct-mutation-state": "error",
239
+ "react/no-is-mounted": "error",
240
+ "react/no-multi-comp": ["error", { "ignoreStateless": true }],
241
+ "react/no-render-return-value": "error",
242
+ "react/no-string-refs": "error",
243
+ "react/no-unknown-property": "error",
244
+ "react/prefer-es6-class": "error",
245
+ "react/react-in-jsx-scope": "off",
246
+ "react/require-render-return": "error",
247
+ "react/self-closing-comp": "error",
248
+ "react/jsx-boolean-value": ["error", "never"],
249
+ "react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
250
+ "react/jsx-handler-names": [
251
+ "error",
252
+ { "eventHandlerPrefix": "on", "eventHandlerPropPrefix": "on" }
253
+ ],
254
+ "react/jsx-key": "error",
255
+ "react/jsx-no-duplicate-props": "error",
256
+ "react/jsx-no-target-blank": "error",
257
+ "react/jsx-no-undef": "error",
258
+ "react/jsx-pascal-case": "error",
259
+
260
+ "react/rules-of-hooks": "error",
261
+ "react/exhaustive-deps": "error"
262
+ },
263
+ "overrides": [
264
+ {
265
+ "files": ["**/*.ts", "**/*.tsx"],
266
+ "rules": {
267
+ "no-unused-vars": "off",
268
+ "no-shadow": "off",
269
+ "typescript/consistent-type-imports": ["error", { "fixStyle": "inline-type-imports" }],
270
+ "typescript/no-deprecated": "error",
271
+ "typescript/no-shadow": "error",
272
+ "typescript/no-floating-promises": "error",
273
+ "typescript/no-misused-promises": "error",
274
+ "typescript/no-unsafe-argument": "error",
275
+ "typescript/no-unsafe-call": "error",
276
+ "typescript/no-unsafe-member-access": "error",
277
+ "typescript/await-thenable": "error",
278
+ "typescript/restrict-plus-operands": "error",
279
+ "typescript/restrict-template-expressions": "error",
280
+ "typescript/unbound-method": "error",
281
+ "typescript/no-implied-eval": "error",
282
+ "typescript/no-unused-vars": [
283
+ "error",
284
+ {
285
+ "argsIgnorePattern": "^_",
286
+ "caughtErrorsIgnorePattern": "^_",
287
+ "destructuredArrayIgnorePattern": "^_",
288
+ "varsIgnorePattern": "^_"
289
+ }
290
+ ]
291
+ }
292
+ },
293
+ {
294
+ "files": ["**/*.test.ts", "**/*.test.tsx"],
295
+ "rules": {
296
+ "typescript/no-non-null-assertion": "off"
297
+ }
298
+ },
299
+ {
300
+ "files": ["**/*.spec.cy.ts", "cypress/**"],
301
+ "rules": {
302
+ "max-statements": "off",
303
+ "jest/valid-expect": "off",
304
+ "jest/no-focused-tests": "off"
305
+ }
306
+ },
307
+ {
308
+ "files": ["packages/scrabble-solver/src/pages/**"],
309
+ "rules": {
310
+ "no-restricted-exports": "off"
311
+ }
312
+ }
313
+ ]
314
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,120 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Repository layout
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`).
8
+
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
+
11
+ ```
12
+ constants → types → configs ──┐
13
+ ├──→ dictionaries → solver → scrabble-solver (Next.js app)
14
+ word-lists ───────────────────┤
15
+ word-definitions ─────────────┘
16
+ logger (independent, used by app + dictionaries)
17
+ ```
18
+
19
+ - `solver` — pure word-finding engine. Given a `Trie`, `Config`, `Board`, and `Tile[]`, returns scored `Result`s. Has no I/O. Pipeline is `generatePatterns → fillPattern (per pattern) → areDigraphsValid (only when `config.twoCharacterTiles` is non-empty) → getUniquePatterns → getPatternScore`. New solving rules belong in this pipeline; `solve.ts` itself is just orchestration.
20
+ - `dictionaries` — downloads/caches per-locale word lists to `$HOME/.scrabble-solver/dictionaries` and exposes them as `Trie`s. 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 refreshed on access. Only this package and `logger` perform filesystem I/O — keep other packages pure so they can run in Edge / browser contexts.
21
+ - `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 (Merriam-Webster, 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`, `Pattern`, `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
+ - `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
+ - `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
+ - `constants` — shared primitives (`BLANK`, `BONUS_CHARACTER`, `BONUS_WORD`, …). No runtime dependencies.
27
+
28
+ ### App package (`@scrabble-solver/scrabble-solver`)
29
+
30
+ - **Routing**: Next.js Pages Router (`src/pages/`). API routes: `solve`, `verify`, `visit`, `dictionary/[locale]/[word]`. The path alias `@/*` resolves to `src/*` (set in `tsconfig.json`).
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
+ - **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
+ - **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
+ - **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`).
35
+ - **i18n**: `src/i18n/languages/<lang>.json` (8 languages, mapped to `Locale` in `i18n.ts`). The `LOCALE_FEATURES` registry in `src/i18n/constants.ts` carries per-locale UI metadata: `direction` ('ltr' | 'rtl'), `comma`/`separator` glyphs (Latin vs Arabic), flag `Icon`, language `label`/`name`, and `consonants`/`vowels` flags that drive the auto-group-tiles UI. The `useDirection` hook applies `direction` to `<html dir>`. Add a new locale by extending this map plus the i18n JSON dictionary plus a `Flag<XX>.svg` icon. `useTranslate()` is the lookup hook.
36
+ - **Layout**: `AppLayoutProvider` (`src/app-layout/`) is a React context exposing `useAppLayoutValue`. It was extracted from a custom hook for memoization reasons — consume layout via `useAppLayout()`, not by re-deriving it. The active modal in `pages/index.tsx` is tracked as a `Record<Modal, boolean>` patched through a single `patchModals` callback.
37
+ - **Styling**: SCSS modules with a shared design-token system. SCSS tokens live in `src/styles/_tokens.scss`; the same values are re-exported to TS via `:export` in `variables.module.scss` (imported in JS as a module, typed by `src/@types/scss.d.ts`). Concrete JS constants live in `src/parameters/index.ts` (e.g. `BREAKPOINTS`, `COLOR_BLUE`, `TRANSITION_DURATION`) — that file is the only place that should read from `variables.module.scss`. Update `_tokens.scss` first → expose via `variables.module.scss` `:export` block → consume via `parameters/`. This was added in PR #228 (Apr 2026); before it, JS-side colors and breakpoints were hard-coded duplicates. Responsive helpers come from `include-media`.
38
+ - **SVGs**: imported as React components via `@svgr/webpack` (configured in `next.config.js`), typed by `src/@types/svg.d.ts`.
39
+ - **Service worker registration**: production-only. Registered by `serviceWorkerManager.ts` from the index page; Cypress tests must call `unregisterServiceWorkers()` (in `cypress/support/lib`) in `beforeEach` and `cy.clearLocalStorage()` in `afterEach` to avoid bleed-through between tests.
40
+
41
+ ## Common commands
42
+
43
+ All commands run from the repo root unless noted.
44
+
45
+ | Task | Command |
46
+ | --- | --- |
47
+ | Install + build everything | `bun install && bun run build` |
48
+ | Build all packages | `bun run build` (Nx-cached, respects dep order) |
49
+ | Build one package | `bun run --filter @scrabble-solver/<name> build` |
50
+ | Dev server (port 3000) | `bun run dev` |
51
+ | Production server (port 3333) | `bun start` |
52
+ | Lint | `bun run lint` (oxlint) / `bun run lint:fix` |
53
+ | 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 `tsgo`, the TypeScript native preview) |
55
+ | Unit tests (all workspaces) | `bun run test-unit` |
56
+ | Unit tests (one package) | `bun run --filter @scrabble-solver/solver test` |
57
+ | One unit test file | `cd packages/solver && bun test src/solve.test.ts` |
58
+ | One unit test by name | `cd packages/solver && bun test -t "pattern"` |
59
+ | Cypress (interactive) | `bun run test-cypress` (expects dev server on :3000) |
60
+ | Cypress (headless) | `bun run test-cypress:run` (expects server on :3333) |
61
+ | 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 |
62
+
63
+ Hot reload only works for the `scrabble-solver` package. Edits to any other package require rebuilding that package before the app picks them up.
64
+
65
+ ## Testing notes
66
+
67
+ - 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 `Trie` from a downloaded dictionary.
69
+ - `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
+ - 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.
72
+
73
+ ## Tooling specifics
74
+
75
+ - **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
+ - **Formatting**: `oxfmt` covers `*.{js,ts,tsx,scss}`.
77
+ - **TypeScript**: the project upgraded to TypeScript 7 via the native-preview compiler (PR #422). The runtime devDep is `typescript@^6.0.3` (kept for tooling that expects classic `tsc`), but the actual compiler used by the app's `type-check` and by `next build` is `tsgo` from `@typescript/native-preview` (a 7.x dev build). Plain `tsc` is not in the build path. 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
+ - **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
+ - **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
+
81
+ ## CI workflows
82
+
83
+ `.github/workflows/`:
84
+
85
+ - `build.yml` — `bun install --frozen-lockfile && bun run build`.
86
+ - `unit-tests.yml` — `bun run build && bun run test-unit`.
87
+ - `e2e-tests.yml` — Cypress against `bun start` on :3333. Uploads screenshots on failure.
88
+ - `oxlint.yml` / `oxfmt.yml` — lint and format-check.
89
+ - `bunx.yml` — runs daily and on PR. Installs the latest published `scrabble-solver` via `bun add --global scrabble-solver@latest`, runs Cypress against it. Catches packaging regressions in the `bin/scrabble-solver.js` launcher.
90
+ - `deploy.yml` — `workflow_dispatch` only. SSHs into prod, pulls, builds, restarts `scrabble-solver.service`.
91
+
92
+ 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
+
94
+ ## Versioning & publishing
95
+
96
+ `bun run release` chains `reinstall → version:bump → np → lerna publish from-package`. `version:bump` runs `lerna version --force-publish` (bumps every package in lockstep) followed by `bump-version.js` to sync any other version references, then commits. Don't hand-edit `version` fields across packages — use the script.
97
+
98
+ ## Deploys
99
+
100
+ The `Deploy` GitHub workflow (`workflow_dispatch` only) SSHs into the production host, pulls the chosen branch, runs `bun install && bun run build`, and restarts `scrabble-solver.service` via `systemctl`. There's no separate staging environment.
101
+
102
+ ## Runtime data
103
+
104
+ The app reads/writes user data outside the project directory:
105
+
106
+ - `$HOME/.scrabble-solver/dictionaries/` — cached `Trie`s, one per locale, refreshed when older than 1 day.
107
+ - `$HOME/.scrabble-solver/logs/{all,error}.log` — Winston JSON logs.
108
+
109
+ 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.
110
+
111
+ ## Recent migrations to keep in mind
112
+
113
+ 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
+
115
+ - **#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 old `npx.yml` workflow was renamed to `bunx.yml` (now installs the published package via `bun add --global`). All workflows use `oven-sh/setup-bun@v2`; `e2e-tests.yml` is the only one that also uses `setup-node` (Cypress action).
116
+ - **#422 — TypeScript 7** (Apr 2026). Build/type-check use `tsgo` (native preview). Don't reintroduce `tsc` calls.
117
+ - **#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
+ - **#321 — Auto-persisted settings** (Apr 2026). Settings, board, and rack are written through a single `useLocalStorage` effect. Don't dispatch save actions manually.
119
+ - **#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.
120
+ - **#360 — Dart Sass deprecations**. SCSS files migrated to modern syntax (`@use`, `math.div`, etc.). New SCSS should follow that style; `next.config.js` sets `sassOptions.quietDeps: true` to keep upstream warnings out of build output.