diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000000..3f501600878 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,25 @@ +name: Check +on: + push: + branches: + - main + paths: + - Makefile + - elm.json + - package.json + - pnpm-lock.yaml + - 'src/**' + workflow_dispatch: {} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - run: pnpm install + - name: Setup Elm + uses: mpizenberg/elm-tooling-action@1.6 + with: + cache-key: elm-home-${{ hashFiles('elm-tooling.json', 'elm.json') }} + - run: make --always-make check diff --git a/Makefile b/Makefile index d58ea77160f..c00c0dbd66e 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,10 @@ all: docs/index.html NikumaruFont.en.woff2 COPYING src/Translations.elm: public/translations/en.json pnpx elm-i18next-gen --source public/translations/en.json --target src/ --type both --overwrite -docs/index.html: fonts elm.json src/Translations.elm src/*.elm src/*/*.elm src/*/*/*.elm +src/Main.elm.d.ts: src/InteropDefinitions.elm + pnpx elm-ts-interop --output src/Main.elm.d.ts + +docs/index.html: fonts elm.json src/Translations.elm src/Main.elm.d.ts src/*.elm src/*/*.elm src/*/*/*.elm pnpx vite build --base '' fonts: public/NikumaruFont.en.woff2 public/NikumaruFont.ja.woff2 @@ -22,6 +25,15 @@ COPYING: scripts/collect_licenses.sh src/COPYING.in elm.json docs/index.html cp src/COPYING.in COPYING ./scripts/collect_licenses.sh >> COPYING +check: check-elm check-ts + +check-elm: + elm make --output=/dev/null src/Main.elm + +check-ts: src/Main.elm.d.ts + pnpx eslint src/index.ts + tsc --noEmit + clean: rm -r docs || true rm public/NikumaruFont.en.woff2 || true diff --git a/elm.json b/elm.json index f3c34db2310..1fa3640571a 100644 --- a/elm.json +++ b/elm.json @@ -7,6 +7,7 @@ "dependencies": { "direct": { "ChristophP/elm-i18next": "4.2.1", + "dillonkearns/elm-ts-json": "2.1.1", "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", @@ -24,7 +25,8 @@ "elm/bytes": "1.0.8", "elm/file": "1.0.5", "elm/parser": "1.1.0", - "elm/virtual-dom": "1.0.3" + "elm/virtual-dom": "1.0.3", + "elm-community/dict-extra": "2.4.0" } }, "test-dependencies": { diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000000..55341fe97a7 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + + +export default [ + { + files: ["**/*.{js,mjs,cjs,ts}"], + rules: { + "@typescript-eslint/switch-exhaustiveness-check": "error", + }, + }, + { + languageOptions: { + globals: globals.browser, + parserOptions: { + project: true, + } + } + }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, +]; diff --git a/index.html b/index.html index 80f6159bd0a..e9697a6563a 100644 --- a/index.html +++ b/index.html @@ -20,7 +20,7 @@ - + diff --git a/package.json b/package.json index 21f9dd977bd..8904c10bfaf 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,12 @@ }, "license": "GPL-3.0-only", "devDependencies": { + "@eslint/js": "^9.7.0", "elm-i18next-gen": "^1.1.0", "elm-tooling": "^1.8.0", + "eslint": "9.x", + "globals": "^15.8.0", + "typescript-eslint": "^7.16.1", "vite": "^5.3.3", "vite-plugin-elm": "^3.0.1", "vite-plugin-html": "^3.2.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c31e40c6ba..ac528a7db57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,24 @@ importers: .: devDependencies: + '@eslint/js': + specifier: ^9.7.0 + version: 9.7.0 elm-i18next-gen: specifier: ^1.1.0 version: 1.1.0 elm-tooling: specifier: ^1.8.0 version: 1.15.1 + eslint: + specifier: 9.x + version: 9.7.0 + globals: + specifier: ^15.8.0 + version: 15.8.0 + typescript-eslint: + specifier: ^7.16.1 + version: 7.16.1(eslint@9.7.0)(typescript@5.5.3) vite: specifier: ^5.3.3 version: 5.3.3(terser@5.31.2) @@ -164,6 +176,40 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.17.0': + resolution: {integrity: sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.7.0': + resolution: {integrity: sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.4': + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -284,6 +330,69 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@typescript-eslint/eslint-plugin@7.16.1': + resolution: {integrity: sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.16.1': + resolution: {integrity: sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.16.1': + resolution: {integrity: sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@7.16.1': + resolution: {integrity: sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.16.1': + resolution: {integrity: sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@7.16.1': + resolution: {integrity: sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.16.1': + resolution: {integrity: sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@7.16.1': + resolution: {integrity: sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==} + engines: {node: ^18.18.0 || >=20.0.0} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.3: resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} @@ -293,10 +402,24 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} @@ -323,6 +446,10 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} @@ -369,6 +496,10 @@ packages: resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} engines: {node: '>=4.8'} + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} @@ -376,6 +507,22 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -425,16 +572,70 @@ packages: engines: {node: '>=12'} hasBin: true + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@8.0.2: + resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.7.0: + resolution: {integrity: sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -447,6 +648,10 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + find-up@7.0.0: resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} engines: {node: '>=18'} @@ -455,6 +660,13 @@ packages: resolution: {integrity: sha512-ycwgqtoxujz1dm0kjkBFOPQMESxB9uKc/PlD951dQDIG+tBXRpYZC2UmJb0gDxopQ1ZX6oyRQN3goRczYu7Deg==} engines: {node: '>=6.4.0'} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -475,13 +687,32 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.8.0: + resolution: {integrity: sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==} + engines: {node: '>=18'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -495,6 +726,18 @@ packages: engines: {node: '>=12'} hasBin: true + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -514,6 +757,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -522,13 +769,40 @@ packages: engines: {node: '>=10'} hasBin: true + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + locate-path@7.2.0: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -550,6 +824,10 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -557,11 +835,17 @@ packages: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} @@ -581,10 +865,22 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + p-locate@6.0.0: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -592,9 +888,17 @@ packages: param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-exists@5.0.0: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -607,6 +911,14 @@ packages: resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} engines: {node: '>=4'} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + pathe@0.2.0: resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} @@ -621,6 +933,14 @@ packages: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -628,6 +948,10 @@ packages: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -649,14 +973,31 @@ packages: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + shebang-regex@1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -668,6 +1009,14 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -681,13 +1030,41 @@ packages: engines: {node: '>=10'} hasBin: true + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@7.16.1: + resolution: {integrity: sha512-889oE5qELj65q/tGeOSvlreNKhimitFwZqQ0o7PcWC7/lgRkAMknznsCsV8J8mZGTP/Z+cIbX8accf2DE33hrA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + typescript@5.5.3: + resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} + engines: {node: '>=14.17'} + hasBin: true + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -696,6 +1073,9 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + vite-plugin-elm@3.0.1: resolution: {integrity: sha512-xdIJ4SCrSfdcjsyvFOicn3xFq3wNXobX+OOnU4z24/7n/XGMJO3k70gUrH30tusdApOPQpIPM50wynoG3F35HQ==} peerDependencies: @@ -743,9 +1123,17 @@ packages: engines: {node: '>= 8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + yocto-queue@1.1.1: resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} @@ -821,6 +1209,43 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@9.7.0)': + dependencies: + eslint: 9.7.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/config-array@0.17.0': + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.5 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.1.0': + dependencies: + ajv: 6.12.6 + debug: 4.3.5 + espree: 10.1.0 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.7.0': {} + + '@eslint/object-schema@2.1.4': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.0': {} + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -910,16 +1335,114 @@ snapshots: '@types/estree@1.0.5': {} + '@typescript-eslint/eslint-plugin@7.16.1(@typescript-eslint/parser@7.16.1(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.16.1(eslint@9.7.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 7.16.1 + '@typescript-eslint/type-utils': 7.16.1(eslint@9.7.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.16.1(eslint@9.7.0)(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.16.1 + eslint: 9.7.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.16.1(eslint@9.7.0)(typescript@5.5.3)': + dependencies: + '@typescript-eslint/scope-manager': 7.16.1 + '@typescript-eslint/types': 7.16.1 + '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.16.1 + debug: 4.3.5 + eslint: 9.7.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.16.1': + dependencies: + '@typescript-eslint/types': 7.16.1 + '@typescript-eslint/visitor-keys': 7.16.1 + + '@typescript-eslint/type-utils@7.16.1(eslint@9.7.0)(typescript@5.5.3)': + dependencies: + '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) + '@typescript-eslint/utils': 7.16.1(eslint@9.7.0)(typescript@5.5.3) + debug: 4.3.5 + eslint: 9.7.0 + ts-api-utils: 1.3.0(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.16.1': {} + + '@typescript-eslint/typescript-estree@7.16.1(typescript@5.5.3)': + dependencies: + '@typescript-eslint/types': 7.16.1 + '@typescript-eslint/visitor-keys': 7.16.1 + debug: 4.3.5 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.16.1(eslint@9.7.0)(typescript@5.5.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) + '@typescript-eslint/scope-manager': 7.16.1 + '@typescript-eslint/types': 7.16.1 + '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) + eslint: 9.7.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@7.16.1': + dependencies: + '@typescript-eslint/types': 7.16.1 + eslint-visitor-keys: 3.4.3 + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 acorn@8.12.1: {} + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + argparse@2.0.1: {} + + array-union@2.1.0: {} + async@3.2.5: {} at-least-node@1.0.0: {} @@ -943,6 +1466,8 @@ snapshots: buffer-from@1.1.2: {} + callsites@3.1.0: {} + camel-case@4.1.2: dependencies: pascal-case: 3.1.2 @@ -985,6 +1510,12 @@ snapshots: shebang-command: 1.2.0 which: 1.3.1 + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-select@4.3.0: dependencies: boolbase: 1.0.0 @@ -995,6 +1526,16 @@ snapshots: css-what@6.1.0: {} + debug@4.3.5: + dependencies: + ms: 2.1.2 + + deep-is@0.1.4: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + dom-serializer@1.4.1: dependencies: domelementtype: 2.3.0 @@ -1065,8 +1606,78 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + escape-string-regexp@4.0.0: {} + + eslint-scope@8.0.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.0.0: {} + + eslint@9.7.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) + '@eslint-community/regexpp': 4.11.0 + '@eslint/config-array': 0.17.0 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.7.0 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.5 + escape-string-regexp: 4.0.0 + eslint-scope: 8.0.2 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@10.1.0: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 4.0.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1075,10 +1686,18 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.7 + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -1092,6 +1711,11 @@ snapshots: firstline: 1.3.1 lodash: 4.17.21 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + find-up@7.0.0: dependencies: locate-path: 7.2.0 @@ -1100,6 +1724,13 @@ snapshots: firstline@1.3.1: {} + flat-cache@4.0.1: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + + flatted@3.3.1: {} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -1122,6 +1753,10 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -1131,8 +1766,23 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + globals@14.0.0: {} + + globals@15.8.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} + has-flag@4.0.0: {} he@1.2.0: {} @@ -1147,6 +1797,15 @@ snapshots: relateurl: 0.2.7 terser: 5.31.2 + ignore@5.3.1: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -1162,6 +1821,8 @@ snapshots: is-number@7.0.0: {} + is-path-inside@3.0.3: {} + isexe@2.0.0: {} jake@10.9.1: @@ -1171,16 +1832,41 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.2 + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + locate-path@7.2.0: dependencies: p-locate: 6.0.0 + lodash.merge@4.6.2: {} + lodash@4.17.21: {} lower-case@2.0.2: @@ -1202,14 +1888,22 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + minimist@1.2.8: {} mkdirp@0.5.6: dependencies: minimist: 1.2.8 + ms@2.1.2: {} + nanoid@3.3.7: {} + natural-compare@1.4.0: {} + nice-try@1.0.5: {} no-case@3.0.4: @@ -1237,10 +1931,27 @@ snapshots: dependencies: wrappy: 1.0.2 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + p-limit@4.0.0: dependencies: yocto-queue: 1.1.1 + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + p-locate@6.0.0: dependencies: p-limit: 4.0.0 @@ -1250,17 +1961,27 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.3 + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + pascal-case@3.1.2: dependencies: no-case: 3.0.4 tslib: 2.6.3 + path-exists@4.0.0: {} + path-exists@5.0.0: {} path-is-absolute@1.0.1: {} path-key@2.0.1: {} + path-key@3.1.1: {} + + path-type@4.0.0: {} + pathe@0.2.0: {} picocolors@1.0.1: {} @@ -1273,10 +1994,16 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + queue-microtask@1.2.3: {} relateurl@0.2.7: {} + resolve-from@4.0.0: {} + reusify@1.0.4: {} rimraf@2.6.3: @@ -1311,12 +2038,22 @@ snapshots: semver@5.7.2: {} + semver@7.6.2: {} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + shebang-regex@1.0.0: {} + shebang-regex@3.0.0: {} + + slash@3.0.0: {} + source-map-js@1.2.0: {} source-map-support@0.5.21: @@ -1326,6 +2063,12 @@ snapshots: source-map@0.6.1: {} + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -1342,16 +2085,43 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + text-table@0.2.0: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + ts-api-utils@1.3.0(typescript@5.5.3): + dependencies: + typescript: 5.5.3 + tslib@2.6.3: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@7.16.1(eslint@9.7.0)(typescript@5.5.3): + dependencies: + '@typescript-eslint/eslint-plugin': 7.16.1(@typescript-eslint/parser@7.16.1(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3) + '@typescript-eslint/parser': 7.16.1(eslint@9.7.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.16.1(eslint@9.7.0)(typescript@5.5.3) + eslint: 9.7.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + typescript@5.5.3: {} + unicorn-magic@0.1.0: {} universalify@2.0.1: {} + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + vite-plugin-elm@3.0.1(vite@5.3.3(terser@5.31.2)): dependencies: acorn: 8.12.1 @@ -1394,6 +2164,10 @@ snapshots: dependencies: isexe: 2.0.0 + word-wrap@1.2.5: {} + wrappy@1.0.2: {} + yocto-queue@0.1.0: {} + yocto-queue@1.1.1: {} diff --git a/src/Feed.elm b/src/Feed.elm index 6f9cbce7e3a..ce3552a3bc5 100644 --- a/src/Feed.elm +++ b/src/Feed.elm @@ -1,4 +1,4 @@ -module Feed exposing (Feed, Meta, Preset, decoder) +module Feed exposing (Feed, Meta, decoder) import EventCore as Event exposing (Event) import Json.Decode as D @@ -18,14 +18,6 @@ type alias Meta = } -type alias Preset = - { id : String - , title : String - , lang : String - , icon : String - } - - decoder : D.Decoder Feed decoder = D.map3 Feed diff --git a/src/Filter.elm b/src/Filter.elm index d2f5385229f..fef79c88c87 100644 --- a/src/Filter.elm +++ b/src/Filter.elm @@ -10,6 +10,7 @@ module Filter exposing ) import Feed +import InteropDefinitions exposing (PresetFeedMeta) import List.Extra as List import Url.Builder @@ -17,7 +18,7 @@ import Url.Builder type alias Feed = { checked : Bool , alternate : String - , preset : Feed.Preset + , preset : PresetFeedMeta } diff --git a/src/InteropDefinitions.elm b/src/InteropDefinitions.elm new file mode 100644 index 00000000000..2f09196ff31 --- /dev/null +++ b/src/InteropDefinitions.elm @@ -0,0 +1,139 @@ +module InteropDefinitions exposing (Features, Flags, FromElm(..), PresetFeedMeta, ShareData, ToElm(..), interop) + +import Json.Decode +import TsJson.Decode as D exposing (Decoder) +import TsJson.Encode as E exposing (Encoder) + + +interop : + { toElm : Decoder ToElm + , fromElm : Encoder FromElm + , flags : Decoder Flags + } +interop = + { toElm = toElm + , fromElm = fromElm + , flags = flagsDecoder + } + + +type FromElm + = Close String + | Copy String + | PreventScrollFocus String + | RemoveScrollEventListener + | SetLang String + | Share ShareData + | ShowModal String + | SlideViewportInto String + + +type alias ShareData = + { title : String + , url : Maybe String + } + + +type ToElm + = OnScrollToBottom + + +type alias Flags = + { features : Features + , languages : List String + , feeds : List PresetFeedMeta + , observances : Json.Decode.Value + } + + +type alias Features = + { copy : Bool + , share : Bool + } + + +type alias PresetFeedMeta = + { id : String + , title : String + , lang : String + , icon : String + } + + +fromElm : Encoder FromElm +fromElm = + E.union + (\vClose vCopy vPreventScrollFocus vRemoveScrollEventListener vSetLang vShare vShowModal vSlideViewportInto value -> + case value of + Close id -> + vClose id + + Copy text -> + vCopy text + + PreventScrollFocus id -> + vPreventScrollFocus id + + RemoveScrollEventListener -> + vRemoveScrollEventListener + + SetLang lang -> + vSetLang lang + + Share data -> + vShare data + + ShowModal id -> + vShowModal id + + SlideViewportInto id -> + vSlideViewportInto id + ) + |> E.variantTagged "close" E.string + |> E.variantTagged "copy" E.string + |> E.variantTagged "preventScrollFocus" E.string + |> E.variant0 "removeScrollEventListener" + |> E.variantTagged "setLang" E.string + |> E.variantTagged "share" + (E.object + [ E.required "title" .title E.string + , E.optional "url" .url E.string + ] + ) + |> E.variantTagged "showModal" E.string + |> E.variantTagged "slideViewportInto" E.string + |> E.buildUnion + + +toElm : Decoder ToElm +toElm = + D.discriminatedUnion "tag" + [ ( "OnScrollToBottom" + , D.succeed OnScrollToBottom + ) + ] + + +flagsDecoder : Decoder Flags +flagsDecoder = + D.map4 Flags + (D.field "features" <| featuresDecoder) + (D.field "languages" <| D.list <| D.string) + (D.field "feeds" <| D.list <| presetFeedMetaDecoder) + (D.field "observances" D.value) + + +featuresDecoder : Decoder Features +featuresDecoder = + D.map2 Features + (D.field "copy" D.bool) + (D.field "share" D.bool) + + +presetFeedMetaDecoder : Decoder PresetFeedMeta +presetFeedMetaDecoder = + D.map4 PresetFeedMeta + (D.field "id" D.string) + (D.field "title" D.string) + (D.field "lang" D.string) + (D.field "icon" D.string) diff --git a/src/InteropPorts.elm b/src/InteropPorts.elm new file mode 100644 index 00000000000..eafefa7294c --- /dev/null +++ b/src/InteropPorts.elm @@ -0,0 +1,49 @@ +port module InteropPorts exposing (fromElm, toElm, decodeFlags) + +{-| This file was generated by elm-ts-interop init. You can manually edit this file, +just avoid exposing the low-level ports to ensure that only the FromElm and ToElm types +can be sent/received by your ports. + +@docs fromElm, toElm, decodeFlags + +-} + +import InteropDefinitions +import Json.Decode +import Json.Encode +import TsJson.Decode as TsDecode +import TsJson.Encode as TsEncode + + +{-| -} +fromElm : InteropDefinitions.FromElm -> Cmd msg +fromElm value = + value + |> (InteropDefinitions.interop.fromElm |> TsEncode.encoder) + |> interopFromElm + + +{-| -} +toElm : Sub (Result Json.Decode.Error InteropDefinitions.ToElm) +toElm = + (InteropDefinitions.interop.toElm |> TsDecode.decoder) + |> Json.Decode.decodeValue + |> interopToElm + + +{-| -} +decodeFlags : Json.Decode.Value -> Result Json.Decode.Error InteropDefinitions.Flags +decodeFlags flags = + Json.Decode.decodeValue + (InteropDefinitions.interop.flags |> TsDecode.decoder) + flags + + + +-- internals - do not expose + + +port interopFromElm : Json.Encode.Value -> Cmd msg + + +port interopToElm : (Json.Decode.Value -> msg) -> Sub msg diff --git a/src/Main.elm b/src/Main.elm index f819e9b8806..b1afec2075f 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,4 +1,4 @@ -port module Main exposing (main) +module Main exposing (main) import Attributes exposing (..) import Browser exposing (Document) @@ -19,7 +19,10 @@ import Html.Lazy exposing (..) import Http import I18Next exposing (Translations, translationsDecoder) import Icon +import InteropDefinitions as ID exposing (Features, Flags, ShareData) +import InteropPorts as IP exposing (decodeFlags) import Json.Decode as D +import Json.Encode as E import KeyboardEvent import List.Extra as List import Markdown @@ -46,7 +49,7 @@ import Util.List as List import Util.String as String -main : Program Flags Model Msg +main : Program D.Value Model Msg main = Browser.application { init = init @@ -59,43 +62,6 @@ main = --- PORTS - - -port setLang : String -> Cmd msg - - -port preventScrollFocus : String -> Cmd msg - - -port slideViewportInto : String -> Cmd msg - - -port showModal : String -> Cmd msg - - -port close : String -> Cmd msg - - -port copy : String -> Cmd msg - - -port share : ShareData -> Cmd msg - - -port onScrollToBottom : (D.Value -> msg) -> Sub msg - - -port removeScrollEventListener : () -> Cmd msg - - -type alias ShareData = - { title : String - , url : Maybe String - } - - - -- MODEL @@ -180,17 +146,17 @@ type Error | Unexpected String -type alias Flags = - { features : Features - , languages : List String - , feeds : List Feed.Preset - , observances : D.Value - } +init : D.Value -> Url -> Nav.Key -> ( Model, Cmd Msg ) +init flagsJson url key = + let + flags = + case decodeFlags flagsJson of + Err _ -> + Flags (Features False False) [] [] (E.list E.object []) + Ok f -> + f -init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg ) -init flags url key = - let query = Query.parseUrl url @@ -457,7 +423,9 @@ update msg model = env = model.env in - ( { model | env = { env | translations = translations } }, setLang lang ) + ( { model | env = { env | translations = translations } } + , ID.SetLang lang |> IP.fromElm + ) Err err -> model |> update (ReportError (TranslationsHttpError lang err)) @@ -544,8 +512,8 @@ update msg model = , Cmd.batch [ -- Set `preventScroll` to avoid halting the sliding -- animation. - preventScrollFocus nowSectionId - , slideViewportInto nowSectionId + ID.PreventScrollFocus nowSectionId |> IP.fromElm + , ID.SlideViewportInto nowSectionId |> IP.fromElm ] ) @@ -637,7 +605,7 @@ update msg model = ( About _, None ) -> Cmd.batch - [ close aboutDialogId + [ ID.Close aboutDialogId |> IP.fromElm , Dom.focus aboutButtonId |> Task.attempt handleDomResult ] @@ -651,23 +619,23 @@ update msg model = Cmd.none About _ -> - close aboutDialogId + ID.Close aboutDialogId |> IP.fromElm Help -> - close helpDialogId + ID.Close helpDialogId |> IP.fromElm , case mode of None -> Cmd.none About _ -> Cmd.batch - [ showModal aboutDialogId + [ ID.ShowModal aboutDialogId |> IP.fromElm , Dom.focus aboutCloseButtonId |> Task.attempt handleDomResult ] Help -> Cmd.batch - [ showModal helpDialogId + [ ID.ShowModal helpDialogId |> IP.fromElm , Dom.focus helpCloseButtonId |> Task.attempt handleDomResult ] ] @@ -788,7 +756,7 @@ update msg model = [] Nothing -> - [ removeScrollEventListener () ] + [ ID.RemoveScrollEventListener |> IP.fromElm ] cmds2 = if @@ -821,7 +789,7 @@ update msg model = -- is large enough to contain current time. If KemoV becomes big enough to fill up -- the first page with upcoming streams in the future, then we should revisit this! ( { model2 | initialized = True } - , Cmd.batch [ cmd, slideViewportInto nowSectionId ] + , Cmd.batch [ cmd, ID.SlideViewportInto nowSectionId |> IP.fromElm ] ) RetryGetFeed url -> @@ -842,10 +810,10 @@ update msg model = } Copy text -> - ( model, copy text ) + ( model, ID.Copy text |> IP.fromElm ) Share title url -> - ( model, share <| ShareData title url ) + ( model, ShareData title url |> ID.Share |> IP.fromElm ) NoOp -> ( model, Cmd.none ) @@ -936,7 +904,18 @@ subscriptions model = subs2 = case model.pendingFeed of OneMore url -> - onScrollToBottom (always <| GetFeed url) :: subs + (IP.toElm + |> Sub.map + (\result -> + case result of + Ok ID.OnScrollToBottom -> + GetFeed url + + Err e -> + ReportError <| Unexpected <| D.errorToString e + ) + ) + :: subs _ -> subs diff --git a/src/Main.elm.d.ts b/src/Main.elm.d.ts new file mode 100644 index 00000000000..7f38ba25622 --- /dev/null +++ b/src/Main.elm.d.ts @@ -0,0 +1,45 @@ +export type JsonObject = { [Key in string]?: JsonValue }; +export type JsonArray = JsonValue[]; + +/** +Matches any valid JSON value. +Source: https://github.com/sindresorhus/type-fest/blob/master/source/basic.d.ts +*/ +export type JsonValue = + | string + | number + | boolean + | null + | JsonObject + | JsonArray; + +export interface ElmApp { + ports: { + interopFromElm: PortFromElm; + interopToElm: PortToElm; + [key: string]: UnknownPort; + }; +} + +export type FromElm = { data : string; tag : "slideViewportInto" } | { data : string; tag : "showModal" } | { data : { title : string; url? : string }; tag : "share" } | { data : string; tag : "setLang" } | { tag : "removeScrollEventListener" } | { data : string; tag : "preventScrollFocus" } | { data : string; tag : "copy" } | { data : string; tag : "close" }; + +export type ToElm = { tag : "OnScrollToBottom" }; + +export type Flags = ({ features : { copy : boolean; share : boolean } } & { languages : string[] } & { feeds : ({ id : string } & { title : string } & { lang : string } & { icon : string })[] } & { observances : JsonValue }); + +export namespace Main { + function init(options: { node?: HTMLElement | null; flags: Flags }): ElmApp; +} + +export as namespace Elm; + +export { Elm }; + +export type UnknownPort = PortFromElm | PortToElm | undefined; + +export type PortFromElm = { + subscribe(callback: (fromElm: Data) => void): void; + unsubscribe(callback: (fromElm: Data) => void): void; +}; + +export type PortToElm = { send(data: Data): void }; diff --git a/src/TranslationsExt.elm b/src/TranslationsExt.elm index 0ce4de53f8e..de8c9f6e4e4 100644 --- a/src/TranslationsExt.elm +++ b/src/TranslationsExt.elm @@ -1,11 +1,11 @@ module TranslationsExt exposing (members) -import Feed import I18Next exposing (Translations) +import InteropDefinitions exposing (PresetFeedMeta) import Translations.Event.Description as TED -members : Translations -> Feed.Preset -> List Feed.Preset -> String +members : Translations -> PresetFeedMeta -> List PresetFeedMeta -> String members translations author guests = case guests of [] -> diff --git a/src/customElements.js b/src/customElements.ts similarity index 84% rename from src/customElements.js rename to src/customElements.ts index 66ba7e335c3..8d1e49ae49c 100644 --- a/src/customElements.js +++ b/src/customElements.ts @@ -23,23 +23,25 @@ customElements.define('intl-date', class extends HTMLElement { return; } - const year = this.dataset.year; - const month = this.dataset.month; - const day = this.dataset.day; - const date = new Date(year, month, day); + // The custom element is only created by `Elements.elm`, which always sets these properties. + const year = this.dataset.year!; + const month = this.dataset.month!; + const day = this.dataset.day!; + const date = new Date(+year, +month, +day); const lang = this.getAttribute('lang'); - const opts = { + const opts: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric', weekday: 'short', }; - if (new Date().getYear() != date.getYear()) { + if (new Date().getFullYear() != date.getFullYear()) { opts.year = 'numeric'; } const fmt = new Intl.DateTimeFormat(lang ?? 'default', opts); - const time = this.firstChild; + // The only child of the custom element is `