From a902c454e38111dd88a2cb6f32f18d478b51ee9c Mon Sep 17 00:00:00 2001 From: Evan Charlton Date: Sun, 22 Sep 2024 16:18:56 +0200 Subject: [PATCH] chore: Setup OpenTofu deployment --- .github/workflows/deploy.yml | 27 +++++++-- .gitignore | 5 ++ ordle.tf | 110 +++++++++++++++++++++++++++++++++++ terraform.tfstate | 1 + tsconfig.app.tsbuildinfo | 2 +- 5 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 ordle.tf create mode 100644 terraform.tfstate diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8bc64ee..559aef4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,24 +4,41 @@ jobs: build-and-deploy: runs-on: ubuntu-latest steps: + - uses: opentofu/setup-opentofu@v1 - name: Checkout 🛎️ uses: actions/checkout@v2.3.1 with: persist-credentials: false - name: Install + env: + PUBLIC_URL: / run: | npm ci + - name: "Set environment variables" run: | echo "VITE_RELEASE=${GITHUB_SHA:0:8}" >> $GITHUB_ENV - name: Build run: | npm run build + echo "ordle-app.no" > dist/CNAME + + - run: tofu init + - id: plan + run: tofu plan -no-color + env: + TF_VAR_cloudflare_api_token: ${{ secrets.TF_VAR_cloudflare_api_token }} + - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@3.7.1 + uses: JamesIves/github-pages-deploy-action@v4 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: dist # The folder the action should deploy. - CLEAN: true # Automatically remove deleted files from the deploy branch + branch: gh-pages # The branch the action should deploy to. + folder: dist # The folder the action should deploy. + clean: true # Automatically remove deleted files from the deploy branch + + - run: | + tofu state rm cloudflare_zone_settings_override.ssl_settings + tofu apply -auto-approve + env: + TF_VAR_cloudflare_api_token: ${{ secrets.TF_VAR_cloudflare_api_token }} diff --git a/.gitignore b/.gitignore index a547bf3..81a7b30 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ dist-ssr *.njsproj *.sln *.sw? + +# Terraform/Tofu stuff +.terraform +.terraform.lock.hcl +terraform.*.backup diff --git a/ordle.tf b/ordle.tf new file mode 100644 index 0000000..fea3c82 --- /dev/null +++ b/ordle.tf @@ -0,0 +1,110 @@ +terraform { + required_providers { + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 4" + } + } +} + +variable "cloudflare_api_token" { + default = "" +} + +locals { + zones = { + "ordle-app.no" = "639b49d65fe403a6d1bdc89b416f7619" + } +} + +provider "cloudflare" { + api_token = var.cloudflare_api_token +} + +resource "cloudflare_record" "a_records" { + for_each = { + for val in setproduct( + toset(["ordle-app.no"]), + [ + "185.199.111.153", + "185.199.110.153", + "185.199.109.153", + "185.199.108.153" + ] + ) : "${val[0]}-${val[1]}" => { + domain = val[0] + ip = val[1] + } + } + zone_id = local.zones[each.value.domain] + content = each.value.ip + name = each.value.domain + proxied = true + ttl = 1 + type = "A" +} + +resource "cloudflare_record" "aaaa_records" { + for_each = { + for val in setproduct( + toset(["ordle-app.no"]), + [ + "2606:50c0:8003::153", + "2606:50c0:8002::153", + "2606:50c0:8001::153", + "2606:50c0:8000::153" + ] + ) : "${val[0]}-${val[1]}" => { + domain = val[0] + ip = val[1] + } + } + zone_id = local.zones[each.value.domain] + content = each.value.ip + name = each.value.domain + proxied = true + ttl = 1 + type = "AAAA" +} + +resource "cloudflare_record" "txt_records_no" { + for_each = { + # Tell recipients that this domain will never send email + "_dmarc" = "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s;", + "*._domainkey" = "v=DKIM1; p=", + "ordle-app.no" = "v=spf1 -all", + "_github-pages-challenge-evancharlton" = "ee90a25383ffe6a653b8c834a4b3c5", + } + zone_id = local.zones["ordle-app.no"] + name = each.key + content = each.value + proxied = false + ttl = 1 + type = "TXT" +} + +resource "cloudflare_record" "cname_no" { + for_each = { + "www" = "ordle-app.no" + } + zone_id = local.zones["ordle-app.no"] + name = each.key + content = each.value + proxied = true + ttl = 1 + type = "CNAME" +} + +# NOTE: There's a bug in Cloudflare somewhere with this. If you run into +# problems, try this: +# tofu state rm cloudflare_zone_settings_override.ssl_settings +# +# https://github.com/cloudflare/terraform-provider-cloudflare/issues/1297 +resource "cloudflare_zone_settings_override" "ssl_settings" { + zone_id = local.zones["ordle-app.no"] + + settings { + automatic_https_rewrites = "on" + ssl = "strict" + } +} \ No newline at end of file diff --git a/terraform.tfstate b/terraform.tfstate new file mode 100644 index 0000000..4518094 --- /dev/null +++ b/terraform.tfstate @@ -0,0 +1 @@ +{"version":4,"terraform_version":"1.8.2","serial":2,"lineage":"a9c6f4f2-ceb5-80b9-f519-53d4ff8ee3b6","outputs":{},"resources":[{"mode":"managed","type":"cloudflare_record","name":"a_records","provider":"provider[\"registry.opentofu.org/cloudflare/cloudflare\"]","instances":[{"index_key":"ordle-app.no-185.199.108.153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"185.199.108.153","created_on":"2024-09-22T13:42:06.554405Z","data":[],"hostname":"ordle-app.no","id":"d355008e36df3989032362ffc3417070","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:06.554405Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"A","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no-185.199.109.153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"185.199.109.153","created_on":"2024-09-22T13:42:07.119418Z","data":[],"hostname":"ordle-app.no","id":"f3777a76dd2d6a7b400bcb904a551748","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:07.119418Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"A","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no-185.199.110.153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"185.199.110.153","created_on":"2024-09-22T13:42:08.374552Z","data":[],"hostname":"ordle-app.no","id":"6ef6dccb4789596a82f7551f41c7a2e1","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:08.374552Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"A","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no-185.199.111.153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"185.199.111.153","created_on":"2024-09-22T13:42:08.110889Z","data":[],"hostname":"ordle-app.no","id":"c75024f5ecf5b0a227cb58130cdce0e6","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:08.110889Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"A","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"}]},{"mode":"managed","type":"cloudflare_record","name":"aaaa_records","provider":"provider[\"registry.opentofu.org/cloudflare/cloudflare\"]","instances":[{"index_key":"ordle-app.no-2606:50c0:8000::153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"2606:50c0:8000::153","created_on":"2024-09-22T13:42:11.11102Z","data":[],"hostname":"ordle-app.no","id":"aa315157cf917ad339ae2281ed73d817","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:11.11102Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"AAAA","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no-2606:50c0:8001::153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"2606:50c0:8001::153","created_on":"2024-09-22T13:42:12.133679Z","data":[],"hostname":"ordle-app.no","id":"785c0059aeb97b409d832cb4edf72f46","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:12.133679Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"AAAA","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no-2606:50c0:8002::153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"2606:50c0:8002::153","created_on":"2024-09-22T13:42:11.62291Z","data":[],"hostname":"ordle-app.no","id":"f243eb4fd93729d6551256364b82bb74","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:11.62291Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"AAAA","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no-2606:50c0:8003::153","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"2606:50c0:8003::153","created_on":"2024-09-22T13:42:07.883667Z","data":[],"hostname":"ordle-app.no","id":"58d764bb7532ecc8d751f3649c701262","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:07.883667Z","name":"ordle-app.no","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"AAAA","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"}]},{"mode":"managed","type":"cloudflare_record","name":"cname_no","provider":"provider[\"registry.opentofu.org/cloudflare/cloudflare\"]","instances":[{"index_key":"www","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"ordle-app.no","created_on":"2024-09-22T13:42:06.605132Z","data":[],"hostname":"www.ordle-app.no","id":"3233819d069a9d45e570f088935a3910","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:06.605132Z","name":"www","priority":null,"proxiable":true,"proxied":true,"tags":[],"timeouts":null,"ttl":1,"type":"CNAME","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"}]},{"mode":"managed","type":"cloudflare_record","name":"txt_records_no","provider":"provider[\"registry.opentofu.org/cloudflare/cloudflare\"]","instances":[{"index_key":"*._domainkey","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"v=DKIM1; p=","created_on":"2024-09-22T13:42:11.866724Z","data":[],"hostname":"*._domainkey.ordle-app.no","id":"e4fbb3996faf9dcb0949a1014f104b58","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:11.866724Z","name":"*._domainkey","priority":null,"proxiable":false,"proxied":false,"tags":[],"timeouts":null,"ttl":1,"type":"TXT","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"_dmarc","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s;","created_on":"2024-09-22T13:42:06.88314Z","data":[],"hostname":"_dmarc.ordle-app.no","id":"247821a69ae04f3386e7b5d8a104b3d2","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:06.88314Z","name":"_dmarc","priority":null,"proxiable":false,"proxied":false,"tags":[],"timeouts":null,"ttl":1,"type":"TXT","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"_github-pages-challenge-evancharlton","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"ee90a25383ffe6a653b8c834a4b3c5","created_on":"2024-09-22T13:42:07.659433Z","data":[],"hostname":"_github-pages-challenge-evancharlton.ordle-app.no","id":"c0f28db2fe64efa9a814c7fc0849f0ca","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:07.659433Z","name":"_github-pages-challenge-evancharlton","priority":null,"proxiable":false,"proxied":false,"tags":[],"timeouts":null,"ttl":1,"type":"TXT","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"},{"index_key":"ordle-app.no","schema_version":3,"attributes":{"allow_overwrite":false,"comment":"","content":"v=spf1 -all","created_on":"2024-09-22T13:42:07.35982Z","data":[],"hostname":"ordle-app.no","id":"c1fa6536d473c866190ae614be7ced4f","metadata":{"auto_added":"false","managed_by_apps":"false","managed_by_argo_tunnel":"false"},"modified_on":"2024-09-22T13:42:07.35982Z","name":"ordle-app.no","priority":null,"proxiable":false,"proxied":false,"tags":[],"timeouts":null,"ttl":1,"type":"TXT","value":null,"zone_id":"639b49d65fe403a6d1bdc89b416f7619"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMyJ9"}]},{"mode":"managed","type":"cloudflare_zone_settings_override","name":"ssl_settings","provider":"provider[\"registry.opentofu.org/cloudflare/cloudflare\"]","instances":[{"schema_version":2,"attributes":{"id":"639b49d65fe403a6d1bdc89b416f7619","initial_settings":[{"always_online":"off","always_use_https":"off","automatic_https_rewrites":"on","binary_ast":"off","brotli":"on","browser_cache_ttl":14400,"browser_check":"on","cache_level":"aggressive","challenge_ttl":1800,"ciphers":[],"cname_flattening":"flatten_at_root","development_mode":"off","early_hints":"off","email_obfuscation":"on","filter_logs_to_cloudflare":"off","fonts":"off","h2_prioritization":"off","hotlink_protection":"off","http2":"on","http3":"on","image_resizing":"off","ip_geolocation":"on","ipv6":"on","log_to_cloudflare":"on","max_upload":100,"min_tls_version":"1.0","minify":[],"mirage":"off","mobile_redirect":[],"nel":[{"enabled":true}],"opportunistic_encryption":"on","opportunistic_onion":"on","orange_to_orange":"off","origin_error_page_pass_thru":"off","origin_max_http_version":"2","polish":"off","prefetch_preload":"off","privacy_pass":"on","proxy_read_timeout":"100","pseudo_ipv4":"off","replace_insecure_js":"on","response_buffering":"off","rocket_loader":"off","security_header":[{"enabled":false,"include_subdomains":false,"max_age":0,"nosniff":false,"preload":false}],"security_level":"medium","server_side_exclude":"on","sort_query_string_for_cache":"off","ssl":"full","tls_1_2_only":"off","tls_1_3":"on","tls_client_auth":"off","true_client_ip_header":"off","universal_ssl":"","visitor_ip":"on","waf":"off","webp":"off","websockets":"on","zero_rtt":"off"}],"initial_settings_read_at":"2024-09-22T13:42:17.423410222Z","readonly_settings":["advanced_ddos","http2","long_lived_grpc","mirage","origin_error_page_pass_thru","polish","prefetch_preload","proxy_read_timeout","response_buffering","sort_query_string_for_cache","true_client_ip_header","webp","image_resizing"],"settings":[{"always_online":"off","always_use_https":"off","automatic_https_rewrites":"on","binary_ast":"off","brotli":"on","browser_cache_ttl":14400,"browser_check":"on","cache_level":"aggressive","challenge_ttl":1800,"ciphers":[],"cname_flattening":"flatten_at_root","development_mode":"off","early_hints":"off","email_obfuscation":"on","filter_logs_to_cloudflare":"off","fonts":"off","h2_prioritization":"off","hotlink_protection":"off","http2":"on","http3":"on","image_resizing":"off","ip_geolocation":"on","ipv6":"on","log_to_cloudflare":"on","max_upload":100,"min_tls_version":"1.0","minify":[],"mirage":"off","mobile_redirect":[],"nel":[{"enabled":true}],"opportunistic_encryption":"on","opportunistic_onion":"on","orange_to_orange":"off","origin_error_page_pass_thru":"off","origin_max_http_version":"2","polish":"off","prefetch_preload":"off","privacy_pass":"on","proxy_read_timeout":"100","pseudo_ipv4":"off","replace_insecure_js":"on","response_buffering":"off","rocket_loader":"off","security_header":[{"enabled":false,"include_subdomains":false,"max_age":0,"nosniff":false,"preload":false}],"security_level":"medium","server_side_exclude":"on","sort_query_string_for_cache":"off","ssl":"strict","tls_1_2_only":"off","tls_1_3":"on","tls_client_auth":"off","true_client_ip_header":"off","universal_ssl":"","visitor_ip":"on","waf":"off","webp":"","websockets":"on","zero_rtt":"off"}],"zone_id":"639b49d65fe403a6d1bdc89b416f7619","zone_status":"pending","zone_type":"full"},"sensitive_attributes":[],"private":"eyJzY2hlbWFfdmVyc2lvbiI6IjIifQ=="}]}],"check_results":null} diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo index 27807a2..c430179 100644 --- a/tsconfig.app.tsbuildinfo +++ b/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/custom-events.ts","./src/index.tsx","./src/main.tsx","./src/service-worker.ts","./src/serviceWorkerRegistration.ts","./src/setupTests.ts","./src/storage.ts","./src/vite-env.d.ts","./src/Accordion/Accordion.tsx","./src/Accordion/context.ts","./src/Accordion/index.ts","./src/Accordion/Segment/Segment.tsx","./src/Accordion/Segment/index.ts","./src/App/App.tsx","./src/App/index.ts","./src/App/ChooseLanguage/ChooseLanguage.tsx","./src/App/ChooseLanguage/index.ts","./src/App/LocalStorageUpgrader/LocalStorageUpgrader.tsx","./src/App/LocalStorageUpgrader/index.ts","./src/App/Setup/Setup.tsx","./src/App/Setup/index.ts","./src/App/Setup/DataLoader/DataLoader.tsx","./src/App/Setup/DataLoader/context.ts","./src/App/Setup/DataLoader/index.ts","./src/App/Setup/FirebaseSyncer/FirebaseSyncer.tsx","./src/App/Setup/FirebaseSyncer/InsertNewRemoteGuesses.tsx","./src/App/Setup/FirebaseSyncer/InsertSettings.tsx","./src/App/Setup/FirebaseSyncer/UploadInitialLocalStorage.tsx","./src/App/Setup/FirebaseSyncer/UploadNewLocalGuesses.tsx","./src/App/Setup/FirebaseSyncer/UploadNewLocalWords.tsx","./src/App/Setup/FirebaseSyncer/UploadSettings.tsx","./src/App/Setup/FirebaseSyncer/index.ts","./src/App/Setup/GameLoader/GameLoader.tsx","./src/App/Setup/GameLoader/context.ts","./src/App/Setup/GameLoader/index.ts","./src/App/Setup/LanguageOptions/LanguageOptions.tsx","./src/App/Setup/LanguageOptions/index.ts","./src/App/Setup/SettingsLoader/SettingsLoader.tsx","./src/App/Setup/SettingsLoader/index.ts","./src/App/Setup/StateLoader/StateLoader.tsx","./src/App/Setup/StateLoader/hooks.ts","./src/App/Setup/StateLoader/index.ts","./src/App/WithLanguage/WithLanguage.tsx","./src/App/WithLanguage/index.ts","./src/App/WithLanguage/Builder/Builder.tsx","./src/App/WithLanguage/Builder/index.ts","./src/App/WithLanguage/Builder/reducer.ts","./src/Dialog/Dialog.tsx","./src/Dialog/index.ts","./src/ErrorMessage/ErrorMessage.tsx","./src/ErrorMessage/hooks.ts","./src/ErrorMessage/index.ts","./src/Game/Game.tsx","./src/Game/control.ts","./src/Game/guess.ts","./src/Game/index.ts","./src/Game/Grid/Grid.tsx","./src/Game/Grid/index.ts","./src/Game/Grid/Guess/Guess.tsx","./src/Game/Grid/Guess/index.ts","./src/Game/Grid/Input/Input.tsx","./src/Game/Grid/Input/index.ts","./src/Game/Grid/Letter/Letter.tsx","./src/Game/Grid/Letter/index.ts","./src/Game/Grid/Remainder/Remainder.tsx","./src/Game/Grid/Remainder/index.ts","./src/Game/Grid/Remaining/Remaining.tsx","./src/Game/Grid/Remaining/index.ts","./src/Game/Header/Header.tsx","./src/Game/Header/index.ts","./src/Game/Header/Help/Help.tsx","./src/Game/Header/Help/index.ts","./src/Game/Header/Language/Language.tsx","./src/Game/Header/Language/index.ts","./src/Game/Header/Settings/Settings.tsx","./src/Game/Header/Settings/index.ts","./src/Game/Header/Title/Title.tsx","./src/Game/Header/Title/index.ts","./src/Game/Keyboard/Keyboard.tsx","./src/Game/Keyboard/context.ts","./src/Game/Keyboard/hooks.ts","./src/Game/Keyboard/index.ts","./src/Game/Keyboard/EndOverlay/EndOverlay.tsx","./src/Game/Keyboard/EndOverlay/index.ts","./src/Game/Keyboard/EndOverlay/ShareButton/ShareButton.tsx","./src/Game/Keyboard/EndOverlay/ShareButton/index.ts","./src/Game/Keyboard/Row/Row.tsx","./src/Game/Keyboard/Row/index.ts","./src/Game/Keyboard/Row/Delete/Delete.tsx","./src/Game/Keyboard/Row/Delete/index.ts","./src/Game/Keyboard/Row/Enter/Enter.tsx","./src/Game/Keyboard/Row/Enter/index.ts","./src/Game/Keyboard/Row/Key/Key.tsx","./src/Game/Keyboard/Row/Key/index.ts","./src/Loading/Loading.tsx","./src/Loading/index.ts","./src/Random/Random.tsx","./src/Random/index.ts","./src/SettingsDialog/SettingsDialog.tsx","./src/SettingsDialog/SettingsSegment.tsx","./src/SettingsDialog/UserSegment.tsx","./src/SettingsDialog/index.ts","./src/sync/firebase-import.ts","./src/sync/index.ts","./src/sync/useLogin.ts","./src/utils/index.ts","./src/utils/logic.test.ts","./src/utils/logic.ts","./src/utils/never.ts"],"errors":true,"version":"5.6.2"} \ No newline at end of file +{"root":["./src/custom-events.ts","./src/main.tsx","./src/service-worker.ts","./src/serviceWorkerRegistration.ts","./src/setupTests.ts","./src/storage.ts","./src/vite-env.d.ts","./src/Accordion/Accordion.tsx","./src/Accordion/context.ts","./src/Accordion/index.ts","./src/Accordion/Segment/Segment.tsx","./src/Accordion/Segment/index.ts","./src/App/App.tsx","./src/App/index.ts","./src/App/ChooseLanguage/ChooseLanguage.tsx","./src/App/ChooseLanguage/index.ts","./src/App/LocalStorageUpgrader/LocalStorageUpgrader.tsx","./src/App/LocalStorageUpgrader/index.ts","./src/App/Setup/Setup.tsx","./src/App/Setup/index.ts","./src/App/Setup/DataLoader/DataLoader.tsx","./src/App/Setup/DataLoader/context.ts","./src/App/Setup/DataLoader/index.ts","./src/App/Setup/FirebaseSyncer/FirebaseSyncer.tsx","./src/App/Setup/FirebaseSyncer/InsertNewRemoteGuesses.tsx","./src/App/Setup/FirebaseSyncer/InsertSettings.tsx","./src/App/Setup/FirebaseSyncer/UploadInitialLocalStorage.tsx","./src/App/Setup/FirebaseSyncer/UploadNewLocalGuesses.tsx","./src/App/Setup/FirebaseSyncer/UploadNewLocalWords.tsx","./src/App/Setup/FirebaseSyncer/UploadSettings.tsx","./src/App/Setup/FirebaseSyncer/index.ts","./src/App/Setup/GameLoader/GameLoader.tsx","./src/App/Setup/GameLoader/context.ts","./src/App/Setup/GameLoader/index.ts","./src/App/Setup/LanguageOptions/LanguageOptions.tsx","./src/App/Setup/LanguageOptions/index.ts","./src/App/Setup/SettingsLoader/SettingsLoader.tsx","./src/App/Setup/SettingsLoader/index.ts","./src/App/Setup/StateLoader/StateLoader.tsx","./src/App/Setup/StateLoader/hooks.ts","./src/App/Setup/StateLoader/index.ts","./src/App/WithLanguage/WithLanguage.tsx","./src/App/WithLanguage/index.ts","./src/App/WithLanguage/Builder/Builder.tsx","./src/App/WithLanguage/Builder/index.ts","./src/App/WithLanguage/Builder/reducer.ts","./src/Dialog/Dialog.tsx","./src/Dialog/index.ts","./src/ErrorMessage/ErrorMessage.tsx","./src/ErrorMessage/hooks.ts","./src/ErrorMessage/index.ts","./src/Game/Game.tsx","./src/Game/control.ts","./src/Game/guess.ts","./src/Game/index.ts","./src/Game/Grid/Grid.tsx","./src/Game/Grid/index.ts","./src/Game/Grid/Guess/Guess.tsx","./src/Game/Grid/Guess/index.ts","./src/Game/Grid/Input/Input.tsx","./src/Game/Grid/Input/index.ts","./src/Game/Grid/Letter/Letter.tsx","./src/Game/Grid/Letter/index.ts","./src/Game/Grid/Remainder/Remainder.tsx","./src/Game/Grid/Remainder/index.ts","./src/Game/Grid/Remaining/Remaining.tsx","./src/Game/Grid/Remaining/index.ts","./src/Game/Header/Header.tsx","./src/Game/Header/index.ts","./src/Game/Header/Help/Help.tsx","./src/Game/Header/Help/index.ts","./src/Game/Header/Language/Language.tsx","./src/Game/Header/Language/index.ts","./src/Game/Header/Settings/Settings.tsx","./src/Game/Header/Settings/index.ts","./src/Game/Header/Title/Title.tsx","./src/Game/Header/Title/index.ts","./src/Game/Keyboard/Keyboard.tsx","./src/Game/Keyboard/context.ts","./src/Game/Keyboard/hooks.ts","./src/Game/Keyboard/index.ts","./src/Game/Keyboard/EndOverlay/EndOverlay.tsx","./src/Game/Keyboard/EndOverlay/index.ts","./src/Game/Keyboard/EndOverlay/ShareButton/ShareButton.tsx","./src/Game/Keyboard/EndOverlay/ShareButton/index.ts","./src/Game/Keyboard/Row/Row.tsx","./src/Game/Keyboard/Row/index.ts","./src/Game/Keyboard/Row/Delete/Delete.tsx","./src/Game/Keyboard/Row/Delete/index.ts","./src/Game/Keyboard/Row/Enter/Enter.tsx","./src/Game/Keyboard/Row/Enter/index.ts","./src/Game/Keyboard/Row/Key/Key.tsx","./src/Game/Keyboard/Row/Key/index.ts","./src/Loading/Loading.tsx","./src/Loading/index.ts","./src/Random/Random.tsx","./src/Random/index.ts","./src/SettingsDialog/SettingsDialog.tsx","./src/SettingsDialog/SettingsSegment.tsx","./src/SettingsDialog/UserSegment.tsx","./src/SettingsDialog/index.ts","./src/sync/firebase-import.ts","./src/sync/index.ts","./src/sync/useLogin.ts","./src/utils/index.ts","./src/utils/logic.test.ts","./src/utils/logic.ts","./src/utils/never.ts"],"version":"5.6.2"} \ No newline at end of file