From 190ade4ea626c90df0d2471f6b5440bad8dbd9f1 Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Sat, 6 Dec 2025 15:50:38 +0000 Subject: [PATCH 01/26] CAP WIP --- edge-apps/cap-alerting/.gitignore | 7 + edge-apps/cap-alerting/.prettierrc | 6 + edge-apps/cap-alerting/README.md | 108 ++++ edge-apps/cap-alerting/bun.lock | 572 ++++++++++++++++++ edge-apps/cap-alerting/eslint.config.ts | 18 + edge-apps/cap-alerting/index.html | 15 + edge-apps/cap-alerting/index.ts | 274 +++++++++ edge-apps/cap-alerting/package.json | 42 ++ edge-apps/cap-alerting/screenly.yml | 58 ++ edge-apps/cap-alerting/screenly_qc.yml | 58 ++ edge-apps/cap-alerting/static/cap-icon.svg | 5 + .../cap-alerting/static/demo-extreme.cap | 53 ++ edge-apps/cap-alerting/static/demo-severe.cap | 46 ++ edge-apps/cap-alerting/static/demo.cap | 51 ++ edge-apps/cap-alerting/static/style.css | 137 +++++ edge-apps/cap-alerting/static/test.cap | 24 + edge-apps/cap-alerting/tsconfig.json | 20 + edge-apps/cap-alerting/vite.config.ts | 26 + 18 files changed, 1520 insertions(+) create mode 100644 edge-apps/cap-alerting/.gitignore create mode 100644 edge-apps/cap-alerting/.prettierrc create mode 100644 edge-apps/cap-alerting/README.md create mode 100644 edge-apps/cap-alerting/bun.lock create mode 100644 edge-apps/cap-alerting/eslint.config.ts create mode 100644 edge-apps/cap-alerting/index.html create mode 100644 edge-apps/cap-alerting/index.ts create mode 100644 edge-apps/cap-alerting/package.json create mode 100644 edge-apps/cap-alerting/screenly.yml create mode 100644 edge-apps/cap-alerting/screenly_qc.yml create mode 100644 edge-apps/cap-alerting/static/cap-icon.svg create mode 100644 edge-apps/cap-alerting/static/demo-extreme.cap create mode 100644 edge-apps/cap-alerting/static/demo-severe.cap create mode 100644 edge-apps/cap-alerting/static/demo.cap create mode 100644 edge-apps/cap-alerting/static/style.css create mode 100644 edge-apps/cap-alerting/static/test.cap create mode 100644 edge-apps/cap-alerting/tsconfig.json create mode 100644 edge-apps/cap-alerting/vite.config.ts diff --git a/edge-apps/cap-alerting/.gitignore b/edge-apps/cap-alerting/.gitignore new file mode 100644 index 000000000..74287eb80 --- /dev/null +++ b/edge-apps/cap-alerting/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +*.log +.DS_Store +mock-data.yml +instance.yml +bun.lockb diff --git a/edge-apps/cap-alerting/.prettierrc b/edge-apps/cap-alerting/.prettierrc new file mode 100644 index 000000000..2924079e5 --- /dev/null +++ b/edge-apps/cap-alerting/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 100, + "trailingComma": "es5" +} diff --git a/edge-apps/cap-alerting/README.md b/edge-apps/cap-alerting/README.md new file mode 100644 index 000000000..8d732afa7 --- /dev/null +++ b/edge-apps/cap-alerting/README.md @@ -0,0 +1,108 @@ +# CAP Alerting Edge App + +Display Common Alerting Protocol (CAP) emergency alerts on Screenly digital signage screens. + +## Features + +- **Full CAP Support**: Parse and display CAP XML alerts with all essential fields including identifier, sender, event type, urgency, severity, certainty, headline, description, and instructions +- **Offline Operation**: Caches alerts in localStorage and continues operating when network is unavailable +- **Nearest Exit Integration**: Uses screen tags (e.g., `exit:North Lobby`) to provide location-aware exit directions +- **Flexible Settings**: Configure feed URL, refresh interval, language preferences, audio alerts, and more +- **Test Mode**: Built-in test mode with sample CAP file for demonstration and debugging + +## Settings + +- **CAP Feed URL**: URL or path to a CAP XML feed +- **Refresh Interval**: Time in minutes between feed updates (default: 5) +- **Default Language**: Preferred language when multiple info blocks exist (default: en) +- **Maximum Alerts**: Maximum number of alerts to display simultaneously (default: 3) +- **Play Audio Alerts**: Enable/disable audio playback from CAP resources +- **Offline Mode**: Use cached or bundled data instead of fetching from network +- **Test Mode**: Load bundled test CAP file for demonstration purposes +- **Demo Mode**: Enable demo mode with realistic CAP alerts (randomly selected) + +## Nearest Exit Tags + +To enable location-aware exit directions, assign tags to your Screenly screens using the format: + +- `exit:North Lobby` +- `exit-South Stairwell` + +The app will automatically inject the nearest exit information into alert instructions that contain the placeholder `{{closest_exit}}` or `[[closest_exit]]`. + +## Demo Mode + +The app includes three realistic demo scenarios that are randomly selected: + +- **Fire Drill**: Bilingual fire drill alert with evacuation instructions +- **Severe Weather**: Severe thunderstorm warning with radar imagery +- **Extreme Emergency**: Tornado emergency with multiple resources + +Demo mode automatically activates when: +- `demo_mode` is enabled +- Test mode is disabled +- No CAP feed URL is configured + +To test locally with demo mode: + +```bash +cd edge-apps/cap-alerting + +# Generate mock data (creates mock-data.yml) +bun run generate-mock-data + +# Edit mock-data.yml and add a tag like: +# tags: ["exit:North Stairwell"] + +# Run the app +bun run dev +``` + +Then access the app at the URL shown in the terminal and enable `demo_mode` in the settings. Each refresh will randomly show one of the three demo scenarios. + +## Testing + +Enable Test Mode in the app settings to load the bundled test CAP file (`static/test.cap`) instead of fetching from a live feed. This allows you to verify parsing, rendering, and nearest-exit substitution without network dependencies. + +## Development + +All commands should be run from within the app directory: + +```bash +cd edge-apps/cap-alerting +``` + +Install dependencies: + +```bash +bun install +``` + +Run tests: + +```bash +bun test # Run all tests +bun test --watch # Watch mode +``` + +Run in development mode: + +```bash +bun run dev +``` + +Build for production: + +```bash +bun run build +``` + +Deploy to Screenly: + +```bash +bun run deploy +``` + +## License + +MIT diff --git a/edge-apps/cap-alerting/bun.lock b/edge-apps/cap-alerting/bun.lock new file mode 100644 index 000000000..81f411473 --- /dev/null +++ b/edge-apps/cap-alerting/bun.lock @@ -0,0 +1,572 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "cap-alerting", + "dependencies": { + "@photostructure/tz-lookup": "^11.3.0", + "country-locale-map": "^1.9.11", + "fast-xml-parser": "^5.3.2", + "offline-geocode-city": "^1.0.2", + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "eslint": "^9.39.1", + "jiti": "^2.6.1", + "npm-run-all2": "^8.0.4", + "prettier": "^3.7.4", + "rimraf": "^6.1.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.48.1", + "vite": "^7.2.6", + "vite-plugin-static-copy": "^3.1.4", + }, + }, + }, + "packages": { + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], + + "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], + + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + + "@jsheaven/easybuild": ["@jsheaven/easybuild@1.2.9", "", { "dependencies": { "@jsheaven/status-message": "^1.1.2", "brotli-size": "^4.0.0", "dts-bundle-generator": "^7.2.0", "esbuild": "^0.17.6", "fast-glob": "^3.2.12", "gzip-size": "^7.0.0", "pretty-bytes": "^6.1.0", "typescript": "^4.9.5" }, "bin": { "easybuild": "dist/cli.esm.js", "easybuild-cjs": "dist/cli.iife.js" } }, "sha512-IJsaER05bGZKEuvBJ+JOQ7YW+RYHryYoO9z67TxpP7fAji8Oq+wJF8eFPEZabIcUbXqe20/Pfhx6P4g7SNP8kQ=="], + + "@jsheaven/status-message": ["@jsheaven/status-message@1.1.2", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-a9ye8kre8pBBtL7zKxDVoaVO+PJjnPLcim71IX0fVpV8OkBk6rvL97c2E8outOZgs3sKBqFfY44kx5wal3DRpA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@photostructure/tz-lookup": ["@photostructure/tz-lookup@11.3.0", "", {}, "sha512-rYGy7ETBHTnXrwbzm47e3LJPKJmzpY7zXnbZhdosNU0lTGWVqzxptSjK4qZkJ1G+Kwy4F6XStNR9ZqMsXAoASQ=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.48.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/type-utils": "8.48.1", "@typescript-eslint/utils": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.48.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.48.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.48.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.48.1", "@typescript-eslint/types": "^8.48.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.48.1", "", { "dependencies": { "@typescript-eslint/types": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1" } }, "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.48.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.48.1", "", { "dependencies": { "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1", "@typescript-eslint/utils": "8.48.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.48.1", "", {}, "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.48.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.48.1", "@typescript-eslint/tsconfig-utils": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.48.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.48.1", "", { "dependencies": { "@typescript-eslint/types": "8.48.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "brotli-size": ["brotli-size@4.0.0", "", { "dependencies": { "duplexer": "0.1.1" } }, "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "country-locale-map": ["country-locale-map@1.9.11", "", { "dependencies": { "fuzzball": "^2.1.2" } }, "sha512-Nrj31H/BmHFLzh2CYZkExQFUIZmqBSJ+nrdSRSjIqh4FMs6VRXOboDPIp7NqXBUoOTJi6Urf2cypPQez0rFYBQ=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csv-parse": ["csv-parse@5.6.0", "", {}, "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "dts-bundle-generator": ["dts-bundle-generator@7.2.0", "", { "dependencies": { "typescript": ">=4.5.2", "yargs": "^17.6.0" }, "bin": { "dts-bundle-generator": "dist/bin/dts-bundle-generator.js" } }, "sha512-pHjRo52hvvLDRijzIYRTS9eJR7vAOs3gd/7jx+7YVnLU8ay3yPUWGtHXPtuMBSlJYk/s4nq1SvXObDCZVguYMg=="], + + "duplexer": ["duplexer@0.1.1", "", {}, "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-xml-parser": ["fast-xml-parser@5.3.2", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "fuzzball": ["fuzzball@2.2.3", "", { "dependencies": { "heap": ">=0.2.0", "lodash": "^4.17.21", "setimmediate": "^1.0.5" } }, "sha512-sQDb3kjI7auA4YyE1YgEW85MTparcSgRgcCweUK06Cn0niY5lN+uhFiRUZKN4MQVGGiHxlbrYCA4nL1QjOXBLQ=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "gzip-size": ["gzip-size@7.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "heap": ["heap@0.2.7", "", {}, "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "", {}, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "long": ["long@3.2.0", "", {}, "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg=="], + + "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + + "lz-ts": ["lz-ts@1.1.2", "", {}, "sha512-ye8sVndmvzs46cPgX1Yjlk3o/Sueu0VHn253rKpsWiK2/bAbsVkD7DEJiaueiPfbZTi17GLRPkv3W5O3BUNd2g=="], + + "memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="], + + "npm-run-all2": ["npm-run-all2@8.0.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "picomatch": "^4.0.2", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA=="], + + "offline-geocode-city": ["offline-geocode-city@1.0.2", "", { "dependencies": { "@jsheaven/easybuild": "^1.2.9", "chokidar": "^3.5.3", "csv-parse": "^5.3.10", "lz-ts": "^1.1.2", "s2-geometry": "^1.2.10" } }, "sha512-6q9XvgYpvOr7kLzi/K2P1GZ36FajNHEI4cFphNcZ6tPxR0kBROzy6CorTn+yU7en3wrDkDTfcn1sPCAKA569xA=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], + + "pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="], + + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rimraf": ["rimraf@6.1.2", "", { "dependencies": { "glob": "^13.0.0", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g=="], + + "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "s2-geometry": ["s2-geometry@1.2.10", "", { "dependencies": { "long": "^3.2.0" } }, "sha512-5WejfQu1XZ25ZerW8uL6xP1sM2krcOYKhI6TbfybGRf+vTQLrm3E+4n0+1lWg+MYqFjPzoe51zKhn2sBRMCt5g=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.48.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.48.1", "@typescript-eslint/parser": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1", "@typescript-eslint/utils": "8.48.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "vite": ["vite@7.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ=="], + + "vite-plugin-static-copy": ["vite-plugin-static-copy@3.1.4", "", { "dependencies": { "chokidar": "^3.6.0", "p-map": "^7.0.3", "picocolors": "^1.1.1", "tinyglobby": "^0.2.15" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-iCmr4GSw4eSnaB+G8zc2f4dxSuDjbkjwpuBLLGvQYR9IW7rnDzftnUjOH5p4RYR+d4GsiBqXRvzuFhs5bnzVyw=="], + + "which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@jsheaven/easybuild/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="], + + "@jsheaven/easybuild/typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + + "gzip-size/duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "@jsheaven/easybuild/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="], + + "@jsheaven/easybuild/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="], + + "@jsheaven/easybuild/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="], + + "@jsheaven/easybuild/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="], + + "@jsheaven/easybuild/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="], + + "@jsheaven/easybuild/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="], + + "@jsheaven/easybuild/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="], + + "@jsheaven/easybuild/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="], + + "@jsheaven/easybuild/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="], + + "@jsheaven/easybuild/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="], + + "@jsheaven/easybuild/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="], + + "@jsheaven/easybuild/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="], + + "@jsheaven/easybuild/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="], + + "@jsheaven/easybuild/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + } +} diff --git a/edge-apps/cap-alerting/eslint.config.ts b/edge-apps/cap-alerting/eslint.config.ts new file mode 100644 index 000000000..24bc787e6 --- /dev/null +++ b/edge-apps/cap-alerting/eslint.config.ts @@ -0,0 +1,18 @@ +import eslint from '@eslint/js' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + ignores: ['dist/', 'node_modules/'] + } +) diff --git a/edge-apps/cap-alerting/index.html b/edge-apps/cap-alerting/index.html new file mode 100644 index 000000000..a55a96749 --- /dev/null +++ b/edge-apps/cap-alerting/index.html @@ -0,0 +1,15 @@ + + + + + + CAP Alerting + + + +
+
+
+ + + diff --git a/edge-apps/cap-alerting/index.ts b/edge-apps/cap-alerting/index.ts new file mode 100644 index 000000000..08f1fc91a --- /dev/null +++ b/edge-apps/cap-alerting/index.ts @@ -0,0 +1,274 @@ +import { + setupTheme, + signalReady, + getMetadata, + getTags, + getSettings, + getCorsProxyUrl, +} from '../edge-apps-library/src/index' + +import { XMLParser } from 'fast-xml-parser' + +interface CAPInfo { + language: string + category?: string + event?: string + urgency?: string + severity?: string + certainty?: string + headline?: string + description?: string + instruction?: string + resources: { mimeType: string; url: string }[] + areas: string[] + expires?: string +} + +interface CAPAlert { + identifier: string + sender: string + sent: string + status?: string + msgType?: string + scope?: string + infos: CAPInfo[] +} + +function parseCap(xml: string): CAPAlert[] { + const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) + const json: any = parser.parse(xml) + const alertsJson = json.alert ? (Array.isArray(json.alert) ? json.alert : [json.alert]) : [] + + const alerts: CAPAlert[] = [] + + alertsJson.forEach((a: any) => { + const infosJson = a.info ? (Array.isArray(a.info) ? a.info : [a.info]) : [] + + const infos: CAPInfo[] = infosJson.map((info: any) => { + const resourcesJson = info.resource + ? Array.isArray(info.resource) + ? info.resource + : [info.resource] + : [] + const areasJson = info.area ? (Array.isArray(info.area) ? info.area : [info.area]) : [] + + return { + language: info.language || '', + category: info.category, + event: info.event, + urgency: info.urgency, + severity: info.severity, + certainty: info.certainty, + headline: info.headline, + description: info.description, + instruction: info.instruction, + resources: resourcesJson.map((res: any) => { + return { + mimeType: res.mimeType || res['mimeType'], + url: res.uri || res.resourceDesc || '', + } + }), + areas: areasJson.map((area: any) => area.areaDesc || '').filter((s: string) => s), + expires: info.expires, + } + }) + + alerts.push({ + identifier: a.identifier || '', + sender: a.sender || '', + sent: a.sent || '', + status: a.status, + msgType: a.msgType, + scope: a.scope, + infos, + }) + }) + + return alerts +} + +function getNearestExit(tags: string[]): string | undefined { + for (const tag of tags) { + const lower = tag.toLowerCase() + if (lower.startsWith('exit:')) { + return tag.slice(5).trim() + } + if (lower.startsWith('exit-')) { + return tag.slice(5).trim() + } + } + return undefined +} + +async function fetchCapData(feedUrl: string, offlineMode: boolean): Promise { + if (offlineMode) { + return localStorage.getItem('cap_last') + } + + try { + const cors = getCorsProxyUrl() + let url = feedUrl + if (feedUrl && feedUrl.match(/^https?:/)) { + url = `${cors}/${feedUrl}` + } + + const response = await fetch(url) + if (!response.ok) throw new Error(`HTTP ${response.status}`) + + const text = await response.text() + localStorage.setItem('cap_last', text) + return text + } catch (err) { + console.warn('CAP fetch failed', err) + return localStorage.getItem('cap_last') + } +} + +function renderAlerts( + alerts: CAPAlert[], + nearestExit: string | undefined, + lang: string, + maxAlerts: number, + playAudio: boolean +): void { + const container = document.getElementById('alerts') + if (!container) return + + container.innerHTML = '' + const slice = alerts.slice(0, maxAlerts) + + slice.forEach((alert) => { + const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0] + const card = document.createElement('div') + card.className = 'alert-card' + + const header = document.createElement('h2') + header.textContent = info.event || alert.identifier + card.appendChild(header) + + const meta = document.createElement('div') + meta.className = 'meta' + meta.textContent = `${info.urgency || ''} ${info.severity || ''} ${info.certainty || ''}`.trim() + card.appendChild(meta) + + if (info.headline) { + const headline = document.createElement('h3') + headline.textContent = info.headline + card.appendChild(headline) + } + + if (info.description) { + const desc = document.createElement('p') + desc.textContent = info.description + card.appendChild(desc) + } + + if (info.instruction) { + let instr = info.instruction + if (nearestExit) { + if (instr.includes('{{closest_exit}}') || instr.includes('[[closest_exit]]')) { + instr = instr + .replace(/\{\{closest_exit\}\}/g, nearestExit) + .replace(/\[\[closest_exit\]\]/g, nearestExit) + } else { + instr += `\nNearest exit: ${nearestExit}` + } + } + const instEl = document.createElement('p') + instEl.className = 'instruction' + instEl.textContent = instr + card.appendChild(instEl) + } + + info.resources.forEach((res) => { + if (res.mimeType.startsWith('image')) { + const img = document.createElement('img') + img.src = res.url + card.appendChild(img) + } else if (res.mimeType.startsWith('audio') && playAudio) { + const audio = document.createElement('audio') + audio.src = res.url + audio.controls = true + card.appendChild(audio) + audio.play().catch(() => { + /* autoplay blocked */ + }) + } + }) + + container.appendChild(card) + }) +} + +async function startApp(): Promise { + setupTheme() + + let settings: Partial> = {} + let metadata: Partial> = {} + + try { + settings = getSettings() + localStorage.setItem('screenly_settings', JSON.stringify(settings)) + } catch (_) { + const cached = localStorage.getItem('screenly_settings') + settings = cached ? (JSON.parse(cached) as Partial>) : {} + } + + try { + metadata = getMetadata() + localStorage.setItem('screenly_metadata', JSON.stringify(metadata)) + } catch (_) { + const cachedMeta = localStorage.getItem('screenly_metadata') + metadata = cachedMeta ? (JSON.parse(cachedMeta) as Partial>) : {} + } + + const feedUrl: string = (settings.cap_feed_url as string) || '' + const interval = parseInt((settings.refresh_interval as string) || '5', 10) + const lang = (settings.language as string) || 'en' + const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) + const playAudio = ((settings.audio_alert as string) || 'false') === 'true' + const offlineMode = ((settings.offline_mode as string) || 'false') === 'true' + const testMode = ((settings.test_mode as string) || 'false') === 'true' + const demoMode = ((settings.demo_mode as string) || 'false') === 'true' + + const tags: string[] = metadata.tags || [] + const nearestExit = getNearestExit(tags) + + async function update() { + let xml: string | null + + if (testMode) { + try { + const resp = await fetch('static/test.cap') + xml = resp.ok ? await resp.text() : null + } catch (_) { + xml = null + } + } else if (demoMode && !feedUrl) { + const demoFiles = ['static/demo.cap', 'static/demo-severe.cap', 'static/demo-extreme.cap'] + const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)] + try { + const resp = await fetch(randomFile) + xml = resp.ok ? await resp.text() : null + } catch (_) { + xml = null + } + } else { + xml = await fetchCapData(feedUrl, offlineMode) + } + + if (xml) { + const alerts = parseCap(xml) + renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) + } else { + console.warn('No CAP data available') + } + } + + await update() + signalReady() + + setInterval(update, interval * 60 * 1000) +} + +startApp() diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json new file mode 100644 index 000000000..fbdd5ff3e --- /dev/null +++ b/edge-apps/cap-alerting/package.json @@ -0,0 +1,42 @@ +{ + "name": "cap-alerting", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "generate-mock-data": "screenly edge-app run --generate-mock-data", + "build:dev": "vite build --mode=development", + "build:dev:watch": "vite build --mode=development --watch", + "predev": "bun run generate-mock-data && bun run build:dev", + "dev": "run-p build:dev:watch dev:edge", + "dev:edge": "screenly edge-app run --path=dist/", + "build": "vite build", + "type-check": "tsc --noEmit", + "test": "bun test", + "test:watch": "bun test --watch", + "lint": "eslint . --fix", + "format": "prettier --write index.ts README.md index.html", + "predeploy": "bun run build", + "deploy": "screenly edge-app deploy --path dist/", + "clear-mock-data": "rimraf mock-data.yml" + }, + "dependencies": { + "@photostructure/tz-lookup": "^11.3.0", + "country-locale-map": "^1.9.11", + "fast-xml-parser": "^5.3.2", + "offline-geocode-city": "^1.0.2" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "eslint": "^9.39.1", + "jiti": "^2.6.1", + "npm-run-all2": "^8.0.4", + "prettier": "^3.7.4", + "rimraf": "^6.1.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.48.1", + "vite": "^7.2.6", + "vite-plugin-static-copy": "^3.1.4" + } +} diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml new file mode 100644 index 000000000..644e55cb6 --- /dev/null +++ b/edge-apps/cap-alerting/screenly.yml @@ -0,0 +1,58 @@ +--- +syntax: manifest_v1 +id: 01KBT555AYQE42KBXJ4NPH87S0 +description: Display CAP emergency alerts on Screenly screens. Supports offline mode and uses screen tags (e.g., exit:North Lobby) to highlight the nearest exit. +icon: https://playground.srly.io/edge-apps/cap-alerting/static/cap-icon.svg +author: Screenly, Inc. +ready_signal: true +entrypoint: + type: file +settings: + cap_feed_url: + type: string + title: CAP Feed URL + optional: true + default_value: '' + help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. + refresh_interval: + type: string + title: Refresh Interval (minutes) + optional: true + default_value: '5' + help_text: Time in minutes between feed updates. + language: + type: string + title: Default Language + optional: true + default_value: en + help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). + max_alerts: + type: string + title: Maximum Alerts + optional: true + default_value: '3' + help_text: Maximum number of alerts to display simultaneously. + audio_alert: + type: string + title: Play Audio Alerts + optional: true + default_value: 'false' + help_text: Play audio from CAP resources when available (true/false). + offline_mode: + type: string + title: Offline Mode + optional: true + default_value: 'false' + help_text: When enabled, avoid network fetches and use cached data (true/false). + test_mode: + type: string + title: Test Mode + optional: true + default_value: 'false' + help_text: Use bundled test CAP file for testing (true/false). + demo_mode: + type: string + title: Demo Mode + optional: true + default_value: 'false' + help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml new file mode 100644 index 000000000..17d20b3c1 --- /dev/null +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -0,0 +1,58 @@ +--- +syntax: manifest_v1 +id: cap-alerting-qc +description: Display CAP emergency alerts on Screenly screens. Supports offline mode and uses screen tags (e.g., exit:North Lobby) to highlight the nearest exit. +icon: https://playground.srly.io/edge-apps/cap-alerting/static/cap-icon.svg +author: Screenly, Inc. +ready_signal: true +entrypoint: + type: file +settings: + cap_feed_url: + type: string + title: CAP Feed URL + optional: true + default_value: '' + help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. + refresh_interval: + type: string + title: Refresh Interval (minutes) + optional: true + default_value: '5' + help_text: Time in minutes between feed updates. + language: + type: string + title: Default Language + optional: true + default_value: en + help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). + max_alerts: + type: string + title: Maximum Alerts + optional: true + default_value: '3' + help_text: Maximum number of alerts to display simultaneously. + audio_alert: + type: string + title: Play Audio Alerts + optional: true + default_value: 'false' + help_text: Play audio from CAP resources when available (true/false). + offline_mode: + type: string + title: Offline Mode + optional: true + default_value: 'false' + help_text: When enabled, avoid network fetches and use cached data (true/false). + test_mode: + type: string + title: Test Mode + optional: true + default_value: 'false' + help_text: Use bundled test CAP file for testing (true/false). + demo_mode: + type: string + title: Demo Mode + optional: true + default_value: 'false' + help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). diff --git a/edge-apps/cap-alerting/static/cap-icon.svg b/edge-apps/cap-alerting/static/cap-icon.svg new file mode 100644 index 000000000..4c6b7982a --- /dev/null +++ b/edge-apps/cap-alerting/static/cap-icon.svg @@ -0,0 +1,5 @@ + + + + ! + diff --git a/edge-apps/cap-alerting/static/demo-extreme.cap b/edge-apps/cap-alerting/static/demo-extreme.cap new file mode 100644 index 000000000..f00aa301d --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-extreme.cap @@ -0,0 +1,53 @@ + + + DEMO-TORNADO-EMERGENCY-001 + emergency@demo.screenly.io + 2024-01-15T15:45:00-00:00 + Actual + Alert + Public + + en-US + Met + Tornado Emergency + Shelter + Immediate + Extreme + Observed + 2024-01-15T15:45:00-00:00 + 2024-01-15T15:45:00-00:00 + 2024-01-15T16:30:00-00:00 + National Weather Service + TORNADO EMERGENCY - TAKE COVER NOW! + THIS IS A PARTICULARLY DANGEROUS SITUATION. A large and extremely dangerous tornado has been confirmed on the ground. This is a life-threatening situation. Flying debris will be deadly to those caught without shelter. Mobile homes will be destroyed. + TAKE COVER NOW! Move to a basement or an interior room on the lowest floor of a sturdy building. Avoid windows. If you are outdoors, in a mobile home, or in a vehicle, move to the closest substantial shelter and protect yourself from flying debris. Use {{closest_exit}} if evacuation is necessary. + https://weather.gov/tornado-emergency + Emergency Services: 911 + + SAME + TOR + + + TornadoRating + EF3 or greater expected + + + Live Radar + image/gif + https://example.com/radar/tornado-live.gif + + + Emergency Siren + audio/mp3 + https://example.com/audio/tornado-siren.mp3 + + + Tornado Path - Northern Suburbs + 39.00,-121.00 39.10,-120.90 39.15,-120.85 39.20,-121.10 39.00,-121.00 + + FIPS6 + 006067 + + + + diff --git a/edge-apps/cap-alerting/static/demo-severe.cap b/edge-apps/cap-alerting/static/demo-severe.cap new file mode 100644 index 000000000..3de7f5c83 --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-severe.cap @@ -0,0 +1,46 @@ + + + DEMO-SEVERE-WEATHER-001 + weather@demo.screenly.io + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + en-US + Met + Severe Thunderstorm Warning + Shelter + Immediate + Severe + Observed + 2024-01-15T10:00:00-00:00 + 2024-01-15T10:15:00-00:00 + 2024-01-15T12:00:00-00:00 + National Weather Service + Severe Thunderstorm Warning - Take Shelter Immediately + A severe thunderstorm capable of producing damaging winds in excess of 60 mph and large hail has been detected. The storm is moving northeast at 35 mph and will affect the area within the next 15 minutes. + Move to an interior room on the lowest floor of a sturdy building. Avoid windows. If you are outdoors, seek shelter immediately. Do not use elevators during severe weather. + https://weather.gov/alerts/severe + National Weather Service: 1-800-WEATHER + + MaxWindSpeed + 65mph + + + MaxHailSize + 1.5 inches + + + Radar Image + image/png + https://example.com/radar/storm-001.png + + + Downtown Metropolitan Area + 38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14 + 0 + 15000 + + + diff --git a/edge-apps/cap-alerting/static/demo.cap b/edge-apps/cap-alerting/static/demo.cap new file mode 100644 index 000000000..19d5963fc --- /dev/null +++ b/edge-apps/cap-alerting/static/demo.cap @@ -0,0 +1,51 @@ + + + DEMO-FIRE-2024-001 + demo@screenly.io + 2024-01-15T14:30:00-00:00 + Actual + Alert + Public + + en-US + Fire + Fire Drill + Evacuate + Immediate + Minor + Observed + 2024-01-15T14:30:00-00:00 + 2024-01-15T15:30:00-00:00 + Building Safety Systems + Scheduled Fire Drill in Progress + This is a scheduled fire drill. All occupants must evacuate the building immediately using the nearest available exit. This is not an actual emergency. + Please proceed calmly to {{closest_exit}} and gather at the designated assembly point in the parking lot. Do not use elevators. Wait for the all-clear signal before re-entering the building. + https://example.com/safety/fire-drill + Building Security: (555) 123-4567 + + Main Building - All Floors + 37.7749,-122.4194 37.7849,-122.4094 37.7949,-122.4194 37.7749,-122.4194 + + + + es-US + Fire + Simulacro de Incendio + Evacuate + Immediate + Minor + Observed + 2024-01-15T14:30:00-00:00 + 2024-01-15T15:30:00-00:00 + Sistemas de Seguridad del Edificio + Simulacro de Incendio Programado en Progreso + Este es un simulacro de incendio programado. Todos los ocupantes deben evacuar el edificio inmediatamente usando la salida disponible más cercana. Esta no es una emergencia real. + Por favor, diríjase con calma a {{closest_exit}} y reúnase en el punto de reunión designado en el estacionamiento. No use los ascensores. Espere la señal de seguridad antes de volver a entrar al edificio. + https://example.com/safety/fire-drill + Seguridad del Edificio: (555) 123-4567 + + Edificio Principal - Todos los Pisos + 37.7749,-122.4194 37.7849,-122.4094 37.7949,-122.4194 37.7749,-122.4194 + + + diff --git a/edge-apps/cap-alerting/static/style.css b/edge-apps/cap-alerting/static/style.css new file mode 100644 index 000000000..61971ef4a --- /dev/null +++ b/edge-apps/cap-alerting/static/style.css @@ -0,0 +1,137 @@ +:root { + --bg-color: #ffffff; + --text-color: #000000; + --card-bg: #f5f5f5; + --border-color: #ddd; + --alert-red: #dc3545; + --alert-orange: #fd7e14; + --alert-yellow: #ffc107; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + background-color: var(--bg-color); + color: var(--text-color); + padding: 2rem; + line-height: 1.6; +} + +#app { + width: 100%; + max-width: 1200px; + margin: 0 auto; +} + +.alerts { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.alert-card { + background: var(--card-bg); + border: 0.125rem solid var(--border-color); + border-radius: 0.5rem; + padding: 2rem; + box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); + animation: fadeIn 0.5s ease-in; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-1.25rem); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.alert-card h2 { + font-size: 2rem; + margin-bottom: 1rem; + color: var(--alert-red); +} + +.alert-card h3 { + font-size: 1.5rem; + margin-bottom: 1rem; + color: var(--text-color); +} + +.alert-card .meta { + display: inline-block; + padding: 0.5rem 1rem; + background: var(--alert-orange); + color: white; + border-radius: 0.25rem; + font-weight: bold; + text-transform: uppercase; + font-size: 0.875rem; + margin-bottom: 1rem; +} + +.alert-card p { + font-size: 1.125rem; + margin-bottom: 1rem; + white-space: pre-line; +} + +.alert-card .instruction { + background: #fff3cd; + border-left: 0.25rem solid var(--alert-yellow); + padding: 1rem; + margin-top: 1rem; + font-weight: bold; +} + +.alert-card img { + max-width: 100%; + height: auto; + margin-top: 1rem; + border-radius: 0.25rem; +} + +.alert-card audio { + width: 100%; + margin-top: 1rem; +} + +@media (max-width: 768px) { + body { + padding: 1rem; + } + + .alert-card { + padding: 1.5rem; + } + + .alert-card h2 { + font-size: 1.5rem; + } + + .alert-card h3 { + font-size: 1.25rem; + } + + .alert-card p { + font-size: 1rem; + } +} + +@media (orientation: portrait) { + .alerts { + gap: 1.5rem; + } + + .alert-card h2 { + font-size: 1.75rem; + } +} diff --git a/edge-apps/cap-alerting/static/test.cap b/edge-apps/cap-alerting/static/test.cap new file mode 100644 index 000000000..340c52aff --- /dev/null +++ b/edge-apps/cap-alerting/static/test.cap @@ -0,0 +1,24 @@ + + + TEST-ALERT-001 + test@screenly.io + 2024-01-15T10:00:00-00:00 + Test + Alert + Public + + en + Safety + Fire Drill + Immediate + Minor + Observed + Scheduled Fire Drill in Progress + This is a test alert for a scheduled fire drill. Please proceed to {{closest_exit}} in an orderly fashion. + Exit the building via {{closest_exit}}. Do not use elevators. Gather at the designated assembly point. + 2024-01-15T11:00:00-00:00 + + Main Building - All Floors + + + diff --git a/edge-apps/cap-alerting/tsconfig.json b/edge-apps/cap-alerting/tsconfig.json new file mode 100644 index 000000000..80784b813 --- /dev/null +++ b/edge-apps/cap-alerting/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "lib": ["ES2020", "DOM"], + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "paths": { + "../edge-apps-library/src/*": ["../edge-apps-library/dist/*"] + } + }, + "include": ["index.ts"], + "exclude": ["node_modules", "dist", "../edge-apps-library"] +} diff --git a/edge-apps/cap-alerting/vite.config.ts b/edge-apps/cap-alerting/vite.config.ts new file mode 100644 index 000000000..a686f3c74 --- /dev/null +++ b/edge-apps/cap-alerting/vite.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite' +import { viteStaticCopy } from 'vite-plugin-static-copy' + +const manifestFileName = process.env.MANIFEST_FILE_NAME || 'screenly.yml' + +export default defineConfig(({ mode }) => ({ + base: '', + build: { + sourcemap: mode === 'development', + }, + publicDir: false, + plugins: [ + viteStaticCopy({ + targets: [ + { + src: manifestFileName, + dest: '.' + }, + { + src: 'static/*', + dest: 'static' + } + ] + }) + ] +})) From 0b1c557002ce1778245297b71b48bf812ebe8903 Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Sat, 6 Dec 2025 19:28:54 +0000 Subject: [PATCH 02/26] More tweaks --- edge-apps/cap-alerting/README.md | 105 +- edge-apps/cap-alerting/bun.lock | 397 +- edge-apps/cap-alerting/index.html | 27 +- edge-apps/cap-alerting/index.test.ts | 1858 +++++ edge-apps/cap-alerting/index.ts | 422 +- edge-apps/cap-alerting/package.json | 33 +- edge-apps/cap-alerting/src/fetcher.test.ts | 612 ++ edge-apps/cap-alerting/src/fetcher.ts | 348 + edge-apps/cap-alerting/src/input.css | 135 + edge-apps/cap-alerting/src/main.ts | 490 ++ .../{demo-extreme.cap => demo-1-tornado.cap} | 31 +- edge-apps/cap-alerting/static/demo-2-fire.cap | 36 + .../cap-alerting/static/demo-3-flood.cap | 43 + .../cap-alerting/static/demo-4-earthquake.cap | 47 + .../cap-alerting/static/demo-5-hazmat.cap | 48 + .../cap-alerting/static/demo-6-shooter.cap | 47 + edge-apps/cap-alerting/static/demo-severe.cap | 46 - edge-apps/cap-alerting/static/demo.cap | 51 - edge-apps/cap-alerting/static/js/main.js | 6118 +++++++++++++++++ edge-apps/cap-alerting/static/style.css | 139 +- edge-apps/cap-alerting/static/test.cap | 5 + edge-apps/cap-alerting/tailwind.config.js | 26 + edge-apps/cap-alerting/tsconfig.json | 7 +- edge-apps/cap-alerting/vite.config.ts | 26 - 24 files changed, 10471 insertions(+), 626 deletions(-) create mode 100644 edge-apps/cap-alerting/index.test.ts create mode 100644 edge-apps/cap-alerting/src/fetcher.test.ts create mode 100644 edge-apps/cap-alerting/src/fetcher.ts create mode 100644 edge-apps/cap-alerting/src/input.css create mode 100644 edge-apps/cap-alerting/src/main.ts rename edge-apps/cap-alerting/static/{demo-extreme.cap => demo-1-tornado.cap} (68%) create mode 100644 edge-apps/cap-alerting/static/demo-2-fire.cap create mode 100644 edge-apps/cap-alerting/static/demo-3-flood.cap create mode 100644 edge-apps/cap-alerting/static/demo-4-earthquake.cap create mode 100644 edge-apps/cap-alerting/static/demo-5-hazmat.cap create mode 100644 edge-apps/cap-alerting/static/demo-6-shooter.cap delete mode 100644 edge-apps/cap-alerting/static/demo-severe.cap delete mode 100644 edge-apps/cap-alerting/static/demo.cap create mode 100644 edge-apps/cap-alerting/static/js/main.js create mode 100644 edge-apps/cap-alerting/tailwind.config.js delete mode 100644 edge-apps/cap-alerting/vite.config.ts diff --git a/edge-apps/cap-alerting/README.md b/edge-apps/cap-alerting/README.md index 8d732afa7..aed9ae5a3 100644 --- a/edge-apps/cap-alerting/README.md +++ b/edge-apps/cap-alerting/README.md @@ -1,108 +1,35 @@ # CAP Alerting Edge App -Display Common Alerting Protocol (CAP) emergency alerts on Screenly digital signage screens. - -## Features - -- **Full CAP Support**: Parse and display CAP XML alerts with all essential fields including identifier, sender, event type, urgency, severity, certainty, headline, description, and instructions -- **Offline Operation**: Caches alerts in localStorage and continues operating when network is unavailable -- **Nearest Exit Integration**: Uses screen tags (e.g., `exit:North Lobby`) to provide location-aware exit directions -- **Flexible Settings**: Configure feed URL, refresh interval, language preferences, audio alerts, and more -- **Test Mode**: Built-in test mode with sample CAP file for demonstration and debugging +Display Common Alerting Protocol (CAP) emergency alerts on Screenly digital signage screens. Designed to work with [Override Playlist](https://developer.screenly.io/api-reference/v4/#tag/Playlists/operation/override_playlist) to automatically interrupt regular content when alerts are active. ## Settings -- **CAP Feed URL**: URL or path to a CAP XML feed -- **Refresh Interval**: Time in minutes between feed updates (default: 5) -- **Default Language**: Preferred language when multiple info blocks exist (default: en) -- **Maximum Alerts**: Maximum number of alerts to display simultaneously (default: 3) -- **Play Audio Alerts**: Enable/disable audio playback from CAP resources -- **Offline Mode**: Use cached or bundled data instead of fetching from network -- **Test Mode**: Load bundled test CAP file for demonstration purposes -- **Demo Mode**: Enable demo mode with realistic CAP alerts (randomly selected) - -## Nearest Exit Tags - -To enable location-aware exit directions, assign tags to your Screenly screens using the format: - -- `exit:North Lobby` -- `exit-South Stairwell` +- **CAP Feed URL**: URL to your CAP XML feed +- **Refresh Interval**: Minutes between feed updates (default: 5) +- **Default Language**: Preferred language code (default: en) +- **Maximum Alerts**: Max alerts to display (default: 3) +- **Play Audio Alerts**: Enable audio playback from CAP `` elements with audio MIME types (default: false) +- **Offline Mode**: Use cached data when network unavailable +- **Test Mode**: Load bundled test CAP file +- **Demo Mode**: Show random demo alerts -The app will automatically inject the nearest exit information into alert instructions that contain the placeholder `{{closest_exit}}` or `[[closest_exit]]`. +## Audio Support -## Demo Mode +The app automatically detects and plays audio resources from CAP alerts when the "Play Audio Alerts" setting is enabled. Audio resources are identified by MIME types starting with `audio/` (e.g., `audio/mpeg`, `audio/wav`, `audio/ogg`). The audio player will attempt to autoplay when an alert is displayed, though browser autoplay policies may require user interaction in some cases. -The app includes three realistic demo scenarios that are randomly selected: - -- **Fire Drill**: Bilingual fire drill alert with evacuation instructions -- **Severe Weather**: Severe thunderstorm warning with radar imagery -- **Extreme Emergency**: Tornado emergency with multiple resources - -Demo mode automatically activates when: -- `demo_mode` is enabled -- Test mode is disabled -- No CAP feed URL is configured - -To test locally with demo mode: - -```bash -cd edge-apps/cap-alerting - -# Generate mock data (creates mock-data.yml) -bun run generate-mock-data - -# Edit mock-data.yml and add a tag like: -# tags: ["exit:North Stairwell"] - -# Run the app -bun run dev -``` +## Nearest Exit Tags -Then access the app at the URL shown in the terminal and enable `demo_mode` in the settings. Each refresh will randomly show one of the three demo scenarios. +Add tags to your Screenly screens (e.g., `exit:North Lobby`) to provide location-aware exit directions. The app substitutes `{{closest_exit}}` or `[[closest_exit]]` placeholders in alert instructions. -## Testing +## Override Playlist Integration -Enable Test Mode in the app settings to load the bundled test CAP file (`static/test.cap`) instead of fetching from a live feed. This allows you to verify parsing, rendering, and nearest-exit substitution without network dependencies. +This app is designed to use Screenly's [Override Playlist API](https://developer.screenly.io/api-reference/v4/#tag/Playlists/operation/override_playlist) to automatically interrupt regular content when alerts are active. Configure your backend to call the API when new CAP alerts are detected. ## Development -All commands should be run from within the app directory: - ```bash cd edge-apps/cap-alerting -``` - -Install dependencies: - -```bash bun install -``` - -Run tests: - -```bash -bun test # Run all tests -bun test --watch # Watch mode -``` - -Run in development mode: - -```bash bun run dev +bun test ``` - -Build for production: - -```bash -bun run build -``` - -Deploy to Screenly: - -```bash -bun run deploy -``` - -## License - -MIT diff --git a/edge-apps/cap-alerting/bun.lock b/edge-apps/cap-alerting/bun.lock index 81f411473..d4e2f82a9 100644 --- a/edge-apps/cap-alerting/bun.lock +++ b/edge-apps/cap-alerting/bun.lock @@ -11,71 +11,102 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@screenly/edge-apps": "workspace:../edge-apps-library", + "@tailwindcss/cli": "^4.0.0", + "@types/bun": "^1.3.3", "@types/node": "^24.10.1", + "autoprefixer": "^10.4.22", + "bun-types": "^1.3.3", "eslint": "^9.39.1", "jiti": "^2.6.1", - "npm-run-all2": "^8.0.4", "prettier": "^3.7.4", - "rimraf": "^6.1.2", + "tailwindcss": "^4.1.17", "typescript": "^5.9.3", "typescript-eslint": "^8.48.1", - "vite": "^7.2.6", - "vite-plugin-static-copy": "^3.1.4", + }, + }, + "../edge-apps-library": { + "name": "@screenly/edge-apps", + "dependencies": { + "@photostructure/tz-lookup": "^11.3.0", + "country-locale-map": "^1.9.11", + "offline-geocode-city": "^1.0.2", + }, + "devDependencies": { + "@types/bun": "^1.3.3", + "@types/jsdom": "^27.0.0", + "@types/node": "^24.10.1", + "bun-types": "^1.3.3", + "jsdom": "^27.2.0", + "prettier": "^3.7.4", + "typescript": "^5.9.3", }, }, }, "packages": { - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + "@acemir/cssom": ["@acemir/cssom@0.9.26", "", {}, "sha512-UMFbL3EnWH/eTvl21dz9s7Td4wYDMtxz/56zD8sL9IZGYyi48RxmdgPMiyT7R6Vn3rjMTwYZ42bqKa7ex74GEQ=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@4.1.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "lru-cache": "^11.2.2" } }, "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w=="], + + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.7.6", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.4" } }, "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], + + "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.0.20", "", {}, "sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], @@ -103,9 +134,15 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], - "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@jsheaven/easybuild": ["@jsheaven/easybuild@1.2.9", "", { "dependencies": { "@jsheaven/status-message": "^1.1.2", "brotli-size": "^4.0.0", "dts-bundle-generator": "^7.2.0", "esbuild": "^0.17.6", "fast-glob": "^3.2.12", "gzip-size": "^7.0.0", "pretty-bytes": "^6.1.0", "typescript": "^4.9.5" }, "bin": { "easybuild": "dist/cli.esm.js", "easybuild-cjs": "dist/cli.iife.js" } }, "sha512-IJsaER05bGZKEuvBJ+JOQ7YW+RYHryYoO9z67TxpP7fAji8Oq+wJF8eFPEZabIcUbXqe20/Pfhx6P4g7SNP8kQ=="], @@ -117,58 +154,80 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@photostructure/tz-lookup": ["@photostructure/tz-lookup@11.3.0", "", {}, "sha512-rYGy7ETBHTnXrwbzm47e3LJPKJmzpY7zXnbZhdosNU0lTGWVqzxptSjK4qZkJ1G+Kwy4F6XStNR9ZqMsXAoASQ=="], + "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="], + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="], + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="], + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="], + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="], + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="], + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="], + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="], + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="], + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="], + "@photostructure/tz-lookup": ["@photostructure/tz-lookup@11.3.0", "", {}, "sha512-rYGy7ETBHTnXrwbzm47e3LJPKJmzpY7zXnbZhdosNU0lTGWVqzxptSjK4qZkJ1G+Kwy4F6XStNR9ZqMsXAoASQ=="], + + "@screenly/edge-apps": ["@screenly/edge-apps@workspace:../edge-apps-library"], + + "@tailwindcss/cli": ["@tailwindcss/cli@4.0.0", "", { "dependencies": { "@parcel/watcher": "^2.5.0", "@tailwindcss/node": "^4.0.0", "@tailwindcss/oxide": "^4.0.0", "enhanced-resolve": "^5.18.0", "lightningcss": "^1.29.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.0.0" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-nh6kzSTalHf9yk6WNsS4MMZakSINsncNQXsSJthvcPI4x+yajEaNQvS2uUti3PGLbsmlGoUvjhnGTBpzh7H0bA=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], + + "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/jsdom": ["@types/jsdom@27.0.0", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw=="], + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.48.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/type-utils": "8.48.1", "@typescript-eslint/utils": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.48.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.48.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA=="], @@ -193,18 +252,26 @@ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "autoprefixer": ["autoprefixer@10.4.22", "", { "dependencies": { "browserslist": "^4.27.0", "caniuse-lite": "^1.0.30001754", "fraction.js": "^5.3.4", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.4", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ZCQ9GEWl73BVm8bu5Fts8nt7MHdbt5vY9bP6WGnUh+r3l8M7CgfyTlwsgCbMC66BNxPr6Xoce3j66Ms5YUQTNA=="], + + "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -213,8 +280,14 @@ "brotli-size": ["brotli-size@4.0.0", "", { "dependencies": { "duplexer": "0.1.1" } }, "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA=="], + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + "caniuse-lite": ["caniuse-lite@1.0.30001759", "", {}, "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw=="], + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -231,19 +304,35 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="], + + "cssstyle": ["cssstyle@5.3.3", "", { "dependencies": { "@asamuzakjp/css-color": "^4.0.3", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0" } }, "sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw=="], + "csv-parse": ["csv-parse@5.6.0", "", {}, "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q=="], + "data-urls": ["data-urls@6.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.0.0" } }, "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + "dts-bundle-generator": ["dts-bundle-generator@7.2.0", "", { "dependencies": { "typescript": ">=4.5.2", "yargs": "^17.6.0" }, "bin": { "dts-bundle-generator": "dist/bin/dts-bundle-generator.js" } }, "sha512-pHjRo52hvvLDRijzIYRTS9eJR7vAOs3gd/7jx+7YVnLU8ay3yPUWGtHXPtuMBSlJYk/s4nq1SvXObDCZVguYMg=="], "duplexer": ["duplexer@0.1.1", "", {}, "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q=="], + "electron-to-chromium": ["electron-to-chromium@1.5.266", "", {}, "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -289,18 +378,20 @@ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "fuzzball": ["fuzzball@2.2.3", "", { "dependencies": { "heap": ">=0.2.0", "lodash": "^4.17.21", "setimmediate": "^1.0.5" } }, "sha512-sQDb3kjI7auA4YyE1YgEW85MTparcSgRgcCweUK06Cn0niY5lN+uhFiRUZKN4MQVGGiHxlbrYCA4nL1QjOXBLQ=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], "gzip-size": ["gzip-size@7.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA=="], @@ -309,6 +400,14 @@ "heap": ["heap@0.2.7", "", {}, "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="], + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], @@ -325,15 +424,17 @@ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + "jsdom": ["jsdom@27.2.0", "", { "dependencies": { "@acemir/cssom": "^0.9.23", "@asamuzakjp/dom-selector": "^6.7.4", "cssstyle": "^5.3.3", "data-urls": "^6.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.1.0", "ws": "^8.18.3", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA=="], - "json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "", {}, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="], + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], @@ -345,6 +446,30 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], @@ -357,7 +482,9 @@ "lz-ts": ["lz-ts@1.1.2", "", {}, "sha512-ye8sVndmvzs46cPgX1Yjlk3o/Sueu0VHn253rKpsWiK2/bAbsVkD7DEJiaueiPfbZTi17GLRPkv3W5O3BUNd2g=="], - "memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], @@ -365,7 +492,7 @@ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -373,11 +500,13 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], - "npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="], + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - "npm-run-all2": ["npm-run-all2@8.0.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "picomatch": "^4.0.2", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA=="], + "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], "offline-geocode-city": ["offline-geocode-city@1.0.2", "", { "dependencies": { "@jsheaven/easybuild": "^1.2.9", "chokidar": "^3.5.3", "csv-parse": "^5.3.10", "lz-ts": "^1.1.2", "s2-geometry": "^1.2.10" } }, "sha512-6q9XvgYpvOr7kLzi/K2P1GZ36FajNHEI4cFphNcZ6tPxR0kBROzy6CorTn+yU7en3wrDkDTfcn1sPCAKA569xA=="], @@ -387,26 +516,22 @@ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], - - "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], @@ -417,24 +542,24 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="], - "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rimraf": ["rimraf@6.1.2", "", { "dependencies": { "glob": "^13.0.0", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g=="], - - "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "s2-geometry": ["s2-geometry@1.2.10", "", { "dependencies": { "long": "^3.2.0" } }, "sha512-5WejfQu1XZ25ZerW8uL6xP1sM2krcOYKhI6TbfybGRf+vTQLrm3E+4n0+1lWg+MYqFjPzoe51zKhn2sBRMCt5g=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], @@ -443,8 +568,6 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -457,10 +580,24 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tldts": ["tldts@7.0.19", "", { "dependencies": { "tldts-core": "^7.0.19" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA=="], + + "tldts-core": ["tldts-core@7.0.19", "", {}, "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], + + "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], @@ -471,18 +608,32 @@ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "update-browserslist-db": ["update-browserslist-db@1.2.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="], + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "vite": ["vite@7.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ=="], + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], - "vite-plugin-static-copy": ["vite-plugin-static-copy@3.1.4", "", { "dependencies": { "chokidar": "^3.6.0", "p-map": "^7.0.3", "picocolors": "^1.1.1", "tinyglobby": "^0.2.15" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-iCmr4GSw4eSnaB+G8zc2f4dxSuDjbkjwpuBLLGvQYR9IW7rnDzftnUjOH5p4RYR+d4GsiBqXRvzuFhs5bnzVyw=="], + "webidl-conversions": ["webidl-conversions@8.0.0", "", {}, "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA=="], - "which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@15.1.0", "", { "dependencies": { "tr46": "^6.0.0", "webidl-conversions": "^8.0.0" } }, "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], @@ -493,80 +644,40 @@ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@jsheaven/easybuild/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="], - "@jsheaven/easybuild/typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="], - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], - - "gzip-size/duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], - - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "@jsheaven/easybuild/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="], - - "@jsheaven/easybuild/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="], - - "@jsheaven/easybuild/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="], - - "@jsheaven/easybuild/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="], - - "@jsheaven/easybuild/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="], + "@tailwindcss/cli/tailwindcss": ["tailwindcss@4.0.0", "", {}, "sha512-ULRPI3A+e39T7pSaf1xoi58AqqJxVCLg8F/uM5A3FadUbnyDTgltVnXJvdkTjwCOGA6NazqHVcwPJC5h2vRYVQ=="], - "@jsheaven/easybuild/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], - "@jsheaven/easybuild/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="], + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="], + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="], - - "@jsheaven/easybuild/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="], - - "@jsheaven/easybuild/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "@jsheaven/easybuild/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "@jsheaven/easybuild/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "@jsheaven/easybuild/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="], + "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "@jsheaven/easybuild/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="], + "gzip-size/duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], - "@jsheaven/easybuild/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="], + "jsdom/parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], - "@jsheaven/easybuild/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="], + "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "@jsheaven/easybuild/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="], + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - - "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], } } diff --git a/edge-apps/cap-alerting/index.html b/edge-apps/cap-alerting/index.html index a55a96749..163c82387 100644 --- a/edge-apps/cap-alerting/index.html +++ b/edge-apps/cap-alerting/index.html @@ -1,15 +1,16 @@ - + - - - - CAP Alerting - - - -
-
-
- - + + + CAP Alerting - Screenly Edge App + + + + +
+
+
+ + + diff --git a/edge-apps/cap-alerting/index.test.ts b/edge-apps/cap-alerting/index.test.ts new file mode 100644 index 000000000..ff7c2d120 --- /dev/null +++ b/edge-apps/cap-alerting/index.test.ts @@ -0,0 +1,1858 @@ +import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test' + +const mockGetSettings = mock() +const mockGetMetadata = mock() +const mockGetCorsProxyUrl = mock() +const mockSetupTheme = mock() +const mockSignalReady = mock() +const mockGetTags = mock() + +mock.module('../edge-apps-library/src/index', () => ({ + getSettings: () => mockGetSettings(), + getMetadata: () => mockGetMetadata(), + getCorsProxyUrl: () => mockGetCorsProxyUrl(), + setupTheme: () => mockSetupTheme(), + signalReady: () => mockSignalReady(), + getTags: () => mockGetTags(), +})) + +import { parseCap, getNearestExit } from './src/main' +import { XMLParser } from 'fast-xml-parser' + +describe('CAP v1.2 Parser', () => { + describe('Basic Alert Structure', () => { + it('should parse a minimal valid CAP alert', () => { + const xml = ` + + 43b080713727 + hsas@dhs.gov + 2003-04-02T14:39:01-05:00 + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts).toHaveLength(1) + expect(alerts[0].identifier).toBe('43b080713727') + expect(alerts[0].sender).toBe('hsas@dhs.gov') + expect(alerts[0].sent).toBe('2003-04-02T14:39:01-05:00') + expect(alerts[0].status).toBe('Actual') + expect(alerts[0].msgType).toBe('Alert') + expect(alerts[0].scope).toBe('Public') + }) + + it('should parse all alert-level required fields', () => { + const xml = ` + + KSTO1055887203 + KSTO@NWS.NOAA.GOV + 2003-06-17T14:57:00-07:00 + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0]).toMatchObject({ + identifier: 'KSTO1055887203', + sender: 'KSTO@NWS.NOAA.GOV', + sent: '2003-06-17T14:57:00-07:00', + status: 'Actual', + msgType: 'Alert', + scope: 'Public', + }) + }) + + it('should parse alert with optional fields', () => { + const xml = ` + + KSTO1055887203 + KSTO@NWS.NOAA.GOV + 2003-06-17T14:57:00-07:00 + Actual + Update + Weather Service + Public + This is a test note + KSTO@NWS.NOAA.GOV,KSTO1055887200,2003-06-17T14:00:00-07:00 + incident1,incident2 +` + + const alerts = parseCap(xml) + expect(alerts[0].source).toBe('Weather Service') + expect(alerts[0].note).toBe('This is a test note') + expect(alerts[0].references).toBe('KSTO@NWS.NOAA.GOV,KSTO1055887200,2003-06-17T14:00:00-07:00') + expect(alerts[0].incidents).toBe('incident1,incident2') + }) + + it('should parse alert with Restricted scope', () => { + const xml = ` + + TEST123 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Restricted + For emergency services only +` + + const alerts = parseCap(xml) + expect(alerts[0].scope).toBe('Restricted') + expect(alerts[0].restriction).toBe('For emergency services only') + }) + + it('should parse alert with Private scope and addresses', () => { + const xml = ` + + TEST124 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Private + user1@example.com user2@example.com +` + + const alerts = parseCap(xml) + expect(alerts[0].scope).toBe('Private') + expect(alerts[0].addresses).toBe('user1@example.com user2@example.com') + }) + }) + + describe('Alert Status Values', () => { + const statuses = ['Actual', 'Exercise', 'System', 'Test', 'Draft'] + + statuses.forEach((status) => { + it(`should parse alert with status: ${status}`, () => { + const xml = ` + + TEST-${status} + test@example.com + 2024-01-15T10:00:00-00:00 + ${status} + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].status).toBe(status) + }) + }) + }) + + describe('Message Type Values', () => { + const msgTypes = ['Alert', 'Update', 'Cancel', 'Ack', 'Error'] + + msgTypes.forEach((msgType) => { + it(`should parse alert with msgType: ${msgType}`, () => { + const xml = ` + + TEST-${msgType} + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + ${msgType} + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].msgType).toBe(msgType) + }) + }) + }) + + describe('Info Element Structure', () => { + it('should parse info with all required fields', () => { + const xml = ` + + KSTO1055887203 + KSTO@NWS.NOAA.GOV + 2003-06-17T14:57:00-07:00 + Actual + Alert + Public + + Met + SEVERE THUNDERSTORM + Immediate + Severe + Observed + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos).toHaveLength(1) + expect(alerts[0].infos[0]).toMatchObject({ + category: 'Met', + event: 'SEVERE THUNDERSTORM', + urgency: 'Immediate', + severity: 'Severe', + certainty: 'Observed', + }) + }) + + it('should parse info with all optional fields', () => { + const xml = ` + + TEST125 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + en-US + Fire + WILDFIRE + Evacuate + Immediate + Extreme + Observed + General public in affected areas + 2024-01-15T10:00:00-00:00 + 2024-01-15T10:30:00-00:00 + 2024-01-15T22:00:00-00:00 + National Weather Service + Wildfire Warning Issued + A rapidly spreading wildfire has been detected. + Evacuate immediately to designated shelters. + http://www.example.com/wildfire + 1-800-EMERGENCY + +` + + const alerts = parseCap(xml) + const info = alerts[0].infos[0] + expect(info.language).toBe('en-US') + expect(info.audience).toBe('General public in affected areas') + expect(info.effective).toBe('2024-01-15T10:00:00-00:00') + expect(info.onset).toBe('2024-01-15T10:30:00-00:00') + expect(info.expires).toBe('2024-01-15T22:00:00-00:00') + expect(info.senderName).toBe('National Weather Service') + expect(info.headline).toBe('Wildfire Warning Issued') + expect(info.description).toBe('A rapidly spreading wildfire has been detected.') + expect(info.instruction).toBe('Evacuate immediately to designated shelters.') + expect(info.web).toBe('http://www.example.com/wildfire') + expect(info.contact).toBe('1-800-EMERGENCY') + }) + + it('should parse multiple categories', () => { + const xml = ` + + TEST126 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Geo + STORM SURGE + Immediate + Severe + Likely + +` + + const alerts = parseCap(xml) + const category = alerts[0].infos[0].category + expect(Array.isArray(category) ? category : [category]).toContain('Met') + expect(Array.isArray(category) ? category : [category]).toContain('Geo') + }) + }) + + describe('Category Values', () => { + const categories = [ + 'Geo', + 'Met', + 'Safety', + 'Security', + 'Rescue', + 'Fire', + 'Health', + 'Env', + 'Transport', + 'Infra', + 'CBRNE', + 'Other', + ] + + categories.forEach((category) => { + it(`should parse category: ${category}`, () => { + const xml = ` + + TEST-CAT-${category} + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + ${category} + Test Event + Immediate + Moderate + Possible + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].category).toBe(category) + }) + }) + }) + + describe('ResponseType Values', () => { + const responseTypes = [ + 'Shelter', + 'Evacuate', + 'Prepare', + 'Execute', + 'Avoid', + 'Monitor', + 'Assess', + 'AllClear', + 'None', + ] + + responseTypes.forEach((responseType) => { + it(`should parse responseType: ${responseType}`, () => { + const xml = ` + + TEST-RT-${responseType} + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + ${responseType} + Immediate + Moderate + Possible + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].responseType).toBe(responseType) + }) + }) + }) + + describe('Urgency Values', () => { + const urgencies = ['Immediate', 'Expected', 'Future', 'Past', 'Unknown'] + + urgencies.forEach((urgency) => { + it(`should parse urgency: ${urgency}`, () => { + const xml = ` + + TEST-URG-${urgency} + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + ${urgency} + Moderate + Possible + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].urgency).toBe(urgency) + }) + }) + }) + + describe('Severity Values', () => { + const severities = ['Extreme', 'Severe', 'Moderate', 'Minor', 'Unknown'] + + severities.forEach((severity) => { + it(`should parse severity: ${severity}`, () => { + const xml = ` + + TEST-SEV-${severity} + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + Immediate + ${severity} + Possible + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].severity).toBe(severity) + }) + }) + }) + + describe('Certainty Values', () => { + const certainties = ['Observed', 'Likely', 'Possible', 'Unlikely', 'Unknown'] + + certainties.forEach((certainty) => { + it(`should parse certainty: ${certainty}`, () => { + const xml = ` + + TEST-CERT-${certainty} + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + Immediate + Moderate + ${certainty} + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].certainty).toBe(certainty) + }) + }) + }) + + describe('Resource Element', () => { + it('should parse resource with required fields', () => { + const xml = ` + + TEST127 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + Immediate + Moderate + Possible + + Evacuation Map + image/png + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].resources).toHaveLength(1) + expect(alerts[0].infos[0].resources[0]).toMatchObject({ + resourceDesc: 'Evacuation Map', + mimeType: 'image/png', + }) + }) + + it('should parse resource with all optional fields', () => { + const xml = ` + + TEST128 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + Immediate + Moderate + Possible + + Evacuation Map + image/png + 12345 + http://example.com/map.png + base64encodeddata + SHA-256HASH + + +` + + const alerts = parseCap(xml) + const resource = alerts[0].infos[0].resources[0] + expect(resource.resourceDesc).toBe('Evacuation Map') + expect(resource.mimeType).toBe('image/png') + expect(resource.size).toBe(12345) + expect(resource.uri).toBe('http://example.com/map.png') + expect(resource.derefUri).toBe('base64encodeddata') + expect(resource.digest).toBe('SHA-256HASH') + }) + + it('should parse multiple resources', () => { + const xml = ` + + TEST129 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test Event + Immediate + Moderate + Possible + + Map + image/png + http://example.com/map.png + + + Audio Alert + audio/mp3 + http://example.com/alert.mp3 + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].resources).toHaveLength(2) + expect(alerts[0].infos[0].resources[0].mimeType).toBe('image/png') + expect(alerts[0].infos[0].resources[1].mimeType).toBe('audio/mp3') + }) + }) + + describe('Area Element', () => { + it('should parse area with required fields', () => { + const xml = ` + + TEST130 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Severe Storm + Immediate + Severe + Observed + + Downtown Metropolitan Area + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].areas).toHaveLength(1) + expect(alerts[0].infos[0].areas[0].areaDesc).toBe('Downtown Metropolitan Area') + }) + + it('should parse area with polygon', () => { + const xml = ` + + TEST131 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Tornado Warning + Immediate + Extreme + Observed + + Storm Path + 38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14 + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + expect(area.areaDesc).toBe('Storm Path') + expect(area.polygon).toBe('38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14') + }) + + it('should parse area with multiple polygons', () => { + const xml = ` + + TEST132 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Fire + Fire Zone + Immediate + Extreme + Observed + + Multiple Fire Zones + 38.47,-120.14 38.34,-119.95 38.52,-119.74 38.47,-120.14 + 39.00,-121.00 39.10,-120.90 39.20,-121.10 39.00,-121.00 + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + const polygons = Array.isArray(area.polygon) ? area.polygon : [area.polygon] + expect(polygons).toHaveLength(2) + }) + + it('should parse area with circle', () => { + const xml = ` + + TEST133 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + CBRNE + Chemical Spill + Immediate + Severe + Observed + + Evacuation Zone + 38.5,-120.5 5.0 + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + expect(area.circle).toBe('38.5,-120.5 5.0') + }) + + it('should parse area with geocode', () => { + const xml = ` + + TEST134 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Flood Warning + Expected + Moderate + Likely + + County Area + + FIPS6 + 006017 + + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + expect(area.geocode).toBeDefined() + expect(area.geocode.valueName).toBe('FIPS6') + expect(area.geocode.value).toBe(6017) + }) + + it('should parse area with altitude and ceiling', () => { + const xml = ` + + TEST135 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Aviation Alert + Immediate + Moderate + Observed + + Flight Restriction Zone + 1000 + 5000 + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + expect(area.altitude).toBe(1000) + expect(area.ceiling).toBe(5000) + }) + + it('should parse multiple areas', () => { + const xml = ` + + TEST136 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Multi-Area Warning + Immediate + Severe + Observed + + Area 1 + 38.47,-120.14 38.34,-119.95 38.52,-119.74 38.47,-120.14 + + + Area 2 + 39.0,-121.0 10.0 + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].areas).toHaveLength(2) + expect(alerts[0].infos[0].areas[0].areaDesc).toBe('Area 1') + expect(alerts[0].infos[0].areas[1].areaDesc).toBe('Area 2') + }) + }) + + describe('Multiple Info Blocks', () => { + it('should parse multiple info blocks with different languages', () => { + const xml = ` + + TEST137 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + en-US + Safety + Emergency Alert + Immediate + Severe + Observed + Emergency Alert + This is an emergency alert in English. + + + es-US + Safety + Alerta de Emergencia + Immediate + Severe + Observed + Alerta de Emergencia + Esta es una alerta de emergencia en español. + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos).toHaveLength(2) + expect(alerts[0].infos[0].language).toBe('en-US') + expect(alerts[0].infos[1].language).toBe('es-US') + expect(alerts[0].infos[0].description).toContain('English') + expect(alerts[0].infos[1].description).toContain('español') + }) + }) + + describe('EventCode and Parameter', () => { + it('should parse eventCode', () => { + const xml = ` + + TEST138 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Severe Thunderstorm + Immediate + Severe + Observed + + SAME + SVR + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].eventCode).toBeDefined() + expect(alerts[0].infos[0].eventCode.valueName).toBe('SAME') + expect(alerts[0].infos[0].eventCode.value).toBe('SVR') + }) + + it('should parse parameter', () => { + const xml = ` + + TEST139 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Hurricane + Expected + Extreme + Likely + + WindSpeed + 120mph + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].parameter).toBeDefined() + expect(alerts[0].infos[0].parameter.valueName).toBe('WindSpeed') + expect(alerts[0].infos[0].parameter.value).toBe('120mph') + }) + }) + + describe('Multiple Alerts', () => { + it('should parse multiple alerts in a single feed', () => { + const xml = ` + + + ALERT001 + sender1@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + + ALERT002 + sender2@example.com + 2024-01-15T11:00:00-00:00 + Actual + Update + Public + +` + + const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) + const json: any = parser.parse(xml) + const alerts = json.feed?.alert + ? Array.isArray(json.feed.alert) + ? json.feed.alert + : [json.feed.alert] + : [] + expect(alerts).toHaveLength(2) + expect(alerts[0].identifier).toBe('ALERT001') + expect(alerts[1].identifier).toBe('ALERT002') + }) + }) + + describe('Nearest Exit Functionality', () => { + it('should extract exit from tag with colon', () => { + const tags = ['exit:North Lobby', 'location:Building A'] + const exit = getNearestExit(tags) + expect(exit).toBe('North Lobby') + }) + + it('should extract exit from tag with dash', () => { + const tags = ['exit-South Stairwell', 'floor:3'] + const exit = getNearestExit(tags) + expect(exit).toBe('South Stairwell') + }) + + it('should be case-insensitive', () => { + const tags = ['EXIT:West Door', 'Exit-East Door'] + const exit = getNearestExit(tags) + expect(exit).toBe('West Door') + }) + + it('should return first exit tag found', () => { + const tags = ['exit:First Exit', 'exit:Second Exit'] + const exit = getNearestExit(tags) + expect(exit).toBe('First Exit') + }) + + it('should return undefined if no exit tag found', () => { + const tags = ['location:Building A', 'floor:3'] + const exit = getNearestExit(tags) + expect(exit).toBeUndefined() + }) + + it('should trim whitespace from exit description', () => { + const tags = ['exit: Main Entrance '] + const exit = getNearestExit(tags) + expect(exit).toBe('Main Entrance') + }) + }) + + describe('Edge Cases', () => { + it('should handle empty alert', () => { + const xml = ` + +` + + const alerts = parseCap(xml) + expect(alerts).toHaveLength(1) + expect(alerts[0].identifier).toBe('') + expect(alerts[0].sender).toBe('') + }) + + it('should handle alert with no info blocks', () => { + const xml = ` + + TEST140 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].infos).toHaveLength(0) + }) + + it('should handle info with no resources', () => { + const xml = ` + + TEST141 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test + Immediate + Moderate + Possible + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].resources).toHaveLength(0) + }) + + it('should handle info with no areas', () => { + const xml = ` + + TEST142 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test + Immediate + Moderate + Possible + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].areas).toHaveLength(0) + }) + }) + + describe('DateTime Formats', () => { + it('should parse ISO 8601 datetime with timezone offset', () => { + const xml = ` + + TEST-DT-001 + test@example.com + 2003-04-02T14:39:01-05:00 + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].sent).toBe('2003-04-02T14:39:01-05:00') + }) + + it('should parse ISO 8601 datetime with positive timezone offset', () => { + const xml = ` + + TEST-DT-002 + test@example.com + 2024-01-15T18:30:00+08:00 + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].sent).toBe('2024-01-15T18:30:00+08:00') + }) + + it('should parse ISO 8601 datetime with UTC timezone (Z)', () => { + const xml = ` + + TEST-DT-003 + test@example.com + 2024-01-15T12:00:00Z + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].sent).toBe('2024-01-15T12:00:00Z') + }) + + it('should parse datetime without seconds', () => { + const xml = ` + + TEST-DT-004 + test@example.com + 2024-01-15T12:00-00:00 + Actual + Alert + Public +` + + const alerts = parseCap(xml) + expect(alerts[0].sent).toBe('2024-01-15T12:00-00:00') + }) + }) + + describe('Multiple ResponseType Values', () => { + it('should parse multiple responseType values', () => { + const xml = ` + + TEST-MRT-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Fire + Wildfire Warning + Evacuate + Shelter + Monitor + Immediate + Extreme + Observed + +` + + const alerts = parseCap(xml) + const responseTypes = Array.isArray(alerts[0].infos[0].responseType) + ? alerts[0].infos[0].responseType + : [alerts[0].infos[0].responseType] + expect(responseTypes).toContain('Evacuate') + expect(responseTypes).toContain('Shelter') + expect(responseTypes).toContain('Monitor') + }) + }) + + describe('Multiple EventCode and Parameter', () => { + it('should parse multiple eventCode values', () => { + const xml = ` + + TEST-MEC-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Severe Weather + Immediate + Severe + Observed + + SAME + SVR + + + NWS + SEVERE.TSTORM + + +` + + const alerts = parseCap(xml) + const eventCodes = Array.isArray(alerts[0].infos[0].eventCode) + ? alerts[0].infos[0].eventCode + : [alerts[0].infos[0].eventCode] + expect(eventCodes.length).toBeGreaterThanOrEqual(1) + }) + + it('should parse multiple parameter values', () => { + const xml = ` + + TEST-MP-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Hurricane + Expected + Extreme + Likely + + WindSpeed + 120mph + + + StormSurge + 15ft + + + Rainfall + 12inches + + +` + + const alerts = parseCap(xml) + const parameters = Array.isArray(alerts[0].infos[0].parameter) + ? alerts[0].infos[0].parameter + : [alerts[0].infos[0].parameter] + expect(parameters.length).toBeGreaterThanOrEqual(1) + }) + }) + + describe('Multiple Geocode Values', () => { + it('should parse multiple geocode values in a single area', () => { + const xml = ` + + TEST-MGC-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Flood Warning + Expected + Moderate + Likely + + Multiple Counties + + FIPS6 + 006037 + + + FIPS6 + 006059 + + + UGC + CAZ041 + + + +` + + const alerts = parseCap(xml) + const geocodes = Array.isArray(alerts[0].infos[0].areas[0].geocode) + ? alerts[0].infos[0].areas[0].geocode + : [alerts[0].infos[0].areas[0].geocode] + expect(geocodes.length).toBeGreaterThanOrEqual(1) + }) + }) + + describe('Code Element', () => { + it('should parse single code value', () => { + const xml = ` + + TEST-CODE-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + IPAWSv1.0 +` + + const alerts = parseCap(xml) + expect(alerts[0].code).toBeDefined() + }) + + it('should parse multiple code values', () => { + const xml = ` + + TEST-CODE-002 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + IPAWSv1.0 + PROFILE:CAP-CP:0.4 +` + + const alerts = parseCap(xml) + expect(alerts[0].code).toBeDefined() + }) + }) + + describe('Polygon Validation', () => { + it('should parse closed polygon (first and last coordinates match)', () => { + const xml = ` + + TEST-POLY-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Geo + Earthquake + Immediate + Extreme + Observed + + Affected Region + 38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14 + + +` + + const alerts = parseCap(xml) + const polygon = alerts[0].infos[0].areas[0].polygon + expect(polygon).toBeDefined() + expect(typeof polygon).toBe('string') + const coords = (polygon as string).split(' ') + expect(coords[0]).toBe(coords[coords.length - 1]) + }) + + it('should parse polygon with minimum 4 coordinate pairs (triangle + closure)', () => { + const xml = ` + + TEST-POLY-002 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Geo + Test Event + Immediate + Moderate + Observed + + Triangle Area + 0.0,0.0 1.0,0.0 0.5,1.0 0.0,0.0 + + +` + + const alerts = parseCap(xml) + const polygon = alerts[0].infos[0].areas[0].polygon + expect(polygon).toBeDefined() + expect(typeof polygon).toBe('string') + const coords = (polygon as string).split(' ') + expect(coords.length).toBeGreaterThanOrEqual(4) + }) + + it('should parse polygon with WGS-84 valid latitude/longitude ranges', () => { + const xml = ` + + TEST-POLY-003 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Geo + Test Event + Immediate + Moderate + Observed + + Valid Coordinates + -90.0,-180.0 -90.0,180.0 90.0,180.0 90.0,-180.0 -90.0,-180.0 + + +` + + const alerts = parseCap(xml) + const polygon = alerts[0].infos[0].areas[0].polygon + expect(polygon).toBeDefined() + }) + }) + + describe('Circle Format Validation', () => { + it('should parse circle with valid format (lat,lon radius)', () => { + const xml = ` + + TEST-CIRCLE-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + CBRNE + Chemical Spill + Immediate + Severe + Observed + + Contamination Zone + 38.5,-120.5 5.0 + + +` + + const alerts = parseCap(xml) + const circle = alerts[0].infos[0].areas[0].circle + expect(circle).toBeDefined() + expect(circle).toMatch(/^-?\d+\.?\d*,-?\d+\.?\d* \d+\.?\d*$/) + }) + + it('should parse multiple circles in a single area', () => { + const xml = ` + + TEST-CIRCLE-002 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + CBRNE + Multiple Hazard Zones + Immediate + Severe + Observed + + Multiple Contamination Zones + 38.5,-120.5 5.0 + 39.0,-121.0 3.5 + + +` + + const alerts = parseCap(xml) + const circles = Array.isArray(alerts[0].infos[0].areas[0].circle) + ? alerts[0].infos[0].areas[0].circle + : [alerts[0].infos[0].areas[0].circle] + expect(circles.length).toBeGreaterThanOrEqual(1) + }) + }) + + describe('Message Type Relationships', () => { + it('should parse Update msgType with references to original alert', () => { + const xml = ` + + UPDATE-001 + nws@noaa.gov + 2024-01-15T12:00:00-00:00 + Actual + Update + Public + nws@noaa.gov,ALERT-001,2024-01-15T10:00:00-00:00 + + Met + Severe Thunderstorm + Immediate + Severe + Observed + Updated: Storm intensifying + +` + + const alerts = parseCap(xml) + expect(alerts[0].msgType).toBe('Update') + expect(alerts[0].references).toBeDefined() + expect(alerts[0].references).toContain('nws@noaa.gov') + expect(alerts[0].references).toContain('ALERT-001') + }) + + it('should parse Cancel msgType with references', () => { + const xml = ` + + CANCEL-001 + nws@noaa.gov + 2024-01-15T14:00:00-00:00 + Actual + Cancel + Public + nws@noaa.gov,ALERT-001,2024-01-15T10:00:00-00:00 nws@noaa.gov,UPDATE-001,2024-01-15T12:00:00-00:00 + + Met + Severe Thunderstorm + Past + Unknown + Unknown + Alert cancelled: Threat has passed + +` + + const alerts = parseCap(xml) + expect(alerts[0].msgType).toBe('Cancel') + expect(alerts[0].references).toBeDefined() + }) + + it('should parse Ack msgType acknowledging receipt', () => { + const xml = ` + + ACK-001 + local-ema@example.com + 2024-01-15T10:05:00-00:00 + Actual + Ack + Public + nws@noaa.gov,ALERT-001,2024-01-15T10:00:00-00:00 +` + + const alerts = parseCap(xml) + expect(alerts[0].msgType).toBe('Ack') + expect(alerts[0].references).toBeDefined() + }) + + it('should parse Error msgType for error notification', () => { + const xml = ` + + ERROR-001 + system@example.com + 2024-01-15T10:10:00-00:00 + Actual + Error + Public + Previous alert contained formatting errors + nws@noaa.gov,ALERT-BAD,2024-01-15T10:00:00-00:00 +` + + const alerts = parseCap(xml) + expect(alerts[0].msgType).toBe('Error') + expect(alerts[0].note).toBeDefined() + }) + }) + + describe('Character Entity References', () => { + it('should handle special characters in text fields', () => { + const xml = ` + + TEST-CHAR-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Emergency Alert + Immediate + Moderate + Observed + Alert & Warning: "Stay Safe" + Temperature < 32°F. Wind > 40mph. + Don't go outside. Stay "indoors". + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].headline).toContain('&') + expect(alerts[0].infos[0].description).toBeDefined() + expect(alerts[0].infos[0].instruction).toBeDefined() + }) + }) + + describe('Temporal Relationships', () => { + it('should parse effective, onset, and expires times', () => { + const xml = ` + + TEST-TIME-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + Winter Storm + Expected + Moderate + Likely + 2024-01-15T10:00:00-00:00 + 2024-01-15T18:00:00-00:00 + 2024-01-16T06:00:00-00:00 + Winter Storm Expected Tonight + Heavy snow expected starting this evening. + +` + + const alerts = parseCap(xml) + const info = alerts[0].infos[0] + expect(info.effective).toBe('2024-01-15T10:00:00-00:00') + expect(info.onset).toBe('2024-01-15T18:00:00-00:00') + expect(info.expires).toBe('2024-01-16T06:00:00-00:00') + }) + + it('should handle alert without onset (immediate effective time)', () => { + const xml = ` + + TEST-TIME-002 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Immediate Threat + Immediate + Extreme + Observed + 2024-01-15T10:00:00-00:00 + 2024-01-15T12:00:00-00:00 + +` + + const alerts = parseCap(xml) + const info = alerts[0].infos[0] + expect(info.effective).toBeDefined() + expect(info.onset).toBeUndefined() + expect(info.expires).toBeDefined() + }) + }) + + describe('Language Support', () => { + it('should parse language code in RFC 3066 format', () => { + const xml = ` + + TEST-LANG-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + fr-CA + Safety + Alerte d'urgence + Immediate + Severe + Observed + Alerte d'urgence en français canadien + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].language).toBe('fr-CA') + }) + + it('should parse three or more info blocks with different languages', () => { + const xml = ` + + TEST-LANG-002 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + en-US + Safety + Emergency Alert + Immediate + Severe + Observed + Emergency Alert + + + es-US + Safety + Alerta de Emergencia + Immediate + Severe + Observed + Alerta de Emergencia + + + zh-CN + Safety + 紧急警报 + Immediate + Severe + Observed + 紧急警报 + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos).toHaveLength(3) + expect(alerts[0].infos[0].language).toBe('en-US') + expect(alerts[0].infos[1].language).toBe('es-US') + expect(alerts[0].infos[2].language).toBe('zh-CN') + }) + }) + + describe('Boundary Conditions', () => { + it('should parse resource with large size value', () => { + const xml = ` + + TEST-BC-001 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Safety + Test + Immediate + Moderate + Possible + + Large Video File + video/mp4 + 1073741824 + + +` + + const alerts = parseCap(xml) + expect(alerts[0].infos[0].resources[0].size).toBe(1073741824) + }) + + it('should parse area with extreme altitude and ceiling values', () => { + const xml = ` + + TEST-BC-002 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Met + High Altitude Warning + Immediate + Moderate + Observed + + High Altitude Zone + 10000 + 50000 + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + expect(area.altitude).toBe(10000) + expect(area.ceiling).toBe(50000) + }) + + it('should parse area with negative altitude (below sea level)', () => { + const xml = ` + + TEST-BC-003 + test@example.com + 2024-01-15T10:00:00-00:00 + Actual + Alert + Public + + Env + Below Sea Level Alert + Immediate + Moderate + Observed + + Below Sea Level Zone + -100 + 0 + + +` + + const alerts = parseCap(xml) + const area = alerts[0].infos[0].areas[0] + expect(area.altitude).toBe(-100) + expect(area.ceiling).toBe(0) + }) + }) + + describe('Complex Real-World Scenarios', () => { + it('should parse comprehensive NOAA-style severe weather alert', () => { + const xml = ` + + NOAA-NWS-ALERTS-CA125ABC123456 + w-nws.webmaster@noaa.gov + 2024-01-15T10:47:00-08:00 + Actual + Alert + NWS National Weather Service + Public + IPAWSv1.0 + + en-US + Met + Severe Thunderstorm Warning + Shelter + Monitor + Immediate + Severe + Observed + 2024-01-15T10:47:00-08:00 + 2024-01-15T10:47:00-08:00 + 2024-01-15T11:30:00-08:00 + NWS Sacramento CA + Severe Thunderstorm Warning issued January 15 at 10:47AM PST until January 15 at 11:30AM PST by NWS Sacramento CA + The National Weather Service in Sacramento has issued a Severe Thunderstorm Warning for Central Sacramento County until 1130 AM PST. At 1047 AM PST, a severe thunderstorm was located near Sacramento, moving northeast at 25 mph. Hazard: 60 mph wind gusts and quarter size hail. Source: Radar indicated. Impact: Hail damage to vehicles is expected. Expect wind damage to roofs, siding, and trees. + For your protection move to an interior room on the lowest floor of a building. Large hail and damaging winds and continuous cloud to ground lightning is occurring with this storm. Move indoors immediately. + http://www.weather.gov + w-nws.webmaster@noaa.gov + + VTEC + /O.NEW.KSTO.SV.W.0001.240115T1847Z-240115T1930Z/ + + + TIME...MOT...LOC + 1847Z 239DEG 22KT 3850 12120 + + + SAME + SVR + + + NWS-IDP-SOURCE + RADAR + + + Radar Image + image/png + 45678 + http://www.weather.gov/radar/image.png + + + Central Sacramento County + 38.47,-121.50 38.51,-121.35 38.56,-121.35 38.60,-121.45 38.55,-121.55 38.47,-121.50 + + FIPS6 + 006067 + + + UGC + CAC067 + + + +` + + const alerts = parseCap(xml) + expect(alerts).toHaveLength(1) + expect(alerts[0].identifier).toBe('NOAA-NWS-ALERTS-CA125ABC123456') + expect(alerts[0].source).toBe('NWS National Weather Service') + expect(alerts[0].infos[0].event).toBe('Severe Thunderstorm Warning') + expect(alerts[0].infos[0].resources).toHaveLength(1) + expect(alerts[0].infos[0].areas[0].polygon).toBeDefined() + }) + + it('should parse AMBER Alert with all required fields', () => { + const xml = ` + + AMBER-CA-2024-001 + chp@doj.ca.gov + 2024-01-15T15:30:00-08:00 + Actual + Alert + California Highway Patrol + Public + AMBER + + en-US + Security + Rescue + Child Abduction Emergency + Monitor + Immediate + Severe + Observed + 2024-01-15T15:30:00-08:00 + 2024-01-16T03:30:00-08:00 + California Highway Patrol + AMBER Alert for Missing Child + The California Highway Patrol has issued an AMBER Alert for a missing 5-year-old child. Suspect is believed to be driving a blue 2015 Honda Civic, license plate 7ABC123. Child was last seen wearing a red jacket and blue jeans. + If you have any information about this abduction, call the California Highway Patrol immediately at 1-800-TELL-CHP (1-800-835-5247). Do not approach the suspect. + http://www.chp.ca.gov/amber + 1-800-835-5247 + + VehicleYear + 2015 + + + VehicleMake + Honda + + + VehicleModel + Civic + + + VehicleColor + Blue + + + LicensePlate + 7ABC123 + + + Statewide California + + FIPS6 + 006000 + + + +` + + const alerts = parseCap(xml) + expect(alerts).toHaveLength(1) + expect(alerts[0].code).toBe('AMBER') + const categories = Array.isArray(alerts[0].infos[0].category) + ? alerts[0].infos[0].category + : [alerts[0].infos[0].category] + expect(categories).toContain('Security') + expect(categories).toContain('Rescue') + const parameters = Array.isArray(alerts[0].infos[0].parameter) + ? alerts[0].infos[0].parameter + : [alerts[0].infos[0].parameter] + expect(parameters.length).toBeGreaterThanOrEqual(1) + }) + }) +}) + diff --git a/edge-apps/cap-alerting/index.ts b/edge-apps/cap-alerting/index.ts index 08f1fc91a..98be87330 100644 --- a/edge-apps/cap-alerting/index.ts +++ b/edge-apps/cap-alerting/index.ts @@ -8,20 +8,49 @@ import { } from '../edge-apps-library/src/index' import { XMLParser } from 'fast-xml-parser' +import { CAPFetcher } from './src/fetcher' + +interface CAPResource { + resourceDesc?: string + mimeType: string + size?: number + uri?: string + derefUri?: string + digest?: string + url: string +} + +interface CAPArea { + areaDesc: string + polygon?: string | string[] + circle?: string | string[] + geocode?: any + altitude?: number + ceiling?: number +} interface CAPInfo { language: string - category?: string + category?: string | string[] event?: string + responseType?: string | string[] urgency?: string severity?: string certainty?: string + audience?: string + effective?: string + onset?: string + expires?: string + senderName?: string headline?: string description?: string instruction?: string - resources: { mimeType: string; url: string }[] - areas: string[] - expires?: string + web?: string + contact?: string + parameter?: any + eventCode?: any + resources: CAPResource[] + areas: CAPArea[] } interface CAPAlert { @@ -30,11 +59,18 @@ interface CAPAlert { sent: string status?: string msgType?: string + source?: string scope?: string + restriction?: string + addresses?: string + code?: string | string[] + note?: string + references?: string + incidents?: string infos: CAPInfo[] } -function parseCap(xml: string): CAPAlert[] { +export function parseCap(xml: string): CAPAlert[] { const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) const json: any = parser.parse(xml) const alertsJson = json.alert ? (Array.isArray(json.alert) ? json.alert : [json.alert]) : [] @@ -56,20 +92,41 @@ function parseCap(xml: string): CAPAlert[] { language: info.language || '', category: info.category, event: info.event, + responseType: info.responseType, urgency: info.urgency, severity: info.severity, certainty: info.certainty, + audience: info.audience, + effective: info.effective, + onset: info.onset, + expires: info.expires, + senderName: info.senderName, headline: info.headline, description: info.description, instruction: info.instruction, + web: info.web, + contact: info.contact, + parameter: info.parameter, + eventCode: info.eventCode, resources: resourcesJson.map((res: any) => { return { + resourceDesc: res.resourceDesc, mimeType: res.mimeType || res['mimeType'], + size: res.size, + uri: res.uri, + derefUri: res.derefUri, + digest: res.digest, url: res.uri || res.resourceDesc || '', } }), - areas: areasJson.map((area: any) => area.areaDesc || '').filter((s: string) => s), - expires: info.expires, + areas: areasJson.map((area: any) => ({ + areaDesc: area.areaDesc || '', + polygon: area.polygon, + circle: area.circle, + geocode: area.geocode, + altitude: area.altitude, + ceiling: area.ceiling, + })), } }) @@ -79,7 +136,14 @@ function parseCap(xml: string): CAPAlert[] { sent: a.sent || '', status: a.status, msgType: a.msgType, + source: a.source, scope: a.scope, + restriction: a.restriction, + addresses: a.addresses, + code: a.code, + note: a.note, + references: a.references, + incidents: a.incidents, infos, }) }) @@ -87,7 +151,7 @@ function parseCap(xml: string): CAPAlert[] { return alerts } -function getNearestExit(tags: string[]): string | undefined { +export function getNearestExit(tags: string[]): string | undefined { for (const tag of tags) { const lower = tag.toLowerCase() if (lower.startsWith('exit:')) { @@ -100,28 +164,122 @@ function getNearestExit(tags: string[]): string | undefined { return undefined } -async function fetchCapData(feedUrl: string, offlineMode: boolean): Promise { - if (offlineMode) { - return localStorage.getItem('cap_last') +// Legacy cache migration - convert old cache to new format +function migrateLegacyCache(): void { + try { + const oldCache = localStorage.getItem('cap_last') + const newCache = localStorage.getItem('cap_feed_cache') + + if (oldCache && !newCache) { + console.log('Migrating legacy cache to new format') + localStorage.setItem('cap_feed_cache', oldCache) + localStorage.setItem('cap_feed_cache_meta', JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: true, + })) + localStorage.removeItem('cap_last') + } + } catch (err) { + console.error('Failed to migrate legacy cache:', err) } +} - try { - const cors = getCorsProxyUrl() - let url = feedUrl - if (feedUrl && feedUrl.match(/^https?:/)) { - url = `${cors}/${feedUrl}` +function highlightKeywords(text: string): string { + const keywords = [ + 'DO NOT', + 'DON\'T', + 'DO NOT', + 'IMMEDIATELY', + 'IMMEDIATE', + 'NOW', + 'MOVE TO', + 'EVACUATE', + 'CALL', + 'WARNING', + 'DANGER', + 'SHELTER', + 'TAKE COVER', + 'AVOID', + 'STAY', + 'SEEK', + ] + + let result = text + keywords.forEach((keyword) => { + const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') + result = result.replace(regex, '$1') + }) + + return result +} + +function splitIntoSentences(text: string): string[] { + return text + .split(/(?<=[.!?])\s+/) + .map((s) => s.trim()) + .filter((s) => s.length > 0) +} + +function getStatusBannerInfo(status?: string, msgType?: string): { text: string; classes: string } | null { + const statusLower = (status || '').toLowerCase() + const msgTypeLower = (msgType || '').toLowerCase() + + // Per CAP spec: status = Actual | Exercise | System | Test | Draft + if (statusLower === 'exercise') { + return { + text: 'EXERCISE - THIS IS A DRILL', + classes: 'bg-blue-600 text-white border-blue-800', + } + } + if (statusLower === 'test') { + return { + text: 'TEST MESSAGE - NOT AN ACTUAL ALERT', + classes: 'bg-gray-800 text-white border-gray-900', + } + } + if (statusLower === 'system') { + return { + text: 'SYSTEM MESSAGE', + classes: 'bg-gray-800 text-white border-gray-900', + } + } + if (statusLower === 'draft') { + return null // Don't show banner for drafts + } + + // Per CAP spec: msgType = Alert | Update | Cancel | Ack | Error + if (msgTypeLower === 'update') { + return { + text: 'UPDATED ALERT', + classes: 'bg-orange-600 text-white border-orange-600', + } + } + if (msgTypeLower === 'cancel') { + return { + text: 'ALERT CANCELLED', + classes: 'bg-gray-800 text-white border-gray-900', } + } + + // For Actual alerts with msgType=Alert, no context banner needed + return null +} - const response = await fetch(url) - if (!response.ok) throw new Error(`HTTP ${response.status}`) +function getSeverityClasses(severity?: string, urgency?: string): string { + const sev = (severity || '').toLowerCase() + const urg = (urgency || '').toLowerCase() - const text = await response.text() - localStorage.setItem('cap_last', text) - return text - } catch (err) { - console.warn('CAP fetch failed', err) - return localStorage.getItem('cap_last') + if (sev === 'extreme' || urg === 'immediate') { + return 'bg-red-600 text-white border-red-800 status-actual-pulse' } + if (sev === 'severe') { + return 'bg-orange-500 text-white border-yellow-600' + } + if (sev === 'moderate') { + return 'bg-yellow-400 text-black border-yellow-600' + } + return 'bg-blue-600 text-white border-blue-800' } function renderAlerts( @@ -139,28 +297,52 @@ function renderAlerts( slice.forEach((alert) => { const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0] + if (!info) return + const card = document.createElement('div') - card.className = 'alert-card' + card.className = 'alert-card w-[90vw] mx-[5vw] my-[3vh] bg-gray-100 rounded-xl overflow-hidden flex flex-col' + + // Use actual CAP status and msgType fields per CAP v1.2 spec + const statusBannerInfo = getStatusBannerInfo(alert.status, alert.msgType) + + // Only show context banner for Exercise/Test/System/Update/Cancel + if (statusBannerInfo) { + const statusBanner = document.createElement('div') + statusBanner.className = `${statusBannerInfo.classes} py-[2vh] px-[4vw] border-b-[1vh] status-stripe-pattern` + const statusText = document.createElement('div') + statusText.className = 'status-banner-text font-black uppercase tracking-[0.15em] text-center leading-none' + statusText.textContent = statusBannerInfo.text + statusBanner.appendChild(statusText) + card.appendChild(statusBanner) + } - const header = document.createElement('h2') - header.textContent = info.event || alert.identifier - card.appendChild(header) + const contentWrapper = document.createElement('div') + contentWrapper.className = 'px-[4vw] py-[3vh] flex-1 overflow-y-auto' - const meta = document.createElement('div') - meta.className = 'meta' - meta.textContent = `${info.urgency || ''} ${info.severity || ''} ${info.certainty || ''}`.trim() - card.appendChild(meta) + const eventTitle = document.createElement('h1') + eventTitle.className = 'event-title-text font-black text-red-600 uppercase leading-tight mb-[2vh]' + eventTitle.textContent = info.event || alert.identifier + contentWrapper.appendChild(eventTitle) - if (info.headline) { - const headline = document.createElement('h3') - headline.textContent = info.headline - card.appendChild(headline) + const metaParts: string[] = [] + if (info.urgency) metaParts.push(info.urgency.toUpperCase()) + if (info.severity) metaParts.push(info.severity.toUpperCase()) + if (info.certainty) metaParts.push(info.certainty.toUpperCase()) + + if (metaParts.length > 0) { + const badge = document.createElement('div') + badge.className = `severity-badge-text inline-block bg-orange-500 text-white font-extrabold uppercase px-[4vw] py-[2vh] rounded-lg mb-[3vh] tracking-wider leading-none` + badge.textContent = metaParts.join(' ') + contentWrapper.appendChild(badge) } + // Only show description if it adds new information beyond event name and context + // Per digital signage best practices: avoid redundancy if (info.description) { const desc = document.createElement('p') + desc.className = 'body-text text-black leading-snug mb-[3vh]' desc.textContent = info.description - card.appendChild(desc) + contentWrapper.appendChild(desc) } if (info.instruction) { @@ -171,37 +353,64 @@ function renderAlerts( .replace(/\{\{closest_exit\}\}/g, nearestExit) .replace(/\[\[closest_exit\]\]/g, nearestExit) } else { - instr += `\nNearest exit: ${nearestExit}` + instr += `\n\nNearest exit: ${nearestExit}` } } - const instEl = document.createElement('p') - instEl.className = 'instruction' - instEl.textContent = instr - card.appendChild(instEl) + + const instructionBox = document.createElement('div') + instructionBox.className = 'bg-yellow-100 border-l-[1vw] border-yellow-600 px-[4vw] py-[3vh] rounded-lg' + + const sentences = splitIntoSentences(instr) + + if (sentences.length > 2) { + const ul = document.createElement('ul') + ul.className = 'instruction-text text-black leading-snug' + sentences.forEach((sentence) => { + const li = document.createElement('li') + li.className = 'mb-[2vh] flex' + li.innerHTML = `•${highlightKeywords(sentence)}` + ul.appendChild(li) + }) + instructionBox.appendChild(ul) + } else { + const instP = document.createElement('p') + instP.className = 'instruction-text text-black leading-snug whitespace-pre-line' + instP.innerHTML = highlightKeywords(instr) + instructionBox.appendChild(instP) + } + + contentWrapper.appendChild(instructionBox) } info.resources.forEach((res) => { - if (res.mimeType.startsWith('image')) { + if (res.mimeType && res.mimeType.startsWith('image')) { + const imgWrapper = document.createElement('div') + imgWrapper.className = 'mt-[4vh] flex justify-center' const img = document.createElement('img') img.src = res.url - card.appendChild(img) - } else if (res.mimeType.startsWith('audio') && playAudio) { + img.className = 'max-w-full max-h-[20vh] object-contain rounded-lg' + imgWrapper.appendChild(img) + contentWrapper.appendChild(imgWrapper) + } else if (res.mimeType && res.mimeType.startsWith('audio') && playAudio) { const audio = document.createElement('audio') audio.src = res.url audio.controls = true - card.appendChild(audio) + audio.className = 'w-full mt-[4vh]' + contentWrapper.appendChild(audio) audio.play().catch(() => { /* autoplay blocked */ }) } }) + card.appendChild(contentWrapper) container.appendChild(card) }) } -async function startApp(): Promise { +export async function startApp(): Promise { setupTheme() + migrateLegacyCache() let settings: Partial> = {} let metadata: Partial> = {} @@ -223,7 +432,6 @@ async function startApp(): Promise { } const feedUrl: string = (settings.cap_feed_url as string) || '' - const interval = parseInt((settings.refresh_interval as string) || '5', 10) const lang = (settings.language as string) || 'en' const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) const playAudio = ((settings.audio_alert as string) || 'false') === 'true' @@ -234,41 +442,105 @@ async function startApp(): Promise { const tags: string[] = metadata.tags || [] const nearestExit = getNearestExit(tags) - async function update() { - let xml: string | null + let fetcher: CAPFetcher | null = null - if (testMode) { - try { - const resp = await fetch('static/test.cap') - xml = resp.ok ? await resp.text() : null - } catch (_) { - xml = null - } - } else if (demoMode && !feedUrl) { - const demoFiles = ['static/demo.cap', 'static/demo-severe.cap', 'static/demo-extreme.cap'] - const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)] + function handleUpdate(xml: string | null) { + if (xml) { try { - const resp = await fetch(randomFile) - xml = resp.ok ? await resp.text() : null - } catch (_) { - xml = null + const alerts = parseCap(xml) + renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) + } catch (err) { + console.error('Failed to parse or render alerts:', err) + // Don't crash, keep displaying previous content } - } else { - xml = await fetchCapData(feedUrl, offlineMode) - } - - if (xml) { - const alerts = parseCap(xml) - renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) } else { console.warn('No CAP data available') } } - await update() - signalReady() + // For test mode and demo mode, use static files + if (testMode) { + try { + const resp = await fetch('static/test.cap') + const xml = resp.ok ? await resp.text() : null + handleUpdate(xml) + signalReady() + } catch (err) { + console.error('Failed to load test data:', err) + signalReady() + } + } else if (demoMode && !feedUrl) { + // For demo mode without feed URL, rotate through demo files + const demoFiles = [ + 'static/demo-1-tornado.cap', + 'static/demo-2-fire.cap', + 'static/demo-3-flood.cap', + 'static/demo-4-earthquake.cap', + 'static/demo-5-hazmat.cap', + 'static/demo-6-shooter.cap', + ] + let currentIndex = 0 + + const loadDemoFile = async () => { + try { + const resp = await fetch(demoFiles[currentIndex]) + const xml = resp.ok ? await resp.text() : null + handleUpdate(xml) + currentIndex = (currentIndex + 1) % demoFiles.length + } catch (err) { + console.error('Failed to load demo file:', err) + } + } + + await loadDemoFile() + signalReady() + + // Rotate demo files every 30 seconds + setInterval(loadDemoFile, 30000) + } else if (!offlineMode && feedUrl) { + // Use the robust fetcher for live feeds + fetcher = new CAPFetcher({ + feedUrl, + refreshInterval: 30, // Hardcoded to 30 seconds as requested + maxRetries: 5, + initialRetryDelay: 1000, + maxRetryDelay: 30000, + corsProxyUrl: getCorsProxyUrl(), + }) + + // Start the fetcher - it will handle initial load and periodic updates + fetcher.start((xml) => { + handleUpdate(xml) + + // Signal ready after first update + if (!offlineMode) { + signalReady() + } + }) + + // Log stats periodically for debugging + setInterval(() => { + if (fetcher) { + const stats = fetcher.getStats() + console.log('Fetcher stats:', stats) + } + }, 60000) // Log every minute + } else if (offlineMode) { + // In offline mode, just load from cache + const cached = localStorage.getItem('cap_feed_cache') + handleUpdate(cached) + signalReady() + } else { + console.warn('No feed URL configured and not in demo mode') + signalReady() + } - setInterval(update, interval * 60 * 1000) + // Add a global reference for debugging + if (typeof window !== 'undefined') { + ;(window as any).capFetcher = fetcher + } } -startApp() +if (typeof window !== 'undefined' && typeof document !== 'undefined') { + startApp() +} diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index fbdd5ff3e..f6a9dbf02 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -4,21 +4,16 @@ "private": true, "type": "module", "scripts": { - "generate-mock-data": "screenly edge-app run --generate-mock-data", - "build:dev": "vite build --mode=development", - "build:dev:watch": "vite build --mode=development --watch", - "predev": "bun run generate-mock-data && bun run build:dev", - "dev": "run-p build:dev:watch dev:edge", - "dev:edge": "screenly edge-app run --path=dist/", - "build": "vite build", - "type-check": "tsc --noEmit", + "dev": "bun run build && screenly edge-app run", + "build": "bun run build:css && bun run build:js", + "build:css": "bunx @tailwindcss/cli -i ./src/input.css -o ./static/style.css --minify", + "build:js": "bun build src/main.ts --outdir static/js --target browser --format iife", "test": "bun test", "test:watch": "bun test --watch", - "lint": "eslint . --fix", - "format": "prettier --write index.ts README.md index.html", - "predeploy": "bun run build", - "deploy": "screenly edge-app deploy --path dist/", - "clear-mock-data": "rimraf mock-data.yml" + "type-check": "tsc --noEmit", + "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.ts index.test.ts src/fetcher.ts src/fetcher.test.ts src/main.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "format": "prettier --write src/ README.md index.html", + "deploy": "bun run build && screenly edge-app deploy" }, "dependencies": { "@photostructure/tz-lookup": "^11.3.0", @@ -28,15 +23,17 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@screenly/edge-apps": "workspace:../edge-apps-library", + "@tailwindcss/cli": "^4.0.0", + "@types/bun": "^1.3.3", "@types/node": "^24.10.1", + "autoprefixer": "^10.4.22", + "bun-types": "^1.3.3", "eslint": "^9.39.1", "jiti": "^2.6.1", - "npm-run-all2": "^8.0.4", "prettier": "^3.7.4", - "rimraf": "^6.1.2", + "tailwindcss": "^4.1.17", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.1", - "vite": "^7.2.6", - "vite-plugin-static-copy": "^3.1.4" + "typescript-eslint": "^8.48.1" } } diff --git a/edge-apps/cap-alerting/src/fetcher.test.ts b/edge-apps/cap-alerting/src/fetcher.test.ts new file mode 100644 index 000000000..a314eb8ff --- /dev/null +++ b/edge-apps/cap-alerting/src/fetcher.test.ts @@ -0,0 +1,612 @@ +/** + * Unit tests for CAPFetcher + */ + +import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test' +import { CAPFetcher } from './fetcher' + +// Mock localStorage +const localStorageMock = (() => { + let store: Record = {} + + return { + getItem: (key: string) => store[key] || null, + setItem: (key: string, value: string) => { + store[key] = value + }, + removeItem: (key: string) => { + delete store[key] + }, + clear: () => { + store = {} + }, + } +})() + +// Mock fetch +const mockFetch = mock() + +// Helper to wait for a condition +const waitFor = async (condition: () => boolean, timeout = 5000) => { + const start = Date.now() + while (!condition()) { + if (Date.now() - start > timeout) { + throw new Error('Timeout waiting for condition') + } + await new Promise(resolve => setTimeout(resolve, 50)) + } +} + +describe('CAPFetcher', () => { + beforeEach(() => { + // Setup mocks + global.localStorage = localStorageMock as any + global.fetch = mockFetch as any + global.window = { setInterval, clearInterval, setTimeout, clearTimeout } as any + + // Clear localStorage before each test + localStorageMock.clear() + + // Reset fetch mock + mockFetch.mockReset() + }) + + afterEach(() => { + mockFetch.mockRestore() + }) + + describe('Constructor', () => { + it('should create a fetcher with default config', () => { + const fetcher = new CAPFetcher({}) + expect(fetcher).toBeDefined() + expect(fetcher.getStats().isRunning).toBe(false) + }) + + it('should create a fetcher with custom config', () => { + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + refreshInterval: 60, + maxRetries: 3, + }) + expect(fetcher).toBeDefined() + }) + }) + + describe('Caching', () => { + it('should save valid data to cache', async () => { + const mockData = 'TEST' + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + // Wait for the fetch to complete + await waitFor(() => updateCallback.mock.calls.length > 0) + + // Check that data was cached + const cached = localStorageMock.getItem('cap_feed_cache') + expect(cached).toBe(mockData) + + // Check that metadata was saved + const meta = JSON.parse(localStorageMock.getItem('cap_feed_cache_meta') || '{}') + expect(meta.isValid).toBe(true) + expect(meta.timestamp).toBeDefined() + + fetcher.stop() + }) + + it('should not cache empty data', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => '', + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => mockFetch.mock.calls.length > 0) + + // Check that nothing was cached + const cached = localStorageMock.getItem('cap_feed_cache') + expect(cached).toBeNull() + + fetcher.stop() + }) + + it('should not cache invalid XML data', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => 'This is not XML', + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => mockFetch.mock.calls.length > 0) + + // Check that data was not cached + const cached = localStorageMock.getItem('cap_feed_cache') + expect(cached).toBeNull() + + fetcher.stop() + }) + + it('should use atomic write for cache', async () => { + const mockData = 'TEST' + + // Set up existing cache + localStorageMock.setItem('cap_feed_cache', 'old data') + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0) + + // Verify cache was updated + const cached = localStorageMock.getItem('cap_feed_cache') + expect(cached).toBe(mockData) + + // Verify temp keys were cleaned up + const tempCache = localStorageMock.getItem('cap_feed_cache_temp') + expect(tempCache).toBeNull() + + fetcher.stop() + }) + + it('should load from cache if initial fetch fails', async () => { + const cachedData = 'CACHED' + + // Set up cache + localStorageMock.setItem('cap_feed_cache', cachedData) + localStorageMock.setItem('cap_feed_cache_meta', JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: true, + })) + + mockFetch.mockRejectedValue(new Error('Network error')) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 2, + initialRetryDelay: 100, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0, 3000) + + // Should have called with cached data + expect(updateCallback.mock.calls[0][0]).toBe(cachedData) + + fetcher.stop() + }) + }) + + describe('Retry Logic', () => { + it('should retry on fetch failure', async () => { + const mockData = 'TEST' + + // Fail first two attempts, succeed on third + mockFetch + .mockRejectedValueOnce(new Error('Network error')) + .mockRejectedValueOnce(new Error('Network error')) + .mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 3, + initialRetryDelay: 100, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0, 3000) + + expect(mockFetch.mock.calls.length).toBe(3) + expect(updateCallback.mock.calls[0][0]).toBe(mockData) + + fetcher.stop() + }) + + it('should fall back to cache after all retries exhausted', async () => { + const cachedData = 'CACHED' + + // Set up cache + localStorageMock.setItem('cap_feed_cache', cachedData) + localStorageMock.setItem('cap_feed_cache_meta', JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: true, + })) + + mockFetch.mockRejectedValue(new Error('Network error')) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 2, + initialRetryDelay: 100, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0, 3000) + + expect(updateCallback.mock.calls[0][0]).toBe(cachedData) + + fetcher.stop() + }) + + it('should use exponential backoff', async () => { + mockFetch.mockRejectedValue(new Error('Network error')) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 3, + initialRetryDelay: 100, + }) + + const updateCallback = mock() + const startTime = Date.now() + + fetcher.start(updateCallback) + + await waitFor(() => mockFetch.mock.calls.length >= 3, 3000) + + const elapsed = Date.now() - startTime + + // Should take at least 100ms (1st retry) + 200ms (2nd retry) = 300ms + // With jitter, it could be slightly less, so check for at least 200ms + expect(elapsed).toBeGreaterThan(200) + + fetcher.stop() + }) + }) + + describe('HTTP Handling', () => { + it('should handle HTTP errors', async () => { + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 404, + statusText: 'Not Found', + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 1, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0, 2000) + + // Should call with null since no cache exists + expect(updateCallback.mock.calls[0][0]).toBeNull() + + fetcher.stop() + }) + + it('should add CORS proxy for http/https URLs', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => '', + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => mockFetch.mock.calls.length > 0) + + expect(mockFetch.mock.calls[0][0]).toBe('https://proxy.com/https://example.com/feed.xml') + + fetcher.stop() + }) + + it('should handle empty response', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => '', + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 1, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => mockFetch.mock.calls.length > 0) + + // Should not cache or call callback with empty data + const cached = localStorageMock.getItem('cap_feed_cache') + expect(cached).toBeNull() + + fetcher.stop() + }) + }) + + describe('Stats', () => { + it('should track statistics', async () => { + const mockData = 'TEST' + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + + let initialStats = fetcher.getStats() + expect(initialStats.isRunning).toBe(false) + expect(initialStats.lastSuccessfulFetch).toBe(0) + expect(initialStats.consecutiveFailures).toBe(0) + + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0) + + const stats = fetcher.getStats() + expect(stats.isRunning).toBe(true) + expect(stats.lastSuccessfulFetch).toBeGreaterThan(0) + expect(stats.cacheAge).toBeLessThan(5) // Should be very recent + + fetcher.stop() + }) + + it('should track consecutive failures', async () => { + mockFetch.mockRejectedValue(new Error('Network error')) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + maxRetries: 2, + initialRetryDelay: 100, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => mockFetch.mock.calls.length >= 2, 2000) + + const stats = fetcher.getStats() + expect(stats.consecutiveFailures).toBeGreaterThan(0) + + fetcher.stop() + }) + }) + + describe('Start and Stop', () => { + it('should not start if already running', async () => { + const mockData = 'TEST' + mockFetch.mockResolvedValue({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + + fetcher.start(updateCallback) + await waitFor(() => fetcher.getStats().isRunning) + + // Try to start again - should log warning + const consoleWarn = mock() + const originalWarn = console.warn + console.warn = consoleWarn + + fetcher.start(updateCallback) + expect(consoleWarn.mock.calls.length).toBeGreaterThan(0) + + console.warn = originalWarn + fetcher.stop() + }) + + it('should stop properly', async () => { + const mockData = 'TEST' + mockFetch.mockResolvedValue({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => fetcher.getStats().isRunning) + + fetcher.stop() + + expect(fetcher.getStats().isRunning).toBe(false) + }) + }) + + describe('Force Refresh', () => { + it('should force an immediate refresh', async () => { + const mockData = 'TEST' + mockFetch.mockResolvedValue({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + + await fetcher.forceRefresh(updateCallback) + + expect(mockFetch.mock.calls.length).toBe(1) + expect(updateCallback.mock.calls[0][0]).toBe(mockData) + }) + }) + + describe('Edge Cases', () => { + it('should handle missing feed URL', async () => { + const fetcher = new CAPFetcher({ + feedUrl: '', + corsProxyUrl: 'https://proxy.com', + maxRetries: 1, + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0, 2000) + + expect(updateCallback.mock.calls[0][0]).toBeNull() + + fetcher.stop() + }) + + it('should handle localStorage errors gracefully', async () => { + // Mock localStorage to throw errors + const originalSetItem = localStorageMock.setItem + localStorageMock.setItem = mock(() => { + throw new Error('QuotaExceededError') + }) + + const mockData = 'TEST' + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0) + + // Should still return data even though caching failed + expect(updateCallback.mock.calls[0][0]).toBe(mockData) + + // Restore original + localStorageMock.setItem = originalSetItem + + fetcher.stop() + }) + + it('should handle corrupted cache metadata', async () => { + // Set up corrupted metadata + localStorageMock.setItem('cap_feed_cache', '') + localStorageMock.setItem('cap_feed_cache_meta', 'invalid json') + + const mockData = 'NEW' + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0) + + // Should fetch fresh data instead of using corrupted cache + expect(updateCallback.mock.calls[0][0]).toBe(mockData) + + fetcher.stop() + }) + + it('should return null when cache has invalid flag', async () => { + // Set up cache with invalid flag + localStorageMock.setItem('cap_feed_cache', '') + localStorageMock.setItem('cap_feed_cache_meta', JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: false, + })) + + const mockData = 'NEW' + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => mockData, + }) + + const fetcher = new CAPFetcher({ + feedUrl: 'https://example.com/feed.xml', + corsProxyUrl: 'https://proxy.com', + }) + + const updateCallback = mock() + fetcher.start(updateCallback) + + await waitFor(() => updateCallback.mock.calls.length > 0) + + // Should fetch fresh data + expect(updateCallback.mock.calls[0][0]).toBe(mockData) + + fetcher.stop() + }) + }) +}) diff --git a/edge-apps/cap-alerting/src/fetcher.ts b/edge-apps/cap-alerting/src/fetcher.ts new file mode 100644 index 000000000..1349cf04c --- /dev/null +++ b/edge-apps/cap-alerting/src/fetcher.ts @@ -0,0 +1,348 @@ +/** + * Robust CAP feed fetcher with exponential backoff, caching, and periodic reloading + */ + +interface FetcherConfig { + feedUrl: string + refreshInterval: number // in seconds, default 30 + maxRetries: number // default 5 + initialRetryDelay: number // in ms, default 1000 + maxRetryDelay: number // in ms, default 30000 + corsProxyUrl: string +} + +interface CacheEntry { + data: string + timestamp: number + isValid: boolean +} + +const CACHE_KEY = 'cap_feed_cache' +const CACHE_METADATA_KEY = 'cap_feed_cache_meta' + +export class CAPFetcher { + private config: FetcherConfig + private intervalId: number | null = null + private isRunning: boolean = false + private lastSuccessfulFetch: number = 0 + private consecutiveFailures: number = 0 + + constructor(config: Partial) { + this.config = { + feedUrl: config.feedUrl || '', + refreshInterval: config.refreshInterval || 30, + maxRetries: config.maxRetries || 5, + initialRetryDelay: config.initialRetryDelay || 1000, + maxRetryDelay: config.maxRetryDelay || 30000, + corsProxyUrl: config.corsProxyUrl || '', + } + } + + /** + * Start the periodic fetching + */ + public start(onUpdate: (data: string | null) => void): void { + if (this.isRunning) { + console.warn('CAPFetcher is already running') + return + } + + this.isRunning = true + + // Initial fetch + this.fetchWithRetry().then((data) => { + if (data) { + onUpdate(data) + } else { + // If initial fetch fails, try to load from cache + const cached = this.getCachedData() + if (cached) { + console.log('Using cached data for initial load') + onUpdate(cached) + } else { + onUpdate(null) + } + } + }) + + // Set up periodic fetching + this.intervalId = window.setInterval(() => { + this.fetchWithRetry().then((data) => { + if (data) { + onUpdate(data) + } + // If fetch fails, we keep displaying the cached data without calling onUpdate + }) + }, this.config.refreshInterval * 1000) + } + + /** + * Stop the periodic fetching + */ + public stop(): void { + if (this.intervalId !== null) { + clearInterval(this.intervalId) + this.intervalId = null + } + this.isRunning = false + } + + /** + * Get cached data from localStorage + */ + private getCachedData(): string | null { + try { + const cached = localStorage.getItem(CACHE_KEY) + if (!cached) return null + + const meta = this.getCacheMetadata() + if (!meta || !meta.isValid) { + console.warn('Cache metadata indicates invalid data') + return null + } + + return cached + } catch (err) { + console.error('Error reading from cache:', err) + return null + } + } + + /** + * Get cache metadata + */ + private getCacheMetadata(): CacheEntry | null { + try { + const metaStr = localStorage.getItem(CACHE_METADATA_KEY) + if (!metaStr) return null + return JSON.parse(metaStr) as CacheEntry + } catch (err) { + console.error('Error reading cache metadata:', err) + return null + } + } + + /** + * Save data to cache with validation + */ + private saveToCacheWithValidation(data: string): boolean { + try { + // Validate the data before saving + if (!data || data.trim().length === 0) { + console.warn('Refusing to cache empty data') + return false + } + + // Basic XML validation - check if it looks like valid CAP XML + if (!data.includes(' { + for (let attempt = 0; attempt < this.config.maxRetries; attempt++) { + try { + const data = await this.fetchData() + + if (data) { + // Successfully fetched - save to cache + const saved = this.saveToCacheWithValidation(data) + if (saved) { + return data + } else { + console.warn('Failed to cache data, but returning it anyway') + return data + } + } + } catch (err) { + console.error(`Fetch attempt ${attempt + 1} failed:`, err) + this.consecutiveFailures++ + + // If this isn't the last attempt, wait before retrying + if (attempt < this.config.maxRetries - 1) { + const delay = this.calculateBackoffDelay(attempt) + console.log(`Retrying in ${delay}ms...`) + await this.sleep(delay) + } + } + } + + // All retries exhausted + console.error(`Failed to fetch after ${this.config.maxRetries} attempts`) + + // Return cached data as fallback + const cached = this.getCachedData() + if (cached) { + console.log('Falling back to cached data') + return cached + } + + return null + } + + /** + * Calculate exponential backoff delay with jitter + */ + private calculateBackoffDelay(attempt: number): number { + const exponentialDelay = Math.min( + this.config.initialRetryDelay * Math.pow(2, attempt), + this.config.maxRetryDelay + ) + + // Add jitter (±25% randomness) to prevent thundering herd + const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1) + return Math.floor(exponentialDelay + jitter) + } + + /** + * Sleep utility + */ + private sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) + } + + /** + * Perform the actual fetch + */ + private async fetchData(): Promise { + if (!this.config.feedUrl) { + throw new Error('Feed URL is not configured') + } + + let url = this.config.feedUrl + + // Add CORS proxy if needed + if (this.config.feedUrl.match(/^https?:/)) { + url = `${this.config.corsProxyUrl}/${this.config.feedUrl}` + } + + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), 15000) // 15 second timeout + + try { + const response = await fetch(url, { + signal: controller.signal, + cache: 'no-cache', + headers: { + 'Accept': 'application/xml, text/xml, */*', + }, + }) + + clearTimeout(timeoutId) + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`) + } + + const text = await response.text() + + if (!text || text.trim().length === 0) { + throw new Error('Received empty response') + } + + return text + } catch (err) { + clearTimeout(timeoutId) + + if (err instanceof Error) { + if (err.name === 'AbortError') { + throw new Error('Fetch timeout after 15 seconds') + } + throw err + } + + throw new Error('Unknown fetch error') + } + } + + /** + * Get statistics about the fetcher's performance + */ + public getStats() { + return { + isRunning: this.isRunning, + lastSuccessfulFetch: this.lastSuccessfulFetch, + consecutiveFailures: this.consecutiveFailures, + cacheAge: this.getCacheAge(), + } + } + + /** + * Get cache age in seconds + */ + private getCacheAge(): number | null { + const meta = this.getCacheMetadata() + if (!meta) return null + return Math.floor((Date.now() - meta.timestamp) / 1000) + } + + /** + * Force a refresh (useful for debugging) + */ + public async forceRefresh(onUpdate: (data: string | null) => void): Promise { + const data = await this.fetchWithRetry() + if (data) { + onUpdate(data) + } + } +} + diff --git a/edge-apps/cap-alerting/src/input.css b/edge-apps/cap-alerting/src/input.css new file mode 100644 index 000000000..08cf91422 --- /dev/null +++ b/edge-apps/cap-alerting/src/input.css @@ -0,0 +1,135 @@ +@import "tailwindcss"; + +/* Modern Digital Signage Design - Viewport-based sizing */ + +/* Custom animations */ +@keyframes pulse-banner { + 0%, 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.95; + transform: scale(1.005); + } +} + +.status-actual-pulse { + animation: pulse-banner 1.5s ease-in-out infinite; +} + +@media (prefers-reduced-motion: reduce) { + .status-actual-pulse { + animation: none; + } +} + +.status-stripe-pattern { + background-image: repeating-linear-gradient( + 135deg, + transparent, + transparent 2rem, + rgba(255, 255, 255, 0.08) 2rem, + rgba(255, 255, 255, 0.08) 4rem + ); +} + +/* Viewport-based typography optimized for digital signage readability */ +/* Balanced for visibility and fitting content in viewport */ + +.status-banner-text { + font-size: clamp(1.25rem, 3.5vmin, 6rem); + line-height: 1.1; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.event-title-text { + font-size: clamp(2rem, 5vmin, 10rem); + line-height: 1; + text-shadow: 0 2px 8px rgba(220, 38, 38, 0.15); + letter-spacing: -0.02em; +} + +.severity-badge-text { + font-size: clamp(1rem, 2vmin, 4rem); + line-height: 1.1; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); +} + +.headline-text { + font-size: clamp(1.5rem, 3.5vmin, 7rem); + line-height: 1.2; + letter-spacing: -0.01em; +} + +.body-text { + font-size: clamp(1.125rem, 2.5vmin, 5rem); + line-height: 1.35; +} + +.instruction-text { + font-size: clamp(1.25rem, 3vmin, 6rem); + line-height: 1.4; +} + +/* Enhanced keyword highlighting for quick scanning */ +.instruction-text strong { + font-weight: 900; + color: rgb(153, 27, 27); +} + +/* Modern card styling */ +.alert-card { + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08); +} + +/* Modern instruction box */ +.instruction-box { + box-shadow: inset 4px 0 0 0 rgb(234, 179, 8), 0 4px 12px rgba(0, 0, 0, 0.05); + backdrop-filter: blur(10px); + background: linear-gradient(135deg, rgb(254, 249, 195) 0%, rgb(254, 243, 199) 100%); +} + +/* Severity badge modern styling */ +.severity-badge { + box-shadow: 0 4px 12px rgba(249, 115, 22, 0.25); + background: linear-gradient(135deg, rgb(249, 115, 22) 0%, rgb(234, 88, 12) 100%); +} + +/* Status banner gradients */ +.status-banner-blue { + background: linear-gradient(135deg, rgb(37, 99, 235) 0%, rgb(29, 78, 216) 100%); +} + +.status-banner-red { + background: linear-gradient(135deg, rgb(220, 38, 38) 0%, rgb(185, 28, 28) 100%); +} + +.status-banner-orange { + background: linear-gradient(135deg, rgb(249, 115, 22) 0%, rgb(234, 88, 12) 100%); +} + +.status-banner-gray { + background: linear-gradient(135deg, rgb(75, 85, 99) 0%, rgb(55, 65, 81) 100%); +} + +/* Responsive adjustments for common signage resolutions */ +@media (min-width: 1920px) and (min-height: 1080px) { + .instruction-text { + font-size: clamp(2rem, 3.5vmin, 7rem); + } + + .event-title-text { + font-size: clamp(3rem, 6vmin, 12rem); + } +} + +@media (min-width: 3840px) and (min-height: 2160px) { + .instruction-text { + font-size: clamp(2.5rem, 3.5vmin, 7rem); + } + + .event-title-text { + font-size: clamp(4rem, 6vmin, 12rem); + } +} diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts new file mode 100644 index 000000000..adb123c5a --- /dev/null +++ b/edge-apps/cap-alerting/src/main.ts @@ -0,0 +1,490 @@ +import { + setupTheme, + signalReady, + getMetadata, + getTags, + getSettings, + getCorsProxyUrl, +} from '@screenly/edge-apps' + +import { XMLParser } from 'fast-xml-parser' + +interface CAPResource { + resourceDesc?: string + mimeType: string + size?: number + uri?: string + derefUri?: string + digest?: string + url: string +} + +interface CAPArea { + areaDesc: string + polygon?: string | string[] + circle?: string | string[] + geocode?: any + altitude?: number + ceiling?: number +} + +interface CAPInfo { + language: string + category?: string | string[] + event?: string + responseType?: string | string[] + urgency?: string + severity?: string + certainty?: string + audience?: string + effective?: string + onset?: string + expires?: string + senderName?: string + headline?: string + description?: string + instruction?: string + web?: string + contact?: string + parameter?: any + eventCode?: any + resources: CAPResource[] + areas: CAPArea[] +} + +interface CAPAlert { + identifier: string + sender: string + sent: string + status?: string + msgType?: string + source?: string + scope?: string + restriction?: string + addresses?: string + code?: string | string[] + note?: string + references?: string + incidents?: string + infos: CAPInfo[] +} + +export function parseCap(xml: string): CAPAlert[] { + const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) + const json: any = parser.parse(xml) + const alertsJson = json.alert ? (Array.isArray(json.alert) ? json.alert : [json.alert]) : [] + + const alerts: CAPAlert[] = [] + + alertsJson.forEach((a: any) => { + const infosJson = a.info ? (Array.isArray(a.info) ? a.info : [a.info]) : [] + + const infos: CAPInfo[] = infosJson.map((info: any) => { + const resourcesJson = info.resource + ? Array.isArray(info.resource) + ? info.resource + : [info.resource] + : [] + const areasJson = info.area ? (Array.isArray(info.area) ? info.area : [info.area]) : [] + + return { + language: info.language || '', + category: info.category, + event: info.event, + responseType: info.responseType, + urgency: info.urgency, + severity: info.severity, + certainty: info.certainty, + audience: info.audience, + effective: info.effective, + onset: info.onset, + expires: info.expires, + senderName: info.senderName, + headline: info.headline, + description: info.description, + instruction: info.instruction, + web: info.web, + contact: info.contact, + parameter: info.parameter, + eventCode: info.eventCode, + resources: resourcesJson.map((res: any) => { + return { + resourceDesc: res.resourceDesc, + mimeType: res.mimeType || res['mimeType'], + size: res.size, + uri: res.uri, + derefUri: res.derefUri, + digest: res.digest, + url: res.uri || res.resourceDesc || '', + } + }), + areas: areasJson.map((area: any) => ({ + areaDesc: area.areaDesc || '', + polygon: area.polygon, + circle: area.circle, + geocode: area.geocode, + altitude: area.altitude, + ceiling: area.ceiling, + })), + } + }) + + alerts.push({ + identifier: a.identifier || '', + sender: a.sender || '', + sent: a.sent || '', + status: a.status, + msgType: a.msgType, + source: a.source, + scope: a.scope, + restriction: a.restriction, + addresses: a.addresses, + code: a.code, + note: a.note, + references: a.references, + incidents: a.incidents, + infos, + }) + }) + + return alerts +} + +export function getNearestExit(tags: string[]): string | undefined { + for (const tag of tags) { + const lower = tag.toLowerCase() + if (lower.startsWith('exit:')) { + return tag.slice(5).trim() + } + if (lower.startsWith('exit-')) { + return tag.slice(5).trim() + } + } + return undefined +} + +async function fetchCapData(feedUrl: string, offlineMode: boolean): Promise { + if (offlineMode) { + return localStorage.getItem('cap_last') + } + + try { + const cors = getCorsProxyUrl() + let url = feedUrl + if (feedUrl && feedUrl.match(/^https?:/)) { + url = `${cors}/${feedUrl}` + } + + const response = await fetch(url) + if (!response.ok) throw new Error(`HTTP ${response.status}`) + + const text = await response.text() + localStorage.setItem('cap_last', text) + return text + } catch (err) { + console.warn('CAP fetch failed', err) + return localStorage.getItem('cap_last') + } +} + +function highlightKeywords(text: string): string { + const keywords = [ + 'DO NOT', + "DON'T", + 'IMMEDIATELY', + 'IMMEDIATE', + 'NOW', + 'MOVE TO', + 'EVACUATE', + 'CALL', + 'WARNING', + 'DANGER', + 'SHELTER', + 'TAKE COVER', + 'AVOID', + 'STAY', + 'SEEK', + 'TURN AROUND', + 'GET TO', + 'LEAVE', + ] + + let result = text + keywords.forEach((keyword) => { + const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') + result = result.replace(regex, '$1') + }) + + return result +} + +function splitIntoSentences(text: string): string[] { + return text + .split(/(?<=[.!?])\s+/) + .map((s) => s.trim()) + .filter((s) => s.length > 0) +} + +function proxyUrl(url: string): string { + if (url && url.match(/^https?:/)) { + const cors = getCorsProxyUrl() + return `${cors}/${url}` + } + return url +} + +function renderAlerts( + alerts: CAPAlert[], + nearestExit: string | undefined, + lang: string, + maxAlerts: number, + playAudio: boolean +): void { + const container = document.getElementById('alerts') + if (!container) return + + container.innerHTML = '' + const slice = alerts.slice(0, maxAlerts) + + slice.forEach((alert) => { + const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0] + if (!info) return + + const card = document.createElement('div') + card.className = 'alert-card w-full h-full bg-white flex flex-col overflow-y-auto' + + if (alert.status) { + const statusBanner = document.createElement('div') + + let baseClasses = + 'w-full text-center font-black uppercase tracking-[0.15em] flex-shrink-0 status-stripe-pattern status-banner-text py-[2.5vh] px-[4vw] text-white ' + + let statusText = alert.status.toUpperCase() + if (alert.status === 'Exercise') { + statusText = 'EXERCISE - THIS IS A DRILL' + baseClasses += 'status-banner-blue' + } else if (alert.status === 'Test') { + statusText = 'TEST - NOT A REAL EMERGENCY' + baseClasses += 'status-banner-gray' + } else if (alert.status === 'System') { + statusText = 'SYSTEM TEST' + baseClasses += 'status-banner-gray' + } else if (alert.status === 'Draft') { + statusText = 'DRAFT - NOT ACTIVE' + baseClasses += 'status-banner-orange' + } else if (alert.status === 'Actual') { + statusText = 'ACTUAL EMERGENCY' + baseClasses += 'status-banner-red status-actual-pulse' + } + + statusBanner.className = baseClasses + statusBanner.textContent = statusText + card.appendChild(statusBanner) + } + + const headerRow = document.createElement('div') + headerRow.className = 'flex items-center justify-between gap-[2vw] mx-[5vw] mt-[2vh] mb-[1.5vh]' + + const header = document.createElement('h2') + header.className = 'text-red-600 font-black uppercase leading-none event-title-text' + header.textContent = info.event || alert.identifier + headerRow.appendChild(header) + + const meta = document.createElement('div') + meta.className = + 'severity-badge inline-block text-white rounded-xl font-black uppercase tracking-wider flex-shrink-0 severity-badge-text px-[4vw] py-[2vh]' + meta.textContent = `${info.urgency || ''} ${info.severity || ''} ${info.certainty || ''}`.trim() + headerRow.appendChild(meta) + + card.appendChild(headerRow) + + if (info.headline) { + const headline = document.createElement('h3') + headline.className = 'font-extrabold leading-tight flex-shrink-0 headline-text text-gray-900 ' + headline.className += 'mx-[5vw] mb-[1.5vh]' + headline.textContent = info.headline + card.appendChild(headline) + } + + if (info.description) { + const desc = document.createElement('p') + desc.className = 'font-semibold leading-snug body-text text-gray-800 ' + desc.className += 'mx-[5vw] mb-[2vh]' + desc.textContent = info.description + card.appendChild(desc) + } + + if (info.instruction) { + let instr = info.instruction + if (nearestExit) { + if (instr.includes('{{closest_exit}}') || instr.includes('[[closest_exit]]')) { + instr = instr.replace(/\{\{closest_exit\}\}/g, nearestExit).replace(/\[\[closest_exit\]\]/g, nearestExit) + } else { + instr += `\n\nNearest exit: ${nearestExit}` + } + } + + const instructionBox = document.createElement('div') + instructionBox.className = + 'instruction-box rounded-xl flex-shrink-0 px-[4vw] py-[2.5vh] mx-[5vw] mb-[2vh]' + + const sentences = splitIntoSentences(instr) + + if (sentences.length > 2) { + const ul = document.createElement('ul') + ul.className = 'instruction-text leading-snug text-gray-900' + sentences.forEach((sentence) => { + const li = document.createElement('li') + li.className = 'mb-[1vh] flex items-start' + const bullet = document.createElement('span') + bullet.className = 'mr-[2vw] flex-shrink-0 font-black text-orange-600' + bullet.textContent = '•' + const content = document.createElement('span') + content.className = 'flex-1 font-extrabold' + content.innerHTML = highlightKeywords(sentence) + li.appendChild(bullet) + li.appendChild(content) + ul.appendChild(li) + }) + instructionBox.appendChild(ul) + } else { + const instP = document.createElement('p') + instP.className = 'instruction-text font-extrabold leading-snug whitespace-pre-line text-gray-900' + instP.innerHTML = highlightKeywords(instr) + instructionBox.appendChild(instP) + } + + card.appendChild(instructionBox) + } + + info.resources.forEach((res) => { + if (res.mimeType && res.mimeType.startsWith('image')) { + const imgWrapper = document.createElement('div') + imgWrapper.className = 'mx-[5vw] my-[2vh]' + const img = document.createElement('img') + img.className = 'max-w-full max-h-[20vh] rounded-2xl object-contain shadow-lg' + img.src = proxyUrl(res.url) + imgWrapper.appendChild(img) + card.appendChild(imgWrapper) + } else if (res.mimeType && res.mimeType.startsWith('audio') && playAudio) { + const proxiedUrl = proxyUrl(res.url) + console.log('Creating audio player for:', res.url, '-> proxied:', proxiedUrl, 'MIME type:', res.mimeType) + const audio = document.createElement('audio') + audio.className = 'w-[90vw] flex-shrink-0 mx-[5vw] my-[2vh] rounded-xl' + audio.style.height = 'clamp(3rem, 5vh, 10rem)' + audio.src = proxiedUrl + audio.controls = true + audio.autoplay = true + audio.preload = 'auto' + audio.crossOrigin = 'anonymous' + card.appendChild(audio) + + audio.addEventListener('loadeddata', () => { + console.log('Audio loaded successfully:', proxiedUrl) + }) + + audio.addEventListener('error', (e) => { + console.error('Audio load error:', proxiedUrl, e) + }) + + audio.play().catch((err) => { + console.warn('Autoplay blocked or failed:', err.message, '- Click play button to start audio') + }) + } + }) + + container.appendChild(card) + }) +} + +export async function startApp(): Promise { + setupTheme() + + let settings: Partial> = {} + let metadata: Partial> = {} + + try { + settings = getSettings() + localStorage.setItem('screenly_settings', JSON.stringify(settings)) + } catch (_) { + const cached = localStorage.getItem('screenly_settings') + settings = cached ? (JSON.parse(cached) as Partial>) : {} + } + + try { + metadata = getMetadata() + localStorage.setItem('screenly_metadata', JSON.stringify(metadata)) + } catch (_) { + const cachedMeta = localStorage.getItem('screenly_metadata') + metadata = cachedMeta ? (JSON.parse(cachedMeta) as Partial>) : {} + } + + const feedUrl: string = (settings.cap_feed_url as string) || '' + const interval = parseInt((settings.refresh_interval as string) || '5', 10) + const lang = (settings.language as string) || 'en' + const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) + const playAudio = ((settings.audio_alert as string) || 'false') === 'true' + const offlineMode = ((settings.offline_mode as string) || 'false') === 'true' + const testMode = ((settings.test_mode as string) || 'false') === 'true' + const demoMode = ((settings.demo_mode as string) || 'false') === 'true' + + console.log('CAP Settings:', { + playAudio, + audio_alert: settings.audio_alert, + demoMode, + testMode, + maxAlerts, + }) + + const tags: string[] = metadata.tags || [] + const nearestExit = getNearestExit(tags) + + async function update() { + let xml: string | null + + if (testMode) { + try { + const resp = await fetch('static/test.cap') + xml = resp.ok ? await resp.text() : null + } catch (_) { + xml = null + } + } else if (demoMode && !feedUrl) { + const demoFiles = [ + 'static/demo-1-tornado.cap', + 'static/demo-2-fire.cap', + 'static/demo-3-flood.cap', + 'static/demo-4-earthquake.cap', + 'static/demo-5-hazmat.cap', + 'static/demo-6-shooter.cap', + ] + const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)] + try { + const resp = await fetch(randomFile) + xml = resp.ok ? await resp.text() : null + } catch (_) { + xml = null + } + } else { + xml = await fetchCapData(feedUrl, offlineMode) + } + + if (xml) { + const alerts = parseCap(xml) + renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) + } else { + console.warn('No CAP data available') + } + } + + await update() + signalReady() + + setInterval(update, interval * 60 * 1000) +} + +if (typeof window !== 'undefined' && typeof document !== 'undefined') { + window.onload = function () { + startApp() + } +} diff --git a/edge-apps/cap-alerting/static/demo-extreme.cap b/edge-apps/cap-alerting/static/demo-1-tornado.cap similarity index 68% rename from edge-apps/cap-alerting/static/demo-extreme.cap rename to edge-apps/cap-alerting/static/demo-1-tornado.cap index f00aa301d..524bd9625 100644 --- a/edge-apps/cap-alerting/static/demo-extreme.cap +++ b/edge-apps/cap-alerting/static/demo-1-tornado.cap @@ -1,11 +1,12 @@ DEMO-TORNADO-EMERGENCY-001 - emergency@demo.screenly.io + demo@screenly.io 2024-01-15T15:45:00-00:00 - Actual + Exercise Alert Public + THIS IS A DEMONSTRATION EXERCISE - NOT A REAL EMERGENCY en-US Met @@ -17,37 +18,21 @@ 2024-01-15T15:45:00-00:00 2024-01-15T15:45:00-00:00 2024-01-15T16:30:00-00:00 - National Weather Service + National Weather Service (DEMO) TORNADO EMERGENCY - TAKE COVER NOW! THIS IS A PARTICULARLY DANGEROUS SITUATION. A large and extremely dangerous tornado has been confirmed on the ground. This is a life-threatening situation. Flying debris will be deadly to those caught without shelter. Mobile homes will be destroyed. TAKE COVER NOW! Move to a basement or an interior room on the lowest floor of a sturdy building. Avoid windows. If you are outdoors, in a mobile home, or in a vehicle, move to the closest substantial shelter and protect yourself from flying debris. Use {{closest_exit}} if evacuation is necessary. https://weather.gov/tornado-emergency Emergency Services: 911 - - SAME - TOR - - - TornadoRating - EF3 or greater expected - - Live Radar - image/gif - https://example.com/radar/tornado-live.gif - - - Emergency Siren - audio/mp3 - https://example.com/audio/tornado-siren.mp3 + Tornado Warning Siren + audio/mpeg + https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3 Tornado Path - Northern Suburbs 39.00,-121.00 39.10,-120.90 39.15,-120.85 39.20,-121.10 39.00,-121.00 - - FIPS6 - 006067 - + diff --git a/edge-apps/cap-alerting/static/demo-2-fire.cap b/edge-apps/cap-alerting/static/demo-2-fire.cap new file mode 100644 index 000000000..ef04cf533 --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-2-fire.cap @@ -0,0 +1,36 @@ + + + DEMO-FIRE-DRILL-001 + demo@screenly.io + 2024-01-15T14:30:00-00:00 + Exercise + Alert + Public + THIS IS A DEMONSTRATION EXERCISE - NOT A REAL EMERGENCY + + en-US + Fire + Fire Drill + Evacuate + Immediate + Minor + Observed + 2024-01-15T14:30:00-00:00 + 2024-01-15T15:30:00-00:00 + Building Safety Systems (DEMO) + Scheduled Fire Drill in Progress + This is a scheduled fire drill. All occupants must evacuate the building immediately using the nearest available exit. This is not an actual emergency. + Please proceed calmly to {{closest_exit}} and gather at the designated assembly point in the parking lot. Do not use elevators. Wait for the all-clear signal before re-entering the building. + https://example.com/safety/fire-drill + Building Security: (555) 123-4567 + + Fire Alarm Evacuation Alert + audio/mpeg + https://archive.org/download/fire-alarm-sound/fire-alarm-1.mp3 + + + Main Building - All Floors + + + + diff --git a/edge-apps/cap-alerting/static/demo-3-flood.cap b/edge-apps/cap-alerting/static/demo-3-flood.cap new file mode 100644 index 000000000..1e494b0f7 --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-3-flood.cap @@ -0,0 +1,43 @@ + + + DEMO-FLASH-FLOOD-001 + demo@screenly.io + 2024-06-20T16:15:00-00:00 + Exercise + Alert + Public + THIS IS A DEMONSTRATION EXERCISE - NOT A REAL EMERGENCY + + en-US + Met + Flash Flood Warning + Evacuate + Avoid + Immediate + Severe + Likely + 2024-06-20T16:15:00-00:00 + 2024-06-20T16:30:00-00:00 + 2024-06-20T21:00:00-00:00 + National Weather Service (DEMO) + Flash Flood Warning - Move to Higher Ground + Flash Flood Warning for urban areas and small streams. Heavy rainfall has caused rapid flooding in low-lying areas. Water levels are rising quickly and roads are becoming impassable. DO NOT attempt to drive through flooded roadways. + Move to higher ground immediately. Do not walk or drive through flood waters - just 6 inches of moving water can knock you down, and 1 foot of water can sweep your vehicle away. Use {{closest_exit}} to reach higher floors. If trapped, move to the highest point and call for help. + https://weather.gov/flood-safety + Emergency Services: 911 + + ExpectedRainfall + 3-5 inches in 2 hours + + + Flood Warning Alert Tone + audio/mpeg + https://archive.org/download/EASAlertTones/EAS_Alert_Tones/Flood_Warning.mp3 + + + Downtown and River Valley Areas + 40.71,-74.01 40.73,-73.99 40.75,-74.00 40.74,-74.02 40.71,-74.01 + + + + diff --git a/edge-apps/cap-alerting/static/demo-4-earthquake.cap b/edge-apps/cap-alerting/static/demo-4-earthquake.cap new file mode 100644 index 000000000..3c8cdd521 --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-4-earthquake.cap @@ -0,0 +1,47 @@ + + + DEMO-EARTHQUAKE-001 + demo@screenly.io + 2024-03-10T08:42:00-00:00 + Exercise + Alert + Public + THIS IS A DEMONSTRATION EXERCISE - NOT A REAL EMERGENCY + + en-US + Geo + Earthquake Alert + Monitor + Assess + Past + Moderate + Observed + 2024-03-10T08:42:00-00:00 + 2024-03-10T08:41:30-00:00 + 2024-03-10T12:00:00-00:00 + US Geological Survey (DEMO) + Magnitude 5.4 Earthquake Detected + A magnitude 5.4 earthquake occurred at 8:41 AM local time. The epicenter was located 15 miles northeast of downtown. Moderate shaking was felt across the metropolitan area. Aftershocks are possible in the coming hours. + Check yourself and others for injuries. Inspect your surroundings for hazards such as damaged buildings, gas leaks, or downed power lines. Be prepared for aftershocks. Use {{closest_exit}} if structural damage is visible. Do not use elevators. Monitor emergency channels for updates. + https://earthquake.usgs.gov + Emergency Services: 911 + + Magnitude + 5.4 + + + Depth + 8.2 km + + + Earthquake Alert Notification + audio/mpeg + https://archive.org/download/earthquake-alert-sound/earthquake-warning.mp3 + + + Metropolitan Area and Surrounding Regions + 34.05,-118.25 50 + + + + diff --git a/edge-apps/cap-alerting/static/demo-5-hazmat.cap b/edge-apps/cap-alerting/static/demo-5-hazmat.cap new file mode 100644 index 000000000..438e57a32 --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-5-hazmat.cap @@ -0,0 +1,48 @@ + + + DEMO-HAZMAT-SHELTER-001 + demo@screenly.io + 2024-09-05T19:20:00-00:00 + Exercise + Alert + Public + THIS IS A DEMONSTRATION EXERCISE - NOT A REAL EMERGENCY + + en-US + CBRNE + Safety + Hazardous Materials Incident + Shelter + Avoid + Immediate + Severe + Observed + 2024-09-05T19:20:00-00:00 + 2024-09-05T19:20:00-00:00 + 2024-09-05T23:00:00-00:00 + Emergency Management (DEMO) + Shelter in Place - Hazardous Materials Release + A chemical spill at an industrial facility has released hazardous vapors into the air. The affected area is downwind of the release. Residents in the evacuation zone should shelter in place immediately until the all-clear is given. + SHELTER IN PLACE IMMEDIATELY. Go indoors and close all windows and doors. Turn off ventilation systems, air conditioning, and fans. Seal gaps under doors with wet towels. Move to an interior room away from windows. Do NOT go outside. Do NOT attempt to evacuate unless instructed by authorities. Use {{closest_exit}} only if ordered to evacuate. + https://emergency.example.com/hazmat + Emergency Information: 1-800-555-SAFE + + Chemical + Ammonia vapor + + + EvacuationRadius + 1 mile + + + Hazmat Shelter-in-Place Alert + audio/mpeg + https://archive.org/download/EASAlertTones/EAS_Alert_Tones/Civil_Emergency_Message.mp3 + + + Industrial Zone and Downwind Areas + 41.88,-87.63 1.5 + + + + diff --git a/edge-apps/cap-alerting/static/demo-6-shooter.cap b/edge-apps/cap-alerting/static/demo-6-shooter.cap new file mode 100644 index 000000000..a82caf8bf --- /dev/null +++ b/edge-apps/cap-alerting/static/demo-6-shooter.cap @@ -0,0 +1,47 @@ + + + DEMO-ACTIVE-SHOOTER-001 + demo@screenly.io + 2024-06-20T14:30:00-00:00 + Exercise + Alert + Public + THIS IS A DEMONSTRATION EXERCISE - NOT A REAL EMERGENCY + + en-US + Security + Active Shooter Alert + Shelter + Execute + Immediate + Extreme + Observed + 2024-06-20T14:30:00-00:00 + 2024-06-20T14:30:00-00:00 + 2024-06-20T18:00:00-00:00 + Campus Security (DEMO) + Active Shooter on Campus - Lockdown in Effect + Active shooter reported on campus. Law enforcement is on scene. This is an active and dangerous situation. All personnel must take immediate protective action. + Run, Hide, Fight. If safe to evacuate, run to {{closest_exit}} immediately. If evacuation is not possible, hide in a secure location, lock and barricade doors, silence phones, and stay quiet. Do not open doors for anyone except law enforcement. Call 911 when safe. As a last resort, if confronted, take action to defend yourself. + https://campus-security.edu/emergency + Emergency: 911 | Campus Security: (555) 123-4567 + + Location + Main Campus - Building C, 3rd Floor + + + Action + Lockdown + + + Emergency Lockdown Alert + audio/mpeg + https://archive.org/download/EASAlertTones/EAS_Alert_Tones/Civil_Emergency_Message.mp3 + + + University Campus and Surrounding Area + 40.75,-74.00 40.76,-73.98 40.77,-73.99 40.76,-74.01 40.75,-74.00 + + + + diff --git a/edge-apps/cap-alerting/static/demo-severe.cap b/edge-apps/cap-alerting/static/demo-severe.cap deleted file mode 100644 index 3de7f5c83..000000000 --- a/edge-apps/cap-alerting/static/demo-severe.cap +++ /dev/null @@ -1,46 +0,0 @@ - - - DEMO-SEVERE-WEATHER-001 - weather@demo.screenly.io - 2024-01-15T10:00:00-00:00 - Actual - Alert - Public - - en-US - Met - Severe Thunderstorm Warning - Shelter - Immediate - Severe - Observed - 2024-01-15T10:00:00-00:00 - 2024-01-15T10:15:00-00:00 - 2024-01-15T12:00:00-00:00 - National Weather Service - Severe Thunderstorm Warning - Take Shelter Immediately - A severe thunderstorm capable of producing damaging winds in excess of 60 mph and large hail has been detected. The storm is moving northeast at 35 mph and will affect the area within the next 15 minutes. - Move to an interior room on the lowest floor of a sturdy building. Avoid windows. If you are outdoors, seek shelter immediately. Do not use elevators during severe weather. - https://weather.gov/alerts/severe - National Weather Service: 1-800-WEATHER - - MaxWindSpeed - 65mph - - - MaxHailSize - 1.5 inches - - - Radar Image - image/png - https://example.com/radar/storm-001.png - - - Downtown Metropolitan Area - 38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14 - 0 - 15000 - - - diff --git a/edge-apps/cap-alerting/static/demo.cap b/edge-apps/cap-alerting/static/demo.cap deleted file mode 100644 index 19d5963fc..000000000 --- a/edge-apps/cap-alerting/static/demo.cap +++ /dev/null @@ -1,51 +0,0 @@ - - - DEMO-FIRE-2024-001 - demo@screenly.io - 2024-01-15T14:30:00-00:00 - Actual - Alert - Public - - en-US - Fire - Fire Drill - Evacuate - Immediate - Minor - Observed - 2024-01-15T14:30:00-00:00 - 2024-01-15T15:30:00-00:00 - Building Safety Systems - Scheduled Fire Drill in Progress - This is a scheduled fire drill. All occupants must evacuate the building immediately using the nearest available exit. This is not an actual emergency. - Please proceed calmly to {{closest_exit}} and gather at the designated assembly point in the parking lot. Do not use elevators. Wait for the all-clear signal before re-entering the building. - https://example.com/safety/fire-drill - Building Security: (555) 123-4567 - - Main Building - All Floors - 37.7749,-122.4194 37.7849,-122.4094 37.7949,-122.4194 37.7749,-122.4194 - - - - es-US - Fire - Simulacro de Incendio - Evacuate - Immediate - Minor - Observed - 2024-01-15T14:30:00-00:00 - 2024-01-15T15:30:00-00:00 - Sistemas de Seguridad del Edificio - Simulacro de Incendio Programado en Progreso - Este es un simulacro de incendio programado. Todos los ocupantes deben evacuar el edificio inmediatamente usando la salida disponible más cercana. Esta no es una emergencia real. - Por favor, diríjase con calma a {{closest_exit}} y reúnase en el punto de reunión designado en el estacionamiento. No use los ascensores. Espere la señal de seguridad antes de volver a entrar al edificio. - https://example.com/safety/fire-drill - Seguridad del Edificio: (555) 123-4567 - - Edificio Principal - Todos los Pisos - 37.7749,-122.4194 37.7849,-122.4094 37.7949,-122.4194 37.7749,-122.4194 - - - diff --git a/edge-apps/cap-alerting/static/js/main.js b/edge-apps/cap-alerting/static/js/main.js new file mode 100644 index 000000000..8630ee074 --- /dev/null +++ b/edge-apps/cap-alerting/static/js/main.js @@ -0,0 +1,6118 @@ +(() => { + var __create = Object.create; + var __getProtoOf = Object.getPrototypeOf; + var __defProp = Object.defineProperty; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __toESM = (mod, isNodeMode, target) => { + target = mod != null ? __create(__getProtoOf(mod)) : {}; + const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; + for (let key of __getOwnPropNames(mod)) + if (!__hasOwnProp.call(to, key)) + __defProp(to, key, { + get: () => mod[key], + enumerable: true + }); + return to; + }; + var __moduleCache = /* @__PURE__ */ new WeakMap; + var __toCommonJS = (from) => { + var entry = __moduleCache.get(from), desc; + if (entry) + return entry; + entry = __defProp({}, "__esModule", { value: true }); + if (from && typeof from === "object" || typeof from === "function") + __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, { + get: () => from[key], + enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable + })); + __moduleCache.set(from, entry); + return entry; + }; + var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); + var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); + }; + + // node_modules/.bun/@photostructure+tz-lookup@11.3.0/node_modules/@photostructure/tz-lookup/tz.js + var require_tz = __commonJS((exports, module) => { + function tzlookup(Y, W) { + var X = "XKXJXJXIXIXSXSXRXRXQXQXP##U;U;U;#$UZUZUZUZUZXHXGXGXTXTXXXXXYXYXZXZY#Y#Y$Y$Y%Y%Y&Y&Y'Y'XUXUXVXVXWXKXJXJXIXIXSXSU:U$#%#&V,#'U;#(#)UZUZUZUZUZ#*UZXGXGVPVPVP#+YIYIYIYI#,W@W@W@W@W@W@Y&X/X/X/X/X/XVXWVTUV#-T,T,#.U:U:#/#0#1#2#3U;U;U;UZUZUZUZ#4#5YJYJXGYJ#6#7#8YIYIYI#9X1#:W@W@W@#;X/X/#<#=#>#?#@VTVT#A#B#CT,T,#D#E#F#G#H#IV,#J#K#L#MUZUZUZUZX9X9#N#OYJ#P#Q#R#SYI#T#U#VX1#W#XW@W@#Y#ZX/$#$$$%$&$'$($)$*$+$,T,T,$-$.$/U$$0$1$2$3$4$5$6$7UZUZXLXLXH$8$9$:$;$<$=$>YI$?$@$A$B$C$D$E$FW6$G$H$I$J$K$LW;$MT+T+T+XJXIXIXSVB$N$O$P$Q$R$S$T$U$V$WV7XMXLXLXHY6$X$Y$Z%#%$%%%&%'%(%)VR%*%+%,%-%.%/%0%1%2%3%4W;XVT+XKXJXJXIXIXSXSUC%5%6%7TN%8%9%:%;U0XMXMX3X3XH%<%=%>%?%@%A%B%C%D%E%F%G%HX+%I%J%KWX%L%M%N%OXUXVXVXWXKXJXJXIXIXSXSUC%P%Q%RTN%SUUXOX4XNXMXMXLX3X8%T%US2%V%W%X%Y%Z&#&$&%&&&'WXWXWXWXWX&(&)X(XUXUXVXVXW&*ZCZCXIXIXSXSXR&+&,&-&.&/&0UTXNXNXMXMXLXL&1&2&3S2&4T(&5&6WT&7&8&9&:&;&<&=&>&?&@&AX(X(XUX(XVXVXWXKZCZCZCXIXSXSXRUK&B&C&D&E&F&G&HXNXMXMXLX6&I&J&K&L&M&N&O&P&Q&R&SWHW?W?&T&U&V&W&X&YY'X(ZUXUXVZYXWXKXJXJXIXIXSXSXRXRXQXQ&Z'#'$'%'&''XMXMXLX6'(')'*'+','-'.'/'0'1VQXZW?'2Y$'3'4'5'6WGZPZ9'7'8ZH'9XWXKXJZEZEXIXSXSXRXRXQXQZ?':';'<'='>'?XMXLUWXH'@'A'B'C'D'E'F'G'HXZXZZ1W*Y$'I'J'K'L'M'NW8Z9'O'P'QZWZDZDXJZEXIXSXSXRXRXQXQZ?'R'S'TUG'U'V'W'XXLXHXHXGSS'Y'Z(#($(%XYZ0XZ(&Y#Y$Y$W7('((()W8(*ZS(+ZA(,(-(.(/ZTZE(0(1XSXRXRXQXQXPUBUB(2(3(4(5(6(7XLXHX;XGXGSP(8(9(:(;((?(@(A(B(CZA(D(E(FZLZT(GZV(HXSXRXRXQXQXPXPUB(I(J(K(L(MXLV3XHXHX;XG(N(O(P(Q(RZ*(SZ2Y#Y#Y$Y$Y%Y%XEXE(T(UX>X>ZN(VZ=(WXJXJZVZVZ@(XZQXRZ:XQXPXPV1(Y(Z)#V3V3XLXLXHXHXGXGT*)$)%)&Z*Z*XZXZY#Y#Y$Y$Y%Y%XEXE)')()))*XV)+XWZ6XJXJXIXIXSXSXRXRXQXQXPXPV1),)-).V3XMXLXLXHX;XGXGXTSG)/SGXYXYXZXZY#Y#Y$Y$Y%Y%XE)0)1)2)3XFXCZ6Z6Z8XJXJXIXIXSXSXRXRXQXQXPXPTR)4)5T0XMXMXLXLXHX;XGXGXTXTXXXXXYXYXZXZY#Z/Y$Y$Y%Y%Y&Y&Y')6)7XDXVZ6Z6XKXJXJXIXIXSXSXRXRXQXQXPXP)8)9):XT;T;XLXLXHXHXGXGXTXTVMVMVMVM)?VJVJVG)@VOVFVFVFVHVHVHVHVH)AVKXWXKXJXJXIXIXSXSXRXRXQXQXPXPVLVLT;T;T;T;)BVL)CY()DVNVNVN)EVMVMVM)FVJVJ)G)HVO)I)J)K)L)M)N)O)P)QVKVKVKVKVKVKXIXSXSXRXRXQXQXPXPVLVLT;T;T;T;)RVL)SY()TVNVNVN)UVMVMVM)VVJVJ)W)XVOVOVO)YXEXEXEXEXE)ZVKVKVKVKVKVKVKVKVKVKVKVKVKVKVK*#*$*%*&*'*(*)***+*,*-*.*/*0*1*2*3*4*5*6*7*8*9*:*;*<*=*>*?*@*A*B*CVKVKXPXPV,*CXNXNU;UZ*BTH*CTHTHV,THV,V,*BV,*CU;*CV>V>*CUZ*DUZUZUZTV*DVP*DVPXXY#W@YIY#XJT,UVT,XSXS*B*CU$U$*C*D*DTH*ETHTH*ETHTH*EV,THTHV,*E*F*GUZ*GUZ*HUZUZV4XHVPVPYJ*GYJYJ*G*HXXXX*HYIYIX1YI*H*HW@X1*IW@*I*J*KX/*KX/*L*L*MX.*N*N*OWZWZ*OWZWZWZXVXVWZ*OVTVTVTUV*NUVUVUVUVT,UVT,*MVC*NVC*NU:VC*OU:U:*OU:U$*OU$U$*O*PU$U$THV,U$V,V,*OV,*P*PU;*Q*RU;U;*RU;U;U;U;*RX9XHXHX7XGXGX7YBYJ*PYJ*QYWYWYW*Q*QY9Y9*R*RYIYIYIYIYI*RYIYIYI*R*S*S*TX1X1X1X1*T*U*UW@*VW@*VX/*W*XX/X/*XX/X/*XX/*Y*Y*ZW>+#+#WZ+$WE+$+%WEWE+%+&WE+'VTVTW;+'VTVT+'VTXKUVXKT+UVUVT+UV+%T,UVT,VE+%XSV5+%VB+&VB+&+'VB+(+(V.+)+*V.VD+*VDVD+*VDVDV,+*V@++U;++U;V@V@++V@V@++U*+,U*XNXNU*V7YBYBXH++YBYB++YBXGYJYB+++++,+-+.YW+.+/+0+0+1+2+3YIYI+3YI+3+4+5+6+6X1+7X1X1X1+7+8X1+8+9+:+:+;+<+=+=+>+?+@W@W@W@+@+@+AW6W6W6+A+BW)X/X/+B+CX/+CX/X.X.X.X.+CX.WEWUXUWE+BXUW;W;W;W;T+VB+A+B+C+C+D+E+F+F+GTYTYV.VD+G+HVDVDTN+HV@V@+H+I+IV@V@V@V@V@V@+IV@+I+J+K+KV7+LV7+L+MYKYK+M+NYK+O+O+P+Q+R+R+S+T+U+U+V+W+X+X+Y+Z,#,#,$,%,&,&,',(,),),*,+VV,+,,,-,.VR,.VRVR,.,/,0X+,0,1,2X*X*,2X*X*,2,3X*X*,3,4X*,5,5,6WXWX,6,7,8,9X.,9X.,:WUXUWUWEWZ,9,:WZ,:,;UC,<,,?,?,@UUUU,@UUUUUU,@U0XNXNXH,@XHY@,@,A,BYD,B,CYDYD,C,DYOYO,D,EYO,F,F,G,H,I,I,JY;Y;,J,KY;,L,L,M,N,O,O,PVW,Q,Q,R,S,T,T,U,V,W,W,X,YX+X+,YWXWX,Y,ZWXWX,Z-#WXWXWX-#WX-$-$X.-%Y'X.X(X(X(-$WEXUXUUC-$-%-&-&TY-'-(-(TN-)TNTN-)TN-*XH-*XHS<-*-+S<-,-,---.-/YOY,T(T(Y,-.-/S;-/-0S;-1-1-2-3-4-4X&-5X&X&-5X&X&-5-6-7-8-8-9-:-;-;-<-=->WXWWWXWW-=X(X(X(ZJZCXKZCV?-->-?-@-ATNTN-AUMTN-AUM-B-BUT-CUTXHX5XHSC-B-C-DT$S2S2-D-E-ET(-F-G-GS;-HS;S;-HS;-I-I-JWT-KX&-K-L-M-MW<-NW<-N-O-PW?W?-PW?W?-P-QW?-R-R-S-T-U-UWX-V-WWXWX-WWXWXWX-W-XWXX(-XX(UK-XUKXQ-X-YUNUNUNUMUN-Y-YU1-ZV=-Z.#.$.%.%V2.&.'VAT-.'.(XH.(X6.).)T$.*T$.*S4.+S4.+.,S4T#.,T#T#T#SZ.,SZSZ.,SJ.-SJ.-SJSJ....WT./.0WTWT.0VQ.0WH.1WHW?.1W?Y$.1X0X0X0.1.2.3.4.4WX.5.6.6X#WX.7.7Y&WGY&XP.7XPXP.7UFXPTQTFTFV$.7TF.7.8TKTK.8TK.9TAXNU/XNXH.8XHS7.8.9.:.;.;.<.=.>.>.?.@SM.@.ASM.B.BSZ.C.DSJSJ.D.ESJ.E.FS1.F.GS1.H.H.ISWSWW?W?W?.IW?.IW?.J.J.K.L.M.MW3W3W3.M.N.OWGZBXUZ9Z9XUXUZ9ZRZHZH.MZHTQV$Z?XP.LTF.MTFTF.MTF.NTK.N.O.P.P.Q.R.S.SXMTBTBSD.SXHXHS/.SXGXG.SSMXGT'.SSB.T.U.U.V.WSL.W.X.Y.ZSH.Z/#/$S1/$SYSW/$SWSWXYW?W7Y$W7/#/$W7/%W7W7W7/%/%/&/'WFWG/'WF/(ZPZP/(/)Z9ZRXUZRZRZFXVXVZHZHXVZWZ?UBXPUB/%/&/'UB/'/(/)/*/*V0/+V0TB/+TB/,U(U(U(/,U(UWU(UWSN/+SN/,/,/-/./////0SQ/1/1/2S@S@/2/3/4XYY#Z1Z+Z+W7WOW7/3/3WF/4WFWFW8WF/4W8/4W8/5ZSZ7ZS/5ZKZWXVXVZWZWZ>Z>Z>Z/?/?S@/@/AS@Z.ST/AZ0Z0/AZ*/AW7Y%Y%/AWFY&Y&WF/AXEXE/A/B/CX@/C/DX@X>/DZSX>XUZS/DZSZSZAXV/DZAXWZ>XWZ=Z=ZX/CZXZVZV/CZVZVZVZV/CUB/CUB/DUAUA/D/E/E/F/G/H/HV3/IV3V3/IV3V3XT/IXTT*/ISPT*/J/J/KSE/L/L/M/NSTSTZ*XYZ*XZZ2Z4Z2/LX@/MX@X@X>/MX>Z;Z;ZN/MZXZXZ6XKZ@ZQXSXSXOV1V1/K/K/L/MT2/M/N/O/PT*/P/Q/R/R/SSG/T/TST/UXX/UX@/VX=X@X>X=/VX>X>/V/WX>X>/W/XZNZNZMXVV1/WV1/X/X/Y/Z0#0#0$0%0&SG0&SGSGXE0&XEXE0&X=Y'X=X=0&X=0'XFXF0'0(0(0)0*T1T70*0+T7Y'0+Y'Y'0+0,XBXBXOTRXOV+0+0,0-0.0.XNT6XVM0>VJ0?VJ0?VG0@VGVGVOVGVO0?XE0@XE0@VK0AVK0AVLVKVKVLVLVKVKT;T;VKVKT;T;VKVKT;T;VKVKT;T;VKVK0UZV>V>TVTVUZUZXXXXVPYIT,TWT,TWTWU:VCU:U$U$THTHU$U$THTHU$THU$THU$THTHTHV,V,THTHV,V,THTHV,U;V,U;V,V,THV,V,U;V,U;UZUZUZV4UZV4UZUZYJYJYWYWYJYJ/IY9YJYJY9YI/HYIYIYIYIX1/HX1X1W@X1X1X1W@X1X1W@X/W@X/W@W@W@X/W@X/X/X/Y'X/X.X.X/X.X/X.X/X/X.X.X/X/X.X.X.X.X.WZX/X/X.WZX/X/WZWZX/XUWZWZWZVTVTVTVTVTVTUVT,TWT,TWT,TWT,TWVCU:VCVCVCU:VCVCU:U:VCU:THTHU$U$THTHU$U$THTHU$THV,U;T>T>T>T>V,U;U;U;T>U;T>U;U;U;U;U;U;V@U;U;V@V@U;XNU*XNYJYJ/*YWYWYW/*YWYW/*/+/,YWY9/,Y9Y9Y9/,/-YIYI/-YIYIYIYI/-YIYIYIX1YIYIX1X1YIYIX1X1/*X1X1X1X1X1X1/*X1X1/*X1W@W@X1W@X1W@X1W@W@X/W@X/W@W6W6W6X/X/W6X/X/X/X//%X.X.X/W>X//$X/W>X.X.W>W>WZWZW>X,W>X,X.X.WZWZX,WZX,WEX.X.WZWZWZWEWZWZWEWEWZVTWEWEVTVTWEW;WEW;W;W;W;VTW;W;VTVTW;VTUVT,XJXJVBU=U=V5.NVBV5VBV5V5VBVBU'U'VBVBU'U'TXTXVBTXVBVBU$V.U$V.U$V.U$.IV.V.VYI.Y.Y.Z/#/$/$/%/&/'YI/'/(/)/)X1/*/+/+X1/,X1X1X1/,WRX1X1WRVRX1X1WLWL/*/+VRVRWLWLVRVR/*X)/+/,X)X)/,X)/,WKVR/-WKWKW%/-X)X)X)/-X)W@X)W@/,/-/.//WJ///0/1W@/1W@W@W@W@/1W6W@W@W6W6W6X/W)W)W6W6W6W)W)X/W)/.X/X//.X/X/X.X.X.X.WUX.WUWEWEW;W;VBVB/+VBVBUCXRUC/*UCUCUCVB/*/+/,U$U$/,/-UCUCUC/-TYTYUCTYU$V/?Z'Z'/?/@/@/A/B/C/C/D/E/F/F/G/HY?/H/IY?Y?/I/JY1Y1/J/KY1/L/L/MY?Y?/MYI/N/OY?Y?Y?/OY?Y?/O/PYIYI/P/Q/Q/R/SZ&/S/TYIYI/T/UYIYI/UYSZ&/V/V/WWMWM/W/XYI/YVXVXVX/Y/YX1WMVV/Y/ZVVVVVXVV/Z0#X1WRVVWRWRVRWRWRVVVVWSWSVRVRWSWSW%/WVRVRW%W%VRVRW%W@W%W%VRVR/UX+W@W@W5W5W@W@W5X*W5W5X+W5W6W6X*X*W6W6/QX*/QW)W)W)W)W)X*X*W)W)/P/QX*WXWXWX/PWXWXWXWXWXWX/PX/X//PX//PX.X.X.WX/PWXWX/PWX/Q/RX.WUX.WUX.WUX./QWZW;WZWZWEWZWEWEUC/OUC/PTGTG/P/QUC/QUCTY/QTY/RTY/RTNTNTNTN/RTN/STZTZ/S/TTN/TTN/U/UUU/VUUV@/V/W/X/X/YUUUU/YUUUUUU/YU0XNXNYDYDYD/YYDYD/YYDYD/YYD/Z/ZYD0#YDYKYK0#0$YK0$YKYK0$YO0%0&0&0'YOYO0'0(0)0*0*0+0,0-0-0.Y,Y,0.0/000101020304Y,04Y,05Y;Y;05Y;XXYTY;Y;YT04Y;Y;YI04Y;05YIYI0506Y;06Y;07YIYI0708VUVUW#VU0708090:W#W#0:XYVUVUVWVUWVWVWV09VWVW090:WVWS0:WVWSWSWVVRVW09VW0:WV0:0;0<0VRVR0>0?0?0@0A0B0B0CW/W/VRVR0C0DVRX+X+X+0CX+X+X+X+X*WXWXX*X*WXWXX*X*WXX*X*X*X*0@X*WX0@WXWXWXWX0@0@0AWP0BWX0B0C0DWPWP0DWWX(0DX(X(0D0E0F0GUC0GXRV?0GV&0HU20HTY0I0JV&0J0K0LTYTYTP0LTY0LTYTNTY0LTNTN0LUU0MUU0MUU0NUUXHY@XHS<0M0NS1>1?X#X(UK1?XQUN1?UNUNUNUN1?UNUNUN1?UNU-UM1?1@TJ1@TCU-1AU1U1TMTMU1UTU1U1XOXOV=TFU<1>U1>SCT$T$XHT$S?1>SCT$T$T$T$T$S?T$T$S4T$S4T$S4T$S4S2S2S4S4S2S2S219S2T#T#T#SZT(SZSZT(SJSZSJSZSJSZSJ15SJSJSJSJSJSJ1515WTSJWT15WTS3S3WT1516VQWTWTVQVQWTWTWTWHWHWHVQWHW?13W?Y$W,13Y$X0X0131415X-W$X-X-14W$15W$1516W$17W$17W$1818W3X-19WXWXW3WX18WXY&Y&Y&WGWXWG17WGWGWG17U-XPXP1718U&UFV$TFV$TFXOXO1718TF18TF19XN19TKV;TK19TKTK191:S?1;S?1;S7S>S4S4S41;S>S>1;SDS>1;1S4S4S41<1S01>T#T%T#1>T#1?SM1?T&1@1AT#T#SM1AT#SZSM1ASM1A1BSBSZSZ1BSZ1BSZSZ1CSZSZS5S5SJSJS5SHSJ1ASHSHSJ1ASJS1SJSHSHSH1@1AS1S11AVQ1B1CS1SWS1S1VQVQVQSWVQVQSWSW1@W*1AW*W?X0W?X0Y$X0W?W$X0W$X01?1?1@1AWN1AW$W$W$WNWNW$W3WN1@WN1AY&Y&W3WGWXWGY&WGWXWGW3WBZHZGZHZHV$V$TFXOXOTFU.U.TF1;TFTFTFTFTFUGTKU/TKTETKTKUGUGTETEUGTEU/17U/V%V%TLV%TLU/U/TEV0V%TLV0TBXMXM14XMSX14SXSX14S015S01516S0XGSM1617SBSSSBSN17SBSBSNS9S5S5S516S5S5SLS5SBS9S915S5S515SQSHSHSQ1515SQSQSQSQSQSQ15S1S1SHS114SI15SISISY15SYS1S1S1SWS1SWSWSWW$13W7WA13WAWAWA13WA1415W7WB15WOW(WB1516WBWG16WBWBWFWOWFWG15WFWFWFWFWFW8W8ZPW8W8ZPY'W8W8U.U.1213U.U.13UBUB13UBUBTFTFUBUBTFUGTFUGUBUBUBU%U%UGU%UGUGV0UGUGUGV0UGV0TBU(TBU(1,U(1-U(U(U(1-U(SN1-S9S91-1.1/10S910S9SLSLSLSLSQ1/SLSPSL1/SQSLSQSLSQSQSQSQ1.SQ1/SQ1/SQSQ1/SI10S@10SYS@11SYSY11SYSWSWSYXY1011S@S@W7WOW7W7WOWOWO10WOWFW7W7WFW8WFW8W8ZSW8ZSW8ZSW8ZSZ7Z7Z7ZAZZXKZZZ5ZZSZSX>X>ZAZAZSZSZAZAZ;Z;Z=Z=Z=ZXZTXIXIZVZVZVZVZ@UBUA0OUAV10OV10PUAUAUA0PUAT=0PT=TTTTUATITTTTTITIT=0NT=TITITI0NTI0NV3V3V30NV30OV30OT?0PT?SPSPT*T*SPSP0OT*T*SET*SESRSRSE0NSR0N0OSFSFSF0O0PSFSTSFSF0OSTSTSTSF0O0PSTXEX@XEX@XEX@XEX@X@0NX@X@Z;XVZNZNV1T10M0NT30NT70O0OT=T2T20O0P0Q0RT=T=0R0S0S0T0UV3T2T=T20U0U0V0WV3T*SET*SET*T*T*SGT*SGSGSGSESESE0T0TSGSGSGSGSG0T0USGST0UST0USGSGSGXEX@XEX@XEX=XEX=X=X>X=XFX>X>XFXFX>X>XF0QX>X>0Q0RX>XUXFXU0QT80R0S0ST50T0UT4T2T90UT2T2T20UT90UT7T70U0V0WT0T20W0X0Y0YV3UR0Z0Z1#T0T0URURT0XN0ZSGSGSGXEXEXEXA0YX=XAY'X=XFX=0YX=0YX=0ZXFXFXD0ZXFXF0ZXFV1V1V10Z0ZT7T7T7V10ZTRTRT0T00ZT0T10ZT1T1X=0ZY'Y'XDXDXBXB0YXDXBXBTR0YTRT60Y0ZT6T6V+T6V+V+T6T60YT60YT1T6T6V+T;V+V+XNXYIX1X1X1X1X1/U/VX1X1X)X)X/X//UW6X/W>X/X/VBVBU=VBU$V/XYI/XY>Y>Y>Y>Y>Y>/XY>Y>YIY>Y>YP/WYPYIYIYI/WYIYIYI/WYI/WYIYIYZYZYZ/W/W/X/Y/Z/Z0#YPYPYP0#YP0$Y>X10$X1YP0$0%0&X1X10&X10&0'0(0)0)X1X1X1X10)X1WRX1X1VRVRX1WLVR0(0(0)0*X)WLX)WLWKX)X)WKWKX)X)WKX)WKWKWLWKWK0&VR0'0'WKW%W%X)X)X)0'WK0'WKWK0'WJ0(WJWK0(W%W%W%0(W%W%0(0)W@W@0)WJW%WJWJW@WJW@W@0(W@W60(W6W6W6X/X/WXWXX/X/WXWXVBVB0&0'0'UCUCUCU$U$VBU$VBVB0&0'VBVB0'TSU$U$0'U$U$U$U$0'UCUCUC0'TNTN0'UX0'TNTYTN0'TNTNTNTN0'TNTNV@V@TZTZ0&TZTNTN0&TZ0'0(TZ0(TZTZ0(V@TZ0)V@V@UU0)V@UUV@UUUU0(UU0)V@V@UPUPUPU0UPU0U*V70'V7Y6Y6XGYBYBYBYKYKYB0%YK0&YBYBYKYKYBYBYB0%YBY)0%0&YK0&YKYKY)Y)0&0'0'0(0)Y.0)0*YKYK0*Y.0+0,YKYKYK0,0,0-YK0.Y.Y.0.Y.Y.0.0/00Y.00Y.0101YM02YM02Y.0304Y.Y.04050506YO070708YOYOY.08090:0:0;Z$Z$YO0;YOYO0;0<0=0>Z'Z'0>Z'YM0>0?0@0@0A0B0CZ'Z'0C0DZ'Z'0D0EZ$0EZ$0F0FY/0G0H0H0I0J0K0KY20L0M0M0NY2Y20N0OY20P0P0Q0RY-0RY10SY10S0TZ'0U0U0VY?Y?0V0WY?Y?0WYGY?Y?YGYGY?Y?0V0WY1Y1Y?0WY1Y10W0XY10Y0YY?Y40Z0Z1#1$1%1%1&1'Y?1'Y?Y?Y?YIYIY?1'Y?1'Y?Y?1'1(Y?Y?Y?1(YTYT1(Y?YTYTY?Y?YTYI1'1(Y?Y?YIYI1(YIYI1(1)1*1*1+1,1-Z&Z&YIZ&Y?Y?1,1-1-YIYIYIYI1-YI1.Z&1.1/YI1/YS101111YSWMWMYS11YSWM11X1WMWM1112YIYIWMWMY+12Y+Y+YIY+VXVXVUVUX1X110WMX1X1X110X1X110X1VX1011VU11VUVUVUW%W%11W%X+X+11X+X*11X*X*W6W611W)11W)WX1212WXWXWXW)WXWXWXWX11WXWX11X/WXWXX/X.X.X.WXX.WXWXX.X.WXWXWXWX1.WX1.X.1/X.WUWUX(1/UCTGTGTGTGTGUCUCTGTGUCUCTGTGTYTY1+TYTYTYTGTYTGTGTGTG1*TY1*TNTNTNTN1*TNTZTNTZTN1*TZTZ1*TZTZ1*1+1,1,1-1.U31.1/1011U311U312U31213UUV@V@V@1313V@14UU1415UUUUV@V@1516V@16UUUU16UUUUUU16U0U0U0YDYD1617YDYD1718YKYK18YK1819YDYD19YD1:YD1:YDYDYD1:1;YDYD1;1YOYKYKYK1>1>YO1?YOYOYOYO1?Z(Z(YOZ(1>1?Z(Z(YRYR1?YRZ(Z(YOYO1>1?YOYL1?Y-YRY-Y-1?Y-1@1@1A1B1CY-1C1DYUYYYYY,Y,1C1DY,Y,1DY11E1FY1Y11F1GYVYV1G1HYVYV1HYV1H1I1J1KY1Y11KXXYVYV1KY;YVXXY;Y;Y,1JY,Y;Y,Y,Y,1J1JY;1KY;YTYIXXXXYIYI1JYIX%X%Y;Y;X%X%X%1IX%1IX%X%1IX21J1KY;1KY;1L1LYI1MX%YIYI1M1N1NW#1O1PW#W#W#1P1P1QX&X&1Q1RX&X&1RW#X&X&1R1SVWVWVWVWX&1SVWVW1S1TWVWV1TWVWVWV1T1UVW1UVWVWWVX$WV1U1U1VVWVWWVWVVWWV1UVR1VVRVRVRVR1VVRVR1V1W1W1X1YW'1Y1ZW'W'X$1Z2#2$2$2%2&2'2'W/2(W/W/W/2(W:2(2)2*W'W'W'W'X+2)W'W'W'W'2)W'W'W'W'X+X+X*X*WXWXX*WXWXWXWXWXWX2&WX2&2'WP2'WPWPWPWPWP2'2(WX2(WXWXWXWX2(WP2(2)WPWP2)WW2*WWX(WEX(WEUCUCUC2)2)TYV&V&UC2)UCUCV&V&2)V&UCUC2)2*UCUC2*2+V?2+V?V?TY2+V&TYV&2+V&V&V&2+V&2,V&2,V&V&U22,U2U22,2-U2U22-TYTOTPTYTNTNTNTNTN2,TN2,2-TN2.2.2/TNUUTN2/TN20TN20TN2121YDYD22YDYD22YDYDYDS2S22122S2?VY2?2@X&VY2@VYVYX&X&2@X&2@X&VY2AX&2AX&X&2A2BX&2CX&W:2CW:2CW:W:W:VW2CW:W:W:2CW:W:X&2CX&X&W:W:W:W2?VSWTWTWTS;WTS;S;WTWTS;WTVYVYWTWTWC2;WT2XZX&2>X&2?X&WW?W?W?W<2>W<2?2?W?W?W?WW?2?W?2?W?W?W=WXW=W=W=W=W?W?W=W=2=W=2=W=W=2>W?X'2>2?W?W?W?2?2?2@2AW,2AW,W?W,X'X'2AW?W?W?W?2AW?W?W,2AW?W?2AW?W,2AW,2BW?X0W?X0X0WX2AWX2AWXX0X0WXWXWX2AWXWXW$WXWXWXWX2@WXWX2@WXWXWX2@WXWXX#Y&X#WXX#2?X#X#Y&2?X(UKUKUK2?UKUN2?UNUN2?UNUNUMUMUNU-UMTJ2>TJUM2>UMTJUM2>U-TCTCV=V=V=US4XH2+XHSDS>S4S>S/SDS>2*SXS4T%S4T%S4S4S4T%S4S4S/S/T%T%2'T%S02'S0S0S/S0S/2'T#T#2'T#T#T#T#2'2'SMSMSM2'T&SOT&S02'S0SOT&SMT&SM2&SMSMSMSMSBSM2&2&SB2'SZSM2'SMSBSZSZ2'SZ2'SZ2(SZSZSZSZS5SJSJSJSHSJS3SJS1S12%S1S12%S3S1S1S3VQS3S3S12$S1SAVQVQ2$XYW?W?2$W*W?W*W*W*W$W$2#Y%W$W$W$WN1ZW$WNWNW$1ZW$W$X01ZX0W$X-W3WNW3WNW3W3W3TKTKTF1XU/XNU/V%TLTLTLTBSX1VSXSX1VS0S/S01VS0XGS01V1W1X1Y1YSM1ZSMSMSMSBSBSMSB1Y1ZSSSSSNSNS5S5S51YS9SLS9SL1XS5SQSQSHSH1XSQSQSQSLSQSQSQSQ1WSQSISQ1W1WSISISISI1W1XSYW$W$WA1XW$W$W$WAWAWA1WWAW71WW7W71WW7W7W7WO1WWOWOY&W(WB1W1WWBWBWBWBWBWF1WWGWGWGWFXOU.UBU.U.U.1UU.U.U.1UUBUBU.UBUBTBU(TB1TT/U(T/U(U(U(U(1SSNS9SNS9S91R1S1TS9S9SL1TSP1TXTSP1TSPSPSPS9SLSLSL1SSLSLSLSLSLSLSQSQ1RSQ1S1S1TSQ1USQS:SQ1USISI1US@1US@S:S@1USY1V1WS@SYS@S@SYSY1VSYS@SYS@S@SYXY1UXYWOWF1UWFUBV/UBUBV/V/V/UAUGUGUG1SV)V)V)1ST/T?T/T?1RV3V3V3U(V-1RT?V-V-T?1R1RT?T?T?T?1RT?UET?1RT?T?1R1S1T1UV-V-1UXLSPSPSP1USQ1USR1VSRSR1VSQSQS@SRSRS@S@1US@SRSRSR1U1U1VST1WS8STS8STS8ST1VS8WFWF1VWFUBUB1V1W1WUA1XUAV1UAV1UAUAUA1WUAUAUA1WT=1WTITITITITI1WTI1WV3V3V3TIV3TI1W1WV3V3V3T?T?V31WV3T?V3V31VSPT*T*SRSRSE1VSRSRSRSFSRSFSFSFSE1TSESESFSFSE1TSTS8ST1T1TSTSFSTSFSF1TSFX@X>X@X>V1V1V1T8T4T4T8T4T3T7T3T71PT7T7T7T71PT7T7T1T1T11PT:T21PT2T41PT4T41PT2T1T2T2T2T21P1PT=1QT=1QTIT=TITIV3V3V3T=T=T=1PT2T2T21PT=T21P1QT2V3V3V31PV3V3V3SE1PSG1QSE1QSESGSG1QSG1R1RSGSUSGSGSGSG1RSGSVSGSGX>X>XFXFX>X>XFXF1O1PXFXFV1T8V1T8V1T8V1T51NT5T5T5V1T5V1T5V1T5T71MT5T51MT71MT21NT2T2T2T21NT9T2T91NT21N1OT01OT0T0T01OT0T0T01O1P1QURT21Q1R1S1SURURUR1SV3UR1T1T1UURV3T01UT0T01UURT01VSG1VSGSGXEXEXA1V1VXFXFXFXD1VXDXDXDXD1VXD1V1WXDXD1WXDXDXDV11WV11X1XT7T7T7V1T7V1T1T0T01WT0T7T7T1T11VXDXDXDXDXDXDXBTR1UTRT6T1T1T61UT1T11U1VT6T6V+T6T1T1T6T6YW1TYWYW1TY91UY9YJYJ1U1VYJYJYJYWYWYWYJ1UYWY9Y9Y9YWYFYWYFYFY9YFY9YWY9Y9Y9Y9Y9Y91QY9Y9YIYIY9Y9YIYIYIYIY9YI1NY>Y>Y>1N1OX)X)X)1OX)X)X/X/X/W6VDVDV.VDY6Y6Y61M1MYBYB1N1N1OY6Y6YBYBY6Y6Y:Y:YBYB1M1N1O1PYJ1PYWYWY5YWY5Y5YJYW1OYWY.1OY.Y.1OY5Y.1PY5Y51PY.Y5Y5Y51P1PYWY51QYWYWYWY5YWYWY.Y.Y5Y5Z'Y5Z'Z'1NZ'1NZ'1O1PYXYXYNYNYNYNYN1OYWYWY5YWZ'Y=Z'Z'Z%1MZ%Z%Y=Z%Y=Y=Y9Y9YXYXYNYN1KYNYN1KYNYNYXYIYXYXYIYIYX1JYIYI1JYIYXYXYNYNYNYIYNYNZ%Z%Y=Z%Z%Z%Z%YGZ'1FZ'Z'Z%Z%1FYG1FYGZ'YGZ%1FYGYG1FYGYGYGYNYNZ%YGYGYIYGYGYGYIYG1DYGYIYIYI1CY>Y>Y>Y>Y>YIYIYIYIY>1BY>Y>Y>1BY>Y>Y>1BYIYIYI1BYIYIYZ1BYIYZ1BYZ1B1CYSYP1C1DYIYIYP1DYIYIYIYIYI1DYIYIYI1DYZYZ1DYPYZYP1DYPYPYPYP1DYPX1X1X1Y>Y>1CYPYPX1YPYPYPYP1B1CYP1CYP1DX1X11DX11DYIYIYIYP1DYI1EYIYIYI1EX1X11EX1X11EX1X1X11EX1WRVR1EVRVRX1X1X11EX1X1X)X)X11DX)X)WKWK1DWKW%W%VR1DW%WKW%W%X)X)X)WJX)WKWKWKWJWJ1AWJWKWJWKWJWKW%W%W%W%1?W%W%WJWJWJW@W@W@1>W@WJWJ1>WJW@1>W@W6W@W@1>W6VBVBVB1>VBVB1>1?VB1?UCUCVBVBUCUCVBVBUC1>VBVB1>UCU$U$TSTYU$U$TYTYUCTGUC11?TZTZULTZ1>UL1?1@TZTZTNTNTZ1?TZTZ1?V@TZV@V@V@TZTZ1>UPUUUPUU1>UUUPUU1>UUUUTDTDTDV7YBYBY81=1=Y8YK1>YBYB1>YKYK1>YK1?1?1@1AY0YK1AYKYK1A1BY0Y01B1CY0Y0Y)Y)1C1D1D1E1FY.1F1G1H1I1IY0YK1JY0Y01JY0Y0Y.1J1K1K1LYKYK1LY.1M1N1N1OZ)Z)YK1O1P1Q1QZ)1RZ)1R1S1T1UY.Y.1U1VY.1VY.1WY.Y.Y.1WY.1W1X1Y1Y1Z2#YMY.YMY.Y.1ZYMYMYMYMYMY.YM1YY.1ZY.1Z2#Z)Z)2#2$Z)Z)2$2%2&Z$2&Y.Z$2'Z)Z)Z)2'Z)Z)2'2(YO2(YOYOZ)2(2)2*2*YOYOYOY.Y.Y.2*2*2+2,2-Z$2-Z$Z$2-Z$Z$Z$2-2.Z$Z$2.2/YOYO2/2021222223YAYA2324Z(Z(YAYAZ(Z(Z'Z'2324YMZ'YMYMYMYMYM23YMYM232424Z'YMYMZ'Z'2425YMYM2526YM2627Y/2728Y/Y/Z'Z'Y/Y/2728Y/Y/28Z'Y/Y/Z$28Z$Z$Z$28292:2:Y/2;2Z(Z(2>2?Z(Z(Z(Z(2?2@Y2Y2Y22@Z(Z(Z(2@Z(2@2A2BY/Y/2B2CY/2C2D2E2E2FY2Y22F2GY2Y2Y2Y12GY1Y2Y22G2HY2Y22H2I2IY-2JY-Y22JY12KY1Y1Y-2KZ'2KZ'Z'2KYGYGYG2KY?Z'2LYGYG2LY?YGYGY?Y?Z'Z'2KY?2KY?Y?Y?YGYGY?YGY?Y?2JY?Y?Y?2J2KY?Y?Y?2KY?Y?Y1Y1Y?2JY4Y4Y4Y4Y12JY?Y?2J2K2KY?Y42LY4Y4Y42LY42LY?Y?2LY?2M2NY?Y?2NY1YGYGYG2NYGYGY?Y?2M2NY?Y?YIY?Y?Y?YIYIY?Y?Y?2LY?Y?2L2MY?Y?2MY?Y?Y?Y?Y?Y?2MY?Y?2MY?YIYIY?2MYIYIY?YIY?2LY?YIYIYIYIYSYIYI2KZ&2K2LZ&Z&YS2LYSYS2LYSYSYSYSYSZ&2LYSYSZ&Z&Y?2KY?Y?2KYIYIYIY?2K2L2M2MZ&Z&Z&YI2MYIYIZ&Z&Z&YI2LYIYIYI2LYSYSYSYSYS2L2MYSYS2MYSZ&WM2MWMYPYPYSYSYPYPYP2L2L2MYIY+Y+WMY+Y+Y+VXY+Y+WM2KWMWMX1X1VVVV2JX1VVX1VXVX2J2KVU2KVUVUVXVX2KVUW%W%VRVRX+X+2JX+2J2KX*X*2KW)W)W)W)W)X*W)2J2KWXWXW)W)2KWXWX2KWXWX2KX/X/X/WXWXWXX.WXWXWXX.2IX.X.X.X(WUX(X(2HTY2ITYTY2ITYTYTYTYTY2ITNTZTNTZ2H2ITN2J2JTZU3U3TZTZ2J2KTZ2KUUUU2KV@UUUUTNTNTNU32J2K2LU3TN2LTN2MTNU3TN2MU3U3U8U3TN2LTNTN2LU52MTNU3UUU3UUU32LU32MU3UUUUUU2L2MUUUUV@V@V@2M2MV@V@V@V@UUUUUUV@V@V@UUV@2KUUUUV@V@UUUUV@V@UUUU2IUUUUUU2IUUUUUUUUUPUUUPYD2HY@Y@Y@2HY@Y@2H2IY@Y@YDYD2IYDYKYK2IYKYD2IYDYDYKYKYDYDY@2HY@2IY@2IY@YDY@2IY@YDYKYKYDYDYKYKYD2HYKYK2H2IYKYK2I2J2JYO2K2LYK2LYKYKYOYO2LYO2LYOYKXTYKYKYK2L2LYOYKYOYKYOYKXT2KYOYOYOZ(2KZ(Z(YRYR2KYRYRYRZ(2KZ(2KZ(Z(2K2L2M2N2N2O2P2QY1Y1Y-Y-Y-2PY-Y-YLYLYL2PYL2PYY2QYL2QYYYYYYYYYY2QY-Y-2QY-2Q2RYUYU2R2SYYY,2SY,Y,Y,2SY1Y1Y12S2T2UYVY1Y1YVYVY12TYVYV2T2UYVYVYUYVYUYVYVYVYVY,YVYV2SYVY1Y12SY1Y1Y12SY12SYVYVYVYV2SYVYV2S2TYVYV2TY;2UY;Y,Y,Y,Y;Y,2TY,Y,Y,Y;Y;Y;Y;Y;Y,Y,YIYIYI2RX%X%Y;X%YIYIX%X%Y;2PY;2QY;2QY;Y;2Q2R2SX&2SX&X&X&Y;X&Y;X&YIYIX%YIX%X%2QX%YIYI2QYIYI2QYIW#X2W#X2W#X2X22PX2W#W#X2W#W#W#2OW#2O2PX&X&2P2QX&X&2QX&X&X&X&W#X&2QW#W#2QW#WVWVVWVWWVWV2P2QVWVWX&X&VWVW2PVWVWVW2PVW2PWV2QWVVW2QVWVWWVWVVWWVVW2PVWVWWVX$WV2P2P2QVW2RWVWV2RWVVRVR2RVRWS2RWSVRVRVRVR2RVRVR2RX$X$W'W'W'2QVRW'W'VRVRW'W'W'W'2P2QVRW'W'W'W'2PW'W'2P2Q2RX$X$X$2R2S2S2T2UW/X$2U2V2W2W2X2Y2Z2Z3#W'3$3$3%W'W'W/3%WV3&WV3&WVW/W/W/3&W/3&3'3(3)3)3*3+3,3,3-W'W'3-3.W'W'VRVRW'W'3-WXWPWPWXWX3-WPWX3-3.WPWX3.WPWPWPWPWP3.3.3/30WWWXWXWX30WXWXWX30WX30WPWP30X.WPX.WPWPWP303031WWWWUCUCUC31UCTYUCV&UC30UC3131V&V&V&UC31XRV?3132V?V?V?32V?V?V?32V?U2V?U2V?V?TYTY31TY31TYV&V&TYTYV&TYV&TYV&30V&TYV&TYV&V&U23/V&V&U2U2V&TY3.U23.3/TPTP3/TNTNTNTN3/TNTNUUUU3/30TNUUTNUUTNTNTN3/3/UUUUUUUUUU3/UU3/UU30UU30UUTNUUTNUUUUUU3/YDYDYDYDYDS<3/3/YD30SW93?Y;Y;3?3@Y;Y;Y;W+Y;3?W+W+W+W+W+3?W+3?VYVY3?Y;W+W+3?3@VYVY3@VYVYVY3@3AVYVYWTVYWTWTX&X&3@X&3@X&VY3AX&X&3AX&3AX&X&X&VY3A3B3C3CX&3DX&3DX&VY3EVYVYVY3E3EX&3F3G3GVWX&X&VWVWVW3GVWVW3GVW3GVWX&VWX&X&X&W:VWVWW:W:3E3FW:W:W:3FW:W:W:W:X&3FW/3FW:W:3FW:W:W:W:W:W:3FW:W:3F3GW:W<3GWS>XHSDSDSDSDSXS/T%S/S/T%3?S0S03?S0S0S0T%T#T%T%T#T#SMSMT#SMSMSMSOT%SO3Y>Y>2X2YX1X)2Y2ZX)X)2ZX1X)X)Y62Z3#YBY6Y63#YBYBYB3#YBYBY63#Y6Y63#Y6Y6Y)3#Y)Y)3#Y.3$Y.Y)Y)Y)3$3$Y.3%Y.YJ3%YWYWYJYJ3%YJY5Y5Y.Y.Y5Y5Y.3$Y.3$Y.Y.3$Y.Y.Y.Y5Y5Y.Y.Y53#Y5Y53#YWYWYWY.Z'Y.Z'Y.2Z3#3$3$Z'Y.Y.Z'Z'3$Z'YNYN3$3%YNZ%Z%Z%YNYN3$YNYXYX3$3%YX3%YXYI3%YIYIYIZ'Z%Z'Z'Z'YGZ'YGZ'YGZ'Z'Z%Z%YGYGYGZ%YGYGYG2XYGYG2X2YYIYIY>YIY>Y>Y>Y>YPYPYPYP2WYP2WYIYZYZYIYIYZYZYI2VYIYIYZYPYZ2VYPYP2VYPYI2VYIYI2VYPYIYIYPYP2VYIYIYIYPYPYIYIYI2UYZYPYPYPYP2TYPYPYP2T2UX1Y>Y>YPYPYP2TYPYI2TYPYPYPYPYP2TX12TX1YPYPX1X12TX1YPYIYIYIYP2SX1X12SX1X1X1YIYIYI2S2SX12TX12TYIX12UX1X12UX1WLWLVRWLX1X12T2UX1X)X)X)WKWKWKW%W%W%VRW%WKWJWKWKWJWJW%WJ2PW@W@W@WJWJW%WJ2OW@W6W6W@W62OW6VB2OVBUC2O2PUCUC2PVBUCUCUCUC2PUC2PVBUCUCVBVBUCUC2OTGTGTGTNTN2OTYUY2OUYUYTNTNUYTNVD2NTNTNTNTZTN2NTZTZULTZTN2MTNTNUL2MTNTN2MULTNULTNTNTN2MULUL2MTNV@V@TZ2MV@V@2MV@UPUP2MUPUUUP2MUPUUUPUU2MY8YKY8YKY8Y8Y82LYZ)3>3?Z)Z)3?3@Z)Z)3@Y.3A3BY.Y.Z$Y.3AZ$Z)Z)Y.Y.Y.Z$Z$Z$Z$3@3@YO3AYOZ)Z)YO3AZ)YO3AYO3AYOYOYOZ)Z)3A3B3BYOYOYO3B3CYOYOZ)YO3CYO3C3DY.3EY.Y.Y.3EY.Y.Z$Z$Z$Z$Z$YOZ$Z$YOYOY.3BZ$Z$Y.3B3CZ$YMYMZ$Z$YMZ$Z$Z$YOZ$YOYOZ$Z$YOYOZ$Z$YOYOZ$Z$3>3?YOYAYO3?YAYA3?YAZ$Z$3?Z$Z$Z$Z$YAYO3>3?3@3@YA3A3BYMZ'YMYMZ'Z'3AZ'YMYMZ$YMYMYM3@YMYMYM3@3AZ'Z'YMYM3@3AYMYM3AZ'3B3CYMYM3C3DYMYM3DY/YMYMY/Y/YM3CY/Y/Z'Z'Y/Y/3BZ'Y/Y/Z'Z'Y/Y/Z'Z'Y/Y/Z'Z'Y/Y/Z$3?Z$Z$3?3@Z$Y2Z$Z$Z$3@3@Y23AY23AY/Y/Y/3AY/3B3CY/Y/3CY/Y23CY2Y23C3DY2Y23D3EY2Y23EZ$YAYA3E3FYA3G3GY23HY2YAYAYAZ(3GZ(Z(Z(Z(Z(YRZ(Z(Z(YRYRY2Y23EY2Z(Z(YRZ(Y2Z(Z(Z(Z(Z(3C3DZ(Z(Z(3DY/Y/3DY23DY2Y2Y2Y/Y/Y/3D3DY2Y2Y23DY2Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/3B3C3C3D3E3FY23F3GY1Y2Y2Y23GY23GY23H3HY2Y-Y-Y23H3I3JZ(Y-Z(Y-Z(Y-Z(3IY2Y23IY13IY1Y1Y1Y1Y1Y-3IZ'YGZ'3IYGYG3IYGZ'Y?Z'Y?Y?Y?Z'Y?YG3GY?Y?Z'Z'Z'3GZ'Z'Z'Y?Y?Y?Y23FY?Y?Y13FY?Y?3FY?Y?Y?Y1Y1Y?Y?3EY?3EY4Y1Y13EY?Y4Y4Y?Y?Y43E3EY?3FY?Y?Y?3FY?Y43FY4Y4Y43FY43GY4Y43GY43GY?Y1Y13G3H3IY13IY1Y1Y1YGY?Y?Y?3HYGY?Y?YGY?Y?Y?Y?YIY?Y?YIYIY?Y?3EYI3FY?YIYIYI3FY?Y?Y?3FY?Y?YTYTYIY?Y?Y?Y?YIY?3DYI3DZ&Z&YIYI3DYIYSYSZ&YS3C3DYSYS3DYSYSYS3DZ&Z&Z&Y?Y?Y?YI3C3DYIYIYIYI3DYIY?Y?Y?3D3DYIYIYIYI3DZ&Z&YIZ&YIYIZ&Z&YIYIYI3BYSYSZ&YSZ&YSYSYSYSZ&YSYS3@YSZ&Z&WMWM3?X1X1X1Z&Z&Y+Y+3>3?Y+Y+WM3?WMWMX1X13?X1VXVXVXVUVX3>VUVUVX3>VUVU3>VUVUVU3>X+3?X+W6W63?X*W6W63?X*W6W)W)W)3>3?WXWX3?WXWXWXW)W)W)WXWXX/WX3>X/X/3>X/WXWXWX3>UCTYUC3>UC3>UCTYTGTGTYTYTY3=TYTNTZTZTZ3=TZTZTZ3=3=U3U4U3TZTZ3=3>TZTZTZ3>TZV@3>V@TZTZ3>3?TZ3?3@UUTN3@TNU33@3AU3U33AU3U3U33AU3TNU33AU33BU33BU83CU8TNU6TNTNU8U8TN3BTN3BTNTNUUUU3BUU3BUUUUUUU>U>U>3B3B3C3D3EV@UU3EUU3EV@3FV@3F3G3H3IV@V@3I3J3JUUUUUUYD3J3KY@YDYDY@3KYDYD3KY@YDYDY@Y@Y@3JY@3K3KYK3LYDYDYKYDYDY@YDY@YDY@YDY@YDYDYDY@YDYDYD3HYD3H3IYDYD3IYK3JYDYKYK3JYD3J3KYDYDYKYDYDYD3J3KYKYKYKYOYKYKYOYO3JYOYKYKYK3JYOYO3JYO3JYO3KYOYKYKYK3KYKYOYKYK3J3KYOYOZ(YRZ(Z(Z(YRZ(3JYRYR3JYRZ(3JZ(Z(YRYR3J3KYRYLYRYLZ(3JZ(Z(3JYLZ(YLZ(3JYR3K3KY-3LY-YR3LYRYRY-Y-YRY-Y-3KY-3LYLYL3LYYY-Y-3LY-Y-Y-3L3M3MYY3NYYYY3NYYYYY-Y-3NYUY-3NYUYU3NYUYUYUYUYUYYYUYUYU3M3NYUYUY,Y,Y1Y13MY1YV3MYVYV3MY1YVYVYVYVY-Y-Y1Y1Y13LY1Y13LY13L3M3NYVYVYVY,Y,Y1Y1Y13MY1Y13MY1YV3MYVYVYVY1YVYVY1Y1YV3LY1Y13LY1YVYVYV3LY,3LY,3MY,3MY,Y,YIYIYIX%X%X23LX2Y;X2X2X2X2X2Y;3KX2X23K3LX2X23LX2Y;Y;Y;3LX&X&Y;X&X%X%X%W#W#YIW#3JYIYIYIW#X2X23IW#3IW#X&X&3IW#X&X&W#W#3IW#X2X2W#X23HW#X2X&W#X&X&X&W#W#3GW#W#W#3GW#WVWVVW3GWVWV3GWVVWVWX&X&VWVW3FVWWVWV3FWVVWWVVW3F3FWVVWWVVW3FVWVWWV3FWV3GWVWVVW3GWVWV3GWVVW3GVWVW3GWVVWVWVRVRWS3G3GVRVRVRVRVR3G3H3HX$3IX$VRVR3I3JW'W'3J3KW'W'3KW'3K3LW'W'VRVR3L3M3MX$X$X$VRX$X$X$3L3MW/W/X$X$3M3N3N3OX$X$3O3PW/W/X$W/3PW/X$X$X$3PW/3PW/W/3PW/W/W/X$X$3P3QX$3QX$X$W/3Q3R3S3SX$X$3TW/3TW'W'3TW/W'W'W'W'W/W/W/3S3T3U3U3VW'3WW/W/3WW/WVW/3WW/3WW/WVWVW/W/3WW/3WX$X$X$3W3XX$X$X$X$3XX$X$X$X$3X3XW'X$3YW'W'3Y3Z3ZX$4#W'4#4$W'W'W'4$W'W'4$4%W'W'VRVR4%4&VRVR4&4'WXWXWX4'WXWXWXWP4&4'4(WPWX4(WXWP4(WPWPWPWP4(WWWWWPWPWP4(4(4)WWWW4)WWWWWWWX4)WXWXWXWP4)WP4)WXWPWPWXWX4)X.WPWPWP4)WW4)WWWW4)WWWWWW4)V&UCV&UCV&UC4)4)4*UCV&V&V&UCV&UCUCV?V?UCUC4(4)UCUCV?V?4(4)V?V?4)4*4+V&4+TYTYTYV&TYV&TY4*TYV&TY4*U2U2U24*U2U2U2TP4*TPTP4*TY4+TY4+TNTNTNTN4+TNTNTN4+TNU?4+UUU?UUTN4+TNUUTNUU4+UUUUUU4+UUUUUU4+4,TNUU4,UU4,UUTNUUYDYD4,YDYDYDSX>X>XF3X3YXFXFT5T53YT53Y3ZT7T73ZT5T74#T5T54#T5T94#T9T9T2T24#T2T9T2T9T23Z4#T0T04#T2T0T0T24#T0T04#V34$URT2URT2URV3V34#4$T2UR4$UR4$URURURT2T2T24$T24$T2T24$T2T04%T2T2T2URURUR4$URV3V34$4%V3V34%V3T2T2T0T04$URT0UR4$SUSGSUX?X?XFX?XFXF4#XFX=XDX=XDXD3ZXDXDXFXF3Z4#4#XDXDXDT74#T7T7X=XDX=XD3Z4#T6T64#4$T6T6T64$T6T6YW4$YWYW4$4%YW4&Y9Y94&Y94&4'Y9Y9YJYIYIYIYJYJYIYIYJYJYIYIX14$X1X14$X1X)X)X1X14$4%X1X1X)X)X1X1X)4$Y64$YBYBY64$YBYBY64$YBYBYBYB4$YBY64$Y6Y6YBYB4$YBY)4$Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.3ZY)Y.Y.Y)Y.3ZY.3ZY.Y.Y.YJYWYWYWYJYJ3Y3ZY5Y5Y53ZY.3ZY.Y.3ZY.Y.Y.YWYWY53ZY5YWY5Y5Y.Z'Y.Z'Y.Y.Y.3X3XZ'3YZ'3YZ'Y.Y.Z'Z'3YZ'3Y3ZZ%Z%YNYNZ%Z%YNYNZ%Z%YN3XYNYN3XYX3YYXYXYIYXYXYIYI3XYIYGYIYGYGY>Y>YIYIY>Y>YIYIY>YPY>Y>YIYIYIYZYZYZYIYZYZYPYZYZYPYP3QYPYIY>YIYIY>Y>YIYIYPYIYIYIYIYIYIYPYZYPYPYPYPX13LX1YP3LYPX1YPYPYP3LYPYP3L3MYPYP3M3N3NX1YPYPX1X13NX13NX1X1X1YI3NYIYIYI3NYI3OYIYI3OX13OX1X1X13O3PYIYI3PYIX1X13PX1WRWRX13PX1X13P3QX)X)3Q3RW@W@W@W@W@3R3RW6W6W6VBVBVB3RVBVB3RVBVBVBVB3RVBVB3RVBVBUCVBUCVBVB3Q3RUCTGTGTGTYTNTYTYUY3PUYUYVDVD3P3QULUL3QULULULTNTNTZTZULUL3OUL3PTNTN3PTNTNULUL3PTNV@V@TZTZV@V@3OTZUPUP3O3PUUUUUU3PUP3PUUUUY8Y8Y8YY.Y.4>4?4@Y.Y.Y.YMYMYMY.Y.Y.YMYMYM4=4>4?Y.4?Y.Y.Y.YKY.4?Y.4?Y.4@Y.4@Y.4A4BY.Y.4BY.4B4CZ)Z)4C4DZ)Z)Y.Y.Y.4DY.Y.4DY.4DZ)Z)Z)Z)4DZ)4E4E4FZ)4GY.Y.4GZ)Y.Y.4GZ)Y.Y.4GY.Z)4GZ)Z)4GZ$4HZ$Z)4HZ)4IZ$Z$4IYOZ)Z)Z)YO4HYOYOYO4H4IYOZ)Z)4I4JYO4J4KYOYOZ)4KZ)Z)4KYOYOYO4KZ)YOYOYOZ)YOYOYOYO4JYO4J4KYOYOY.4KY.4L4L4MZ$Z$Z$Z$4MZ$Y.Z$Z$Z$4LZ$Y.Y.Y.Y.4L4MY.Y.Y.4MZ$Z$YOYAZ$Z$YA4LYO4LYO4MYAYA4MYAZ$Z$4MZ$YO4MYOYOYOYOYOZ(YOYOZ(4L4LYA4M4NYO4NYAYA4NYAYAYAZ'Z'YMYMYMYM4MYMYMYM4M4NYMYM4NYMZ'Z'4N4OZ'Z'YM4OZ'Z'4O4PYM4PYMYM4PZ'4Q4RYMYMYM4R4RYM4S4TYMYMY/Y/YMY/4SY/Z'Z'Y/Y/Z$4RZ$Z$4R4S4T4U4UZ$Y2Y2Z$4U4VY2Z$Y2Y2Y2Z$Y2Y2Y24TY/Z$Y/4TY/Z$4UZ$4UZ$4V4V4WY2Y2Y/Y/Y24W4W4XY2Y2Y/4XY2Y24X4YY2Y24Y4ZY2Y24Z5#Y2Y2Z$Z$Z$5#Z$YA5#YAYAY2YAYAYAYAYAZ(Y2Y24YY24YY2Z(Z(YA4YZ(Z(Y2Y24YY2Z(4YZ(YR4YZ(YRYRZ(Z(YR4YY2Y24YY2Y/Y/4YY/Y/Y2Y2Y2Y/Y/4XY24X4YY2Y2Y2Y/Y2Y2Y/Y/Y2Y2Y/4WY/4X4XY?Y?Y?4X4Y4ZY2Y?Y?4ZY?Y2Y14ZY1Y24ZY2Y1Y2Y2Y2Y-Y2Y2Y24Y4YY-Y-Y-Y2Y24YY2Y2Y24YY2Y-Y1Y-4YY1Y14YY1Z(Z(Z(Y-Y24XY2Y2Y2Y1Y1Y1Y-Y14WY1Z'YGZ'Z'YGYGZ'4VYGYG4V4WZ'Z'Z'Y?Y2Y?4VY1Y14VY1Y14VY?Y1Y14V4WY4Y4Y4Y4Y14WY44WY44XY4Y?Y4Y4Y4Y?Y4Y?Y4Y?Y4Y4Y4Y?4U4VY44VY4Y4Y44VY44WY44WY4Y4Y4Y4Y44WY14WY1Y1Y?Y?Y?4WY?Y?4WY1Y14WY1Y1Y?4WY1Y1YGYGY?Y?YIYI4VYIY?4VY?Y?4V4WY?Y?Y?YTYTYT4VYIY?YIYIYIZ&Z&YIYI4UYIYIYIYSYSYIYIYSYSYIYIYSYI4RZ&Z&Z&Y?Y?YIYIY?YIYIYI4PYI4Q4RY?Y?Y?4RY?YI4RYIYIYI4RYIYIYIYSYSYSYS4QYSYPX1X1X1Z&Z&Y+Y+4OY+Y+Y+X14OWMWMX1X1X14OVXVXVU4OVXVXVUVUVXVX4NVUX+X+4NX+4NX+X+X+X*4NX*X*4NX*X*X*WX4NWXWX4N4OWXWXW)W)4O4PWXX/WX4PX/X/4PX/WXX.X.X.UCTY4OTY4OTYUCTYTNTNTY4OTZTZ4OTZTZTZTZ4OTN4OTNTNTZTZ4OTZTZTZ4OTZTZ4O4PV@4P4QV@V@TZTZTZ4QTZTZ4Q4R4RV@TZV@TZTZ4RUU4RU44SU9U44SU9U94SU3U3U34SU3U3U3TNU3TNU3TNU3TNU3TNU3TNU3TNU8TN4P4PU8U8TNTNU7TN4PTN4PTNTNUUUU4PUU4PUUUUUUU>U>U>UUU>4OU>U>UUUU4OUUU>U>UUUUU>UUUUUUV@UUV@4M4MV@4NV@4NV@4OV@V@4OV@V@UUUU4OUUV@V@UUUU4NUUUUUUV@V@V@4NV@V@4NUU4NUUUUUUYDYD4NY@4NY@Y@Y@YDYDY@Y@YDYDY@Y@YDYD4LYD4LYDYDYDYDYK4LYK4L4MYDYDYDYD4MYDY*4MY*Y*4MYKYDYDYKYK4M4N4NYKYDYDYDYKYDYDYKYK4M4NYKYK4N4OYK4OYKYKYOYOYKYKYO4NYKYKYK4NYKYKYOYOYKYKYKYO4MYO4MYOYKYOYKYKYKYOYO4LYO4M4MYO4NYOYRYRZ(YRYRYRZ(YR4LYRZ(YRZ(YRZ(Z(YRYR4KYRZ(4KZ(Z(Z(YRZ(4KZ(Y-YRYRYRYRYR4JY-Y-YRY-4IY-Y-Y-YR4IYRY-Y-Y-Y-YVY-YVY-Y-YLYLYLYYYLY-Y-Y-YYY-YYYYY-Y-YYY-YLYYYLYYYLYYYLYYYYYU4A4BY-Y-YU4BY-YUYUYUYUY-YUYUYUYUY,Y,YUYUY,Y,4>Y14?4@YV4@YVY14@Y1Y1Y1Y1Y1YV4@Y1Y14@Y1Y1Y1Y14@Y1Y14@YVY14@YVYVY1Y1Y14@Y1Y14@Y1YV4@YVYVY1Y1YVYVY1Y1YVYVYVY;Y;Y;4=Y;4>Y;Y,Y;Y;Y;Y,4=Y,4>X2X2Y;X2Y;4=Y;Y;4=Y;Y;Y;Y;X2Y;Y;X2X24X2W#4>W#W#W#X&X&W#W#4=W#4=WVVWVWWVWVVWVWVWVW4W/W/X$X$4>4?X$4?W/W/4?4@W/W/4@4AW/W/X$W/W/W/X$X$X$W/X$4?W/W/4?W/W/W/X$W/W/W/X$X$W/X$4=4>X$X$W/4>4?4@W/X$4@4AX$X$4AX$4AX$X$X$X$X$4A4BW/W/4B4CW/W/W'W'X$X$4B4CW'4CW'W'4CW'W'W'4CX$4DW'4DW'4EW'4E4F4G4HW/W/WVWVWVW/WVW/4F4GWVWVW/W/W/4GW'4GX$X$X$4GX$X$W'4G4H4IX$X$4IX$X$4I4JW'W'W'W'4J4J4KX$X$W'W'4K4LW'W'4L4MX$X$X$4M4MX$W'W'X$4M4NW'4N4OW'W'4O4PW'4QX$4Q4RW'4RW'W'W'W'4RW'W'4RW'W'W'W'4RW'W'4RW'W'W'WXWX4R4SWXWXWX4SWXWP4SWPWX4SWPWPWX4SWXWPWXWX4SWPWPWPWW4SWP4SWWWWWP4S4T4UWPWPWWWWWPWW4T4UWXX.WX4UWX4UWPWPWXWX4UWPWXWXWXX.WPWP4TWWWP4TWWWWWP4T4UWWUCV&UCV&V&V&4TV&UC4TUCUC4TV&V&V&4T4UV?V?4UV?V?V?UCUCV?4UUCUC4U4VUC4VV?V&4VV&V&V&4VV&U2U24V4W4X4YV&TYV&TY4X4YU2U24Y4ZU2U24Z5#TP5$TYTY5$TY5$TYTPTP5$TNTYTNUUUUTNUUTNTNU?U?U?UUU?U?TN4YUUUUTNUUUUUUTNUU4XUUTNUUTNTNUUUU4WUUTNTNTN4WTN4WTNTN4WYDY@YDYDYD4WYDYDS=4WS=4W4XS4LXFXF4LXFXFXFT5T5T7T5T74KT7T7T5T54KT7T5T5T74K4K4LT7T7T5T54LT7T2T2T9T2T2T24K4LT24LT2T04L4MT04NT2T2T0T04MT0T0T0T2T24MV34MURURURURV3UR4MV3V34MV3T2T2T24M4MURT2URT24MT2T2T24MT24NT2T24NT2T2T2T04NURUR4NURV3V3V34NV3V34NV34NURURURURURT04NSGSUSG4NXFXF4N4OXD4OXDXDXFXF4O4PXFXF4PXDXD4PXDXD4PT0T7T74P4QT6T6T1T1T6T6T14PT6T64PT1T6T6T14PT6T6YWY9YW4PY9Y9YWYWY9Y94OY9YW4OYWYW4OY9YWYWYWYWYWY9YW4NY9Y9X1X1X14NX1X14N4OX)4OX)X)4OX)X)X)4O4PX)X)Y6Y64P4Q4Q4R4SYB4S4TYBYBYBYB4T4U4UY6Y6Y6YBYB4U4VY.Y.4VY.Y)Y)Y.Y.4UY.4VY.4VY.4WY.YJYJ4W4XYJYJ4XYJY5Y5Y.Y.Y54WY.Y.4WY.Y.Y.YWYWY54WY.Y.Y.Z'Y.Z'Y.Z'4UZ'Z'Z'Y.Z'Y.4UY.4UY.Z'YNYNZ%Z%YNYNZ%4TYX4TYNYNYXYX4TYX4TYNYNYN4T4U4VYI4V4WYZYPYPX1X1X1YP4VYPX1YPYPYP4VYPYP4V4WYPYP4WYPYPYP4W4X4XX14YX1X1X1YPYPX1X14XX1YPYP4XX14X4YYIYIYIYIYI4YYI4YYIX1YIYIYI4Y4YX1X1X1YI4YX1X1YIYIX1YIYIYIX14XX1X1WR4XX1X1X1X)4W4XX)X)4XX)X)X)WJWJW@4X4XWJW@W@W64XW6W6W@W@W@W6VBVBVB4WVBVB4WVBVBVBVB4WVBVB4W4XVBVBVB4XVBVB4XVB4XTNUY4Y4Y4ZVDTN4ZTNTNTN4ZUL5#5$ULUL5$UL5$5%TNTNTNULTN5%5%TNTNTN5%5&TZTZUPUPUP5&UPUP5&UPUUUPUUUPUPUP5%UPY0Y0YK5%YK5%YKYKY0Y)Y05%Y)Y)Y0Y0Y0Y05$5%5%5&YKYK5&YKYKYKY0Y05&5'Y0Y05'Y05'5(YK5)Y0Y0Y05)5)5*YKYK5*Y05+Y05+Y0YKYKY)Y)5+Y)Y)Y)5+5,5,5-Y0Y0Y)Y)5-5.Y)Y05.5/Y)Y)Y0Y)Y)Y)Y)Y0Y0Y)Y0Y05,Y0Y0Y0Y0Y)Y05,Y05,Y0Y0Y)Y)5,Y0Y)5,Y0Y05,Y)5-5.Y)Y)Y.Y.Y)5-Y)Y)Y.Y.Y)Y.Y)5,Y)Y)Y)5,Y.Y.5,5-Y.Y.Y)Y)Y.Y.Y.Y.5,Y.Y)Y.Y)Y)Y.5+Y)5,Y)Y)Y)5,Y)5,5-Y.Y)Y.Y.Y.Y.Y.5,Y.5,Y.Y.Y.Y)5,Y)Y)5,5-Y.Y.5-Y.Y.Y.5-Y0Y0Y0Y)5-Y05.Y)Y)Y)5.Y)5.Y)5/Y)5/Y)Y)Y.Y.5/Y.Y)Y)Y)5/Y.Y.5/Y.5/50Y.Y.50Y.51Y.51Y)52Y)Y052535454Y)Y)Y)54Y)55565657Y)585859Y05:Y)Y)5:Y)5:5;Y)Y.Y.Y.Y0Y.Y0Y0YKYKY0Y0YK59Y0Y059Y0Y059YKYKYKY0YKY0Y0YCY058YCYC58YCY0Y0Y058YK58YKYK5859YKYK59YCYCYCYCYC595:YCYCYC5:YCYC5:YKYCYC5:5;5;Y.YCY.YK5;YK5YK5>5?YKYKY.Y.5?5@Z)Z)YKYKYK5?Z)Z)Z)5?YKYK5?5@5AZ)5A5BYKYK5BZ)Z)5CZ)YK5C5D5D5EYKYKZ)Z)YKYKZ)Z)YKYKYKYKYOYOYKYOYK5BZ)Z)YOYOYOYO5AYOYKYK5A5BYKYOYKYK5AYOYOYOY.5AYKYKY.Y.5AY.YK5A5B5CYKYK5CY.5CY.Y.Y.5CY.Y.Y.Y.Z'5CZ'5CZ'5DZ'Y.Z'5DZ'5DZ'Y.Y.Y.5DY.5EY.Y.Y.Z'5DZ'5EZ'5EYMYMYMY.Y.Y.5EY.Y.YMYMY.Y.YMYMY.YMYMYMY.Y.5BY.YMY.YMYMY.5A5BZ'Y.5BY.5C5CZ'Z'Z'YKYKYK5CYK5C5DY.YK5DYK5E5E5F5GY.YKYKYK5GYK5GYKYKYKY.5GY.5GY.YKY.5GY.YK5HY.Y.5HY.Y.Y.Y.5H5HZ)Z)Z)5H5IZ)Z)5I5JZ)Z)Z)5JZ)Z)Y.Y.Y.5JY.Y.5JY.5J5KZ)Z)Z)Y.Z)Z)5JZ)Z)Z)Y.Y.5JY.Y.Y.5JY.Z)Y.Z)Z)Y.Z)Z)Z)5H5IZ)Z)Y.Y.Z)Y.Y.Y.Z)5HY.Y.Z$Z$5GZ$5HZ$Z)Z$Z)5H5HZ$5IZ#Z$Z$Z$YOZ)Z)Z)YOZ)Z)YOYOZ)Z)YOZ)Z)Z)Z)YOZ)Z)Z)5D5DZ)5E5FZ)YO5FYOZ)Z)Z)5F5FYO5GYOYO5GYOYOZ)5GYOYOZ)Z)Z)5GZ)YO5GYOY.Y.Y.5G5GZ$Z$Z$Y.Y.Z$Z$5FZ$Z$Z$Y.Z$Y.5FY.5FY.Z$Y.Y.Y.5FY.Y.5FZ$Y.Y.Y.Z$YAZ$YAYAYOYOYOYA5CYA5D5EYAYA5EYAZ$Z$YAYAYOYOYO5DYAYAZ(5D5DYAYAYA5D5EYOYOYAYA5E5FYOYO5FYO5FYA5G5HYMYMZ$YMYMYMYMZ$YMYMZ$5FYMYM5FYMZ'Z'YMYMZ'YMYMYMZ'Z'YM5DZ'Z'5D5EZ'Z'5EZ'YM5EYMYM5EZ'YM5FYM5FYMYMZ'Z'5FZ'YMYMYMY/YMYMYM5E5EY/Y/Y/5EY/Y/Y/YMY/Y/Y/YM5DZ$Z$Z$Z$Z$5DZ$Z$5DY2Z$Y2Z$Z$Y2Y2Z$Z$Z$Z$5BZ$Z$Z$5BZ$Z$5BY2Y25BYMZ$YMZ$5BZ$5C5C5DZ$5EZ$5EZ$5FZ$5FZ$Y25FY/Y2Y2Y/Y/Y2Y2Y/Y/Y25EY25EY2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y25CY/Y/5C5DY/Y/5D5EY/5EY2Y25EY/Y2Y25E5FY2Y2Z$5FYAYAZ$5F5G5HY2Y25HY25HY2YAYAYAYAYA5HY2Y25HY2Z(Z(5H5IZ(Z(5IZ(5IZ(YR5JY2Y25JY2Y/Y/5J5KY25KY2Y2Y/Y2Y2Y25JY2Y2Y2Y/Y/Y/5JY/Y?Y?Y?Y/Y?5IY?Y/Y/Y/5IY?Y?5I5J5J5KY2Y2Y?Y?Y2Y?Y2Y1Y1Y1Y2Y1Y1Y1Y2Y2Y25HY25HY-Y-Y2Y25HY2Y2Y2Y25HY-Y1Y-Y-Y1Y15G5HY25HY2Y1Y-Y-Y-Y15GYGZ'5HY?5HY?Y?5HY?Y?Y?Y2Y1Y1Y1Y?Y?Y15GY?Y?5GY1Y?Y?Y4Y4Y?Y?5FY?5FY4Y1Y4Y?Y?Y4Y4Y45EY4Y4Y4Y?Y4Y4Y?Y?Y4Y?5CY?5DY?Y?Y?Y45DY45DY45EY45EY4Y?Y4Y4Y?Y?5DY?5EY?Y?5EY?Y15EY?Y1Y15EY1Y1Y1Y?5EY1Y1YIYIY?5E5EYIY?YIYIYIY?Y?YIYIY?Y?YIYI5C5DYIYIZ&Z&YSZ&Z&Z&YIYIY?5BY?5B5C5DYIYI5DYIY?YIYIYIY?YI5CYIYIYIYI5CYSYSZ&Z&Z&Z&5BY+X1X1WMWMX1X1VVVVVX5@VUVU5@VUVUVUVRX+VR5@5@5AX+X+5AW6X*X*W65A5B5CW)W)WXWXW)W)WX5BW)W)5BW)WXW)WXWXW)W)WXWXWX5@WXWX5@5AWXWXUCUCUC5A5A5BUCUC5BTNTNTNTZTZ5B5CTZTZ5C5DTN5DTN5ETZTZ5E5FTZTZ5F5GTZTZ5G5HTZ5HTZ5ITZTZ5IV@TZV@V@V@TZTZTZ5HTZTZ5H5ITZTZ5I5JTZ5JTZ5KTZTZ5KUUU4U45K5L5LU9U9U9U4U4U95L5LU35MU35M5NU3U3TNU85NU8TNU8U8U8TNU7TNU7TN5LTNTNU3UU5LUU5LUUU3UUU>UUU>5L5LUUU>U>5LUU5MUUTZTZTZ5MTZ5M5NV@5NV@5OV@5OV@V@V@5OUUV@V@5OUUV@5PV@UUUUUUV@V@V@5OV@UU5OUUUU5OUUUU5O5PY@Y@YD5P5QY@Y@Y@Y@5Q5QYDYDYDYDYDYD5QYD5QYDYD5QYKYDYDY@YD5QYDY*5QY*Y*YKYKY*Y*YK5P5Q5RYKYK5RYKYD5RYDYDYKYK5R5SYKYKYDYDYKYK5R5SYKYK5SYKYKYOYKYOYOYO5RYOYKYK5R5SYKYO5SYO5SYOYKYOYOYOYO5SYO5SYOYOYOYO5S5T5T5UYOYO5U5VZ(5W5WYRZ(5X5XYRZ(Z(5XYLZ(5YYRYRYR5YYRYRYRY-YRY-YRY-YY5WYY5X5XYUYUYUY-Y-YUYUY1Y15WY1Y-5WY-Y-5W5XY-Y-YVYV5XY1YVY1Y1Y1YVY1YVYVY1Y15V5WY1Y15W5X5XYVYVYV5XYVYVYVY1Y1Y15XY1Y1YV5X5XYVYVYV5XY;5YY;Y,5YY,5ZY,Y;Y,Y;5YY;Y,Y;X2X2Y;Y;X2X2Y;Y;X2X2Y;X2Y;Y;5V5WX&X&5WX&W#W#5WW#W#W#X&X&5VW#X&X&X&5VX&X&W#W#X&X&W#W#5U5VX2X25VW#W#W#5V5WWVWVVWVWX&VWX&X&5UWVVWVWVWVWVWWV5TWV5UWVVWWVVWVWWVWV5TW/5TWVVW5UWVWV5UWVVW5UVWVW5UWVVWVWWS5UWSWSVRVRWS5UVRVRVR5UX$5UX$X$5U5V5WX$5W5XX$X$VRX$X$X$5W5XX$X$W'W'W'5XW'W'5X5Y5YX$X$X$5YW'X$W'VRVR5Y5ZW'5ZW'W'VRVRW'W'5Y5ZX$X$VRVRX$X$X$X$X$5YX$X$5YW/W/5YW/W/X$X$W/W/X$X$W/W/X$X$W/W/X$5VX$5W5WW/W/W/X$X$X$5WX$X$5WX$X$X$W/5WX$5W5X5Y5YW/5ZW/X$X$X$5ZX$X$W/W/W'W'X$5YW'W'5YX$W/W/W/5YW/5YW/X$5Y5ZX$X$W/X$W/W/X$X$5Y5ZX$X$5ZX$5ZX$X$X$X$X$X$5ZX$X$X$5ZW/5ZW'W'5Z6#W'W'W/6#W/W/6#6$6%W'W'W/W'6%W/6%6&6'X$X$6'X$6'W'W'W'6'W'6(W'6(W'X$W'X$X$W'6(W'W'6(W'W'6(W'W'X$W'W'W'WV6'WVWV6'W/WVWVW/6'W:W:W'6'W'X$6'W'X$W'W'W'6'6(X$6(X$X$6(6)X$X$X$X$6)X$X$X$X$W'X$X$6(6)W'W'6)6*X$6*X$X$W'W'6*W'W'6*X$X$6*W'X$X$W'W'6*6+W'W'X$X$X$X$X$W'X$X$W'6)X$X$6)6*X$6*6+6,6,6-W'W'6-X$W'W'6-X$W'6.6.X$6/W'W'6/W'W'X$X$W'W'6.W'W'W'6.6/W'W'W'6/W'W'6/60W'W'VRVR6061VRW'61W'WX61WXWP61WXWPWPWX6162WP62WPWPWP62WP63WPWXWPWXWPWX62WPWP62WPWW6363WW64WWWPWPWP64WP64WWWW64WWWWWWWP64656666WWWWWWWXX.WXX.WXWXWX6565WPWPWPWP65WPWPWPWP65WPWPWP65WWWW65WWWW65V&66V&UC66UC6767V&V&V&UC67V?V?6768V?V?6869V?V?UC69V?V?696:V?V?6:6;V?V?UCUC6;6TYTYV&V&TYTY6=TYV&6=TYTY6=TYTYTYV&V&U26=V&V&6=6>V&V&U26>V&V&6>U2TYTY6>6?TYTY6?TY6?6@TPTP6@TYTP6ATP6ATPTPTYTNTYTNTNUUTNUUTNUUTNUUUUUUUU6>TNTNTN6>TNUUTNUUY@6=Y@6>YDYDY7Y7S=S=6=S=SW/W/6>W/6>6?W:W:6?W:6@W:6@W<6A6BW:6BW:W:W:W:W:6BW:W6?W$X-X-W$6?W$6?W$W$6?X-6@X-W$W$W$6@WXWXWX6@WX6@WX6AWXWXWDWXWX6@WXWX6@WXWXWX6@6AW4W4TF6ATF6BTFTKTFTKS6S6S?S?SZSZSB6@SZSZ6@SZSBSZSBSZ6?SZSBSBSB6?SBSB6?SZ6@SZTFTFTF6@S0SOS06@6@6AS0S06ASO6BSO6BT&SOSOSOSOSO6BSOSO6BT&T&SMT&6B6BSM6CSM6CSM6DSMSMSM6DSMS5S5S56DS5S56DSLS5S56D6E6ESQ6FSQ6FSISISISISISQ6F6FSI6G6HSISISI6HSISYSYSYWAWAWA6GWA6G6H6I6IWA6J6KWAWA6KWYWAWY6KWYWYWYWYW7WYW7W7W76IW(6JW(U(U(T/U(T/6IT/T/SL6ISL6JS9S9S96JS9SLSLSLS9S96ISLS9S9SLSLSQSQSQ6H6HSISISISQ6HSQSQSQSKSKSKSQ6GSQSKSQSKSQ6GSQ6GSQSK6GS:6HS:6HSKSKSKS:SKS:S:SK6GSKS:S:6GS:S@6G6HS@S@S@SYS@6HSYSY6HSYS@6HS@S@V)TT6HTTV-V-V-6HT?V-T?6HV-V-V-6H6H6IT?T?6IUET?T?6ISQ6J6KSR6KSRSR6K6LSR6MSR6MSRSRS8S86MS8STS8S8S8S8S8S86LS86L6MSTS8S8S86M6MSTSTSTSTS8STST6L6MT3T3UA6MTITIT=TI6MTITTTT6MV3TIV3TIV3SRSR6L6MS86MS8S8STST6MSTSG6MSGSGT16MT1T16MT26NT2T26NT26O6OT=6P6QT26QT2T2T=T=6Q6RT26RT2T=6RTIT=6ST=6ST=T=V3V36SV36S6T6UT2T26U6VV3T2T2T26V6VV36WV3T=6WT2T26WT=6XT=T26XV3V3SESGSESGSGSGSU6W6WSGSUSUSGSGSG6W6WSUSUSUX>6WXFXF6WXFXFXFT5T56W6XT76XT7T7T7T5T7T7T76WT76XT5T5T56X6XT7T7T7T2T2T26XT2T26XT2T26XT0T06XT2T06YT2T26YT2T0T2T06YT2T2T2T0T2T26X6Y6Y6ZURURUR6ZURUR6ZV37#7$7$UR7%URT27%T2URT2URT27%T2UR7%UR7%URT2URT2T27%T27%T0T0T07%UR7&URV37&URURV3V37&V3UR7&URURURURT0URSG7%SG7&XFXFXD7&7&XF7'7(7(7)XDXDXFXFXD7)XFXF7)XFXFXF7)XFXD7)XDXD7)7*T7T7T1T1T6T6T1T1T6T6T1T1T6T6T1T1T6T6T1T1T6T67%YWYWYWY9Y97%Y9Y9Y9YW7%Y9Y97%Y9YWY9YWY9X1X1X1X)7#7$X)X)X1X1X)X)7#7$X)X)7$X)X)X)X1X17$7%X1X1X17%Y6Y6YB7%7%YB7&YBY6Y6Y67&Y67&YBYB7&YBYBYBY6Y67&YBY6Y6YBYBYBYBY6Y6YBYB7$YB7$7%Y6Y6Y67%Y6Y67%Y6Y6Y6Y)Y.Y)Y)Y)Y)Y)7$Y)7$Y)Y.Y)Y.Y)Y.Y)Y.Y.Y.YJ6ZYWYW6ZYWYWYWYJYJYWYJY5Y56YY.Y.6YY.Y.Y5YWY5Y5Y.Y.6XZ'Y.6X6YZ'6YZ'Y.6Z6ZYNZ%Z%YXYXYNYNYNYX6YYXYN6YYNYNYX6YYXYX6YYI6ZYIYX6ZYXYI6Z7#YZYZYPYP7#YPYP7#7$X1YP7$YPYI7$7%YIYI7%7&YIYI7&YPYPYPX17&X1X1YPYPX1X1YPYPYP7%7%X1X1X1YPX17%X1YPX1X1X1X1YIYIYIYIX1YIYIYI6ZYI7#YI7#YIX1YIYIYIX1YI6ZX1X1YIYIX16ZX1YIX1YIX1X1WRX1X1X1X16XX1X16XX)X1X)X)X)WJW@W@W@WJWJ6V6WW@W@W6W@VBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUC6OTNUY6P6PTNTNTNVDVDVD6PVDVD6PTNVDVDTNTNULULTN6OTN6OTNTNULULTNULTN6N6OUL6OULTNTNULULTNTNULULTN6N6N6OTNTNV@6OTZTZV@V@TZTZUPUP6N6OUPUP6OUPUPUPUUUPY0Y06NY0YK6NYKYK6NY)Y0Y)Y0Y0Y06NY0Y06NYKY0Y0YKYKY0Y06M6N6NYKYKYK6N6OYKYK6O6PYKYKY0Y06PY0YK6PYKYKY0Y0YKY06OY0YKY0Y0Y0YKYKYK6NYKYK6N6OYKYKY0Y06OY0YK6OYK6PYKY0YKY0Y06OY06PY)Y)Y0Y0Y)6OY0Y0Y)Y)Y)6OY)6OY0Y0Y06O6PY06P6QY0Y0Y)Y)Y0Y0Y)Y0Y0Y0Y)Y)Y0Y06NY)Y0Y)Y)Y)6NY)6NY0Y0Y0Y)Y)6NY0Y)Y)6NY)Y06NY06OY)Y)6OY)Y)Y)Y)6OY)Y.Y)Y)Y.Y)Y.6NY)Y)6N6OY)Y)6OY)Y.Y.Y)6OY.Y.Y)Y.Y)6NY)Y)Y)Y)Y)Y.Y)Y)Y.6MY)6MY.Y.Y.Y.6MY.6MY.6NY.Y)Y.Y)Y)Y)6MY.Y.6MY.Y.Y.Y.Y)Y.Y.Y0Y)Y0Y0Y)Y)6KY)Y06K6LY)Y)Y)Y)Y.Y)Y.Y)6KY)Y)6KY.Y)Y.Y)6K6KY.6LY.Y)Y)Y)6L6LY.Y.Y.Y)Y)Y)6L6LY.Y.Y.Y.Y.6LY)6L6MY.Y.Y0Y0Y0Y)Y0Y)6LY)Y06LY06MY0Y06M6N6N6OY)Y)6OY)Y)Y)Y)Y)6O6P6PY.Y)6QY.Y.6QY.Y)6QY)6RY.Y.6RY.Y)6RY)6S6SY)Y06TY)Y)6TY)6TY)6U6V6VY)Y0Y0Y)6VY)Y.6V6WY.Y.YK6WYKYKY0Y06WY0Y06WYKYKY0YCY0Y0YCYC6VYCY0Y0Y06VY0Y0YKYK6U6VYKYK6V6WYK6X6X6YY06Z6Z7#YKYK7#7$YKYKYCYC7$7%YCYC7%YKYCYCYC7%YCY.Y.Y.YC7$YCY.Y.Y.7$Y.YK7$YKYKY.Y.7$Y.YK7$YK7%Y.7%Y.7&YK7&YKYKYK7&YKYKYK7&YKYK7&Y.YKYK7&Y.7'7(7(7)YKYKYKYK7)7*7*7+YKYKYKZ)YKYKZ)Z)7*Z)YK7*YKZ)Z)Z)Z)7*Z)YKYKYK7)Z)7*Z)Z)Z)Z)7*Z)7*YKYK7*YKYKYKZ)Z)YKYK7)YKYKYKYKYOYKYKYOYO7(YOYKYKYO7(YKYKYOYOYOYKYOYOY.Y.Y.7&7&Y.YKYKYK7&YKY.YKYKYK7&7&Y.Y.Y.YKYK7&YKYKY.YKY.7%Y.Y.Y.Y.Z'7%Z'7%Z'Y.Z'Y.7%Y.Y.Y.7%Y.7&Y.Z'Y.Y.7%Z'7&Z'7&Z'Z'Z'Z'Z'7&Z'7&Z'Y.Y.Y.YMYMYMY.Y.Y.7%Y.Y.YM7%Y.7%Y.Z'Y.Y.Y.7%Y.Y.Y.7%7%Z'7&7'7'Z'7(Z'YKYKYK7(YKYKYKY.YKYK7'Y.YK7'7(Y.7(Y.7)Y.YK7)YKY.7)Y.Y.Y.YKY.Y.Y.7(Y.7)Y.7)Y.YKYKYKYKYK7)YK7)YKY.7)Y.YK7*7*Y.YK7+Y.Y.7+7,Y.Y.Y.7,YK7,Z)Z)Z)7,Z)7-Y.Y.7-7.Y.Y.Z)Z)7-Z)Z)Z)Y.7-Z)Z)Y.7-Z)Z)7-Y.Z)Z)Y.Y.Z)Z)7,7-Z)Z)Z)Z)Y.Y.Z)Z)7,Z)Y.Y.Z)Z)Y.Y.Z)7+Y.Y.7+Z)7+Z$Z)7,Z)7,Z)7-Z)Z)7-Z$Z$Z$7-Z$7-Z#Z#Z#7-Z#7.Z#7.YOZ)Z)Z)Z)7.Z)YOZ)7.7/Z)Z)7/Z)Z)YO7/YOZ)Z)Z)YOZ)Z)7.YO7.YOYOYOYOZ)YO7.Z)YO7.YOZ)Z)YOYOZ)Z)7-7.Y.7.7/Z$Y.7/Z$Z$7/70Z$Z$Y.70Y.7171Z$Z$Z$7172Y.Z$72Z$Z$Z$YOYA72YA72YAYOYOYA72YOYO7273YO74YO74YO75YAYAZ(75YO7576YA76YAYOYOYAYAYO76YO76YOYO76YAYO77YOYO777878YAYO79YOYOYO7979YAYAYAYMYMZ$Z$YMYMZ$YMYMZ'YMYMZ'Z'76Z'Z'Z'7677Z'Z'77787879YMYM79YMYMYM79Z'7:Z'7:Z'YM7;Z'Z'YM7;YMYMYM7;YM7;Y/Y/Y/7;Y/Y/YMYMZ$Z$Z$Z$Z$7:7:Y27;Y27;77?Z(Z(Z(7?Z(Z(YR7?Z(Z(YRZ(Z(Z(Z(7>7>Z(YRZ(Y/Y/Y/7>Y/Y/Y/Y27=7>Y2Y2Y27>Y2Y27>7?Y2Y2Y/Y/Y/7?Y/7?Y?Y?Y/7?Y/7@Y?Y?7@7AY?Y?7AY?Y/Y/Y2Y27@7AY2Y2Y2Y27A7BY27BY-Y-Y2Y27BY2Y2Y2Y27BY1Y1Y-Y-Y1Y17AY1Y27AY2Y1YGYGZ'Z'Z'7@Z'YGYG7@Y?Y?YGYGY?Y?Y?Y?Y?7?Y?Y?7?7@7@Y?Y4Y47@Y4Y1Y4Y?Y?Y4Y4Y?Y?Y4Y?Y47>Y4Y?Y?Y?Y4Y?Y4Y?Y4Y?Y4Y?7Y?Y1Y?Y?Y1Y17=Y1Y1Y1Y?Y?Y1Y1YIYI7<7=YIYIY?Y?Y?7TZTZ7>7?TZTZ7?7@7@V@7AV@7AV@V@V@TZTZUUUUTNU4TNU9U4U4U9U9TNU9TNU9U47=U9U9U4U37=U37=U3U9U3TNTNTN7=TNU37=U37=U87>U8U7U7TNU7U3UUU3UUU3UUU3UUUUUUU>7;U>7;U>U>V@7;V@7V@TZ7>TZ7?TZV@TZV@7>V@TZV@TZV@TZV@V@7=V@7>UUUU7>7?7?UU7@UUV@V@V@UU7?UUUUUUV@UUUUUUYD7>YDY@7>YDY@Y@YDYDYDY@YD7=Y@Y@Y@YD7=YDY@7=Y@YDYDYDYDYKYD7W07>7?WIW0W07?W0WIW0WIWIY;Y;W+7>7>W+W+W+Y;Y;W+7>Y;Y;7>Y;W0W0WIW0W0W0WIWIW+W+7<7=W+W+7=7>7>W&W&W+W+W+7>W+7>W+W+W+W&W&W&W+W&W9W&W9W9W9W9W2W97;W9W9W+W+7;W+7;W+W9W+VS7;VSVSW+W+7;W+VSW+VSW+W9W9W17:W1W17:W9W1W1W1W9W9W9W2W2W9W9W9W2W9W9W977W9VSW9VS7677W+W+W+VYW+VYW+76W+VY76W+W+W+Y;Y;Y;76VY76VYVYY;Y;VYY;X&75X&X&7576X&X&VYVY767777X&VYVY77X&78X&X&X&VYX&VY7778X&X&X&VYX&X&X&7778X&X&X&78X&X&VYVYX&X&VY7777X&78X&VYX&VYVY7778VYVYVYVYWCVYX&X&77X&X&X&VYX&VY76VY77VWVWX&VWX&76X&X&VWVW76VWX&X&X&7676VWVWVWW:76W:W:76WVW:WVW:76W:W:7677W:W:7778W:7979W/W:7:W/W/7:W/7:W/W:7;W/W/7;W/W:7;W:W:7;W/W:W/W:W:W/W:W/W:W/W/W:WW.W.W.7>W.WHW.W.7=WH7>WHWHWH7>7?WHWH7?7@WHWH7@W.WHW.W.W.WH7?WHW.WWX7>WX7?WX7?WXWXWDWD7?WDWX7?W4W47?W4W4W47?TK7@TK7@TKTFTKSZ7@SB7A7ASZSB7BSB7BSBSBSB7BSBSBSZSZ7BSZSB7BSBSBTF7BTF7CS0SOS0S0S0SOS0S0SOSO7A7BSOSO7BSOS0SOS0SOSO7ASOSOSOSOSO7A7A7BT&T&T&SM7BSMT&7BT&7CT&7CT&7DT&7D7ESMT&SMSMSM7DSMT&SMS5S57D7ES5S5SLSLS5S5S57D7DS5SQ7ES5SQS5SQ7DSQSQSQSQ7D7ESI7ESISQSQSQSISQSISQ7DSQSQ7DSQSQSQSISISYSYWAWAWAWYWAWAWA7BWY7BWYWY7BWYWYWYWAWA7BWAWY7BWYWYWAWA7BWYWAWAWYWYWAWYWAWYW(W(W(WBW(WBWBWB7>U(T/U(SLSLSL7>SL7>SLSLS9S97>7?S97?7@SLSQSQSQ7@SISI7@SISQ7@SQSI7@SKSKSKSKSK7@SKSKSKSQSKSQS:SQS:SQS:SQS:SI7=SI7>SKSKSKS:S:S:S:S@SKSKS@S@SKSKS@S@S@7:S@S@7:SYS@7;S@S@S@SYV)7:V)V)V-V-V-7:7:UET?7;V-V-T?T?V-V-7:T?V-7:7;T?UEUE7;UESRSQSR7;SR7;SRSR7;SQSRSRSR7;SRSRS@S@SRSRS@S@7:S@SR7:SRSRSRS8SRSRS8S8STS8S8S8S8STSTST77ST7778STSTS8S87879S879STSTUAUAUA79UAUA79UAUATITITIT=TIT=TI777879V3SFSRSFSFSRSR78SRS8S8S878STST78STSGSFSGSGT177T17878T279T279T2T1T2T2T2T279T279T2T2T2T=T279T279T2T2T=T=79T=T2T=T2T27879T2T2T=T=79T=T279T2T=T=79T=7:T=TIT=T=T=T=T=7979V37:V37:V37;7X>7:7;X>X>7;7T7T7T7T5T7T5T7T5T7T7T5T57<7=T77=T7T7T2T2T9T9T2T2T9T9T2T0T0T0T0T2T0T0797:T0T0T2T27:T2T07:T0T0URURT2URV3V3URV3T278T2URURV3URURURV3UR77V3V377V3UR77URURV3V3URV3T2URT2UR75UR76UR7677URUR77URT278T2T2T2URT2URT2T276T0T0T0T2T276T276URT2URT2UR76URV3V3UR7676V3UR777778URURSUSUSG78SGSUSUSU77XDXDXDXFXF77XF7778XDXD78XF79XF79XFXDXDXFXDXDXDXFXFXD78XFXF7879XFXF79XF797:XDXD7:7;T7T7T0T0T7T0YWY9YWYWYWY9YWYWY9Y9YWYWY9Y9YWY9X1X1X)X)X1X1X)X)X)X1X)X)X1X1X)X)X1X)X)X)X1X1X)X)X1X1X)X)X1X1X1X)Y6Y6YBYBY6YBY6YBY6YBYBYBY6Y6YBYBY6Y6YBYBY6YBYBYBY6YBYBYBY6YBY6Y6YBYBY6Y6YBYBY6Y6Y6YBY6Y6YBYBY6Y6Y)Y.Y)Y.Y)Y.Y)Y.YJYJYWYWYJYJYWYWY5Y5Y.Y.Y.Y5Y.Y.Y.Y.Y.Z'Z'Z'Y.Z'Y.Y.Y.Z'Z'Z'Y.Z'Z'Z'Y.Z'YNYNZ%Z%YNYXYNYXYXYXYNYNYXYIYXYXYIYIYXYIYXYXYXYIYXYXYXYIYPYPYZYZYPYPYZYPYZYZYZYPYPYPX1X1YPYPYPX1YPYIYPYPYIYPYIYIYPYPYIYIYIYPYIYIYPYPYIYIYPYPYPYIYPYPX1X1YPX1X1X1YPYPX1X1YPX1YPX1YIYIYIX1YIX1YIX1YIYIYIX1YIYIX1X1X1YIX1YIX1X1X1X)X1X)X)X)WJWJW@W@WJWJW@W@UYTNUYUYUYTNUYUYUYTNUYTNVDVDVDTNVDVDTNTNULULTNULTNULTNTNTNULULULTNULULULULULTNULULULTNULULULTNTNULULTNTNV@V@TZV@UPUPUPUUUPUPUUUUUPUPUUUUY0Y0YKY0Y0Y0YKY0Y0Y)Y0Y0Y0Y0YKYKY0YKYKYKY0Y0YKYKY0Y0YKYKY0YKYKYKY0Y0YKY0Y0Y0Y0YKY0Y0YKYKY0Y0YKY0Y0Y0YKY0Y0Y0YKY0YKYKYKY0Y0Y0YKYKY0Y0YKYKY0Y0YKY0Y0Y0YKYKY0Y0YKY0YKY0YKY0Y)Y)Y0Y)Y0Y)Y0Y)Y)Y)Y0Y0Y)Y)Y0Y0Y)Y0Y0Y0Y)Y)Y0Y0Y)Y0Y0Y0Y)Y)Y0Y0Y)Y)Y0Y0Y)Y)Y0Y)Y0Y)Y0Y)Y0Y)Y0Y0Y)Y)Y0Y0Y0Y)Y0Y0Y0Y)Y0Y)Y0Y)Y0Y0Y)Y)Y0Y)Y)Y)Y.Y.Y.Y)Y.Y.Y)Y)Y.Y.Y)Y)Y.Y.Y)Y)Y.Y.Y.Y.Y)Y)Y)Y.Y)Y.Y)Y)Y.Y.Y)Y)Y)Y.Y.Y.Y)Y)Y)Y.Y)Y)Y)Y.Y.Y.Y)Y)Y.Y.Y)Y)Y.Y.Y)Y)Y0Y0Y0Y)Y)Y)Y0Y)Y0Y0Y)Y.Y)Y)Y)Y)Y)Y.Y)Y.Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.Y.Y.Y)Y.Y.Y.Y.Y)Y)Y)Y)Y)Y.Y.Y)Y.Y.Y.Y0Y0Y0Y)Y0Y)Y0Y0Y0Y0Y0Y)Y0Y0Y0Y)Y0Y)Y)Y)Y0Y)Y0Y)Y0Y)Y)Y)Y)Y)Y0Y)Y)Y)Y)Y.Y)Y.Y.Y.Y)Y.Y)Y)Y)Y.Y)Y.Y.Y.Y.Y)Y)Y.Y.Y.Y)Y.Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.Y)Y.Y)Y.Y0Y)Y0Y)Y)Y)Y0Y0Y)Y)Y0Y)Y0Y)Y0Y)Y)Y)Y0Y0Y)Y)Y0Y0Y)Y)Y)Y0Y)Y)Y)Y.Y)Y.Y.Y.Y)Y.Y.Y.Y0Y0YKYKYKY0YKYKY0Y0YKYKY0Y0Y0YCY0YCY0Y0Y0Y0Y0YKY0Y0YKYKY0Y0YKYCY0YCYCYCYKYCYKYCYCYCY0Y0YCYCY0YCY0YCY0YCYCYCYKYKYCYCYKYKYCYCYKYKYCYCYKYCYCYCYCYKYCYCYKYCYCYCYKYKYCY.YCY.YCY.YCY.Y.Y.YKYKYKY.YKYKYKY.YKY.YKY.YKY.Y.Y.YKYKY.YKY.Y.Y.Y.Y.YKY.YKYKYKY.YKYKYKY.Y.YKYKY.Y.YKYKY.Y.YKY.YKY.YKYKY.YKYKYKYKY.YKYKY.Y.YKY.YKYKYKZ)YKYKZ)Z)Z)Z)Z)YKZ)Z)Z)YKYKZ)Z)Z)YKZ)YKZ)Z)Z)Z)YKYKZ)YKZ)YKYKZ)Z)Z)Z)Z)YKZ)Z)Z)YKZ)YKYKYKZ)Z)YKYKYKYOYKYOYOYKYOYOY.Y.YKY.Y.Y.YKYKYKY.YKYKYKY.YKY.YKY.Y.Y.YKYKY.Y.Y.Y.YKY.Y.Z'Z'Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Y.Y.Z'Y.Y.Y.Z'Y.Z'Z'Z'Z'Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Y.Y.Z'Y.Z'YMY.YMYMY.Z'Z'Z'Y.Z'Z'Z'Y.Y.Y.Z'Y.Y.Y.Z'Z'Z'YMYMZ'Z'YMYMY.Y.Y.Z'Y.Z'Z'Z'YKYKYKY.YKY.Y.Y.YKY.Y.Y.YKY.YKY.YKY.YKY.YKY.YKY.YKYKYKY.YKY.Y.Y.YKY.YKY.YKY.YKY.YKY.YKY.YKYKYKY.YKY.Y.Y.YKY.YKY.Y.Y.YKY.YKY.YKY.Y.Y.Z)Y.Y.Y.Y.Z)Y.Y.Z)Z)Y.Y.Z)Y.YKYKZ)Z)Z)Z)Y.Y.Z)Y.Z)Z)Y.Y.Z)Z)Y.Z)Z)Z)Y.Z)Z)Z)Y.Y.Y.Z)Y.Y.Y.Z)Y.Y.Z)Y.Y.Y.Y.Z)Y.Y.Z)Z)Z)Z)Y3Y3Y.Y.Z)Z)Y.Y.Z)Z)Z)Z$Z)Z$Z$Z$Z)Z$Z)Z$Z)Z$Z)Z$Z)Z$Z)Z$Z$Z$Z#Z$Z#Z$Z)Z#Z#Z#Z#Z#Z)Z#Z)Z#Z)Z)Z)Z)YOZ)YOZ)YOYOYOZ)YOYOZ)Z)YOYOZ)Z)YOYOZ)YOZ)YOZ)Z)Z)YOZ)YOYOYOYOZ)YOYOZ)Z)Z)YOZ)Z)YOYOZ)YOYOYOY.Y.Y.Z$Y.Y.Y.Z$Y.Y.Z$Z$Y.Y.Y.Z$Y.Y.Z$Z$Y.Z$Y.Y.Y.Z$Z$Z$Y.Z$Z$Z$Y.Y.Y.Z$Y.Y.Z$Z$Y.Z$Z$Z$YOYAYAYAYOYAYOYOYAYAYAYOYAYAYOYOYAYAYOYAYOYAYOYAYOYOYOYAYOYAYOYOYAYAZ(Z(YOYAYAYAYOYAYAYAYAYAYOYOYOYAYOYOYOYAYOYOYAYAYOYOYOYAYOYOYOYOYAYAYOYOYAYOYAYAYOYAYAYAYOYOYOYAYAYAYOYOYOYAZ'Z'YMZ'Z'Z'YMYMZ'Z'YMYMZ'Z'YMYMZ'Z'YMZ'YMZ'YMYMZ'Z'YMYMZ'Z'YMYMYMYMYMZ'Z'Z'YMZ'YMZ'YMYMZ'Z'YMZ'Z'Z'YMYMYMYMY/YMYMYMYMY/Y/YMY/Y/Z$Z$Y2Y2Z$Z$Z$Y2Z$Y2Y2Y2Z$Z$Y2Y2Z$Z$Y2Y2Z$Z$Y2Y2Z$Y2Z$Y2Z$Y2Y2Y2Z$Y/Z$Y/Z$Y/Z$Z$Z$Y/Z$Z$Z$Y/Y/Y/Z$Z$Z$Y2Z$Z$Z$Y2Y2Y/Y2Y2Y2Y/Y2Y2Y2Y/Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y/Y2Y/Y/Y2Y2Y/Y/Y2Y/Y/Y/Y/Y2Y/Y/Y2Y2Y/Y2Y2Y2Y/Y2Y2Y2Z$Z$Z$YAYAYAZ$Z$Z$Z$YAYAZ$Z$YAYAZ$YAZ$YAYAY2Y2Y2Y2Y2YAY2YAZ(Z(Z(Y2Z(Z(Z(Z(Y2Y2Y2Y2Y2Z(Y2Z(Z(Z(YRYRZ(YRYRYRYRYRZ(Z(Z(YRYRY/Y/Y2Y2Y/Y/Y/Y2Y/Y/Y2Y2Y2Y/Y2Y2Y/Y/Y2Y2Y/Y2Y2Y2Y/Y/Y/Y?Y/Y/Y?Y?Y/Y?Y/Y?Y/Y?Y/Y/Y?Y?Y2Y2Y?Y2Y2Y2Y2Y?Y2Y2Y/Y/Y2Y2Y/Y2Y2Y2Y2Y2Y2Y-Y2Y2Y-Y-Y2Y-Y-Y-Y2Y2Y-Y-Y2Y2Y2Y1Y-Y1Y-Y-Y2Y2Y2Y1YGYGZ'YGYGYGY?Y?Y?Y?Y1Y1Y?Y?Y1Y1Y?Y1Y1Y1Y?Y?Y4Y4Y4Y4Y1Y4Y4Y?Y4Y4Y4Y?Y?Y?Y4Y?Y4Y?Y?Y?Y1Y1Y1Y?Y1Y?Y1Y?Y1Y1Y?Y?Y?Y1Y?Y?Y1Y1Y?Y1Y1Y1Y?YIY?Y?YIYIY?YIYIYIY?Y?YIYIY?YIYIYIY?YIY?YIY?YIY?Y?Y?YIYIYIY?YIYIYIY?Y?YIYIY?YIYIYIZ&Z&VXVXVXVUVXVXVUVUVXVUVUVUVRVRX+X+VRX+X+X+VRVRVRX+W6W6W6X*W6W6X*X*W6W6X*X*W)W)WXWXW)W)WXW)WXX/WXWXX/X/WXWXX/X/WXWXWXX/WXX/UCTYUCTYUCTYTYTYTYTNTYTYTZTZU3U3TZTZU3U3TNU3TNU3TNU3TNTNU3U3TNTNTZTZU3U3TZTZU3U3TZTZU3U3TZTZTZV@TZTZV@V@TZTZTZV@TZV@V@V@TZV@TZV@TZV@TZV@TZTZTZV@TZTZV@V@TZTZTZUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZV@TZV@TZV@TZV@TZV@TZV@U4U4U9U9U4U3U4U3U4U3U9U3TNTNU3U3TNTNU3U3TNTNTNU8TNU8U8U8UUUUU>U>UUUUU>U>V@UUV@UUV@UUV@UUV@UUV@V@V@V@V@UUTZV@V@V@TZV@TZV@TZTZTZV@TZTZTZV@TZV@V@V@TZV@TZV@V@UUV@UUUUUUV@UUUUUUV@V@UUUUV@UUV@UUV@UUV@UUV@UUV@V@UUUUYDYDYDY@YDYDY@Y@YDYDYDY@Y@YDYDYDY@YDYDYDYKYKYDYDYKYKYKYDYDYKYDYKYKYKYDYKYKYKYDYDYKYKYDYDYKYKYDYDYOYOYKYOYKYKYKYHYHYHYKYHYOYOYKYOYKYOYKYOYOYOYOYQYOYQYOYQYOYOYQYQYOYOYQYQYQYQYOYOYQYQYOYOYQYQYQYOYRYRZ(Z(YRYRZ(YRZ(YRZ(Z(YRYRZ(YRYRYRZ(YRZ(YLZ(YLYLYLZ(YLYRY-Y-Y-YYYYYUYUYYYUYYYUYYYUYUYUY1Y1Y-Y1Y1Y1Y-Y1Y-Y1Y-Y-YVYVY1Y1YVYVYVY1Y1Y1YVYVY1Y1YVY1Y1YVYVYVY1Y1YVYVY1Y1Y1YVY1YVYVYVY1YVYVYVY1Y1YVYVY1YVYVYVY;Y;Y,Y;Y,Y;Y,Y,Y,Y,Y,Y;Y,Y,Y,Y;Y;Y;X&X&Y;X&X&X&W#W#X&W#W#W#W#X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&WVWVVWVWVWWVVWWVVWWVVWWVWVWVWVW/WVWVVWVWVWWVVWVWWVWVVWWVVWWVVWVWWVWVVWWVVRVRWSVRVRVRWSWSVRVRVRX$VRX$X$X$VRVRVRX$VRX$X$X$VRVRVRX$VRX$VRVRX$VRX$X$VRVRX$X$VRVRX$X$VRVRX$X$VRVRX$X$W'W'X$X$W'X$X$X$W'W'X$X$W'W'X$X$VRVRW'VRVRVRX$X$VRX$VRX$VRX$X$X$X$X$W/W/X$X$W/X$X$X$X$W/W/W/X$W/X$X$W/W/X$X$W/W/X$W/W/W/X$W/X$X$X$X$W/W/X$X$W/W/X$X$W/W/X$X$W/W/X$X$W/W/W/W/X$W/W/W/X$W/W'W'X$X$W'X$X$X$W/W/W/X$W/X$X$X$W/X$X$X$W/W/W/X$W/X$X$X$X$X$W/W/X$X$W/W/X$X$W/X$X$X$W'W'W/W/W/W'W/W/W'W'W/X$W/W/X$X$W/W/X$X$W/W/X$W/W/W/W/X$W/W/W'W'W/W/W/W/W/W'W/W/W/W'W/W'W'W'X$X$X$W/W'X$W'X$W'W'X$X$W'W'X$W'W'X$W'X$W'X$W'W'X$X$W'X$WVWVWVW/W/W/WVWVW/W/WVWVW/W/W:W:W/W/W:W:X$W'X$W'W'W'X$X$X$W'X$X$W'X$X$X$X$W'W'W'W'X$W'X$X$X$X$W'X$X$W'W'X$W'X$X$X$W'X$X$W'W'X$W'X$W'X$X$W'W'X$X$W'W'X$X$W'W'X$X$W'W'X$W'X$W'X$X$W'W'W'X$W'X$X$X$X$X$X$W'X$X$W'W'X$X$W'W'X$X$W'W'X$W'X$W'X$X$X$W'X$X$W'W'X$X$W'X$W'W'X$W'X$X$X$W'X$X$W'W'X$W'W'W'X$X$W'W'X$X$X$W'X$X$W'W'X$W'X$W'X$X$W'W'X$W'W'W'X$W'W'W'W'W'X$X$X$X$W'W'W'W'X$X$X$X$W'W'W'VRW'W'VRVRW'W'VRVRW'VRWXWXWPWPWPWPWPWXWXWPWPWPWXWXWPWPWXWPWXWXWXWXWPWPWPWPWWWWWPWPWWWWWPWWWPWWWPWPWPWWWPWWWWWWWPWPWWWPWPWPWWWWWPWWWWWWWPWPWWWWWPWWWPWWWPWPWPWWWPWPWWWWWPWWWWWWWPWWWWWWWXWXWPWPWXWPWPWPWPWXWPWPWPWWWPWWWPWPWPWWWPWPWWWPWPWPWPWWWPWPWWWWWPWWWWWWV&V&UCV&UCV&UCV&UCV&V&V&UCV&UCUCUCUCV?V?UCUCV?V?UCUCV?V?UCUCV?V?UCUCV?V?UCUCUCV?UCUCV?V?UCUCV&V&UCUCV&V&UCUCV&V&UCV&V&V&V?V&V?V&V?V?U2U2U2V&U2U2V&V&U2U2V&TYV&V&V&V&V&TYV&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2TYTYTPTYTYTPTPTPTPTYTPTPTYTYTPTYTYTYTPTYTPTYTPTPUUUUTNUUTNTNUUUUY@YDY@YDY@YDY@YDSXFXFXFXFX>XFXFX>X>XFXFX>XFXFXFT5T5T7T5T7T5T7T7T5T5T7T5T5T5T7T5T5T5T7T5T5T5T7T7T5T5T5T7T5T5T7T7T0T2T0T0T2T2T0T2T0T2T0T0T0T2T0T0URURT2URURV3URURV3URURURV3V3URV3T2URT2URT2T2T2URT2T2T2URT2URURURT2URT2T2T2URT2T2T2T2T0T0T0T2T0T0T2URT2URT2URURURURV3URV3V3V3URURURV3URURURV3URURV3V3URURSGSUSUSUXFXFXDXDXFXFXDXFXDXFXDXFXFXFXDXFXFXFXDXFXDXFXDXDXFXFXDXFXDXFXDXDXFXFXDXDXFXFXDXFXFXFXDXFXDXFXDXDXFXFXDXDT7T0T7T7T0T0T7T7", T = ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers", "Africa/Asmara", "Africa/Bamako", "Africa/Bangui", "Africa/Banjul", "Africa/Bissau", "Africa/Blantyre", "Africa/Brazzaville", "Africa/Bujumbura", "Africa/Cairo", "Africa/Casablanca", "Africa/Ceuta", "Africa/Conakry", "Africa/Dakar", "Africa/Dar_es_Salaam", "Africa/Djibouti", "Africa/Douala", "Africa/El_Aaiun", "Africa/Freetown", "Africa/Gaborone", "Africa/Harare", "Africa/Johannesburg", "Africa/Juba", "Africa/Kampala", "Africa/Khartoum", "Africa/Kigali", "Africa/Kinshasa", "Africa/Lagos", "Africa/Libreville", "Africa/Lome", "Africa/Luanda", "Africa/Lubumbashi", "Africa/Lusaka", "Africa/Malabo", "Africa/Maputo", "Africa/Maseru", "Africa/Mbabane", "Africa/Mogadishu", "Africa/Monrovia", "Africa/Nairobi", "Africa/Ndjamena", "Africa/Niamey", "Africa/Nouakchott", "Africa/Ouagadougou", "Africa/Porto-Novo", "Africa/Sao_Tome", "Africa/Tripoli", "Africa/Tunis", "Africa/Windhoek", "America/Adak", "America/Anchorage", "America/Anguilla", "America/Antigua", "America/Araguaina", "America/Argentina/Buenos_Aires", "America/Argentina/Catamarca", "America/Argentina/Cordoba", "America/Argentina/Jujuy", "America/Argentina/La_Rioja", "America/Argentina/Mendoza", "America/Argentina/Rio_Gallegos", "America/Argentina/Salta", "America/Argentina/San_Juan", "America/Argentina/San_Luis", "America/Argentina/Tucuman", "America/Argentina/Ushuaia", "America/Aruba", "America/Asuncion", "America/Atikokan", "America/Bahia", "America/Bahia_Banderas", "America/Barbados", "America/Belem", "America/Belize", "America/Blanc-Sablon", "America/Boa_Vista", "America/Bogota", "America/Boise", "America/Cambridge_Bay", "America/Campo_Grande", "America/Cancun", "America/Caracas", "America/Cayenne", "America/Cayman", "America/Chicago", "America/Chihuahua", "America/Ciudad_Juarez", "America/Costa_Rica", "America/Coyhaique", "America/Creston", "America/Cuiaba", "America/Curacao", "America/Danmarkshavn", "America/Dawson", "America/Dawson_Creek", "America/Denver", "America/Detroit", "America/Dominica", "America/Edmonton", "America/Eirunepe", "America/El_Salvador", "America/Fort_Nelson", "America/Fortaleza", "America/Glace_Bay", "America/Goose_Bay", "America/Grand_Turk", "America/Guadeloupe", "America/Guatemala", "America/Guayaquil", "America/Guyana", "America/Halifax", "America/Havana", "America/Hermosillo", "America/Indiana/Indianapolis", "America/Indiana/Knox", "America/Indiana/Marengo", "America/Indiana/Petersburg", "America/Indiana/Tell_City", "America/Indiana/Vincennes", "America/Indiana/Winamac", "America/Inuvik", "America/Iqaluit", "America/Jamaica", "America/Juneau", "America/Kentucky/Louisville", "America/Kentucky/Monticello", "America/Kralendijk", "America/La_Paz", "America/Lima", "America/Los_Angeles", "America/Lower_Princes", "America/Maceio", "America/Managua", "America/Manaus", "America/Marigot", "America/Martinique", "America/Matamoros", "America/Mazatlan", "America/Menominee", "America/Merida", "America/Mexico_City", "America/Miquelon", "America/Moncton", "America/Monterrey", "America/Montevideo", "America/Montserrat", "America/Nassau", "America/New_York", "America/Nome", "America/Noronha", "America/North_Dakota/Beulah", "America/North_Dakota/New_Salem", "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Paramaribo", "America/Phoenix", "America/Port-au-Prince", "America/Port_of_Spain", "America/Porto_Velho", "America/Puerto_Rico", "America/Punta_Arenas", "America/Rankin_Inlet", "America/Recife", "America/Regina", "America/Rio_Branco", "America/Santarem", "America/Santiago", "America/Santo_Domingo", "America/Sao_Paulo", "America/Scoresbysund", "America/Sitka", "America/St_Barthelemy", "America/St_Johns", "America/St_Kitts", "America/St_Lucia", "America/St_Thomas", "America/St_Vincent", "America/Swift_Current", "America/Tegucigalpa", "America/Thule", "America/Tijuana", "America/Toronto", "America/Tortola", "America/Vancouver", "America/Whitehorse", "America/Winnipeg", "America/Yakutat", "Antarctica/Casey", "Antarctica/Davis", "Antarctica/DumontDUrville", "Antarctica/Macquarie", "Antarctica/Mawson", "Antarctica/McMurdo", "Antarctica/Rothera", "Antarctica/Syowa", "Antarctica/Troll", "Antarctica/Vostok", "Arctic/Longyearbyen", "Asia/Aden", "Asia/Almaty", "Asia/Amman", "Asia/Anadyr", "Asia/Aqtau", "Asia/Aqtobe", "Asia/Ashgabat", "Asia/Atyrau", "Asia/Baghdad", "Asia/Bahrain", "Asia/Baku", "Asia/Bangkok", "Asia/Barnaul", "Asia/Beirut", "Asia/Bishkek", "Asia/Brunei", "Asia/Chita", "Asia/Colombo", "Asia/Damascus", "Asia/Dhaka", "Asia/Dili", "Asia/Dubai", "Asia/Dushanbe", "Asia/Famagusta", "Asia/Gaza", "Asia/Hebron", "Asia/Ho_Chi_Minh", "Asia/Hong_Kong", "Asia/Hovd", "Asia/Irkutsk", "Asia/Jakarta", "Asia/Jayapura", "Asia/Jerusalem", "Asia/Kabul", "Asia/Kamchatka", "Asia/Karachi", "Asia/Kathmandu", "Asia/Khandyga", "Asia/Kolkata", "Asia/Krasnoyarsk", "Asia/Kuala_Lumpur", "Asia/Kuching", "Asia/Kuwait", "Asia/Macau", "Asia/Magadan", "Asia/Makassar", "Asia/Manila", "Asia/Muscat", "Asia/Nicosia", "Asia/Novokuznetsk", "Asia/Novosibirsk", "Asia/Omsk", "Asia/Oral", "Asia/Phnom_Penh", "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", "Asia/Qostanay", "Asia/Qyzylorda", "Asia/Riyadh", "Asia/Sakhalin", "Asia/Samarkand", "Asia/Seoul", "Asia/Shanghai", "Asia/Singapore", "Asia/Srednekolymsk", "Asia/Taipei", "Asia/Tashkent", "Asia/Tbilisi", "Asia/Tehran", "Asia/Thimphu", "Asia/Tokyo", "Asia/Tomsk", "Asia/Ulaanbaatar", "Asia/Urumqi", "Asia/Ust-Nera", "Asia/Vientiane", "Asia/Vladivostok", "Asia/Yakutsk", "Asia/Yangon", "Asia/Yekaterinburg", "Asia/Yerevan", "Atlantic/Azores", "Atlantic/Bermuda", "Atlantic/Canary", "Atlantic/Cape_Verde", "Atlantic/Faroe", "Atlantic/Madeira", "Atlantic/Reykjavik", "Atlantic/South_Georgia", "Atlantic/St_Helena", "Atlantic/Stanley", "Australia/Adelaide", "Australia/Brisbane", "Australia/Broken_Hill", "Australia/Darwin", "Australia/Eucla", "Australia/Hobart", "Australia/Lord_Howe", "Australia/Melbourne", "Australia/Perth", "Australia/Sydney", "Etc/GMT", "Etc/GMT+1", "Etc/GMT+10", "Etc/GMT+11", "Etc/GMT+12", "Etc/GMT+2", "Etc/GMT+3", "Etc/GMT+4", "Etc/GMT+5", "Etc/GMT+6", "Etc/GMT+7", "Etc/GMT+8", "Etc/GMT+9", "Etc/GMT-1", "Etc/GMT-10", "Etc/GMT-11", "Etc/GMT-12", "Etc/GMT-2", "Etc/GMT-3", "Etc/GMT-4", "Etc/GMT-5", "Etc/GMT-6", "Etc/GMT-7", "Etc/GMT-8", "Etc/GMT-9", "Etc/UTC", "Europe/Amsterdam", "Europe/Andorra", "Europe/Astrakhan", "Europe/Athens", "Europe/Belgrade", "Europe/Berlin", "Europe/Bratislava", "Europe/Brussels", "Europe/Bucharest", "Europe/Budapest", "Europe/Busingen", "Europe/Chisinau", "Europe/Copenhagen", "Europe/Dublin", "Europe/Gibraltar", "Europe/Guernsey", "Europe/Helsinki", "Europe/Isle_of_Man", "Europe/Istanbul", "Europe/Jersey", "Europe/Kaliningrad", "Europe/Kirov", "Europe/Kyiv", "Europe/Lisbon", "Europe/Ljubljana", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", "Europe/Malta", "Europe/Mariehamn", "Europe/Minsk", "Europe/Monaco", "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Podgorica", "Europe/Prague", "Europe/Riga", "Europe/Rome", "Europe/Samara", "Europe/San_Marino", "Europe/Sarajevo", "Europe/Saratov", "Europe/Simferopol", "Europe/Skopje", "Europe/Sofia", "Europe/Stockholm", "Europe/Tallinn", "Europe/Tirane", "Europe/Ulyanovsk", "Europe/Vaduz", "Europe/Vienna", "Europe/Vilnius", "Europe/Volgograd", "Europe/Warsaw", "Europe/Zagreb", "Europe/Zurich", "Indian/Antananarivo", "Indian/Chagos", "Indian/Christmas", "Indian/Cocos", "Indian/Comoro", "Indian/Kerguelen", "Indian/Mahe", "Indian/Maldives", "Indian/Mauritius", "Indian/Mayotte", "Indian/Reunion", "Pacific/Apia", "Pacific/Auckland", "Pacific/Bougainville", "Pacific/Chatham", "Pacific/Chuuk", "Pacific/Easter", "Pacific/Efate", "Pacific/Fakaofo", "Pacific/Fiji", "Pacific/Funafuti", "Pacific/Galapagos", "Pacific/Gambier", "Pacific/Guadalcanal", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", "Pacific/Majuro", "Pacific/Marquesas", "Pacific/Midway", "Pacific/Nauru", "Pacific/Niue", "Pacific/Norfolk", "Pacific/Noumea", "Pacific/Pago_Pago", "Pacific/Palau", "Pacific/Pitcairn", "Pacific/Pohnpei", "Pacific/Port_Moresby", "Pacific/Rarotonga", "Pacific/Saipan", "Pacific/Tahiti", "Pacific/Tarawa", "Pacific/Tongatapu", "Pacific/Wake", "Pacific/Wallis"]; + if (W = +W, !(-90 <= (Y = +Y) && Y <= 90 && -180 <= W && W <= 180)) + throw new RangeError("invalid coordinates"); + if (90 <= Y) + return "Etc/GMT"; + for (var V = -1, S = 48 * (180 + W) / 360.00000000000006, U = 24 * (90 - Y) / 180.00000000000003, Z = 0 | S, $ = 0 | U, K = 96 * $ + 2 * Z, K = 56 * X.charCodeAt(K) + X.charCodeAt(K + 1) - 1995;K + T.length < 3136; ) + K = 56 * X.charCodeAt(K = 8 * (V = V + K + 1) + 4 * ($ = 0 | (U = 2 * (U - $) % 2)) + 2 * (Z = 0 | (S = 2 * (S - Z) % 2)) + 2304) + X.charCodeAt(K + 1) - 1995; + return T[K + T.length - 3136]; + } + typeof module != "undefined" && (module.exports = tzlookup); + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/fbdifflib.js + var require_fbdifflib = __commonJS((exports, module) => { + var floor = Math.floor; + var max = Math.max; + var min = Math.min; + var _calculateRatio = function(matches, length) { + if (length) { + return 2 * matches / length; + } else { + return 1; + } + }; + var _arrayCmp = function(a, b) { + var i, la, lb, _i, _ref, _ref1; + _ref = [a.length, b.length], la = _ref[0], lb = _ref[1]; + for (i = _i = 0, _ref1 = min(la, lb);0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) { + if (a[i] < b[i]) { + return -1; + } + if (a[i] > b[i]) { + return 1; + } + } + return la - lb; + }; + var _has = function(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + }; + var SequenceMatcher = function() { + function SequenceMatcher2(isjunk, a, b, autojunk) { + this.isjunk = isjunk; + if (a == null) { + a = ""; + } + if (b == null) { + b = ""; + } + this.autojunk = autojunk != null ? autojunk : true; + this.a = this.b = null; + this.setSeqs(a, b); + } + SequenceMatcher2.prototype.setSeqs = function(a, b) { + this.setSeq1(a); + return this.setSeq2(b); + }; + SequenceMatcher2.prototype.setSeq1 = function(a) { + if (a === this.a) { + return; + } + this.a = a; + return this.matchingBlocks = this.opcodes = null; + }; + SequenceMatcher2.prototype.setSeq2 = function(b) { + if (b === this.b) { + return; + } + this.b = b; + this.matchingBlocks = this.opcodes = null; + this.fullbcount = null; + return this._chainB(); + }; + SequenceMatcher2.prototype._chainB = function() { + var b, b2j, elt, i, idxs, indices, isjunk, junk, n, ntest, popular, _i, _j, _len, _len1, _ref; + b = this.b; + this.b2j = b2j = {}; + for (i = _i = 0, _len = b.length;_i < _len; i = ++_i) { + elt = b[i]; + indices = _has(b2j, elt) ? b2j[elt] : b2j[elt] = []; + indices.push(i); + } + junk = {}; + isjunk = this.isjunk; + if (isjunk) { + _ref = Object.keys(b2j); + for (_j = 0, _len1 = _ref.length;_j < _len1; _j++) { + elt = _ref[_j]; + if (isjunk(elt)) { + junk[elt] = true; + delete b2j[elt]; + } + } + } + popular = {}; + n = b.length; + if (this.autojunk && n >= 200) { + ntest = floor(n / 100) + 1; + for (elt in b2j) { + idxs = b2j[elt]; + if (idxs.length > ntest) { + popular[elt] = true; + delete b2j[elt]; + } + } + } + this.isbjunk = function(b2) { + return _has(junk, b2); + }; + return this.isbpopular = function(b2) { + return _has(popular, b2); + }; + }; + SequenceMatcher2.prototype.findLongestMatch = function(alo, ahi, blo, bhi) { + var a, b, b2j, besti, bestj, bestsize, i, isbjunk, j, j2len, k, newj2len, _i, _j, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + _ref = [this.a, this.b, this.b2j, this.isbjunk], a = _ref[0], b = _ref[1], b2j = _ref[2], isbjunk = _ref[3]; + _ref1 = [alo, blo, 0], besti = _ref1[0], bestj = _ref1[1], bestsize = _ref1[2]; + j2len = {}; + for (i = _i = alo;alo <= ahi ? _i < ahi : _i > ahi; i = alo <= ahi ? ++_i : --_i) { + newj2len = {}; + _ref2 = _has(b2j, a[i]) ? b2j[a[i]] : []; + for (_j = 0, _len = _ref2.length;_j < _len; _j++) { + j = _ref2[_j]; + if (j < blo) { + continue; + } + if (j >= bhi) { + break; + } + k = newj2len[j] = (j2len[j - 1] || 0) + 1; + if (k > bestsize) { + _ref3 = [i - k + 1, j - k + 1, k], besti = _ref3[0], bestj = _ref3[1], bestsize = _ref3[2]; + } + } + j2len = newj2len; + } + while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] === b[bestj - 1]) { + _ref4 = [besti - 1, bestj - 1, bestsize + 1], besti = _ref4[0], bestj = _ref4[1], bestsize = _ref4[2]; + } + while (besti + bestsize < ahi && bestj + bestsize < bhi && !isbjunk(b[bestj + bestsize]) && a[besti + bestsize] === b[bestj + bestsize]) { + bestsize++; + } + while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] === b[bestj - 1]) { + _ref5 = [besti - 1, bestj - 1, bestsize + 1], besti = _ref5[0], bestj = _ref5[1], bestsize = _ref5[2]; + } + while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) && a[besti + bestsize] === b[bestj + bestsize]) { + bestsize++; + } + return [besti, bestj, bestsize]; + }; + SequenceMatcher2.prototype.getMatchingBlocks = function() { + var ahi, alo, bhi, blo, i, i1, i2, j, j1, j2, k, k1, k2, la, lb, matchingBlocks, nonAdjacent, queue, x, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + if (this.matchingBlocks) { + return this.matchingBlocks; + } + _ref = [this.a.length, this.b.length], la = _ref[0], lb = _ref[1]; + queue = [[0, la, 0, lb]]; + matchingBlocks = []; + while (queue.length) { + _ref1 = queue.pop(), alo = _ref1[0], ahi = _ref1[1], blo = _ref1[2], bhi = _ref1[3]; + _ref2 = x = this.findLongestMatch(alo, ahi, blo, bhi), i = _ref2[0], j = _ref2[1], k = _ref2[2]; + if (k) { + matchingBlocks.push(x); + if (alo < i && blo < j) { + queue.push([alo, i, blo, j]); + } + if (i + k < ahi && j + k < bhi) { + queue.push([i + k, ahi, j + k, bhi]); + } + } + } + matchingBlocks.sort(_arrayCmp); + i1 = j1 = k1 = 0; + nonAdjacent = []; + for (_i = 0, _len = matchingBlocks.length;_i < _len; _i++) { + _ref3 = matchingBlocks[_i], i2 = _ref3[0], j2 = _ref3[1], k2 = _ref3[2]; + if (i1 + k1 === i2 && j1 + k1 === j2) { + k1 += k2; + } else { + if (k1) { + nonAdjacent.push([i1, j1, k1]); + } + _ref4 = [i2, j2, k2], i1 = _ref4[0], j1 = _ref4[1], k1 = _ref4[2]; + } + } + if (k1) { + nonAdjacent.push([i1, j1, k1]); + } + nonAdjacent.push([la, lb, 0]); + return this.matchingBlocks = nonAdjacent; + }; + SequenceMatcher2.prototype.getOpcodes = function() { + var ai, answer, bj, i, j, size, tag, _i, _len, _ref, _ref1, _ref2; + if (this.opcodes) { + return this.opcodes; + } + i = j = 0; + this.opcodes = answer = []; + _ref = this.getMatchingBlocks(); + for (_i = 0, _len = _ref.length;_i < _len; _i++) { + _ref1 = _ref[_i], ai = _ref1[0], bj = _ref1[1], size = _ref1[2]; + tag = ""; + if (i < ai && j < bj) { + tag = "replace"; + } else if (i < ai) { + tag = "delete"; + } else if (j < bj) { + tag = "insert"; + } + if (tag) { + answer.push([tag, i, ai, j, bj]); + } + _ref2 = [ai + size, bj + size], i = _ref2[0], j = _ref2[1]; + if (size) { + answer.push(["equal", ai, i, bj, j]); + } + } + return answer; + }; + SequenceMatcher2.prototype.getGroupedOpcodes = function(n) { + var codes, group, groups, i1, i2, j1, j2, nn, tag, _i, _len, _ref, _ref1, _ref2, _ref3; + if (n == null) { + n = 3; + } + codes = this.getOpcodes(); + if (!codes.length) { + codes = [["equal", 0, 1, 0, 1]]; + } + if (codes[0][0] === "equal") { + _ref = codes[0], tag = _ref[0], i1 = _ref[1], i2 = _ref[2], j1 = _ref[3], j2 = _ref[4]; + codes[0] = [tag, max(i1, i2 - n), i2, max(j1, j2 - n), j2]; + } + if (codes[codes.length - 1][0] === "equal") { + _ref1 = codes[codes.length - 1], tag = _ref1[0], i1 = _ref1[1], i2 = _ref1[2], j1 = _ref1[3], j2 = _ref1[4]; + codes[codes.length - 1] = [tag, i1, min(i2, i1 + n), j1, min(j2, j1 + n)]; + } + nn = n + n; + groups = []; + group = []; + for (_i = 0, _len = codes.length;_i < _len; _i++) { + _ref2 = codes[_i], tag = _ref2[0], i1 = _ref2[1], i2 = _ref2[2], j1 = _ref2[3], j2 = _ref2[4]; + if (tag === "equal" && i2 - i1 > nn) { + group.push([tag, i1, min(i2, i1 + n), j1, min(j2, j1 + n)]); + groups.push(group); + group = []; + _ref3 = [max(i1, i2 - n), max(j1, j2 - n)], i1 = _ref3[0], j1 = _ref3[1]; + } + group.push([tag, i1, i2, j1, j2]); + } + if (group.length && !(group.length === 1 && group[0][0] === "equal")) { + groups.push(group); + } + return groups; + }; + SequenceMatcher2.prototype.ratio = function() { + var match, matches, _i, _len, _ref; + matches = 0; + _ref = this.getMatchingBlocks(); + for (_i = 0, _len = _ref.length;_i < _len; _i++) { + match = _ref[_i]; + matches += match[2]; + } + return _calculateRatio(matches, this.a.length + this.b.length); + }; + SequenceMatcher2.prototype.quickRatio = function() { + var avail, elt, fullbcount, matches, numb, _i, _j, _len, _len1, _ref, _ref1; + if (!this.fullbcount) { + this.fullbcount = fullbcount = {}; + _ref = this.b; + for (_i = 0, _len = _ref.length;_i < _len; _i++) { + elt = _ref[_i]; + fullbcount[elt] = (fullbcount[elt] || 0) + 1; + } + } + fullbcount = this.fullbcount; + avail = {}; + matches = 0; + _ref1 = this.a; + for (_j = 0, _len1 = _ref1.length;_j < _len1; _j++) { + elt = _ref1[_j]; + if (_has(avail, elt)) { + numb = avail[elt]; + } else { + numb = fullbcount[elt] || 0; + } + avail[elt] = numb - 1; + if (numb > 0) { + matches++; + } + } + return _calculateRatio(matches, this.a.length + this.b.length); + }; + SequenceMatcher2.prototype.realQuickRatio = function() { + var la, lb, _ref; + _ref = [this.a.length, this.b.length], la = _ref[0], lb = _ref[1]; + return _calculateRatio(min(la, lb), la + lb); + }; + return SequenceMatcher2; + }(); + module.exports = SequenceMatcher; + }); + + // node_modules/.bun/heap@0.2.7/node_modules/heap/lib/heap.js + var require_heap = __commonJS((exports, module) => { + (function() { + var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup; + floor = Math.floor, min = Math.min; + defaultCmp = function(x, y) { + if (x < y) { + return -1; + } + if (x > y) { + return 1; + } + return 0; + }; + insort = function(a, x, lo, hi, cmp) { + var mid; + if (lo == null) { + lo = 0; + } + if (cmp == null) { + cmp = defaultCmp; + } + if (lo < 0) { + throw new Error("lo must be non-negative"); + } + if (hi == null) { + hi = a.length; + } + while (lo < hi) { + mid = floor((lo + hi) / 2); + if (cmp(x, a[mid]) < 0) { + hi = mid; + } else { + lo = mid + 1; + } + } + return [].splice.apply(a, [lo, lo - lo].concat(x)), x; + }; + heappush = function(array, item, cmp) { + if (cmp == null) { + cmp = defaultCmp; + } + array.push(item); + return _siftdown(array, 0, array.length - 1, cmp); + }; + heappop = function(array, cmp) { + var lastelt, returnitem; + if (cmp == null) { + cmp = defaultCmp; + } + lastelt = array.pop(); + if (array.length) { + returnitem = array[0]; + array[0] = lastelt; + _siftup(array, 0, cmp); + } else { + returnitem = lastelt; + } + return returnitem; + }; + heapreplace = function(array, item, cmp) { + var returnitem; + if (cmp == null) { + cmp = defaultCmp; + } + returnitem = array[0]; + array[0] = item; + _siftup(array, 0, cmp); + return returnitem; + }; + heappushpop = function(array, item, cmp) { + var _ref; + if (cmp == null) { + cmp = defaultCmp; + } + if (array.length && cmp(array[0], item) < 0) { + _ref = [array[0], item], item = _ref[0], array[0] = _ref[1]; + _siftup(array, 0, cmp); + } + return item; + }; + heapify = function(array, cmp) { + var i, _i, _j, _len, _ref, _ref1, _results, _results1; + if (cmp == null) { + cmp = defaultCmp; + } + _ref1 = function() { + _results1 = []; + for (var _j2 = 0, _ref2 = floor(array.length / 2);0 <= _ref2 ? _j2 < _ref2 : _j2 > _ref2; 0 <= _ref2 ? _j2++ : _j2--) { + _results1.push(_j2); + } + return _results1; + }.apply(this).reverse(); + _results = []; + for (_i = 0, _len = _ref1.length;_i < _len; _i++) { + i = _ref1[_i]; + _results.push(_siftup(array, i, cmp)); + } + return _results; + }; + updateItem = function(array, item, cmp) { + var pos; + if (cmp == null) { + cmp = defaultCmp; + } + pos = array.indexOf(item); + if (pos === -1) { + return; + } + _siftdown(array, 0, pos, cmp); + return _siftup(array, pos, cmp); + }; + nlargest = function(array, n, cmp) { + var elem, result, _i, _len, _ref; + if (cmp == null) { + cmp = defaultCmp; + } + result = array.slice(0, n); + if (!result.length) { + return result; + } + heapify(result, cmp); + _ref = array.slice(n); + for (_i = 0, _len = _ref.length;_i < _len; _i++) { + elem = _ref[_i]; + heappushpop(result, elem, cmp); + } + return result.sort(cmp).reverse(); + }; + nsmallest = function(array, n, cmp) { + var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results; + if (cmp == null) { + cmp = defaultCmp; + } + if (n * 10 <= array.length) { + result = array.slice(0, n).sort(cmp); + if (!result.length) { + return result; + } + los = result[result.length - 1]; + _ref = array.slice(n); + for (_i = 0, _len = _ref.length;_i < _len; _i++) { + elem = _ref[_i]; + if (cmp(elem, los) < 0) { + insort(result, elem, 0, null, cmp); + result.pop(); + los = result[result.length - 1]; + } + } + return result; + } + heapify(array, cmp); + _results = []; + for (i = _j = 0, _ref1 = min(n, array.length);0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) { + _results.push(heappop(array, cmp)); + } + return _results; + }; + _siftdown = function(array, startpos, pos, cmp) { + var newitem, parent, parentpos; + if (cmp == null) { + cmp = defaultCmp; + } + newitem = array[pos]; + while (pos > startpos) { + parentpos = pos - 1 >> 1; + parent = array[parentpos]; + if (cmp(newitem, parent) < 0) { + array[pos] = parent; + pos = parentpos; + continue; + } + break; + } + return array[pos] = newitem; + }; + _siftup = function(array, pos, cmp) { + var childpos, endpos, newitem, rightpos, startpos; + if (cmp == null) { + cmp = defaultCmp; + } + endpos = array.length; + startpos = pos; + newitem = array[pos]; + childpos = 2 * pos + 1; + while (childpos < endpos) { + rightpos = childpos + 1; + if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) { + childpos = rightpos; + } + array[pos] = array[childpos]; + pos = childpos; + childpos = 2 * pos + 1; + } + array[pos] = newitem; + return _siftdown(array, startpos, pos, cmp); + }; + Heap = function() { + Heap2.push = heappush; + Heap2.pop = heappop; + Heap2.replace = heapreplace; + Heap2.pushpop = heappushpop; + Heap2.heapify = heapify; + Heap2.updateItem = updateItem; + Heap2.nlargest = nlargest; + Heap2.nsmallest = nsmallest; + function Heap2(cmp) { + this.cmp = cmp != null ? cmp : defaultCmp; + this.nodes = []; + } + Heap2.prototype.push = function(x) { + return heappush(this.nodes, x, this.cmp); + }; + Heap2.prototype.pop = function() { + return heappop(this.nodes, this.cmp); + }; + Heap2.prototype.peek = function() { + return this.nodes[0]; + }; + Heap2.prototype.contains = function(x) { + return this.nodes.indexOf(x) !== -1; + }; + Heap2.prototype.replace = function(x) { + return heapreplace(this.nodes, x, this.cmp); + }; + Heap2.prototype.pushpop = function(x) { + return heappushpop(this.nodes, x, this.cmp); + }; + Heap2.prototype.heapify = function() { + return heapify(this.nodes, this.cmp); + }; + Heap2.prototype.updateItem = function(x) { + return updateItem(this.nodes, x, this.cmp); + }; + Heap2.prototype.clear = function() { + return this.nodes = []; + }; + Heap2.prototype.empty = function() { + return this.nodes.length === 0; + }; + Heap2.prototype.size = function() { + return this.nodes.length; + }; + Heap2.prototype.clone = function() { + var heap; + heap = new Heap2; + heap.nodes = this.nodes.slice(0); + return heap; + }; + Heap2.prototype.toArray = function() { + return this.nodes.slice(0); + }; + Heap2.prototype.insert = Heap2.prototype.push; + Heap2.prototype.top = Heap2.prototype.peek; + Heap2.prototype.front = Heap2.prototype.peek; + Heap2.prototype.has = Heap2.prototype.contains; + Heap2.prototype.copy = Heap2.prototype.clone; + return Heap2; + }(); + (function(root, factory) { + if (typeof define === "function" && define.amd) { + return define([], factory); + } else if (typeof exports === "object") { + return module.exports = factory(); + } else { + return root.Heap = factory(); + } + })(this, function() { + return Heap; + }); + }).call(exports); + }); + + // node_modules/.bun/heap@0.2.7/node_modules/heap/index.js + var require_heap2 = __commonJS((exports, module) => { + module.exports = require_heap(); + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayMap.js + var require__arrayMap = __commonJS((exports, module) => { + function arrayMap(array, iteratee) { + var index = -1, length = array == null ? 0 : array.length, result = Array(length); + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + module.exports = arrayMap; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isArray.js + var require_isArray = __commonJS((exports, module) => { + var isArray = Array.isArray; + module.exports = isArray; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_freeGlobal.js + var require__freeGlobal = __commonJS((exports, module) => { + var freeGlobal = typeof global == "object" && global && global.Object === Object && global; + module.exports = freeGlobal; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_root.js + var require__root = __commonJS((exports, module) => { + var freeGlobal = require__freeGlobal(); + var freeSelf = typeof self == "object" && self && self.Object === Object && self; + var root = freeGlobal || freeSelf || Function("return this")(); + module.exports = root; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Symbol.js + var require__Symbol = __commonJS((exports, module) => { + var root = require__root(); + var Symbol2 = root.Symbol; + module.exports = Symbol2; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getRawTag.js + var require__getRawTag = __commonJS((exports, module) => { + var Symbol2 = require__Symbol(); + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + var nativeObjectToString = objectProto.toString; + var symToStringTag = Symbol2 ? Symbol2.toStringTag : undefined; + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; + } + module.exports = getRawTag; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_objectToString.js + var require__objectToString = __commonJS((exports, module) => { + var objectProto = Object.prototype; + var nativeObjectToString = objectProto.toString; + function objectToString(value) { + return nativeObjectToString.call(value); + } + module.exports = objectToString; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseGetTag.js + var require__baseGetTag = __commonJS((exports, module) => { + var Symbol2 = require__Symbol(); + var getRawTag = require__getRawTag(); + var objectToString = require__objectToString(); + var nullTag = "[object Null]"; + var undefinedTag = "[object Undefined]"; + var symToStringTag = Symbol2 ? Symbol2.toStringTag : undefined; + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value); + } + module.exports = baseGetTag; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isObjectLike.js + var require_isObjectLike = __commonJS((exports, module) => { + function isObjectLike(value) { + return value != null && typeof value == "object"; + } + module.exports = isObjectLike; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isSymbol.js + var require_isSymbol = __commonJS((exports, module) => { + var baseGetTag = require__baseGetTag(); + var isObjectLike = require_isObjectLike(); + var symbolTag = "[object Symbol]"; + function isSymbol(value) { + return typeof value == "symbol" || isObjectLike(value) && baseGetTag(value) == symbolTag; + } + module.exports = isSymbol; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isKey.js + var require__isKey = __commonJS((exports, module) => { + var isArray = require_isArray(); + var isSymbol = require_isSymbol(); + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/; + var reIsPlainProp = /^\w*$/; + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == "number" || type == "symbol" || type == "boolean" || value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object); + } + module.exports = isKey; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isObject.js + var require_isObject = __commonJS((exports, module) => { + function isObject(value) { + var type = typeof value; + return value != null && (type == "object" || type == "function"); + } + module.exports = isObject; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isFunction.js + var require_isFunction = __commonJS((exports, module) => { + var baseGetTag = require__baseGetTag(); + var isObject = require_isObject(); + var asyncTag = "[object AsyncFunction]"; + var funcTag = "[object Function]"; + var genTag = "[object GeneratorFunction]"; + var proxyTag = "[object Proxy]"; + function isFunction(value) { + if (!isObject(value)) { + return false; + } + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + module.exports = isFunction; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_coreJsData.js + var require__coreJsData = __commonJS((exports, module) => { + var root = require__root(); + var coreJsData = root["__core-js_shared__"]; + module.exports = coreJsData; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isMasked.js + var require__isMasked = __commonJS((exports, module) => { + var coreJsData = require__coreJsData(); + var maskSrcKey = function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ""); + return uid ? "Symbol(src)_1." + uid : ""; + }(); + function isMasked(func) { + return !!maskSrcKey && maskSrcKey in func; + } + module.exports = isMasked; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_toSource.js + var require__toSource = __commonJS((exports, module) => { + var funcProto = Function.prototype; + var funcToString = funcProto.toString; + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return func + ""; + } catch (e) {} + } + return ""; + } + module.exports = toSource; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsNative.js + var require__baseIsNative = __commonJS((exports, module) => { + var isFunction = require_isFunction(); + var isMasked = require__isMasked(); + var isObject = require_isObject(); + var toSource = require__toSource(); + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + var reIsHostCtor = /^\[object .+?Constructor\]$/; + var funcProto = Function.prototype; + var objectProto = Object.prototype; + var funcToString = funcProto.toString; + var hasOwnProperty = objectProto.hasOwnProperty; + var reIsNative = RegExp("^" + funcToString.call(hasOwnProperty).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"); + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + module.exports = baseIsNative; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getValue.js + var require__getValue = __commonJS((exports, module) => { + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + module.exports = getValue; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getNative.js + var require__getNative = __commonJS((exports, module) => { + var baseIsNative = require__baseIsNative(); + var getValue = require__getValue(); + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + module.exports = getNative; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_nativeCreate.js + var require__nativeCreate = __commonJS((exports, module) => { + var getNative = require__getNative(); + var nativeCreate = getNative(Object, "create"); + module.exports = nativeCreate; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashClear.js + var require__hashClear = __commonJS((exports, module) => { + var nativeCreate = require__nativeCreate(); + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + this.size = 0; + } + module.exports = hashClear; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashDelete.js + var require__hashDelete = __commonJS((exports, module) => { + function hashDelete(key) { + var result = this.has(key) && delete this.__data__[key]; + this.size -= result ? 1 : 0; + return result; + } + module.exports = hashDelete; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashGet.js + var require__hashGet = __commonJS((exports, module) => { + var nativeCreate = require__nativeCreate(); + var HASH_UNDEFINED = "__lodash_hash_undefined__"; + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + module.exports = hashGet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashHas.js + var require__hashHas = __commonJS((exports, module) => { + var nativeCreate = require__nativeCreate(); + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); + } + module.exports = hashHas; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashSet.js + var require__hashSet = __commonJS((exports, module) => { + var nativeCreate = require__nativeCreate(); + var HASH_UNDEFINED = "__lodash_hash_undefined__"; + function hashSet(key, value) { + var data = this.__data__; + this.size += this.has(key) ? 0 : 1; + data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED : value; + return this; + } + module.exports = hashSet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Hash.js + var require__Hash = __commonJS((exports, module) => { + var hashClear = require__hashClear(); + var hashDelete = require__hashDelete(); + var hashGet = require__hashGet(); + var hashHas = require__hashHas(); + var hashSet = require__hashSet(); + function Hash(entries) { + var index = -1, length = entries == null ? 0 : entries.length; + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + Hash.prototype.clear = hashClear; + Hash.prototype["delete"] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + module.exports = Hash; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheClear.js + var require__listCacheClear = __commonJS((exports, module) => { + function listCacheClear() { + this.__data__ = []; + this.size = 0; + } + module.exports = listCacheClear; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/eq.js + var require_eq = __commonJS((exports, module) => { + function eq(value, other) { + return value === other || value !== value && other !== other; + } + module.exports = eq; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_assocIndexOf.js + var require__assocIndexOf = __commonJS((exports, module) => { + var eq = require_eq(); + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + module.exports = assocIndexOf; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheDelete.js + var require__listCacheDelete = __commonJS((exports, module) => { + var assocIndexOf = require__assocIndexOf(); + var arrayProto = Array.prototype; + var splice = arrayProto.splice; + function listCacheDelete(key) { + var data = this.__data__, index = assocIndexOf(data, key); + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + --this.size; + return true; + } + module.exports = listCacheDelete; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheGet.js + var require__listCacheGet = __commonJS((exports, module) => { + var assocIndexOf = require__assocIndexOf(); + function listCacheGet(key) { + var data = this.__data__, index = assocIndexOf(data, key); + return index < 0 ? undefined : data[index][1]; + } + module.exports = listCacheGet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheHas.js + var require__listCacheHas = __commonJS((exports, module) => { + var assocIndexOf = require__assocIndexOf(); + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + module.exports = listCacheHas; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheSet.js + var require__listCacheSet = __commonJS((exports, module) => { + var assocIndexOf = require__assocIndexOf(); + function listCacheSet(key, value) { + var data = this.__data__, index = assocIndexOf(data, key); + if (index < 0) { + ++this.size; + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + module.exports = listCacheSet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_ListCache.js + var require__ListCache = __commonJS((exports, module) => { + var listCacheClear = require__listCacheClear(); + var listCacheDelete = require__listCacheDelete(); + var listCacheGet = require__listCacheGet(); + var listCacheHas = require__listCacheHas(); + var listCacheSet = require__listCacheSet(); + function ListCache(entries) { + var index = -1, length = entries == null ? 0 : entries.length; + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + ListCache.prototype.clear = listCacheClear; + ListCache.prototype["delete"] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + module.exports = ListCache; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Map.js + var require__Map = __commonJS((exports, module) => { + var getNative = require__getNative(); + var root = require__root(); + var Map = getNative(root, "Map"); + module.exports = Map; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheClear.js + var require__mapCacheClear = __commonJS((exports, module) => { + var Hash = require__Hash(); + var ListCache = require__ListCache(); + var Map = require__Map(); + function mapCacheClear() { + this.size = 0; + this.__data__ = { + hash: new Hash, + map: new (Map || ListCache), + string: new Hash + }; + } + module.exports = mapCacheClear; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isKeyable.js + var require__isKeyable = __commonJS((exports, module) => { + function isKeyable(value) { + var type = typeof value; + return type == "string" || type == "number" || type == "symbol" || type == "boolean" ? value !== "__proto__" : value === null; + } + module.exports = isKeyable; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getMapData.js + var require__getMapData = __commonJS((exports, module) => { + var isKeyable = require__isKeyable(); + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) ? data[typeof key == "string" ? "string" : "hash"] : data.map; + } + module.exports = getMapData; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheDelete.js + var require__mapCacheDelete = __commonJS((exports, module) => { + var getMapData = require__getMapData(); + function mapCacheDelete(key) { + var result = getMapData(this, key)["delete"](key); + this.size -= result ? 1 : 0; + return result; + } + module.exports = mapCacheDelete; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheGet.js + var require__mapCacheGet = __commonJS((exports, module) => { + var getMapData = require__getMapData(); + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + module.exports = mapCacheGet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheHas.js + var require__mapCacheHas = __commonJS((exports, module) => { + var getMapData = require__getMapData(); + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + module.exports = mapCacheHas; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheSet.js + var require__mapCacheSet = __commonJS((exports, module) => { + var getMapData = require__getMapData(); + function mapCacheSet(key, value) { + var data = getMapData(this, key), size = data.size; + data.set(key, value); + this.size += data.size == size ? 0 : 1; + return this; + } + module.exports = mapCacheSet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_MapCache.js + var require__MapCache = __commonJS((exports, module) => { + var mapCacheClear = require__mapCacheClear(); + var mapCacheDelete = require__mapCacheDelete(); + var mapCacheGet = require__mapCacheGet(); + var mapCacheHas = require__mapCacheHas(); + var mapCacheSet = require__mapCacheSet(); + function MapCache(entries) { + var index = -1, length = entries == null ? 0 : entries.length; + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype["delete"] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + module.exports = MapCache; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/memoize.js + var require_memoize = __commonJS((exports, module) => { + var MapCache = require__MapCache(); + var FUNC_ERROR_TEXT = "Expected a function"; + function memoize(func, resolver) { + if (typeof func != "function" || resolver != null && typeof resolver != "function") { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache; + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + memoize.Cache = MapCache; + module.exports = memoize; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_memoizeCapped.js + var require__memoizeCapped = __commonJS((exports, module) => { + var memoize = require_memoize(); + var MAX_MEMOIZE_SIZE = 500; + function memoizeCapped(func) { + var result = memoize(func, function(key) { + if (cache.size === MAX_MEMOIZE_SIZE) { + cache.clear(); + } + return key; + }); + var cache = result.cache; + return result; + } + module.exports = memoizeCapped; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stringToPath.js + var require__stringToPath = __commonJS((exports, module) => { + var memoizeCapped = require__memoizeCapped(); + var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + var reEscapeChar = /\\(\\)?/g; + var stringToPath = memoizeCapped(function(string) { + var result = []; + if (string.charCodeAt(0) === 46) { + result.push(""); + } + string.replace(rePropName, function(match, number, quote, subString) { + result.push(quote ? subString.replace(reEscapeChar, "$1") : number || match); + }); + return result; + }); + module.exports = stringToPath; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseToString.js + var require__baseToString = __commonJS((exports, module) => { + var Symbol2 = require__Symbol(); + var arrayMap = require__arrayMap(); + var isArray = require_isArray(); + var isSymbol = require_isSymbol(); + var INFINITY = 1 / 0; + var symbolProto = Symbol2 ? Symbol2.prototype : undefined; + var symbolToString = symbolProto ? symbolProto.toString : undefined; + function baseToString(value) { + if (typeof value == "string") { + return value; + } + if (isArray(value)) { + return arrayMap(value, baseToString) + ""; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ""; + } + var result = value + ""; + return result == "0" && 1 / value == -INFINITY ? "-0" : result; + } + module.exports = baseToString; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/toString.js + var require_toString = __commonJS((exports, module) => { + var baseToString = require__baseToString(); + function toString(value) { + return value == null ? "" : baseToString(value); + } + module.exports = toString; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_castPath.js + var require__castPath = __commonJS((exports, module) => { + var isArray = require_isArray(); + var isKey = require__isKey(); + var stringToPath = require__stringToPath(); + var toString = require_toString(); + function castPath(value, object) { + if (isArray(value)) { + return value; + } + return isKey(value, object) ? [value] : stringToPath(toString(value)); + } + module.exports = castPath; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_toKey.js + var require__toKey = __commonJS((exports, module) => { + var isSymbol = require_isSymbol(); + var INFINITY = 1 / 0; + function toKey(value) { + if (typeof value == "string" || isSymbol(value)) { + return value; + } + var result = value + ""; + return result == "0" && 1 / value == -INFINITY ? "-0" : result; + } + module.exports = toKey; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseGet.js + var require__baseGet = __commonJS((exports, module) => { + var castPath = require__castPath(); + var toKey = require__toKey(); + function baseGet(object, path) { + path = castPath(path, object); + var index = 0, length = path.length; + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return index && index == length ? object : undefined; + } + module.exports = baseGet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackClear.js + var require__stackClear = __commonJS((exports, module) => { + var ListCache = require__ListCache(); + function stackClear() { + this.__data__ = new ListCache; + this.size = 0; + } + module.exports = stackClear; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackDelete.js + var require__stackDelete = __commonJS((exports, module) => { + function stackDelete(key) { + var data = this.__data__, result = data["delete"](key); + this.size = data.size; + return result; + } + module.exports = stackDelete; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackGet.js + var require__stackGet = __commonJS((exports, module) => { + function stackGet(key) { + return this.__data__.get(key); + } + module.exports = stackGet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackHas.js + var require__stackHas = __commonJS((exports, module) => { + function stackHas(key) { + return this.__data__.has(key); + } + module.exports = stackHas; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackSet.js + var require__stackSet = __commonJS((exports, module) => { + var ListCache = require__ListCache(); + var Map = require__Map(); + var MapCache = require__MapCache(); + var LARGE_ARRAY_SIZE = 200; + function stackSet(key, value) { + var data = this.__data__; + if (data instanceof ListCache) { + var pairs = data.__data__; + if (!Map || pairs.length < LARGE_ARRAY_SIZE - 1) { + pairs.push([key, value]); + this.size = ++data.size; + return this; + } + data = this.__data__ = new MapCache(pairs); + } + data.set(key, value); + this.size = data.size; + return this; + } + module.exports = stackSet; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Stack.js + var require__Stack = __commonJS((exports, module) => { + var ListCache = require__ListCache(); + var stackClear = require__stackClear(); + var stackDelete = require__stackDelete(); + var stackGet = require__stackGet(); + var stackHas = require__stackHas(); + var stackSet = require__stackSet(); + function Stack(entries) { + var data = this.__data__ = new ListCache(entries); + this.size = data.size; + } + Stack.prototype.clear = stackClear; + Stack.prototype["delete"] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + module.exports = Stack; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_setCacheAdd.js + var require__setCacheAdd = __commonJS((exports, module) => { + var HASH_UNDEFINED = "__lodash_hash_undefined__"; + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + module.exports = setCacheAdd; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_setCacheHas.js + var require__setCacheHas = __commonJS((exports, module) => { + function setCacheHas(value) { + return this.__data__.has(value); + } + module.exports = setCacheHas; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_SetCache.js + var require__SetCache = __commonJS((exports, module) => { + var MapCache = require__MapCache(); + var setCacheAdd = require__setCacheAdd(); + var setCacheHas = require__setCacheHas(); + function SetCache(values) { + var index = -1, length = values == null ? 0 : values.length; + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + module.exports = SetCache; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arraySome.js + var require__arraySome = __commonJS((exports, module) => { + function arraySome(array, predicate) { + var index = -1, length = array == null ? 0 : array.length; + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + module.exports = arraySome; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_cacheHas.js + var require__cacheHas = __commonJS((exports, module) => { + function cacheHas(cache, key) { + return cache.has(key); + } + module.exports = cacheHas; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_equalArrays.js + var require__equalArrays = __commonJS((exports, module) => { + var SetCache = require__SetCache(); + var arraySome = require__arraySome(); + var cacheHas = require__cacheHas(); + var COMPARE_PARTIAL_FLAG = 1; + var COMPARE_UNORDERED_FLAG = 2; + function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length; + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + var arrStacked = stack.get(array); + var othStacked = stack.get(other); + if (arrStacked && othStacked) { + return arrStacked == other && othStacked == array; + } + var index = -1, result = true, seen = bitmask & COMPARE_UNORDERED_FLAG ? new SetCache : undefined; + stack.set(array, other); + stack.set(other, array); + while (++index < arrLength) { + var arrValue = array[index], othValue = other[index]; + if (customizer) { + var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + if (seen) { + if (!arraySome(other, function(othValue2, othIndex) { + if (!cacheHas(seen, othIndex) && (arrValue === othValue2 || equalFunc(arrValue, othValue2, bitmask, customizer, stack))) { + return seen.push(othIndex); + } + })) { + result = false; + break; + } + } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + result = false; + break; + } + } + stack["delete"](array); + stack["delete"](other); + return result; + } + module.exports = equalArrays; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Uint8Array.js + var require__Uint8Array = __commonJS((exports, module) => { + var root = require__root(); + var Uint8Array2 = root.Uint8Array; + module.exports = Uint8Array2; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapToArray.js + var require__mapToArray = __commonJS((exports, module) => { + function mapToArray(map) { + var index = -1, result = Array(map.size); + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + module.exports = mapToArray; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_setToArray.js + var require__setToArray = __commonJS((exports, module) => { + function setToArray(set) { + var index = -1, result = Array(set.size); + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + module.exports = setToArray; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_equalByTag.js + var require__equalByTag = __commonJS((exports, module) => { + var Symbol2 = require__Symbol(); + var Uint8Array2 = require__Uint8Array(); + var eq = require_eq(); + var equalArrays = require__equalArrays(); + var mapToArray = require__mapToArray(); + var setToArray = require__setToArray(); + var COMPARE_PARTIAL_FLAG = 1; + var COMPARE_UNORDERED_FLAG = 2; + var boolTag = "[object Boolean]"; + var dateTag = "[object Date]"; + var errorTag = "[object Error]"; + var mapTag = "[object Map]"; + var numberTag = "[object Number]"; + var regexpTag = "[object RegExp]"; + var setTag = "[object Set]"; + var stringTag = "[object String]"; + var symbolTag = "[object Symbol]"; + var arrayBufferTag = "[object ArrayBuffer]"; + var dataViewTag = "[object DataView]"; + var symbolProto = Symbol2 ? Symbol2.prototype : undefined; + var symbolValueOf = symbolProto ? symbolProto.valueOf : undefined; + function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) { + return false; + } + object = object.buffer; + other = other.buffer; + case arrayBufferTag: + if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array2(object), new Uint8Array2(other))) { + return false; + } + return true; + case boolTag: + case dateTag: + case numberTag: + return eq(+object, +other); + case errorTag: + return object.name == other.name && object.message == other.message; + case regexpTag: + case stringTag: + return object == other + ""; + case mapTag: + var convert = mapToArray; + case setTag: + var isPartial = bitmask & COMPARE_PARTIAL_FLAG; + convert || (convert = setToArray); + if (object.size != other.size && !isPartial) { + return false; + } + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= COMPARE_UNORDERED_FLAG; + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); + stack["delete"](object); + return result; + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + module.exports = equalByTag; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayPush.js + var require__arrayPush = __commonJS((exports, module) => { + function arrayPush(array, values) { + var index = -1, length = values.length, offset = array.length; + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + module.exports = arrayPush; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseGetAllKeys.js + var require__baseGetAllKeys = __commonJS((exports, module) => { + var arrayPush = require__arrayPush(); + var isArray = require_isArray(); + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + module.exports = baseGetAllKeys; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayFilter.js + var require__arrayFilter = __commonJS((exports, module) => { + function arrayFilter(array, predicate) { + var index = -1, length = array == null ? 0 : array.length, resIndex = 0, result = []; + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } + module.exports = arrayFilter; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/stubArray.js + var require_stubArray = __commonJS((exports, module) => { + function stubArray() { + return []; + } + module.exports = stubArray; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getSymbols.js + var require__getSymbols = __commonJS((exports, module) => { + var arrayFilter = require__arrayFilter(); + var stubArray = require_stubArray(); + var objectProto = Object.prototype; + var propertyIsEnumerable = objectProto.propertyIsEnumerable; + var nativeGetSymbols = Object.getOwnPropertySymbols; + var getSymbols = !nativeGetSymbols ? stubArray : function(object) { + if (object == null) { + return []; + } + object = Object(object); + return arrayFilter(nativeGetSymbols(object), function(symbol) { + return propertyIsEnumerable.call(object, symbol); + }); + }; + module.exports = getSymbols; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseTimes.js + var require__baseTimes = __commonJS((exports, module) => { + function baseTimes(n, iteratee) { + var index = -1, result = Array(n); + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + module.exports = baseTimes; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsArguments.js + var require__baseIsArguments = __commonJS((exports, module) => { + var baseGetTag = require__baseGetTag(); + var isObjectLike = require_isObjectLike(); + var argsTag = "[object Arguments]"; + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; + } + module.exports = baseIsArguments; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isArguments.js + var require_isArguments = __commonJS((exports, module) => { + var baseIsArguments = require__baseIsArguments(); + var isObjectLike = require_isObjectLike(); + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + var propertyIsEnumerable = objectProto.propertyIsEnumerable; + var isArguments = baseIsArguments(function() { + return arguments; + }()) ? baseIsArguments : function(value) { + return isObjectLike(value) && hasOwnProperty.call(value, "callee") && !propertyIsEnumerable.call(value, "callee"); + }; + module.exports = isArguments; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/stubFalse.js + var require_stubFalse = __commonJS((exports, module) => { + function stubFalse() { + return false; + } + module.exports = stubFalse; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isBuffer.js + var require_isBuffer = __commonJS((exports, module) => { + var root = require__root(); + var stubFalse = require_stubFalse(); + var freeExports = typeof exports == "object" && exports && !exports.nodeType && exports; + var freeModule = freeExports && typeof module == "object" && module && !module.nodeType && module; + var moduleExports = freeModule && freeModule.exports === freeExports; + var Buffer = moduleExports ? root.Buffer : undefined; + var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined; + var isBuffer = nativeIsBuffer || stubFalse; + module.exports = isBuffer; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isIndex.js + var require__isIndex = __commonJS((exports, module) => { + var MAX_SAFE_INTEGER = 9007199254740991; + var reIsUint = /^(?:0|[1-9]\d*)$/; + function isIndex(value, length) { + var type = typeof value; + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && (type == "number" || type != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); + } + module.exports = isIndex; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isLength.js + var require_isLength = __commonJS((exports, module) => { + var MAX_SAFE_INTEGER = 9007199254740991; + function isLength(value) { + return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + module.exports = isLength; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsTypedArray.js + var require__baseIsTypedArray = __commonJS((exports, module) => { + var baseGetTag = require__baseGetTag(); + var isLength = require_isLength(); + var isObjectLike = require_isObjectLike(); + var argsTag = "[object Arguments]"; + var arrayTag = "[object Array]"; + var boolTag = "[object Boolean]"; + var dateTag = "[object Date]"; + var errorTag = "[object Error]"; + var funcTag = "[object Function]"; + var mapTag = "[object Map]"; + var numberTag = "[object Number]"; + var objectTag = "[object Object]"; + var regexpTag = "[object RegExp]"; + var setTag = "[object Set]"; + var stringTag = "[object String]"; + var weakMapTag = "[object WeakMap]"; + var arrayBufferTag = "[object ArrayBuffer]"; + var dataViewTag = "[object DataView]"; + var float32Tag = "[object Float32Array]"; + var float64Tag = "[object Float64Array]"; + var int8Tag = "[object Int8Array]"; + var int16Tag = "[object Int16Array]"; + var int32Tag = "[object Int32Array]"; + var uint8Tag = "[object Uint8Array]"; + var uint8ClampedTag = "[object Uint8ClampedArray]"; + var uint16Tag = "[object Uint16Array]"; + var uint32Tag = "[object Uint32Array]"; + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; + function baseIsTypedArray(value) { + return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; + } + module.exports = baseIsTypedArray; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseUnary.js + var require__baseUnary = __commonJS((exports, module) => { + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + module.exports = baseUnary; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_nodeUtil.js + var require__nodeUtil = __commonJS((exports, module) => { + var freeGlobal = require__freeGlobal(); + var freeExports = typeof exports == "object" && exports && !exports.nodeType && exports; + var freeModule = freeExports && typeof module == "object" && module && !module.nodeType && module; + var moduleExports = freeModule && freeModule.exports === freeExports; + var freeProcess = moduleExports && freeGlobal.process; + var nodeUtil = function() { + try { + var types = freeModule && freeModule.require && freeModule.require("util").types; + if (types) { + return types; + } + return freeProcess && freeProcess.binding && freeProcess.binding("util"); + } catch (e) {} + }(); + module.exports = nodeUtil; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isTypedArray.js + var require_isTypedArray = __commonJS((exports, module) => { + var baseIsTypedArray = require__baseIsTypedArray(); + var baseUnary = require__baseUnary(); + var nodeUtil = require__nodeUtil(); + var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + module.exports = isTypedArray; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayLikeKeys.js + var require__arrayLikeKeys = __commonJS((exports, module) => { + var baseTimes = require__baseTimes(); + var isArguments = require_isArguments(); + var isArray = require_isArray(); + var isBuffer = require_isBuffer(); + var isIndex = require__isIndex(); + var isTypedArray = require_isTypedArray(); + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length; + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == "length" || isBuff && (key == "offset" || key == "parent") || isType && (key == "buffer" || key == "byteLength" || key == "byteOffset") || isIndex(key, length)))) { + result.push(key); + } + } + return result; + } + module.exports = arrayLikeKeys; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isPrototype.js + var require__isPrototype = __commonJS((exports, module) => { + var objectProto = Object.prototype; + function isPrototype(value) { + var Ctor = value && value.constructor, proto = typeof Ctor == "function" && Ctor.prototype || objectProto; + return value === proto; + } + module.exports = isPrototype; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_overArg.js + var require__overArg = __commonJS((exports, module) => { + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + module.exports = overArg; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_nativeKeys.js + var require__nativeKeys = __commonJS((exports, module) => { + var overArg = require__overArg(); + var nativeKeys = overArg(Object.keys, Object); + module.exports = nativeKeys; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseKeys.js + var require__baseKeys = __commonJS((exports, module) => { + var isPrototype = require__isPrototype(); + var nativeKeys = require__nativeKeys(); + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != "constructor") { + result.push(key); + } + } + return result; + } + module.exports = baseKeys; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isArrayLike.js + var require_isArrayLike = __commonJS((exports, module) => { + var isFunction = require_isFunction(); + var isLength = require_isLength(); + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + module.exports = isArrayLike; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/keys.js + var require_keys = __commonJS((exports, module) => { + var arrayLikeKeys = require__arrayLikeKeys(); + var baseKeys = require__baseKeys(); + var isArrayLike = require_isArrayLike(); + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + module.exports = keys; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getAllKeys.js + var require__getAllKeys = __commonJS((exports, module) => { + var baseGetAllKeys = require__baseGetAllKeys(); + var getSymbols = require__getSymbols(); + var keys = require_keys(); + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + module.exports = getAllKeys; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_equalObjects.js + var require__equalObjects = __commonJS((exports, module) => { + var getAllKeys = require__getAllKeys(); + var COMPARE_PARTIAL_FLAG = 1; + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, objProps = getAllKeys(object), objLength = objProps.length, othProps = getAllKeys(other), othLength = othProps.length; + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + var objStacked = stack.get(object); + var othStacked = stack.get(other); + if (objStacked && othStacked) { + return objStacked == other && othStacked == object; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], othValue = other[key]; + if (customizer) { + var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack); + } + if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) { + result = false; + break; + } + skipCtor || (skipCtor = key == "constructor"); + } + if (result && !skipCtor) { + var objCtor = object.constructor, othCtor = other.constructor; + if (objCtor != othCtor && (("constructor" in object) && ("constructor" in other)) && !(typeof objCtor == "function" && objCtor instanceof objCtor && typeof othCtor == "function" && othCtor instanceof othCtor)) { + result = false; + } + } + stack["delete"](object); + stack["delete"](other); + return result; + } + module.exports = equalObjects; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_DataView.js + var require__DataView = __commonJS((exports, module) => { + var getNative = require__getNative(); + var root = require__root(); + var DataView = getNative(root, "DataView"); + module.exports = DataView; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Promise.js + var require__Promise = __commonJS((exports, module) => { + var getNative = require__getNative(); + var root = require__root(); + var Promise2 = getNative(root, "Promise"); + module.exports = Promise2; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Set.js + var require__Set = __commonJS((exports, module) => { + var getNative = require__getNative(); + var root = require__root(); + var Set2 = getNative(root, "Set"); + module.exports = Set2; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_WeakMap.js + var require__WeakMap = __commonJS((exports, module) => { + var getNative = require__getNative(); + var root = require__root(); + var WeakMap2 = getNative(root, "WeakMap"); + module.exports = WeakMap2; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getTag.js + var require__getTag = __commonJS((exports, module) => { + var DataView = require__DataView(); + var Map = require__Map(); + var Promise2 = require__Promise(); + var Set2 = require__Set(); + var WeakMap2 = require__WeakMap(); + var baseGetTag = require__baseGetTag(); + var toSource = require__toSource(); + var mapTag = "[object Map]"; + var objectTag = "[object Object]"; + var promiseTag = "[object Promise]"; + var setTag = "[object Set]"; + var weakMapTag = "[object WeakMap]"; + var dataViewTag = "[object DataView]"; + var dataViewCtorString = toSource(DataView); + var mapCtorString = toSource(Map); + var promiseCtorString = toSource(Promise2); + var setCtorString = toSource(Set2); + var weakMapCtorString = toSource(WeakMap2); + var getTag = baseGetTag; + if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag || Map && getTag(new Map) != mapTag || Promise2 && getTag(Promise2.resolve()) != promiseTag || Set2 && getTag(new Set2) != setTag || WeakMap2 && getTag(new WeakMap2) != weakMapTag) { + getTag = function(value) { + var result = baseGetTag(value), Ctor = result == objectTag ? value.constructor : undefined, ctorString = Ctor ? toSource(Ctor) : ""; + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: + return dataViewTag; + case mapCtorString: + return mapTag; + case promiseCtorString: + return promiseTag; + case setCtorString: + return setTag; + case weakMapCtorString: + return weakMapTag; + } + } + return result; + }; + } + module.exports = getTag; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsEqualDeep.js + var require__baseIsEqualDeep = __commonJS((exports, module) => { + var Stack = require__Stack(); + var equalArrays = require__equalArrays(); + var equalByTag = require__equalByTag(); + var equalObjects = require__equalObjects(); + var getTag = require__getTag(); + var isArray = require_isArray(); + var isBuffer = require_isBuffer(); + var isTypedArray = require_isTypedArray(); + var COMPARE_PARTIAL_FLAG = 1; + var argsTag = "[object Arguments]"; + var arrayTag = "[object Array]"; + var objectTag = "[object Object]"; + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + var objIsArr = isArray(object), othIsArr = isArray(other), objTag = objIsArr ? arrayTag : getTag(object), othTag = othIsArr ? arrayTag : getTag(other); + objTag = objTag == argsTag ? objectTag : objTag; + othTag = othTag == argsTag ? objectTag : othTag; + var objIsObj = objTag == objectTag, othIsObj = othTag == objectTag, isSameTag = objTag == othTag; + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false; + } + objIsArr = true; + objIsObj = false; + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, "__wrapped__"), othIsWrapped = othIsObj && hasOwnProperty.call(other, "__wrapped__"); + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, othUnwrapped = othIsWrapped ? other.value() : other; + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, bitmask, customizer, equalFunc, stack); + } + module.exports = baseIsEqualDeep; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsEqual.js + var require__baseIsEqual = __commonJS((exports, module) => { + var baseIsEqualDeep = require__baseIsEqualDeep(); + var isObjectLike = require_isObjectLike(); + function baseIsEqual(value, other, bitmask, customizer, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); + } + module.exports = baseIsEqual; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsMatch.js + var require__baseIsMatch = __commonJS((exports, module) => { + var Stack = require__Stack(); + var baseIsEqual = require__baseIsEqual(); + var COMPARE_PARTIAL_FLAG = 1; + var COMPARE_UNORDERED_FLAG = 2; + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, length = index, noCustomizer = !customizer; + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], objValue = object[key], srcValue = data[1]; + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result)) { + return false; + } + } + } + return true; + } + module.exports = baseIsMatch; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isStrictComparable.js + var require__isStrictComparable = __commonJS((exports, module) => { + var isObject = require_isObject(); + function isStrictComparable(value) { + return value === value && !isObject(value); + } + module.exports = isStrictComparable; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getMatchData.js + var require__getMatchData = __commonJS((exports, module) => { + var isStrictComparable = require__isStrictComparable(); + var keys = require_keys(); + function getMatchData(object) { + var result = keys(object), length = result.length; + while (length--) { + var key = result[length], value = object[key]; + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + module.exports = getMatchData; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_matchesStrictComparable.js + var require__matchesStrictComparable = __commonJS((exports, module) => { + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && (srcValue !== undefined || (key in Object(object))); + }; + } + module.exports = matchesStrictComparable; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseMatches.js + var require__baseMatches = __commonJS((exports, module) => { + var baseIsMatch = require__baseIsMatch(); + var getMatchData = require__getMatchData(); + var matchesStrictComparable = require__matchesStrictComparable(); + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + module.exports = baseMatches; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/get.js + var require_get = __commonJS((exports, module) => { + var baseGet = require__baseGet(); + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + module.exports = get; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseHasIn.js + var require__baseHasIn = __commonJS((exports, module) => { + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + module.exports = baseHasIn; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hasPath.js + var require__hasPath = __commonJS((exports, module) => { + var castPath = require__castPath(); + var isArguments = require_isArguments(); + var isArray = require_isArray(); + var isIndex = require__isIndex(); + var isLength = require_isLength(); + var toKey = require__toKey(); + function hasPath(object, path, hasFunc) { + path = castPath(path, object); + var index = -1, length = path.length, result = false; + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index != length) { + return result; + } + length = object == null ? 0 : object.length; + return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); + } + module.exports = hasPath; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/hasIn.js + var require_hasIn = __commonJS((exports, module) => { + var baseHasIn = require__baseHasIn(); + var hasPath = require__hasPath(); + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + module.exports = hasIn; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseMatchesProperty.js + var require__baseMatchesProperty = __commonJS((exports, module) => { + var baseIsEqual = require__baseIsEqual(); + var get = require_get(); + var hasIn = require_hasIn(); + var isKey = require__isKey(); + var isStrictComparable = require__isStrictComparable(); + var matchesStrictComparable = require__matchesStrictComparable(); + var toKey = require__toKey(); + var COMPARE_PARTIAL_FLAG = 1; + var COMPARE_UNORDERED_FLAG = 2; + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return objValue === undefined && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); + }; + } + module.exports = baseMatchesProperty; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/identity.js + var require_identity = __commonJS((exports, module) => { + function identity(value) { + return value; + } + module.exports = identity; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseProperty.js + var require__baseProperty = __commonJS((exports, module) => { + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + module.exports = baseProperty; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_basePropertyDeep.js + var require__basePropertyDeep = __commonJS((exports, module) => { + var baseGet = require__baseGet(); + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + module.exports = basePropertyDeep; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/property.js + var require_property = __commonJS((exports, module) => { + var baseProperty = require__baseProperty(); + var basePropertyDeep = require__basePropertyDeep(); + var isKey = require__isKey(); + var toKey = require__toKey(); + function property(path) { + return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); + } + module.exports = property; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIteratee.js + var require__baseIteratee = __commonJS((exports, module) => { + var baseMatches = require__baseMatches(); + var baseMatchesProperty = require__baseMatchesProperty(); + var identity = require_identity(); + var isArray = require_isArray(); + var property = require_property(); + function baseIteratee(value) { + if (typeof value == "function") { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == "object") { + return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value); + } + return property(value); + } + module.exports = baseIteratee; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_createBaseFor.js + var require__createBaseFor = __commonJS((exports, module) => { + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length; + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + module.exports = createBaseFor; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseFor.js + var require__baseFor = __commonJS((exports, module) => { + var createBaseFor = require__createBaseFor(); + var baseFor = createBaseFor(); + module.exports = baseFor; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseForOwn.js + var require__baseForOwn = __commonJS((exports, module) => { + var baseFor = require__baseFor(); + var keys = require_keys(); + function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); + } + module.exports = baseForOwn; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_createBaseEach.js + var require__createBaseEach = __commonJS((exports, module) => { + var isArrayLike = require_isArrayLike(); + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, index = fromRight ? length : -1, iterable = Object(collection); + while (fromRight ? index-- : ++index < length) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + module.exports = createBaseEach; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseEach.js + var require__baseEach = __commonJS((exports, module) => { + var baseForOwn = require__baseForOwn(); + var createBaseEach = require__createBaseEach(); + var baseEach = createBaseEach(baseForOwn); + module.exports = baseEach; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseMap.js + var require__baseMap = __commonJS((exports, module) => { + var baseEach = require__baseEach(); + var isArrayLike = require_isArrayLike(); + function baseMap(collection, iteratee) { + var index = -1, result = isArrayLike(collection) ? Array(collection.length) : []; + baseEach(collection, function(value, key, collection2) { + result[++index] = iteratee(value, key, collection2); + }); + return result; + } + module.exports = baseMap; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseSortBy.js + var require__baseSortBy = __commonJS((exports, module) => { + function baseSortBy(array, comparer) { + var length = array.length; + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + module.exports = baseSortBy; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_compareAscending.js + var require__compareAscending = __commonJS((exports, module) => { + var isSymbol = require_isSymbol(); + function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined, valIsNull = value === null, valIsReflexive = value === value, valIsSymbol = isSymbol(value); + var othIsDefined = other !== undefined, othIsNull = other === null, othIsReflexive = other === other, othIsSymbol = isSymbol(other); + if (!othIsNull && !othIsSymbol && !valIsSymbol && value > other || valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol || valIsNull && othIsDefined && othIsReflexive || !valIsDefined && othIsReflexive || !valIsReflexive) { + return 1; + } + if (!valIsNull && !valIsSymbol && !othIsSymbol && value < other || othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol || othIsNull && valIsDefined && valIsReflexive || !othIsDefined && valIsReflexive || !othIsReflexive) { + return -1; + } + } + return 0; + } + module.exports = compareAscending; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_compareMultiple.js + var require__compareMultiple = __commonJS((exports, module) => { + var compareAscending = require__compareAscending(); + function compareMultiple(object, other, orders) { + var index = -1, objCriteria = object.criteria, othCriteria = other.criteria, length = objCriteria.length, ordersLength = orders.length; + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == "desc" ? -1 : 1); + } + } + return object.index - other.index; + } + module.exports = compareMultiple; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseOrderBy.js + var require__baseOrderBy = __commonJS((exports, module) => { + var arrayMap = require__arrayMap(); + var baseGet = require__baseGet(); + var baseIteratee = require__baseIteratee(); + var baseMap = require__baseMap(); + var baseSortBy = require__baseSortBy(); + var baseUnary = require__baseUnary(); + var compareMultiple = require__compareMultiple(); + var identity = require_identity(); + var isArray = require_isArray(); + function baseOrderBy(collection, iteratees, orders) { + if (iteratees.length) { + iteratees = arrayMap(iteratees, function(iteratee) { + if (isArray(iteratee)) { + return function(value) { + return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee); + }; + } + return iteratee; + }); + } else { + iteratees = [identity]; + } + var index = -1; + iteratees = arrayMap(iteratees, baseUnary(baseIteratee)); + var result = baseMap(collection, function(value, key, collection2) { + var criteria = arrayMap(iteratees, function(iteratee) { + return iteratee(value); + }); + return { criteria, index: ++index, value }; + }); + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + module.exports = baseOrderBy; + }); + + // node_modules/.bun/lodash@4.17.21/node_modules/lodash/orderBy.js + var require_orderBy = __commonJS((exports, module) => { + var baseOrderBy = require__baseOrderBy(); + var isArray = require_isArray(); + function orderBy(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + orders = guard ? undefined : orders; + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseOrderBy(collection, iteratees, orders); + } + module.exports = orderBy; + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/native_utils.js + var require_native_utils = __commonJS((exports, module) => { + function _intersect(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr2.length === 0) + return []; + if (arr1.length < 100 && arr2.length < 100) { + return arr1.filter((item) => arr2.includes(item)); + } + const set = new Set(arr2); + return arr1.filter((item) => set.has(item)); + } + function _intersectWith(arr1, arr2, comparator) { + if (!arr1 || !arr2 || arr1.length === 0 || arr2.length === 0) + return []; + return arr1.filter((a) => arr2.some((b) => comparator(a, b))); + } + function _difference(arr1, arr2) { + if (!arr1) + return []; + if (!arr2 || arr2.length === 0) + return arr1.slice(); + if (arr1.length < 100 && arr2.length < 100) { + return arr1.filter((item) => !arr2.includes(item)); + } + const set = new Set(arr2); + return arr1.filter((item) => !set.has(item)); + } + function _differenceWith(arr1, arr2, comparator) { + if (!arr1) + return []; + if (!arr2 || arr2.length === 0) + return arr1.slice(); + return arr1.filter((a) => !arr2.some((b) => comparator(a, b))); + } + function _uniq(arr) { + if (!arr || arr.length === 0) + return []; + if (arr.length === 1) + return arr.slice(); + return [...new Set(arr)]; + } + function _uniqWith(arr, comparator) { + if (!arr || arr.length === 0) + return []; + if (arr.length === 1) + return arr.slice(); + const result = []; + outer: + for (let i = 0;i < arr.length; i++) { + const current = arr[i]; + for (let j = 0;j < result.length; j++) { + if (comparator(current, result[j])) { + continue outer; + } + } + result.push(current); + } + return result; + } + function _partialRight(func) { + const boundArgs = Array.prototype.slice.call(arguments, 1); + return function() { + const args = Array.prototype.slice.call(arguments); + return func.apply(this, args.concat(boundArgs)); + }; + } + function _forEach(obj, callback) { + if (!obj) + return; + if (Array.isArray(obj)) { + for (let i = 0;i < obj.length; i++) { + callback(obj[i], i); + } + } else { + const keys = Object.keys(obj); + for (let i = 0;i < keys.length; i++) { + callback(obj[keys[i]], keys[i]); + } + } + } + module.exports = { + _intersect, + _intersectWith, + _difference, + _differenceWith, + _uniq, + _uniqWith, + _partialRight, + _forEach, + _isArray: Array.isArray + }; + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/iLeven.js + var require_iLeven = __commonJS((exports, module) => { + var collator; + try { + collator = typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined" ? Intl.Collator("generic", { sensitivity: "base" }) : null; + } catch (err) { + if (typeof console !== undefined) + console.warn("Collator could not be initialized and wouldn't be used"); + } + module.exports = function leven(a, b, options) { + var arr = []; + var charCodeCache = []; + var useCollator = options && collator && options.useCollator; + var subcost = 1; + if (options && options.subcost && typeof options.subcost === "number") + subcost = options.subcost; + if (a === b) { + return 0; + } + var achars = Array.from(a); + var bchars = Array.from(b); + var aLen = achars.length; + var bLen = bchars.length; + if (aLen === 0) { + return bLen; + } + if (bLen === 0) { + return aLen; + } + var bCharCode; + var ret; + var tmp; + var tmp2; + var i = 0; + var j = 0; + while (i < aLen) { + charCodeCache[i] = achars[i].codePointAt(0); + arr[i] = ++i; + } + if (!useCollator) { + while (j < bLen) { + bCharCode = bchars[j].codePointAt(0); + tmp = j++; + ret = j; + for (i = 0;i < aLen; i++) { + tmp2 = bCharCode === charCodeCache[i] ? tmp : tmp + subcost; + tmp = arr[i]; + ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; + } + } + } else { + while (j < bLen) { + bCharCode = bchars[j].codePointAt(0); + tmp = j++; + ret = j; + for (i = 0;i < aLen; i++) { + tmp2 = collator.compare(String.fromCodePoint(bCharCode), String.fromCodePoint(charCodeCache[i])) === 0 ? tmp : tmp + subcost; + tmp = arr[i]; + ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; + } + } + } + return ret; + }; + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/wildcardLeven.js + var require_wildcardLeven = __commonJS((exports, module) => { + var collator; + try { + collator = typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined" ? Intl.Collator("generic", { sensitivity: "base" }) : null; + } catch (err) { + if (typeof console !== undefined) + console.warn("Collator could not be initialized and wouldn't be used"); + } + module.exports = function leven(a, b, options, regLeven) { + var arr = []; + var charCodeCache = []; + var useCollator = options && collator && options.useCollator; + var subcost = 1; + if (options && options.subcost && typeof options.subcost === "number") + subcost = options.subcost; + if (a === b) { + return 0; + } + var aLen = a.length; + var bLen = b.length; + if (aLen === 0) { + return bLen; + } + if (bLen === 0) { + return aLen; + } + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + if (options && options.wildcards && typeof options.wildcards === "string" && options.wildcards.length > 0) { + var wildchar; + var wildcode; + if (options.full_process === false && options.processed !== true) { + wildchar = options.wildcards[0]; + wildcode = wildchar.charCodeAt(0); + var pattern = "[" + escapeRegExp(options.wildcards) + "]"; + a = a.replace(new RegExp(pattern, "g"), wildchar); + b = b.replace(new RegExp(pattern, "g"), wildchar); + if (a === b) + return 0; + } else { + wildchar = options.wildcards[0].toLowerCase(); + wildcode = wildchar.charCodeAt(0); + } + var bCharCode; + var ret; + var tmp; + var tmp2; + var i = 0; + var j = 0; + while (i < aLen) { + charCodeCache[i] = a.charCodeAt(i); + arr[i] = ++i; + } + if (!useCollator) { + while (j < bLen) { + bCharCode = b.charCodeAt(j); + tmp = j++; + ret = j; + for (i = 0;i < aLen; i++) { + tmp2 = bCharCode === charCodeCache[i] || bCharCode === wildcode || charCodeCache[i] === wildcode ? tmp : tmp + subcost; + tmp = arr[i]; + ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; + } + } + } else { + while (j < bLen) { + bCharCode = b.charCodeAt(j); + tmp = j++; + ret = j; + for (i = 0;i < aLen; i++) { + tmp2 = collator.compare(String.fromCharCode(bCharCode), String.fromCharCode(charCodeCache[i])) === 0 || bCharCode === wildcode || charCodeCache[i] === wildcode ? tmp : tmp + subcost; + tmp = arr[i]; + ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; + } + } + } + return ret; + } else { + return regLeven(a, b, options); + } + }; + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/leven.js + var require_leven = __commonJS((exports, module) => { + var collator; + try { + collator = typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined" ? Intl.Collator("generic", { sensitivity: "base" }) : null; + } catch (err) { + if (typeof console !== undefined) + console.warn("Collator could not be initialized and wouldn't be used"); + } + module.exports = function leven(a, b, options) { + var arr = []; + var charCodeCache = []; + var useCollator = options && collator && options.useCollator; + var subcost = 1; + if (options && options.subcost && typeof options.subcost === "number") + subcost = options.subcost; + if (a === b) { + return 0; + } + var aLen = a.length; + var bLen = b.length; + if (aLen === 0) { + return bLen; + } + if (bLen === 0) { + return aLen; + } + var bCharCode; + var ret; + var tmp; + var tmp2; + var i = 0; + var j = 0; + while (i < aLen) { + charCodeCache[i] = a.charCodeAt(i); + arr[i] = ++i; + } + if (!useCollator) { + while (j < bLen) { + bCharCode = b.charCodeAt(j); + tmp = j++; + ret = j; + for (i = 0;i < aLen; i++) { + tmp2 = bCharCode === charCodeCache[i] ? tmp : tmp + subcost; + tmp = arr[i]; + ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; + } + } + } else { + while (j < bLen) { + bCharCode = b.charCodeAt(j); + tmp = j++; + ret = j; + for (i = 0;i < aLen; i++) { + tmp2 = collator.compare(String.fromCharCode(bCharCode), String.fromCharCode(charCodeCache[i])) === 0 ? tmp : tmp + subcost; + tmp = arr[i]; + ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; + } + } + } + return ret; + }; + }); + + // node_modules/.bun/setimmediate@1.0.5/node_modules/setimmediate/setImmediate.js + var require_setImmediate = __commonJS((exports) => { + (function(global2, undefined2) { + if (global2.setImmediate) { + return; + } + var nextHandle = 1; + var tasksByHandle = {}; + var currentlyRunningATask = false; + var doc = global2.document; + var registerImmediate; + function setImmediate2(callback) { + if (typeof callback !== "function") { + callback = new Function("" + callback); + } + var args = new Array(arguments.length - 1); + for (var i = 0;i < args.length; i++) { + args[i] = arguments[i + 1]; + } + var task = { callback, args }; + tasksByHandle[nextHandle] = task; + registerImmediate(nextHandle); + return nextHandle++; + } + function clearImmediate(handle) { + delete tasksByHandle[handle]; + } + function run(task) { + var callback = task.callback; + var args = task.args; + switch (args.length) { + case 0: + callback(); + break; + case 1: + callback(args[0]); + break; + case 2: + callback(args[0], args[1]); + break; + case 3: + callback(args[0], args[1], args[2]); + break; + default: + callback.apply(undefined2, args); + break; + } + } + function runIfPresent(handle) { + if (currentlyRunningATask) { + setTimeout(runIfPresent, 0, handle); + } else { + var task = tasksByHandle[handle]; + if (task) { + currentlyRunningATask = true; + try { + run(task); + } finally { + clearImmediate(handle); + currentlyRunningATask = false; + } + } + } + } + function installNextTickImplementation() { + registerImmediate = function(handle) { + process.nextTick(function() { + runIfPresent(handle); + }); + }; + } + function canUsePostMessage() { + if (global2.postMessage && !global2.importScripts) { + var postMessageIsAsynchronous = true; + var oldOnMessage = global2.onmessage; + global2.onmessage = function() { + postMessageIsAsynchronous = false; + }; + global2.postMessage("", "*"); + global2.onmessage = oldOnMessage; + return postMessageIsAsynchronous; + } + } + function installPostMessageImplementation() { + var messagePrefix = "setImmediate$" + Math.random() + "$"; + var onGlobalMessage = function(event) { + if (event.source === global2 && typeof event.data === "string" && event.data.indexOf(messagePrefix) === 0) { + runIfPresent(+event.data.slice(messagePrefix.length)); + } + }; + if (global2.addEventListener) { + global2.addEventListener("message", onGlobalMessage, false); + } else { + global2.attachEvent("onmessage", onGlobalMessage); + } + registerImmediate = function(handle) { + global2.postMessage(messagePrefix + handle, "*"); + }; + } + function installMessageChannelImplementation() { + var channel = new MessageChannel; + channel.port1.onmessage = function(event) { + var handle = event.data; + runIfPresent(handle); + }; + registerImmediate = function(handle) { + channel.port2.postMessage(handle); + }; + } + function installReadyStateChangeImplementation() { + var html = doc.documentElement; + registerImmediate = function(handle) { + var script = doc.createElement("script"); + script.onreadystatechange = function() { + runIfPresent(handle); + script.onreadystatechange = null; + html.removeChild(script); + script = null; + }; + html.appendChild(script); + }; + } + function installSetTimeoutImplementation() { + registerImmediate = function(handle) { + setTimeout(runIfPresent, 0, handle); + }; + } + var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global2); + attachTo = attachTo && attachTo.setTimeout ? attachTo : global2; + if ({}.toString.call(global2.process) === "[object process]") { + installNextTickImplementation(); + } else if (canUsePostMessage()) { + installPostMessageImplementation(); + } else if (global2.MessageChannel) { + installMessageChannelImplementation(); + } else if (doc && "onreadystatechange" in doc.createElement("script")) { + installReadyStateChangeImplementation(); + } else { + installSetTimeoutImplementation(); + } + attachTo.setImmediate = setImmediate2; + attachTo.clearImmediate = clearImmediate; + })(typeof self === "undefined" ? typeof global === "undefined" ? exports : global : self); + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/utils.js + var require_utils = __commonJS((exports, module) => { + module.exports = function(_uniq, _uniqWith, _partialRight) { + var module2 = {}; + var wildLeven = require_wildcardLeven(); + var leven = require_leven(); + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + function validate(str) { + if ((typeof str === "string" || str instanceof String) && str.length > 0) + return true; + else + return false; + } + module2.validate = validate; + module2.process_and_sort = function process_and_sort(str) { + if (!validate(str)) + return ""; + return str.match(/\S+/g).sort().join(" ").trim(); + }; + module2.tokenize = function unique_tokens(str, options) { + if (options && options.wildcards && _uniqWith && _partialRight) { + var partWild = _partialRight(wildLeven, options, leven); + var wildCompare = function(a, b) { + return partWild(a, b) === 0; + }; + return _uniqWith(str.match(/\S+/g), wildCompare); + } else + return _uniq(str.match(/\S+/g)); + }; + const alphaNumUnicode = /[^\p{L}\p{N}]/gu; + module2.full_process = function full_process(str, options) { + if (!(str instanceof String) && typeof str !== "string") + return ""; + var processedtext; + if (options && typeof options === "object" && options.wildcards && typeof options.wildcards === "string" && options.wildcards.length > 0) { + var wildcards = options.wildcards.toLowerCase(); + str = str.toLowerCase(); + if (options.force_ascii) { + var pattern = "[^\x00 -|" + escapeRegExp(wildcards) + "]"; + str = str.replace(new RegExp(pattern, "g"), ""); + var wildpattern = "[" + escapeRegExp(wildcards) + "]"; + var wildchar = wildcards[0]; + str = str.replace(new RegExp(wildpattern, "g"), wildchar); + var alphanumPat = "[^A-Za-z0-9" + escapeRegExp(wildcards) + "]"; + str = str.replace(new RegExp(alphanumPat, "g"), " "); + str = str.replace(/_/g, " "); + processedtext = str.trim(); + } else { + var upattern = "[^\\p{L}\\p{N}|" + escapeRegExp(wildcards) + "]"; + str = str.replace(new RegExp(upattern, "gu"), " "); + var wildpattern = "[" + escapeRegExp(wildcards) + "]"; + var wildchar = wildcards[0]; + str = str.replace(new RegExp(wildpattern, "g"), wildchar); + processedtext = str.trim(); + } + } else { + if (options && (options.force_ascii || options === true)) { + str = str.replace(/[^\x00-\x7F]/g, ""); + processedtext = str.replace(/\W|_/g, " ").toLowerCase().trim(); + } else { + processedtext = str.replace(alphaNumUnicode, " ").toLowerCase().trim(); + } + } + if (options && options.collapseWhitespace) { + processedtext = processedtext.replace(/\s+/g, " "); + } + return processedtext; + }; + module2.clone_and_set_option_defaults = function(options) { + if (options && options.isAClone) + return options; + var optclone = { isAClone: true }; + if (options) { + var i, keys = Object.keys(options); + for (i = 0;i < keys.length; i++) { + optclone[keys[i]] = options[keys[i]]; + } + } + if (!(optclone.full_process === false)) + optclone.full_process = true; + if (!(optclone.force_ascii === true)) + optclone.force_ascii = false; + if (!(optclone.normalize === false) && optclone.astral === true) { + optclone.normalize = true; + } + if (!(optclone.collapseWhitespace === false)) + optclone.collapseWhitespace = true; + return optclone; + }; + module2.isCustomFunc = function(func) { + if (typeof func === "function" && (func.name === "token_set_ratio" || func.name === "partial_token_set_ratio" || func.name === "token_sort_ratio" || func.name === "partial_token_sort_ratio" || func.name === "QRatio" || func.name === "WRatio" || func.name === "distance" || func.name === "partial_ratio")) { + return false; + } else { + return true; + } + }; + return module2; + }; + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/process.js + var require_process = __commonJS((exports, module) => { + module.exports = function(_clone_and_set_option_defaults, _isArray, QRatio, extract) { + module = {}; + module.dedupe = function dedupe(contains_dupes, options_p) { + var options = _clone_and_set_option_defaults(options_p); + if (!(_isArray(contains_dupes) || typeof contains_dupes === "object")) { + throw new Error("contains_dupes must be an array or object"); + return; + } + if (Object.keys(contains_dupes).length === 0) { + if (typeof console !== undefined) + console.warn("contains_dupes is empty"); + return []; + } + if (options.limit) { + if (typeof console !== undefined) + console.warn("options.limit will be ignored in dedupe"); + options.limit = 0; + } + if (!options.cutoff || typeof options.cutoff !== "number") { + if (typeof console !== undefined) + console.warn("Using default cutoff of 70"); + options.cutoff = 70; + } + if (!options.scorer) { + options.scorer = QRatio; + if (typeof console !== undefined) + console.log("Using default scorer 'ratio' for dedupe"); + } + var processor; + if (options.processor && typeof options.processor === "function") { + processor = options.processor; + } else + processor = function(x) { + return x; + }; + var uniqueItems = {}; + for (var i in contains_dupes) { + var item = processor(contains_dupes[i]); + if (typeof item !== "string" && item instanceof String === false) { + throw new Error("Each processed item in dedupe must be a string."); + } + var matches = extract(item, contains_dupes, options); + if (options.returnObjects) { + if (matches.length === 1) { + if (options.keepmap) + uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key, matches }; + else + uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key }; + } else { + matches = matches.sort(function(a, b) { + var pa = processor(a.choice); + var pb = processor(b.choice); + var aLen = pa.length; + var bLen = pb.length; + if (aLen === bLen) { + if (pa < pb) + return -1; + else + return 1; + } else + return bLen - aLen; + }); + if (options.keepmap) + uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key, matches }; + else + uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key }; + } + } else { + if (matches.length === 1) { + if (options.keepmap) + uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2], matches]; + else + uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2]]; + } else { + matches = matches.sort(function(a, b) { + var pa = processor(a[0]); + var pb = processor(b[0]); + var aLen = pa.length; + var bLen = pb.length; + if (aLen === bLen) { + if (pa < pb) + return -1; + else + return 1; + } else + return bLen - aLen; + }); + if (options.keepmap) + uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2], matches]; + else + uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2]]; + } + } + } + var uniqueVals = []; + for (var u in uniqueItems) { + uniqueVals.push(uniqueItems[u]); + } + return uniqueVals; + }; + return module; + }; + }); + + // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/fuzzball.js + var require_fuzzball = __commonJS((exports, module) => { + (function() { + var SequenceMatcher = require_fbdifflib(); + var Heap = require_heap2(); + var orderBy = require_orderBy(); + var nativeUtils = require_native_utils(); + var _intersect = nativeUtils._intersect; + var _intersectWith = nativeUtils._intersectWith; + var _difference = nativeUtils._difference; + var _differenceWith = nativeUtils._differenceWith; + var _uniq = nativeUtils._uniq; + var _uniqWith = nativeUtils._uniqWith; + var _partialRight = nativeUtils._partialRight; + var _forEach = nativeUtils._forEach; + var _isArray = nativeUtils._isArray; + var iLeven = require_iLeven(); + var wildleven = require_wildcardLeven(); + var leven = require_leven(); + if (typeof setImmediate !== "function") { + require_setImmediate(); + } + var utils = require_utils()(_uniq, _uniqWith, _partialRight); + var validate = utils.validate; + var process_and_sort = utils.process_and_sort; + var tokenize = utils.tokenize; + var full_process = utils.full_process; + var clone_and_set_option_defaults = utils.clone_and_set_option_defaults; + var isCustomFunc = utils.isCustomFunc; + var processing = require_process()(clone_and_set_option_defaults, _isArray, QRatio, extract); + var dedupe = processing.dedupe; + function distance(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (typeof options.subcost === "undefined") + options.subcost = 1; + if (options.astral) + return iLeven(str1, str2, options); + else + return wildleven(str1, str2, options, leven); + } + function QRatio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + return _ratio(str1, str2, options); + } + function partial_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + return _partial_ratio(str1, str2, options); + } + function token_set_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + return _token_set(str1, str2, options); + } + function partial_token_set_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + options.partial = true; + return _token_set(str1, str2, options); + } + function token_sort_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + if (!options.proc_sorted) { + str1 = process_and_sort(str1); + str2 = process_and_sort(str2); + } + return _ratio(str1, str2, options); + } + function partial_token_sort_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + options.partial = true; + if (!options.proc_sorted) { + str1 = process_and_sort(str1); + str2 = process_and_sort(str2); + } + return _partial_ratio(str1, str2, options); + } + function token_similarity_sort_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + return _token_similarity_sort_ratio(str1, str2, options); + } + function partial_token_similarity_sort_ratio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + options.partial = true; + return _token_similarity_sort_ratio(str1, str2, options); + } + function WRatio(str1, str2, options_p) { + var options = clone_and_set_option_defaults(options_p); + str1 = options.normalize ? str1.normalize() : str1; + str2 = options.normalize ? str2.normalize() : str2; + str1 = options.full_process ? full_process(str1, options) : str1; + str2 = options.full_process ? full_process(str2, options) : str2; + options.full_process = false; + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + var try_partial = true; + var unbase_scale = 0.95; + var partial_scale = 0.9; + var base = _ratio(str1, str2, options); + var len_ratio = Math.max(str1.length, str2.length) / Math.min(str1.length, str2.length); + if (len_ratio < 1.5) + try_partial = false; + if (len_ratio > 8) + partial_scale = 0.6; + if (try_partial) { + var partial = _partial_ratio(str1, str2, options) * partial_scale; + var ptsor = partial_token_sort_ratio(str1, str2, options) * unbase_scale * partial_scale; + var ptser = partial_token_set_ratio(str1, str2, options) * unbase_scale * partial_scale; + return Math.round(Math.max(base, partial, ptsor, ptser)); + } else { + var tsor = token_sort_ratio(str1, str2, options) * unbase_scale; + var tser = token_set_ratio(str1, str2, options) * unbase_scale; + return Math.round(Math.max(base, tsor, tser)); + } + } + function extract(query, choices, options_p) { + var options = clone_and_set_option_defaults(options_p); + var numchoices; + if (_isArray(choices)) { + numchoices = choices.length; + } else if (!(choices instanceof Object)) { + throw new Error("Invalid choices"); + } else + numchoices = Object.keys(choices).length; + if (!choices || numchoices === 0) { + if (typeof console !== undefined) + console.warn("No choices"); + return []; + } + if (options.processor && typeof options.processor !== "function") { + throw new Error("Invalid Processor"); + } + if (!options.processor) + options.processor = function(x) { + return x; + }; + if (options.scorer && typeof options.scorer !== "function") { + throw new Error("Invalid Scorer"); + } + if (!options.scorer) { + options.scorer = QRatio; + } + var isCustom = isCustomFunc(options.scorer); + if (!options.cutoff || typeof options.cutoff !== "number") { + options.cutoff = -1; + } + var pre_processor = function(choice, force_ascii) { + return choice; + }; + if (options.full_process) { + pre_processor = full_process; + if (!isCustom) + options.processed = true; + } + var normalize = false; + if (!isCustom) { + if (options.astral && options.normalize) { + options.normalize = false; + if (String.prototype.normalize) { + normalize = true; + query = query.normalize(); + } else { + if (typeof console !== undefined) + console.warn("Normalization not supported in your environment"); + } + } + query = pre_processor(query, options); + options.full_process = false; + if (query.length === 0) { + if (typeof console !== undefined) + console.warn("Processed query is empty string"); + } + } + var results = []; + var anyblank = false; + var tsort = false; + var tset = false; + if (options.scorer.name === "token_sort_ratio" || options.scorer.name === "partial_token_sort_ratio") { + var proc_sorted_query = process_and_sort(query); + tsort = true; + } else if (options.scorer.name === "token_set_ratio" || options.scorer.name === "partial_token_set_ratio") { + var query_tokens = tokenize(query, options); + tset = true; + } + var result, mychoice, cmpHeap, cmpSort; + if (options.returnObjects) { + cmpHeap = function(a, b) { + return a.score - b.score; + }; + cmpSort = function(a, b) { + return b.score - a.score; + }; + } else { + cmpHeap = function(a, b) { + return a[1] - b[1]; + }; + cmpSort = function(a, b) { + return b[1] - a[1]; + }; + } + _forEach(choices, function(value, key) { + options.tokens = undefined; + options.proc_sorted = false; + if (tsort) { + options.proc_sorted = true; + if (value && value.proc_sorted) + mychoice = value.proc_sorted; + else { + mychoice = pre_processor(normalize ? options.processor(value).normalize() : options.processor(value), options); + mychoice = process_and_sort(mychoice); + } + result = options.scorer(proc_sorted_query, mychoice, options); + } else if (tset) { + mychoice = "x"; + if (value && value.tokens) { + options.tokens = [query_tokens, value.tokens]; + if (options.trySimple) + mychoice = pre_processor(options.processor(value), options); + } else { + mychoice = pre_processor(normalize ? options.processor(value).normalize() : options.processor(value), options); + options.tokens = [query_tokens, tokenize(mychoice, options)]; + } + result = options.scorer(query, mychoice, options); + } else if (isCustom) { + mychoice = options.processor(value); + result = options.scorer(query, mychoice, options); + } else { + mychoice = pre_processor(normalize ? options.processor(value).normalize() : options.processor(value), options); + if (typeof mychoice !== "string" || mychoice.length === 0) + anyblank = true; + result = options.scorer(query, mychoice, options); + } + if (result > options.cutoff) { + if (options.returnObjects) + results.push({ choice: value, score: result, key }); + else + results.push([value, result, key]); + } + }); + if (anyblank) { + if (typeof console !== undefined) + console.log("One or more choices were empty. (post-processing if applied)"); + } + if (options.limit && typeof options.limit === "number" && options.limit > 0 && options.limit < numchoices && !options.unsorted) { + results = Heap.nlargest(results, options.limit, cmpHeap); + } else if (!options.unsorted) { + results = results.sort(cmpSort); + } + return results; + } + function extractAsync(query, choices, options_p, callback) { + var options = clone_and_set_option_defaults(options_p); + var abortController; + if (typeof options_p.abortController === "object") { + abortController = options_p.abortController; + } + var cancelToken; + if (typeof options_p.cancelToken === "object") { + cancelToken = options_p.cancelToken; + } + var loopOffset = 256; + if (typeof options.asyncLoopOffset === "number") { + if (options.asyncLoopOffset < 1) + loopOffset = 1; + else + loopOffset = options.asyncLoopOffset; + } + var isArray = false; + var numchoices; + if (choices && choices.length && _isArray(choices)) { + numchoices = choices.length; + isArray = true; + } else if (!(choices instanceof Object)) { + callback(new Error("Invalid choices")); + return; + } else + numchoices = Object.keys(choices).length; + if (!choices || numchoices === 0) { + if (typeof console !== undefined) + console.warn("No choices"); + callback(null, []); + return; + } + if (options.processor && typeof options.processor !== "function") { + callback(new Error("Invalid Processor")); + return; + } + if (!options.processor) + options.processor = function(x) { + return x; + }; + if (options.scorer && typeof options.scorer !== "function") { + callback(new Error("Invalid Scorer")); + return; + } + if (!options.scorer) { + options.scorer = QRatio; + } + var isCustom = isCustomFunc(options.scorer); + if (!options.cutoff || typeof options.cutoff !== "number") { + options.cutoff = -1; + } + var pre_processor = function(choice, force_ascii) { + return choice; + }; + if (options.full_process) { + pre_processor = full_process; + if (!isCustom) + options.processed = true; + } + var normalize = false; + if (!isCustom) { + if (options.astral && options.normalize) { + options.normalize = false; + if (String.prototype.normalize) { + normalize = true; + query = query.normalize(); + } else { + if (typeof console !== undefined) + console.warn("Normalization not supported in your environment"); + } + } + query = pre_processor(query, options); + options.full_process = false; + if (query.length === 0) { + if (typeof console !== undefined) + console.warn("Processed query is empty string"); + } + } + var results = []; + var anyblank = false; + var tsort = false; + var tset = false; + if (options.scorer.name === "token_sort_ratio" || options.scorer.name === "partial_token_sort_ratio") { + var proc_sorted_query = process_and_sort(query); + tsort = true; + } else if (options.scorer.name === "token_set_ratio" || options.scorer.name === "partial_token_set_ratio") { + var query_tokens = tokenize(query, options); + tset = true; + } + var idx, mychoice, result, cmpHeap, cmpSort; + if (options.returnObjects) { + cmpHeap = function(a, b) { + return a.score - b.score; + }; + cmpSort = function(a, b) { + return b.score - a.score; + }; + } else { + cmpHeap = function(a, b) { + return a[1] - b[1]; + }; + cmpSort = function(a, b) { + return b[1] - a[1]; + }; + } + var keys = Object.keys(choices); + isArray ? searchLoop(0) : searchLoop(keys[0], 0); + function searchLoop(c, i) { + if (isArray || choices.hasOwnProperty(c)) { + options.tokens = undefined; + options.proc_sorted = false; + if (tsort) { + options.proc_sorted = true; + if (choices[c] && choices[c].proc_sorted) + mychoice = choices[c].proc_sorted; + else { + mychoice = pre_processor(normalize ? options.processor(choices[c]).normalize() : options.processor(choices[c]), options); + mychoice = process_and_sort(mychoice); + } + result = options.scorer(proc_sorted_query, mychoice, options); + } else if (tset) { + mychoice = "x"; + if (choices[c] && choices[c].tokens) { + options.tokens = [query_tokens, choices[c].tokens]; + if (options.trySimple) + mychoice = pre_processor(options.processor(choices[c]), options); + } else { + mychoice = pre_processor(normalize ? options.processor(choices[c]).normalize() : options.processor(choices[c]), options); + options.tokens = [query_tokens, tokenize(mychoice, options)]; + } + result = options.scorer(query, mychoice, options); + } else if (isCustom) { + mychoice = options.processor(choices[c]); + result = options.scorer(query, mychoice, options); + } else { + mychoice = pre_processor(normalize ? options.processor(choices[c]).normalize() : options.processor(choices[c]), options); + if (typeof mychoice !== "string" || mychoice.length === 0) + anyblank = true; + result = options.scorer(query, mychoice, options); + } + if (isArray) + idx = parseInt(c); + else + idx = c; + if (result > options.cutoff) { + if (options.returnObjects) + results.push({ choice: choices[c], score: result, key: idx }); + else + results.push([choices[c], result, idx]); + } + } + if (abortController && abortController.signal.aborted === true) { + callback(new Error("aborted")); + return; + } + if (cancelToken && cancelToken.canceled === true) { + callback(new Error("canceled")); + return; + } + if (isArray && c < choices.length - 1) { + if (c % loopOffset === 0) { + setImmediate(function() { + searchLoop(c + 1); + }); + } else { + searchLoop(c + 1); + } + } else if (i < keys.length - 1) { + if (i % loopOffset === 0) { + setImmediate(function() { + searchLoop(keys[i + 1], i + 1); + }); + } else { + searchLoop(keys[i + 1], i + 1); + } + } else { + if (anyblank) { + if (typeof console !== undefined) + console.log("One or more choices were empty. (post-processing if applied)"); + } + if (options.limit && typeof options.limit === "number" && options.limit > 0 && options.limit < numchoices && !options.unsorted) { + results = Heap.nlargest(results, options.limit, cmpHeap); + } else if (!options.unsorted) { + results = results.sort(cmpSort); + } + callback(null, results); + } + } + } + function _cosineSim(v1, v2, options) { + var keysV1 = Object.keys(v1); + var keysV2 = Object.keys(v2); + var intersection = _intersect(keysV1, keysV2); + var prods = intersection.map(function(x) { + return v1[x] * v2[x]; + }); + var numerator = prods.reduce(function(acc, x) { + return acc + x; + }, 0); + var v1Prods = keysV1.map(function(x) { + return Math.pow(v1[x], 2); + }); + var v1sum = v1Prods.reduce(function(acc, x) { + return acc + x; + }, 0); + var v2Prods = keysV2.map(function(x) { + return Math.pow(v2[x], 2); + }); + var v2sum = v2Prods.reduce(function(acc, x) { + return acc + x; + }, 0); + var denominator = Math.sqrt(v1sum) * Math.sqrt(v2sum); + return numerator / denominator; + } + var WILDCARD_KEY = "%*SuperUniqueWildcardKey*%"; + function _getCharacterCounts(str, options) { + var normalString = str; + if (options.astral) { + var charArray = Array.from(normalString); + } else { + var charArray = normalString.split(""); + } + var charCounts = {}; + if (options.wildcards) { + for (var i = 0;i < charArray.length; i++) { + var char = charArray[i]; + if (options.wildcards.indexOf(char) > -1) { + if (charCounts[WILDCARD_KEY]) { + charCounts[WILDCARD_KEY] += 1; + } else { + charCounts[WILDCARD_KEY] = 1; + } + } else if (charCounts[char]) { + charCounts[char] += 1; + } else { + charCounts[char] = 1; + } + } + } else { + for (var i = 0;i < charArray.length; i++) { + var char = charArray[i]; + if (charCounts[char]) { + charCounts[char] += 1; + } else { + charCounts[char] = 1; + } + } + } + return charCounts; + } + function _token_similarity_sort(sorted1, sorted2, options) { + var oldSorted2 = sorted2; + var charCounts1 = sorted1.reduce(function(acc, str) { + acc[str] = _getCharacterCounts(str, options); + return acc; + }, {}); + var charCounts2 = oldSorted2.reduce(function(acc, str) { + acc[str] = _getCharacterCounts(str, options); + return acc; + }, {}); + var newSorted2 = []; + var i = 0; + while (oldSorted2.length && i < sorted1.length) { + var sim = orderBy(oldSorted2, function(x) { + return _cosineSim(charCounts1[sorted1[i]], charCounts2[x]); + }, "desc")[0]; + newSorted2.push(sim); + i++; + oldSorted2 = oldSorted2.filter(function(token) { + return token !== sim; + }); + } + return newSorted2.concat(oldSorted2); + } + function _order_token_lists(str1, tokens1, str2, tokens2) { + var first = tokens1; + var second = tokens2; + if (tokens1.length > tokens2.length) { + first = tokens2; + second = tokens1; + } else if (tokens1.length === tokens2.length) { + if (str1.length > str2.length) { + first = tokens2; + second = tokens1; + } else { + var sortedStrings = [str1, str2].sort(); + if (sortedStrings[0] === str2) { + first = tokens2; + second = tokens1; + } + } + } + return [first, second]; + } + function _token_similarity_sort_ratio(str1, str2, options) { + if (!options.tokens) { + var tokens1 = tokenize(str1, options); + var tokens2 = tokenize(str2, options); + } else { + var tokens1 = options.tokens[0]; + var tokens2 = options.tokens[1]; + } + var sorted1 = tokens1.sort(); + var sorted2 = tokens2.sort(); + var orderedTokenLists = _order_token_lists(str1, sorted1, str2, sorted2); + var first = orderedTokenLists[0]; + var second = orderedTokenLists[1]; + const newSecond = _token_similarity_sort(first, second, options); + if (!options.partial) { + return _ratio(first.join(" "), newSecond.join(" "), options); + } else { + return _partial_ratio(first.join(" "), newSecond.join(" "), options); + } + } + function _token_set(str1, str2, options) { + if (!options.tokens) { + var tokens1 = tokenize(str1, options); + var tokens2 = tokenize(str2, options); + } else { + var tokens1 = options.tokens[0]; + var tokens2 = options.tokens[1]; + } + if (options.wildcards) { + var partWild = _partialRight(wildleven, options, leven); + var wildCompare = function(a, b) { + return partWild(a, b) === 0; + }; + var intersection = _intersectWith(tokens1, tokens2, wildCompare); + var diff1to2 = _differenceWith(tokens1, tokens2, wildCompare); + var diff2to1 = _differenceWith(tokens2, tokens1, wildCompare); + } else { + var intersection = _intersect(tokens1, tokens2); + var diff1to2 = _difference(tokens1, tokens2); + var diff2to1 = _difference(tokens2, tokens1); + } + var sorted_sect = intersection.sort().join(" "); + var sorted_1to2List = diff1to2.sort(); + var sorted_2to1List = diff2to1.sort(); + if (options.sortBySimilarity) { + var orderedTokenLists = _order_token_lists(str1, sorted_1to2List, str2, sorted_2to1List); + var first = orderedTokenLists[0]; + var second = orderedTokenLists[1]; + var sorted_1to2 = first.join(" "); + var sorted_2to1 = _token_similarity_sort(first, second, options).join(" "); + } else { + var sorted_1to2 = sorted_1to2List.join(" "); + var sorted_2to1 = sorted_2to1List.join(" "); + } + var combined_1to2 = sorted_sect + " " + sorted_1to2; + var combined_2to1 = sorted_sect + " " + sorted_2to1; + sorted_sect = sorted_sect.trim(); + combined_1to2 = combined_1to2.trim(); + combined_2to1 = combined_2to1.trim(); + var ratio_func = _ratio; + if (options.partial) { + ratio_func = _partial_ratio; + if (sorted_sect.length > 0) + return 100; + } + var pairwise = [ + ratio_func(sorted_sect, combined_1to2, options), + ratio_func(sorted_sect, combined_2to1, options), + ratio_func(combined_1to2, combined_2to1, options) + ]; + if (options.trySimple) { + pairwise.push(ratio_func(str1, str2, options)); + } + return Math.max.apply(null, pairwise); + } + function _ratio(str1, str2, options) { + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + if (options.ratio_alg && options.ratio_alg === "difflib") { + var m = new SequenceMatcher(null, str1, str2, options.autojunk); + var r = m.ratio(); + return Math.round(100 * r); + } + if (typeof options.subcost === "undefined") + options.subcost = 2; + var levdistance, lensum; + if (options.astral) { + levdistance = iLeven(str1, str2, options); + lensum = Array.from(str1).length + Array.from(str2).length; + } else { + if (!options.wildcards) { + levdistance = leven(str1, str2, options); + lensum = str1.length + str2.length; + } else { + levdistance = wildleven(str1, str2, options, leven); + lensum = str1.length + str2.length; + } + } + return Math.round(100 * ((lensum - levdistance) / lensum)); + } + function _partial_ratio(str1, str2, options) { + if (!validate(str1)) + return 0; + if (!validate(str2)) + return 0; + if (str1.length <= str2.length) { + var shorter = str1; + var longer = str2; + } else { + var shorter = str2; + var longer = str1; + } + var m = new SequenceMatcher(null, shorter, longer, options.autojunk); + var blocks = m.getMatchingBlocks(); + var scores = []; + for (var b = 0;b < blocks.length; b++) { + var long_start = blocks[b][1] - blocks[b][0] > 0 ? blocks[b][1] - blocks[b][0] : 0; + var long_end = long_start + shorter.length; + var long_substr = longer.substring(long_start, long_end); + var r = _ratio(shorter, long_substr, options); + if (r > 99.5) + return 100; + else + scores.push(r); + } + return Math.max.apply(null, scores); + } + if (!Object.keys) { + Object.keys = function() { + var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !{ toString: null }.propertyIsEnumerable("toString"), dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], dontEnumsLength = dontEnums.length; + return function(obj) { + if (typeof obj !== "object" && (typeof obj !== "function" || obj === null)) { + throw new TypeError("Object.keys called on non-object"); + } + var result = [], prop, i; + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + if (hasDontEnumBug) { + for (i = 0;i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }(); + } + var extractAsPromised = undefined; + if (typeof Promise !== "undefined") { + extractAsPromised = function(query, choices, options) { + return new Promise(function(resolve, reject) { + extractAsync(query, choices, options, function(err, response) { + if (err) + reject(err); + else + resolve(response); + }); + }); + }; + } + var fuzzball = { + distance, + ratio: QRatio, + partial_ratio, + token_set_ratio, + token_sort_ratio, + partial_token_set_ratio, + partial_token_sort_ratio, + token_similarity_sort_ratio, + partial_token_similarity_sort_ratio, + WRatio, + full_process, + extract, + extractAsync, + extractAsPromised, + process_and_sort, + unique_tokens: tokenize, + dedupe + }; + module.exports = fuzzball; + })(); + }); + + // node_modules/.bun/country-locale-map@1.9.11/node_modules/country-locale-map/countries-intl.json + var require_countries_intl = __commonJS((exports, module) => { + module.exports = [{ name: "Afghanistan", alpha2: "AF", alpha3: "AFG", numeric: "004", locales: ["ps-AF", "fa-AF", "uz-Arab-AF"], default_locale: "ps-AF", currency: "AFN", latitude: "33.93911", longitude: "67.709953", currency_name: "Afghani", languages: ["ps", "uz", "tk"], capital: "Kabul", emoji: "🇦🇫", emojiU: "U+1F1E6 U+1F1EB", fips: "AF", internet: "AF", continent: "Asia", region: "South Asia" }, { name: "Albania", alpha2: "AL", alpha3: "ALB", numeric: "008", locales: ["sq-AL"], default_locale: "sq-AL", currency: "ALL", latitude: "41.153332", longitude: "20.168331", currency_name: "Lek", languages: ["sq"], capital: "Tirana", emoji: "🇦🇱", emojiU: "U+1F1E6 U+1F1F1", fips: "AL", internet: "AL", continent: "Europe", region: "South East Europe" }, { name: "Algeria", alpha2: "DZ", alpha3: "DZA", numeric: "012", locales: ["ar-DZ", "kab-DZ"], default_locale: "ar-DZ", currency: "DZD", latitude: "28.033886", longitude: "1.659626", currency_name: "Algerian Dinar", languages: ["ar"], capital: "Algiers", emoji: "🇩🇿", emojiU: "U+1F1E9 U+1F1FF", fips: "AG", internet: "DZ", continent: "Africa", region: "Northern Africa" }, { name: "American Samoa", alpha2: "AS", alpha3: "ASM", numeric: "016", locales: ["en-AS"], default_locale: "en-AS", currency: "USD", latitude: "-14.270972", longitude: "-170.132217", currency_name: "US Dollar", languages: ["en", "sm"], capital: "Pago Pago", emoji: "🇦🇸", emojiU: "U+1F1E6 U+1F1F8", fips: "AQ", internet: "AS", continent: "Oceania", region: "Pacific" }, { name: "Andorra", alpha2: "AD", alpha3: "AND", numeric: "020", locales: ["ca"], default_locale: "ca", currency: "EUR", latitude: "42.546245", longitude: "1.601554", currency_name: "Euro", languages: ["ca"], capital: "Andorra la Vella", emoji: "🇦🇩", emojiU: "U+1F1E6 U+1F1E9", fips: "AN", internet: "AD", continent: "Europe", region: "South West Europe" }, { name: "Angola", alpha2: "AO", alpha3: "AGO", numeric: "024", locales: ["pt"], default_locale: "pt", currency: "AOA", latitude: "-11.202692", longitude: "17.873887", currency_name: "Kwanza", languages: ["pt"], capital: "Luanda", emoji: "🇦🇴", emojiU: "U+1F1E6 U+1F1F4", fips: "AO", internet: "AO", continent: "Africa", region: "Southern Africa" }, { name: "Anguilla", alpha2: "AI", alpha3: "AIA", numeric: "660", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "18.220554", longitude: "-63.068615", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "The Valley", emoji: "🇦🇮", emojiU: "U+1F1E6 U+1F1EE", fips: "AV", internet: "AI", continent: "Americas", region: "West Indies" }, { name: "Antarctica", alpha2: "AQ", alpha3: "ATA", numeric: "010", locales: ["en-US"], default_locale: "en-US", currency: "USD", latitude: "-75.250973", longitude: "-0.071389", currency_name: "US Dollar", languages: [], capital: "", emoji: "🇦🇶", emojiU: "U+1F1E6 U+1F1F6", fips: "AY", internet: "AQ", continent: "Antarctica", region: "Antarctica" }, { name: "Antigua and Barbuda", alpha2: "AG", alpha3: "ATG", numeric: "028", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.060816", longitude: "-61.796428", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Saint John's", emoji: "🇦🇬", emojiU: "U+1F1E6 U+1F1EC", fips: "AC", internet: "AG", continent: "Americas", region: "West Indies" }, { name: "Argentina", alpha2: "AR", alpha3: "ARG", numeric: "032", locales: ["es-AR"], default_locale: "es-AR", currency: "ARS", latitude: "-38.416097", longitude: "-63.616672", currency_name: "Argentine Peso", languages: ["es", "gn"], capital: "Buenos Aires", emoji: "🇦🇷", emojiU: "U+1F1E6 U+1F1F7", fips: "AR", internet: "AR", continent: "Americas", region: "South America" }, { name: "Armenia", alpha2: "AM", alpha3: "ARM", numeric: "051", locales: ["hy-AM"], default_locale: "hy-AM", currency: "AMD", latitude: "40.069099", longitude: "45.038189", currency_name: "Armenian Dram", languages: ["hy", "ru"], capital: "Yerevan", emoji: "🇦🇲", emojiU: "U+1F1E6 U+1F1F2", fips: "AM", internet: "AM", continent: "Asia", region: "South West Asia" }, { name: "Aruba", alpha2: "AW", alpha3: "ABW", numeric: "533", locales: ["nl"], default_locale: "nl", currency: "AWG", latitude: "12.52111", longitude: "-69.968338", currency_name: "Aruban Florin", languages: ["nl", "pa"], capital: "Oranjestad", emoji: "🇦🇼", emojiU: "U+1F1E6 U+1F1FC", fips: "AA", internet: "AW", continent: "Americas", region: "West Indies" }, { name: "Australia", alpha2: "AU", alpha3: "AUS", numeric: "036", locales: ["en-AU"], default_locale: "en-AU", currency: "AUD", latitude: "-25.274398", longitude: "133.775136", currency_name: "Australian Dollar", languages: ["en"], capital: "Canberra", emoji: "🇦🇺", emojiU: "U+1F1E6 U+1F1FA", fips: "AS", internet: "AU", continent: "Oceania", region: "Pacific" }, { name: "Austria", alpha2: "AT", alpha3: "AUT", numeric: "040", locales: ["de-AT"], default_locale: "de-AT", currency: "EUR", latitude: "47.516231", longitude: "14.550072", currency_name: "Euro", languages: ["de"], capital: "Vienna", emoji: "🇦🇹", emojiU: "U+1F1E6 U+1F1F9", fips: "AU", internet: "AT", continent: "Europe", region: "Central Europe" }, { name: "Azerbaijan", alpha2: "AZ", alpha3: "AZE", numeric: "031", locales: ["az-Cyrl-AZ", "az-Latn-AZ"], default_locale: "az-Cyrl-AZ", currency: "AZN", latitude: "40.143105", longitude: "47.576927", currency_name: "Azerbaijan Manat", languages: ["az"], capital: "Baku", emoji: "🇦🇿", emojiU: "U+1F1E6 U+1F1FF", fips: "AJ", internet: "AZ", continent: "Asia", region: "South West Asia" }, { name: "Bahamas", alpha2: "BS", alpha3: "BHS", numeric: "044", locales: ["en"], default_locale: "en", currency: "BSD", latitude: "25.03428", longitude: "-77.39628", currency_name: "Bahamian Dollar", languages: ["en"], capital: "Nassau", emoji: "🇧🇸", emojiU: "U+1F1E7 U+1F1F8", fips: "BF", internet: "BS", continent: "Americas", region: "West Indies" }, { name: "Bahrain", alpha2: "BH", alpha3: "BHR", numeric: "048", locales: ["ar-BH"], default_locale: "ar-BH", currency: "BHD", latitude: "25.930414", longitude: "50.637772", currency_name: "Bahraini Dinar", languages: ["ar"], capital: "Manama", emoji: "🇧🇭", emojiU: "U+1F1E7 U+1F1ED", fips: "BA", internet: "BH", continent: "Asia", region: "South West Asia" }, { name: "Bangladesh", alpha2: "BD", alpha3: "BGD", numeric: "050", locales: ["bn-BD"], default_locale: "bn-BD", currency: "BDT", latitude: "23.684994", longitude: "90.356331", currency_name: "Taka", languages: ["bn"], capital: "Dhaka", emoji: "🇧🇩", emojiU: "U+1F1E7 U+1F1E9", fips: "BG", internet: "BD", continent: "Asia", region: "South Asia" }, { name: "Barbados", alpha2: "BB", alpha3: "BRB", numeric: "052", locales: ["en"], default_locale: "en", currency: "BBD", latitude: "13.193887", longitude: "-59.543198", currency_name: "Barbados Dollar", languages: ["en"], capital: "Bridgetown", emoji: "🇧🇧", emojiU: "U+1F1E7 U+1F1E7", fips: "BB", internet: "BB", continent: "Americas", region: "West Indies" }, { name: "Belarus", alpha2: "BY", alpha3: "BLR", numeric: "112", locales: ["be-BY"], default_locale: "be-BY", currency: "BYN", latitude: "53.709807", longitude: "27.953389", currency_name: "Belarusian Ruble", languages: ["be", "ru"], capital: "Minsk", emoji: "🇧🇾", emojiU: "U+1F1E7 U+1F1FE", fips: "BO", internet: "BY", continent: "Europe", region: "Eastern Europe" }, { name: "Belgium", alpha2: "BE", alpha3: "BEL", numeric: "056", locales: ["nl-BE", "en-BE", "fr-BE", "de-BE"], default_locale: "nl-BE", currency: "EUR", latitude: "50.503887", longitude: "4.469936", currency_name: "Euro", languages: ["nl", "fr", "de"], capital: "Brussels", emoji: "🇧🇪", emojiU: "U+1F1E7 U+1F1EA", fips: "BE", internet: "BE", continent: "Europe", region: "Western Europe" }, { name: "Belize", alpha2: "BZ", alpha3: "BLZ", numeric: "084", locales: ["en-BZ"], default_locale: "en-BZ", currency: "BZD", latitude: "17.189877", longitude: "-88.49765", currency_name: "Belize Dollar", languages: ["en", "es"], capital: "Belmopan", emoji: "🇧🇿", emojiU: "U+1F1E7 U+1F1FF", fips: "BH", internet: "BZ", continent: "Americas", region: "Central America" }, { name: "Benin", alpha2: "BJ", alpha3: "BEN", numeric: "204", locales: ["fr-BJ"], default_locale: "fr-BJ", currency: "XOF", latitude: "9.30769", longitude: "2.315834", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Porto-Novo", emoji: "🇧🇯", emojiU: "U+1F1E7 U+1F1EF", fips: "BN", internet: "BJ", continent: "Africa", region: "Western Africa" }, { name: "Bermuda", alpha2: "BM", alpha3: "BMU", numeric: "060", locales: ["en"], default_locale: "en", currency: "BMD", latitude: "32.321384", longitude: "-64.75737", currency_name: "Bermudian Dollar", languages: ["en"], capital: "Hamilton", emoji: "🇧🇲", emojiU: "U+1F1E7 U+1F1F2", fips: "BD", internet: "BM", continent: "Americas", region: "West Indies" }, { name: "Bhutan", alpha2: "BT", alpha3: "BTN", numeric: "064", locales: ["dz"], default_locale: "dz", currency: "BTN", latitude: "27.514162", longitude: "90.433601", currency_name: "Ngultrum", languages: ["dz"], capital: "Thimphu", emoji: "🇧🇹", emojiU: "U+1F1E7 U+1F1F9", fips: "BT", internet: "BT", continent: "Asia", region: "South Asia" }, { name: "Bolivia", alpha2: "BO", alpha3: "BOL", numeric: "068", locales: ["es-BO"], default_locale: "es-BO", currency: "BOB", latitude: "-16.290154", longitude: "-63.588653", currency_name: "Bolivia", languages: ["es", "ay", "qu"], capital: "Sucre", emoji: "🇧🇴", emojiU: "U+1F1E7 U+1F1F4", fips: "BL", internet: "BO", continent: "Americas", region: "South America", alternate_names: ["Plurinational State of Bolivia"] }, { name: "Bonaire", alpha2: "BQ", alpha3: "BES", numeric: "535", locales: ["nl"], default_locale: "nl", currency: "USD", currency_name: "US Dollar", languages: ["nl"], capital: "Kralendijk", emoji: "🇧🇶", emojiU: "U+1F1E7 U+1F1F6", fips: "BQ", internet: "BQ", continent: "Americas", region: "West Indies", alternate_names: ["Bonaire, Sint Eustatius and Saba"] }, { name: "Bosnia and Herzegovina", alpha2: "BA", alpha3: "BIH", numeric: "070", locales: ["bs-BA", "sr-Cyrl-BA", "sr-Latn-BA"], default_locale: "bs-BA", currency: "BAM", latitude: "43.915886", longitude: "17.679076", currency_name: "Convertible Mark", languages: ["bs", "hr", "sr"], capital: "Sarajevo", emoji: "🇧🇦", emojiU: "U+1F1E7 U+1F1E6", fips: "BK", internet: "BA", continent: "Europe", region: "South East Europe" }, { name: "Botswana", alpha2: "BW", alpha3: "BWA", numeric: "072", locales: ["en-BW"], default_locale: "en-BW", currency: "BWP", latitude: "-22.328474", longitude: "24.684866", currency_name: "Pula", languages: ["en", "tn"], capital: "Gaborone", emoji: "🇧🇼", emojiU: "U+1F1E7 U+1F1FC", fips: "BC", internet: "BW", continent: "Africa", region: "Southern Africa" }, { name: "Bouvet Island", alpha2: "BV", alpha3: "BVT", numeric: "074", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "-54.423199", longitude: "3.413194", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "", emoji: "🇧🇻", emojiU: "U+1F1E7 U+1F1FB", fips: "BV", internet: "BV", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "Brazil", alpha2: "BR", alpha3: "BRA", numeric: "076", locales: ["pt-BR"], default_locale: "pt-BR", currency: "BRL", latitude: "-14.235004", longitude: "-51.92528", currency_name: "Brazilian Real", languages: ["pt"], capital: "Brasília", emoji: "🇧🇷", emojiU: "U+1F1E7 U+1F1F7", fips: "BR", internet: "BR", continent: "Americas", region: "South America" }, { name: "British Indian Ocean Territory", alpha2: "IO", alpha3: "IOT", numeric: "086", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-6.343194", longitude: "71.876519", currency_name: "US Dollar", languages: ["en"], capital: "Diego Garcia", emoji: "🇮🇴", emojiU: "U+1F1EE U+1F1F4", fips: "IO", internet: "IO", continent: "Asia", region: "South Asia" }, { name: "Brunei Darussalam", alpha2: "BN", alpha3: "BRN", numeric: "096", locales: ["ms-BN"], default_locale: "ms-BN", currency: "BND", latitude: "4.535277", longitude: "114.727669", currency_name: "Brunei Dollar", languages: ["ms"], capital: "Bandar Seri Begawan", emoji: "🇧🇳", emojiU: "U+1F1E7 U+1F1F3", fips: "BX", internet: "BN", continent: "Asia", region: "South East Asia" }, { name: "Bulgaria", alpha2: "BG", alpha3: "BGR", numeric: "100", locales: ["bg-BG"], default_locale: "bg-BG", currency: "BGN", latitude: "42.733883", longitude: "25.48583", currency_name: "Bulgarian Lev", languages: ["bg"], capital: "Sofia", emoji: "🇧🇬", emojiU: "U+1F1E7 U+1F1EC", fips: "BU", internet: "BG", continent: "Europe", region: "South East Europe" }, { name: "Burkina Faso", alpha2: "BF", alpha3: "BFA", numeric: "854", locales: ["fr-BF"], default_locale: "fr-BF", currency: "XOF", latitude: "12.238333", longitude: "-1.561593", currency_name: "CFA Franc BCEAO", languages: ["fr", "ff"], capital: "Ouagadougou", emoji: "🇧🇫", emojiU: "U+1F1E7 U+1F1EB", fips: "UV", internet: "BF", continent: "Africa", region: "Western Africa" }, { name: "Burundi", alpha2: "BI", alpha3: "BDI", numeric: "108", locales: ["fr-BI"], default_locale: "fr-BI", currency: "BIF", latitude: "-3.373056", longitude: "29.918886", currency_name: "Burundi Franc", languages: ["fr", "rn"], capital: "Bujumbura", emoji: "🇧🇮", emojiU: "U+1F1E7 U+1F1EE", fips: "BY", internet: "BI", continent: "Africa", region: "Central Africa" }, { name: "Cabo Verde", alpha2: "CV", alpha3: "CPV", numeric: "132", locales: ["kea-CV"], default_locale: "kea-CV", currency: "CVE", latitude: "16.002082", longitude: "-24.013197", currency_name: "Cabo Verde Escudo", languages: ["pt"], capital: "Praia", emoji: "🇨🇻", emojiU: "U+1F1E8 U+1F1FB", fips: "CV", internet: "CV", continent: "Africa", region: "Western Africa" }, { name: "Cambodia", alpha2: "KH", alpha3: "KHM", numeric: "116", locales: ["km-KH"], default_locale: "km-KH", currency: "KHR", latitude: "12.565679", longitude: "104.990963", currency_name: "Riel", languages: ["km"], capital: "Phnom Penh", emoji: "🇰🇭", emojiU: "U+1F1F0 U+1F1ED", fips: "CB", internet: "KH", continent: "Asia", region: "South East Asia" }, { name: "Cameroon", alpha2: "CM", alpha3: "CMR", numeric: "120", locales: ["fr-CM"], default_locale: "fr-CM", currency: "XAF", latitude: "7.369722", longitude: "12.354722", currency_name: "CFA Franc BEAC", languages: ["en", "fr"], capital: "Yaoundé", emoji: "🇨🇲", emojiU: "U+1F1E8 U+1F1F2", fips: "CM", internet: "CM", continent: "Africa", region: "Western Africa" }, { name: "Canada", alpha2: "CA", alpha3: "CAN", numeric: "124", locales: ["en-CA", "fr-CA"], default_locale: "en-CA", currency: "CAD", latitude: "56.130366", longitude: "-106.346771", currency_name: "Canadian Dollar", languages: ["en", "fr"], capital: "Ottawa", emoji: "🇨🇦", emojiU: "U+1F1E8 U+1F1E6", fips: "CA", internet: "CA", continent: "Americas", region: "North America" }, { name: "Cayman Islands", alpha2: "KY", alpha3: "CYM", numeric: "136", locales: ["en"], default_locale: "en", currency: "KYD", latitude: "19.513469", longitude: "-80.566956", currency_name: "Cayman Islands Dollar", languages: ["en"], capital: "George Town", emoji: "🇰🇾", emojiU: "U+1F1F0 U+1F1FE", fips: "CJ", internet: "KY", continent: "Americas", region: "West Indies" }, { name: "Central African Republic", alpha2: "CF", alpha3: "CAF", numeric: "140", locales: ["fr-CF", "sg-CF"], default_locale: "fr-CF", currency: "XAF", latitude: "6.611111", longitude: "20.939444", currency_name: "CFA Franc BEAC", languages: ["fr", "sg"], capital: "Bangui", emoji: "🇨🇫", emojiU: "U+1F1E8 U+1F1EB", fips: "CT", internet: "CF", continent: "Africa", region: "Central Africa" }, { name: "Chad", alpha2: "TD", alpha3: "TCD", numeric: "148", locales: ["fr-TD"], default_locale: "fr-TD", currency: "XAF", latitude: "15.454166", longitude: "18.732207", currency_name: "CFA Franc BEAC", languages: ["fr", "ar"], capital: "N'Djamena", emoji: "🇹🇩", emojiU: "U+1F1F9 U+1F1E9", fips: "CD", internet: "TD", continent: "Africa", region: "Central Africa" }, { name: "Chile", alpha2: "CL", alpha3: "CHL", numeric: "152", locales: ["es-CL"], default_locale: "es-CL", currency: "CLP", latitude: "-35.675147", longitude: "-71.542969", currency_name: "Chilean Peso", languages: ["es"], capital: "Santiago", emoji: "🇨🇱", emojiU: "U+1F1E8 U+1F1F1", fips: "CI", internet: "CL", continent: "Americas", region: "South America" }, { name: "China", alpha2: "CN", alpha3: "CHN", numeric: "156", locales: ["zh-CN", "zh-Hans-CN", "ii-CN", "bo-CN"], default_locale: "zh-CN", currency: "CNY", latitude: "35.86166", longitude: "104.195397", currency_name: "Yuan Renminbi", languages: ["zh"], capital: "Beijing", emoji: "🇨🇳", emojiU: "U+1F1E8 U+1F1F3", fips: "CH", internet: "CN", continent: "Asia", region: "East Asia" }, { name: "Christmas Island", alpha2: "CX", alpha3: "CXR", numeric: "162", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-10.447525", longitude: "105.690449", currency_name: "Australian Dollar", languages: ["en"], capital: "Flying Fish Cove", emoji: "🇨🇽", emojiU: "U+1F1E8 U+1F1FD", fips: "KT", internet: "CX", continent: "Asia", region: "South East Asia" }, { name: "Cocos Islands", alpha2: "CC", alpha3: "CCK", numeric: "166", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-12.164165", longitude: "96.870956", currency_name: "Australian Dollar", languages: ["en"], capital: "West Island", emoji: "🇨🇨", emojiU: "U+1F1E8 U+1F1E8", fips: "CK", internet: "CC", continent: "Asia", region: "South East Asia", alternate_names: ["Cocos Keeling Islands"] }, { name: "Colombia", alpha2: "CO", alpha3: "COL", numeric: "170", locales: ["es-CO"], default_locale: "es-CO", currency: "COP", latitude: "4.570868", longitude: "-74.297333", currency_name: "Colombian Peso", languages: ["es"], capital: "Bogotá", emoji: "🇨🇴", emojiU: "U+1F1E8 U+1F1F4", fips: "CO", internet: "CO", continent: "Americas", region: "South America" }, { name: "Comoros", alpha2: "KM", alpha3: "COM", numeric: "174", locales: ["fr-KM"], default_locale: "fr-KM", currency: "KMF", latitude: "-11.875001", longitude: "43.872219", currency_name: "Comorian Franc ", languages: ["ar", "fr"], capital: "Moroni", emoji: "🇰🇲", emojiU: "U+1F1F0 U+1F1F2", fips: "CN", internet: "KM", continent: "Africa", region: "Indian Ocean" }, { name: "Democratic Republic of the Congo", alpha2: "CD", alpha3: "COD", numeric: "180", locales: ["fr-CD"], default_locale: "fr-CD", currency: "CDF", latitude: "-4.038333", longitude: "21.758664", currency_name: "Congolese Franc", languages: ["fr", "ln", "kg", "sw", "lu"], capital: "Kinshasa", emoji: "🇨🇩", emojiU: "U+1F1E8 U+1F1E9", fips: "CG", internet: "ZR", continent: "Africa", region: "Central Africa" }, { name: "Congo", alpha2: "CG", alpha3: "COG", numeric: "178", locales: ["fr-CG"], default_locale: "fr-CG", currency: "XAF", latitude: "-0.228021", longitude: "15.827659", currency_name: "CFA Franc BEAC", languages: ["fr", "ln"], capital: "Brazzaville", emoji: "🇨🇬", emojiU: "U+1F1E8 U+1F1EC", fips: "CF", internet: "CG", continent: "Africa", region: "Central Africa" }, { name: "Cook Islands", alpha2: "CK", alpha3: "COK", numeric: "184", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-21.236736", longitude: "-159.777671", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Avarua", emoji: "🇨🇰", emojiU: "U+1F1E8 U+1F1F0", fips: "CW", internet: "CK", continent: "Oceania", region: "Pacific" }, { name: "Costa Rica", alpha2: "CR", alpha3: "CRI", numeric: "188", locales: ["es-CR"], default_locale: "es-CR", currency: "CRC", latitude: "9.748917", longitude: "-83.753428", currency_name: "Costa Rican Colon", languages: ["es"], capital: "San José", emoji: "🇨🇷", emojiU: "U+1F1E8 U+1F1F7", fips: "CS", internet: "CR", continent: "Americas", region: "Central America" }, { name: "Croatia", alpha2: "HR", alpha3: "HRV", numeric: "191", locales: ["hr-HR"], default_locale: "hr-HR", currency: "EUR", latitude: "45.1", longitude: "15.2", currency_name: "Euro", languages: ["hr"], capital: "Zagreb", emoji: "🇭🇷", emojiU: "U+1F1ED U+1F1F7", fips: "HR", internet: "HR", continent: "Europe", region: "South East Europe" }, { name: "Cuba", alpha2: "CU", alpha3: "CUB", numeric: "192", locales: ["es"], default_locale: "es", currency: "CUC", latitude: "21.521757", longitude: "-77.781167", currency_name: "Peso Convertible", languages: ["es"], capital: "Havana", emoji: "🇨🇺", emojiU: "U+1F1E8 U+1F1FA", fips: "CU", internet: "CU", continent: "Americas", region: "West Indies" }, { name: "Curaçao", alpha2: "CW", alpha3: "CUW", numeric: "531", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "pa", "en"], capital: "Willemstad", emoji: "🇨🇼", emojiU: "U+1F1E8 U+1F1FC", fips: "UC", internet: "CW", continent: "Americas", region: "West Indies" }, { name: "Cyprus", alpha2: "CY", alpha3: "CYP", numeric: "196", locales: ["el-CY"], default_locale: "el-CY", currency: "EUR", latitude: "35.126413", longitude: "33.429859", currency_name: "Euro", languages: ["el", "tr", "hy"], capital: "Nicosia", emoji: "🇨🇾", emojiU: "U+1F1E8 U+1F1FE", fips: "CY", internet: "CY", continent: "Asia", region: "South West Asia" }, { name: "Czechia", alpha2: "CZ", alpha3: "CZE", numeric: "203", locales: ["cs-CZ"], default_locale: "cs-CZ", currency: "CZK", latitude: "49.817492", longitude: "15.472962", currency_name: "Czech Koruna", languages: ["cs", "sk"], capital: "Prague", emoji: "🇨🇿", emojiU: "U+1F1E8 U+1F1FF", fips: "EZ", internet: "CZ", continent: "Europe", region: "Central Europe" }, { name: "Côte d'Ivoire", alpha2: "CI", alpha3: "CIV", numeric: "384", locales: ["fr-CI"], default_locale: "fr-CI", currency: "CZK", latitude: "7.539989", longitude: "-5.54708", currency_name: "Czech Koruna", languages: ["fr"], capital: "Yamoussoukro", emoji: "🇨🇮", emojiU: "U+1F1E8 U+1F1EE", fips: "IV", internet: "CI", continent: "Africa", region: "Western Africa" }, { name: "Denmark", alpha2: "DK", alpha3: "DNK", numeric: "208", locales: ["da-DK"], default_locale: "da-DK", currency: "DKK", latitude: "56.26392", longitude: "9.501785", currency_name: "Danish Krone", languages: ["da"], capital: "Copenhagen", emoji: "🇩🇰", emojiU: "U+1F1E9 U+1F1F0", fips: "DA", internet: "DK", continent: "Europe", region: "Northern Europe" }, { name: "Djibouti", alpha2: "DJ", alpha3: "DJI", numeric: "262", locales: ["fr-DJ", "so-DJ"], default_locale: "fr-DJ", currency: "DJF", latitude: "11.825138", longitude: "42.590275", currency_name: "Djibouti Franc", languages: ["fr", "ar"], capital: "Djibouti", emoji: "🇩🇯", emojiU: "U+1F1E9 U+1F1EF", fips: "DJ", internet: "DJ", continent: "Africa", region: "Eastern Africa" }, { name: "Dominica", alpha2: "DM", alpha3: "DMA", numeric: "212", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "15.414999", longitude: "-61.370976", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Roseau", emoji: "🇩🇲", emojiU: "U+1F1E9 U+1F1F2", fips: "DO", internet: "DM", continent: "Americas", region: "West Indies" }, { name: "Dominican Republic", alpha2: "DO", alpha3: "DOM", numeric: "214", locales: ["es-DO"], default_locale: "es-DO", currency: "DOP", latitude: "18.735693", longitude: "-70.162651", currency_name: "Dominican Peso", languages: ["es"], capital: "Santo Domingo", emoji: "🇩🇴", emojiU: "U+1F1E9 U+1F1F4", fips: "DR", internet: "DO", continent: "Americas", region: "West Indies" }, { name: "Ecuador", alpha2: "EC", alpha3: "ECU", numeric: "218", locales: ["es-EC"], default_locale: "es-EC", currency: "USD", latitude: "-1.831239", longitude: "-78.183406", currency_name: "US Dollar", languages: ["es"], capital: "Quito", emoji: "🇪🇨", emojiU: "U+1F1EA U+1F1E8", fips: "EC", internet: "EC", continent: "Americas", region: "South America" }, { name: "Egypt", alpha2: "EG", alpha3: "EGY", numeric: "818", locales: ["ar-EG"], default_locale: "ar-EG", currency: "EGP", latitude: "26.820553", longitude: "30.802498", currency_name: "Egyptian Pound", languages: ["ar"], capital: "Cairo", emoji: "🇪🇬", emojiU: "U+1F1EA U+1F1EC", fips: "EG", internet: "EG", continent: "Africa", region: "Northern Africa" }, { name: "El Salvador", alpha2: "SV", alpha3: "SLV", numeric: "222", locales: ["es-SV"], default_locale: "es-SV", currency: "USD", latitude: "13.794185", longitude: "-88.89653", currency_name: "US Dollar", languages: ["es"], capital: "San Salvador", emoji: "🇸🇻", emojiU: "U+1F1F8 U+1F1FB", fips: "ES", internet: "SV", continent: "Americas", region: "Central America" }, { name: "Equatorial Guinea", alpha2: "GQ", alpha3: "GNQ", numeric: "226", locales: ["fr-GQ", "es-GQ"], default_locale: "fr-GQ", currency: "XAF", latitude: "1.650801", longitude: "10.267895", currency_name: "CFA Franc BEAC", languages: ["es", "fr"], capital: "Malabo", emoji: "🇬🇶", emojiU: "U+1F1EC U+1F1F6", fips: "EK", internet: "GQ", continent: "Africa", region: "Western Africa" }, { name: "Eritrea", alpha2: "ER", alpha3: "ERI", numeric: "232", locales: ["ti-ER"], default_locale: "ti-ER", currency: "ERN", latitude: "15.179384", longitude: "39.782334", currency_name: "Nakfa", languages: ["ti", "ar", "en"], capital: "Asmara", emoji: "🇪🇷", emojiU: "U+1F1EA U+1F1F7", fips: "ER", internet: "ER", continent: "Africa", region: "Eastern Africa" }, { name: "Estonia", alpha2: "EE", alpha3: "EST", numeric: "233", locales: ["et-EE"], default_locale: "et-EE", currency: "EUR", latitude: "58.595272", longitude: "25.013607", currency_name: "Euro", languages: ["et"], capital: "Tallinn", emoji: "🇪🇪", emojiU: "U+1F1EA U+1F1EA", fips: "EN", internet: "EE", continent: "Europe", region: "Eastern Europe" }, { name: "Eswatini", alpha2: "SZ", alpha3: "SWZ", numeric: "748", locales: ["en"], default_locale: "en", currency: "EUR", latitude: "-26.522503", longitude: "31.465866", currency_name: "Euro", languages: ["en", "ss"], capital: "Lobamba", emoji: "🇸🇿", emojiU: "U+1F1F8 U+1F1FF", fips: "WZ", internet: "SZ", continent: "Africa", region: "Southern Africa" }, { name: "Ethiopia", alpha2: "ET", alpha3: "ETH", numeric: "231", locales: ["am-ET", "om-ET", "so-ET", "ti-ET"], default_locale: "am-ET", currency: "ETB", latitude: "9.145", longitude: "40.489673", currency_name: "Ethiopian Birr", languages: ["am"], capital: "Addis Ababa", emoji: "🇪🇹", emojiU: "U+1F1EA U+1F1F9", fips: "ET", internet: "ET", continent: "Africa", region: "Eastern Africa" }, { name: "Falkland Islands", alpha2: "FK", alpha3: "FLK", numeric: "238", locales: ["en"], default_locale: "en", currency: "DKK", latitude: "-51.796253", longitude: "-59.523613", currency_name: "Danish Krone", languages: ["en"], capital: "Stanley", emoji: "🇫🇰", emojiU: "U+1F1EB U+1F1F0", fips: "FA", internet: "FK", continent: "Americas", region: "South America", alternate_names: ["Malvinas Falkland Islands"] }, { name: "Faroe Islands", alpha2: "FO", alpha3: "FRO", numeric: "234", locales: ["fo-FO"], default_locale: "fo-FO", currency: "DKK", latitude: "61.892635", longitude: "-6.911806", currency_name: "Danish Krone", languages: ["fo"], capital: "Tórshavn", emoji: "🇫🇴", emojiU: "U+1F1EB U+1F1F4", fips: "FO", internet: "FO", continent: "Europe", region: "Northern Europe" }, { name: "Fiji", alpha2: "FJ", alpha3: "FJI", numeric: "242", locales: ["en"], default_locale: "en", currency: "FJD", latitude: "-16.578193", longitude: "179.414413", currency_name: "Fiji Dollar", languages: ["en", "fj", "hi", "ur"], capital: "Suva", emoji: "🇫🇯", emojiU: "U+1F1EB U+1F1EF", fips: "FJ", internet: "FJ", continent: "Oceania", region: "Pacific" }, { name: "Finland", alpha2: "FI", alpha3: "FIN", numeric: "246", locales: ["fi-FI", "sv-FI"], default_locale: "fi-FI", currency: "EUR", latitude: "61.92411", longitude: "25.748151", currency_name: "Euro", languages: ["fi", "sv"], capital: "Helsinki", emoji: "🇫🇮", emojiU: "U+1F1EB U+1F1EE", fips: "FI", internet: "FI", continent: "Europe", region: "Northern Europe" }, { name: "France", alpha2: "FR", alpha3: "FRA", numeric: "250", locales: ["fr-FR"], default_locale: "fr-FR", currency: "EUR", latitude: "46.227638", longitude: "2.213749", currency_name: "Euro", languages: ["fr"], capital: "Paris", emoji: "🇫🇷", emojiU: "U+1F1EB U+1F1F7", fips: "FR", internet: "FR", continent: "Europe", region: "Western Europe" }, { name: "French Guiana", alpha2: "GF", alpha3: "GUF", numeric: "254", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "3.933889", longitude: "-53.125782", currency_name: "Euro", languages: ["fr"], capital: "Cayenne", emoji: "🇬🇫", emojiU: "U+1F1EC U+1F1EB", fips: "FG", internet: "GF", continent: "Americas", region: "South America" }, { name: "French Polynesia", alpha2: "PF", alpha3: "PYF", numeric: "258", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-17.679742", longitude: "-149.406843", currency_name: "CFP Franc", languages: ["fr"], capital: "Papeetē", emoji: "🇵🇫", emojiU: "U+1F1F5 U+1F1EB", fips: "FP", internet: "PF", continent: "Oceania", region: "Pacific" }, { name: "French Southern Territories", alpha2: "TF", alpha3: "ATF", numeric: "260", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-49.280366", longitude: "69.348557", currency_name: "Euro", languages: ["fr"], capital: "Port-aux-Français", emoji: "🇹🇫", emojiU: "U+1F1F9 U+1F1EB", fips: "FS", internet: "--", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Gabon", alpha2: "GA", alpha3: "GAB", numeric: "266", locales: ["fr-GA"], default_locale: "fr-GA", currency: "XAF", latitude: "-0.803689", longitude: "11.609444", currency_name: "CFA Franc BEAC", languages: ["fr"], capital: "Libreville", emoji: "🇬🇦", emojiU: "U+1F1EC U+1F1E6", fips: "GB", internet: "GA", continent: "Africa", region: "Western Africa" }, { name: "Gambia", alpha2: "GM", alpha3: "GMB", numeric: "270", locales: ["en"], default_locale: "en", currency: "GMD", latitude: "13.443182", longitude: "-15.310139", currency_name: "Dalasi", languages: ["en"], capital: "Banjul", emoji: "🇬🇲", emojiU: "U+1F1EC U+1F1F2", fips: "GA", internet: "GM", continent: "Africa", region: "Western Africa" }, { name: "Georgia", alpha2: "GE", alpha3: "GEO", numeric: "268", locales: ["ka-GE"], default_locale: "ka-GE", currency: "GEL", latitude: "42.315407", longitude: "43.356892", currency_name: "Lari", languages: ["ka"], capital: "Tbilisi", emoji: "🇬🇪", emojiU: "U+1F1EC U+1F1EA", fips: "GG", internet: "GE", continent: "Asia", region: "South West Asia" }, { name: "Germany", alpha2: "DE", alpha3: "DEU", numeric: "276", locales: ["de-DE"], default_locale: "de-DE", currency: "EUR", latitude: "51.165691", longitude: "10.451526", currency_name: "Euro", languages: ["de"], capital: "Berlin", emoji: "🇩🇪", emojiU: "U+1F1E9 U+1F1EA", fips: "GM", internet: "DE", continent: "Europe", region: "Western Europe" }, { name: "Ghana", alpha2: "GH", alpha3: "GHA", numeric: "288", locales: ["ak-GH", "ee-GH", "ha-Latn-GH"], default_locale: "ak-GH", currency: "GHS", latitude: "7.946527", longitude: "-1.023194", currency_name: "Ghana Cedi", languages: ["en"], capital: "Accra", emoji: "🇬🇭", emojiU: "U+1F1EC U+1F1ED", fips: "GH", internet: "GH", continent: "Africa", region: "Western Africa" }, { name: "Gibraltar", alpha2: "GI", alpha3: "GIB", numeric: "292", locales: ["en"], default_locale: "en", currency: "GIP", latitude: "36.137741", longitude: "-5.345374", currency_name: "Gibraltar Pound", languages: ["en"], capital: "Gibraltar", emoji: "🇬🇮", emojiU: "U+1F1EC U+1F1EE", fips: "GI", internet: "GI", continent: "Europe", region: "South West Europe" }, { name: "Greece", alpha2: "GR", alpha3: "GRC", numeric: "300", locales: ["el-GR"], default_locale: "el-GR", currency: "EUR", latitude: "39.074208", longitude: "21.824312", currency_name: "Euro", languages: ["el"], capital: "Athens", emoji: "🇬🇷", emojiU: "U+1F1EC U+1F1F7", fips: "GR", internet: "GR", continent: "Europe", region: "South East Europe" }, { name: "Greenland", alpha2: "GL", alpha3: "GRL", numeric: "304", locales: ["kl-GL"], default_locale: "kl-GL", currency: "DKK", latitude: "71.706936", longitude: "-42.604303", currency_name: "Danish Krone", languages: ["kl"], capital: "Nuuk", emoji: "🇬🇱", emojiU: "U+1F1EC U+1F1F1", fips: "GL", internet: "GL", continent: "Americas", region: "North America" }, { name: "Grenada", alpha2: "GD", alpha3: "GRD", numeric: "308", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.262776", longitude: "-61.604171", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "St. George's", emoji: "🇬🇩", emojiU: "U+1F1EC U+1F1E9", fips: "GJ", internet: "GD", continent: "Americas", region: "West Indies" }, { name: "Guadeloupe", alpha2: "GP", alpha3: "GLP", numeric: "312", locales: ["fr-GP"], default_locale: "fr-GP", currency: "EUR", latitude: "16.995971", longitude: "-62.067641", currency_name: "Euro", languages: ["fr"], capital: "Basse-Terre", emoji: "🇬🇵", emojiU: "U+1F1EC U+1F1F5", fips: "GP", internet: "GP", continent: "Americas", region: "West Indies" }, { name: "Guam", alpha2: "GU", alpha3: "GUM", numeric: "316", locales: ["en-GU"], default_locale: "en-GU", currency: "USD", latitude: "13.444304", longitude: "144.793731", currency_name: "US Dollar", languages: ["en", "ch", "es"], capital: "Hagåtña", emoji: "🇬🇺", emojiU: "U+1F1EC U+1F1FA", fips: "GQ", internet: "GU", continent: "Oceania", region: "Pacific" }, { name: "Guatemala", alpha2: "GT", alpha3: "GTM", numeric: "320", locales: ["es-GT"], default_locale: "es-GT", currency: "GTQ", latitude: "15.783471", longitude: "-90.230759", currency_name: "Quetzal", languages: ["es"], capital: "Guatemala City", emoji: "🇬🇹", emojiU: "U+1F1EC U+1F1F9", fips: "GT", internet: "GT", continent: "Americas", region: "Central America" }, { name: "Guernsey", alpha2: "GG", alpha3: "GGY", numeric: "831", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.465691", longitude: "-2.585278", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "St. Peter Port", emoji: "🇬🇬", emojiU: "U+1F1EC U+1F1EC", fips: "GK", internet: "GG", continent: "Europe", region: "Western Europe" }, { name: "Guinea", alpha2: "GN", alpha3: "GIN", numeric: "324", locales: ["fr-GN"], default_locale: "fr-GN", currency: "GNF", latitude: "9.945587", longitude: "-9.696645", currency_name: "Guinean Franc", languages: ["fr", "ff"], capital: "Conakry", emoji: "🇬🇳", emojiU: "U+1F1EC U+1F1F3", fips: "GV", internet: "GN", continent: "Africa", region: "Western Africa" }, { name: "Guinea-Bissau", alpha2: "GW", alpha3: "GNB", numeric: "624", locales: ["pt-GW"], default_locale: "pt-GW", currency: "XOF", latitude: "11.803749", longitude: "-15.180413", currency_name: "CFA Franc BCEAO", languages: ["pt"], capital: "Bissau", emoji: "🇬🇼", emojiU: "U+1F1EC U+1F1FC", fips: "PU", internet: "GW", continent: "Africa", region: "Western Africa" }, { name: "Guyana", alpha2: "GY", alpha3: "GUY", numeric: "328", locales: ["en"], default_locale: "en", currency: "GYD", latitude: "4.860416", longitude: "-58.93018", currency_name: "Guyana Dollar", languages: ["en"], capital: "Georgetown", emoji: "🇬🇾", emojiU: "U+1F1EC U+1F1FE", fips: "GY", internet: "GY", continent: "Americas", region: "South America" }, { name: "Haiti", alpha2: "HT", alpha3: "HTI", numeric: "332", locales: ["fr"], default_locale: "fr", currency: "USD", latitude: "18.971187", longitude: "-72.285215", currency_name: "US Dollar", languages: ["fr", "ht"], capital: "Port-au-Prince", emoji: "🇭🇹", emojiU: "U+1F1ED U+1F1F9", fips: "HA", internet: "HT", continent: "Americas", region: "West Indies" }, { name: "Heard Island and McDonald Islands", alpha2: "HM", alpha3: "HMD", numeric: "334", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-53.08181", longitude: "73.504158", currency_name: "Australian Dollar", languages: ["en"], capital: "", emoji: "🇭🇲", emojiU: "U+1F1ED U+1F1F2", fips: "HM", internet: "HM", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Holy See", alpha2: "VA", alpha3: "VAT", numeric: "336", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "41.902916", longitude: "12.453389", currency_name: "Euro", languages: ["it", "la"], capital: "Vatican City", emoji: "🇻🇦", emojiU: "U+1F1FB U+1F1E6", fips: "VT", internet: "VA", continent: "Europe", region: "Southern Europe" }, { name: "Honduras", alpha2: "HN", alpha3: "HND", numeric: "340", locales: ["es-HN"], default_locale: "es-HN", currency: "HNL", latitude: "15.199999", longitude: "-86.241905", currency_name: "Lempira", languages: ["es"], capital: "Tegucigalpa", emoji: "🇭🇳", emojiU: "U+1F1ED U+1F1F3", fips: "HO", internet: "HN", continent: "Americas", region: "Central America" }, { name: "Hong Kong", alpha2: "HK", alpha3: "HKG", numeric: "344", locales: ["yue-Hant-HK", "zh-Hans-HK", "zh-Hant-HK", "en-HK"], default_locale: "en-HK", currency: "HKD", latitude: "22.396428", longitude: "114.109497", currency_name: "Hong Kong Dollar", languages: ["zh", "en"], capital: "City of Victoria", emoji: "🇭🇰", emojiU: "U+1F1ED U+1F1F0", fips: "HK", internet: "HK", continent: "Asia", region: "East Asia" }, { name: "Hungary", alpha2: "HU", alpha3: "HUN", numeric: "348", locales: ["hu-HU"], default_locale: "hu-HU", currency: "HUF", latitude: "47.162494", longitude: "19.503304", currency_name: "Forint", languages: ["hu"], capital: "Budapest", emoji: "🇭🇺", emojiU: "U+1F1ED U+1F1FA", fips: "HU", internet: "HU", continent: "Europe", region: "Central Europe" }, { name: "Iceland", alpha2: "IS", alpha3: "ISL", numeric: "352", locales: ["is-IS"], default_locale: "is-IS", currency: "ISK", latitude: "64.963051", longitude: "-19.020835", currency_name: "Iceland Krona", languages: ["is"], capital: "Reykjavik", emoji: "🇮🇸", emojiU: "U+1F1EE U+1F1F8", fips: "IC", internet: "IS", continent: "Europe", region: "Northern Europe" }, { name: "India", alpha2: "IN", alpha3: "IND", numeric: "356", locales: ["as-IN", "bn-IN", "en-IN", "gu-IN", "hi-IN", "kn-IN", "kok-IN", "ml-IN", "mr-IN", "ne-IN", "or-IN", "pa-Guru-IN", "ta-IN", "te-IN", "bo-IN", "ur-IN"], default_locale: "hi-IN", currency: "INR", latitude: "20.593684", longitude: "78.96288", currency_name: "Indian Rupee", languages: ["hi", "en"], capital: "New Delhi", emoji: "🇮🇳", emojiU: "U+1F1EE U+1F1F3", fips: "IN", internet: "IN", continent: "Asia", region: "South Asia" }, { name: "Indonesia", alpha2: "ID", alpha3: "IDN", numeric: "360", locales: ["id-ID"], default_locale: "id-ID", currency: "IDR", latitude: "-0.789275", longitude: "113.921327", currency_name: "Rupiah", languages: ["id"], capital: "Jakarta", emoji: "🇮🇩", emojiU: "U+1F1EE U+1F1E9", fips: "ID", internet: "ID", continent: "Asia", region: "South East Asia" }, { name: "Iran", alpha2: "IR", alpha3: "IRN", numeric: "364", locales: ["fa-IR"], default_locale: "fa-IR", currency: "XDR", latitude: "32.427908", longitude: "53.688046", currency_name: "SDR (Special Drawing Right)", languages: ["fa"], capital: "Tehran", emoji: "🇮🇷", emojiU: "U+1F1EE U+1F1F7", fips: "IR", internet: "IR", continent: "Asia", region: "South West Asia", alternate_names: ["Islamic Republic of Iran"] }, { name: "Iraq", alpha2: "IQ", alpha3: "IRQ", numeric: "368", locales: ["ar-IQ"], default_locale: "ar-IQ", currency: "IQD", latitude: "33.223191", longitude: "43.679291", currency_name: "Iraqi Dinar", languages: ["ar", "ku"], capital: "Baghdad", emoji: "🇮🇶", emojiU: "U+1F1EE U+1F1F6", fips: "IZ", internet: "IQ", continent: "Asia", region: "South West Asia" }, { name: "Ireland", alpha2: "IE", alpha3: "IRL", numeric: "372", locales: ["en-IE", "ga-IE"], default_locale: "en-IE", currency: "EUR", latitude: "53.41291", longitude: "-8.24389", currency_name: "Euro", languages: ["ga", "en"], capital: "Dublin", emoji: "🇮🇪", emojiU: "U+1F1EE U+1F1EA", fips: "EI", internet: "IE", continent: "Europe", region: "Western Europe" }, { name: "Isle of Man", alpha2: "IM", alpha3: "IMN", numeric: "833", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "54.236107", longitude: "-4.548056", currency_name: "Pound Sterling", languages: ["en", "gv"], capital: "Douglas", emoji: "🇮🇲", emojiU: "U+1F1EE U+1F1F2", fips: "IM", internet: "IM", continent: "Europe", region: "Western Europe" }, { name: "Israel", alpha2: "IL", alpha3: "ISR", numeric: "376", locales: ["en-IL", "he-IL"], default_locale: "he-IL", currency: "ILS", latitude: "31.046051", longitude: "34.851612", currency_name: "New Israeli Sheqel", languages: ["he", "ar"], capital: "Jerusalem", emoji: "🇮🇱", emojiU: "U+1F1EE U+1F1F1", fips: "IS", internet: "IL", continent: "Asia", region: "South West Asia" }, { name: "Italy", alpha2: "IT", alpha3: "ITA", numeric: "380", locales: ["it-IT"], default_locale: "it-IT", currency: "EUR", latitude: "41.87194", longitude: "12.56738", currency_name: "Euro", languages: ["it"], capital: "Rome", emoji: "🇮🇹", emojiU: "U+1F1EE U+1F1F9", fips: "IT", internet: "IT", continent: "Europe", region: "Southern Europe" }, { name: "Jamaica", alpha2: "JM", alpha3: "JAM", numeric: "388", locales: ["en-JM"], default_locale: "en-JM", currency: "JMD", latitude: "18.109581", longitude: "-77.297508", currency_name: "Jamaican Dollar", languages: ["en"], capital: "Kingston", emoji: "🇯🇲", emojiU: "U+1F1EF U+1F1F2", fips: "JM", internet: "JM", continent: "Americas", region: "West Indies" }, { name: "Japan", alpha2: "JP", alpha3: "JPN", numeric: "392", locales: ["ja-JP"], default_locale: "ja-JP", currency: "JPY", latitude: "36.204824", longitude: "138.252924", currency_name: "Yen", languages: ["ja"], capital: "Tokyo", emoji: "🇯🇵", emojiU: "U+1F1EF U+1F1F5", fips: "JA", internet: "JP", continent: "Asia", region: "East Asia" }, { name: "Jersey", alpha2: "JE", alpha3: "JEY", numeric: "832", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.214439", longitude: "-2.13125", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "Saint Helier", emoji: "🇯🇪", emojiU: "U+1F1EF U+1F1EA", fips: "JE", internet: "JE", continent: "Europe", region: "Western Europe" }, { name: "Jordan", alpha2: "JO", alpha3: "JOR", numeric: "400", locales: ["ar-JO"], default_locale: "ar-JO", currency: "JOD", latitude: "30.585164", longitude: "36.238414", currency_name: "Jordanian Dinar", languages: ["ar"], capital: "Amman", emoji: "🇯🇴", emojiU: "U+1F1EF U+1F1F4", fips: "JO", internet: "JO", continent: "Asia", region: "South West Asia" }, { name: "Kazakhstan", alpha2: "KZ", alpha3: "KAZ", numeric: "398", locales: ["kk-Cyrl-KZ"], default_locale: "kk-Cyrl-KZ", currency: "KZT", latitude: "48.019573", longitude: "66.923684", currency_name: "Tenge", languages: ["kk", "ru"], capital: "Astana", emoji: "🇰🇿", emojiU: "U+1F1F0 U+1F1FF", fips: "KZ", internet: "KZ", continent: "Asia", region: "Central Asia" }, { name: "Kenya", alpha2: "KE", alpha3: "KEN", numeric: "404", locales: ["ebu-KE", "guz-KE", "kln-KE", "kam-KE", "ki-KE", "luo-KE", "luy-KE", "mas-KE", "mer-KE", "om-KE", "saq-KE", "so-KE", "sw-KE", "dav-KE", "teo-KE"], default_locale: "ebu-KE", currency: "KES", latitude: "-0.023559", longitude: "37.906193", currency_name: "Kenyan Shilling", languages: ["en", "sw"], capital: "Nairobi", emoji: "🇰🇪", emojiU: "U+1F1F0 U+1F1EA", fips: "KE", internet: "KE", continent: "Africa", region: "Eastern Africa" }, { name: "Kiribati", alpha2: "KI", alpha3: "KIR", numeric: "296", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-3.370417", longitude: "-168.734039", currency_name: "Australian Dollar", languages: ["en"], capital: "South Tarawa", emoji: "🇰🇮", emojiU: "U+1F1F0 U+1F1EE", fips: "KR", internet: "KI", continent: "Oceania", region: "Pacific" }, { name: "North Korea", alpha2: "KP", alpha3: "PRK", numeric: "408", locales: ["ko"], default_locale: "ko", currency: "KPW", latitude: "40.339852", longitude: "127.510093", currency_name: "North Korean Won", languages: ["ko"], capital: "Pyongyang", emoji: "🇰🇵", emojiU: "U+1F1F0 U+1F1F5", fips: "KN", internet: "KP", continent: "Asia", region: "East Asia", alternate_names: ["Democratic People's Republic of Korea"] }, { name: "South Korea", alpha2: "KR", alpha3: "KOR", numeric: "410", locales: ["ko-KR"], default_locale: "ko-KR", currency: "KRW", latitude: "35.907757", longitude: "127.766922", currency_name: "Won", languages: ["ko"], capital: "Seoul", emoji: "🇰🇷", emojiU: "U+1F1F0 U+1F1F7", fips: "KS", internet: "KR", continent: "Asia", region: "East Asia", alternate_names: ["Republic of Korea"] }, { name: "Kuwait", alpha2: "KW", alpha3: "KWT", numeric: "414", locales: ["ar-KW"], default_locale: "ar-KW", currency: "KWD", latitude: "29.31166", longitude: "47.481766", currency_name: "Kuwaiti Dinar", languages: ["ar"], capital: "Kuwait City", emoji: "🇰🇼", emojiU: "U+1F1F0 U+1F1FC", fips: "KU", internet: "KW", continent: "Asia", region: "South West Asia" }, { name: "Kyrgyzstan", alpha2: "KG", alpha3: "KGZ", numeric: "417", locales: ["ky"], default_locale: "ky", currency: "KGS", latitude: "41.20438", longitude: "74.766098", currency_name: "Som", languages: ["ky", "ru"], capital: "Bishkek", emoji: "🇰🇬", emojiU: "U+1F1F0 U+1F1EC", fips: "KG", internet: "KG", continent: "Asia", region: "Central Asia" }, { name: "Lao People's Democratic Republic", alpha2: "LA", alpha3: "LAO", numeric: "418", locales: ["lo"], default_locale: "lo", currency: "LAK", latitude: "19.85627", longitude: "102.495496", currency_name: "Lao Kip", languages: ["lo"], capital: "Vientiane", emoji: "🇱🇦", emojiU: "U+1F1F1 U+1F1E6", fips: "LA", internet: "LA", continent: "Asia", region: "South East Asia" }, { name: "Latvia", alpha2: "LV", alpha3: "LVA", numeric: "428", locales: ["lv-LV"], default_locale: "lv-LV", currency: "EUR", latitude: "56.879635", longitude: "24.603189", currency_name: "Euro", languages: ["lv"], capital: "Riga", emoji: "🇱🇻", emojiU: "U+1F1F1 U+1F1FB", fips: "LG", internet: "LV", continent: "Europe", region: "Eastern Europe" }, { name: "Lebanon", alpha2: "LB", alpha3: "LBN", numeric: "422", locales: ["ar-LB"], default_locale: "ar-LB", currency: "LBP", latitude: "33.854721", longitude: "35.862285", currency_name: "Lebanese Pound", languages: ["ar", "fr"], capital: "Beirut", emoji: "🇱🇧", emojiU: "U+1F1F1 U+1F1E7", fips: "LE", internet: "LB", continent: "Asia", region: "South West Asia" }, { name: "Lesotho", alpha2: "LS", alpha3: "LSO", numeric: "426", locales: ["en"], default_locale: "en", currency: "ZAR", latitude: "-29.609988", longitude: "28.233608", currency_name: "Rand", languages: ["en", "st"], capital: "Maseru", emoji: "🇱🇸", emojiU: "U+1F1F1 U+1F1F8", fips: "LT", internet: "LS", continent: "Africa", region: "Southern Africa" }, { name: "Liberia", alpha2: "LR", alpha3: "LBR", numeric: "430", locales: ["en"], default_locale: "en", currency: "LRD", latitude: "6.428055", longitude: "-9.429499", currency_name: "Liberian Dollar", languages: ["en"], capital: "Monrovia", emoji: "🇱🇷", emojiU: "U+1F1F1 U+1F1F7", fips: "LI", internet: "LR", continent: "Africa", region: "Western Africa" }, { name: "Libya", alpha2: "LY", alpha3: "LBY", numeric: "434", locales: ["ar-LY"], default_locale: "ar-LY", currency: "LYD", latitude: "26.3351", longitude: "17.228331", currency_name: "Libyan Dinar", languages: ["ar"], capital: "Tripoli", emoji: "🇱🇾", emojiU: "U+1F1F1 U+1F1FE", fips: "LY", internet: "LY", continent: "Africa", region: "Northern Africa" }, { name: "Liechtenstein", alpha2: "LI", alpha3: "LIE", numeric: "438", locales: ["de-LI"], default_locale: "de-LI", currency: "CHF", latitude: "47.166", longitude: "9.555373", currency_name: "Swiss Franc", languages: ["de"], capital: "Vaduz", emoji: "🇱🇮", emojiU: "U+1F1F1 U+1F1EE", fips: "LS", internet: "LI", continent: "Europe", region: "Central Europe" }, { name: "Lithuania", alpha2: "LT", alpha3: "LTU", numeric: "440", locales: ["lt-LT"], default_locale: "lt-LT", currency: "EUR", latitude: "55.169438", longitude: "23.881275", currency_name: "Euro", languages: ["lt"], capital: "Vilnius", emoji: "🇱🇹", emojiU: "U+1F1F1 U+1F1F9", fips: "LH", internet: "LT", continent: "Europe", region: "Eastern Europe" }, { name: "Luxembourg", alpha2: "LU", alpha3: "LUX", numeric: "442", locales: ["fr-LU", "de-LU"], default_locale: "fr-LU", currency: "EUR", latitude: "49.815273", longitude: "6.129583", currency_name: "Euro", languages: ["fr", "de", "lb"], capital: "Luxembourg", emoji: "🇱🇺", emojiU: "U+1F1F1 U+1F1FA", fips: "LU", internet: "LU", continent: "Europe", region: "Western Europe" }, { name: "Macao", alpha2: "MO", alpha3: "MAC", numeric: "446", locales: ["zh-Hans-MO", "zh-Hant-MO"], default_locale: "zh-Hans-MO", currency: "MOP", latitude: "22.198745", longitude: "113.543873", currency_name: "Pataca", languages: ["zh", "pt"], capital: "", emoji: "🇲🇴", emojiU: "U+1F1F2 U+1F1F4", fips: "MC", internet: "MO", continent: "Asia", region: "East Asia" }, { name: "Madagascar", alpha2: "MG", alpha3: "MDG", numeric: "450", locales: ["fr-MG", "mg-MG"], default_locale: "fr-MG", currency: "MGA", latitude: "-18.766947", longitude: "46.869107", currency_name: "Malagasy Ariary", languages: ["fr", "mg"], capital: "Antananarivo", emoji: "🇲🇬", emojiU: "U+1F1F2 U+1F1EC", fips: "MA", internet: "MG", continent: "Africa", region: "Indian Ocean" }, { name: "Malawi", alpha2: "MW", alpha3: "MWI", numeric: "454", locales: ["en"], default_locale: "en", currency: "MWK", latitude: "-13.254308", longitude: "34.301525", currency_name: "Malawi Kwacha", languages: ["en", "ny"], capital: "Lilongwe", emoji: "🇲🇼", emojiU: "U+1F1F2 U+1F1FC", fips: "MI", internet: "MW", continent: "Africa", region: "Southern Africa" }, { name: "Malaysia", alpha2: "MY", alpha3: "MYS", numeric: "458", locales: ["ms-MY"], default_locale: "ms-MY", currency: "MYR", latitude: "4.210484", longitude: "101.975766", currency_name: "Malaysian Ringgit", languages: ["ms"], capital: "Kuala Lumpur", emoji: "🇲🇾", emojiU: "U+1F1F2 U+1F1FE", fips: "MY", internet: "MY", continent: "Asia", region: "South East Asia" }, { name: "Maldives", alpha2: "MV", alpha3: "MDV", numeric: "462", locales: ["dv"], default_locale: "dv", currency: "MVR", latitude: "3.202778", longitude: "73.22068", currency_name: "Rufiyaa", languages: ["dv"], capital: "Malé", emoji: "🇲🇻", emojiU: "U+1F1F2 U+1F1FB", fips: "MV", internet: "MV", continent: "Asia", region: "South Asia" }, { name: "Mali", alpha2: "ML", alpha3: "MLI", numeric: "466", locales: ["bm-ML", "fr-ML", "khq-ML", "ses-ML"], default_locale: "fr-ML", currency: "XOF", latitude: "17.570692", longitude: "-3.996166", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Bamako", emoji: "🇲🇱", emojiU: "U+1F1F2 U+1F1F1", fips: "ML", internet: "ML", continent: "Africa", region: "Western Africa" }, { name: "Malta", alpha2: "MT", alpha3: "MLT", numeric: "470", locales: ["en-MT", "mt-MT"], default_locale: "en-MT", currency: "EUR", latitude: "35.937496", longitude: "14.375416", currency_name: "Euro", languages: ["mt", "en"], capital: "Valletta", emoji: "🇲🇹", emojiU: "U+1F1F2 U+1F1F9", fips: "MT", internet: "MT", continent: "Europe", region: "Southern Europe" }, { name: "Marshall Islands", alpha2: "MH", alpha3: "MHL", numeric: "584", locales: ["en-MH"], default_locale: "en-MH", currency: "USD", latitude: "7.131474", longitude: "171.184478", currency_name: "US Dollar", languages: ["en", "mh"], capital: "Majuro", emoji: "🇲🇭", emojiU: "U+1F1F2 U+1F1ED", fips: "RM", internet: "MH", continent: "Oceania", region: "Pacific" }, { name: "Martinique", alpha2: "MQ", alpha3: "MTQ", numeric: "474", locales: ["fr-MQ"], default_locale: "fr-MQ", currency: "EUR", latitude: "14.641528", longitude: "-61.024174", currency_name: "Euro", languages: ["fr"], capital: "Fort-de-France", emoji: "🇲🇶", emojiU: "U+1F1F2 U+1F1F6", fips: "MB", internet: "MQ", continent: "Americas", region: "West Indies" }, { name: "Mauritania", alpha2: "MR", alpha3: "MRT", numeric: "478", locales: ["ar"], default_locale: "ar", currency: "MRU", latitude: "21.00789", longitude: "-10.940835", currency_name: "Ouguiya", languages: ["ar"], capital: "Nouakchott", emoji: "🇲🇷", emojiU: "U+1F1F2 U+1F1F7", fips: "MR", internet: "MR", continent: "Africa", region: "Western Africa" }, { name: "Mauritius", alpha2: "MU", alpha3: "MUS", numeric: "480", locales: ["en-MU", "mfe-MU"], default_locale: "en-MU", currency: "MUR", latitude: "-20.348404", longitude: "57.552152", currency_name: "Mauritius Rupee", languages: ["en"], capital: "Port Louis", emoji: "🇲🇺", emojiU: "U+1F1F2 U+1F1FA", fips: "MP", internet: "MU", continent: "Africa", region: "Indian Ocean" }, { name: "Mayotte", alpha2: "YT", alpha3: "MYT", numeric: "175", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-12.8275", longitude: "45.166244", currency_name: "Euro", languages: ["fr"], capital: "Mamoudzou", emoji: "🇾🇹", emojiU: "U+1F1FE U+1F1F9", fips: "MF", internet: "YT", continent: "Africa", region: "Indian Ocean" }, { name: "Mexico", alpha2: "MX", alpha3: "MEX", numeric: "484", locales: ["es-MX"], default_locale: "es-MX", currency: "MXN", latitude: "23.634501", longitude: "-102.552784", currency_name: "Mexican Peso", languages: ["es"], capital: "Mexico City", emoji: "🇲🇽", emojiU: "U+1F1F2 U+1F1FD", fips: "MX", internet: "MX", continent: "Americas", region: "Central America" }, { name: "Micronesia", alpha2: "FM", alpha3: "FSM", numeric: "583", locales: ["en"], default_locale: "en", currency: "RUB", latitude: "7.425554", longitude: "150.550812", currency_name: "Russian Ruble", languages: ["en"], capital: "Palikir", emoji: "🇫🇲", emojiU: "U+1F1EB U+1F1F2", fips: "", internet: "FM", continent: "Oceania", region: "Pacific", alternate_names: ["Federated States of Micronesia"] }, { name: "Moldova", alpha2: "MD", alpha3: "MDA", numeric: "498", locales: ["ro-MD", "ru-MD"], default_locale: "ro-MD", currency: "MDL", latitude: "47.411631", longitude: "28.369885", currency_name: "Moldovan Leu", languages: ["ro"], capital: "Chișinău", emoji: "🇲🇩", emojiU: "U+1F1F2 U+1F1E9", fips: "MD", internet: "MD", continent: "Europe", region: "Eastern Europe", alternate_names: ["Republic of Moldova"] }, { name: "Monaco", alpha2: "MC", alpha3: "MCO", numeric: "492", locales: ["fr-MC"], default_locale: "fr-MC", currency: "EUR", latitude: "43.750298", longitude: "7.412841", currency_name: "Euro", languages: ["fr"], capital: "Monaco", emoji: "🇲🇨", emojiU: "U+1F1F2 U+1F1E8", fips: "MN", internet: "MC", continent: "Europe", region: "Western Europe" }, { name: "Mongolia", alpha2: "MN", alpha3: "MNG", numeric: "496", locales: ["mn"], default_locale: "mn", currency: "MNT", latitude: "46.862496", longitude: "103.846656", currency_name: "Tugrik", languages: ["mn"], capital: "Ulan Bator", emoji: "🇲🇳", emojiU: "U+1F1F2 U+1F1F3", fips: "MG", internet: "MN", continent: "Asia", region: "Northern Asia" }, { name: "Montenegro", alpha2: "ME", alpha3: "MNE", numeric: "499", locales: ["sr-Cyrl-ME", "sr-Latn-ME"], default_locale: "sr-Cyrl-ME", currency: "EUR", latitude: "42.708678", longitude: "19.37439", currency_name: "Euro", languages: ["sr", "bs", "sq", "hr"], capital: "Podgorica", emoji: "🇲🇪", emojiU: "U+1F1F2 U+1F1EA", fips: "MJ", internet: "ME", continent: "Europe", region: "South East Europe" }, { name: "Montserrat", alpha2: "MS", alpha3: "MSR", numeric: "500", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "16.742498", longitude: "-62.187366", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Plymouth", emoji: "🇲🇸", emojiU: "U+1F1F2 U+1F1F8", fips: "MH", internet: "MS", continent: "Americas", region: "West Indies" }, { name: "Morocco", alpha2: "MA", alpha3: "MAR", numeric: "504", locales: ["ar-MA", "tzm-Latn-MA", "shi-Latn-MA", "shi-Tfng-MA"], default_locale: "ar-MA", currency: "MAD", latitude: "31.791702", longitude: "-7.09262", currency_name: "Moroccan Dirham", languages: ["ar"], capital: "Rabat", emoji: "🇲🇦", emojiU: "U+1F1F2 U+1F1E6", fips: "MO", internet: "MA", continent: "Africa", region: "Northern Africa" }, { name: "Mozambique", alpha2: "MZ", alpha3: "MOZ", numeric: "508", locales: ["pt-MZ", "seh-MZ"], default_locale: "pt-MZ", currency: "MZN", latitude: "-18.665695", longitude: "35.529562", currency_name: "Mozambique Metical", languages: ["pt"], capital: "Maputo", emoji: "🇲🇿", emojiU: "U+1F1F2 U+1F1FF", fips: "MZ", internet: "MZ", continent: "Africa", region: "Southern Africa" }, { name: "Myanmar", alpha2: "MM", alpha3: "MMR", numeric: "104", locales: ["my-MM"], default_locale: "my-MM", currency: "MMK", latitude: "21.913965", longitude: "95.956223", currency_name: "Kyat", languages: ["my"], capital: "Naypyidaw", emoji: "🇲🇲", emojiU: "U+1F1F2 U+1F1F2", fips: "BM", internet: "MM", continent: "Asia", region: "South East Asia" }, { name: "Namibia", alpha2: "NA", alpha3: "NAM", numeric: "516", locales: ["af-NA", "en-NA", "naq-NA"], default_locale: "en-NA", currency: "ZAR", latitude: "-22.95764", longitude: "18.49041", currency_name: "Rand", languages: ["en", "af"], capital: "Windhoek", emoji: "🇳🇦", emojiU: "U+1F1F3 U+1F1E6", fips: "WA", internet: "NA", continent: "Africa", region: "Southern Africa" }, { name: "Nauru", alpha2: "NR", alpha3: "NRU", numeric: "520", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-0.522778", longitude: "166.931503", currency_name: "Australian Dollar", languages: ["en", "na"], capital: "Yaren", emoji: "🇳🇷", emojiU: "U+1F1F3 U+1F1F7", fips: "NR", internet: "NR", continent: "Oceania", region: "Pacific" }, { name: "Nepal", alpha2: "NP", alpha3: "NPL", numeric: "524", locales: ["ne-NP"], default_locale: "ne-NP", currency: "NPR", latitude: "28.394857", longitude: "84.124008", currency_name: "Nepalese Rupee", languages: ["ne"], capital: "Kathmandu", emoji: "🇳🇵", emojiU: "U+1F1F3 U+1F1F5", fips: "NP", internet: "NP", continent: "Asia", region: "South Asia" }, { name: "Netherlands", alpha2: "NL", alpha3: "NLD", numeric: "528", locales: ["nl-NL"], default_locale: "nl-NL", currency: "EUR", latitude: "52.132633", longitude: "5.291266", currency_name: "Euro", languages: ["nl"], capital: "Amsterdam", emoji: "🇳🇱", emojiU: "U+1F1F3 U+1F1F1", fips: "NL", internet: "NL", continent: "Europe", region: "Western Europe" }, { name: "Netherlands Antilles", alpha2: "AN", alpha3: "ANT", numeric: "530", locales: ["nl-AN"], default_locale: "nl-AN", currency: "ANG", latitude: "12.226079", longitude: "-69.060087", currency_name: "Netherlands Antillean Guilder", fips: "NT", internet: "AN", continent: "Americas", region: "West Indies" }, { name: "New Caledonia", alpha2: "NC", alpha3: "NCL", numeric: "540", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-20.904305", longitude: "165.618042", currency_name: "CFP Franc", languages: ["fr"], capital: "Nouméa", emoji: "🇳🇨", emojiU: "U+1F1F3 U+1F1E8", fips: "NC", internet: "NC", continent: "Oceania", region: "Pacific" }, { name: "New Zealand", alpha2: "NZ", alpha3: "NZL", numeric: "554", locales: ["en-NZ"], default_locale: "en-NZ", currency: "NZD", latitude: "-40.900557", longitude: "174.885971", currency_name: "New Zealand Dollar", languages: ["en", "mi"], capital: "Wellington", emoji: "🇳🇿", emojiU: "U+1F1F3 U+1F1FF", fips: "NZ", internet: "NZ", continent: "Oceania", region: "Pacific" }, { name: "Nicaragua", alpha2: "NI", alpha3: "NIC", numeric: "558", locales: ["es-NI"], default_locale: "es-NI", currency: "NIO", latitude: "12.865416", longitude: "-85.207229", currency_name: "Cordoba Oro", languages: ["es"], capital: "Managua", emoji: "🇳🇮", emojiU: "U+1F1F3 U+1F1EE", fips: "NU", internet: "NI", continent: "Americas", region: "Central America" }, { name: "Niger", alpha2: "NE", alpha3: "NER", numeric: "562", locales: ["fr-NE", "ha-Latn-NE"], default_locale: "fr-NE", currency: "XOF", latitude: "17.607789", longitude: "8.081666", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Niamey", emoji: "🇳🇪", emojiU: "U+1F1F3 U+1F1EA", fips: "NG", internet: "NE", continent: "Africa", region: "Western Africa" }, { name: "Nigeria", alpha2: "NG", alpha3: "NGA", numeric: "566", locales: ["ha-Latn-NG", "ig-NG", "yo-NG"], default_locale: "ha-Latn-NG", currency: "NGN", latitude: "9.081999", longitude: "8.675277", currency_name: "Naira", languages: ["en"], capital: "Abuja", emoji: "🇳🇬", emojiU: "U+1F1F3 U+1F1EC", fips: "NI", internet: "NG", continent: "Africa", region: "Western Africa" }, { name: "Niue", alpha2: "NU", alpha3: "NIU", numeric: "570", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-19.054445", longitude: "-169.867233", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Alofi", emoji: "🇳🇺", emojiU: "U+1F1F3 U+1F1FA", fips: "NE", internet: "NU", continent: "Oceania", region: "Pacific" }, { name: "Norfolk Island", alpha2: "NF", alpha3: "NFK", numeric: "574", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-29.040835", longitude: "167.954712", currency_name: "Australian Dollar", languages: ["en"], capital: "Kingston", emoji: "🇳🇫", emojiU: "U+1F1F3 U+1F1EB", fips: "NF", internet: "NF", continent: "Oceania", region: "Pacific" }, { name: "North Macedonia", alpha2: "MK", alpha3: "MKD", numeric: "807", locales: ["mk-MK"], default_locale: "mk-MK", currency: "MKD", latitude: "41.608635", longitude: "21.745275", currency_name: "Denar", languages: ["mk"], capital: "Skopje", emoji: "🇲🇰", emojiU: "U+1F1F2 U+1F1F0", fips: "MK", internet: "MK", continent: "Europe", region: "South East Europe" }, { name: "Northern Mariana Islands", alpha2: "MP", alpha3: "MNP", numeric: "580", locales: ["en-MP"], default_locale: "en-MP", currency: "USD", latitude: "17.33083", longitude: "145.38469", currency_name: "US Dollar", languages: ["en", "ch"], capital: "Saipan", emoji: "🇲🇵", emojiU: "U+1F1F2 U+1F1F5", fips: "CQ", internet: "MP", continent: "Oceania", region: "Pacific" }, { name: "Norway", alpha2: "NO", alpha3: "NOR", numeric: "578", locales: ["nb-NO", "nn-NO"], default_locale: "nb-NO", currency: "NOK", latitude: "60.472024", longitude: "8.468946", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "Oslo", emoji: "🇳🇴", emojiU: "U+1F1F3 U+1F1F4", fips: "NO", internet: "NO", continent: "Europe", region: "Northern Europe" }, { name: "Oman", alpha2: "OM", alpha3: "OMN", numeric: "512", locales: ["ar-OM"], default_locale: "ar-OM", currency: "OMR", latitude: "21.512583", longitude: "55.923255", currency_name: "Rial Omani", languages: ["ar"], capital: "Muscat", emoji: "🇴🇲", emojiU: "U+1F1F4 U+1F1F2", fips: "MU", internet: "OM", continent: "Asia", region: "South West Asia" }, { name: "Pakistan", alpha2: "PK", alpha3: "PAK", numeric: "586", locales: ["en-PK", "pa-Arab-PK", "ur-PK"], default_locale: "en-PK", currency: "PKR", latitude: "30.375321", longitude: "69.345116", currency_name: "Pakistan Rupee", languages: ["en", "ur"], capital: "Islamabad", emoji: "🇵🇰", emojiU: "U+1F1F5 U+1F1F0", fips: "PK", internet: "PK", continent: "Asia", region: "South Asia" }, { name: "Palau", alpha2: "PW", alpha3: "PLW", numeric: "585", locales: ["en"], default_locale: "en", currency: "USD", latitude: "7.51498", longitude: "134.58252", currency_name: "US Dollar", languages: ["en"], capital: "Ngerulmud", emoji: "🇵🇼", emojiU: "U+1F1F5 U+1F1FC", fips: "PS", internet: "PW", continent: "Oceania", region: "Pacific" }, { name: "Palestine", alpha2: "PS", alpha3: "PSE", numeric: "275", locales: ["ar"], default_locale: "ar", currency: "USD", latitude: "31.952162", longitude: "35.233154", currency_name: "US Dollar", languages: ["ar"], capital: "Ramallah", emoji: "🇵🇸", emojiU: "U+1F1F5 U+1F1F8", fips: "WE", internet: "PS", continent: "Asia", region: "South West Asia", alternate_names: ["State of Palestine"] }, { name: "Panama", alpha2: "PA", alpha3: "PAN", numeric: "591", locales: ["es-PA"], default_locale: "es-PA", currency: "USD", latitude: "8.537981", longitude: "-80.782127", currency_name: "US Dollar", languages: ["es"], capital: "Panama City", emoji: "🇵🇦", emojiU: "U+1F1F5 U+1F1E6", fips: "PM", internet: "PA", continent: "Americas", region: "Central America" }, { name: "Papua New Guinea", alpha2: "PG", alpha3: "PNG", numeric: "598", locales: ["en"], default_locale: "en", currency: "PGK", latitude: "-6.314993", longitude: "143.95555", currency_name: "Kina", languages: ["en"], capital: "Port Moresby", emoji: "🇵🇬", emojiU: "U+1F1F5 U+1F1EC", fips: "PP", internet: "PG", continent: "Oceania", region: "Pacific" }, { name: "Paraguay", alpha2: "PY", alpha3: "PRY", numeric: "600", locales: ["es-PY"], default_locale: "es-PY", currency: "PYG", latitude: "-23.442503", longitude: "-58.443832", currency_name: "Guarani", languages: ["es", "gn"], capital: "Asunción", emoji: "🇵🇾", emojiU: "U+1F1F5 U+1F1FE", fips: "PA", internet: "PY", continent: "Americas", region: "South America" }, { name: "Peru", alpha2: "PE", alpha3: "PER", numeric: "604", locales: ["es-PE"], default_locale: "es-PE", currency: "PEN", latitude: "-9.189967", longitude: "-75.015152", currency_name: "Sol", languages: ["es"], capital: "Lima", emoji: "🇵🇪", emojiU: "U+1F1F5 U+1F1EA", fips: "PE", internet: "PE", continent: "Americas", region: "South America" }, { name: "Philippines", alpha2: "PH", alpha3: "PHL", numeric: "608", locales: ["en-PH", "fil-PH"], default_locale: "en-PH", currency: "PHP", latitude: "12.879721", longitude: "121.774017", currency_name: "Philippine Peso", languages: ["en"], capital: "Manila", emoji: "🇵🇭", emojiU: "U+1F1F5 U+1F1ED", fips: "RP", internet: "PH", continent: "Asia", region: "South East Asia" }, { name: "Pitcairn", alpha2: "PN", alpha3: "PCN", numeric: "612", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-24.703615", longitude: "-127.439308", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Adamstown", emoji: "🇵🇳", emojiU: "U+1F1F5 U+1F1F3", fips: "PC", internet: "PN", continent: "Oceania", region: "Pacific" }, { name: "Poland", alpha2: "PL", alpha3: "POL", numeric: "616", locales: ["pl-PL"], default_locale: "pl-PL", currency: "PLN", latitude: "51.919438", longitude: "19.145136", currency_name: "Zloty", languages: ["pl"], capital: "Warsaw", emoji: "🇵🇱", emojiU: "U+1F1F5 U+1F1F1", fips: "PL", internet: "PL", continent: "Europe", region: "Eastern Europe" }, { name: "Portugal", alpha2: "PT", alpha3: "PRT", numeric: "620", locales: ["pt-PT"], default_locale: "pt-PT", currency: "EUR", latitude: "39.399872", longitude: "-8.224454", currency_name: "Euro", languages: ["pt"], capital: "Lisbon", emoji: "🇵🇹", emojiU: "U+1F1F5 U+1F1F9", fips: "PO", internet: "PT", continent: "Europe", region: "South West Europe" }, { name: "Puerto Rico", alpha2: "PR", alpha3: "PRI", numeric: "630", locales: ["es-PR"], default_locale: "es-PR", currency: "USD", latitude: "18.220833", longitude: "-66.590149", currency_name: "US Dollar", languages: ["es", "en"], capital: "San Juan", emoji: "🇵🇷", emojiU: "U+1F1F5 U+1F1F7", fips: "RQ", internet: "PR", continent: "Americas", region: "West Indies" }, { name: "Qatar", alpha2: "QA", alpha3: "QAT", numeric: "634", locales: ["ar-QA"], default_locale: "ar-QA", currency: "QAR", latitude: "25.354826", longitude: "51.183884", currency_name: "Qatari Rial", languages: ["ar"], capital: "Doha", emoji: "🇶🇦", emojiU: "U+1F1F6 U+1F1E6", fips: "QA", internet: "QA", continent: "Asia", region: "South West Asia" }, { name: "Romania", alpha2: "RO", alpha3: "ROU", numeric: "642", locales: ["ro-RO"], default_locale: "ro-RO", currency: "RON", latitude: "45.943161", longitude: "24.96676", currency_name: "Romanian Leu", languages: ["ro"], capital: "Bucharest", emoji: "🇷🇴", emojiU: "U+1F1F7 U+1F1F4", fips: "RO", internet: "RO", continent: "Europe", region: "South East Europe" }, { name: "Russia", alpha2: "RU", alpha3: "RUS", numeric: "643", locales: ["ru-RU"], default_locale: "ru-RU", currency: "RUB", latitude: "61.52401", longitude: "105.318756", currency_name: "Russian Ruble", languages: ["ru"], capital: "Moscow", emoji: "🇷🇺", emojiU: "U+1F1F7 U+1F1FA", fips: "RS", internet: "RU", continent: "Asia", region: "Northern Asia", alternate_names: ["Russian Federation"] }, { name: "Rwanda", alpha2: "RW", alpha3: "RWA", numeric: "646", locales: ["fr-RW", "rw-RW"], default_locale: "fr-RW", currency: "RWF", latitude: "-1.940278", longitude: "29.873888", currency_name: "Rwanda Franc", languages: ["rw", "en", "fr"], capital: "Kigali", emoji: "🇷🇼", emojiU: "U+1F1F7 U+1F1FC", fips: "RW", internet: "RW", continent: "Africa", region: "Central Africa" }, { name: "Réunion", alpha2: "RE", alpha3: "REU", numeric: "638", locales: ["fr-RE"], default_locale: "fr-RE", currency: "RWF", latitude: "-21.115141", longitude: "55.536384", currency_name: "Rwanda Franc", languages: ["fr"], capital: "Saint-Denis", emoji: "🇷🇪", emojiU: "U+1F1F7 U+1F1EA", fips: "RE", internet: "RE", continent: "Africa", region: "Indian Ocean" }, { name: "Saint Barthélemy", alpha2: "BL", alpha3: "BLM", numeric: "652", locales: ["fr-BL"], default_locale: "fr-BL", currency: "EUR", currency_name: "Euro", languages: ["fr"], capital: "Gustavia", emoji: "🇧🇱", emojiU: "U+1F1E7 U+1F1F1", fips: "TB", internet: "BL", continent: "Americas", region: "West Indies" }, { name: "Saint Helena", alpha2: "SH", alpha3: "SHN", numeric: "654", locales: ["en"], default_locale: "en", currency: "SHP", latitude: "-24.143474", longitude: "-10.030696", currency_name: "Saint Helena Pound", languages: ["en"], capital: "Jamestown", emoji: "🇸🇭", emojiU: "U+1F1F8 U+1F1ED", fips: "SH", internet: "SH", continent: "Atlantic Ocean", region: "South Atlantic Ocean", alternate_names: ["Saint Helena, Ascension and Tristan da Cunha"] }, { name: "Saint Kitts and Nevis", alpha2: "KN", alpha3: "KNA", numeric: "659", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.357822", longitude: "-62.782998", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Basseterre", emoji: "🇰🇳", emojiU: "U+1F1F0 U+1F1F3", fips: "SC", internet: "KN", continent: "Americas", region: "West Indies" }, { name: "Saint Lucia", alpha2: "LC", alpha3: "LCA", numeric: "662", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "13.909444", longitude: "-60.978893", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Castries", emoji: "🇱🇨", emojiU: "U+1F1F1 U+1F1E8", fips: "ST", internet: "LC", continent: "Americas", region: "West Indies" }, { name: "Saint Martin", alpha2: "MF", alpha3: "MAF", numeric: "663", locales: ["fr-MF"], default_locale: "fr-MF", currency: "EUR", currency_name: "Euro", languages: ["en", "fr", "nl"], capital: "Marigot", emoji: "🇲🇫", emojiU: "U+1F1F2 U+1F1EB", fips: "RN", internet: "MF", continent: "Americas", region: "West Indies", alternate_names: ["Saint Martin French part"] }, { name: "Saint Pierre and Miquelon", alpha2: "PM", alpha3: "SPM", numeric: "666", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "46.941936", longitude: "-56.27111", currency_name: "Euro", languages: ["fr"], capital: "Saint-Pierre", emoji: "🇵🇲", emojiU: "U+1F1F5 U+1F1F2", fips: "SB", internet: "PM", continent: "Americas", region: "North America" }, { name: "Saint Vincent and the Grenadines", alpha2: "VC", alpha3: "VCT", numeric: "670", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.984305", longitude: "-61.287228", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Kingstown", emoji: "🇻🇨", emojiU: "U+1F1FB U+1F1E8", fips: "VC", internet: "VC", continent: "Americas", region: "West Indies" }, { name: "Samoa", alpha2: "WS", alpha3: "WSM", numeric: "882", locales: ["sm"], default_locale: "sm", currency: "WST", latitude: "-13.759029", longitude: "-172.104629", currency_name: "Tala", languages: ["sm", "en"], capital: "Apia", emoji: "🇼🇸", emojiU: "U+1F1FC U+1F1F8", fips: "WS", internet: "WS", continent: "Oceania", region: "Pacific" }, { name: "San Marino", alpha2: "SM", alpha3: "SMR", numeric: "674", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "43.94236", longitude: "12.457777", currency_name: "Euro", languages: ["it"], capital: "City of San Marino", emoji: "🇸🇲", emojiU: "U+1F1F8 U+1F1F2", fips: "SM", internet: "SM", continent: "Europe", region: "Southern Europe" }, { name: "Sao Tome and Principe", alpha2: "ST", alpha3: "STP", numeric: "678", locales: ["pt"], default_locale: "pt", currency: "STN", latitude: "0.18636", longitude: "6.613081", currency_name: "Dobra", languages: ["pt"], capital: "São Tomé", emoji: "🇸🇹", emojiU: "U+1F1F8 U+1F1F9", fips: "TP", internet: "ST", continent: "Africa", region: "Western Africa" }, { name: "Saudi Arabia", alpha2: "SA", alpha3: "SAU", numeric: "682", locales: ["ar-SA"], default_locale: "ar-SA", currency: "SAR", latitude: "23.885942", longitude: "45.079162", currency_name: "Saudi Riyal", languages: ["ar"], capital: "Riyadh", emoji: "🇸🇦", emojiU: "U+1F1F8 U+1F1E6", fips: "SA", internet: "SA", continent: "Asia", region: "South West Asia" }, { name: "Senegal", alpha2: "SN", alpha3: "SEN", numeric: "686", locales: ["fr-SN", "ff-SN"], default_locale: "fr-SN", currency: "XOF", latitude: "14.497401", longitude: "-14.452362", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Dakar", emoji: "🇸🇳", emojiU: "U+1F1F8 U+1F1F3", fips: "SG", internet: "SN", continent: "Africa", region: "Western Africa" }, { name: "Serbia", alpha2: "RS", alpha3: "SRB", numeric: "688", locales: ["sr-Cyrl-RS", "sr-Latn-RS"], default_locale: "sr-Cyrl-RS", currency: "RSD", latitude: "44.016521", longitude: "21.005859", currency_name: "Serbian Dinar", languages: ["sr"], capital: "Belgrade", emoji: "🇷🇸", emojiU: "U+1F1F7 U+1F1F8", fips: "RI", internet: "RS", continent: "Europe", region: "South East Europe" }, { name: "Seychelles", alpha2: "SC", alpha3: "SYC", numeric: "690", locales: ["fr"], default_locale: "fr", currency: "SCR", latitude: "-4.679574", longitude: "55.491977", currency_name: "Seychelles Rupee", languages: ["fr", "en"], capital: "Victoria", emoji: "🇸🇨", emojiU: "U+1F1F8 U+1F1E8", fips: "SE", internet: "SC", continent: "Africa", region: "Indian Ocean" }, { name: "Sierra Leone", alpha2: "SL", alpha3: "SLE", numeric: "694", locales: ["en"], default_locale: "en", currency: "SLL", latitude: "8.460555", longitude: "-11.779889", currency_name: "Leone", languages: ["en"], capital: "Freetown", emoji: "🇸🇱", emojiU: "U+1F1F8 U+1F1F1", fips: "SL", internet: "SL", continent: "Africa", region: "Western Africa" }, { name: "Singapore", alpha2: "SG", alpha3: "SGP", numeric: "702", locales: ["zh-Hans-SG", "en-SG"], default_locale: "en-SG", currency: "SGD", latitude: "1.352083", longitude: "103.819836", currency_name: "Singapore Dollar", languages: ["en", "ms", "ta", "zh"], capital: "Singapore", emoji: "🇸🇬", emojiU: "U+1F1F8 U+1F1EC", fips: "SN", internet: "SG", continent: "Asia", region: "South East Asia" }, { name: "Sint Maarten", alpha2: "SX", alpha3: "SXM", numeric: "534", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "en"], capital: "Philipsburg", emoji: "🇸🇽", emojiU: "U+1F1F8 U+1F1FD", fips: "NN", internet: "SX", continent: "Americas", region: "West Indies", alternate_names: ["Sint Maarten Dutch part"] }, { name: "Slovakia", alpha2: "SK", alpha3: "SVK", numeric: "703", locales: ["sk-SK"], default_locale: "sk-SK", currency: "EUR", latitude: "48.669026", longitude: "19.699024", currency_name: "Euro", languages: ["sk"], capital: "Bratislava", emoji: "🇸🇰", emojiU: "U+1F1F8 U+1F1F0", fips: "LO", internet: "SK", continent: "Europe", region: "Central Europe" }, { name: "Slovenia", alpha2: "SI", alpha3: "SVN", numeric: "705", locales: ["sl-SI"], default_locale: "sl-SI", currency: "EUR", latitude: "46.151241", longitude: "14.995463", currency_name: "Euro", languages: ["sl"], capital: "Ljubljana", emoji: "🇸🇮", emojiU: "U+1F1F8 U+1F1EE", fips: "SI", internet: "SI", continent: "Europe", region: "South East Europe" }, { name: "Solomon Islands", alpha2: "SB", alpha3: "SLB", numeric: "090", locales: ["en"], default_locale: "en", currency: "SBD", latitude: "-9.64571", longitude: "160.156194", currency_name: "Solomon Islands Dollar", languages: ["en"], capital: "Honiara", emoji: "🇸🇧", emojiU: "U+1F1F8 U+1F1E7", fips: "BP", internet: "SB", continent: "Oceania", region: "Pacific" }, { name: "Somalia", alpha2: "SO", alpha3: "SOM", numeric: "706", locales: ["so-SO"], default_locale: "so-SO", currency: "SOS", latitude: "5.152149", longitude: "46.199616", currency_name: "Somali Shilling", languages: ["so", "ar"], capital: "Mogadishu", emoji: "🇸🇴", emojiU: "U+1F1F8 U+1F1F4", fips: "SO", internet: "SO", continent: "Africa", region: "Eastern Africa" }, { name: "South Africa", alpha2: "ZA", alpha3: "ZAF", numeric: "710", locales: ["af-ZA", "en-ZA", "zu-ZA"], default_locale: "af-ZA", currency: "ZAR", latitude: "-30.559482", longitude: "22.937506", currency_name: "Rand", languages: ["af", "en", "nr", "st", "ss", "tn", "ts", "ve", "xh", "zu"], capital: "Pretoria", emoji: "🇿🇦", emojiU: "U+1F1FF U+1F1E6", fips: "SF", internet: "ZA", continent: "Africa", region: "Southern Africa" }, { name: "South Georgia and the South Sandwich Islands", alpha2: "GS", alpha3: "SGS", numeric: "239", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-54.429579", longitude: "-36.587909", currency_name: "US Dollar", languages: ["en"], capital: "King Edward Point", emoji: "🇬🇸", emojiU: "U+1F1EC U+1F1F8", fips: "SX", internet: "GS", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "South Sudan", alpha2: "SS", alpha3: "SSD", numeric: "728", locales: ["en"], default_locale: "en", currency: "SSP", currency_name: "South Sudanese Pound", languages: ["en"], capital: "Juba", emoji: "🇸🇸", emojiU: "U+1F1F8 U+1F1F8", fips: "OD", internet: "SS", continent: "Africa", region: "Northern Africa" }, { name: "Spain", alpha2: "ES", alpha3: "ESP", numeric: "724", locales: ["eu-ES", "ca-ES", "gl-ES", "es-ES"], default_locale: "es-ES", currency: "EUR", latitude: "40.463667", longitude: "-3.74922", currency_name: "Euro", languages: ["es", "eu", "ca", "gl", "oc"], capital: "Madrid", emoji: "🇪🇸", emojiU: "U+1F1EA U+1F1F8", fips: "SP", internet: "ES", continent: "Europe", region: "South West Europe" }, { name: "Sri Lanka", alpha2: "LK", alpha3: "LKA", numeric: "144", locales: ["si-LK", "ta-LK"], default_locale: "si-LK", currency: "LKR", latitude: "7.873054", longitude: "80.771797", currency_name: "Sri Lanka Rupee", languages: ["si", "ta"], capital: "Colombo", emoji: "🇱🇰", emojiU: "U+1F1F1 U+1F1F0", fips: "CE", internet: "LK", continent: "Asia", region: "South Asia" }, { name: "Sudan", alpha2: "SD", alpha3: "SDN", numeric: "729", locales: ["ar-SD"], default_locale: "ar-SD", currency: "SDG", latitude: "12.862807", longitude: "30.217636", currency_name: "Sudanese Pound", languages: ["ar", "en"], capital: "Khartoum", emoji: "🇸🇩", emojiU: "U+1F1F8 U+1F1E9", fips: "SU", internet: "SD", continent: "Africa", region: "Northern Africa" }, { name: "Suriname", alpha2: "SR", alpha3: "SUR", numeric: "740", locales: ["nl"], default_locale: "nl", currency: "SRD", latitude: "3.919305", longitude: "-56.027783", currency_name: "Surinam Dollar", languages: ["nl"], capital: "Paramaribo", emoji: "🇸🇷", emojiU: "U+1F1F8 U+1F1F7", fips: "NS", internet: "SR", continent: "Americas", region: "South America" }, { name: "Svalbard and Jan Mayen", alpha2: "SJ", alpha3: "SJM", numeric: "744", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "77.553604", longitude: "23.670272", currency_name: "Norwegian Krone", languages: ["no"], capital: "Longyearbyen", emoji: "🇸🇯", emojiU: "U+1F1F8 U+1F1EF", fips: "SV", internet: "SJ", continent: "Europe", region: "Northern Europe" }, { name: "Sweden", alpha2: "SE", alpha3: "SWE", numeric: "752", locales: ["sv-SE"], default_locale: "sv-SE", currency: "SEK", latitude: "60.128161", longitude: "18.643501", currency_name: "Swedish Krona", languages: ["sv"], capital: "Stockholm", emoji: "🇸🇪", emojiU: "U+1F1F8 U+1F1EA", fips: "SW", internet: "SE", continent: "Europe", region: "Northern Europe" }, { name: "Switzerland", alpha2: "CH", alpha3: "CHE", numeric: "756", locales: ["fr-CH", "de-CH", "it-CH", "rm-CH", "gsw-CH"], default_locale: "fr-CH", currency: "CHF", latitude: "46.818188", longitude: "8.227512", currency_name: "Swiss Franc", languages: ["de", "fr", "it"], capital: "Bern", emoji: "🇨🇭", emojiU: "U+1F1E8 U+1F1ED", fips: "SZ", internet: "CH", continent: "Europe", region: "Central Europe" }, { name: "Syrian Arab Republic", alpha2: "SY", alpha3: "SYR", numeric: "760", locales: ["ar-SY"], default_locale: "ar-SY", currency: "SYP", latitude: "34.802075", longitude: "38.996815", currency_name: "Syrian Pound", languages: ["ar"], capital: "Damascus", emoji: "🇸🇾", emojiU: "U+1F1F8 U+1F1FE", fips: "SY", internet: "SY", continent: "Asia", region: "South West Asia" }, { name: "Taiwan", alpha2: "TW", alpha3: "TWN", numeric: "158", locales: ["zh-Hant-TW"], default_locale: "zh-Hant-TW", currency: "TWD", latitude: "23.69781", longitude: "120.960515", currency_name: "New Taiwan Dollar", languages: ["zh"], capital: "Taipei", emoji: "🇹🇼", emojiU: "U+1F1F9 U+1F1FC", fips: "TW", internet: "TW", continent: "Asia", region: "East Asia", alternate_names: ["Province of China Taiwan"] }, { name: "Tajikistan", alpha2: "TJ", alpha3: "TJK", numeric: "762", locales: ["tg"], default_locale: "tg", currency: "TJS", latitude: "38.861034", longitude: "71.276093", currency_name: "Somoni", languages: ["tg", "ru"], capital: "Dushanbe", emoji: "🇹🇯", emojiU: "U+1F1F9 U+1F1EF", fips: "TI", internet: "TJ", continent: "Asia", region: "Central Asia" }, { name: "Tanzania", alpha2: "TZ", alpha3: "TZA", numeric: "834", locales: ["asa-TZ", "bez-TZ", "lag-TZ", "jmc-TZ", "kde-TZ", "mas-TZ", "rof-TZ", "rwk-TZ", "sw-TZ", "vun-TZ"], default_locale: "asa-TZ", currency: "TZS", latitude: "-6.369028", longitude: "34.888822", currency_name: "Tanzanian Shilling", languages: ["sw", "en"], capital: "Dodoma", emoji: "🇹🇿", emojiU: "U+1F1F9 U+1F1FF", fips: "TZ", internet: "TZ", continent: "Africa", region: "Eastern Africa", alternate_names: ["United Republic of Tanzania"] }, { name: "Thailand", alpha2: "TH", alpha3: "THA", numeric: "764", locales: ["th-TH"], default_locale: "th-TH", currency: "THB", latitude: "15.870032", longitude: "100.992541", currency_name: "Baht", languages: ["th"], capital: "Bangkok", emoji: "🇹🇭", emojiU: "U+1F1F9 U+1F1ED", fips: "TH", internet: "TH", continent: "Asia", region: "South East Asia" }, { name: "Timor-Leste", alpha2: "TL", alpha3: "TLS", numeric: "626", locales: ["pt"], default_locale: "pt", currency: "USD", latitude: "-8.874217", longitude: "125.727539", currency_name: "US Dollar", languages: ["pt"], capital: "Dili", emoji: "🇹🇱", emojiU: "U+1F1F9 U+1F1F1", fips: "TT", internet: "TL", continent: "Asia", region: "South East Asia" }, { name: "Togo", alpha2: "TG", alpha3: "TGO", numeric: "768", locales: ["ee-TG", "fr-TG"], default_locale: "fr-TG", currency: "XOF", latitude: "8.619543", longitude: "0.824782", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Lomé", emoji: "🇹🇬", emojiU: "U+1F1F9 U+1F1EC", fips: "TO", internet: "TG", continent: "Africa", region: "Western Africa" }, { name: "Tokelau", alpha2: "TK", alpha3: "TKL", numeric: "772", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-8.967363", longitude: "-171.855881", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Fakaofo", emoji: "🇹🇰", emojiU: "U+1F1F9 U+1F1F0", fips: "TL", internet: "TK", continent: "Oceania", region: "Pacific" }, { name: "Tonga", alpha2: "TO", alpha3: "TON", numeric: "776", locales: ["to-TO"], default_locale: "to-TO", currency: "TOP", latitude: "-21.178986", longitude: "-175.198242", currency_name: "Pa’anga", languages: ["en", "to"], capital: "Nuku'alofa", emoji: "🇹🇴", emojiU: "U+1F1F9 U+1F1F4", fips: "TN", internet: "TO", continent: "Oceania", region: "Pacific" }, { name: "Trinidad and Tobago", alpha2: "TT", alpha3: "TTO", numeric: "780", locales: ["en-TT"], default_locale: "en-TT", currency: "TTD", latitude: "10.691803", longitude: "-61.222503", currency_name: "Trinidad and Tobago Dollar", languages: ["en"], capital: "Port of Spain", emoji: "🇹🇹", emojiU: "U+1F1F9 U+1F1F9", fips: "TD", internet: "TT", continent: "Americas", region: "West Indies" }, { name: "Tunisia", alpha2: "TN", alpha3: "TUN", numeric: "788", locales: ["ar-TN"], default_locale: "ar-TN", currency: "TND", latitude: "33.886917", longitude: "9.537499", currency_name: "Tunisian Dinar", languages: ["ar"], capital: "Tunis", emoji: "🇹🇳", emojiU: "U+1F1F9 U+1F1F3", fips: "TS", internet: "TN", continent: "Africa", region: "Northern Africa" }, { name: "Turkey", alpha2: "TR", alpha3: "TUR", numeric: "792", locales: ["tr-TR"], default_locale: "tr-TR", currency: "TRY", latitude: "38.963745", longitude: "35.243322", currency_name: "Turkish Lira", languages: ["tr"], capital: "Ankara", emoji: "🇹🇷", emojiU: "U+1F1F9 U+1F1F7", fips: "TU", internet: "TR", continent: "Asia", region: "South West Asia" }, { name: "Turkmenistan", alpha2: "TM", alpha3: "TKM", numeric: "795", locales: ["tk"], default_locale: "tk", currency: "TMT", latitude: "38.969719", longitude: "59.556278", currency_name: "Turkmenistan New Manat", languages: ["tk", "ru"], capital: "Ashgabat", emoji: "🇹🇲", emojiU: "U+1F1F9 U+1F1F2", fips: "TX", internet: "TM", continent: "Asia", region: "Central Asia" }, { name: "Turks and Caicos Islands", alpha2: "TC", alpha3: "TCA", numeric: "796", locales: ["en"], default_locale: "en", currency: "USD", latitude: "21.694025", longitude: "-71.797928", currency_name: "US Dollar", languages: ["en"], capital: "Cockburn Town", emoji: "🇹🇨", emojiU: "U+1F1F9 U+1F1E8", fips: "TK", internet: "TC", continent: "Americas", region: "West Indies" }, { name: "Tuvalu", alpha2: "TV", alpha3: "TUV", numeric: "798", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-7.109535", longitude: "177.64933", currency_name: "Australian Dollar", languages: ["en"], capital: "Funafuti", emoji: "🇹🇻", emojiU: "U+1F1F9 U+1F1FB", fips: "TV", internet: "TV", continent: "Oceania", region: "Pacific" }, { name: "Uganda", alpha2: "UG", alpha3: "UGA", numeric: "800", locales: ["cgg-UG", "lg-UG", "nyn-UG", "xog-UG", "teo-UG"], default_locale: "cgg-UG", currency: "UGX", latitude: "1.373333", longitude: "32.290275", currency_name: "Uganda Shilling", languages: ["en", "sw"], capital: "Kampala", emoji: "🇺🇬", emojiU: "U+1F1FA U+1F1EC", fips: "UG", internet: "UG", continent: "Africa", region: "Eastern Africa" }, { name: "Ukraine", alpha2: "UA", alpha3: "UKR", numeric: "804", locales: ["ru-UA", "uk-UA"], default_locale: "uk-UA", currency: "UAH", latitude: "48.379433", longitude: "31.16558", currency_name: "Hryvnia", languages: ["uk"], capital: "Kyiv", emoji: "🇺🇦", emojiU: "U+1F1FA U+1F1E6", fips: "UP", internet: "UA", continent: "Europe", region: "Eastern Europe" }, { name: "United Arab Emirates", alpha2: "AE", alpha3: "ARE", numeric: "784", locales: ["ar-AE"], default_locale: "ar-AE", currency: "AED", latitude: "23.424076", longitude: "53.847818", currency_name: "UAE Dirham", languages: ["ar"], capital: "Abu Dhabi", emoji: "🇦🇪", emojiU: "U+1F1E6 U+1F1EA", fips: "TC", internet: "AE", continent: "Asia", region: "South West Asia" }, { name: "United Kingdom", alpha2: "GB", alpha3: "GBR", numeric: "826", locales: ["kw-GB", "en-GB", "gv-GB", "cy-GB"], default_locale: "en-GB", currency: "GBP", latitude: "55.378051", longitude: "-3.435973", currency_name: "Pound Sterling", languages: ["en"], capital: "London", emoji: "🇬🇧", emojiU: "U+1F1EC U+1F1E7", fips: "UK", internet: "UK", continent: "Europe", region: "Western Europe", alternate_names: ["United Kingdom of Great Britain and Northern Ireland"] }, { name: "United States Minor Outlying Islands", alpha2: "UM", alpha3: "UMI", numeric: "581", locales: ["en-UM"], default_locale: "en-UM", currency: "USD", currency_name: "US Dollar", languages: ["en"], capital: "", emoji: "🇺🇲", emojiU: "U+1F1FA U+1F1F2", fips: "", internet: "US", continent: "Americas", region: "North America" }, { name: "United States of America", alpha2: "US", alpha3: "USA", numeric: "840", locales: ["chr-US", "en-US", "haw-US", "es-US"], default_locale: "en-US", currency: "USD", latitude: "37.09024", longitude: "-95.712891", currency_name: "US Dollar", languages: ["en"], capital: "Washington D.C.", emoji: "🇺🇸", emojiU: "U+1F1FA U+1F1F8", fips: "US", internet: "US", continent: "Americas", region: "North America", alternate_names: ["United States"] }, { name: "Uruguay", alpha2: "UY", alpha3: "URY", numeric: "858", locales: ["es-UY"], default_locale: "es-UY", currency: "UYU", latitude: "-32.522779", longitude: "-55.765835", currency_name: "Peso Uruguayo", languages: ["es"], capital: "Montevideo", emoji: "🇺🇾", emojiU: "U+1F1FA U+1F1FE", fips: "UY", internet: "UY", continent: "Americas", region: "South America" }, { name: "Uzbekistan", alpha2: "UZ", alpha3: "UZB", numeric: "860", locales: ["uz-Cyrl-UZ", "uz-Latn-UZ"], default_locale: "uz-Cyrl-UZ", currency: "UZS", latitude: "41.377491", longitude: "64.585262", currency_name: "Uzbekistan Sum", languages: ["uz", "ru"], capital: "Tashkent", emoji: "🇺🇿", emojiU: "U+1F1FA U+1F1FF", fips: "UZ", internet: "UZ", continent: "Asia", region: "Central Asia" }, { name: "Vanuatu", alpha2: "VU", alpha3: "VUT", numeric: "548", locales: ["bi"], default_locale: "bi", currency: "VUV", latitude: "-15.376706", longitude: "166.959158", currency_name: "Vatu", languages: ["bi", "en", "fr"], capital: "Port Vila", emoji: "🇻🇺", emojiU: "U+1F1FB U+1F1FA", fips: "NH", internet: "VU", continent: "Oceania", region: "Pacific" }, { name: "Venezuela", alpha2: "VE", alpha3: "VEN", numeric: "862", locales: ["es-VE"], default_locale: "es-VE", currency: "VUV", latitude: "6.42375", longitude: "-66.58973", currency_name: "Vatu", languages: ["es"], capital: "Caracas", emoji: "🇻🇪", emojiU: "U+1F1FB U+1F1EA", fips: "VE", internet: "UE", continent: "Americas", region: "South America", alternate_names: ["Bolivarian Republic of Venezuela"] }, { name: "Viet Nam", alpha2: "VN", alpha3: "VNM", numeric: "704", locales: ["vi-VN"], default_locale: "vi-VN", currency: "VND", latitude: "14.058324", longitude: "108.277199", currency_name: "Dong", languages: ["vi"], capital: "Hanoi", emoji: "🇻🇳", emojiU: "U+1F1FB U+1F1F3", fips: "VN", internet: "VN", continent: "Asia", region: "South East Asia" }, { name: "Virgin Islands (British)", alpha2: "VG", alpha3: "VGB", numeric: "092", locales: ["en"], default_locale: "en", currency: "USD", latitude: "18.420695", longitude: "-64.639968", currency_name: "US Dollar", languages: ["en"], capital: "Road Town", emoji: "🇻🇬", emojiU: "U+1F1FB U+1F1EC", fips: "VI", internet: "VG", continent: "Americas", region: "West Indies" }, { name: "Virgin Islands (U.S.)", alpha2: "VI", alpha3: "VIR", numeric: "850", locales: ["en-VI"], default_locale: "en-VI", currency: "USD", latitude: "18.335765", longitude: "-64.896335", currency_name: "US Dollar", languages: ["en"], capital: "Charlotte Amalie", emoji: "🇻🇮", emojiU: "U+1F1FB U+1F1EE", fips: "VQ", internet: "VI", continent: "Americas", region: "West Indies" }, { name: "Wallis and Futuna", alpha2: "WF", alpha3: "WLF", numeric: "876", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-13.768752", longitude: "-177.156097", currency_name: "CFP Franc", languages: ["fr"], capital: "Mata-Utu", emoji: "🇼🇫", emojiU: "U+1F1FC U+1F1EB", fips: "WF", internet: "WF", continent: "Oceania", region: "Pacific" }, { name: "Western Sahara", alpha2: "EH", alpha3: "ESH", numeric: "732", locales: ["es"], default_locale: "es", currency: "MAD", latitude: "24.215527", longitude: "-12.885834", currency_name: "Moroccan Dirham", languages: ["es"], capital: "El Aaiún", emoji: "🇪🇭", emojiU: "U+1F1EA U+1F1ED", fips: "WI", internet: "EH", continent: "Africa", region: "Northern Africa" }, { name: "Yemen", alpha2: "YE", alpha3: "YEM", numeric: "887", locales: ["ar-YE"], default_locale: "ar-YE", currency: "YER", latitude: "15.552727", longitude: "48.516388", currency_name: "Yemeni Rial", languages: ["ar"], capital: "Sana'a", emoji: "🇾🇪", emojiU: "U+1F1FE U+1F1EA", fips: "YM", internet: "YE", continent: "Asia", region: "South West Asia" }, { name: "Zambia", alpha2: "ZM", alpha3: "ZMB", numeric: "894", locales: ["bem-ZM"], default_locale: "bem-ZM", currency: "ZMW", latitude: "-13.133897", longitude: "27.849332", currency_name: "Zambian Kwacha", languages: ["en"], capital: "Lusaka", emoji: "🇿🇲", emojiU: "U+1F1FF U+1F1F2", fips: "ZA", internet: "ZM", continent: "Africa", region: "Southern Africa" }, { name: "Zimbabwe", alpha2: "ZW", alpha3: "ZWE", numeric: "716", locales: ["en-ZW", "nd-ZW", "sn-ZW"], default_locale: "en-ZW", currency: "ZWL", latitude: "-19.015438", longitude: "29.154857", currency_name: "Zimbabwe Dollar", languages: ["en", "sn", "nd"], capital: "Harare", emoji: "🇿🇼", emojiU: "U+1F1FF U+1F1FC", fips: "ZI", internet: "ZW", continent: "Africa", region: "Southern Africa" }, { name: "Åland Islands", alpha2: "AX", alpha3: "ALA", numeric: "248", locales: ["sv"], default_locale: "sv", currency: "EUR", currency_name: "Euro", languages: ["sv"], capital: "Mariehamn", emoji: "🇦🇽", emojiU: "U+1F1E6 U+1F1FD", fips: "AX", internet: "AX", continent: "Europe", region: "Northern Europe" }, { name: "Kosovo", alpha2: "XK", alpha3: "XKX", numeric: "383", locales: ["sq"], default_locale: "sq", currency: "EUR", latitude: "42.602636", longitude: "20.902977", currency_name: "Euro", languages: ["sq", "sr"], capital: "Pristina", emoji: "🇽🇰", emojiU: "U+1F1FD U+1F1F0", fips: "KV", internet: "XK", continent: "Europe", region: "South East Europe" }]; + }); + + // node_modules/.bun/country-locale-map@1.9.11/node_modules/country-locale-map/countries.json + var require_countries = __commonJS((exports, module) => { + module.exports = [{ name: "Afghanistan", alpha2: "AF", alpha3: "AFG", numeric: "004", locales: ["ps_AF", "fa_AF", "uz_Arab_AF"], default_locale: "ps_AF", currency: "AFN", latitude: "33.93911", longitude: "67.709953", currency_name: "Afghani", languages: ["ps", "uz", "tk"], capital: "Kabul", emoji: "🇦🇫", emojiU: "U+1F1E6 U+1F1EB", fips: "AF", internet: "AF", continent: "Asia", region: "South Asia" }, { name: "Albania", alpha2: "AL", alpha3: "ALB", numeric: "008", locales: ["sq_AL"], default_locale: "sq_AL", currency: "ALL", latitude: "41.153332", longitude: "20.168331", currency_name: "Lek", languages: ["sq"], capital: "Tirana", emoji: "🇦🇱", emojiU: "U+1F1E6 U+1F1F1", fips: "AL", internet: "AL", continent: "Europe", region: "South East Europe" }, { name: "Algeria", alpha2: "DZ", alpha3: "DZA", numeric: "012", locales: ["ar_DZ", "kab_DZ"], default_locale: "ar_DZ", currency: "DZD", latitude: "28.033886", longitude: "1.659626", currency_name: "Algerian Dinar", languages: ["ar"], capital: "Algiers", emoji: "🇩🇿", emojiU: "U+1F1E9 U+1F1FF", fips: "AG", internet: "DZ", continent: "Africa", region: "Northern Africa" }, { name: "American Samoa", alpha2: "AS", alpha3: "ASM", numeric: "016", locales: ["en_AS"], default_locale: "en_AS", currency: "USD", latitude: "-14.270972", longitude: "-170.132217", currency_name: "US Dollar", languages: ["en", "sm"], capital: "Pago Pago", emoji: "🇦🇸", emojiU: "U+1F1E6 U+1F1F8", fips: "AQ", internet: "AS", continent: "Oceania", region: "Pacific" }, { name: "Andorra", alpha2: "AD", alpha3: "AND", numeric: "020", locales: ["ca"], default_locale: "ca", currency: "EUR", latitude: "42.546245", longitude: "1.601554", currency_name: "Euro", languages: ["ca"], capital: "Andorra la Vella", emoji: "🇦🇩", emojiU: "U+1F1E6 U+1F1E9", fips: "AN", internet: "AD", continent: "Europe", region: "South West Europe" }, { name: "Angola", alpha2: "AO", alpha3: "AGO", numeric: "024", locales: ["pt"], default_locale: "pt", currency: "AOA", latitude: "-11.202692", longitude: "17.873887", currency_name: "Kwanza", languages: ["pt"], capital: "Luanda", emoji: "🇦🇴", emojiU: "U+1F1E6 U+1F1F4", fips: "AO", internet: "AO", continent: "Africa", region: "Southern Africa" }, { name: "Anguilla", alpha2: "AI", alpha3: "AIA", numeric: "660", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "18.220554", longitude: "-63.068615", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "The Valley", emoji: "🇦🇮", emojiU: "U+1F1E6 U+1F1EE", fips: "AV", internet: "AI", continent: "Americas", region: "West Indies" }, { name: "Antarctica", alpha2: "AQ", alpha3: "ATA", numeric: "010", locales: ["en_US"], default_locale: "en_US", currency: "USD", latitude: "-75.250973", longitude: "-0.071389", currency_name: "US Dollar", languages: [], capital: "", emoji: "🇦🇶", emojiU: "U+1F1E6 U+1F1F6", fips: "AY", internet: "AQ", continent: "Antarctica", region: "Antarctica" }, { name: "Antigua and Barbuda", alpha2: "AG", alpha3: "ATG", numeric: "028", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.060816", longitude: "-61.796428", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Saint John's", emoji: "🇦🇬", emojiU: "U+1F1E6 U+1F1EC", fips: "AC", internet: "AG", continent: "Americas", region: "West Indies" }, { name: "Argentina", alpha2: "AR", alpha3: "ARG", numeric: "032", locales: ["es_AR"], default_locale: "es_AR", currency: "ARS", latitude: "-38.416097", longitude: "-63.616672", currency_name: "Argentine Peso", languages: ["es", "gn"], capital: "Buenos Aires", emoji: "🇦🇷", emojiU: "U+1F1E6 U+1F1F7", fips: "AR", internet: "AR", continent: "Americas", region: "South America" }, { name: "Armenia", alpha2: "AM", alpha3: "ARM", numeric: "051", locales: ["hy_AM"], default_locale: "hy_AM", currency: "AMD", latitude: "40.069099", longitude: "45.038189", currency_name: "Armenian Dram", languages: ["hy", "ru"], capital: "Yerevan", emoji: "🇦🇲", emojiU: "U+1F1E6 U+1F1F2", fips: "AM", internet: "AM", continent: "Asia", region: "South West Asia" }, { name: "Aruba", alpha2: "AW", alpha3: "ABW", numeric: "533", locales: ["nl"], default_locale: "nl", currency: "AWG", latitude: "12.52111", longitude: "-69.968338", currency_name: "Aruban Florin", languages: ["nl", "pa"], capital: "Oranjestad", emoji: "🇦🇼", emojiU: "U+1F1E6 U+1F1FC", fips: "AA", internet: "AW", continent: "Americas", region: "West Indies" }, { name: "Australia", alpha2: "AU", alpha3: "AUS", numeric: "036", locales: ["en_AU"], default_locale: "en_AU", currency: "AUD", latitude: "-25.274398", longitude: "133.775136", currency_name: "Australian Dollar", languages: ["en"], capital: "Canberra", emoji: "🇦🇺", emojiU: "U+1F1E6 U+1F1FA", fips: "AS", internet: "AU", continent: "Oceania", region: "Pacific" }, { name: "Austria", alpha2: "AT", alpha3: "AUT", numeric: "040", locales: ["de_AT"], default_locale: "de_AT", currency: "EUR", latitude: "47.516231", longitude: "14.550072", currency_name: "Euro", languages: ["de"], capital: "Vienna", emoji: "🇦🇹", emojiU: "U+1F1E6 U+1F1F9", fips: "AU", internet: "AT", continent: "Europe", region: "Central Europe" }, { name: "Azerbaijan", alpha2: "AZ", alpha3: "AZE", numeric: "031", locales: ["az_Cyrl_AZ", "az_Latn_AZ"], default_locale: "az_Cyrl_AZ", currency: "AZN", latitude: "40.143105", longitude: "47.576927", currency_name: "Azerbaijan Manat", languages: ["az"], capital: "Baku", emoji: "🇦🇿", emojiU: "U+1F1E6 U+1F1FF", fips: "AJ", internet: "AZ", continent: "Asia", region: "South West Asia" }, { name: "Bahamas", alpha2: "BS", alpha3: "BHS", numeric: "044", locales: ["en"], default_locale: "en", currency: "BSD", latitude: "25.03428", longitude: "-77.39628", currency_name: "Bahamian Dollar", languages: ["en"], capital: "Nassau", emoji: "🇧🇸", emojiU: "U+1F1E7 U+1F1F8", fips: "BF", internet: "BS", continent: "Americas", region: "West Indies" }, { name: "Bahrain", alpha2: "BH", alpha3: "BHR", numeric: "048", locales: ["ar_BH"], default_locale: "ar_BH", currency: "BHD", latitude: "25.930414", longitude: "50.637772", currency_name: "Bahraini Dinar", languages: ["ar"], capital: "Manama", emoji: "🇧🇭", emojiU: "U+1F1E7 U+1F1ED", fips: "BA", internet: "BH", continent: "Asia", region: "South West Asia" }, { name: "Bangladesh", alpha2: "BD", alpha3: "BGD", numeric: "050", locales: ["bn_BD"], default_locale: "bn_BD", currency: "BDT", latitude: "23.684994", longitude: "90.356331", currency_name: "Taka", languages: ["bn"], capital: "Dhaka", emoji: "🇧🇩", emojiU: "U+1F1E7 U+1F1E9", fips: "BG", internet: "BD", continent: "Asia", region: "South Asia" }, { name: "Barbados", alpha2: "BB", alpha3: "BRB", numeric: "052", locales: ["en"], default_locale: "en", currency: "BBD", latitude: "13.193887", longitude: "-59.543198", currency_name: "Barbados Dollar", languages: ["en"], capital: "Bridgetown", emoji: "🇧🇧", emojiU: "U+1F1E7 U+1F1E7", fips: "BB", internet: "BB", continent: "Americas", region: "West Indies" }, { name: "Belarus", alpha2: "BY", alpha3: "BLR", numeric: "112", locales: ["be_BY"], default_locale: "be_BY", currency: "BYN", latitude: "53.709807", longitude: "27.953389", currency_name: "Belarusian Ruble", languages: ["be", "ru"], capital: "Minsk", emoji: "🇧🇾", emojiU: "U+1F1E7 U+1F1FE", fips: "BO", internet: "BY", continent: "Europe", region: "Eastern Europe" }, { name: "Belgium", alpha2: "BE", alpha3: "BEL", numeric: "056", locales: ["nl_BE", "en_BE", "fr_BE", "de_BE"], default_locale: "nl_BE", currency: "EUR", latitude: "50.503887", longitude: "4.469936", currency_name: "Euro", languages: ["nl", "fr", "de"], capital: "Brussels", emoji: "🇧🇪", emojiU: "U+1F1E7 U+1F1EA", fips: "BE", internet: "BE", continent: "Europe", region: "Western Europe" }, { name: "Belize", alpha2: "BZ", alpha3: "BLZ", numeric: "084", locales: ["en_BZ"], default_locale: "en_BZ", currency: "BZD", latitude: "17.189877", longitude: "-88.49765", currency_name: "Belize Dollar", languages: ["en", "es"], capital: "Belmopan", emoji: "🇧🇿", emojiU: "U+1F1E7 U+1F1FF", fips: "BH", internet: "BZ", continent: "Americas", region: "Central America" }, { name: "Benin", alpha2: "BJ", alpha3: "BEN", numeric: "204", locales: ["fr_BJ"], default_locale: "fr_BJ", currency: "XOF", latitude: "9.30769", longitude: "2.315834", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Porto-Novo", emoji: "🇧🇯", emojiU: "U+1F1E7 U+1F1EF", fips: "BN", internet: "BJ", continent: "Africa", region: "Western Africa" }, { name: "Bermuda", alpha2: "BM", alpha3: "BMU", numeric: "060", locales: ["en"], default_locale: "en", currency: "BMD", latitude: "32.321384", longitude: "-64.75737", currency_name: "Bermudian Dollar", languages: ["en"], capital: "Hamilton", emoji: "🇧🇲", emojiU: "U+1F1E7 U+1F1F2", fips: "BD", internet: "BM", continent: "Americas", region: "West Indies" }, { name: "Bhutan", alpha2: "BT", alpha3: "BTN", numeric: "064", locales: ["dz"], default_locale: "dz", currency: "BTN", latitude: "27.514162", longitude: "90.433601", currency_name: "Ngultrum", languages: ["dz"], capital: "Thimphu", emoji: "🇧🇹", emojiU: "U+1F1E7 U+1F1F9", fips: "BT", internet: "BT", continent: "Asia", region: "South Asia" }, { name: "Bolivia", alpha2: "BO", alpha3: "BOL", numeric: "068", locales: ["es_BO"], default_locale: "es_BO", currency: "BOB", latitude: "-16.290154", longitude: "-63.588653", currency_name: "Bolivia", languages: ["es", "ay", "qu"], capital: "Sucre", emoji: "🇧🇴", emojiU: "U+1F1E7 U+1F1F4", fips: "BL", internet: "BO", continent: "Americas", region: "South America", alternate_names: ["Plurinational State of Bolivia"] }, { name: "Bonaire", alpha2: "BQ", alpha3: "BES", numeric: "535", locales: ["nl"], default_locale: "nl", currency: "USD", currency_name: "US Dollar", languages: ["nl"], capital: "Kralendijk", emoji: "🇧🇶", emojiU: "U+1F1E7 U+1F1F6", fips: "BQ", internet: "BQ", continent: "Americas", region: "West Indies", alternate_names: ["Bonaire, Sint Eustatius and Saba"] }, { name: "Bosnia and Herzegovina", alpha2: "BA", alpha3: "BIH", numeric: "070", locales: ["bs_BA", "sr_Cyrl_BA", "sr_Latn_BA"], default_locale: "bs_BA", currency: "BAM", latitude: "43.915886", longitude: "17.679076", currency_name: "Convertible Mark", languages: ["bs", "hr", "sr"], capital: "Sarajevo", emoji: "🇧🇦", emojiU: "U+1F1E7 U+1F1E6", fips: "BK", internet: "BA", continent: "Europe", region: "South East Europe" }, { name: "Botswana", alpha2: "BW", alpha3: "BWA", numeric: "072", locales: ["en_BW"], default_locale: "en_BW", currency: "BWP", latitude: "-22.328474", longitude: "24.684866", currency_name: "Pula", languages: ["en", "tn"], capital: "Gaborone", emoji: "🇧🇼", emojiU: "U+1F1E7 U+1F1FC", fips: "BC", internet: "BW", continent: "Africa", region: "Southern Africa" }, { name: "Bouvet Island", alpha2: "BV", alpha3: "BVT", numeric: "074", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "-54.423199", longitude: "3.413194", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "", emoji: "🇧🇻", emojiU: "U+1F1E7 U+1F1FB", fips: "BV", internet: "BV", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "Brazil", alpha2: "BR", alpha3: "BRA", numeric: "076", locales: ["pt_BR"], default_locale: "pt_BR", currency: "BRL", latitude: "-14.235004", longitude: "-51.92528", currency_name: "Brazilian Real", languages: ["pt"], capital: "Brasília", emoji: "🇧🇷", emojiU: "U+1F1E7 U+1F1F7", fips: "BR", internet: "BR", continent: "Americas", region: "South America" }, { name: "British Indian Ocean Territory", alpha2: "IO", alpha3: "IOT", numeric: "086", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-6.343194", longitude: "71.876519", currency_name: "US Dollar", languages: ["en"], capital: "Diego Garcia", emoji: "🇮🇴", emojiU: "U+1F1EE U+1F1F4", fips: "IO", internet: "IO", continent: "Asia", region: "South Asia" }, { name: "Brunei Darussalam", alpha2: "BN", alpha3: "BRN", numeric: "096", locales: ["ms_BN"], default_locale: "ms_BN", currency: "BND", latitude: "4.535277", longitude: "114.727669", currency_name: "Brunei Dollar", languages: ["ms"], capital: "Bandar Seri Begawan", emoji: "🇧🇳", emojiU: "U+1F1E7 U+1F1F3", fips: "BX", internet: "BN", continent: "Asia", region: "South East Asia" }, { name: "Bulgaria", alpha2: "BG", alpha3: "BGR", numeric: "100", locales: ["bg_BG"], default_locale: "bg_BG", currency: "BGN", latitude: "42.733883", longitude: "25.48583", currency_name: "Bulgarian Lev", languages: ["bg"], capital: "Sofia", emoji: "🇧🇬", emojiU: "U+1F1E7 U+1F1EC", fips: "BU", internet: "BG", continent: "Europe", region: "South East Europe" }, { name: "Burkina Faso", alpha2: "BF", alpha3: "BFA", numeric: "854", locales: ["fr_BF"], default_locale: "fr_BF", currency: "XOF", latitude: "12.238333", longitude: "-1.561593", currency_name: "CFA Franc BCEAO", languages: ["fr", "ff"], capital: "Ouagadougou", emoji: "🇧🇫", emojiU: "U+1F1E7 U+1F1EB", fips: "UV", internet: "BF", continent: "Africa", region: "Western Africa" }, { name: "Burundi", alpha2: "BI", alpha3: "BDI", numeric: "108", locales: ["fr_BI"], default_locale: "fr_BI", currency: "BIF", latitude: "-3.373056", longitude: "29.918886", currency_name: "Burundi Franc", languages: ["fr", "rn"], capital: "Bujumbura", emoji: "🇧🇮", emojiU: "U+1F1E7 U+1F1EE", fips: "BY", internet: "BI", continent: "Africa", region: "Central Africa" }, { name: "Cabo Verde", alpha2: "CV", alpha3: "CPV", numeric: "132", locales: ["kea_CV"], default_locale: "kea_CV", currency: "CVE", latitude: "16.002082", longitude: "-24.013197", currency_name: "Cabo Verde Escudo", languages: ["pt"], capital: "Praia", emoji: "🇨🇻", emojiU: "U+1F1E8 U+1F1FB", fips: "CV", internet: "CV", continent: "Africa", region: "Western Africa" }, { name: "Cambodia", alpha2: "KH", alpha3: "KHM", numeric: "116", locales: ["km_KH"], default_locale: "km_KH", currency: "KHR", latitude: "12.565679", longitude: "104.990963", currency_name: "Riel", languages: ["km"], capital: "Phnom Penh", emoji: "🇰🇭", emojiU: "U+1F1F0 U+1F1ED", fips: "CB", internet: "KH", continent: "Asia", region: "South East Asia" }, { name: "Cameroon", alpha2: "CM", alpha3: "CMR", numeric: "120", locales: ["fr_CM"], default_locale: "fr_CM", currency: "XAF", latitude: "7.369722", longitude: "12.354722", currency_name: "CFA Franc BEAC", languages: ["en", "fr"], capital: "Yaoundé", emoji: "🇨🇲", emojiU: "U+1F1E8 U+1F1F2", fips: "CM", internet: "CM", continent: "Africa", region: "Western Africa" }, { name: "Canada", alpha2: "CA", alpha3: "CAN", numeric: "124", locales: ["en_CA", "fr_CA"], default_locale: "en_CA", currency: "CAD", latitude: "56.130366", longitude: "-106.346771", currency_name: "Canadian Dollar", languages: ["en", "fr"], capital: "Ottawa", emoji: "🇨🇦", emojiU: "U+1F1E8 U+1F1E6", fips: "CA", internet: "CA", continent: "Americas", region: "North America" }, { name: "Cayman Islands", alpha2: "KY", alpha3: "CYM", numeric: "136", locales: ["en"], default_locale: "en", currency: "KYD", latitude: "19.513469", longitude: "-80.566956", currency_name: "Cayman Islands Dollar", languages: ["en"], capital: "George Town", emoji: "🇰🇾", emojiU: "U+1F1F0 U+1F1FE", fips: "CJ", internet: "KY", continent: "Americas", region: "West Indies" }, { name: "Central African Republic", alpha2: "CF", alpha3: "CAF", numeric: "140", locales: ["fr_CF", "sg_CF"], default_locale: "fr_CF", currency: "XAF", latitude: "6.611111", longitude: "20.939444", currency_name: "CFA Franc BEAC", languages: ["fr", "sg"], capital: "Bangui", emoji: "🇨🇫", emojiU: "U+1F1E8 U+1F1EB", fips: "CT", internet: "CF", continent: "Africa", region: "Central Africa" }, { name: "Chad", alpha2: "TD", alpha3: "TCD", numeric: "148", locales: ["fr_TD"], default_locale: "fr_TD", currency: "XAF", latitude: "15.454166", longitude: "18.732207", currency_name: "CFA Franc BEAC", languages: ["fr", "ar"], capital: "N'Djamena", emoji: "🇹🇩", emojiU: "U+1F1F9 U+1F1E9", fips: "CD", internet: "TD", continent: "Africa", region: "Central Africa" }, { name: "Chile", alpha2: "CL", alpha3: "CHL", numeric: "152", locales: ["es_CL"], default_locale: "es_CL", currency: "CLP", latitude: "-35.675147", longitude: "-71.542969", currency_name: "Chilean Peso", languages: ["es"], capital: "Santiago", emoji: "🇨🇱", emojiU: "U+1F1E8 U+1F1F1", fips: "CI", internet: "CL", continent: "Americas", region: "South America" }, { name: "China", alpha2: "CN", alpha3: "CHN", numeric: "156", locales: ["zh_CN", "zh_Hans_CN", "ii_CN", "bo_CN"], default_locale: "zh_CN", currency: "CNY", latitude: "35.86166", longitude: "104.195397", currency_name: "Yuan Renminbi", languages: ["zh"], capital: "Beijing", emoji: "🇨🇳", emojiU: "U+1F1E8 U+1F1F3", fips: "CH", internet: "CN", continent: "Asia", region: "East Asia" }, { name: "Christmas Island", alpha2: "CX", alpha3: "CXR", numeric: "162", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-10.447525", longitude: "105.690449", currency_name: "Australian Dollar", languages: ["en"], capital: "Flying Fish Cove", emoji: "🇨🇽", emojiU: "U+1F1E8 U+1F1FD", fips: "KT", internet: "CX", continent: "Asia", region: "South East Asia" }, { name: "Cocos Islands", alpha2: "CC", alpha3: "CCK", numeric: "166", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-12.164165", longitude: "96.870956", currency_name: "Australian Dollar", languages: ["en"], capital: "West Island", emoji: "🇨🇨", emojiU: "U+1F1E8 U+1F1E8", fips: "CK", internet: "CC", continent: "Asia", region: "South East Asia", alternate_names: ["Cocos Keeling Islands"] }, { name: "Colombia", alpha2: "CO", alpha3: "COL", numeric: "170", locales: ["es_CO"], default_locale: "es_CO", currency: "COP", latitude: "4.570868", longitude: "-74.297333", currency_name: "Colombian Peso", languages: ["es"], capital: "Bogotá", emoji: "🇨🇴", emojiU: "U+1F1E8 U+1F1F4", fips: "CO", internet: "CO", continent: "Americas", region: "South America" }, { name: "Comoros", alpha2: "KM", alpha3: "COM", numeric: "174", locales: ["fr_KM"], default_locale: "fr_KM", currency: "KMF", latitude: "-11.875001", longitude: "43.872219", currency_name: "Comorian Franc ", languages: ["ar", "fr"], capital: "Moroni", emoji: "🇰🇲", emojiU: "U+1F1F0 U+1F1F2", fips: "CN", internet: "KM", continent: "Africa", region: "Indian Ocean" }, { name: "Democratic Republic of the Congo", alpha2: "CD", alpha3: "COD", numeric: "180", locales: ["fr_CD"], default_locale: "fr_CD", currency: "CDF", latitude: "-4.038333", longitude: "21.758664", currency_name: "Congolese Franc", languages: ["fr", "ln", "kg", "sw", "lu"], capital: "Kinshasa", emoji: "🇨🇩", emojiU: "U+1F1E8 U+1F1E9", fips: "CG", internet: "ZR", continent: "Africa", region: "Central Africa" }, { name: "Congo", alpha2: "CG", alpha3: "COG", numeric: "178", locales: ["fr_CG"], default_locale: "fr_CG", currency: "XAF", latitude: "-0.228021", longitude: "15.827659", currency_name: "CFA Franc BEAC", languages: ["fr", "ln"], capital: "Brazzaville", emoji: "🇨🇬", emojiU: "U+1F1E8 U+1F1EC", fips: "CF", internet: "CG", continent: "Africa", region: "Central Africa" }, { name: "Cook Islands", alpha2: "CK", alpha3: "COK", numeric: "184", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-21.236736", longitude: "-159.777671", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Avarua", emoji: "🇨🇰", emojiU: "U+1F1E8 U+1F1F0", fips: "CW", internet: "CK", continent: "Oceania", region: "Pacific" }, { name: "Costa Rica", alpha2: "CR", alpha3: "CRI", numeric: "188", locales: ["es_CR"], default_locale: "es_CR", currency: "CRC", latitude: "9.748917", longitude: "-83.753428", currency_name: "Costa Rican Colon", languages: ["es"], capital: "San José", emoji: "🇨🇷", emojiU: "U+1F1E8 U+1F1F7", fips: "CS", internet: "CR", continent: "Americas", region: "Central America" }, { name: "Croatia", alpha2: "HR", alpha3: "HRV", numeric: "191", locales: ["hr_HR"], default_locale: "hr_HR", currency: "EUR", latitude: "45.1", longitude: "15.2", currency_name: "Euro", languages: ["hr"], capital: "Zagreb", emoji: "🇭🇷", emojiU: "U+1F1ED U+1F1F7", fips: "HR", internet: "HR", continent: "Europe", region: "South East Europe" }, { name: "Cuba", alpha2: "CU", alpha3: "CUB", numeric: "192", locales: ["es"], default_locale: "es", currency: "CUC", latitude: "21.521757", longitude: "-77.781167", currency_name: "Peso Convertible", languages: ["es"], capital: "Havana", emoji: "🇨🇺", emojiU: "U+1F1E8 U+1F1FA", fips: "CU", internet: "CU", continent: "Americas", region: "West Indies" }, { name: "Curaçao", alpha2: "CW", alpha3: "CUW", numeric: "531", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "pa", "en"], capital: "Willemstad", emoji: "🇨🇼", emojiU: "U+1F1E8 U+1F1FC", fips: "UC", internet: "CW", continent: "Americas", region: "West Indies" }, { name: "Cyprus", alpha2: "CY", alpha3: "CYP", numeric: "196", locales: ["el_CY"], default_locale: "el_CY", currency: "EUR", latitude: "35.126413", longitude: "33.429859", currency_name: "Euro", languages: ["el", "tr", "hy"], capital: "Nicosia", emoji: "🇨🇾", emojiU: "U+1F1E8 U+1F1FE", fips: "CY", internet: "CY", continent: "Asia", region: "South West Asia" }, { name: "Czechia", alpha2: "CZ", alpha3: "CZE", numeric: "203", locales: ["cs_CZ"], default_locale: "cs_CZ", currency: "CZK", latitude: "49.817492", longitude: "15.472962", currency_name: "Czech Koruna", languages: ["cs", "sk"], capital: "Prague", emoji: "🇨🇿", emojiU: "U+1F1E8 U+1F1FF", fips: "EZ", internet: "CZ", continent: "Europe", region: "Central Europe" }, { name: "Côte d'Ivoire", alpha2: "CI", alpha3: "CIV", numeric: "384", locales: ["fr_CI"], default_locale: "fr_CI", currency: "CZK", latitude: "7.539989", longitude: "-5.54708", currency_name: "Czech Koruna", languages: ["fr"], capital: "Yamoussoukro", emoji: "🇨🇮", emojiU: "U+1F1E8 U+1F1EE", fips: "IV", internet: "CI", continent: "Africa", region: "Western Africa" }, { name: "Denmark", alpha2: "DK", alpha3: "DNK", numeric: "208", locales: ["da_DK"], default_locale: "da_DK", currency: "DKK", latitude: "56.26392", longitude: "9.501785", currency_name: "Danish Krone", languages: ["da"], capital: "Copenhagen", emoji: "🇩🇰", emojiU: "U+1F1E9 U+1F1F0", fips: "DA", internet: "DK", continent: "Europe", region: "Northern Europe" }, { name: "Djibouti", alpha2: "DJ", alpha3: "DJI", numeric: "262", locales: ["fr_DJ", "so_DJ"], default_locale: "fr_DJ", currency: "DJF", latitude: "11.825138", longitude: "42.590275", currency_name: "Djibouti Franc", languages: ["fr", "ar"], capital: "Djibouti", emoji: "🇩🇯", emojiU: "U+1F1E9 U+1F1EF", fips: "DJ", internet: "DJ", continent: "Africa", region: "Eastern Africa" }, { name: "Dominica", alpha2: "DM", alpha3: "DMA", numeric: "212", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "15.414999", longitude: "-61.370976", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Roseau", emoji: "🇩🇲", emojiU: "U+1F1E9 U+1F1F2", fips: "DO", internet: "DM", continent: "Americas", region: "West Indies" }, { name: "Dominican Republic", alpha2: "DO", alpha3: "DOM", numeric: "214", locales: ["es_DO"], default_locale: "es_DO", currency: "DOP", latitude: "18.735693", longitude: "-70.162651", currency_name: "Dominican Peso", languages: ["es"], capital: "Santo Domingo", emoji: "🇩🇴", emojiU: "U+1F1E9 U+1F1F4", fips: "DR", internet: "DO", continent: "Americas", region: "West Indies" }, { name: "Ecuador", alpha2: "EC", alpha3: "ECU", numeric: "218", locales: ["es_EC"], default_locale: "es_EC", currency: "USD", latitude: "-1.831239", longitude: "-78.183406", currency_name: "US Dollar", languages: ["es"], capital: "Quito", emoji: "🇪🇨", emojiU: "U+1F1EA U+1F1E8", fips: "EC", internet: "EC", continent: "Americas", region: "South America" }, { name: "Egypt", alpha2: "EG", alpha3: "EGY", numeric: "818", locales: ["ar_EG"], default_locale: "ar_EG", currency: "EGP", latitude: "26.820553", longitude: "30.802498", currency_name: "Egyptian Pound", languages: ["ar"], capital: "Cairo", emoji: "🇪🇬", emojiU: "U+1F1EA U+1F1EC", fips: "EG", internet: "EG", continent: "Africa", region: "Northern Africa" }, { name: "El Salvador", alpha2: "SV", alpha3: "SLV", numeric: "222", locales: ["es_SV"], default_locale: "es_SV", currency: "USD", latitude: "13.794185", longitude: "-88.89653", currency_name: "US Dollar", languages: ["es"], capital: "San Salvador", emoji: "🇸🇻", emojiU: "U+1F1F8 U+1F1FB", fips: "ES", internet: "SV", continent: "Americas", region: "Central America" }, { name: "Equatorial Guinea", alpha2: "GQ", alpha3: "GNQ", numeric: "226", locales: ["fr_GQ", "es_GQ"], default_locale: "fr_GQ", currency: "XAF", latitude: "1.650801", longitude: "10.267895", currency_name: "CFA Franc BEAC", languages: ["es", "fr"], capital: "Malabo", emoji: "🇬🇶", emojiU: "U+1F1EC U+1F1F6", fips: "EK", internet: "GQ", continent: "Africa", region: "Western Africa" }, { name: "Eritrea", alpha2: "ER", alpha3: "ERI", numeric: "232", locales: ["ti_ER"], default_locale: "ti_ER", currency: "ERN", latitude: "15.179384", longitude: "39.782334", currency_name: "Nakfa", languages: ["ti", "ar", "en"], capital: "Asmara", emoji: "🇪🇷", emojiU: "U+1F1EA U+1F1F7", fips: "ER", internet: "ER", continent: "Africa", region: "Eastern Africa" }, { name: "Estonia", alpha2: "EE", alpha3: "EST", numeric: "233", locales: ["et_EE"], default_locale: "et_EE", currency: "EUR", latitude: "58.595272", longitude: "25.013607", currency_name: "Euro", languages: ["et"], capital: "Tallinn", emoji: "🇪🇪", emojiU: "U+1F1EA U+1F1EA", fips: "EN", internet: "EE", continent: "Europe", region: "Eastern Europe" }, { name: "Eswatini", alpha2: "SZ", alpha3: "SWZ", numeric: "748", locales: ["en"], default_locale: "en", currency: "EUR", latitude: "-26.522503", longitude: "31.465866", currency_name: "Euro", languages: ["en", "ss"], capital: "Lobamba", emoji: "🇸🇿", emojiU: "U+1F1F8 U+1F1FF", fips: "WZ", internet: "SZ", continent: "Africa", region: "Southern Africa" }, { name: "Ethiopia", alpha2: "ET", alpha3: "ETH", numeric: "231", locales: ["am_ET", "om_ET", "so_ET", "ti_ET"], default_locale: "am_ET", currency: "ETB", latitude: "9.145", longitude: "40.489673", currency_name: "Ethiopian Birr", languages: ["am"], capital: "Addis Ababa", emoji: "🇪🇹", emojiU: "U+1F1EA U+1F1F9", fips: "ET", internet: "ET", continent: "Africa", region: "Eastern Africa" }, { name: "Falkland Islands", alpha2: "FK", alpha3: "FLK", numeric: "238", locales: ["en"], default_locale: "en", currency: "DKK", latitude: "-51.796253", longitude: "-59.523613", currency_name: "Danish Krone", languages: ["en"], capital: "Stanley", emoji: "🇫🇰", emojiU: "U+1F1EB U+1F1F0", fips: "FA", internet: "FK", continent: "Americas", region: "South America", alternate_names: ["Malvinas Falkland Islands"] }, { name: "Faroe Islands", alpha2: "FO", alpha3: "FRO", numeric: "234", locales: ["fo_FO"], default_locale: "fo_FO", currency: "DKK", latitude: "61.892635", longitude: "-6.911806", currency_name: "Danish Krone", languages: ["fo"], capital: "Tórshavn", emoji: "🇫🇴", emojiU: "U+1F1EB U+1F1F4", fips: "FO", internet: "FO", continent: "Europe", region: "Northern Europe" }, { name: "Fiji", alpha2: "FJ", alpha3: "FJI", numeric: "242", locales: ["en"], default_locale: "en", currency: "FJD", latitude: "-16.578193", longitude: "179.414413", currency_name: "Fiji Dollar", languages: ["en", "fj", "hi", "ur"], capital: "Suva", emoji: "🇫🇯", emojiU: "U+1F1EB U+1F1EF", fips: "FJ", internet: "FJ", continent: "Oceania", region: "Pacific" }, { name: "Finland", alpha2: "FI", alpha3: "FIN", numeric: "246", locales: ["fi_FI", "sv_FI"], default_locale: "fi_FI", currency: "EUR", latitude: "61.92411", longitude: "25.748151", currency_name: "Euro", languages: ["fi", "sv"], capital: "Helsinki", emoji: "🇫🇮", emojiU: "U+1F1EB U+1F1EE", fips: "FI", internet: "FI", continent: "Europe", region: "Northern Europe" }, { name: "France", alpha2: "FR", alpha3: "FRA", numeric: "250", locales: ["fr_FR"], default_locale: "fr_FR", currency: "EUR", latitude: "46.227638", longitude: "2.213749", currency_name: "Euro", languages: ["fr"], capital: "Paris", emoji: "🇫🇷", emojiU: "U+1F1EB U+1F1F7", fips: "FR", internet: "FR", continent: "Europe", region: "Western Europe" }, { name: "French Guiana", alpha2: "GF", alpha3: "GUF", numeric: "254", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "3.933889", longitude: "-53.125782", currency_name: "Euro", languages: ["fr"], capital: "Cayenne", emoji: "🇬🇫", emojiU: "U+1F1EC U+1F1EB", fips: "FG", internet: "GF", continent: "Americas", region: "South America" }, { name: "French Polynesia", alpha2: "PF", alpha3: "PYF", numeric: "258", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-17.679742", longitude: "-149.406843", currency_name: "CFP Franc", languages: ["fr"], capital: "Papeetē", emoji: "🇵🇫", emojiU: "U+1F1F5 U+1F1EB", fips: "FP", internet: "PF", continent: "Oceania", region: "Pacific" }, { name: "French Southern Territories", alpha2: "TF", alpha3: "ATF", numeric: "260", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-49.280366", longitude: "69.348557", currency_name: "Euro", languages: ["fr"], capital: "Port-aux-Français", emoji: "🇹🇫", emojiU: "U+1F1F9 U+1F1EB", fips: "FS", internet: "--", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Gabon", alpha2: "GA", alpha3: "GAB", numeric: "266", locales: ["fr_GA"], default_locale: "fr_GA", currency: "XAF", latitude: "-0.803689", longitude: "11.609444", currency_name: "CFA Franc BEAC", languages: ["fr"], capital: "Libreville", emoji: "🇬🇦", emojiU: "U+1F1EC U+1F1E6", fips: "GB", internet: "GA", continent: "Africa", region: "Western Africa" }, { name: "Gambia", alpha2: "GM", alpha3: "GMB", numeric: "270", locales: ["en"], default_locale: "en", currency: "GMD", latitude: "13.443182", longitude: "-15.310139", currency_name: "Dalasi", languages: ["en"], capital: "Banjul", emoji: "🇬🇲", emojiU: "U+1F1EC U+1F1F2", fips: "GA", internet: "GM", continent: "Africa", region: "Western Africa" }, { name: "Georgia", alpha2: "GE", alpha3: "GEO", numeric: "268", locales: ["ka_GE"], default_locale: "ka_GE", currency: "GEL", latitude: "42.315407", longitude: "43.356892", currency_name: "Lari", languages: ["ka"], capital: "Tbilisi", emoji: "🇬🇪", emojiU: "U+1F1EC U+1F1EA", fips: "GG", internet: "GE", continent: "Asia", region: "South West Asia" }, { name: "Germany", alpha2: "DE", alpha3: "DEU", numeric: "276", locales: ["de_DE"], default_locale: "de_DE", currency: "EUR", latitude: "51.165691", longitude: "10.451526", currency_name: "Euro", languages: ["de"], capital: "Berlin", emoji: "🇩🇪", emojiU: "U+1F1E9 U+1F1EA", fips: "GM", internet: "DE", continent: "Europe", region: "Western Europe" }, { name: "Ghana", alpha2: "GH", alpha3: "GHA", numeric: "288", locales: ["ak_GH", "ee_GH", "ha_Latn_GH"], default_locale: "ak_GH", currency: "GHS", latitude: "7.946527", longitude: "-1.023194", currency_name: "Ghana Cedi", languages: ["en"], capital: "Accra", emoji: "🇬🇭", emojiU: "U+1F1EC U+1F1ED", fips: "GH", internet: "GH", continent: "Africa", region: "Western Africa" }, { name: "Gibraltar", alpha2: "GI", alpha3: "GIB", numeric: "292", locales: ["en"], default_locale: "en", currency: "GIP", latitude: "36.137741", longitude: "-5.345374", currency_name: "Gibraltar Pound", languages: ["en"], capital: "Gibraltar", emoji: "🇬🇮", emojiU: "U+1F1EC U+1F1EE", fips: "GI", internet: "GI", continent: "Europe", region: "South West Europe" }, { name: "Greece", alpha2: "GR", alpha3: "GRC", numeric: "300", locales: ["el_GR"], default_locale: "el_GR", currency: "EUR", latitude: "39.074208", longitude: "21.824312", currency_name: "Euro", languages: ["el"], capital: "Athens", emoji: "🇬🇷", emojiU: "U+1F1EC U+1F1F7", fips: "GR", internet: "GR", continent: "Europe", region: "South East Europe" }, { name: "Greenland", alpha2: "GL", alpha3: "GRL", numeric: "304", locales: ["kl_GL"], default_locale: "kl_GL", currency: "DKK", latitude: "71.706936", longitude: "-42.604303", currency_name: "Danish Krone", languages: ["kl"], capital: "Nuuk", emoji: "🇬🇱", emojiU: "U+1F1EC U+1F1F1", fips: "GL", internet: "GL", continent: "Americas", region: "North America" }, { name: "Grenada", alpha2: "GD", alpha3: "GRD", numeric: "308", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.262776", longitude: "-61.604171", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "St. George's", emoji: "🇬🇩", emojiU: "U+1F1EC U+1F1E9", fips: "GJ", internet: "GD", continent: "Americas", region: "West Indies" }, { name: "Guadeloupe", alpha2: "GP", alpha3: "GLP", numeric: "312", locales: ["fr_GP"], default_locale: "fr_GP", currency: "EUR", latitude: "16.995971", longitude: "-62.067641", currency_name: "Euro", languages: ["fr"], capital: "Basse-Terre", emoji: "🇬🇵", emojiU: "U+1F1EC U+1F1F5", fips: "GP", internet: "GP", continent: "Americas", region: "West Indies" }, { name: "Guam", alpha2: "GU", alpha3: "GUM", numeric: "316", locales: ["en_GU"], default_locale: "en_GU", currency: "USD", latitude: "13.444304", longitude: "144.793731", currency_name: "US Dollar", languages: ["en", "ch", "es"], capital: "Hagåtña", emoji: "🇬🇺", emojiU: "U+1F1EC U+1F1FA", fips: "GQ", internet: "GU", continent: "Oceania", region: "Pacific" }, { name: "Guatemala", alpha2: "GT", alpha3: "GTM", numeric: "320", locales: ["es_GT"], default_locale: "es_GT", currency: "GTQ", latitude: "15.783471", longitude: "-90.230759", currency_name: "Quetzal", languages: ["es"], capital: "Guatemala City", emoji: "🇬🇹", emojiU: "U+1F1EC U+1F1F9", fips: "GT", internet: "GT", continent: "Americas", region: "Central America" }, { name: "Guernsey", alpha2: "GG", alpha3: "GGY", numeric: "831", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.465691", longitude: "-2.585278", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "St. Peter Port", emoji: "🇬🇬", emojiU: "U+1F1EC U+1F1EC", fips: "GK", internet: "GG", continent: "Europe", region: "Western Europe" }, { name: "Guinea", alpha2: "GN", alpha3: "GIN", numeric: "324", locales: ["fr_GN"], default_locale: "fr_GN", currency: "GNF", latitude: "9.945587", longitude: "-9.696645", currency_name: "Guinean Franc", languages: ["fr", "ff"], capital: "Conakry", emoji: "🇬🇳", emojiU: "U+1F1EC U+1F1F3", fips: "GV", internet: "GN", continent: "Africa", region: "Western Africa" }, { name: "Guinea-Bissau", alpha2: "GW", alpha3: "GNB", numeric: "624", locales: ["pt_GW"], default_locale: "pt_GW", currency: "XOF", latitude: "11.803749", longitude: "-15.180413", currency_name: "CFA Franc BCEAO", languages: ["pt"], capital: "Bissau", emoji: "🇬🇼", emojiU: "U+1F1EC U+1F1FC", fips: "PU", internet: "GW", continent: "Africa", region: "Western Africa" }, { name: "Guyana", alpha2: "GY", alpha3: "GUY", numeric: "328", locales: ["en"], default_locale: "en", currency: "GYD", latitude: "4.860416", longitude: "-58.93018", currency_name: "Guyana Dollar", languages: ["en"], capital: "Georgetown", emoji: "🇬🇾", emojiU: "U+1F1EC U+1F1FE", fips: "GY", internet: "GY", continent: "Americas", region: "South America" }, { name: "Haiti", alpha2: "HT", alpha3: "HTI", numeric: "332", locales: ["fr"], default_locale: "fr", currency: "USD", latitude: "18.971187", longitude: "-72.285215", currency_name: "US Dollar", languages: ["fr", "ht"], capital: "Port-au-Prince", emoji: "🇭🇹", emojiU: "U+1F1ED U+1F1F9", fips: "HA", internet: "HT", continent: "Americas", region: "West Indies" }, { name: "Heard Island and McDonald Islands", alpha2: "HM", alpha3: "HMD", numeric: "334", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-53.08181", longitude: "73.504158", currency_name: "Australian Dollar", languages: ["en"], capital: "", emoji: "🇭🇲", emojiU: "U+1F1ED U+1F1F2", fips: "HM", internet: "HM", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Holy See", alpha2: "VA", alpha3: "VAT", numeric: "336", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "41.902916", longitude: "12.453389", currency_name: "Euro", languages: ["it", "la"], capital: "Vatican City", emoji: "🇻🇦", emojiU: "U+1F1FB U+1F1E6", fips: "VT", internet: "VA", continent: "Europe", region: "Southern Europe" }, { name: "Honduras", alpha2: "HN", alpha3: "HND", numeric: "340", locales: ["es_HN"], default_locale: "es_HN", currency: "HNL", latitude: "15.199999", longitude: "-86.241905", currency_name: "Lempira", languages: ["es"], capital: "Tegucigalpa", emoji: "🇭🇳", emojiU: "U+1F1ED U+1F1F3", fips: "HO", internet: "HN", continent: "Americas", region: "Central America" }, { name: "Hong Kong", alpha2: "HK", alpha3: "HKG", numeric: "344", locales: ["yue_Hant_HK", "zh_Hans_HK", "zh_Hant_HK", "en_HK"], default_locale: "en_HK", currency: "HKD", latitude: "22.396428", longitude: "114.109497", currency_name: "Hong Kong Dollar", languages: ["zh", "en"], capital: "City of Victoria", emoji: "🇭🇰", emojiU: "U+1F1ED U+1F1F0", fips: "HK", internet: "HK", continent: "Asia", region: "East Asia" }, { name: "Hungary", alpha2: "HU", alpha3: "HUN", numeric: "348", locales: ["hu_HU"], default_locale: "hu_HU", currency: "HUF", latitude: "47.162494", longitude: "19.503304", currency_name: "Forint", languages: ["hu"], capital: "Budapest", emoji: "🇭🇺", emojiU: "U+1F1ED U+1F1FA", fips: "HU", internet: "HU", continent: "Europe", region: "Central Europe" }, { name: "Iceland", alpha2: "IS", alpha3: "ISL", numeric: "352", locales: ["is_IS"], default_locale: "is_IS", currency: "ISK", latitude: "64.963051", longitude: "-19.020835", currency_name: "Iceland Krona", languages: ["is"], capital: "Reykjavik", emoji: "🇮🇸", emojiU: "U+1F1EE U+1F1F8", fips: "IC", internet: "IS", continent: "Europe", region: "Northern Europe" }, { name: "India", alpha2: "IN", alpha3: "IND", numeric: "356", locales: ["as_IN", "bn_IN", "en_IN", "gu_IN", "hi_IN", "kn_IN", "kok_IN", "ml_IN", "mr_IN", "ne_IN", "or_IN", "pa_Guru_IN", "ta_IN", "te_IN", "bo_IN", "ur_IN"], default_locale: "hi_IN", currency: "INR", latitude: "20.593684", longitude: "78.96288", currency_name: "Indian Rupee", languages: ["hi", "en"], capital: "New Delhi", emoji: "🇮🇳", emojiU: "U+1F1EE U+1F1F3", fips: "IN", internet: "IN", continent: "Asia", region: "South Asia" }, { name: "Indonesia", alpha2: "ID", alpha3: "IDN", numeric: "360", locales: ["id_ID"], default_locale: "id_ID", currency: "IDR", latitude: "-0.789275", longitude: "113.921327", currency_name: "Rupiah", languages: ["id"], capital: "Jakarta", emoji: "🇮🇩", emojiU: "U+1F1EE U+1F1E9", fips: "ID", internet: "ID", continent: "Asia", region: "South East Asia" }, { name: "Iran", alpha2: "IR", alpha3: "IRN", numeric: "364", locales: ["fa_IR"], default_locale: "fa_IR", currency: "XDR", latitude: "32.427908", longitude: "53.688046", currency_name: "SDR (Special Drawing Right)", languages: ["fa"], capital: "Tehran", emoji: "🇮🇷", emojiU: "U+1F1EE U+1F1F7", fips: "IR", internet: "IR", continent: "Asia", region: "South West Asia", alternate_names: ["Islamic Republic of Iran"] }, { name: "Iraq", alpha2: "IQ", alpha3: "IRQ", numeric: "368", locales: ["ar_IQ"], default_locale: "ar_IQ", currency: "IQD", latitude: "33.223191", longitude: "43.679291", currency_name: "Iraqi Dinar", languages: ["ar", "ku"], capital: "Baghdad", emoji: "🇮🇶", emojiU: "U+1F1EE U+1F1F6", fips: "IZ", internet: "IQ", continent: "Asia", region: "South West Asia" }, { name: "Ireland", alpha2: "IE", alpha3: "IRL", numeric: "372", locales: ["en_IE", "ga_IE"], default_locale: "en_IE", currency: "EUR", latitude: "53.41291", longitude: "-8.24389", currency_name: "Euro", languages: ["ga", "en"], capital: "Dublin", emoji: "🇮🇪", emojiU: "U+1F1EE U+1F1EA", fips: "EI", internet: "IE", continent: "Europe", region: "Western Europe" }, { name: "Isle of Man", alpha2: "IM", alpha3: "IMN", numeric: "833", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "54.236107", longitude: "-4.548056", currency_name: "Pound Sterling", languages: ["en", "gv"], capital: "Douglas", emoji: "🇮🇲", emojiU: "U+1F1EE U+1F1F2", fips: "IM", internet: "IM", continent: "Europe", region: "Western Europe" }, { name: "Israel", alpha2: "IL", alpha3: "ISR", numeric: "376", locales: ["en_IL", "he_IL"], default_locale: "he_IL", currency: "ILS", latitude: "31.046051", longitude: "34.851612", currency_name: "New Israeli Sheqel", languages: ["he", "ar"], capital: "Jerusalem", emoji: "🇮🇱", emojiU: "U+1F1EE U+1F1F1", fips: "IS", internet: "IL", continent: "Asia", region: "South West Asia" }, { name: "Italy", alpha2: "IT", alpha3: "ITA", numeric: "380", locales: ["it_IT"], default_locale: "it_IT", currency: "EUR", latitude: "41.87194", longitude: "12.56738", currency_name: "Euro", languages: ["it"], capital: "Rome", emoji: "🇮🇹", emojiU: "U+1F1EE U+1F1F9", fips: "IT", internet: "IT", continent: "Europe", region: "Southern Europe" }, { name: "Jamaica", alpha2: "JM", alpha3: "JAM", numeric: "388", locales: ["en_JM"], default_locale: "en_JM", currency: "JMD", latitude: "18.109581", longitude: "-77.297508", currency_name: "Jamaican Dollar", languages: ["en"], capital: "Kingston", emoji: "🇯🇲", emojiU: "U+1F1EF U+1F1F2", fips: "JM", internet: "JM", continent: "Americas", region: "West Indies" }, { name: "Japan", alpha2: "JP", alpha3: "JPN", numeric: "392", locales: ["ja_JP"], default_locale: "ja_JP", currency: "JPY", latitude: "36.204824", longitude: "138.252924", currency_name: "Yen", languages: ["ja"], capital: "Tokyo", emoji: "🇯🇵", emojiU: "U+1F1EF U+1F1F5", fips: "JA", internet: "JP", continent: "Asia", region: "East Asia" }, { name: "Jersey", alpha2: "JE", alpha3: "JEY", numeric: "832", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.214439", longitude: "-2.13125", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "Saint Helier", emoji: "🇯🇪", emojiU: "U+1F1EF U+1F1EA", fips: "JE", internet: "JE", continent: "Europe", region: "Western Europe" }, { name: "Jordan", alpha2: "JO", alpha3: "JOR", numeric: "400", locales: ["ar_JO"], default_locale: "ar_JO", currency: "JOD", latitude: "30.585164", longitude: "36.238414", currency_name: "Jordanian Dinar", languages: ["ar"], capital: "Amman", emoji: "🇯🇴", emojiU: "U+1F1EF U+1F1F4", fips: "JO", internet: "JO", continent: "Asia", region: "South West Asia" }, { name: "Kazakhstan", alpha2: "KZ", alpha3: "KAZ", numeric: "398", locales: ["kk_Cyrl_KZ"], default_locale: "kk_Cyrl_KZ", currency: "KZT", latitude: "48.019573", longitude: "66.923684", currency_name: "Tenge", languages: ["kk", "ru"], capital: "Astana", emoji: "🇰🇿", emojiU: "U+1F1F0 U+1F1FF", fips: "KZ", internet: "KZ", continent: "Asia", region: "Central Asia" }, { name: "Kenya", alpha2: "KE", alpha3: "KEN", numeric: "404", locales: ["ebu_KE", "guz_KE", "kln_KE", "kam_KE", "ki_KE", "luo_KE", "luy_KE", "mas_KE", "mer_KE", "om_KE", "saq_KE", "so_KE", "sw_KE", "dav_KE", "teo_KE"], default_locale: "ebu_KE", currency: "KES", latitude: "-0.023559", longitude: "37.906193", currency_name: "Kenyan Shilling", languages: ["en", "sw"], capital: "Nairobi", emoji: "🇰🇪", emojiU: "U+1F1F0 U+1F1EA", fips: "KE", internet: "KE", continent: "Africa", region: "Eastern Africa" }, { name: "Kiribati", alpha2: "KI", alpha3: "KIR", numeric: "296", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-3.370417", longitude: "-168.734039", currency_name: "Australian Dollar", languages: ["en"], capital: "South Tarawa", emoji: "🇰🇮", emojiU: "U+1F1F0 U+1F1EE", fips: "KR", internet: "KI", continent: "Oceania", region: "Pacific" }, { name: "North Korea", alpha2: "KP", alpha3: "PRK", numeric: "408", locales: ["ko"], default_locale: "ko", currency: "KPW", latitude: "40.339852", longitude: "127.510093", currency_name: "North Korean Won", languages: ["ko"], capital: "Pyongyang", emoji: "🇰🇵", emojiU: "U+1F1F0 U+1F1F5", fips: "KN", internet: "KP", continent: "Asia", region: "East Asia", alternate_names: ["Democratic People's Republic of Korea"] }, { name: "South Korea", alpha2: "KR", alpha3: "KOR", numeric: "410", locales: ["ko_KR"], default_locale: "ko_KR", currency: "KRW", latitude: "35.907757", longitude: "127.766922", currency_name: "Won", languages: ["ko"], capital: "Seoul", emoji: "🇰🇷", emojiU: "U+1F1F0 U+1F1F7", fips: "KS", internet: "KR", continent: "Asia", region: "East Asia", alternate_names: ["Republic of Korea"] }, { name: "Kuwait", alpha2: "KW", alpha3: "KWT", numeric: "414", locales: ["ar_KW"], default_locale: "ar_KW", currency: "KWD", latitude: "29.31166", longitude: "47.481766", currency_name: "Kuwaiti Dinar", languages: ["ar"], capital: "Kuwait City", emoji: "🇰🇼", emojiU: "U+1F1F0 U+1F1FC", fips: "KU", internet: "KW", continent: "Asia", region: "South West Asia" }, { name: "Kyrgyzstan", alpha2: "KG", alpha3: "KGZ", numeric: "417", locales: ["ky"], default_locale: "ky", currency: "KGS", latitude: "41.20438", longitude: "74.766098", currency_name: "Som", languages: ["ky", "ru"], capital: "Bishkek", emoji: "🇰🇬", emojiU: "U+1F1F0 U+1F1EC", fips: "KG", internet: "KG", continent: "Asia", region: "Central Asia" }, { name: "Lao People's Democratic Republic", alpha2: "LA", alpha3: "LAO", numeric: "418", locales: ["lo"], default_locale: "lo", currency: "LAK", latitude: "19.85627", longitude: "102.495496", currency_name: "Lao Kip", languages: ["lo"], capital: "Vientiane", emoji: "🇱🇦", emojiU: "U+1F1F1 U+1F1E6", fips: "LA", internet: "LA", continent: "Asia", region: "South East Asia" }, { name: "Latvia", alpha2: "LV", alpha3: "LVA", numeric: "428", locales: ["lv_LV"], default_locale: "lv_LV", currency: "EUR", latitude: "56.879635", longitude: "24.603189", currency_name: "Euro", languages: ["lv"], capital: "Riga", emoji: "🇱🇻", emojiU: "U+1F1F1 U+1F1FB", fips: "LG", internet: "LV", continent: "Europe", region: "Eastern Europe" }, { name: "Lebanon", alpha2: "LB", alpha3: "LBN", numeric: "422", locales: ["ar_LB"], default_locale: "ar_LB", currency: "LBP", latitude: "33.854721", longitude: "35.862285", currency_name: "Lebanese Pound", languages: ["ar", "fr"], capital: "Beirut", emoji: "🇱🇧", emojiU: "U+1F1F1 U+1F1E7", fips: "LE", internet: "LB", continent: "Asia", region: "South West Asia" }, { name: "Lesotho", alpha2: "LS", alpha3: "LSO", numeric: "426", locales: ["en"], default_locale: "en", currency: "ZAR", latitude: "-29.609988", longitude: "28.233608", currency_name: "Rand", languages: ["en", "st"], capital: "Maseru", emoji: "🇱🇸", emojiU: "U+1F1F1 U+1F1F8", fips: "LT", internet: "LS", continent: "Africa", region: "Southern Africa" }, { name: "Liberia", alpha2: "LR", alpha3: "LBR", numeric: "430", locales: ["en"], default_locale: "en", currency: "LRD", latitude: "6.428055", longitude: "-9.429499", currency_name: "Liberian Dollar", languages: ["en"], capital: "Monrovia", emoji: "🇱🇷", emojiU: "U+1F1F1 U+1F1F7", fips: "LI", internet: "LR", continent: "Africa", region: "Western Africa" }, { name: "Libya", alpha2: "LY", alpha3: "LBY", numeric: "434", locales: ["ar_LY"], default_locale: "ar_LY", currency: "LYD", latitude: "26.3351", longitude: "17.228331", currency_name: "Libyan Dinar", languages: ["ar"], capital: "Tripoli", emoji: "🇱🇾", emojiU: "U+1F1F1 U+1F1FE", fips: "LY", internet: "LY", continent: "Africa", region: "Northern Africa" }, { name: "Liechtenstein", alpha2: "LI", alpha3: "LIE", numeric: "438", locales: ["de_LI"], default_locale: "de_LI", currency: "CHF", latitude: "47.166", longitude: "9.555373", currency_name: "Swiss Franc", languages: ["de"], capital: "Vaduz", emoji: "🇱🇮", emojiU: "U+1F1F1 U+1F1EE", fips: "LS", internet: "LI", continent: "Europe", region: "Central Europe" }, { name: "Lithuania", alpha2: "LT", alpha3: "LTU", numeric: "440", locales: ["lt_LT"], default_locale: "lt_LT", currency: "EUR", latitude: "55.169438", longitude: "23.881275", currency_name: "Euro", languages: ["lt"], capital: "Vilnius", emoji: "🇱🇹", emojiU: "U+1F1F1 U+1F1F9", fips: "LH", internet: "LT", continent: "Europe", region: "Eastern Europe" }, { name: "Luxembourg", alpha2: "LU", alpha3: "LUX", numeric: "442", locales: ["fr_LU", "de_LU"], default_locale: "fr_LU", currency: "EUR", latitude: "49.815273", longitude: "6.129583", currency_name: "Euro", languages: ["fr", "de", "lb"], capital: "Luxembourg", emoji: "🇱🇺", emojiU: "U+1F1F1 U+1F1FA", fips: "LU", internet: "LU", continent: "Europe", region: "Western Europe" }, { name: "Macao", alpha2: "MO", alpha3: "MAC", numeric: "446", locales: ["zh_Hans_MO", "zh_Hant_MO"], default_locale: "zh_Hans_MO", currency: "MOP", latitude: "22.198745", longitude: "113.543873", currency_name: "Pataca", languages: ["zh", "pt"], capital: "", emoji: "🇲🇴", emojiU: "U+1F1F2 U+1F1F4", fips: "MC", internet: "MO", continent: "Asia", region: "East Asia" }, { name: "Madagascar", alpha2: "MG", alpha3: "MDG", numeric: "450", locales: ["fr_MG", "mg_MG"], default_locale: "fr_MG", currency: "MGA", latitude: "-18.766947", longitude: "46.869107", currency_name: "Malagasy Ariary", languages: ["fr", "mg"], capital: "Antananarivo", emoji: "🇲🇬", emojiU: "U+1F1F2 U+1F1EC", fips: "MA", internet: "MG", continent: "Africa", region: "Indian Ocean" }, { name: "Malawi", alpha2: "MW", alpha3: "MWI", numeric: "454", locales: ["en"], default_locale: "en", currency: "MWK", latitude: "-13.254308", longitude: "34.301525", currency_name: "Malawi Kwacha", languages: ["en", "ny"], capital: "Lilongwe", emoji: "🇲🇼", emojiU: "U+1F1F2 U+1F1FC", fips: "MI", internet: "MW", continent: "Africa", region: "Southern Africa" }, { name: "Malaysia", alpha2: "MY", alpha3: "MYS", numeric: "458", locales: ["ms_MY"], default_locale: "ms_MY", currency: "MYR", latitude: "4.210484", longitude: "101.975766", currency_name: "Malaysian Ringgit", languages: ["ms"], capital: "Kuala Lumpur", emoji: "🇲🇾", emojiU: "U+1F1F2 U+1F1FE", fips: "MY", internet: "MY", continent: "Asia", region: "South East Asia" }, { name: "Maldives", alpha2: "MV", alpha3: "MDV", numeric: "462", locales: ["dv"], default_locale: "dv", currency: "MVR", latitude: "3.202778", longitude: "73.22068", currency_name: "Rufiyaa", languages: ["dv"], capital: "Malé", emoji: "🇲🇻", emojiU: "U+1F1F2 U+1F1FB", fips: "MV", internet: "MV", continent: "Asia", region: "South Asia" }, { name: "Mali", alpha2: "ML", alpha3: "MLI", numeric: "466", locales: ["bm_ML", "fr_ML", "khq_ML", "ses_ML"], default_locale: "fr_ML", currency: "XOF", latitude: "17.570692", longitude: "-3.996166", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Bamako", emoji: "🇲🇱", emojiU: "U+1F1F2 U+1F1F1", fips: "ML", internet: "ML", continent: "Africa", region: "Western Africa" }, { name: "Malta", alpha2: "MT", alpha3: "MLT", numeric: "470", locales: ["en_MT", "mt_MT"], default_locale: "en_MT", currency: "EUR", latitude: "35.937496", longitude: "14.375416", currency_name: "Euro", languages: ["mt", "en"], capital: "Valletta", emoji: "🇲🇹", emojiU: "U+1F1F2 U+1F1F9", fips: "MT", internet: "MT", continent: "Europe", region: "Southern Europe" }, { name: "Marshall Islands", alpha2: "MH", alpha3: "MHL", numeric: "584", locales: ["en_MH"], default_locale: "en_MH", currency: "USD", latitude: "7.131474", longitude: "171.184478", currency_name: "US Dollar", languages: ["en", "mh"], capital: "Majuro", emoji: "🇲🇭", emojiU: "U+1F1F2 U+1F1ED", fips: "RM", internet: "MH", continent: "Oceania", region: "Pacific" }, { name: "Martinique", alpha2: "MQ", alpha3: "MTQ", numeric: "474", locales: ["fr_MQ"], default_locale: "fr_MQ", currency: "EUR", latitude: "14.641528", longitude: "-61.024174", currency_name: "Euro", languages: ["fr"], capital: "Fort-de-France", emoji: "🇲🇶", emojiU: "U+1F1F2 U+1F1F6", fips: "MB", internet: "MQ", continent: "Americas", region: "West Indies" }, { name: "Mauritania", alpha2: "MR", alpha3: "MRT", numeric: "478", locales: ["ar"], default_locale: "ar", currency: "MRU", latitude: "21.00789", longitude: "-10.940835", currency_name: "Ouguiya", languages: ["ar"], capital: "Nouakchott", emoji: "🇲🇷", emojiU: "U+1F1F2 U+1F1F7", fips: "MR", internet: "MR", continent: "Africa", region: "Western Africa" }, { name: "Mauritius", alpha2: "MU", alpha3: "MUS", numeric: "480", locales: ["en_MU", "mfe_MU"], default_locale: "en_MU", currency: "MUR", latitude: "-20.348404", longitude: "57.552152", currency_name: "Mauritius Rupee", languages: ["en"], capital: "Port Louis", emoji: "🇲🇺", emojiU: "U+1F1F2 U+1F1FA", fips: "MP", internet: "MU", continent: "Africa", region: "Indian Ocean" }, { name: "Mayotte", alpha2: "YT", alpha3: "MYT", numeric: "175", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-12.8275", longitude: "45.166244", currency_name: "Euro", languages: ["fr"], capital: "Mamoudzou", emoji: "🇾🇹", emojiU: "U+1F1FE U+1F1F9", fips: "MF", internet: "YT", continent: "Africa", region: "Indian Ocean" }, { name: "Mexico", alpha2: "MX", alpha3: "MEX", numeric: "484", locales: ["es_MX"], default_locale: "es_MX", currency: "MXN", latitude: "23.634501", longitude: "-102.552784", currency_name: "Mexican Peso", languages: ["es"], capital: "Mexico City", emoji: "🇲🇽", emojiU: "U+1F1F2 U+1F1FD", fips: "MX", internet: "MX", continent: "Americas", region: "Central America" }, { name: "Micronesia", alpha2: "FM", alpha3: "FSM", numeric: "583", locales: ["en"], default_locale: "en", currency: "RUB", latitude: "7.425554", longitude: "150.550812", currency_name: "Russian Ruble", languages: ["en"], capital: "Palikir", emoji: "🇫🇲", emojiU: "U+1F1EB U+1F1F2", fips: "", internet: "FM", continent: "Oceania", region: "Pacific", alternate_names: ["Federated States of Micronesia"] }, { name: "Moldova", alpha2: "MD", alpha3: "MDA", numeric: "498", locales: ["ro_MD", "ru_MD"], default_locale: "ro_MD", currency: "MDL", latitude: "47.411631", longitude: "28.369885", currency_name: "Moldovan Leu", languages: ["ro"], capital: "Chișinău", emoji: "🇲🇩", emojiU: "U+1F1F2 U+1F1E9", fips: "MD", internet: "MD", continent: "Europe", region: "Eastern Europe", alternate_names: ["Republic of Moldova"] }, { name: "Monaco", alpha2: "MC", alpha3: "MCO", numeric: "492", locales: ["fr_MC"], default_locale: "fr_MC", currency: "EUR", latitude: "43.750298", longitude: "7.412841", currency_name: "Euro", languages: ["fr"], capital: "Monaco", emoji: "🇲🇨", emojiU: "U+1F1F2 U+1F1E8", fips: "MN", internet: "MC", continent: "Europe", region: "Western Europe" }, { name: "Mongolia", alpha2: "MN", alpha3: "MNG", numeric: "496", locales: ["mn"], default_locale: "mn", currency: "MNT", latitude: "46.862496", longitude: "103.846656", currency_name: "Tugrik", languages: ["mn"], capital: "Ulan Bator", emoji: "🇲🇳", emojiU: "U+1F1F2 U+1F1F3", fips: "MG", internet: "MN", continent: "Asia", region: "Northern Asia" }, { name: "Montenegro", alpha2: "ME", alpha3: "MNE", numeric: "499", locales: ["sr_Cyrl_ME", "sr_Latn_ME"], default_locale: "sr_Cyrl_ME", currency: "EUR", latitude: "42.708678", longitude: "19.37439", currency_name: "Euro", languages: ["sr", "bs", "sq", "hr"], capital: "Podgorica", emoji: "🇲🇪", emojiU: "U+1F1F2 U+1F1EA", fips: "MJ", internet: "ME", continent: "Europe", region: "South East Europe" }, { name: "Montserrat", alpha2: "MS", alpha3: "MSR", numeric: "500", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "16.742498", longitude: "-62.187366", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Plymouth", emoji: "🇲🇸", emojiU: "U+1F1F2 U+1F1F8", fips: "MH", internet: "MS", continent: "Americas", region: "West Indies" }, { name: "Morocco", alpha2: "MA", alpha3: "MAR", numeric: "504", locales: ["ar_MA", "tzm_Latn_MA", "shi_Latn_MA", "shi_Tfng_MA"], default_locale: "ar_MA", currency: "MAD", latitude: "31.791702", longitude: "-7.09262", currency_name: "Moroccan Dirham", languages: ["ar"], capital: "Rabat", emoji: "🇲🇦", emojiU: "U+1F1F2 U+1F1E6", fips: "MO", internet: "MA", continent: "Africa", region: "Northern Africa" }, { name: "Mozambique", alpha2: "MZ", alpha3: "MOZ", numeric: "508", locales: ["pt_MZ", "seh_MZ"], default_locale: "pt_MZ", currency: "MZN", latitude: "-18.665695", longitude: "35.529562", currency_name: "Mozambique Metical", languages: ["pt"], capital: "Maputo", emoji: "🇲🇿", emojiU: "U+1F1F2 U+1F1FF", fips: "MZ", internet: "MZ", continent: "Africa", region: "Southern Africa" }, { name: "Myanmar", alpha2: "MM", alpha3: "MMR", numeric: "104", locales: ["my_MM"], default_locale: "my_MM", currency: "MMK", latitude: "21.913965", longitude: "95.956223", currency_name: "Kyat", languages: ["my"], capital: "Naypyidaw", emoji: "🇲🇲", emojiU: "U+1F1F2 U+1F1F2", fips: "BM", internet: "MM", continent: "Asia", region: "South East Asia" }, { name: "Namibia", alpha2: "NA", alpha3: "NAM", numeric: "516", locales: ["af_NA", "en_NA", "naq_NA"], default_locale: "en_NA", currency: "ZAR", latitude: "-22.95764", longitude: "18.49041", currency_name: "Rand", languages: ["en", "af"], capital: "Windhoek", emoji: "🇳🇦", emojiU: "U+1F1F3 U+1F1E6", fips: "WA", internet: "NA", continent: "Africa", region: "Southern Africa" }, { name: "Nauru", alpha2: "NR", alpha3: "NRU", numeric: "520", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-0.522778", longitude: "166.931503", currency_name: "Australian Dollar", languages: ["en", "na"], capital: "Yaren", emoji: "🇳🇷", emojiU: "U+1F1F3 U+1F1F7", fips: "NR", internet: "NR", continent: "Oceania", region: "Pacific" }, { name: "Nepal", alpha2: "NP", alpha3: "NPL", numeric: "524", locales: ["ne_NP"], default_locale: "ne_NP", currency: "NPR", latitude: "28.394857", longitude: "84.124008", currency_name: "Nepalese Rupee", languages: ["ne"], capital: "Kathmandu", emoji: "🇳🇵", emojiU: "U+1F1F3 U+1F1F5", fips: "NP", internet: "NP", continent: "Asia", region: "South Asia" }, { name: "Netherlands", alpha2: "NL", alpha3: "NLD", numeric: "528", locales: ["nl_NL"], default_locale: "nl_NL", currency: "EUR", latitude: "52.132633", longitude: "5.291266", currency_name: "Euro", languages: ["nl"], capital: "Amsterdam", emoji: "🇳🇱", emojiU: "U+1F1F3 U+1F1F1", fips: "NL", internet: "NL", continent: "Europe", region: "Western Europe" }, { name: "Netherlands Antilles", alpha2: "AN", alpha3: "ANT", numeric: "530", locales: ["nl_AN"], default_locale: "nl_AN", currency: "ANG", latitude: "12.226079", longitude: "-69.060087", currency_name: "Netherlands Antillean Guilder", fips: "NT", internet: "AN", continent: "Americas", region: "West Indies" }, { name: "New Caledonia", alpha2: "NC", alpha3: "NCL", numeric: "540", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-20.904305", longitude: "165.618042", currency_name: "CFP Franc", languages: ["fr"], capital: "Nouméa", emoji: "🇳🇨", emojiU: "U+1F1F3 U+1F1E8", fips: "NC", internet: "NC", continent: "Oceania", region: "Pacific" }, { name: "New Zealand", alpha2: "NZ", alpha3: "NZL", numeric: "554", locales: ["en_NZ"], default_locale: "en_NZ", currency: "NZD", latitude: "-40.900557", longitude: "174.885971", currency_name: "New Zealand Dollar", languages: ["en", "mi"], capital: "Wellington", emoji: "🇳🇿", emojiU: "U+1F1F3 U+1F1FF", fips: "NZ", internet: "NZ", continent: "Oceania", region: "Pacific" }, { name: "Nicaragua", alpha2: "NI", alpha3: "NIC", numeric: "558", locales: ["es_NI"], default_locale: "es_NI", currency: "NIO", latitude: "12.865416", longitude: "-85.207229", currency_name: "Cordoba Oro", languages: ["es"], capital: "Managua", emoji: "🇳🇮", emojiU: "U+1F1F3 U+1F1EE", fips: "NU", internet: "NI", continent: "Americas", region: "Central America" }, { name: "Niger", alpha2: "NE", alpha3: "NER", numeric: "562", locales: ["fr_NE", "ha_Latn_NE"], default_locale: "fr_NE", currency: "XOF", latitude: "17.607789", longitude: "8.081666", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Niamey", emoji: "🇳🇪", emojiU: "U+1F1F3 U+1F1EA", fips: "NG", internet: "NE", continent: "Africa", region: "Western Africa" }, { name: "Nigeria", alpha2: "NG", alpha3: "NGA", numeric: "566", locales: ["en_NG", "ha_Latn_NG", "ig_NG", "yo_NG"], default_locale: "en_NG", currency: "NGN", latitude: "9.081999", longitude: "8.675277", currency_name: "Naira", languages: ["en"], capital: "Abuja", emoji: "🇳🇬", emojiU: "U+1F1F3 U+1F1EC", fips: "NI", internet: "NG", continent: "Africa", region: "Western Africa" }, { name: "Niue", alpha2: "NU", alpha3: "NIU", numeric: "570", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-19.054445", longitude: "-169.867233", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Alofi", emoji: "🇳🇺", emojiU: "U+1F1F3 U+1F1FA", fips: "NE", internet: "NU", continent: "Oceania", region: "Pacific" }, { name: "Norfolk Island", alpha2: "NF", alpha3: "NFK", numeric: "574", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-29.040835", longitude: "167.954712", currency_name: "Australian Dollar", languages: ["en"], capital: "Kingston", emoji: "🇳🇫", emojiU: "U+1F1F3 U+1F1EB", fips: "NF", internet: "NF", continent: "Oceania", region: "Pacific" }, { name: "North Macedonia", alpha2: "MK", alpha3: "MKD", numeric: "807", locales: ["mk_MK"], default_locale: "mk_MK", currency: "MKD", latitude: "41.608635", longitude: "21.745275", currency_name: "Denar", languages: ["mk"], capital: "Skopje", emoji: "🇲🇰", emojiU: "U+1F1F2 U+1F1F0", fips: "MK", internet: "MK", continent: "Europe", region: "South East Europe" }, { name: "Northern Mariana Islands", alpha2: "MP", alpha3: "MNP", numeric: "580", locales: ["en_MP"], default_locale: "en_MP", currency: "USD", latitude: "17.33083", longitude: "145.38469", currency_name: "US Dollar", languages: ["en", "ch"], capital: "Saipan", emoji: "🇲🇵", emojiU: "U+1F1F2 U+1F1F5", fips: "CQ", internet: "MP", continent: "Oceania", region: "Pacific" }, { name: "Norway", alpha2: "NO", alpha3: "NOR", numeric: "578", locales: ["nb_NO", "nn_NO"], default_locale: "nb_NO", currency: "NOK", latitude: "60.472024", longitude: "8.468946", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "Oslo", emoji: "🇳🇴", emojiU: "U+1F1F3 U+1F1F4", fips: "NO", internet: "NO", continent: "Europe", region: "Northern Europe" }, { name: "Oman", alpha2: "OM", alpha3: "OMN", numeric: "512", locales: ["ar_OM"], default_locale: "ar_OM", currency: "OMR", latitude: "21.512583", longitude: "55.923255", currency_name: "Rial Omani", languages: ["ar"], capital: "Muscat", emoji: "🇴🇲", emojiU: "U+1F1F4 U+1F1F2", fips: "MU", internet: "OM", continent: "Asia", region: "South West Asia" }, { name: "Pakistan", alpha2: "PK", alpha3: "PAK", numeric: "586", locales: ["en_PK", "pa_Arab_PK", "ur_PK"], default_locale: "en_PK", currency: "PKR", latitude: "30.375321", longitude: "69.345116", currency_name: "Pakistan Rupee", languages: ["en", "ur"], capital: "Islamabad", emoji: "🇵🇰", emojiU: "U+1F1F5 U+1F1F0", fips: "PK", internet: "PK", continent: "Asia", region: "South Asia" }, { name: "Palau", alpha2: "PW", alpha3: "PLW", numeric: "585", locales: ["en"], default_locale: "en", currency: "USD", latitude: "7.51498", longitude: "134.58252", currency_name: "US Dollar", languages: ["en"], capital: "Ngerulmud", emoji: "🇵🇼", emojiU: "U+1F1F5 U+1F1FC", fips: "PS", internet: "PW", continent: "Oceania", region: "Pacific" }, { name: "Palestine", alpha2: "PS", alpha3: "PSE", numeric: "275", locales: ["ar"], default_locale: "ar", currency: "USD", latitude: "31.952162", longitude: "35.233154", currency_name: "US Dollar", languages: ["ar"], capital: "Ramallah", emoji: "🇵🇸", emojiU: "U+1F1F5 U+1F1F8", fips: "WE", internet: "PS", continent: "Asia", region: "South West Asia", alternate_names: ["State of Palestine"] }, { name: "Panama", alpha2: "PA", alpha3: "PAN", numeric: "591", locales: ["es_PA"], default_locale: "es_PA", currency: "USD", latitude: "8.537981", longitude: "-80.782127", currency_name: "US Dollar", languages: ["es"], capital: "Panama City", emoji: "🇵🇦", emojiU: "U+1F1F5 U+1F1E6", fips: "PM", internet: "PA", continent: "Americas", region: "Central America" }, { name: "Papua New Guinea", alpha2: "PG", alpha3: "PNG", numeric: "598", locales: ["en"], default_locale: "en", currency: "PGK", latitude: "-6.314993", longitude: "143.95555", currency_name: "Kina", languages: ["en"], capital: "Port Moresby", emoji: "🇵🇬", emojiU: "U+1F1F5 U+1F1EC", fips: "PP", internet: "PG", continent: "Oceania", region: "Pacific" }, { name: "Paraguay", alpha2: "PY", alpha3: "PRY", numeric: "600", locales: ["es_PY"], default_locale: "es_PY", currency: "PYG", latitude: "-23.442503", longitude: "-58.443832", currency_name: "Guarani", languages: ["es", "gn"], capital: "Asunción", emoji: "🇵🇾", emojiU: "U+1F1F5 U+1F1FE", fips: "PA", internet: "PY", continent: "Americas", region: "South America" }, { name: "Peru", alpha2: "PE", alpha3: "PER", numeric: "604", locales: ["es_PE"], default_locale: "es_PE", currency: "PEN", latitude: "-9.189967", longitude: "-75.015152", currency_name: "Sol", languages: ["es"], capital: "Lima", emoji: "🇵🇪", emojiU: "U+1F1F5 U+1F1EA", fips: "PE", internet: "PE", continent: "Americas", region: "South America" }, { name: "Philippines", alpha2: "PH", alpha3: "PHL", numeric: "608", locales: ["en_PH", "fil_PH"], default_locale: "en_PH", currency: "PHP", latitude: "12.879721", longitude: "121.774017", currency_name: "Philippine Peso", languages: ["en"], capital: "Manila", emoji: "🇵🇭", emojiU: "U+1F1F5 U+1F1ED", fips: "RP", internet: "PH", continent: "Asia", region: "South East Asia" }, { name: "Pitcairn", alpha2: "PN", alpha3: "PCN", numeric: "612", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-24.703615", longitude: "-127.439308", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Adamstown", emoji: "🇵🇳", emojiU: "U+1F1F5 U+1F1F3", fips: "PC", internet: "PN", continent: "Oceania", region: "Pacific" }, { name: "Poland", alpha2: "PL", alpha3: "POL", numeric: "616", locales: ["pl_PL"], default_locale: "pl_PL", currency: "PLN", latitude: "51.919438", longitude: "19.145136", currency_name: "Zloty", languages: ["pl"], capital: "Warsaw", emoji: "🇵🇱", emojiU: "U+1F1F5 U+1F1F1", fips: "PL", internet: "PL", continent: "Europe", region: "Eastern Europe" }, { name: "Portugal", alpha2: "PT", alpha3: "PRT", numeric: "620", locales: ["pt_PT"], default_locale: "pt_PT", currency: "EUR", latitude: "39.399872", longitude: "-8.224454", currency_name: "Euro", languages: ["pt"], capital: "Lisbon", emoji: "🇵🇹", emojiU: "U+1F1F5 U+1F1F9", fips: "PO", internet: "PT", continent: "Europe", region: "South West Europe" }, { name: "Puerto Rico", alpha2: "PR", alpha3: "PRI", numeric: "630", locales: ["es_PR"], default_locale: "es_PR", currency: "USD", latitude: "18.220833", longitude: "-66.590149", currency_name: "US Dollar", languages: ["es", "en"], capital: "San Juan", emoji: "🇵🇷", emojiU: "U+1F1F5 U+1F1F7", fips: "RQ", internet: "PR", continent: "Americas", region: "West Indies" }, { name: "Qatar", alpha2: "QA", alpha3: "QAT", numeric: "634", locales: ["ar_QA"], default_locale: "ar_QA", currency: "QAR", latitude: "25.354826", longitude: "51.183884", currency_name: "Qatari Rial", languages: ["ar"], capital: "Doha", emoji: "🇶🇦", emojiU: "U+1F1F6 U+1F1E6", fips: "QA", internet: "QA", continent: "Asia", region: "South West Asia" }, { name: "Romania", alpha2: "RO", alpha3: "ROU", numeric: "642", locales: ["ro_RO"], default_locale: "ro_RO", currency: "RON", latitude: "45.943161", longitude: "24.96676", currency_name: "Romanian Leu", languages: ["ro"], capital: "Bucharest", emoji: "🇷🇴", emojiU: "U+1F1F7 U+1F1F4", fips: "RO", internet: "RO", continent: "Europe", region: "South East Europe" }, { name: "Russia", alpha2: "RU", alpha3: "RUS", numeric: "643", locales: ["ru_RU"], default_locale: "ru_RU", currency: "RUB", latitude: "61.52401", longitude: "105.318756", currency_name: "Russian Ruble", languages: ["ru"], capital: "Moscow", emoji: "🇷🇺", emojiU: "U+1F1F7 U+1F1FA", fips: "RS", internet: "RU", continent: "Asia", region: "Northern Asia", alternate_names: ["Russian Federation"] }, { name: "Rwanda", alpha2: "RW", alpha3: "RWA", numeric: "646", locales: ["fr_RW", "rw_RW"], default_locale: "fr_RW", currency: "RWF", latitude: "-1.940278", longitude: "29.873888", currency_name: "Rwanda Franc", languages: ["rw", "en", "fr"], capital: "Kigali", emoji: "🇷🇼", emojiU: "U+1F1F7 U+1F1FC", fips: "RW", internet: "RW", continent: "Africa", region: "Central Africa" }, { name: "Réunion", alpha2: "RE", alpha3: "REU", numeric: "638", locales: ["fr_RE"], default_locale: "fr_RE", currency: "RWF", latitude: "-21.115141", longitude: "55.536384", currency_name: "Rwanda Franc", languages: ["fr"], capital: "Saint-Denis", emoji: "🇷🇪", emojiU: "U+1F1F7 U+1F1EA", fips: "RE", internet: "RE", continent: "Africa", region: "Indian Ocean" }, { name: "Saint Barthélemy", alpha2: "BL", alpha3: "BLM", numeric: "652", locales: ["fr_BL"], default_locale: "fr_BL", currency: "EUR", currency_name: "Euro", languages: ["fr"], capital: "Gustavia", emoji: "🇧🇱", emojiU: "U+1F1E7 U+1F1F1", fips: "TB", internet: "BL", continent: "Americas", region: "West Indies" }, { name: "Saint Helena", alpha2: "SH", alpha3: "SHN", numeric: "654", locales: ["en"], default_locale: "en", currency: "SHP", latitude: "-24.143474", longitude: "-10.030696", currency_name: "Saint Helena Pound", languages: ["en"], capital: "Jamestown", emoji: "🇸🇭", emojiU: "U+1F1F8 U+1F1ED", fips: "SH", internet: "SH", continent: "Atlantic Ocean", region: "South Atlantic Ocean", alternate_names: ["Saint Helena, Ascension and Tristan da Cunha"] }, { name: "Saint Kitts and Nevis", alpha2: "KN", alpha3: "KNA", numeric: "659", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.357822", longitude: "-62.782998", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Basseterre", emoji: "🇰🇳", emojiU: "U+1F1F0 U+1F1F3", fips: "SC", internet: "KN", continent: "Americas", region: "West Indies" }, { name: "Saint Lucia", alpha2: "LC", alpha3: "LCA", numeric: "662", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "13.909444", longitude: "-60.978893", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Castries", emoji: "🇱🇨", emojiU: "U+1F1F1 U+1F1E8", fips: "ST", internet: "LC", continent: "Americas", region: "West Indies" }, { name: "Saint Martin", alpha2: "MF", alpha3: "MAF", numeric: "663", locales: ["fr_MF"], default_locale: "fr_MF", currency: "EUR", currency_name: "Euro", languages: ["en", "fr", "nl"], capital: "Marigot", emoji: "🇲🇫", emojiU: "U+1F1F2 U+1F1EB", fips: "RN", internet: "MF", continent: "Americas", region: "West Indies", alternate_names: ["Saint Martin French part"] }, { name: "Saint Pierre and Miquelon", alpha2: "PM", alpha3: "SPM", numeric: "666", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "46.941936", longitude: "-56.27111", currency_name: "Euro", languages: ["fr"], capital: "Saint-Pierre", emoji: "🇵🇲", emojiU: "U+1F1F5 U+1F1F2", fips: "SB", internet: "PM", continent: "Americas", region: "North America" }, { name: "Saint Vincent and the Grenadines", alpha2: "VC", alpha3: "VCT", numeric: "670", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.984305", longitude: "-61.287228", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Kingstown", emoji: "🇻🇨", emojiU: "U+1F1FB U+1F1E8", fips: "VC", internet: "VC", continent: "Americas", region: "West Indies" }, { name: "Samoa", alpha2: "WS", alpha3: "WSM", numeric: "882", locales: ["sm"], default_locale: "sm", currency: "WST", latitude: "-13.759029", longitude: "-172.104629", currency_name: "Tala", languages: ["sm", "en"], capital: "Apia", emoji: "🇼🇸", emojiU: "U+1F1FC U+1F1F8", fips: "WS", internet: "WS", continent: "Oceania", region: "Pacific" }, { name: "San Marino", alpha2: "SM", alpha3: "SMR", numeric: "674", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "43.94236", longitude: "12.457777", currency_name: "Euro", languages: ["it"], capital: "City of San Marino", emoji: "🇸🇲", emojiU: "U+1F1F8 U+1F1F2", fips: "SM", internet: "SM", continent: "Europe", region: "Southern Europe" }, { name: "Sao Tome and Principe", alpha2: "ST", alpha3: "STP", numeric: "678", locales: ["pt"], default_locale: "pt", currency: "STN", latitude: "0.18636", longitude: "6.613081", currency_name: "Dobra", languages: ["pt"], capital: "São Tomé", emoji: "🇸🇹", emojiU: "U+1F1F8 U+1F1F9", fips: "TP", internet: "ST", continent: "Africa", region: "Western Africa" }, { name: "Saudi Arabia", alpha2: "SA", alpha3: "SAU", numeric: "682", locales: ["ar_SA"], default_locale: "ar_SA", currency: "SAR", latitude: "23.885942", longitude: "45.079162", currency_name: "Saudi Riyal", languages: ["ar"], capital: "Riyadh", emoji: "🇸🇦", emojiU: "U+1F1F8 U+1F1E6", fips: "SA", internet: "SA", continent: "Asia", region: "South West Asia" }, { name: "Senegal", alpha2: "SN", alpha3: "SEN", numeric: "686", locales: ["fr_SN", "ff_SN"], default_locale: "fr_SN", currency: "XOF", latitude: "14.497401", longitude: "-14.452362", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Dakar", emoji: "🇸🇳", emojiU: "U+1F1F8 U+1F1F3", fips: "SG", internet: "SN", continent: "Africa", region: "Western Africa" }, { name: "Serbia", alpha2: "RS", alpha3: "SRB", numeric: "688", locales: ["sr_Cyrl_RS", "sr_Latn_RS"], default_locale: "sr_Cyrl_RS", currency: "RSD", latitude: "44.016521", longitude: "21.005859", currency_name: "Serbian Dinar", languages: ["sr"], capital: "Belgrade", emoji: "🇷🇸", emojiU: "U+1F1F7 U+1F1F8", fips: "RI", internet: "RS", continent: "Europe", region: "South East Europe" }, { name: "Seychelles", alpha2: "SC", alpha3: "SYC", numeric: "690", locales: ["fr"], default_locale: "fr", currency: "SCR", latitude: "-4.679574", longitude: "55.491977", currency_name: "Seychelles Rupee", languages: ["fr", "en"], capital: "Victoria", emoji: "🇸🇨", emojiU: "U+1F1F8 U+1F1E8", fips: "SE", internet: "SC", continent: "Africa", region: "Indian Ocean" }, { name: "Sierra Leone", alpha2: "SL", alpha3: "SLE", numeric: "694", locales: ["en"], default_locale: "en", currency: "SLL", latitude: "8.460555", longitude: "-11.779889", currency_name: "Leone", languages: ["en"], capital: "Freetown", emoji: "🇸🇱", emojiU: "U+1F1F8 U+1F1F1", fips: "SL", internet: "SL", continent: "Africa", region: "Western Africa" }, { name: "Singapore", alpha2: "SG", alpha3: "SGP", numeric: "702", locales: ["zh_Hans_SG", "en_SG"], default_locale: "en_SG", currency: "SGD", latitude: "1.352083", longitude: "103.819836", currency_name: "Singapore Dollar", languages: ["en", "ms", "ta", "zh"], capital: "Singapore", emoji: "🇸🇬", emojiU: "U+1F1F8 U+1F1EC", fips: "SN", internet: "SG", continent: "Asia", region: "South East Asia" }, { name: "Sint Maarten", alpha2: "SX", alpha3: "SXM", numeric: "534", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "en"], capital: "Philipsburg", emoji: "🇸🇽", emojiU: "U+1F1F8 U+1F1FD", fips: "NN", internet: "SX", continent: "Americas", region: "West Indies", alternate_names: ["Sint Maarten Dutch part"] }, { name: "Slovakia", alpha2: "SK", alpha3: "SVK", numeric: "703", locales: ["sk_SK"], default_locale: "sk_SK", currency: "EUR", latitude: "48.669026", longitude: "19.699024", currency_name: "Euro", languages: ["sk"], capital: "Bratislava", emoji: "🇸🇰", emojiU: "U+1F1F8 U+1F1F0", fips: "LO", internet: "SK", continent: "Europe", region: "Central Europe" }, { name: "Slovenia", alpha2: "SI", alpha3: "SVN", numeric: "705", locales: ["sl_SI"], default_locale: "sl_SI", currency: "EUR", latitude: "46.151241", longitude: "14.995463", currency_name: "Euro", languages: ["sl"], capital: "Ljubljana", emoji: "🇸🇮", emojiU: "U+1F1F8 U+1F1EE", fips: "SI", internet: "SI", continent: "Europe", region: "South East Europe" }, { name: "Solomon Islands", alpha2: "SB", alpha3: "SLB", numeric: "090", locales: ["en"], default_locale: "en", currency: "SBD", latitude: "-9.64571", longitude: "160.156194", currency_name: "Solomon Islands Dollar", languages: ["en"], capital: "Honiara", emoji: "🇸🇧", emojiU: "U+1F1F8 U+1F1E7", fips: "BP", internet: "SB", continent: "Oceania", region: "Pacific" }, { name: "Somalia", alpha2: "SO", alpha3: "SOM", numeric: "706", locales: ["so_SO"], default_locale: "so_SO", currency: "SOS", latitude: "5.152149", longitude: "46.199616", currency_name: "Somali Shilling", languages: ["so", "ar"], capital: "Mogadishu", emoji: "🇸🇴", emojiU: "U+1F1F8 U+1F1F4", fips: "SO", internet: "SO", continent: "Africa", region: "Eastern Africa" }, { name: "South Africa", alpha2: "ZA", alpha3: "ZAF", numeric: "710", locales: ["af_ZA", "en_ZA", "zu_ZA"], default_locale: "af_ZA", currency: "ZAR", latitude: "-30.559482", longitude: "22.937506", currency_name: "Rand", languages: ["af", "en", "nr", "st", "ss", "tn", "ts", "ve", "xh", "zu"], capital: "Pretoria", emoji: "🇿🇦", emojiU: "U+1F1FF U+1F1E6", fips: "SF", internet: "ZA", continent: "Africa", region: "Southern Africa" }, { name: "South Georgia and the South Sandwich Islands", alpha2: "GS", alpha3: "SGS", numeric: "239", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-54.429579", longitude: "-36.587909", currency_name: "US Dollar", languages: ["en"], capital: "King Edward Point", emoji: "🇬🇸", emojiU: "U+1F1EC U+1F1F8", fips: "SX", internet: "GS", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "South Sudan", alpha2: "SS", alpha3: "SSD", numeric: "728", locales: ["en"], default_locale: "en", currency: "SSP", currency_name: "South Sudanese Pound", languages: ["en"], capital: "Juba", emoji: "🇸🇸", emojiU: "U+1F1F8 U+1F1F8", fips: "OD", internet: "SS", continent: "Africa", region: "Northern Africa" }, { name: "Spain", alpha2: "ES", alpha3: "ESP", numeric: "724", locales: ["eu_ES", "ca_ES", "gl_ES", "es_ES"], default_locale: "es_ES", currency: "EUR", latitude: "40.463667", longitude: "-3.74922", currency_name: "Euro", languages: ["es", "eu", "ca", "gl", "oc"], capital: "Madrid", emoji: "🇪🇸", emojiU: "U+1F1EA U+1F1F8", fips: "SP", internet: "ES", continent: "Europe", region: "South West Europe" }, { name: "Sri Lanka", alpha2: "LK", alpha3: "LKA", numeric: "144", locales: ["si_LK", "ta_LK"], default_locale: "si_LK", currency: "LKR", latitude: "7.873054", longitude: "80.771797", currency_name: "Sri Lanka Rupee", languages: ["si", "ta"], capital: "Colombo", emoji: "🇱🇰", emojiU: "U+1F1F1 U+1F1F0", fips: "CE", internet: "LK", continent: "Asia", region: "South Asia" }, { name: "Sudan", alpha2: "SD", alpha3: "SDN", numeric: "729", locales: ["ar_SD"], default_locale: "ar_SD", currency: "SDG", latitude: "12.862807", longitude: "30.217636", currency_name: "Sudanese Pound", languages: ["ar", "en"], capital: "Khartoum", emoji: "🇸🇩", emojiU: "U+1F1F8 U+1F1E9", fips: "SU", internet: "SD", continent: "Africa", region: "Northern Africa" }, { name: "Suriname", alpha2: "SR", alpha3: "SUR", numeric: "740", locales: ["nl"], default_locale: "nl", currency: "SRD", latitude: "3.919305", longitude: "-56.027783", currency_name: "Surinam Dollar", languages: ["nl"], capital: "Paramaribo", emoji: "🇸🇷", emojiU: "U+1F1F8 U+1F1F7", fips: "NS", internet: "SR", continent: "Americas", region: "South America" }, { name: "Svalbard and Jan Mayen", alpha2: "SJ", alpha3: "SJM", numeric: "744", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "77.553604", longitude: "23.670272", currency_name: "Norwegian Krone", languages: ["no"], capital: "Longyearbyen", emoji: "🇸🇯", emojiU: "U+1F1F8 U+1F1EF", fips: "SV", internet: "SJ", continent: "Europe", region: "Northern Europe" }, { name: "Sweden", alpha2: "SE", alpha3: "SWE", numeric: "752", locales: ["sv_SE"], default_locale: "sv_SE", currency: "SEK", latitude: "60.128161", longitude: "18.643501", currency_name: "Swedish Krona", languages: ["sv"], capital: "Stockholm", emoji: "🇸🇪", emojiU: "U+1F1F8 U+1F1EA", fips: "SW", internet: "SE", continent: "Europe", region: "Northern Europe" }, { name: "Switzerland", alpha2: "CH", alpha3: "CHE", numeric: "756", locales: ["fr_CH", "de_CH", "it_CH", "rm_CH", "gsw_CH"], default_locale: "fr_CH", currency: "CHF", latitude: "46.818188", longitude: "8.227512", currency_name: "Swiss Franc", languages: ["de", "fr", "it"], capital: "Bern", emoji: "🇨🇭", emojiU: "U+1F1E8 U+1F1ED", fips: "SZ", internet: "CH", continent: "Europe", region: "Central Europe" }, { name: "Syrian Arab Republic", alpha2: "SY", alpha3: "SYR", numeric: "760", locales: ["ar_SY"], default_locale: "ar_SY", currency: "SYP", latitude: "34.802075", longitude: "38.996815", currency_name: "Syrian Pound", languages: ["ar"], capital: "Damascus", emoji: "🇸🇾", emojiU: "U+1F1F8 U+1F1FE", fips: "SY", internet: "SY", continent: "Asia", region: "South West Asia" }, { name: "Taiwan", alpha2: "TW", alpha3: "TWN", numeric: "158", locales: ["zh_Hant_TW"], default_locale: "zh_Hant_TW", currency: "TWD", latitude: "23.69781", longitude: "120.960515", currency_name: "New Taiwan Dollar", languages: ["zh"], capital: "Taipei", emoji: "🇹🇼", emojiU: "U+1F1F9 U+1F1FC", fips: "TW", internet: "TW", continent: "Asia", region: "East Asia", alternate_names: ["Province of China Taiwan"] }, { name: "Tajikistan", alpha2: "TJ", alpha3: "TJK", numeric: "762", locales: ["tg"], default_locale: "tg", currency: "TJS", latitude: "38.861034", longitude: "71.276093", currency_name: "Somoni", languages: ["tg", "ru"], capital: "Dushanbe", emoji: "🇹🇯", emojiU: "U+1F1F9 U+1F1EF", fips: "TI", internet: "TJ", continent: "Asia", region: "Central Asia" }, { name: "Tanzania", alpha2: "TZ", alpha3: "TZA", numeric: "834", locales: ["asa_TZ", "bez_TZ", "lag_TZ", "jmc_TZ", "kde_TZ", "mas_TZ", "rof_TZ", "rwk_TZ", "sw_TZ", "vun_TZ"], default_locale: "asa_TZ", currency: "TZS", latitude: "-6.369028", longitude: "34.888822", currency_name: "Tanzanian Shilling", languages: ["sw", "en"], capital: "Dodoma", emoji: "🇹🇿", emojiU: "U+1F1F9 U+1F1FF", fips: "TZ", internet: "TZ", continent: "Africa", region: "Eastern Africa", alternate_names: ["United Republic of Tanzania"] }, { name: "Thailand", alpha2: "TH", alpha3: "THA", numeric: "764", locales: ["th_TH"], default_locale: "th_TH", currency: "THB", latitude: "15.870032", longitude: "100.992541", currency_name: "Baht", languages: ["th"], capital: "Bangkok", emoji: "🇹🇭", emojiU: "U+1F1F9 U+1F1ED", fips: "TH", internet: "TH", continent: "Asia", region: "South East Asia" }, { name: "Timor-Leste", alpha2: "TL", alpha3: "TLS", numeric: "626", locales: ["pt"], default_locale: "pt", currency: "USD", latitude: "-8.874217", longitude: "125.727539", currency_name: "US Dollar", languages: ["pt"], capital: "Dili", emoji: "🇹🇱", emojiU: "U+1F1F9 U+1F1F1", fips: "TT", internet: "TL", continent: "Asia", region: "South East Asia" }, { name: "Togo", alpha2: "TG", alpha3: "TGO", numeric: "768", locales: ["ee_TG", "fr_TG"], default_locale: "fr_TG", currency: "XOF", latitude: "8.619543", longitude: "0.824782", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Lomé", emoji: "🇹🇬", emojiU: "U+1F1F9 U+1F1EC", fips: "TO", internet: "TG", continent: "Africa", region: "Western Africa" }, { name: "Tokelau", alpha2: "TK", alpha3: "TKL", numeric: "772", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-8.967363", longitude: "-171.855881", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Fakaofo", emoji: "🇹🇰", emojiU: "U+1F1F9 U+1F1F0", fips: "TL", internet: "TK", continent: "Oceania", region: "Pacific" }, { name: "Tonga", alpha2: "TO", alpha3: "TON", numeric: "776", locales: ["to_TO"], default_locale: "to_TO", currency: "TOP", latitude: "-21.178986", longitude: "-175.198242", currency_name: "Pa’anga", languages: ["en", "to"], capital: "Nuku'alofa", emoji: "🇹🇴", emojiU: "U+1F1F9 U+1F1F4", fips: "TN", internet: "TO", continent: "Oceania", region: "Pacific" }, { name: "Trinidad and Tobago", alpha2: "TT", alpha3: "TTO", numeric: "780", locales: ["en_TT"], default_locale: "en_TT", currency: "TTD", latitude: "10.691803", longitude: "-61.222503", currency_name: "Trinidad and Tobago Dollar", languages: ["en"], capital: "Port of Spain", emoji: "🇹🇹", emojiU: "U+1F1F9 U+1F1F9", fips: "TD", internet: "TT", continent: "Americas", region: "West Indies" }, { name: "Tunisia", alpha2: "TN", alpha3: "TUN", numeric: "788", locales: ["ar_TN"], default_locale: "ar_TN", currency: "TND", latitude: "33.886917", longitude: "9.537499", currency_name: "Tunisian Dinar", languages: ["ar"], capital: "Tunis", emoji: "🇹🇳", emojiU: "U+1F1F9 U+1F1F3", fips: "TS", internet: "TN", continent: "Africa", region: "Northern Africa" }, { name: "Turkey", alpha2: "TR", alpha3: "TUR", numeric: "792", locales: ["tr_TR"], default_locale: "tr_TR", currency: "TRY", latitude: "38.963745", longitude: "35.243322", currency_name: "Turkish Lira", languages: ["tr"], capital: "Ankara", emoji: "🇹🇷", emojiU: "U+1F1F9 U+1F1F7", fips: "TU", internet: "TR", continent: "Asia", region: "South West Asia" }, { name: "Turkmenistan", alpha2: "TM", alpha3: "TKM", numeric: "795", locales: ["tk"], default_locale: "tk", currency: "TMT", latitude: "38.969719", longitude: "59.556278", currency_name: "Turkmenistan New Manat", languages: ["tk", "ru"], capital: "Ashgabat", emoji: "🇹🇲", emojiU: "U+1F1F9 U+1F1F2", fips: "TX", internet: "TM", continent: "Asia", region: "Central Asia" }, { name: "Turks and Caicos Islands", alpha2: "TC", alpha3: "TCA", numeric: "796", locales: ["en"], default_locale: "en", currency: "USD", latitude: "21.694025", longitude: "-71.797928", currency_name: "US Dollar", languages: ["en"], capital: "Cockburn Town", emoji: "🇹🇨", emojiU: "U+1F1F9 U+1F1E8", fips: "TK", internet: "TC", continent: "Americas", region: "West Indies" }, { name: "Tuvalu", alpha2: "TV", alpha3: "TUV", numeric: "798", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-7.109535", longitude: "177.64933", currency_name: "Australian Dollar", languages: ["en"], capital: "Funafuti", emoji: "🇹🇻", emojiU: "U+1F1F9 U+1F1FB", fips: "TV", internet: "TV", continent: "Oceania", region: "Pacific" }, { name: "Uganda", alpha2: "UG", alpha3: "UGA", numeric: "800", locales: ["cgg_UG", "lg_UG", "nyn_UG", "xog_UG", "teo_UG"], default_locale: "cgg_UG", currency: "UGX", latitude: "1.373333", longitude: "32.290275", currency_name: "Uganda Shilling", languages: ["en", "sw"], capital: "Kampala", emoji: "🇺🇬", emojiU: "U+1F1FA U+1F1EC", fips: "UG", internet: "UG", continent: "Africa", region: "Eastern Africa" }, { name: "Ukraine", alpha2: "UA", alpha3: "UKR", numeric: "804", locales: ["ru_UA", "uk_UA"], default_locale: "uk_UA", currency: "UAH", latitude: "48.379433", longitude: "31.16558", currency_name: "Hryvnia", languages: ["uk"], capital: "Kyiv", emoji: "🇺🇦", emojiU: "U+1F1FA U+1F1E6", fips: "UP", internet: "UA", continent: "Europe", region: "Eastern Europe" }, { name: "United Arab Emirates", alpha2: "AE", alpha3: "ARE", numeric: "784", locales: ["ar_AE"], default_locale: "ar_AE", currency: "AED", latitude: "23.424076", longitude: "53.847818", currency_name: "UAE Dirham", languages: ["ar"], capital: "Abu Dhabi", emoji: "🇦🇪", emojiU: "U+1F1E6 U+1F1EA", fips: "TC", internet: "AE", continent: "Asia", region: "South West Asia" }, { name: "United Kingdom", alpha2: "GB", alpha3: "GBR", numeric: "826", locales: ["kw_GB", "en_GB", "gv_GB", "cy_GB"], default_locale: "en_GB", currency: "GBP", latitude: "55.378051", longitude: "-3.435973", currency_name: "Pound Sterling", languages: ["en"], capital: "London", emoji: "🇬🇧", emojiU: "U+1F1EC U+1F1E7", fips: "UK", internet: "UK", continent: "Europe", region: "Western Europe", alternate_names: ["United Kingdom of Great Britain and Northern Ireland"] }, { name: "United States Minor Outlying Islands", alpha2: "UM", alpha3: "UMI", numeric: "581", locales: ["en_UM"], default_locale: "en_UM", currency: "USD", currency_name: "US Dollar", languages: ["en"], capital: "", emoji: "🇺🇲", emojiU: "U+1F1FA U+1F1F2", fips: "", internet: "US", continent: "Americas", region: "North America" }, { name: "United States of America", alpha2: "US", alpha3: "USA", numeric: "840", locales: ["chr_US", "en_US", "haw_US", "es_US"], default_locale: "en_US", currency: "USD", latitude: "37.09024", longitude: "-95.712891", currency_name: "US Dollar", languages: ["en"], capital: "Washington D.C.", emoji: "🇺🇸", emojiU: "U+1F1FA U+1F1F8", fips: "US", internet: "US", continent: "Americas", region: "North America", alternate_names: ["United States"] }, { name: "Uruguay", alpha2: "UY", alpha3: "URY", numeric: "858", locales: ["es_UY"], default_locale: "es_UY", currency: "UYU", latitude: "-32.522779", longitude: "-55.765835", currency_name: "Peso Uruguayo", languages: ["es"], capital: "Montevideo", emoji: "🇺🇾", emojiU: "U+1F1FA U+1F1FE", fips: "UY", internet: "UY", continent: "Americas", region: "South America" }, { name: "Uzbekistan", alpha2: "UZ", alpha3: "UZB", numeric: "860", locales: ["uz_Cyrl_UZ", "uz_Latn_UZ"], default_locale: "uz_Cyrl_UZ", currency: "UZS", latitude: "41.377491", longitude: "64.585262", currency_name: "Uzbekistan Sum", languages: ["uz", "ru"], capital: "Tashkent", emoji: "🇺🇿", emojiU: "U+1F1FA U+1F1FF", fips: "UZ", internet: "UZ", continent: "Asia", region: "Central Asia" }, { name: "Vanuatu", alpha2: "VU", alpha3: "VUT", numeric: "548", locales: ["bi"], default_locale: "bi", currency: "VUV", latitude: "-15.376706", longitude: "166.959158", currency_name: "Vatu", languages: ["bi", "en", "fr"], capital: "Port Vila", emoji: "🇻🇺", emojiU: "U+1F1FB U+1F1FA", fips: "NH", internet: "VU", continent: "Oceania", region: "Pacific" }, { name: "Venezuela", alpha2: "VE", alpha3: "VEN", numeric: "862", locales: ["es_VE"], default_locale: "es_VE", currency: "VUV", latitude: "6.42375", longitude: "-66.58973", currency_name: "Vatu", languages: ["es"], capital: "Caracas", emoji: "🇻🇪", emojiU: "U+1F1FB U+1F1EA", fips: "VE", internet: "UE", continent: "Americas", region: "South America", alternate_names: ["Bolivarian Republic of Venezuela"] }, { name: "Viet Nam", alpha2: "VN", alpha3: "VNM", numeric: "704", locales: ["vi_VN"], default_locale: "vi_VN", currency: "VND", latitude: "14.058324", longitude: "108.277199", currency_name: "Dong", languages: ["vi"], capital: "Hanoi", emoji: "🇻🇳", emojiU: "U+1F1FB U+1F1F3", fips: "VN", internet: "VN", continent: "Asia", region: "South East Asia" }, { name: "Virgin Islands (British)", alpha2: "VG", alpha3: "VGB", numeric: "092", locales: ["en"], default_locale: "en", currency: "USD", latitude: "18.420695", longitude: "-64.639968", currency_name: "US Dollar", languages: ["en"], capital: "Road Town", emoji: "🇻🇬", emojiU: "U+1F1FB U+1F1EC", fips: "VI", internet: "VG", continent: "Americas", region: "West Indies" }, { name: "Virgin Islands (U.S.)", alpha2: "VI", alpha3: "VIR", numeric: "850", locales: ["en_VI"], default_locale: "en_VI", currency: "USD", latitude: "18.335765", longitude: "-64.896335", currency_name: "US Dollar", languages: ["en"], capital: "Charlotte Amalie", emoji: "🇻🇮", emojiU: "U+1F1FB U+1F1EE", fips: "VQ", internet: "VI", continent: "Americas", region: "West Indies" }, { name: "Wallis and Futuna", alpha2: "WF", alpha3: "WLF", numeric: "876", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-13.768752", longitude: "-177.156097", currency_name: "CFP Franc", languages: ["fr"], capital: "Mata-Utu", emoji: "🇼🇫", emojiU: "U+1F1FC U+1F1EB", fips: "WF", internet: "WF", continent: "Oceania", region: "Pacific" }, { name: "Western Sahara", alpha2: "EH", alpha3: "ESH", numeric: "732", locales: ["es"], default_locale: "es", currency: "MAD", latitude: "24.215527", longitude: "-12.885834", currency_name: "Moroccan Dirham", languages: ["es"], capital: "El Aaiún", emoji: "🇪🇭", emojiU: "U+1F1EA U+1F1ED", fips: "WI", internet: "EH", continent: "Africa", region: "Northern Africa" }, { name: "Yemen", alpha2: "YE", alpha3: "YEM", numeric: "887", locales: ["ar_YE"], default_locale: "ar_YE", currency: "YER", latitude: "15.552727", longitude: "48.516388", currency_name: "Yemeni Rial", languages: ["ar"], capital: "Sana'a", emoji: "🇾🇪", emojiU: "U+1F1FE U+1F1EA", fips: "YM", internet: "YE", continent: "Asia", region: "South West Asia" }, { name: "Zambia", alpha2: "ZM", alpha3: "ZMB", numeric: "894", locales: ["bem_ZM"], default_locale: "bem_ZM", currency: "ZMW", latitude: "-13.133897", longitude: "27.849332", currency_name: "Zambian Kwacha", languages: ["en"], capital: "Lusaka", emoji: "🇿🇲", emojiU: "U+1F1FF U+1F1F2", fips: "ZA", internet: "ZM", continent: "Africa", region: "Southern Africa" }, { name: "Zimbabwe", alpha2: "ZW", alpha3: "ZWE", numeric: "716", locales: ["en_ZW", "nd_ZW", "sn_ZW"], default_locale: "en_ZW", currency: "ZWL", latitude: "-19.015438", longitude: "29.154857", currency_name: "Zimbabwe Dollar", languages: ["en", "sn", "nd"], capital: "Harare", emoji: "🇿🇼", emojiU: "U+1F1FF U+1F1FC", fips: "ZI", internet: "ZW", continent: "Africa", region: "Southern Africa" }, { name: "Åland Islands", alpha2: "AX", alpha3: "ALA", numeric: "248", locales: ["sv"], default_locale: "sv", currency: "EUR", currency_name: "Euro", languages: ["sv"], capital: "Mariehamn", emoji: "🇦🇽", emojiU: "U+1F1E6 U+1F1FD", fips: "AX", internet: "AX", continent: "Europe", region: "Northern Europe" }, { name: "Kosovo", alpha2: "XK", alpha3: "XKX", numeric: "383", locales: ["sq-XK"], default_locale: "sq-XK", currency: "EUR", latitude: "42.602636", longitude: "20.902977", currency_name: "Euro", languages: ["sq", "sr"], capital: "Pristina", emoji: "🇽🇰", emojiU: "U+1F1FD U+1F1F0", fips: "KV", internet: "XK", continent: "Europe", region: "South East Europe" }]; + }); + + // node_modules/.bun/country-locale-map@1.9.11/node_modules/country-locale-map/index.js + var require_country_locale_map = __commonJS((exports, module) => { + var fuzz = require_fuzzball(); + function CLM() { + var clm = {}; + var countries; + if (typeof process !== "undefined" && process && process.env && process.env.CLM_MODE == "INTL") { + countries = require_countries_intl(); + } else { + countries = require_countries(); + } + var countryByAlpha2Code = {}; + var countryByAlpha3Code = {}; + var countryByNumericCode = {}; + var countryByName = {}; + var countryNames = []; + var countriesByContinent = {}; + for (let i = 0;i < countries.length; ++i) { + countryByAlpha2Code[countries[i]["alpha2"]] = countries[i]; + countryByAlpha3Code[countries[i]["alpha3"]] = countries[i]; + countryByNumericCode[countries[i]["numeric"]] = countries[i]; + countryByName[countries[i]["name"]] = countries[i]; + countryNames.push(countries[i]["name"]); + countriesByContinent[countries[i]["continent"]] = [ + ...countriesByContinent[countries[i]["continent"]] || [], + countries[i] + ]; + if (countries[i]["alternate_names"]) { + for (let j = 0;j < countries[i]["alternate_names"].length; ++j) { + countryByName[countries[i]["alternate_names"][j]] = countries[i]; + countryNames.push(countries[i]["alternate_names"][j]); + } + } + } + Object.freeze(countryByAlpha2Code); + Object.freeze(countryByAlpha3Code); + Object.freeze(countryByNumericCode); + Object.freeze(countryByName); + Object.freeze(countryNames); + Object.freeze(countriesByContinent); + clm.getAllCountries = function() { + return countries; + }; + clm.getAlpha3ByAlpha2 = function(alpha2) { + if (countryByAlpha2Code.hasOwnProperty(alpha2)) + return countryByAlpha2Code[alpha2].alpha3; + else + return; + }; + clm.getLocaleByAlpha2 = function(alpha2) { + if (countryByAlpha2Code.hasOwnProperty(alpha2)) + return countryByAlpha2Code[alpha2].default_locale; + else + return; + }; + clm.getCountryNameByAlpha2 = function(alpha2) { + if (countryByAlpha2Code.hasOwnProperty(alpha2)) + return countryByAlpha2Code[alpha2].name; + else + return; + }; + clm.getNumericByAlpha2 = function(alpha2) { + if (countryByAlpha2Code.hasOwnProperty(alpha2)) + return countryByAlpha2Code[alpha2].numeric; + else + return; + }; + clm.getCurrencyByAlpha2 = function(alpha2) { + if (countryByAlpha2Code.hasOwnProperty(alpha2)) + return countryByAlpha2Code[alpha2].currency; + else + return; + }; + clm.getCountryByAlpha2 = function(alpha2) { + return countryByAlpha2Code.hasOwnProperty(alpha2) ? countryByAlpha2Code[alpha2] : undefined; + }; + clm.getAlpha2ByAlpha3 = function(alpha3) { + if (countryByAlpha3Code.hasOwnProperty(alpha3)) + return countryByAlpha3Code[alpha3].alpha2; + else + return; + }; + clm.getLocaleByAlpha3 = function(alpha3) { + if (countryByAlpha3Code.hasOwnProperty(alpha3)) + return countryByAlpha3Code[alpha3].default_locale; + else + return; + }; + clm.getCountryNameByAlpha3 = function(alpha3) { + if (countryByAlpha3Code.hasOwnProperty(alpha3)) + return countryByAlpha3Code[alpha3].name; + else + return; + }; + clm.getNumericByAlpha3 = function(alpha3) { + if (countryByAlpha3Code.hasOwnProperty(alpha3)) + return countryByAlpha3Code[alpha3].numeric; + else + return; + }; + clm.getCurrencyByAlpha3 = function(alpha3) { + if (countryByAlpha3Code.hasOwnProperty(alpha3)) + return countryByAlpha3Code[alpha3].currency; + else + return; + }; + clm.getCountryByAlpha3 = function(alpha3) { + return countryByAlpha3Code.hasOwnProperty(alpha3) ? countryByAlpha3Code[alpha3] : undefined; + }; + clm.getAlpha2ByNumeric = function(numeric) { + if (countryByNumericCode.hasOwnProperty(numeric)) + return countryByNumericCode[numeric].alpha2; + else + return; + }; + clm.getAlpha3ByNumeric = function(numeric) { + if (countryByNumericCode.hasOwnProperty(numeric)) + return countryByNumericCode[numeric].alpha3; + else + return; + }; + clm.getLocaleByNumeric = function(numeric) { + if (countryByNumericCode.hasOwnProperty(numeric)) + return countryByNumericCode[numeric].default_locale; + else + return; + }; + clm.getCountryNameByNumeric = function(numeric) { + if (countryByNumericCode.hasOwnProperty(numeric)) + return countryByNumericCode[numeric].name; + else + return; + }; + clm.getCurrencyByNumeric = function(numeric) { + if (countryByNumericCode.hasOwnProperty(numeric)) + return countryByNumericCode[numeric].currency; + else + return; + }; + clm.getCountryByNumeric = function(numeric) { + return countryByNumericCode.hasOwnProperty(numeric) ? countryByNumericCode[numeric] : undefined; + }; + clm.getAlpha2ByName = function(name, fuzzy) { + if (countryByName.hasOwnProperty(name)) { + return countryByName[name].alpha2; + } else if (fuzzy) { + let match = getClosestMatch(name); + if (match) { + return countryByName[match].alpha2; + } + } + return; + }; + clm.getAlpha3ByName = function(name, fuzzy) { + if (countryByName.hasOwnProperty(name)) { + return countryByName[name].alpha3; + } else if (fuzzy) { + let match = getClosestMatch(name); + if (match) { + return countryByName[match].alpha3; + } + } + return; + }; + clm.getLocaleByName = function(name, fuzzy) { + if (countryByName.hasOwnProperty(name)) { + return countryByName[name].default_locale; + } else if (fuzzy) { + let match = getClosestMatch(name); + if (match) { + return countryByName[match].default_locale; + } + } + return; + }; + clm.getNumericByName = function(name, fuzzy) { + if (countryByName.hasOwnProperty(name)) { + return countryByName[name].numeric; + } else if (fuzzy) { + let match = getClosestMatch(name); + if (match) { + return countryByName[match].numeric; + } + } + return; + }; + clm.getCurrencyByName = function(name, fuzzy) { + if (countryByName.hasOwnProperty(name)) { + return countryByName[name].currency; + } else if (fuzzy) { + let match = getClosestMatch(name); + if (match) { + return countryByName[match].currency; + } + } + return; + }; + clm.getCountryByName = function(name, fuzzy) { + if (countryByName.hasOwnProperty(name)) { + return countryByName[name]; + } else if (fuzzy) { + let match = getClosestMatch(name); + if (match) { + return countryByName[match]; + } + } + return; + }; + clm.getCountriesByContinent = function(name) { + if (countriesByContinent.hasOwnProperty(name)) { + return countriesByContinent[name]; + } + return; + }; + function getClosestMatch(name) { + let result = fuzz.extract(name, countryNames); + if (result[0][1] >= 60) { + return result[0][0]; + } + return; + } + return clm; + } + module.exports = CLM(); + }); + + // src/main.ts + var exports_main = {}; + __export(exports_main, { + startApp: () => startApp, + parseCap: () => parseCap, + getNearestExit: () => getNearestExit + }); + // ../edge-apps-library/dist/utils/theme.js + var DEFAULT_THEME_COLORS = { + primary: "#972EFF", + secondary: "#454BD2", + tertiary: "#FFFFFF", + background: "#C9CDD0" + }; + function getPrimaryColor(accentColor) { + if (!accentColor || accentColor.toLowerCase() === "#ffffff") { + return DEFAULT_THEME_COLORS.primary; + } + return accentColor; + } + function getSecondaryColor(theme, lightColor, darkColor) { + const defaultSecondary = "#adafbe"; + if (theme === "light") { + return !lightColor || lightColor.toLowerCase() === "#ffffff" ? defaultSecondary : lightColor; + } + if (theme === "dark") { + return !darkColor || darkColor.toLowerCase() === "#ffffff" ? defaultSecondary : darkColor; + } + return DEFAULT_THEME_COLORS.secondary; + } + function getThemeColors() { + const settings = screenly.settings; + const primary = getPrimaryColor(settings.screenly_color_accent); + const secondary = getSecondaryColor(settings.theme, settings.screenly_color_light, settings.screenly_color_dark); + return { + primary, + secondary, + tertiary: DEFAULT_THEME_COLORS.tertiary, + background: DEFAULT_THEME_COLORS.background + }; + } + function applyThemeColors(colors) { + document.documentElement.style.setProperty("--theme-color-primary", colors.primary); + document.documentElement.style.setProperty("--theme-color-secondary", colors.secondary); + document.documentElement.style.setProperty("--theme-color-tertiary", colors.tertiary); + document.documentElement.style.setProperty("--theme-color-background", colors.background); + } + function setupTheme() { + const colors = getThemeColors(); + applyThemeColors(colors); + return colors; + } + // ../edge-apps-library/dist/utils/locale.js + var import_tz_lookup = __toESM(require_tz(), 1); + var import_country_locale_map = __toESM(require_country_locale_map(), 1); + // ../edge-apps-library/dist/utils/metadata.js + function getMetadata() { + return screenly.metadata; + } + // ../edge-apps-library/dist/utils/settings.js + function getSettings() { + return screenly.settings; + } + function getCorsProxyUrl() { + return screenly.cors_proxy_url; + } + function signalReady() { + screenly.signalReadyForRendering(); + } + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/util.js + var nameStartChar = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; + var nameChar = nameStartChar + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; + var nameRegexp = "[" + nameStartChar + "][" + nameChar + "]*"; + var regexName = new RegExp("^" + nameRegexp + "$"); + function getAllMatches(string, regex) { + const matches = []; + let match = regex.exec(string); + while (match) { + const allmatches = []; + allmatches.startIndex = regex.lastIndex - match[0].length; + const len = match.length; + for (let index = 0;index < len; index++) { + allmatches.push(match[index]); + } + matches.push(allmatches); + match = regex.exec(string); + } + return matches; + } + var isName = function(string) { + const match = regexName.exec(string); + return !(match === null || typeof match === "undefined"); + }; + function isExist(v) { + return typeof v !== "undefined"; + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/validator.js + var defaultOptions = { + allowBooleanAttributes: false, + unpairedTags: [] + }; + function validate(xmlData, options) { + options = Object.assign({}, defaultOptions, options); + const tags = []; + let tagFound = false; + let reachedRoot = false; + if (xmlData[0] === "\uFEFF") { + xmlData = xmlData.substr(1); + } + for (let i = 0;i < xmlData.length; i++) { + if (xmlData[i] === "<" && xmlData[i + 1] === "?") { + i += 2; + i = readPI(xmlData, i); + if (i.err) + return i; + } else if (xmlData[i] === "<") { + let tagStartPos = i; + i++; + if (xmlData[i] === "!") { + i = readCommentAndCDATA(xmlData, i); + continue; + } else { + let closingTag = false; + if (xmlData[i] === "/") { + closingTag = true; + i++; + } + let tagName = ""; + for (;i < xmlData.length && xmlData[i] !== ">" && xmlData[i] !== " " && xmlData[i] !== "\t" && xmlData[i] !== ` +` && xmlData[i] !== "\r"; i++) { + tagName += xmlData[i]; + } + tagName = tagName.trim(); + if (tagName[tagName.length - 1] === "/") { + tagName = tagName.substring(0, tagName.length - 1); + i--; + } + if (!validateTagName(tagName)) { + let msg; + if (tagName.trim().length === 0) { + msg = "Invalid space after '<'."; + } else { + msg = "Tag '" + tagName + "' is an invalid name."; + } + return getErrorObject("InvalidTag", msg, getLineNumberForPosition(xmlData, i)); + } + const result = readAttributeStr(xmlData, i); + if (result === false) { + return getErrorObject("InvalidAttr", "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i)); + } + let attrStr = result.value; + i = result.index; + if (attrStr[attrStr.length - 1] === "/") { + const attrStrStart = i - attrStr.length; + attrStr = attrStr.substring(0, attrStr.length - 1); + const isValid = validateAttributeString(attrStr, options); + if (isValid === true) { + tagFound = true; + } else { + return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line)); + } + } else if (closingTag) { + if (!result.tagClosed) { + return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i)); + } else if (attrStr.trim().length > 0) { + return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos)); + } else if (tags.length === 0) { + return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos)); + } else { + const otg = tags.pop(); + if (tagName !== otg.tagName) { + let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos); + return getErrorObject("InvalidTag", "Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ", col " + openPos.col + ") instead of closing tag '" + tagName + "'.", getLineNumberForPosition(xmlData, tagStartPos)); + } + if (tags.length == 0) { + reachedRoot = true; + } + } + } else { + const isValid = validateAttributeString(attrStr, options); + if (isValid !== true) { + return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line)); + } + if (reachedRoot === true) { + return getErrorObject("InvalidXml", "Multiple possible root nodes found.", getLineNumberForPosition(xmlData, i)); + } else if (options.unpairedTags.indexOf(tagName) !== -1) {} else { + tags.push({ tagName, tagStartPos }); + } + tagFound = true; + } + for (i++;i < xmlData.length; i++) { + if (xmlData[i] === "<") { + if (xmlData[i + 1] === "!") { + i++; + i = readCommentAndCDATA(xmlData, i); + continue; + } else if (xmlData[i + 1] === "?") { + i = readPI(xmlData, ++i); + if (i.err) + return i; + } else { + break; + } + } else if (xmlData[i] === "&") { + const afterAmp = validateAmpersand(xmlData, i); + if (afterAmp == -1) + return getErrorObject("InvalidChar", "char '&' is not expected.", getLineNumberForPosition(xmlData, i)); + i = afterAmp; + } else { + if (reachedRoot === true && !isWhiteSpace(xmlData[i])) { + return getErrorObject("InvalidXml", "Extra text at the end", getLineNumberForPosition(xmlData, i)); + } + } + } + if (xmlData[i] === "<") { + i--; + } + } + } else { + if (isWhiteSpace(xmlData[i])) { + continue; + } + return getErrorObject("InvalidChar", "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i)); + } + } + if (!tagFound) { + return getErrorObject("InvalidXml", "Start tag expected.", 1); + } else if (tags.length == 1) { + return getErrorObject("InvalidTag", "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos)); + } else if (tags.length > 0) { + return getErrorObject("InvalidXml", "Invalid '" + JSON.stringify(tags.map((t) => t.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 }); + } + return true; + } + function isWhiteSpace(char) { + return char === " " || char === "\t" || char === ` +` || char === "\r"; + } + function readPI(xmlData, i) { + const start = i; + for (;i < xmlData.length; i++) { + if (xmlData[i] == "?" || xmlData[i] == " ") { + const tagname = xmlData.substr(start, i - start); + if (i > 5 && tagname === "xml") { + return getErrorObject("InvalidXml", "XML declaration allowed only at the start of the document.", getLineNumberForPosition(xmlData, i)); + } else if (xmlData[i] == "?" && xmlData[i + 1] == ">") { + i++; + break; + } else { + continue; + } + } + } + return i; + } + function readCommentAndCDATA(xmlData, i) { + if (xmlData.length > i + 5 && xmlData[i + 1] === "-" && xmlData[i + 2] === "-") { + for (i += 3;i < xmlData.length; i++) { + if (xmlData[i] === "-" && xmlData[i + 1] === "-" && xmlData[i + 2] === ">") { + i += 2; + break; + } + } + } else if (xmlData.length > i + 8 && xmlData[i + 1] === "D" && xmlData[i + 2] === "O" && xmlData[i + 3] === "C" && xmlData[i + 4] === "T" && xmlData[i + 5] === "Y" && xmlData[i + 6] === "P" && xmlData[i + 7] === "E") { + let angleBracketsCount = 1; + for (i += 8;i < xmlData.length; i++) { + if (xmlData[i] === "<") { + angleBracketsCount++; + } else if (xmlData[i] === ">") { + angleBracketsCount--; + if (angleBracketsCount === 0) { + break; + } + } + } + } else if (xmlData.length > i + 9 && xmlData[i + 1] === "[" && xmlData[i + 2] === "C" && xmlData[i + 3] === "D" && xmlData[i + 4] === "A" && xmlData[i + 5] === "T" && xmlData[i + 6] === "A" && xmlData[i + 7] === "[") { + for (i += 8;i < xmlData.length; i++) { + if (xmlData[i] === "]" && xmlData[i + 1] === "]" && xmlData[i + 2] === ">") { + i += 2; + break; + } + } + } + return i; + } + var doubleQuote = '"'; + var singleQuote = "'"; + function readAttributeStr(xmlData, i) { + let attrStr = ""; + let startChar = ""; + let tagClosed = false; + for (;i < xmlData.length; i++) { + if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) { + if (startChar === "") { + startChar = xmlData[i]; + } else if (startChar !== xmlData[i]) {} else { + startChar = ""; + } + } else if (xmlData[i] === ">") { + if (startChar === "") { + tagClosed = true; + break; + } + } + attrStr += xmlData[i]; + } + if (startChar !== "") { + return false; + } + return { + value: attrStr, + index: i, + tagClosed + }; + } + var validAttrStrRegxp = new RegExp(`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['"])(([\\s\\S])*?)\\5)?`, "g"); + function validateAttributeString(attrStr, options) { + const matches = getAllMatches(attrStr, validAttrStrRegxp); + const attrNames = {}; + for (let i = 0;i < matches.length; i++) { + if (matches[i][1].length === 0) { + return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i])); + } else if (matches[i][3] !== undefined && matches[i][4] === undefined) { + return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i])); + } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) { + return getErrorObject("InvalidAttr", "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i])); + } + const attrName = matches[i][2]; + if (!validateAttrName(attrName)) { + return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i])); + } + if (!attrNames.hasOwnProperty(attrName)) { + attrNames[attrName] = 1; + } else { + return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i])); + } + } + return true; + } + function validateNumberAmpersand(xmlData, i) { + let re = /\d/; + if (xmlData[i] === "x") { + i++; + re = /[\da-fA-F]/; + } + for (;i < xmlData.length; i++) { + if (xmlData[i] === ";") + return i; + if (!xmlData[i].match(re)) + break; + } + return -1; + } + function validateAmpersand(xmlData, i) { + i++; + if (xmlData[i] === ";") + return -1; + if (xmlData[i] === "#") { + i++; + return validateNumberAmpersand(xmlData, i); + } + let count = 0; + for (;i < xmlData.length; i++, count++) { + if (xmlData[i].match(/\w/) && count < 20) + continue; + if (xmlData[i] === ";") + break; + return -1; + } + return i; + } + function getErrorObject(code, message, lineNumber) { + return { + err: { + code, + msg: message, + line: lineNumber.line || lineNumber, + col: lineNumber.col + } + }; + } + function validateAttrName(attrName) { + return isName(attrName); + } + function validateTagName(tagname) { + return isName(tagname); + } + function getLineNumberForPosition(xmlData, index) { + const lines = xmlData.substring(0, index).split(/\r?\n/); + return { + line: lines.length, + col: lines[lines.length - 1].length + 1 + }; + } + function getPositionFromMatch(match) { + return match.startIndex + match[1].length; + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js + var defaultOptions2 = { + preserveOrder: false, + attributeNamePrefix: "@_", + attributesGroupName: false, + textNodeName: "#text", + ignoreAttributes: true, + removeNSPrefix: false, + allowBooleanAttributes: false, + parseTagValue: true, + parseAttributeValue: false, + trimValues: true, + cdataPropName: false, + numberParseOptions: { + hex: true, + leadingZeros: true, + eNotation: true + }, + tagValueProcessor: function(tagName, val) { + return val; + }, + attributeValueProcessor: function(attrName, val) { + return val; + }, + stopNodes: [], + alwaysCreateTextNode: false, + isArray: () => false, + commentPropName: false, + unpairedTags: [], + processEntities: true, + htmlEntities: false, + ignoreDeclaration: false, + ignorePiTags: false, + transformTagName: false, + transformAttributeName: false, + updateTag: function(tagName, jPath, attrs) { + return tagName; + }, + captureMetaData: false + }; + var buildOptions = function(options) { + return Object.assign({}, defaultOptions2, options); + }; + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js + var METADATA_SYMBOL; + if (typeof Symbol !== "function") { + METADATA_SYMBOL = "@@xmlMetadata"; + } else { + METADATA_SYMBOL = Symbol("XML Node Metadata"); + } + + class XmlNode { + constructor(tagname) { + this.tagname = tagname; + this.child = []; + this[":@"] = {}; + } + add(key, val) { + if (key === "__proto__") + key = "#__proto__"; + this.child.push({ [key]: val }); + } + addChild(node, startIndex) { + if (node.tagname === "__proto__") + node.tagname = "#__proto__"; + if (node[":@"] && Object.keys(node[":@"]).length > 0) { + this.child.push({ [node.tagname]: node.child, [":@"]: node[":@"] }); + } else { + this.child.push({ [node.tagname]: node.child }); + } + if (startIndex !== undefined) { + this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex }; + } + } + static getMetaDataSymbol() { + return METADATA_SYMBOL; + } + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js + class DocTypeReader { + constructor(processEntities) { + this.suppressValidationErr = !processEntities; + } + readDocType(xmlData, i) { + const entities = {}; + if (xmlData[i + 3] === "O" && xmlData[i + 4] === "C" && xmlData[i + 5] === "T" && xmlData[i + 6] === "Y" && xmlData[i + 7] === "P" && xmlData[i + 8] === "E") { + i = i + 9; + let angleBracketsCount = 1; + let hasBody = false, comment = false; + let exp = ""; + for (;i < xmlData.length; i++) { + if (xmlData[i] === "<" && !comment) { + if (hasBody && hasSeq(xmlData, "!ENTITY", i)) { + i += 7; + let entityName, val; + [entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr); + if (val.indexOf("&") === -1) + entities[entityName] = { + regx: RegExp(`&${entityName};`, "g"), + val + }; + } else if (hasBody && hasSeq(xmlData, "!ELEMENT", i)) { + i += 8; + const { index } = this.readElementExp(xmlData, i + 1); + i = index; + } else if (hasBody && hasSeq(xmlData, "!ATTLIST", i)) { + i += 8; + } else if (hasBody && hasSeq(xmlData, "!NOTATION", i)) { + i += 9; + const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr); + i = index; + } else if (hasSeq(xmlData, "!--", i)) + comment = true; + else + throw new Error(`Invalid DOCTYPE`); + angleBracketsCount++; + exp = ""; + } else if (xmlData[i] === ">") { + if (comment) { + if (xmlData[i - 1] === "-" && xmlData[i - 2] === "-") { + comment = false; + angleBracketsCount--; + } + } else { + angleBracketsCount--; + } + if (angleBracketsCount === 0) { + break; + } + } else if (xmlData[i] === "[") { + hasBody = true; + } else { + exp += xmlData[i]; + } + } + if (angleBracketsCount !== 0) { + throw new Error(`Unclosed DOCTYPE`); + } + } else { + throw new Error(`Invalid Tag instead of DOCTYPE`); + } + return { entities, i }; + } + readEntityExp(xmlData, i) { + i = skipWhitespace(xmlData, i); + let entityName = ""; + while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") { + entityName += xmlData[i]; + i++; + } + validateEntityName(entityName); + i = skipWhitespace(xmlData, i); + if (!this.suppressValidationErr) { + if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") { + throw new Error("External entities are not supported"); + } else if (xmlData[i] === "%") { + throw new Error("Parameter entities are not supported"); + } + } + let entityValue = ""; + [i, entityValue] = this.readIdentifierVal(xmlData, i, "entity"); + i--; + return [entityName, entityValue, i]; + } + readNotationExp(xmlData, i) { + i = skipWhitespace(xmlData, i); + let notationName = ""; + while (i < xmlData.length && !/\s/.test(xmlData[i])) { + notationName += xmlData[i]; + i++; + } + !this.suppressValidationErr && validateEntityName(notationName); + i = skipWhitespace(xmlData, i); + const identifierType = xmlData.substring(i, i + 6).toUpperCase(); + if (!this.suppressValidationErr && identifierType !== "SYSTEM" && identifierType !== "PUBLIC") { + throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`); + } + i += identifierType.length; + i = skipWhitespace(xmlData, i); + let publicIdentifier = null; + let systemIdentifier = null; + if (identifierType === "PUBLIC") { + [i, publicIdentifier] = this.readIdentifierVal(xmlData, i, "publicIdentifier"); + i = skipWhitespace(xmlData, i); + if (xmlData[i] === '"' || xmlData[i] === "'") { + [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier"); + } + } else if (identifierType === "SYSTEM") { + [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier"); + if (!this.suppressValidationErr && !systemIdentifier) { + throw new Error("Missing mandatory system identifier for SYSTEM notation"); + } + } + return { notationName, publicIdentifier, systemIdentifier, index: --i }; + } + readIdentifierVal(xmlData, i, type) { + let identifierVal = ""; + const startChar = xmlData[i]; + if (startChar !== '"' && startChar !== "'") { + throw new Error(`Expected quoted string, found "${startChar}"`); + } + i++; + while (i < xmlData.length && xmlData[i] !== startChar) { + identifierVal += xmlData[i]; + i++; + } + if (xmlData[i] !== startChar) { + throw new Error(`Unterminated ${type} value`); + } + i++; + return [i, identifierVal]; + } + readElementExp(xmlData, i) { + i = skipWhitespace(xmlData, i); + let elementName = ""; + while (i < xmlData.length && !/\s/.test(xmlData[i])) { + elementName += xmlData[i]; + i++; + } + if (!this.suppressValidationErr && !isName(elementName)) { + throw new Error(`Invalid element name: "${elementName}"`); + } + i = skipWhitespace(xmlData, i); + let contentModel = ""; + if (xmlData[i] === "E" && hasSeq(xmlData, "MPTY", i)) + i += 4; + else if (xmlData[i] === "A" && hasSeq(xmlData, "NY", i)) + i += 2; + else if (xmlData[i] === "(") { + i++; + while (i < xmlData.length && xmlData[i] !== ")") { + contentModel += xmlData[i]; + i++; + } + if (xmlData[i] !== ")") { + throw new Error("Unterminated content model"); + } + } else if (!this.suppressValidationErr) { + throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`); + } + return { + elementName, + contentModel: contentModel.trim(), + index: i + }; + } + readAttlistExp(xmlData, i) { + i = skipWhitespace(xmlData, i); + let elementName = ""; + while (i < xmlData.length && !/\s/.test(xmlData[i])) { + elementName += xmlData[i]; + i++; + } + validateEntityName(elementName); + i = skipWhitespace(xmlData, i); + let attributeName = ""; + while (i < xmlData.length && !/\s/.test(xmlData[i])) { + attributeName += xmlData[i]; + i++; + } + if (!validateEntityName(attributeName)) { + throw new Error(`Invalid attribute name: "${attributeName}"`); + } + i = skipWhitespace(xmlData, i); + let attributeType = ""; + if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") { + attributeType = "NOTATION"; + i += 8; + i = skipWhitespace(xmlData, i); + if (xmlData[i] !== "(") { + throw new Error(`Expected '(', found "${xmlData[i]}"`); + } + i++; + let allowedNotations = []; + while (i < xmlData.length && xmlData[i] !== ")") { + let notation = ""; + while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") { + notation += xmlData[i]; + i++; + } + notation = notation.trim(); + if (!validateEntityName(notation)) { + throw new Error(`Invalid notation name: "${notation}"`); + } + allowedNotations.push(notation); + if (xmlData[i] === "|") { + i++; + i = skipWhitespace(xmlData, i); + } + } + if (xmlData[i] !== ")") { + throw new Error("Unterminated list of notations"); + } + i++; + attributeType += " (" + allowedNotations.join("|") + ")"; + } else { + while (i < xmlData.length && !/\s/.test(xmlData[i])) { + attributeType += xmlData[i]; + i++; + } + const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"]; + if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) { + throw new Error(`Invalid attribute type: "${attributeType}"`); + } + } + i = skipWhitespace(xmlData, i); + let defaultValue = ""; + if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") { + defaultValue = "#REQUIRED"; + i += 8; + } else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") { + defaultValue = "#IMPLIED"; + i += 7; + } else { + [i, defaultValue] = this.readIdentifierVal(xmlData, i, "ATTLIST"); + } + return { + elementName, + attributeName, + attributeType, + defaultValue, + index: i + }; + } + } + var skipWhitespace = (data, index) => { + while (index < data.length && /\s/.test(data[index])) { + index++; + } + return index; + }; + function hasSeq(data, seq, i) { + for (let j = 0;j < seq.length; j++) { + if (seq[j] !== data[i + j + 1]) + return false; + } + return true; + } + function validateEntityName(name) { + if (isName(name)) + return name; + else + throw new Error(`Invalid entity name ${name}`); + } + + // node_modules/.bun/strnum@2.1.1/node_modules/strnum/strnum.js + var hexRegex = /^[-+]?0x[a-fA-F0-9]+$/; + var numRegex = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/; + var consider = { + hex: true, + leadingZeros: true, + decimalPoint: ".", + eNotation: true + }; + function toNumber(str, options = {}) { + options = Object.assign({}, consider, options); + if (!str || typeof str !== "string") + return str; + let trimmedStr = str.trim(); + if (options.skipLike !== undefined && options.skipLike.test(trimmedStr)) + return str; + else if (str === "0") + return 0; + else if (options.hex && hexRegex.test(trimmedStr)) { + return parse_int(trimmedStr, 16); + } else if (trimmedStr.search(/.+[eE].+/) !== -1) { + return resolveEnotation(str, trimmedStr, options); + } else { + const match = numRegex.exec(trimmedStr); + if (match) { + const sign = match[1] || ""; + const leadingZeros = match[2]; + let numTrimmedByZeros = trimZeros(match[3]); + const decimalAdjacentToLeadingZeros = sign ? str[leadingZeros.length + 1] === "." : str[leadingZeros.length] === "."; + if (!options.leadingZeros && (leadingZeros.length > 1 || leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros)) { + return str; + } else { + const num = Number(trimmedStr); + const parsedStr = String(num); + if (num === 0) + return num; + if (parsedStr.search(/[eE]/) !== -1) { + if (options.eNotation) + return num; + else + return str; + } else if (trimmedStr.indexOf(".") !== -1) { + if (parsedStr === "0") + return num; + else if (parsedStr === numTrimmedByZeros) + return num; + else if (parsedStr === `${sign}${numTrimmedByZeros}`) + return num; + else + return str; + } + let n = leadingZeros ? numTrimmedByZeros : trimmedStr; + if (leadingZeros) { + return n === parsedStr || sign + n === parsedStr ? num : str; + } else { + return n === parsedStr || n === sign + parsedStr ? num : str; + } + } + } else { + return str; + } + } + } + var eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/; + function resolveEnotation(str, trimmedStr, options) { + if (!options.eNotation) + return str; + const notation = trimmedStr.match(eNotationRegx); + if (notation) { + let sign = notation[1] || ""; + const eChar = notation[3].indexOf("e") === -1 ? "E" : "e"; + const leadingZeros = notation[2]; + const eAdjacentToLeadingZeros = sign ? str[leadingZeros.length + 1] === eChar : str[leadingZeros.length] === eChar; + if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) + return str; + else if (leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) { + return Number(trimmedStr); + } else if (options.leadingZeros && !eAdjacentToLeadingZeros) { + trimmedStr = (notation[1] || "") + notation[3]; + return Number(trimmedStr); + } else + return str; + } else { + return str; + } + } + function trimZeros(numStr) { + if (numStr && numStr.indexOf(".") !== -1) { + numStr = numStr.replace(/0+$/, ""); + if (numStr === ".") + numStr = "0"; + else if (numStr[0] === ".") + numStr = "0" + numStr; + else if (numStr[numStr.length - 1] === ".") + numStr = numStr.substring(0, numStr.length - 1); + return numStr; + } + return numStr; + } + function parse_int(numStr, base) { + if (parseInt) + return parseInt(numStr, base); + else if (Number.parseInt) + return Number.parseInt(numStr, base); + else if (window && window.parseInt) + return window.parseInt(numStr, base); + else + throw new Error("parseInt, Number.parseInt, window.parseInt are not supported"); + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/ignoreAttributes.js + function getIgnoreAttributesFn(ignoreAttributes) { + if (typeof ignoreAttributes === "function") { + return ignoreAttributes; + } + if (Array.isArray(ignoreAttributes)) { + return (attrName) => { + for (const pattern of ignoreAttributes) { + if (typeof pattern === "string" && attrName === pattern) { + return true; + } + if (pattern instanceof RegExp && pattern.test(attrName)) { + return true; + } + } + }; + } + return () => false; + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js + class OrderedObjParser { + constructor(options) { + this.options = options; + this.currentNode = null; + this.tagsNodeStack = []; + this.docTypeEntities = {}; + this.lastEntities = { + apos: { regex: /&(apos|#39|#x27);/g, val: "'" }, + gt: { regex: /&(gt|#62|#x3E);/g, val: ">" }, + lt: { regex: /&(lt|#60|#x3C);/g, val: "<" }, + quot: { regex: /&(quot|#34|#x22);/g, val: '"' } + }; + this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" }; + this.htmlEntities = { + space: { regex: /&(nbsp|#160);/g, val: " " }, + cent: { regex: /&(cent|#162);/g, val: "¢" }, + pound: { regex: /&(pound|#163);/g, val: "£" }, + yen: { regex: /&(yen|#165);/g, val: "¥" }, + euro: { regex: /&(euro|#8364);/g, val: "€" }, + copyright: { regex: /&(copy|#169);/g, val: "©" }, + reg: { regex: /&(reg|#174);/g, val: "®" }, + inr: { regex: /&(inr|#8377);/g, val: "₹" }, + num_dec: { regex: /&#([0-9]{1,7});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) }, + num_hex: { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) } + }; + this.addExternalEntities = addExternalEntities; + this.parseXml = parseXml; + this.parseTextData = parseTextData; + this.resolveNameSpace = resolveNameSpace; + this.buildAttributesMap = buildAttributesMap; + this.isItStopNode = isItStopNode; + this.replaceEntitiesValue = replaceEntitiesValue; + this.readStopNodeData = readStopNodeData; + this.saveTextToParentTag = saveTextToParentTag; + this.addChild = addChild; + this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes); + if (this.options.stopNodes && this.options.stopNodes.length > 0) { + this.stopNodesExact = new Set; + this.stopNodesWildcard = new Set; + for (let i = 0;i < this.options.stopNodes.length; i++) { + const stopNodeExp = this.options.stopNodes[i]; + if (typeof stopNodeExp !== "string") + continue; + if (stopNodeExp.startsWith("*.")) { + this.stopNodesWildcard.add(stopNodeExp.substring(2)); + } else { + this.stopNodesExact.add(stopNodeExp); + } + } + } + } + } + function addExternalEntities(externalEntities) { + const entKeys = Object.keys(externalEntities); + for (let i = 0;i < entKeys.length; i++) { + const ent = entKeys[i]; + this.lastEntities[ent] = { + regex: new RegExp("&" + ent + ";", "g"), + val: externalEntities[ent] + }; + } + } + function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) { + if (val !== undefined) { + if (this.options.trimValues && !dontTrim) { + val = val.trim(); + } + if (val.length > 0) { + if (!escapeEntities) + val = this.replaceEntitiesValue(val); + const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode); + if (newval === null || newval === undefined) { + return val; + } else if (typeof newval !== typeof val || newval !== val) { + return newval; + } else if (this.options.trimValues) { + return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions); + } else { + const trimmedVal = val.trim(); + if (trimmedVal === val) { + return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions); + } else { + return val; + } + } + } + } + } + function resolveNameSpace(tagname) { + if (this.options.removeNSPrefix) { + const tags = tagname.split(":"); + const prefix = tagname.charAt(0) === "/" ? "/" : ""; + if (tags[0] === "xmlns") { + return ""; + } + if (tags.length === 2) { + tagname = prefix + tags[1]; + } + } + return tagname; + } + var attrsRegx = new RegExp(`([^\\s=]+)\\s*(=\\s*(['"])([\\s\\S]*?)\\3)?`, "gm"); + function buildAttributesMap(attrStr, jPath, tagName) { + if (this.options.ignoreAttributes !== true && typeof attrStr === "string") { + const matches = getAllMatches(attrStr, attrsRegx); + const len = matches.length; + const attrs = {}; + for (let i = 0;i < len; i++) { + const attrName = this.resolveNameSpace(matches[i][1]); + if (this.ignoreAttributesFn(attrName, jPath)) { + continue; + } + let oldVal = matches[i][4]; + let aName = this.options.attributeNamePrefix + attrName; + if (attrName.length) { + if (this.options.transformAttributeName) { + aName = this.options.transformAttributeName(aName); + } + if (aName === "__proto__") + aName = "#__proto__"; + if (oldVal !== undefined) { + if (this.options.trimValues) { + oldVal = oldVal.trim(); + } + oldVal = this.replaceEntitiesValue(oldVal); + const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath); + if (newVal === null || newVal === undefined) { + attrs[aName] = oldVal; + } else if (typeof newVal !== typeof oldVal || newVal !== oldVal) { + attrs[aName] = newVal; + } else { + attrs[aName] = parseValue(oldVal, this.options.parseAttributeValue, this.options.numberParseOptions); + } + } else if (this.options.allowBooleanAttributes) { + attrs[aName] = true; + } + } + } + if (!Object.keys(attrs).length) { + return; + } + if (this.options.attributesGroupName) { + const attrCollection = {}; + attrCollection[this.options.attributesGroupName] = attrs; + return attrCollection; + } + return attrs; + } + } + var parseXml = function(xmlData) { + xmlData = xmlData.replace(/\r\n?/g, ` +`); + const xmlObj = new XmlNode("!xml"); + let currentNode = xmlObj; + let textData = ""; + let jPath = ""; + const docTypeReader = new DocTypeReader(this.options.processEntities); + for (let i = 0;i < xmlData.length; i++) { + const ch = xmlData[i]; + if (ch === "<") { + if (xmlData[i + 1] === "/") { + const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed."); + let tagName = xmlData.substring(i + 2, closeIndex).trim(); + if (this.options.removeNSPrefix) { + const colonIndex = tagName.indexOf(":"); + if (colonIndex !== -1) { + tagName = tagName.substr(colonIndex + 1); + } + } + if (this.options.transformTagName) { + tagName = this.options.transformTagName(tagName); + } + if (currentNode) { + textData = this.saveTextToParentTag(textData, currentNode, jPath); + } + const lastTagName = jPath.substring(jPath.lastIndexOf(".") + 1); + if (tagName && this.options.unpairedTags.indexOf(tagName) !== -1) { + throw new Error(`Unpaired tag can not be used as closing tag: `); + } + let propIndex = 0; + if (lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1) { + propIndex = jPath.lastIndexOf(".", jPath.lastIndexOf(".") - 1); + this.tagsNodeStack.pop(); + } else { + propIndex = jPath.lastIndexOf("."); + } + jPath = jPath.substring(0, propIndex); + currentNode = this.tagsNodeStack.pop(); + textData = ""; + i = closeIndex; + } else if (xmlData[i + 1] === "?") { + let tagData = readTagExp(xmlData, i, false, "?>"); + if (!tagData) + throw new Error("Pi Tag is not closed."); + textData = this.saveTextToParentTag(textData, currentNode, jPath); + if (this.options.ignoreDeclaration && tagData.tagName === "?xml" || this.options.ignorePiTags) {} else { + const childNode = new XmlNode(tagData.tagName); + childNode.add(this.options.textNodeName, ""); + if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) { + childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName); + } + this.addChild(currentNode, childNode, jPath, i); + } + i = tagData.closeIndex + 1; + } else if (xmlData.substr(i + 1, 3) === "!--") { + const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed."); + if (this.options.commentPropName) { + const comment = xmlData.substring(i + 4, endIndex - 2); + textData = this.saveTextToParentTag(textData, currentNode, jPath); + currentNode.add(this.options.commentPropName, [{ [this.options.textNodeName]: comment }]); + } + i = endIndex; + } else if (xmlData.substr(i + 1, 2) === "!D") { + const result = docTypeReader.readDocType(xmlData, i); + this.docTypeEntities = result.entities; + i = result.i; + } else if (xmlData.substr(i + 1, 2) === "![") { + const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2; + const tagExp = xmlData.substring(i + 9, closeIndex); + textData = this.saveTextToParentTag(textData, currentNode, jPath); + let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true); + if (val == undefined) + val = ""; + if (this.options.cdataPropName) { + currentNode.add(this.options.cdataPropName, [{ [this.options.textNodeName]: tagExp }]); + } else { + currentNode.add(this.options.textNodeName, val); + } + i = closeIndex + 2; + } else { + let result = readTagExp(xmlData, i, this.options.removeNSPrefix); + let tagName = result.tagName; + const rawTagName = result.rawTagName; + let tagExp = result.tagExp; + let attrExpPresent = result.attrExpPresent; + let closeIndex = result.closeIndex; + if (this.options.transformTagName) { + tagName = this.options.transformTagName(tagName); + } + if (currentNode && textData) { + if (currentNode.tagname !== "!xml") { + textData = this.saveTextToParentTag(textData, currentNode, jPath, false); + } + } + const lastTag = currentNode; + if (lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1) { + currentNode = this.tagsNodeStack.pop(); + jPath = jPath.substring(0, jPath.lastIndexOf(".")); + } + if (tagName !== xmlObj.tagname) { + jPath += jPath ? "." + tagName : tagName; + } + const startIndex = i; + if (this.isItStopNode(this.stopNodesExact, this.stopNodesWildcard, jPath, tagName)) { + let tagContent = ""; + if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) { + if (tagName[tagName.length - 1] === "/") { + tagName = tagName.substr(0, tagName.length - 1); + jPath = jPath.substr(0, jPath.length - 1); + tagExp = tagName; + } else { + tagExp = tagExp.substr(0, tagExp.length - 1); + } + i = result.closeIndex; + } else if (this.options.unpairedTags.indexOf(tagName) !== -1) { + i = result.closeIndex; + } else { + const result2 = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1); + if (!result2) + throw new Error(`Unexpected end of ${rawTagName}`); + i = result2.i; + tagContent = result2.tagContent; + } + const childNode = new XmlNode(tagName); + if (tagName !== tagExp && attrExpPresent) { + childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); + } + if (tagContent) { + tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true); + } + jPath = jPath.substr(0, jPath.lastIndexOf(".")); + childNode.add(this.options.textNodeName, tagContent); + this.addChild(currentNode, childNode, jPath, startIndex); + } else { + if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) { + if (tagName[tagName.length - 1] === "/") { + tagName = tagName.substr(0, tagName.length - 1); + jPath = jPath.substr(0, jPath.length - 1); + tagExp = tagName; + } else { + tagExp = tagExp.substr(0, tagExp.length - 1); + } + if (this.options.transformTagName) { + tagName = this.options.transformTagName(tagName); + } + const childNode = new XmlNode(tagName); + if (tagName !== tagExp && attrExpPresent) { + childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); + } + this.addChild(currentNode, childNode, jPath, startIndex); + jPath = jPath.substr(0, jPath.lastIndexOf(".")); + } else { + const childNode = new XmlNode(tagName); + this.tagsNodeStack.push(currentNode); + if (tagName !== tagExp && attrExpPresent) { + childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); + } + this.addChild(currentNode, childNode, jPath, startIndex); + currentNode = childNode; + } + textData = ""; + i = closeIndex; + } + } + } else { + textData += xmlData[i]; + } + } + return xmlObj.child; + }; + function addChild(currentNode, childNode, jPath, startIndex) { + if (!this.options.captureMetaData) + startIndex = undefined; + const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"]); + if (result === false) {} else if (typeof result === "string") { + childNode.tagname = result; + currentNode.addChild(childNode, startIndex); + } else { + currentNode.addChild(childNode, startIndex); + } + } + var replaceEntitiesValue = function(val) { + if (this.options.processEntities) { + for (let entityName in this.docTypeEntities) { + const entity = this.docTypeEntities[entityName]; + val = val.replace(entity.regx, entity.val); + } + for (let entityName in this.lastEntities) { + const entity = this.lastEntities[entityName]; + val = val.replace(entity.regex, entity.val); + } + if (this.options.htmlEntities) { + for (let entityName in this.htmlEntities) { + const entity = this.htmlEntities[entityName]; + val = val.replace(entity.regex, entity.val); + } + } + val = val.replace(this.ampEntity.regex, this.ampEntity.val); + } + return val; + }; + function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) { + if (textData) { + if (isLeafNode === undefined) + isLeafNode = currentNode.child.length === 0; + textData = this.parseTextData(textData, currentNode.tagname, jPath, false, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false, isLeafNode); + if (textData !== undefined && textData !== "") + currentNode.add(this.options.textNodeName, textData); + textData = ""; + } + return textData; + } + function isItStopNode(stopNodesExact, stopNodesWildcard, jPath, currentTagName) { + if (stopNodesWildcard && stopNodesWildcard.has(currentTagName)) + return true; + if (stopNodesExact && stopNodesExact.has(jPath)) + return true; + return false; + } + function tagExpWithClosingIndex(xmlData, i, closingChar = ">") { + let attrBoundary; + let tagExp = ""; + for (let index = i;index < xmlData.length; index++) { + let ch = xmlData[index]; + if (attrBoundary) { + if (ch === attrBoundary) + attrBoundary = ""; + } else if (ch === '"' || ch === "'") { + attrBoundary = ch; + } else if (ch === closingChar[0]) { + if (closingChar[1]) { + if (xmlData[index + 1] === closingChar[1]) { + return { + data: tagExp, + index + }; + } + } else { + return { + data: tagExp, + index + }; + } + } else if (ch === "\t") { + ch = " "; + } + tagExp += ch; + } + } + function findClosingIndex(xmlData, str, i, errMsg) { + const closingIndex = xmlData.indexOf(str, i); + if (closingIndex === -1) { + throw new Error(errMsg); + } else { + return closingIndex + str.length - 1; + } + } + function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") { + const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar); + if (!result) + return; + let tagExp = result.data; + const closeIndex = result.index; + const separatorIndex = tagExp.search(/\s/); + let tagName = tagExp; + let attrExpPresent = true; + if (separatorIndex !== -1) { + tagName = tagExp.substring(0, separatorIndex); + tagExp = tagExp.substring(separatorIndex + 1).trimStart(); + } + const rawTagName = tagName; + if (removeNSPrefix) { + const colonIndex = tagName.indexOf(":"); + if (colonIndex !== -1) { + tagName = tagName.substr(colonIndex + 1); + attrExpPresent = tagName !== result.data.substr(colonIndex + 1); + } + } + return { + tagName, + tagExp, + closeIndex, + attrExpPresent, + rawTagName + }; + } + function readStopNodeData(xmlData, tagName, i) { + const startIndex = i; + let openTagCount = 1; + for (;i < xmlData.length; i++) { + if (xmlData[i] === "<") { + if (xmlData[i + 1] === "/") { + const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`); + let closeTagName = xmlData.substring(i + 2, closeIndex).trim(); + if (closeTagName === tagName) { + openTagCount--; + if (openTagCount === 0) { + return { + tagContent: xmlData.substring(startIndex, i), + i: closeIndex + }; + } + } + i = closeIndex; + } else if (xmlData[i + 1] === "?") { + const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed."); + i = closeIndex; + } else if (xmlData.substr(i + 1, 3) === "!--") { + const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed."); + i = closeIndex; + } else if (xmlData.substr(i + 1, 2) === "![") { + const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2; + i = closeIndex; + } else { + const tagData = readTagExp(xmlData, i, ">"); + if (tagData) { + const openTagName = tagData && tagData.tagName; + if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") { + openTagCount++; + } + i = tagData.closeIndex; + } + } + } + } + } + function parseValue(val, shouldParse, options) { + if (shouldParse && typeof val === "string") { + const newval = val.trim(); + if (newval === "true") + return true; + else if (newval === "false") + return false; + else + return toNumber(val, options); + } else { + if (isExist(val)) { + return val; + } else { + return ""; + } + } + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/node2json.js + var METADATA_SYMBOL2 = XmlNode.getMetaDataSymbol(); + function prettify(node, options) { + return compress(node, options); + } + function compress(arr, options, jPath) { + let text; + const compressedObj = {}; + for (let i = 0;i < arr.length; i++) { + const tagObj = arr[i]; + const property = propName(tagObj); + let newJpath = ""; + if (jPath === undefined) + newJpath = property; + else + newJpath = jPath + "." + property; + if (property === options.textNodeName) { + if (text === undefined) + text = tagObj[property]; + else + text += "" + tagObj[property]; + } else if (property === undefined) { + continue; + } else if (tagObj[property]) { + let val = compress(tagObj[property], options, newJpath); + const isLeaf = isLeafTag(val, options); + if (tagObj[METADATA_SYMBOL2] !== undefined) { + val[METADATA_SYMBOL2] = tagObj[METADATA_SYMBOL2]; + } + if (tagObj[":@"]) { + assignAttributes(val, tagObj[":@"], newJpath, options); + } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) { + val = val[options.textNodeName]; + } else if (Object.keys(val).length === 0) { + if (options.alwaysCreateTextNode) + val[options.textNodeName] = ""; + else + val = ""; + } + if (compressedObj[property] !== undefined && compressedObj.hasOwnProperty(property)) { + if (!Array.isArray(compressedObj[property])) { + compressedObj[property] = [compressedObj[property]]; + } + compressedObj[property].push(val); + } else { + if (options.isArray(property, newJpath, isLeaf)) { + compressedObj[property] = [val]; + } else { + compressedObj[property] = val; + } + } + } + } + if (typeof text === "string") { + if (text.length > 0) + compressedObj[options.textNodeName] = text; + } else if (text !== undefined) + compressedObj[options.textNodeName] = text; + return compressedObj; + } + function propName(obj) { + const keys = Object.keys(obj); + for (let i = 0;i < keys.length; i++) { + const key = keys[i]; + if (key !== ":@") + return key; + } + } + function assignAttributes(obj, attrMap, jpath, options) { + if (attrMap) { + const keys = Object.keys(attrMap); + const len = keys.length; + for (let i = 0;i < len; i++) { + const atrrName = keys[i]; + if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) { + obj[atrrName] = [attrMap[atrrName]]; + } else { + obj[atrrName] = attrMap[atrrName]; + } + } + } + } + function isLeafTag(obj, options) { + const { textNodeName } = options; + const propCount = Object.keys(obj).length; + if (propCount === 0) { + return true; + } + if (propCount === 1 && (obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)) { + return true; + } + return false; + } + + // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js + class XMLParser { + constructor(options) { + this.externalEntities = {}; + this.options = buildOptions(options); + } + parse(xmlData, validationOption) { + if (typeof xmlData !== "string" && xmlData.toString) { + xmlData = xmlData.toString(); + } else if (typeof xmlData !== "string") { + throw new Error("XML data is accepted in String or Bytes[] form."); + } + if (validationOption) { + if (validationOption === true) + validationOption = {}; + const result = validate(xmlData, validationOption); + if (result !== true) { + throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`); + } + } + const orderedObjParser = new OrderedObjParser(this.options); + orderedObjParser.addExternalEntities(this.externalEntities); + const orderedResult = orderedObjParser.parseXml(xmlData); + if (this.options.preserveOrder || orderedResult === undefined) + return orderedResult; + else + return prettify(orderedResult, this.options); + } + addEntity(key, value) { + if (value.indexOf("&") !== -1) { + throw new Error("Entity value can't have '&'"); + } else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) { + throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for ' '"); + } else if (value === "&") { + throw new Error("An entity with value '&' is not permitted"); + } else { + this.externalEntities[key] = value; + } + } + static getMetaDataSymbol() { + return XmlNode.getMetaDataSymbol(); + } + } + + // src/main.ts + function parseCap(xml) { + const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_" }); + const json = parser.parse(xml); + const alertsJson = json.alert ? Array.isArray(json.alert) ? json.alert : [json.alert] : []; + const alerts = []; + alertsJson.forEach((a) => { + const infosJson = a.info ? Array.isArray(a.info) ? a.info : [a.info] : []; + const infos = infosJson.map((info) => { + const resourcesJson = info.resource ? Array.isArray(info.resource) ? info.resource : [info.resource] : []; + const areasJson = info.area ? Array.isArray(info.area) ? info.area : [info.area] : []; + return { + language: info.language || "", + category: info.category, + event: info.event, + responseType: info.responseType, + urgency: info.urgency, + severity: info.severity, + certainty: info.certainty, + audience: info.audience, + effective: info.effective, + onset: info.onset, + expires: info.expires, + senderName: info.senderName, + headline: info.headline, + description: info.description, + instruction: info.instruction, + web: info.web, + contact: info.contact, + parameter: info.parameter, + eventCode: info.eventCode, + resources: resourcesJson.map((res) => { + return { + resourceDesc: res.resourceDesc, + mimeType: res.mimeType || res["mimeType"], + size: res.size, + uri: res.uri, + derefUri: res.derefUri, + digest: res.digest, + url: res.uri || res.resourceDesc || "" + }; + }), + areas: areasJson.map((area) => ({ + areaDesc: area.areaDesc || "", + polygon: area.polygon, + circle: area.circle, + geocode: area.geocode, + altitude: area.altitude, + ceiling: area.ceiling + })) + }; + }); + alerts.push({ + identifier: a.identifier || "", + sender: a.sender || "", + sent: a.sent || "", + status: a.status, + msgType: a.msgType, + source: a.source, + scope: a.scope, + restriction: a.restriction, + addresses: a.addresses, + code: a.code, + note: a.note, + references: a.references, + incidents: a.incidents, + infos + }); + }); + return alerts; + } + function getNearestExit(tags) { + for (const tag of tags) { + const lower = tag.toLowerCase(); + if (lower.startsWith("exit:")) { + return tag.slice(5).trim(); + } + if (lower.startsWith("exit-")) { + return tag.slice(5).trim(); + } + } + return; + } + async function fetchCapData(feedUrl, offlineMode) { + if (offlineMode) { + return localStorage.getItem("cap_last"); + } + try { + const cors = getCorsProxyUrl(); + let url = feedUrl; + if (feedUrl && feedUrl.match(/^https?:/)) { + url = `${cors}/${feedUrl}`; + } + const response = await fetch(url); + if (!response.ok) + throw new Error(`HTTP ${response.status}`); + const text = await response.text(); + localStorage.setItem("cap_last", text); + return text; + } catch (err) { + console.warn("CAP fetch failed", err); + return localStorage.getItem("cap_last"); + } + } + function highlightKeywords(text) { + const keywords = [ + "DO NOT", + "DON'T", + "IMMEDIATELY", + "IMMEDIATE", + "NOW", + "MOVE TO", + "EVACUATE", + "CALL", + "WARNING", + "DANGER", + "SHELTER", + "TAKE COVER", + "AVOID", + "STAY", + "SEEK", + "TURN AROUND", + "GET TO", + "LEAVE" + ]; + let result = text; + keywords.forEach((keyword) => { + const regex = new RegExp(`\\b(${keyword})\\b`, "gi"); + result = result.replace(regex, '$1'); + }); + return result; + } + function splitIntoSentences(text) { + return text.split(/(?<=[.!?])\s+/).map((s) => s.trim()).filter((s) => s.length > 0); + } + function proxyUrl(url) { + if (url && url.match(/^https?:/)) { + const cors = getCorsProxyUrl(); + return `${cors}/${url}`; + } + return url; + } + function renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) { + const container = document.getElementById("alerts"); + if (!container) + return; + container.innerHTML = ""; + const slice = alerts.slice(0, maxAlerts); + slice.forEach((alert) => { + const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0]; + if (!info) + return; + const card = document.createElement("div"); + card.className = "alert-card w-full h-full bg-white flex flex-col overflow-y-auto"; + if (alert.status) { + const statusBanner = document.createElement("div"); + let baseClasses = "w-full text-center font-black uppercase tracking-[0.15em] flex-shrink-0 status-stripe-pattern status-banner-text py-[2.5vh] px-[4vw] text-white "; + let statusText = alert.status.toUpperCase(); + if (alert.status === "Exercise") { + statusText = "EXERCISE - THIS IS A DRILL"; + baseClasses += "status-banner-blue"; + } else if (alert.status === "Test") { + statusText = "TEST - NOT A REAL EMERGENCY"; + baseClasses += "status-banner-gray"; + } else if (alert.status === "System") { + statusText = "SYSTEM TEST"; + baseClasses += "status-banner-gray"; + } else if (alert.status === "Draft") { + statusText = "DRAFT - NOT ACTIVE"; + baseClasses += "status-banner-orange"; + } else if (alert.status === "Actual") { + statusText = "ACTUAL EMERGENCY"; + baseClasses += "status-banner-red status-actual-pulse"; + } + statusBanner.className = baseClasses; + statusBanner.textContent = statusText; + card.appendChild(statusBanner); + } + const headerRow = document.createElement("div"); + headerRow.className = "flex items-center justify-between gap-[2vw] mx-[5vw] mt-[2vh] mb-[1.5vh]"; + const header = document.createElement("h2"); + header.className = "text-red-600 font-black uppercase leading-none event-title-text"; + header.textContent = info.event || alert.identifier; + headerRow.appendChild(header); + const meta = document.createElement("div"); + meta.className = "severity-badge inline-block text-white rounded-xl font-black uppercase tracking-wider flex-shrink-0 severity-badge-text px-[4vw] py-[2vh]"; + meta.textContent = `${info.urgency || ""} ${info.severity || ""} ${info.certainty || ""}`.trim(); + headerRow.appendChild(meta); + card.appendChild(headerRow); + if (info.headline) { + const headline = document.createElement("h3"); + headline.className = "font-extrabold leading-tight flex-shrink-0 headline-text text-gray-900 "; + headline.className += "mx-[5vw] mb-[1.5vh]"; + headline.textContent = info.headline; + card.appendChild(headline); + } + if (info.description) { + const desc = document.createElement("p"); + desc.className = "font-semibold leading-snug body-text text-gray-800 "; + desc.className += "mx-[5vw] mb-[2vh]"; + desc.textContent = info.description; + card.appendChild(desc); + } + if (info.instruction) { + let instr = info.instruction; + if (nearestExit) { + if (instr.includes("{{closest_exit}}") || instr.includes("[[closest_exit]]")) { + instr = instr.replace(/\{\{closest_exit\}\}/g, nearestExit).replace(/\[\[closest_exit\]\]/g, nearestExit); + } else { + instr += ` + +Nearest exit: ${nearestExit}`; + } + } + const instructionBox = document.createElement("div"); + instructionBox.className = "instruction-box rounded-xl flex-shrink-0 px-[4vw] py-[2.5vh] mx-[5vw] mb-[2vh]"; + const sentences = splitIntoSentences(instr); + if (sentences.length > 2) { + const ul = document.createElement("ul"); + ul.className = "instruction-text leading-snug text-gray-900"; + sentences.forEach((sentence) => { + const li = document.createElement("li"); + li.className = "mb-[1vh] flex items-start"; + const bullet = document.createElement("span"); + bullet.className = "mr-[2vw] flex-shrink-0 font-black text-orange-600"; + bullet.textContent = "•"; + const content = document.createElement("span"); + content.className = "flex-1 font-extrabold"; + content.innerHTML = highlightKeywords(sentence); + li.appendChild(bullet); + li.appendChild(content); + ul.appendChild(li); + }); + instructionBox.appendChild(ul); + } else { + const instP = document.createElement("p"); + instP.className = "instruction-text font-extrabold leading-snug whitespace-pre-line text-gray-900"; + instP.innerHTML = highlightKeywords(instr); + instructionBox.appendChild(instP); + } + card.appendChild(instructionBox); + } + info.resources.forEach((res) => { + if (res.mimeType && res.mimeType.startsWith("image")) { + const imgWrapper = document.createElement("div"); + imgWrapper.className = "mx-[5vw] my-[2vh]"; + const img = document.createElement("img"); + img.className = "max-w-full max-h-[20vh] rounded-2xl object-contain shadow-lg"; + img.src = proxyUrl(res.url); + imgWrapper.appendChild(img); + card.appendChild(imgWrapper); + } else if (res.mimeType && res.mimeType.startsWith("audio") && playAudio) { + const proxiedUrl = proxyUrl(res.url); + console.log("Creating audio player for:", res.url, "-> proxied:", proxiedUrl, "MIME type:", res.mimeType); + const audio = document.createElement("audio"); + audio.className = "w-[90vw] flex-shrink-0 mx-[5vw] my-[2vh] rounded-xl"; + audio.style.height = "clamp(3rem, 5vh, 10rem)"; + audio.src = proxiedUrl; + audio.controls = true; + audio.autoplay = true; + audio.preload = "auto"; + audio.crossOrigin = "anonymous"; + card.appendChild(audio); + audio.addEventListener("loadeddata", () => { + console.log("Audio loaded successfully:", proxiedUrl); + }); + audio.addEventListener("error", (e) => { + console.error("Audio load error:", proxiedUrl, e); + }); + audio.play().catch((err) => { + console.warn("Autoplay blocked or failed:", err.message, "- Click play button to start audio"); + }); + } + }); + container.appendChild(card); + }); + } + async function startApp() { + setupTheme(); + let settings2 = {}; + let metadata2 = {}; + try { + settings2 = getSettings(); + localStorage.setItem("screenly_settings", JSON.stringify(settings2)); + } catch (_) { + const cached = localStorage.getItem("screenly_settings"); + settings2 = cached ? JSON.parse(cached) : {}; + } + try { + metadata2 = getMetadata(); + localStorage.setItem("screenly_metadata", JSON.stringify(metadata2)); + } catch (_) { + const cachedMeta = localStorage.getItem("screenly_metadata"); + metadata2 = cachedMeta ? JSON.parse(cachedMeta) : {}; + } + const feedUrl = settings2.cap_feed_url || ""; + const interval = parseInt(settings2.refresh_interval || "5", 10); + const lang = settings2.language || "en"; + const maxAlerts = parseInt(settings2.max_alerts || "3", 10); + const playAudio = (settings2.audio_alert || "false") === "true"; + const offlineMode = (settings2.offline_mode || "false") === "true"; + const testMode = (settings2.test_mode || "false") === "true"; + const demoMode = (settings2.demo_mode || "false") === "true"; + console.log("CAP Settings:", { + playAudio, + audio_alert: settings2.audio_alert, + demoMode, + testMode, + maxAlerts + }); + const tags = metadata2.tags || []; + const nearestExit = getNearestExit(tags); + async function update() { + let xml; + if (testMode) { + try { + const resp = await fetch("static/test.cap"); + xml = resp.ok ? await resp.text() : null; + } catch (_) { + xml = null; + } + } else if (demoMode && !feedUrl) { + const demoFiles = [ + "static/demo-1-tornado.cap", + "static/demo-2-fire.cap", + "static/demo-3-flood.cap", + "static/demo-4-earthquake.cap", + "static/demo-5-hazmat.cap", + "static/demo-6-shooter.cap" + ]; + const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)]; + try { + const resp = await fetch(randomFile); + xml = resp.ok ? await resp.text() : null; + } catch (_) { + xml = null; + } + } else { + xml = await fetchCapData(feedUrl, offlineMode); + } + if (xml) { + const alerts = parseCap(xml); + renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio); + } else { + console.warn("No CAP data available"); + } + } + await update(); + signalReady(); + setInterval(update, interval * 60 * 1000); + } + if (typeof window !== "undefined" && typeof document !== "undefined") { + window.onload = function() { + startApp(); + }; + } +})(); diff --git a/edge-apps/cap-alerting/static/style.css b/edge-apps/cap-alerting/static/style.css index 61971ef4a..2ddf63983 100644 --- a/edge-apps/cap-alerting/static/style.css +++ b/edge-apps/cap-alerting/static/style.css @@ -1,137 +1,2 @@ -:root { - --bg-color: #ffffff; - --text-color: #000000; - --card-bg: #f5f5f5; - --border-color: #ddd; - --alert-red: #dc3545; - --alert-orange: #fd7e14; - --alert-yellow: #ffc107; -} - -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - background-color: var(--bg-color); - color: var(--text-color); - padding: 2rem; - line-height: 1.6; -} - -#app { - width: 100%; - max-width: 1200px; - margin: 0 auto; -} - -.alerts { - display: flex; - flex-direction: column; - gap: 2rem; -} - -.alert-card { - background: var(--card-bg); - border: 0.125rem solid var(--border-color); - border-radius: 0.5rem; - padding: 2rem; - box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); - animation: fadeIn 0.5s ease-in; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(-1.25rem); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.alert-card h2 { - font-size: 2rem; - margin-bottom: 1rem; - color: var(--alert-red); -} - -.alert-card h3 { - font-size: 1.5rem; - margin-bottom: 1rem; - color: var(--text-color); -} - -.alert-card .meta { - display: inline-block; - padding: 0.5rem 1rem; - background: var(--alert-orange); - color: white; - border-radius: 0.25rem; - font-weight: bold; - text-transform: uppercase; - font-size: 0.875rem; - margin-bottom: 1rem; -} - -.alert-card p { - font-size: 1.125rem; - margin-bottom: 1rem; - white-space: pre-line; -} - -.alert-card .instruction { - background: #fff3cd; - border-left: 0.25rem solid var(--alert-yellow); - padding: 1rem; - margin-top: 1rem; - font-weight: bold; -} - -.alert-card img { - max-width: 100%; - height: auto; - margin-top: 1rem; - border-radius: 0.25rem; -} - -.alert-card audio { - width: 100%; - margin-top: 1rem; -} - -@media (max-width: 768px) { - body { - padding: 1rem; - } - - .alert-card { - padding: 1.5rem; - } - - .alert-card h2 { - font-size: 1.5rem; - } - - .alert-card h3 { - font-size: 1.25rem; - } - - .alert-card p { - font-size: 1rem; - } -} - -@media (orientation: portrait) { - .alerts { - gap: 1.5rem; - } - - .alert-card h2 { - font-size: 1.75rem; - } -} +/*! tailwindcss v4.1.17 | MIT License | https://tailwindcss.com */ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-600:oklch(57.7% .245 27.325);--color-red-800:oklch(44.4% .177 26.899);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-600:oklch(68.1% .162 75.834);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-800:oklch(42.4% .199 265.638);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--font-weight-semibold:600;--font-weight-extrabold:800;--font-weight-black:900;--tracking-wider:.05em;--leading-tight:1.25;--leading-snug:1.375;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.relative{position:relative}.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing)*0)}.mx-\[5vw\]{margin-inline:5vw}.my-\[2vh\]{margin-block:2vh}.my-\[3vh\]{margin-block:3vh}.mt-\[2vh\]{margin-top:2vh}.mt-\[4vh\]{margin-top:4vh}.mr-\[2vw\]{margin-right:2vw}.mb-\[1\.5vh\]{margin-bottom:1.5vh}.mb-\[1vh\]{margin-bottom:1vh}.mb-\[2vh\]{margin-bottom:2vh}.mb-\[3vh\]{margin-bottom:3vh}.flex{display:flex}.inline-block{display:inline-block}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[20vh\]{max-height:20vh}.w-\[90vw\]{width:90vw}.w-full{width:100%}.max-w-full{max-width:100%}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.flex-col{flex-direction:column}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-\[2vw\]{gap:2vw}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border-b-\[1vh\]{border-bottom-style:var(--tw-border-style);border-bottom-width:1vh}.border-l-\[1vw\]{border-left-style:var(--tw-border-style);border-left-width:1vw}.border-blue-800{border-color:var(--color-blue-800)}.border-gray-900{border-color:var(--color-gray-900)}.border-orange-600{border-color:var(--color-orange-600)}.border-red-800{border-color:var(--color-red-800)}.border-yellow-600{border-color:var(--color-yellow-600)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-600{background-color:var(--color-orange-600)}.bg-red-600{background-color:var(--color-red-600)}.bg-white{background-color:var(--color-white)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-400{background-color:var(--color-yellow-400)}.object-contain{object-fit:contain}.p-0{padding:calc(var(--spacing)*0)}.px-\[4vw\]{padding-inline:4vw}.py-\[2\.5vh\]{padding-block:2.5vh}.py-\[2vh\]{padding-block:2vh}.py-\[3vh\]{padding-block:3vh}.text-center{text-align:center}.leading-none{--tw-leading:1;line-height:1}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-pre-line{white-space:pre-line}.text-black{color:var(--color-black)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-orange-600{color:var(--color-orange-600)}.text-red-600{color:var(--color-red-600)}.text-red-800{color:var(--color-red-800)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}@keyframes pulse-banner{0%,to{opacity:1;transform:scale(1)}50%{opacity:.95;transform:scale(1.005)}}.status-actual-pulse{animation:1.5s ease-in-out infinite pulse-banner}@media (prefers-reduced-motion:reduce){.status-actual-pulse{animation:none}}.status-stripe-pattern{background-image:repeating-linear-gradient(135deg,#0000,#0000 2rem,#ffffff14 2rem 4rem)}.status-banner-text{text-shadow:0 2px 4px #0003;font-size:clamp(1.25rem,3.5vmin,6rem);line-height:1.1}.event-title-text{text-shadow:0 2px 8px #dc262626;letter-spacing:-.02em;font-size:clamp(2rem,5vmin,10rem);line-height:1}.severity-badge-text{text-shadow:0 1px 2px #00000026;font-size:clamp(1rem,2vmin,4rem);line-height:1.1}.headline-text{letter-spacing:-.01em;font-size:clamp(1.5rem,3.5vmin,7rem);line-height:1.2}.body-text{font-size:clamp(1.125rem,2.5vmin,5rem);line-height:1.35}.instruction-text{font-size:clamp(1.25rem,3vmin,6rem);line-height:1.4}.instruction-text strong{color:#991b1b;font-weight:900}.alert-card{box-shadow:0 8px 32px #0000001f,0 2px 8px #00000014}.instruction-box{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background:linear-gradient(135deg,#fef9c3 0%,#fef3c7 100%);box-shadow:inset 4px 0 #eab308,0 4px 12px #0000000d}.severity-badge{background:linear-gradient(135deg,#f97316 0%,#ea580c 100%);box-shadow:0 4px 12px #f9731640}.status-banner-blue{background:linear-gradient(135deg,#2563eb 0%,#1d4ed8 100%)}.status-banner-red{background:linear-gradient(135deg,#dc2626 0%,#b91c1c 100%)}.status-banner-orange{background:linear-gradient(135deg,#f97316 0%,#ea580c 100%)}.status-banner-gray{background:linear-gradient(135deg,#4b5563 0%,#374151 100%)}@media (min-width:1920px) and (min-height:1080px){.instruction-text{font-size:clamp(2rem,3.5vmin,7rem)}.event-title-text{font-size:clamp(3rem,6vmin,12rem)}}@media (min-width:3840px) and (min-height:2160px){.instruction-text{font-size:clamp(2.5rem,3.5vmin,7rem)}.event-title-text{font-size:clamp(4rem,6vmin,12rem)}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000} \ No newline at end of file diff --git a/edge-apps/cap-alerting/static/test.cap b/edge-apps/cap-alerting/static/test.cap index 340c52aff..1e1400f10 100644 --- a/edge-apps/cap-alerting/static/test.cap +++ b/edge-apps/cap-alerting/static/test.cap @@ -17,6 +17,11 @@ This is a test alert for a scheduled fire drill. Please proceed to {{closest_exit}} in an orderly fashion. Exit the building via {{closest_exit}}. Do not use elevators. Gather at the designated assembly point. 2024-01-15T11:00:00-00:00 + + Fire Drill Alert Tone + audio/mpeg + https://archive.org/download/fire-alarm-sound/fire-alarm-1.mp3 + Main Building - All Floors diff --git a/edge-apps/cap-alerting/tailwind.config.js b/edge-apps/cap-alerting/tailwind.config.js new file mode 100644 index 000000000..82729d7b2 --- /dev/null +++ b/edge-apps/cap-alerting/tailwind.config.js @@ -0,0 +1,26 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + screens: { + 'xs': '480px', + 'sm': '640px', + 'md': '768px', + 'lg': '1024px', + 'xl': '1280px', + '720p': '1280px', + '1080p': '1920px', + '2xl': '1536px', + '2k': '2560px', + '4k': '3840px', + '8k': '7680px', + }, + }, + }, + plugins: [], +} + diff --git a/edge-apps/cap-alerting/tsconfig.json b/edge-apps/cap-alerting/tsconfig.json index 80784b813..ed69f9960 100644 --- a/edge-apps/cap-alerting/tsconfig.json +++ b/edge-apps/cap-alerting/tsconfig.json @@ -11,10 +11,7 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "paths": { - "../edge-apps-library/src/*": ["../edge-apps-library/dist/*"] - } + "types": ["bun-types"] }, - "include": ["index.ts"], - "exclude": ["node_modules", "dist", "../edge-apps-library"] + "exclude": ["node_modules", "dist", "../edge-apps-library/src"] } diff --git a/edge-apps/cap-alerting/vite.config.ts b/edge-apps/cap-alerting/vite.config.ts deleted file mode 100644 index a686f3c74..000000000 --- a/edge-apps/cap-alerting/vite.config.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { defineConfig } from 'vite' -import { viteStaticCopy } from 'vite-plugin-static-copy' - -const manifestFileName = process.env.MANIFEST_FILE_NAME || 'screenly.yml' - -export default defineConfig(({ mode }) => ({ - base: '', - build: { - sourcemap: mode === 'development', - }, - publicDir: false, - plugins: [ - viteStaticCopy({ - targets: [ - { - src: manifestFileName, - dest: '.' - }, - { - src: 'static/*', - dest: 'static' - } - ] - }) - ] -})) From 47880dba3946fac1ce3583391cf4356587e5b376 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 10:29:06 -0800 Subject: [PATCH 03/26] chore(cap-alerting): add a `configVersion` field in `bun.lock` The change was made by Bun when `bun install` was executed. --- edge-apps/cap-alerting/bun.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/edge-apps/cap-alerting/bun.lock b/edge-apps/cap-alerting/bun.lock index d4e2f82a9..495c6b57d 100644 --- a/edge-apps/cap-alerting/bun.lock +++ b/edge-apps/cap-alerting/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "cap-alerting", From b3669f09b6f64c894b7f6d1522e51a22dd3ec110 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 10:32:20 -0800 Subject: [PATCH 04/26] chore(cap-alerting): remove app IDs from manifest files --- edge-apps/cap-alerting/screenly.yml | 1 - edge-apps/cap-alerting/screenly_qc.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml index 644e55cb6..68bfaa004 100644 --- a/edge-apps/cap-alerting/screenly.yml +++ b/edge-apps/cap-alerting/screenly.yml @@ -1,6 +1,5 @@ --- syntax: manifest_v1 -id: 01KBT555AYQE42KBXJ4NPH87S0 description: Display CAP emergency alerts on Screenly screens. Supports offline mode and uses screen tags (e.g., exit:North Lobby) to highlight the nearest exit. icon: https://playground.srly.io/edge-apps/cap-alerting/static/cap-icon.svg author: Screenly, Inc. diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index 17d20b3c1..68bfaa004 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -1,6 +1,5 @@ --- syntax: manifest_v1 -id: cap-alerting-qc description: Display CAP emergency alerts on Screenly screens. Supports offline mode and uses screen tags (e.g., exit:North Lobby) to highlight the nearest exit. icon: https://playground.srly.io/edge-apps/cap-alerting/static/cap-icon.svg author: Screenly, Inc. From 3d51c1f2a5e5c2eab1e85b9a594a7c0d42fc0dc6 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 10:39:28 -0800 Subject: [PATCH 05/26] chore(cap-alerting): ignore generated JS and CSS files in Git --- edge-apps/cap-alerting/.gitignore | 3 + edge-apps/cap-alerting/static/js/main.js | 6118 ---------------------- edge-apps/cap-alerting/static/style.css | 2 - 3 files changed, 3 insertions(+), 6120 deletions(-) delete mode 100644 edge-apps/cap-alerting/static/js/main.js delete mode 100644 edge-apps/cap-alerting/static/style.css diff --git a/edge-apps/cap-alerting/.gitignore b/edge-apps/cap-alerting/.gitignore index 74287eb80..50ae8ce57 100644 --- a/edge-apps/cap-alerting/.gitignore +++ b/edge-apps/cap-alerting/.gitignore @@ -5,3 +5,6 @@ dist/ mock-data.yml instance.yml bun.lockb +static/js/*.js +static/js/*.js.map +static/style.css diff --git a/edge-apps/cap-alerting/static/js/main.js b/edge-apps/cap-alerting/static/js/main.js deleted file mode 100644 index 8630ee074..000000000 --- a/edge-apps/cap-alerting/static/js/main.js +++ /dev/null @@ -1,6118 +0,0 @@ -(() => { - var __create = Object.create; - var __getProtoOf = Object.getPrototypeOf; - var __defProp = Object.defineProperty; - var __getOwnPropNames = Object.getOwnPropertyNames; - var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - var __hasOwnProp = Object.prototype.hasOwnProperty; - var __toESM = (mod, isNodeMode, target) => { - target = mod != null ? __create(__getProtoOf(mod)) : {}; - const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; - for (let key of __getOwnPropNames(mod)) - if (!__hasOwnProp.call(to, key)) - __defProp(to, key, { - get: () => mod[key], - enumerable: true - }); - return to; - }; - var __moduleCache = /* @__PURE__ */ new WeakMap; - var __toCommonJS = (from) => { - var entry = __moduleCache.get(from), desc; - if (entry) - return entry; - entry = __defProp({}, "__esModule", { value: true }); - if (from && typeof from === "object" || typeof from === "function") - __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, { - get: () => from[key], - enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable - })); - __moduleCache.set(from, entry); - return entry; - }; - var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); - var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { - get: all[name], - enumerable: true, - configurable: true, - set: (newValue) => all[name] = () => newValue - }); - }; - - // node_modules/.bun/@photostructure+tz-lookup@11.3.0/node_modules/@photostructure/tz-lookup/tz.js - var require_tz = __commonJS((exports, module) => { - function tzlookup(Y, W) { - var X = "XKXJXJXIXIXSXSXRXRXQXQXP##U;U;U;#$UZUZUZUZUZXHXGXGXTXTXXXXXYXYXZXZY#Y#Y$Y$Y%Y%Y&Y&Y'Y'XUXUXVXVXWXKXJXJXIXIXSXSU:U$#%#&V,#'U;#(#)UZUZUZUZUZ#*UZXGXGVPVPVP#+YIYIYIYI#,W@W@W@W@W@W@Y&X/X/X/X/X/XVXWVTUV#-T,T,#.U:U:#/#0#1#2#3U;U;U;UZUZUZUZ#4#5YJYJXGYJ#6#7#8YIYIYI#9X1#:W@W@W@#;X/X/#<#=#>#?#@VTVT#A#B#CT,T,#D#E#F#G#H#IV,#J#K#L#MUZUZUZUZX9X9#N#OYJ#P#Q#R#SYI#T#U#VX1#W#XW@W@#Y#ZX/$#$$$%$&$'$($)$*$+$,T,T,$-$.$/U$$0$1$2$3$4$5$6$7UZUZXLXLXH$8$9$:$;$<$=$>YI$?$@$A$B$C$D$E$FW6$G$H$I$J$K$LW;$MT+T+T+XJXIXIXSVB$N$O$P$Q$R$S$T$U$V$WV7XMXLXLXHY6$X$Y$Z%#%$%%%&%'%(%)VR%*%+%,%-%.%/%0%1%2%3%4W;XVT+XKXJXJXIXIXSXSUC%5%6%7TN%8%9%:%;U0XMXMX3X3XH%<%=%>%?%@%A%B%C%D%E%F%G%HX+%I%J%KWX%L%M%N%OXUXVXVXWXKXJXJXIXIXSXSUC%P%Q%RTN%SUUXOX4XNXMXMXLX3X8%T%US2%V%W%X%Y%Z&#&$&%&&&'WXWXWXWXWX&(&)X(XUXUXVXVXW&*ZCZCXIXIXSXSXR&+&,&-&.&/&0UTXNXNXMXMXLXL&1&2&3S2&4T(&5&6WT&7&8&9&:&;&<&=&>&?&@&AX(X(XUX(XVXVXWXKZCZCZCXIXSXSXRUK&B&C&D&E&F&G&HXNXMXMXLX6&I&J&K&L&M&N&O&P&Q&R&SWHW?W?&T&U&V&W&X&YY'X(ZUXUXVZYXWXKXJXJXIXIXSXSXRXRXQXQ&Z'#'$'%'&''XMXMXLX6'(')'*'+','-'.'/'0'1VQXZW?'2Y$'3'4'5'6WGZPZ9'7'8ZH'9XWXKXJZEZEXIXSXSXRXRXQXQZ?':';'<'='>'?XMXLUWXH'@'A'B'C'D'E'F'G'HXZXZZ1W*Y$'I'J'K'L'M'NW8Z9'O'P'QZWZDZDXJZEXIXSXSXRXRXQXQZ?'R'S'TUG'U'V'W'XXLXHXHXGSS'Y'Z(#($(%XYZ0XZ(&Y#Y$Y$W7('((()W8(*ZS(+ZA(,(-(.(/ZTZE(0(1XSXRXRXQXQXPUBUB(2(3(4(5(6(7XLXHX;XGXGSP(8(9(:(;((?(@(A(B(CZA(D(E(FZLZT(GZV(HXSXRXRXQXQXPXPUB(I(J(K(L(MXLV3XHXHX;XG(N(O(P(Q(RZ*(SZ2Y#Y#Y$Y$Y%Y%XEXE(T(UX>X>ZN(VZ=(WXJXJZVZVZ@(XZQXRZ:XQXPXPV1(Y(Z)#V3V3XLXLXHXHXGXGT*)$)%)&Z*Z*XZXZY#Y#Y$Y$Y%Y%XEXE)')()))*XV)+XWZ6XJXJXIXIXSXSXRXRXQXQXPXPV1),)-).V3XMXLXLXHX;XGXGXTSG)/SGXYXYXZXZY#Y#Y$Y$Y%Y%XE)0)1)2)3XFXCZ6Z6Z8XJXJXIXIXSXSXRXRXQXQXPXPTR)4)5T0XMXMXLXLXHX;XGXGXTXTXXXXXYXYXZXZY#Z/Y$Y$Y%Y%Y&Y&Y')6)7XDXVZ6Z6XKXJXJXIXIXSXSXRXRXQXQXPXP)8)9):XT;T;XLXLXHXHXGXGXTXTVMVMVMVM)?VJVJVG)@VOVFVFVFVHVHVHVHVH)AVKXWXKXJXJXIXIXSXSXRXRXQXQXPXPVLVLT;T;T;T;)BVL)CY()DVNVNVN)EVMVMVM)FVJVJ)G)HVO)I)J)K)L)M)N)O)P)QVKVKVKVKVKVKXIXSXSXRXRXQXQXPXPVLVLT;T;T;T;)RVL)SY()TVNVNVN)UVMVMVM)VVJVJ)W)XVOVOVO)YXEXEXEXEXE)ZVKVKVKVKVKVKVKVKVKVKVKVKVKVKVK*#*$*%*&*'*(*)***+*,*-*.*/*0*1*2*3*4*5*6*7*8*9*:*;*<*=*>*?*@*A*B*CVKVKXPXPV,*CXNXNU;UZ*BTH*CTHTHV,THV,V,*BV,*CU;*CV>V>*CUZ*DUZUZUZTV*DVP*DVPXXY#W@YIY#XJT,UVT,XSXS*B*CU$U$*C*D*DTH*ETHTH*ETHTH*EV,THTHV,*E*F*GUZ*GUZ*HUZUZV4XHVPVPYJ*GYJYJ*G*HXXXX*HYIYIX1YI*H*HW@X1*IW@*I*J*KX/*KX/*L*L*MX.*N*N*OWZWZ*OWZWZWZXVXVWZ*OVTVTVTUV*NUVUVUVUVT,UVT,*MVC*NVC*NU:VC*OU:U:*OU:U$*OU$U$*O*PU$U$THV,U$V,V,*OV,*P*PU;*Q*RU;U;*RU;U;U;U;*RX9XHXHX7XGXGX7YBYJ*PYJ*QYWYWYW*Q*QY9Y9*R*RYIYIYIYIYI*RYIYIYI*R*S*S*TX1X1X1X1*T*U*UW@*VW@*VX/*W*XX/X/*XX/X/*XX/*Y*Y*ZW>+#+#WZ+$WE+$+%WEWE+%+&WE+'VTVTW;+'VTVT+'VTXKUVXKT+UVUVT+UV+%T,UVT,VE+%XSV5+%VB+&VB+&+'VB+(+(V.+)+*V.VD+*VDVD+*VDVDV,+*V@++U;++U;V@V@++V@V@++U*+,U*XNXNU*V7YBYBXH++YBYB++YBXGYJYB+++++,+-+.YW+.+/+0+0+1+2+3YIYI+3YI+3+4+5+6+6X1+7X1X1X1+7+8X1+8+9+:+:+;+<+=+=+>+?+@W@W@W@+@+@+AW6W6W6+A+BW)X/X/+B+CX/+CX/X.X.X.X.+CX.WEWUXUWE+BXUW;W;W;W;T+VB+A+B+C+C+D+E+F+F+GTYTYV.VD+G+HVDVDTN+HV@V@+H+I+IV@V@V@V@V@V@+IV@+I+J+K+KV7+LV7+L+MYKYK+M+NYK+O+O+P+Q+R+R+S+T+U+U+V+W+X+X+Y+Z,#,#,$,%,&,&,',(,),),*,+VV,+,,,-,.VR,.VRVR,.,/,0X+,0,1,2X*X*,2X*X*,2,3X*X*,3,4X*,5,5,6WXWX,6,7,8,9X.,9X.,:WUXUWUWEWZ,9,:WZ,:,;UC,<,,?,?,@UUUU,@UUUUUU,@U0XNXNXH,@XHY@,@,A,BYD,B,CYDYD,C,DYOYO,D,EYO,F,F,G,H,I,I,JY;Y;,J,KY;,L,L,M,N,O,O,PVW,Q,Q,R,S,T,T,U,V,W,W,X,YX+X+,YWXWX,Y,ZWXWX,Z-#WXWXWX-#WX-$-$X.-%Y'X.X(X(X(-$WEXUXUUC-$-%-&-&TY-'-(-(TN-)TNTN-)TN-*XH-*XHS<-*-+S<-,-,---.-/YOY,T(T(Y,-.-/S;-/-0S;-1-1-2-3-4-4X&-5X&X&-5X&X&-5-6-7-8-8-9-:-;-;-<-=->WXWWWXWW-=X(X(X(ZJZCXKZCV?-->-?-@-ATNTN-AUMTN-AUM-B-BUT-CUTXHX5XHSC-B-C-DT$S2S2-D-E-ET(-F-G-GS;-HS;S;-HS;-I-I-JWT-KX&-K-L-M-MW<-NW<-N-O-PW?W?-PW?W?-P-QW?-R-R-S-T-U-UWX-V-WWXWX-WWXWXWX-W-XWXX(-XX(UK-XUKXQ-X-YUNUNUNUMUN-Y-YU1-ZV=-Z.#.$.%.%V2.&.'VAT-.'.(XH.(X6.).)T$.*T$.*S4.+S4.+.,S4T#.,T#T#T#SZ.,SZSZ.,SJ.-SJ.-SJSJ....WT./.0WTWT.0VQ.0WH.1WHW?.1W?Y$.1X0X0X0.1.2.3.4.4WX.5.6.6X#WX.7.7Y&WGY&XP.7XPXP.7UFXPTQTFTFV$.7TF.7.8TKTK.8TK.9TAXNU/XNXH.8XHS7.8.9.:.;.;.<.=.>.>.?.@SM.@.ASM.B.BSZ.C.DSJSJ.D.ESJ.E.FS1.F.GS1.H.H.ISWSWW?W?W?.IW?.IW?.J.J.K.L.M.MW3W3W3.M.N.OWGZBXUZ9Z9XUXUZ9ZRZHZH.MZHTQV$Z?XP.LTF.MTFTF.MTF.NTK.N.O.P.P.Q.R.S.SXMTBTBSD.SXHXHS/.SXGXG.SSMXGT'.SSB.T.U.U.V.WSL.W.X.Y.ZSH.Z/#/$S1/$SYSW/$SWSWXYW?W7Y$W7/#/$W7/%W7W7W7/%/%/&/'WFWG/'WF/(ZPZP/(/)Z9ZRXUZRZRZFXVXVZHZHXVZWZ?UBXPUB/%/&/'UB/'/(/)/*/*V0/+V0TB/+TB/,U(U(U(/,U(UWU(UWSN/+SN/,/,/-/./////0SQ/1/1/2S@S@/2/3/4XYY#Z1Z+Z+W7WOW7/3/3WF/4WFWFW8WF/4W8/4W8/5ZSZ7ZS/5ZKZWXVXVZWZWZ>Z>Z>Z/?/?S@/@/AS@Z.ST/AZ0Z0/AZ*/AW7Y%Y%/AWFY&Y&WF/AXEXE/A/B/CX@/C/DX@X>/DZSX>XUZS/DZSZSZAXV/DZAXWZ>XWZ=Z=ZX/CZXZVZV/CZVZVZVZV/CUB/CUB/DUAUA/D/E/E/F/G/H/HV3/IV3V3/IV3V3XT/IXTT*/ISPT*/J/J/KSE/L/L/M/NSTSTZ*XYZ*XZZ2Z4Z2/LX@/MX@X@X>/MX>Z;Z;ZN/MZXZXZ6XKZ@ZQXSXSXOV1V1/K/K/L/MT2/M/N/O/PT*/P/Q/R/R/SSG/T/TST/UXX/UX@/VX=X@X>X=/VX>X>/V/WX>X>/W/XZNZNZMXVV1/WV1/X/X/Y/Z0#0#0$0%0&SG0&SGSGXE0&XEXE0&X=Y'X=X=0&X=0'XFXF0'0(0(0)0*T1T70*0+T7Y'0+Y'Y'0+0,XBXBXOTRXOV+0+0,0-0.0.XNT6XVM0>VJ0?VJ0?VG0@VGVGVOVGVO0?XE0@XE0@VK0AVK0AVLVKVKVLVLVKVKT;T;VKVKT;T;VKVKT;T;VKVKT;T;VKVK0UZV>V>TVTVUZUZXXXXVPYIT,TWT,TWTWU:VCU:U$U$THTHU$U$THTHU$THU$THU$THTHTHV,V,THTHV,V,THTHV,U;V,U;V,V,THV,V,U;V,U;UZUZUZV4UZV4UZUZYJYJYWYWYJYJ/IY9YJYJY9YI/HYIYIYIYIX1/HX1X1W@X1X1X1W@X1X1W@X/W@X/W@W@W@X/W@X/X/X/Y'X/X.X.X/X.X/X.X/X/X.X.X/X/X.X.X.X.X.WZX/X/X.WZX/X/WZWZX/XUWZWZWZVTVTVTVTVTVTUVT,TWT,TWT,TWT,TWVCU:VCVCVCU:VCVCU:U:VCU:THTHU$U$THTHU$U$THTHU$THV,U;T>T>T>T>V,U;U;U;T>U;T>U;U;U;U;U;U;V@U;U;V@V@U;XNU*XNYJYJ/*YWYWYW/*YWYW/*/+/,YWY9/,Y9Y9Y9/,/-YIYI/-YIYIYIYI/-YIYIYIX1YIYIX1X1YIYIX1X1/*X1X1X1X1X1X1/*X1X1/*X1W@W@X1W@X1W@X1W@W@X/W@X/W@W6W6W6X/X/W6X/X/X/X//%X.X.X/W>X//$X/W>X.X.W>W>WZWZW>X,W>X,X.X.WZWZX,WZX,WEX.X.WZWZWZWEWZWZWEWEWZVTWEWEVTVTWEW;WEW;W;W;W;VTW;W;VTVTW;VTUVT,XJXJVBU=U=V5.NVBV5VBV5V5VBVBU'U'VBVBU'U'TXTXVBTXVBVBU$V.U$V.U$V.U$.IV.V.VYI.Y.Y.Z/#/$/$/%/&/'YI/'/(/)/)X1/*/+/+X1/,X1X1X1/,WRX1X1WRVRX1X1WLWL/*/+VRVRWLWLVRVR/*X)/+/,X)X)/,X)/,WKVR/-WKWKW%/-X)X)X)/-X)W@X)W@/,/-/.//WJ///0/1W@/1W@W@W@W@/1W6W@W@W6W6W6X/W)W)W6W6W6W)W)X/W)/.X/X//.X/X/X.X.X.X.WUX.WUWEWEW;W;VBVB/+VBVBUCXRUC/*UCUCUCVB/*/+/,U$U$/,/-UCUCUC/-TYTYUCTYU$V/?Z'Z'/?/@/@/A/B/C/C/D/E/F/F/G/HY?/H/IY?Y?/I/JY1Y1/J/KY1/L/L/MY?Y?/MYI/N/OY?Y?Y?/OY?Y?/O/PYIYI/P/Q/Q/R/SZ&/S/TYIYI/T/UYIYI/UYSZ&/V/V/WWMWM/W/XYI/YVXVXVX/Y/YX1WMVV/Y/ZVVVVVXVV/Z0#X1WRVVWRWRVRWRWRVVVVWSWSVRVRWSWSW%/WVRVRW%W%VRVRW%W@W%W%VRVR/UX+W@W@W5W5W@W@W5X*W5W5X+W5W6W6X*X*W6W6/QX*/QW)W)W)W)W)X*X*W)W)/P/QX*WXWXWX/PWXWXWXWXWXWX/PX/X//PX//PX.X.X.WX/PWXWX/PWX/Q/RX.WUX.WUX.WUX./QWZW;WZWZWEWZWEWEUC/OUC/PTGTG/P/QUC/QUCTY/QTY/RTY/RTNTNTNTN/RTN/STZTZ/S/TTN/TTN/U/UUU/VUUV@/V/W/X/X/YUUUU/YUUUUUU/YU0XNXNYDYDYD/YYDYD/YYDYD/YYD/Z/ZYD0#YDYKYK0#0$YK0$YKYK0$YO0%0&0&0'YOYO0'0(0)0*0*0+0,0-0-0.Y,Y,0.0/000101020304Y,04Y,05Y;Y;05Y;XXYTY;Y;YT04Y;Y;YI04Y;05YIYI0506Y;06Y;07YIYI0708VUVUW#VU0708090:W#W#0:XYVUVUVWVUWVWVWV09VWVW090:WVWS0:WVWSWSWVVRVW09VW0:WV0:0;0<0VRVR0>0?0?0@0A0B0B0CW/W/VRVR0C0DVRX+X+X+0CX+X+X+X+X*WXWXX*X*WXWXX*X*WXX*X*X*X*0@X*WX0@WXWXWXWX0@0@0AWP0BWX0B0C0DWPWP0DWWX(0DX(X(0D0E0F0GUC0GXRV?0GV&0HU20HTY0I0JV&0J0K0LTYTYTP0LTY0LTYTNTY0LTNTN0LUU0MUU0MUU0NUUXHY@XHS<0M0NS1>1?X#X(UK1?XQUN1?UNUNUNUN1?UNUNUN1?UNU-UM1?1@TJ1@TCU-1AU1U1TMTMU1UTU1U1XOXOV=TFU<1>U1>SCT$T$XHT$S?1>SCT$T$T$T$T$S?T$T$S4T$S4T$S4T$S4S2S2S4S4S2S2S219S2T#T#T#SZT(SZSZT(SJSZSJSZSJSZSJ15SJSJSJSJSJSJ1515WTSJWT15WTS3S3WT1516VQWTWTVQVQWTWTWTWHWHWHVQWHW?13W?Y$W,13Y$X0X0131415X-W$X-X-14W$15W$1516W$17W$17W$1818W3X-19WXWXW3WX18WXY&Y&Y&WGWXWG17WGWGWG17U-XPXP1718U&UFV$TFV$TFXOXO1718TF18TF19XN19TKV;TK19TKTK191:S?1;S?1;S7S>S4S4S41;S>S>1;SDS>1;1S4S4S41<1S01>T#T%T#1>T#1?SM1?T&1@1AT#T#SM1AT#SZSM1ASM1A1BSBSZSZ1BSZ1BSZSZ1CSZSZS5S5SJSJS5SHSJ1ASHSHSJ1ASJS1SJSHSHSH1@1AS1S11AVQ1B1CS1SWS1S1VQVQVQSWVQVQSWSW1@W*1AW*W?X0W?X0Y$X0W?W$X0W$X01?1?1@1AWN1AW$W$W$WNWNW$W3WN1@WN1AY&Y&W3WGWXWGY&WGWXWGW3WBZHZGZHZHV$V$TFXOXOTFU.U.TF1;TFTFTFTFTFUGTKU/TKTETKTKUGUGTETEUGTEU/17U/V%V%TLV%TLU/U/TEV0V%TLV0TBXMXM14XMSX14SXSX14S015S01516S0XGSM1617SBSSSBSN17SBSBSNS9S5S5S516S5S5SLS5SBS9S915S5S515SQSHSHSQ1515SQSQSQSQSQSQ15S1S1SHS114SI15SISISY15SYS1S1S1SWS1SWSWSWW$13W7WA13WAWAWA13WA1415W7WB15WOW(WB1516WBWG16WBWBWFWOWFWG15WFWFWFWFWFW8W8ZPW8W8ZPY'W8W8U.U.1213U.U.13UBUB13UBUBTFTFUBUBTFUGTFUGUBUBUBU%U%UGU%UGUGV0UGUGUGV0UGV0TBU(TBU(1,U(1-U(U(U(1-U(SN1-S9S91-1.1/10S910S9SLSLSLSLSQ1/SLSPSL1/SQSLSQSLSQSQSQSQ1.SQ1/SQ1/SQSQ1/SI10S@10SYS@11SYSY11SYSWSWSYXY1011S@S@W7WOW7W7WOWOWO10WOWFW7W7WFW8WFW8W8ZSW8ZSW8ZSW8ZSZ7Z7Z7ZAZZXKZZZ5ZZSZSX>X>ZAZAZSZSZAZAZ;Z;Z=Z=Z=ZXZTXIXIZVZVZVZVZ@UBUA0OUAV10OV10PUAUAUA0PUAT=0PT=TTTTUATITTTTTITIT=0NT=TITITI0NTI0NV3V3V30NV30OV30OT?0PT?SPSPT*T*SPSP0OT*T*SET*SESRSRSE0NSR0N0OSFSFSF0O0PSFSTSFSF0OSTSTSTSF0O0PSTXEX@XEX@XEX@XEX@X@0NX@X@Z;XVZNZNV1T10M0NT30NT70O0OT=T2T20O0P0Q0RT=T=0R0S0S0T0UV3T2T=T20U0U0V0WV3T*SET*SET*T*T*SGT*SGSGSGSESESE0T0TSGSGSGSGSG0T0USGST0UST0USGSGSGXEX@XEX@XEX=XEX=X=X>X=XFX>X>XFXFX>X>XF0QX>X>0Q0RX>XUXFXU0QT80R0S0ST50T0UT4T2T90UT2T2T20UT90UT7T70U0V0WT0T20W0X0Y0YV3UR0Z0Z1#T0T0URURT0XN0ZSGSGSGXEXEXEXA0YX=XAY'X=XFX=0YX=0YX=0ZXFXFXD0ZXFXF0ZXFV1V1V10Z0ZT7T7T7V10ZTRTRT0T00ZT0T10ZT1T1X=0ZY'Y'XDXDXBXB0YXDXBXBTR0YTRT60Y0ZT6T6V+T6V+V+T6T60YT60YT1T6T6V+T;V+V+XNXYIX1X1X1X1X1/U/VX1X1X)X)X/X//UW6X/W>X/X/VBVBU=VBU$V/XYI/XY>Y>Y>Y>Y>Y>/XY>Y>YIY>Y>YP/WYPYIYIYI/WYIYIYI/WYI/WYIYIYZYZYZ/W/W/X/Y/Z/Z0#YPYPYP0#YP0$Y>X10$X1YP0$0%0&X1X10&X10&0'0(0)0)X1X1X1X10)X1WRX1X1VRVRX1WLVR0(0(0)0*X)WLX)WLWKX)X)WKWKX)X)WKX)WKWKWLWKWK0&VR0'0'WKW%W%X)X)X)0'WK0'WKWK0'WJ0(WJWK0(W%W%W%0(W%W%0(0)W@W@0)WJW%WJWJW@WJW@W@0(W@W60(W6W6W6X/X/WXWXX/X/WXWXVBVB0&0'0'UCUCUCU$U$VBU$VBVB0&0'VBVB0'TSU$U$0'U$U$U$U$0'UCUCUC0'TNTN0'UX0'TNTYTN0'TNTNTNTN0'TNTNV@V@TZTZ0&TZTNTN0&TZ0'0(TZ0(TZTZ0(V@TZ0)V@V@UU0)V@UUV@UUUU0(UU0)V@V@UPUPUPU0UPU0U*V70'V7Y6Y6XGYBYBYBYKYKYB0%YK0&YBYBYKYKYBYBYB0%YBY)0%0&YK0&YKYKY)Y)0&0'0'0(0)Y.0)0*YKYK0*Y.0+0,YKYKYK0,0,0-YK0.Y.Y.0.Y.Y.0.0/00Y.00Y.0101YM02YM02Y.0304Y.Y.04050506YO070708YOYOY.08090:0:0;Z$Z$YO0;YOYO0;0<0=0>Z'Z'0>Z'YM0>0?0@0@0A0B0CZ'Z'0C0DZ'Z'0D0EZ$0EZ$0F0FY/0G0H0H0I0J0K0KY20L0M0M0NY2Y20N0OY20P0P0Q0RY-0RY10SY10S0TZ'0U0U0VY?Y?0V0WY?Y?0WYGY?Y?YGYGY?Y?0V0WY1Y1Y?0WY1Y10W0XY10Y0YY?Y40Z0Z1#1$1%1%1&1'Y?1'Y?Y?Y?YIYIY?1'Y?1'Y?Y?1'1(Y?Y?Y?1(YTYT1(Y?YTYTY?Y?YTYI1'1(Y?Y?YIYI1(YIYI1(1)1*1*1+1,1-Z&Z&YIZ&Y?Y?1,1-1-YIYIYIYI1-YI1.Z&1.1/YI1/YS101111YSWMWMYS11YSWM11X1WMWM1112YIYIWMWMY+12Y+Y+YIY+VXVXVUVUX1X110WMX1X1X110X1X110X1VX1011VU11VUVUVUW%W%11W%X+X+11X+X*11X*X*W6W611W)11W)WX1212WXWXWXW)WXWXWXWX11WXWX11X/WXWXX/X.X.X.WXX.WXWXX.X.WXWXWXWX1.WX1.X.1/X.WUWUX(1/UCTGTGTGTGTGUCUCTGTGUCUCTGTGTYTY1+TYTYTYTGTYTGTGTGTG1*TY1*TNTNTNTN1*TNTZTNTZTN1*TZTZ1*TZTZ1*1+1,1,1-1.U31.1/1011U311U312U31213UUV@V@V@1313V@14UU1415UUUUV@V@1516V@16UUUU16UUUUUU16U0U0U0YDYD1617YDYD1718YKYK18YK1819YDYD19YD1:YD1:YDYDYD1:1;YDYD1;1YOYKYKYK1>1>YO1?YOYOYOYO1?Z(Z(YOZ(1>1?Z(Z(YRYR1?YRZ(Z(YOYO1>1?YOYL1?Y-YRY-Y-1?Y-1@1@1A1B1CY-1C1DYUYYYYY,Y,1C1DY,Y,1DY11E1FY1Y11F1GYVYV1G1HYVYV1HYV1H1I1J1KY1Y11KXXYVYV1KY;YVXXY;Y;Y,1JY,Y;Y,Y,Y,1J1JY;1KY;YTYIXXXXYIYI1JYIX%X%Y;Y;X%X%X%1IX%1IX%X%1IX21J1KY;1KY;1L1LYI1MX%YIYI1M1N1NW#1O1PW#W#W#1P1P1QX&X&1Q1RX&X&1RW#X&X&1R1SVWVWVWVWX&1SVWVW1S1TWVWV1TWVWVWV1T1UVW1UVWVWWVX$WV1U1U1VVWVWWVWVVWWV1UVR1VVRVRVRVR1VVRVR1V1W1W1X1YW'1Y1ZW'W'X$1Z2#2$2$2%2&2'2'W/2(W/W/W/2(W:2(2)2*W'W'W'W'X+2)W'W'W'W'2)W'W'W'W'X+X+X*X*WXWXX*WXWXWXWXWXWX2&WX2&2'WP2'WPWPWPWPWP2'2(WX2(WXWXWXWX2(WP2(2)WPWP2)WW2*WWX(WEX(WEUCUCUC2)2)TYV&V&UC2)UCUCV&V&2)V&UCUC2)2*UCUC2*2+V?2+V?V?TY2+V&TYV&2+V&V&V&2+V&2,V&2,V&V&U22,U2U22,2-U2U22-TYTOTPTYTNTNTNTNTN2,TN2,2-TN2.2.2/TNUUTN2/TN20TN20TN2121YDYD22YDYD22YDYDYDS2S22122S2?VY2?2@X&VY2@VYVYX&X&2@X&2@X&VY2AX&2AX&X&2A2BX&2CX&W:2CW:2CW:W:W:VW2CW:W:W:2CW:W:X&2CX&X&W:W:W:W2?VSWTWTWTS;WTS;S;WTWTS;WTVYVYWTWTWC2;WT2XZX&2>X&2?X&WW?W?W?W<2>W<2?2?W?W?W?WW?2?W?2?W?W?W=WXW=W=W=W=W?W?W=W=2=W=2=W=W=2>W?X'2>2?W?W?W?2?2?2@2AW,2AW,W?W,X'X'2AW?W?W?W?2AW?W?W,2AW?W?2AW?W,2AW,2BW?X0W?X0X0WX2AWX2AWXX0X0WXWXWX2AWXWXW$WXWXWXWX2@WXWX2@WXWXWX2@WXWXX#Y&X#WXX#2?X#X#Y&2?X(UKUKUK2?UKUN2?UNUN2?UNUNUMUMUNU-UMTJ2>TJUM2>UMTJUM2>U-TCTCV=V=V=US4XH2+XHSDS>S4S>S/SDS>2*SXS4T%S4T%S4S4S4T%S4S4S/S/T%T%2'T%S02'S0S0S/S0S/2'T#T#2'T#T#T#T#2'2'SMSMSM2'T&SOT&S02'S0SOT&SMT&SM2&SMSMSMSMSBSM2&2&SB2'SZSM2'SMSBSZSZ2'SZ2'SZ2(SZSZSZSZS5SJSJSJSHSJS3SJS1S12%S1S12%S3S1S1S3VQS3S3S12$S1SAVQVQ2$XYW?W?2$W*W?W*W*W*W$W$2#Y%W$W$W$WN1ZW$WNWNW$1ZW$W$X01ZX0W$X-W3WNW3WNW3W3W3TKTKTF1XU/XNU/V%TLTLTLTBSX1VSXSX1VS0S/S01VS0XGS01V1W1X1Y1YSM1ZSMSMSMSBSBSMSB1Y1ZSSSSSNSNS5S5S51YS9SLS9SL1XS5SQSQSHSH1XSQSQSQSLSQSQSQSQ1WSQSISQ1W1WSISISISI1W1XSYW$W$WA1XW$W$W$WAWAWA1WWAW71WW7W71WW7W7W7WO1WWOWOY&W(WB1W1WWBWBWBWBWBWF1WWGWGWGWFXOU.UBU.U.U.1UU.U.U.1UUBUBU.UBUBTBU(TB1TT/U(T/U(U(U(U(1SSNS9SNS9S91R1S1TS9S9SL1TSP1TXTSP1TSPSPSPS9SLSLSL1SSLSLSLSLSLSLSQSQ1RSQ1S1S1TSQ1USQS:SQ1USISI1US@1US@S:S@1USY1V1WS@SYS@S@SYSY1VSYS@SYS@S@SYXY1UXYWOWF1UWFUBV/UBUBV/V/V/UAUGUGUG1SV)V)V)1ST/T?T/T?1RV3V3V3U(V-1RT?V-V-T?1R1RT?T?T?T?1RT?UET?1RT?T?1R1S1T1UV-V-1UXLSPSPSP1USQ1USR1VSRSR1VSQSQS@SRSRS@S@1US@SRSRSR1U1U1VST1WS8STS8STS8ST1VS8WFWF1VWFUBUB1V1W1WUA1XUAV1UAV1UAUAUA1WUAUAUA1WT=1WTITITITITI1WTI1WV3V3V3TIV3TI1W1WV3V3V3T?T?V31WV3T?V3V31VSPT*T*SRSRSE1VSRSRSRSFSRSFSFSFSE1TSESESFSFSE1TSTS8ST1T1TSTSFSTSFSF1TSFX@X>X@X>V1V1V1T8T4T4T8T4T3T7T3T71PT7T7T7T71PT7T7T1T1T11PT:T21PT2T41PT4T41PT2T1T2T2T2T21P1PT=1QT=1QTIT=TITIV3V3V3T=T=T=1PT2T2T21PT=T21P1QT2V3V3V31PV3V3V3SE1PSG1QSE1QSESGSG1QSG1R1RSGSUSGSGSGSG1RSGSVSGSGX>X>XFXFX>X>XFXF1O1PXFXFV1T8V1T8V1T8V1T51NT5T5T5V1T5V1T5V1T5T71MT5T51MT71MT21NT2T2T2T21NT9T2T91NT21N1OT01OT0T0T01OT0T0T01O1P1QURT21Q1R1S1SURURUR1SV3UR1T1T1UURV3T01UT0T01UURT01VSG1VSGSGXEXEXA1V1VXFXFXFXD1VXDXDXDXD1VXD1V1WXDXD1WXDXDXDV11WV11X1XT7T7T7V1T7V1T1T0T01WT0T7T7T1T11VXDXDXDXDXDXDXBTR1UTRT6T1T1T61UT1T11U1VT6T6V+T6T1T1T6T6YW1TYWYW1TY91UY9YJYJ1U1VYJYJYJYWYWYWYJ1UYWY9Y9Y9YWYFYWYFYFY9YFY9YWY9Y9Y9Y9Y9Y91QY9Y9YIYIY9Y9YIYIYIYIY9YI1NY>Y>Y>1N1OX)X)X)1OX)X)X/X/X/W6VDVDV.VDY6Y6Y61M1MYBYB1N1N1OY6Y6YBYBY6Y6Y:Y:YBYB1M1N1O1PYJ1PYWYWY5YWY5Y5YJYW1OYWY.1OY.Y.1OY5Y.1PY5Y51PY.Y5Y5Y51P1PYWY51QYWYWYWY5YWYWY.Y.Y5Y5Z'Y5Z'Z'1NZ'1NZ'1O1PYXYXYNYNYNYNYN1OYWYWY5YWZ'Y=Z'Z'Z%1MZ%Z%Y=Z%Y=Y=Y9Y9YXYXYNYN1KYNYN1KYNYNYXYIYXYXYIYIYX1JYIYI1JYIYXYXYNYNYNYIYNYNZ%Z%Y=Z%Z%Z%Z%YGZ'1FZ'Z'Z%Z%1FYG1FYGZ'YGZ%1FYGYG1FYGYGYGYNYNZ%YGYGYIYGYGYGYIYG1DYGYIYIYI1CY>Y>Y>Y>Y>YIYIYIYIY>1BY>Y>Y>1BY>Y>Y>1BYIYIYI1BYIYIYZ1BYIYZ1BYZ1B1CYSYP1C1DYIYIYP1DYIYIYIYIYI1DYIYIYI1DYZYZ1DYPYZYP1DYPYPYPYP1DYPX1X1X1Y>Y>1CYPYPX1YPYPYPYP1B1CYP1CYP1DX1X11DX11DYIYIYIYP1DYI1EYIYIYI1EX1X11EX1X11EX1X1X11EX1WRVR1EVRVRX1X1X11EX1X1X)X)X11DX)X)WKWK1DWKW%W%VR1DW%WKW%W%X)X)X)WJX)WKWKWKWJWJ1AWJWKWJWKWJWKW%W%W%W%1?W%W%WJWJWJW@W@W@1>W@WJWJ1>WJW@1>W@W6W@W@1>W6VBVBVB1>VBVB1>1?VB1?UCUCVBVBUCUCVBVBUC1>VBVB1>UCU$U$TSTYU$U$TYTYUCTGUC11?TZTZULTZ1>UL1?1@TZTZTNTNTZ1?TZTZ1?V@TZV@V@V@TZTZ1>UPUUUPUU1>UUUPUU1>UUUUTDTDTDV7YBYBY81=1=Y8YK1>YBYB1>YKYK1>YK1?1?1@1AY0YK1AYKYK1A1BY0Y01B1CY0Y0Y)Y)1C1D1D1E1FY.1F1G1H1I1IY0YK1JY0Y01JY0Y0Y.1J1K1K1LYKYK1LY.1M1N1N1OZ)Z)YK1O1P1Q1QZ)1RZ)1R1S1T1UY.Y.1U1VY.1VY.1WY.Y.Y.1WY.1W1X1Y1Y1Z2#YMY.YMY.Y.1ZYMYMYMYMYMY.YM1YY.1ZY.1Z2#Z)Z)2#2$Z)Z)2$2%2&Z$2&Y.Z$2'Z)Z)Z)2'Z)Z)2'2(YO2(YOYOZ)2(2)2*2*YOYOYOY.Y.Y.2*2*2+2,2-Z$2-Z$Z$2-Z$Z$Z$2-2.Z$Z$2.2/YOYO2/2021222223YAYA2324Z(Z(YAYAZ(Z(Z'Z'2324YMZ'YMYMYMYMYM23YMYM232424Z'YMYMZ'Z'2425YMYM2526YM2627Y/2728Y/Y/Z'Z'Y/Y/2728Y/Y/28Z'Y/Y/Z$28Z$Z$Z$28292:2:Y/2;2Z(Z(2>2?Z(Z(Z(Z(2?2@Y2Y2Y22@Z(Z(Z(2@Z(2@2A2BY/Y/2B2CY/2C2D2E2E2FY2Y22F2GY2Y2Y2Y12GY1Y2Y22G2HY2Y22H2I2IY-2JY-Y22JY12KY1Y1Y-2KZ'2KZ'Z'2KYGYGYG2KY?Z'2LYGYG2LY?YGYGY?Y?Z'Z'2KY?2KY?Y?Y?YGYGY?YGY?Y?2JY?Y?Y?2J2KY?Y?Y?2KY?Y?Y1Y1Y?2JY4Y4Y4Y4Y12JY?Y?2J2K2KY?Y42LY4Y4Y42LY42LY?Y?2LY?2M2NY?Y?2NY1YGYGYG2NYGYGY?Y?2M2NY?Y?YIY?Y?Y?YIYIY?Y?Y?2LY?Y?2L2MY?Y?2MY?Y?Y?Y?Y?Y?2MY?Y?2MY?YIYIY?2MYIYIY?YIY?2LY?YIYIYIYIYSYIYI2KZ&2K2LZ&Z&YS2LYSYS2LYSYSYSYSYSZ&2LYSYSZ&Z&Y?2KY?Y?2KYIYIYIY?2K2L2M2MZ&Z&Z&YI2MYIYIZ&Z&Z&YI2LYIYIYI2LYSYSYSYSYS2L2MYSYS2MYSZ&WM2MWMYPYPYSYSYPYPYP2L2L2MYIY+Y+WMY+Y+Y+VXY+Y+WM2KWMWMX1X1VVVV2JX1VVX1VXVX2J2KVU2KVUVUVXVX2KVUW%W%VRVRX+X+2JX+2J2KX*X*2KW)W)W)W)W)X*W)2J2KWXWXW)W)2KWXWX2KWXWX2KX/X/X/WXWXWXX.WXWXWXX.2IX.X.X.X(WUX(X(2HTY2ITYTY2ITYTYTYTYTY2ITNTZTNTZ2H2ITN2J2JTZU3U3TZTZ2J2KTZ2KUUUU2KV@UUUUTNTNTNU32J2K2LU3TN2LTN2MTNU3TN2MU3U3U8U3TN2LTNTN2LU52MTNU3UUU3UUU32LU32MU3UUUUUU2L2MUUUUV@V@V@2M2MV@V@V@V@UUUUUUV@V@V@UUV@2KUUUUV@V@UUUUV@V@UUUU2IUUUUUU2IUUUUUUUUUPUUUPYD2HY@Y@Y@2HY@Y@2H2IY@Y@YDYD2IYDYKYK2IYKYD2IYDYDYKYKYDYDY@2HY@2IY@2IY@YDY@2IY@YDYKYKYDYDYKYKYD2HYKYK2H2IYKYK2I2J2JYO2K2LYK2LYKYKYOYO2LYO2LYOYKXTYKYKYK2L2LYOYKYOYKYOYKXT2KYOYOYOZ(2KZ(Z(YRYR2KYRYRYRZ(2KZ(2KZ(Z(2K2L2M2N2N2O2P2QY1Y1Y-Y-Y-2PY-Y-YLYLYL2PYL2PYY2QYL2QYYYYYYYYYY2QY-Y-2QY-2Q2RYUYU2R2SYYY,2SY,Y,Y,2SY1Y1Y12S2T2UYVY1Y1YVYVY12TYVYV2T2UYVYVYUYVYUYVYVYVYVY,YVYV2SYVY1Y12SY1Y1Y12SY12SYVYVYVYV2SYVYV2S2TYVYV2TY;2UY;Y,Y,Y,Y;Y,2TY,Y,Y,Y;Y;Y;Y;Y;Y,Y,YIYIYI2RX%X%Y;X%YIYIX%X%Y;2PY;2QY;2QY;Y;2Q2R2SX&2SX&X&X&Y;X&Y;X&YIYIX%YIX%X%2QX%YIYI2QYIYI2QYIW#X2W#X2W#X2X22PX2W#W#X2W#W#W#2OW#2O2PX&X&2P2QX&X&2QX&X&X&X&W#X&2QW#W#2QW#WVWVVWVWWVWV2P2QVWVWX&X&VWVW2PVWVWVW2PVW2PWV2QWVVW2QVWVWWVWVVWWVVW2PVWVWWVX$WV2P2P2QVW2RWVWV2RWVVRVR2RVRWS2RWSVRVRVRVR2RVRVR2RX$X$W'W'W'2QVRW'W'VRVRW'W'W'W'2P2QVRW'W'W'W'2PW'W'2P2Q2RX$X$X$2R2S2S2T2UW/X$2U2V2W2W2X2Y2Z2Z3#W'3$3$3%W'W'W/3%WV3&WV3&WVW/W/W/3&W/3&3'3(3)3)3*3+3,3,3-W'W'3-3.W'W'VRVRW'W'3-WXWPWPWXWX3-WPWX3-3.WPWX3.WPWPWPWPWP3.3.3/30WWWXWXWX30WXWXWX30WX30WPWP30X.WPX.WPWPWP303031WWWWUCUCUC31UCTYUCV&UC30UC3131V&V&V&UC31XRV?3132V?V?V?32V?V?V?32V?U2V?U2V?V?TYTY31TY31TYV&V&TYTYV&TYV&TYV&30V&TYV&TYV&V&U23/V&V&U2U2V&TY3.U23.3/TPTP3/TNTNTNTN3/TNTNUUUU3/30TNUUTNUUTNTNTN3/3/UUUUUUUUUU3/UU3/UU30UU30UUTNUUTNUUUUUU3/YDYDYDYDYDS<3/3/YD30SW93?Y;Y;3?3@Y;Y;Y;W+Y;3?W+W+W+W+W+3?W+3?VYVY3?Y;W+W+3?3@VYVY3@VYVYVY3@3AVYVYWTVYWTWTX&X&3@X&3@X&VY3AX&X&3AX&3AX&X&X&VY3A3B3C3CX&3DX&3DX&VY3EVYVYVY3E3EX&3F3G3GVWX&X&VWVWVW3GVWVW3GVW3GVWX&VWX&X&X&W:VWVWW:W:3E3FW:W:W:3FW:W:W:W:X&3FW/3FW:W:3FW:W:W:W:W:W:3FW:W:3F3GW:W<3GWS>XHSDSDSDSDSXS/T%S/S/T%3?S0S03?S0S0S0T%T#T%T%T#T#SMSMT#SMSMSMSOT%SO3Y>Y>2X2YX1X)2Y2ZX)X)2ZX1X)X)Y62Z3#YBY6Y63#YBYBYB3#YBYBY63#Y6Y63#Y6Y6Y)3#Y)Y)3#Y.3$Y.Y)Y)Y)3$3$Y.3%Y.YJ3%YWYWYJYJ3%YJY5Y5Y.Y.Y5Y5Y.3$Y.3$Y.Y.3$Y.Y.Y.Y5Y5Y.Y.Y53#Y5Y53#YWYWYWY.Z'Y.Z'Y.2Z3#3$3$Z'Y.Y.Z'Z'3$Z'YNYN3$3%YNZ%Z%Z%YNYN3$YNYXYX3$3%YX3%YXYI3%YIYIYIZ'Z%Z'Z'Z'YGZ'YGZ'YGZ'Z'Z%Z%YGYGYGZ%YGYGYG2XYGYG2X2YYIYIY>YIY>Y>Y>Y>YPYPYPYP2WYP2WYIYZYZYIYIYZYZYI2VYIYIYZYPYZ2VYPYP2VYPYI2VYIYI2VYPYIYIYPYP2VYIYIYIYPYPYIYIYI2UYZYPYPYPYP2TYPYPYP2T2UX1Y>Y>YPYPYP2TYPYI2TYPYPYPYPYP2TX12TX1YPYPX1X12TX1YPYIYIYIYP2SX1X12SX1X1X1YIYIYI2S2SX12TX12TYIX12UX1X12UX1WLWLVRWLX1X12T2UX1X)X)X)WKWKWKW%W%W%VRW%WKWJWKWKWJWJW%WJ2PW@W@W@WJWJW%WJ2OW@W6W6W@W62OW6VB2OVBUC2O2PUCUC2PVBUCUCUCUC2PUC2PVBUCUCVBVBUCUC2OTGTGTGTNTN2OTYUY2OUYUYTNTNUYTNVD2NTNTNTNTZTN2NTZTZULTZTN2MTNTNUL2MTNTN2MULTNULTNTNTN2MULUL2MTNV@V@TZ2MV@V@2MV@UPUP2MUPUUUP2MUPUUUPUU2MY8YKY8YKY8Y8Y82LYZ)3>3?Z)Z)3?3@Z)Z)3@Y.3A3BY.Y.Z$Y.3AZ$Z)Z)Y.Y.Y.Z$Z$Z$Z$3@3@YO3AYOZ)Z)YO3AZ)YO3AYO3AYOYOYOZ)Z)3A3B3BYOYOYO3B3CYOYOZ)YO3CYO3C3DY.3EY.Y.Y.3EY.Y.Z$Z$Z$Z$Z$YOZ$Z$YOYOY.3BZ$Z$Y.3B3CZ$YMYMZ$Z$YMZ$Z$Z$YOZ$YOYOZ$Z$YOYOZ$Z$YOYOZ$Z$3>3?YOYAYO3?YAYA3?YAZ$Z$3?Z$Z$Z$Z$YAYO3>3?3@3@YA3A3BYMZ'YMYMZ'Z'3AZ'YMYMZ$YMYMYM3@YMYMYM3@3AZ'Z'YMYM3@3AYMYM3AZ'3B3CYMYM3C3DYMYM3DY/YMYMY/Y/YM3CY/Y/Z'Z'Y/Y/3BZ'Y/Y/Z'Z'Y/Y/Z'Z'Y/Y/Z'Z'Y/Y/Z$3?Z$Z$3?3@Z$Y2Z$Z$Z$3@3@Y23AY23AY/Y/Y/3AY/3B3CY/Y/3CY/Y23CY2Y23C3DY2Y23D3EY2Y23EZ$YAYA3E3FYA3G3GY23HY2YAYAYAZ(3GZ(Z(Z(Z(Z(YRZ(Z(Z(YRYRY2Y23EY2Z(Z(YRZ(Y2Z(Z(Z(Z(Z(3C3DZ(Z(Z(3DY/Y/3DY23DY2Y2Y2Y/Y/Y/3D3DY2Y2Y23DY2Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/3B3C3C3D3E3FY23F3GY1Y2Y2Y23GY23GY23H3HY2Y-Y-Y23H3I3JZ(Y-Z(Y-Z(Y-Z(3IY2Y23IY13IY1Y1Y1Y1Y1Y-3IZ'YGZ'3IYGYG3IYGZ'Y?Z'Y?Y?Y?Z'Y?YG3GY?Y?Z'Z'Z'3GZ'Z'Z'Y?Y?Y?Y23FY?Y?Y13FY?Y?3FY?Y?Y?Y1Y1Y?Y?3EY?3EY4Y1Y13EY?Y4Y4Y?Y?Y43E3EY?3FY?Y?Y?3FY?Y43FY4Y4Y43FY43GY4Y43GY43GY?Y1Y13G3H3IY13IY1Y1Y1YGY?Y?Y?3HYGY?Y?YGY?Y?Y?Y?YIY?Y?YIYIY?Y?3EYI3FY?YIYIYI3FY?Y?Y?3FY?Y?YTYTYIY?Y?Y?Y?YIY?3DYI3DZ&Z&YIYI3DYIYSYSZ&YS3C3DYSYS3DYSYSYS3DZ&Z&Z&Y?Y?Y?YI3C3DYIYIYIYI3DYIY?Y?Y?3D3DYIYIYIYI3DZ&Z&YIZ&YIYIZ&Z&YIYIYI3BYSYSZ&YSZ&YSYSYSYSZ&YSYS3@YSZ&Z&WMWM3?X1X1X1Z&Z&Y+Y+3>3?Y+Y+WM3?WMWMX1X13?X1VXVXVXVUVX3>VUVUVX3>VUVU3>VUVUVU3>X+3?X+W6W63?X*W6W63?X*W6W)W)W)3>3?WXWX3?WXWXWXW)W)W)WXWXX/WX3>X/X/3>X/WXWXWX3>UCTYUC3>UC3>UCTYTGTGTYTYTY3=TYTNTZTZTZ3=TZTZTZ3=3=U3U4U3TZTZ3=3>TZTZTZ3>TZV@3>V@TZTZ3>3?TZ3?3@UUTN3@TNU33@3AU3U33AU3U3U33AU3TNU33AU33BU33BU83CU8TNU6TNTNU8U8TN3BTN3BTNTNUUUU3BUU3BUUUUUUU>U>U>3B3B3C3D3EV@UU3EUU3EV@3FV@3F3G3H3IV@V@3I3J3JUUUUUUYD3J3KY@YDYDY@3KYDYD3KY@YDYDY@Y@Y@3JY@3K3KYK3LYDYDYKYDYDY@YDY@YDY@YDY@YDYDYDY@YDYDYD3HYD3H3IYDYD3IYK3JYDYKYK3JYD3J3KYDYDYKYDYDYD3J3KYKYKYKYOYKYKYOYO3JYOYKYKYK3JYOYO3JYO3JYO3KYOYKYKYK3KYKYOYKYK3J3KYOYOZ(YRZ(Z(Z(YRZ(3JYRYR3JYRZ(3JZ(Z(YRYR3J3KYRYLYRYLZ(3JZ(Z(3JYLZ(YLZ(3JYR3K3KY-3LY-YR3LYRYRY-Y-YRY-Y-3KY-3LYLYL3LYYY-Y-3LY-Y-Y-3L3M3MYY3NYYYY3NYYYYY-Y-3NYUY-3NYUYU3NYUYUYUYUYUYYYUYUYU3M3NYUYUY,Y,Y1Y13MY1YV3MYVYV3MY1YVYVYVYVY-Y-Y1Y1Y13LY1Y13LY13L3M3NYVYVYVY,Y,Y1Y1Y13MY1Y13MY1YV3MYVYVYVY1YVYVY1Y1YV3LY1Y13LY1YVYVYV3LY,3LY,3MY,3MY,Y,YIYIYIX%X%X23LX2Y;X2X2X2X2X2Y;3KX2X23K3LX2X23LX2Y;Y;Y;3LX&X&Y;X&X%X%X%W#W#YIW#3JYIYIYIW#X2X23IW#3IW#X&X&3IW#X&X&W#W#3IW#X2X2W#X23HW#X2X&W#X&X&X&W#W#3GW#W#W#3GW#WVWVVW3GWVWV3GWVVWVWX&X&VWVW3FVWWVWV3FWVVWWVVW3F3FWVVWWVVW3FVWVWWV3FWV3GWVWVVW3GWVWV3GWVVW3GVWVW3GWVVWVWVRVRWS3G3GVRVRVRVRVR3G3H3HX$3IX$VRVR3I3JW'W'3J3KW'W'3KW'3K3LW'W'VRVR3L3M3MX$X$X$VRX$X$X$3L3MW/W/X$X$3M3N3N3OX$X$3O3PW/W/X$W/3PW/X$X$X$3PW/3PW/W/3PW/W/W/X$X$3P3QX$3QX$X$W/3Q3R3S3SX$X$3TW/3TW'W'3TW/W'W'W'W'W/W/W/3S3T3U3U3VW'3WW/W/3WW/WVW/3WW/3WW/WVWVW/W/3WW/3WX$X$X$3W3XX$X$X$X$3XX$X$X$X$3X3XW'X$3YW'W'3Y3Z3ZX$4#W'4#4$W'W'W'4$W'W'4$4%W'W'VRVR4%4&VRVR4&4'WXWXWX4'WXWXWXWP4&4'4(WPWX4(WXWP4(WPWPWPWP4(WWWWWPWPWP4(4(4)WWWW4)WWWWWWWX4)WXWXWXWP4)WP4)WXWPWPWXWX4)X.WPWPWP4)WW4)WWWW4)WWWWWW4)V&UCV&UCV&UC4)4)4*UCV&V&V&UCV&UCUCV?V?UCUC4(4)UCUCV?V?4(4)V?V?4)4*4+V&4+TYTYTYV&TYV&TY4*TYV&TY4*U2U2U24*U2U2U2TP4*TPTP4*TY4+TY4+TNTNTNTN4+TNTNTN4+TNU?4+UUU?UUTN4+TNUUTNUU4+UUUUUU4+UUUUUU4+4,TNUU4,UU4,UUTNUUYDYD4,YDYDYDSX>X>XF3X3YXFXFT5T53YT53Y3ZT7T73ZT5T74#T5T54#T5T94#T9T9T2T24#T2T9T2T9T23Z4#T0T04#T2T0T0T24#T0T04#V34$URT2URT2URV3V34#4$T2UR4$UR4$URURURT2T2T24$T24$T2T24$T2T04%T2T2T2URURUR4$URV3V34$4%V3V34%V3T2T2T0T04$URT0UR4$SUSGSUX?X?XFX?XFXF4#XFX=XDX=XDXD3ZXDXDXFXF3Z4#4#XDXDXDT74#T7T7X=XDX=XD3Z4#T6T64#4$T6T6T64$T6T6YW4$YWYW4$4%YW4&Y9Y94&Y94&4'Y9Y9YJYIYIYIYJYJYIYIYJYJYIYIX14$X1X14$X1X)X)X1X14$4%X1X1X)X)X1X1X)4$Y64$YBYBY64$YBYBY64$YBYBYBYB4$YBY64$Y6Y6YBYB4$YBY)4$Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.3ZY)Y.Y.Y)Y.3ZY.3ZY.Y.Y.YJYWYWYWYJYJ3Y3ZY5Y5Y53ZY.3ZY.Y.3ZY.Y.Y.YWYWY53ZY5YWY5Y5Y.Z'Y.Z'Y.Y.Y.3X3XZ'3YZ'3YZ'Y.Y.Z'Z'3YZ'3Y3ZZ%Z%YNYNZ%Z%YNYNZ%Z%YN3XYNYN3XYX3YYXYXYIYXYXYIYI3XYIYGYIYGYGY>Y>YIYIY>Y>YIYIY>YPY>Y>YIYIYIYZYZYZYIYZYZYPYZYZYPYP3QYPYIY>YIYIY>Y>YIYIYPYIYIYIYIYIYIYPYZYPYPYPYPX13LX1YP3LYPX1YPYPYP3LYPYP3L3MYPYP3M3N3NX1YPYPX1X13NX13NX1X1X1YI3NYIYIYI3NYI3OYIYI3OX13OX1X1X13O3PYIYI3PYIX1X13PX1WRWRX13PX1X13P3QX)X)3Q3RW@W@W@W@W@3R3RW6W6W6VBVBVB3RVBVB3RVBVBVBVB3RVBVB3RVBVBUCVBUCVBVB3Q3RUCTGTGTGTYTNTYTYUY3PUYUYVDVD3P3QULUL3QULULULTNTNTZTZULUL3OUL3PTNTN3PTNTNULUL3PTNV@V@TZTZV@V@3OTZUPUP3O3PUUUUUU3PUP3PUUUUY8Y8Y8YY.Y.4>4?4@Y.Y.Y.YMYMYMY.Y.Y.YMYMYM4=4>4?Y.4?Y.Y.Y.YKY.4?Y.4?Y.4@Y.4@Y.4A4BY.Y.4BY.4B4CZ)Z)4C4DZ)Z)Y.Y.Y.4DY.Y.4DY.4DZ)Z)Z)Z)4DZ)4E4E4FZ)4GY.Y.4GZ)Y.Y.4GZ)Y.Y.4GY.Z)4GZ)Z)4GZ$4HZ$Z)4HZ)4IZ$Z$4IYOZ)Z)Z)YO4HYOYOYO4H4IYOZ)Z)4I4JYO4J4KYOYOZ)4KZ)Z)4KYOYOYO4KZ)YOYOYOZ)YOYOYOYO4JYO4J4KYOYOY.4KY.4L4L4MZ$Z$Z$Z$4MZ$Y.Z$Z$Z$4LZ$Y.Y.Y.Y.4L4MY.Y.Y.4MZ$Z$YOYAZ$Z$YA4LYO4LYO4MYAYA4MYAZ$Z$4MZ$YO4MYOYOYOYOYOZ(YOYOZ(4L4LYA4M4NYO4NYAYA4NYAYAYAZ'Z'YMYMYMYM4MYMYMYM4M4NYMYM4NYMZ'Z'4N4OZ'Z'YM4OZ'Z'4O4PYM4PYMYM4PZ'4Q4RYMYMYM4R4RYM4S4TYMYMY/Y/YMY/4SY/Z'Z'Y/Y/Z$4RZ$Z$4R4S4T4U4UZ$Y2Y2Z$4U4VY2Z$Y2Y2Y2Z$Y2Y2Y24TY/Z$Y/4TY/Z$4UZ$4UZ$4V4V4WY2Y2Y/Y/Y24W4W4XY2Y2Y/4XY2Y24X4YY2Y24Y4ZY2Y24Z5#Y2Y2Z$Z$Z$5#Z$YA5#YAYAY2YAYAYAYAYAZ(Y2Y24YY24YY2Z(Z(YA4YZ(Z(Y2Y24YY2Z(4YZ(YR4YZ(YRYRZ(Z(YR4YY2Y24YY2Y/Y/4YY/Y/Y2Y2Y2Y/Y/4XY24X4YY2Y2Y2Y/Y2Y2Y/Y/Y2Y2Y/4WY/4X4XY?Y?Y?4X4Y4ZY2Y?Y?4ZY?Y2Y14ZY1Y24ZY2Y1Y2Y2Y2Y-Y2Y2Y24Y4YY-Y-Y-Y2Y24YY2Y2Y24YY2Y-Y1Y-4YY1Y14YY1Z(Z(Z(Y-Y24XY2Y2Y2Y1Y1Y1Y-Y14WY1Z'YGZ'Z'YGYGZ'4VYGYG4V4WZ'Z'Z'Y?Y2Y?4VY1Y14VY1Y14VY?Y1Y14V4WY4Y4Y4Y4Y14WY44WY44XY4Y?Y4Y4Y4Y?Y4Y?Y4Y?Y4Y4Y4Y?4U4VY44VY4Y4Y44VY44WY44WY4Y4Y4Y4Y44WY14WY1Y1Y?Y?Y?4WY?Y?4WY1Y14WY1Y1Y?4WY1Y1YGYGY?Y?YIYI4VYIY?4VY?Y?4V4WY?Y?Y?YTYTYT4VYIY?YIYIYIZ&Z&YIYI4UYIYIYIYSYSYIYIYSYSYIYIYSYI4RZ&Z&Z&Y?Y?YIYIY?YIYIYI4PYI4Q4RY?Y?Y?4RY?YI4RYIYIYI4RYIYIYIYSYSYSYS4QYSYPX1X1X1Z&Z&Y+Y+4OY+Y+Y+X14OWMWMX1X1X14OVXVXVU4OVXVXVUVUVXVX4NVUX+X+4NX+4NX+X+X+X*4NX*X*4NX*X*X*WX4NWXWX4N4OWXWXW)W)4O4PWXX/WX4PX/X/4PX/WXX.X.X.UCTY4OTY4OTYUCTYTNTNTY4OTZTZ4OTZTZTZTZ4OTN4OTNTNTZTZ4OTZTZTZ4OTZTZ4O4PV@4P4QV@V@TZTZTZ4QTZTZ4Q4R4RV@TZV@TZTZ4RUU4RU44SU9U44SU9U94SU3U3U34SU3U3U3TNU3TNU3TNU3TNU3TNU3TNU3TNU8TN4P4PU8U8TNTNU7TN4PTN4PTNTNUUUU4PUU4PUUUUUUU>U>U>UUU>4OU>U>UUUU4OUUU>U>UUUUU>UUUUUUV@UUV@4M4MV@4NV@4NV@4OV@V@4OV@V@UUUU4OUUV@V@UUUU4NUUUUUUV@V@V@4NV@V@4NUU4NUUUUUUYDYD4NY@4NY@Y@Y@YDYDY@Y@YDYDY@Y@YDYD4LYD4LYDYDYDYDYK4LYK4L4MYDYDYDYD4MYDY*4MY*Y*4MYKYDYDYKYK4M4N4NYKYDYDYDYKYDYDYKYK4M4NYKYK4N4OYK4OYKYKYOYOYKYKYO4NYKYKYK4NYKYKYOYOYKYKYKYO4MYO4MYOYKYOYKYKYKYOYO4LYO4M4MYO4NYOYRYRZ(YRYRYRZ(YR4LYRZ(YRZ(YRZ(Z(YRYR4KYRZ(4KZ(Z(Z(YRZ(4KZ(Y-YRYRYRYRYR4JY-Y-YRY-4IY-Y-Y-YR4IYRY-Y-Y-Y-YVY-YVY-Y-YLYLYLYYYLY-Y-Y-YYY-YYYYY-Y-YYY-YLYYYLYYYLYYYLYYYYYU4A4BY-Y-YU4BY-YUYUYUYUY-YUYUYUYUY,Y,YUYUY,Y,4>Y14?4@YV4@YVY14@Y1Y1Y1Y1Y1YV4@Y1Y14@Y1Y1Y1Y14@Y1Y14@YVY14@YVYVY1Y1Y14@Y1Y14@Y1YV4@YVYVY1Y1YVYVY1Y1YVYVYVY;Y;Y;4=Y;4>Y;Y,Y;Y;Y;Y,4=Y,4>X2X2Y;X2Y;4=Y;Y;4=Y;Y;Y;Y;X2Y;Y;X2X24X2W#4>W#W#W#X&X&W#W#4=W#4=WVVWVWWVWVVWVWVWVW4W/W/X$X$4>4?X$4?W/W/4?4@W/W/4@4AW/W/X$W/W/W/X$X$X$W/X$4?W/W/4?W/W/W/X$W/W/W/X$X$W/X$4=4>X$X$W/4>4?4@W/X$4@4AX$X$4AX$4AX$X$X$X$X$4A4BW/W/4B4CW/W/W'W'X$X$4B4CW'4CW'W'4CW'W'W'4CX$4DW'4DW'4EW'4E4F4G4HW/W/WVWVWVW/WVW/4F4GWVWVW/W/W/4GW'4GX$X$X$4GX$X$W'4G4H4IX$X$4IX$X$4I4JW'W'W'W'4J4J4KX$X$W'W'4K4LW'W'4L4MX$X$X$4M4MX$W'W'X$4M4NW'4N4OW'W'4O4PW'4QX$4Q4RW'4RW'W'W'W'4RW'W'4RW'W'W'W'4RW'W'4RW'W'W'WXWX4R4SWXWXWX4SWXWP4SWPWX4SWPWPWX4SWXWPWXWX4SWPWPWPWW4SWP4SWWWWWP4S4T4UWPWPWWWWWPWW4T4UWXX.WX4UWX4UWPWPWXWX4UWPWXWXWXX.WPWP4TWWWP4TWWWWWP4T4UWWUCV&UCV&V&V&4TV&UC4TUCUC4TV&V&V&4T4UV?V?4UV?V?V?UCUCV?4UUCUC4U4VUC4VV?V&4VV&V&V&4VV&U2U24V4W4X4YV&TYV&TY4X4YU2U24Y4ZU2U24Z5#TP5$TYTY5$TY5$TYTPTP5$TNTYTNUUUUTNUUTNTNU?U?U?UUU?U?TN4YUUUUTNUUUUUUTNUU4XUUTNUUTNTNUUUU4WUUTNTNTN4WTN4WTNTN4WYDY@YDYDYD4WYDYDS=4WS=4W4XS4LXFXF4LXFXFXFT5T5T7T5T74KT7T7T5T54KT7T5T5T74K4K4LT7T7T5T54LT7T2T2T9T2T2T24K4LT24LT2T04L4MT04NT2T2T0T04MT0T0T0T2T24MV34MURURURURV3UR4MV3V34MV3T2T2T24M4MURT2URT24MT2T2T24MT24NT2T24NT2T2T2T04NURUR4NURV3V3V34NV3V34NV34NURURURURURT04NSGSUSG4NXFXF4N4OXD4OXDXDXFXF4O4PXFXF4PXDXD4PXDXD4PT0T7T74P4QT6T6T1T1T6T6T14PT6T64PT1T6T6T14PT6T6YWY9YW4PY9Y9YWYWY9Y94OY9YW4OYWYW4OY9YWYWYWYWYWY9YW4NY9Y9X1X1X14NX1X14N4OX)4OX)X)4OX)X)X)4O4PX)X)Y6Y64P4Q4Q4R4SYB4S4TYBYBYBYB4T4U4UY6Y6Y6YBYB4U4VY.Y.4VY.Y)Y)Y.Y.4UY.4VY.4VY.4WY.YJYJ4W4XYJYJ4XYJY5Y5Y.Y.Y54WY.Y.4WY.Y.Y.YWYWY54WY.Y.Y.Z'Y.Z'Y.Z'4UZ'Z'Z'Y.Z'Y.4UY.4UY.Z'YNYNZ%Z%YNYNZ%4TYX4TYNYNYXYX4TYX4TYNYNYN4T4U4VYI4V4WYZYPYPX1X1X1YP4VYPX1YPYPYP4VYPYP4V4WYPYP4WYPYPYP4W4X4XX14YX1X1X1YPYPX1X14XX1YPYP4XX14X4YYIYIYIYIYI4YYI4YYIX1YIYIYI4Y4YX1X1X1YI4YX1X1YIYIX1YIYIYIX14XX1X1WR4XX1X1X1X)4W4XX)X)4XX)X)X)WJWJW@4X4XWJW@W@W64XW6W6W@W@W@W6VBVBVB4WVBVB4WVBVBVBVB4WVBVB4W4XVBVBVB4XVBVB4XVB4XTNUY4Y4Y4ZVDTN4ZTNTNTN4ZUL5#5$ULUL5$UL5$5%TNTNTNULTN5%5%TNTNTN5%5&TZTZUPUPUP5&UPUP5&UPUUUPUUUPUPUP5%UPY0Y0YK5%YK5%YKYKY0Y)Y05%Y)Y)Y0Y0Y0Y05$5%5%5&YKYK5&YKYKYKY0Y05&5'Y0Y05'Y05'5(YK5)Y0Y0Y05)5)5*YKYK5*Y05+Y05+Y0YKYKY)Y)5+Y)Y)Y)5+5,5,5-Y0Y0Y)Y)5-5.Y)Y05.5/Y)Y)Y0Y)Y)Y)Y)Y0Y0Y)Y0Y05,Y0Y0Y0Y0Y)Y05,Y05,Y0Y0Y)Y)5,Y0Y)5,Y0Y05,Y)5-5.Y)Y)Y.Y.Y)5-Y)Y)Y.Y.Y)Y.Y)5,Y)Y)Y)5,Y.Y.5,5-Y.Y.Y)Y)Y.Y.Y.Y.5,Y.Y)Y.Y)Y)Y.5+Y)5,Y)Y)Y)5,Y)5,5-Y.Y)Y.Y.Y.Y.Y.5,Y.5,Y.Y.Y.Y)5,Y)Y)5,5-Y.Y.5-Y.Y.Y.5-Y0Y0Y0Y)5-Y05.Y)Y)Y)5.Y)5.Y)5/Y)5/Y)Y)Y.Y.5/Y.Y)Y)Y)5/Y.Y.5/Y.5/50Y.Y.50Y.51Y.51Y)52Y)Y052535454Y)Y)Y)54Y)55565657Y)585859Y05:Y)Y)5:Y)5:5;Y)Y.Y.Y.Y0Y.Y0Y0YKYKY0Y0YK59Y0Y059Y0Y059YKYKYKY0YKY0Y0YCY058YCYC58YCY0Y0Y058YK58YKYK5859YKYK59YCYCYCYCYC595:YCYCYC5:YCYC5:YKYCYC5:5;5;Y.YCY.YK5;YK5YK5>5?YKYKY.Y.5?5@Z)Z)YKYKYK5?Z)Z)Z)5?YKYK5?5@5AZ)5A5BYKYK5BZ)Z)5CZ)YK5C5D5D5EYKYKZ)Z)YKYKZ)Z)YKYKYKYKYOYOYKYOYK5BZ)Z)YOYOYOYO5AYOYKYK5A5BYKYOYKYK5AYOYOYOY.5AYKYKY.Y.5AY.YK5A5B5CYKYK5CY.5CY.Y.Y.5CY.Y.Y.Y.Z'5CZ'5CZ'5DZ'Y.Z'5DZ'5DZ'Y.Y.Y.5DY.5EY.Y.Y.Z'5DZ'5EZ'5EYMYMYMY.Y.Y.5EY.Y.YMYMY.Y.YMYMY.YMYMYMY.Y.5BY.YMY.YMYMY.5A5BZ'Y.5BY.5C5CZ'Z'Z'YKYKYK5CYK5C5DY.YK5DYK5E5E5F5GY.YKYKYK5GYK5GYKYKYKY.5GY.5GY.YKY.5GY.YK5HY.Y.5HY.Y.Y.Y.5H5HZ)Z)Z)5H5IZ)Z)5I5JZ)Z)Z)5JZ)Z)Y.Y.Y.5JY.Y.5JY.5J5KZ)Z)Z)Y.Z)Z)5JZ)Z)Z)Y.Y.5JY.Y.Y.5JY.Z)Y.Z)Z)Y.Z)Z)Z)5H5IZ)Z)Y.Y.Z)Y.Y.Y.Z)5HY.Y.Z$Z$5GZ$5HZ$Z)Z$Z)5H5HZ$5IZ#Z$Z$Z$YOZ)Z)Z)YOZ)Z)YOYOZ)Z)YOZ)Z)Z)Z)YOZ)Z)Z)5D5DZ)5E5FZ)YO5FYOZ)Z)Z)5F5FYO5GYOYO5GYOYOZ)5GYOYOZ)Z)Z)5GZ)YO5GYOY.Y.Y.5G5GZ$Z$Z$Y.Y.Z$Z$5FZ$Z$Z$Y.Z$Y.5FY.5FY.Z$Y.Y.Y.5FY.Y.5FZ$Y.Y.Y.Z$YAZ$YAYAYOYOYOYA5CYA5D5EYAYA5EYAZ$Z$YAYAYOYOYO5DYAYAZ(5D5DYAYAYA5D5EYOYOYAYA5E5FYOYO5FYO5FYA5G5HYMYMZ$YMYMYMYMZ$YMYMZ$5FYMYM5FYMZ'Z'YMYMZ'YMYMYMZ'Z'YM5DZ'Z'5D5EZ'Z'5EZ'YM5EYMYM5EZ'YM5FYM5FYMYMZ'Z'5FZ'YMYMYMY/YMYMYM5E5EY/Y/Y/5EY/Y/Y/YMY/Y/Y/YM5DZ$Z$Z$Z$Z$5DZ$Z$5DY2Z$Y2Z$Z$Y2Y2Z$Z$Z$Z$5BZ$Z$Z$5BZ$Z$5BY2Y25BYMZ$YMZ$5BZ$5C5C5DZ$5EZ$5EZ$5FZ$5FZ$Y25FY/Y2Y2Y/Y/Y2Y2Y/Y/Y25EY25EY2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y25CY/Y/5C5DY/Y/5D5EY/5EY2Y25EY/Y2Y25E5FY2Y2Z$5FYAYAZ$5F5G5HY2Y25HY25HY2YAYAYAYAYA5HY2Y25HY2Z(Z(5H5IZ(Z(5IZ(5IZ(YR5JY2Y25JY2Y/Y/5J5KY25KY2Y2Y/Y2Y2Y25JY2Y2Y2Y/Y/Y/5JY/Y?Y?Y?Y/Y?5IY?Y/Y/Y/5IY?Y?5I5J5J5KY2Y2Y?Y?Y2Y?Y2Y1Y1Y1Y2Y1Y1Y1Y2Y2Y25HY25HY-Y-Y2Y25HY2Y2Y2Y25HY-Y1Y-Y-Y1Y15G5HY25HY2Y1Y-Y-Y-Y15GYGZ'5HY?5HY?Y?5HY?Y?Y?Y2Y1Y1Y1Y?Y?Y15GY?Y?5GY1Y?Y?Y4Y4Y?Y?5FY?5FY4Y1Y4Y?Y?Y4Y4Y45EY4Y4Y4Y?Y4Y4Y?Y?Y4Y?5CY?5DY?Y?Y?Y45DY45DY45EY45EY4Y?Y4Y4Y?Y?5DY?5EY?Y?5EY?Y15EY?Y1Y15EY1Y1Y1Y?5EY1Y1YIYIY?5E5EYIY?YIYIYIY?Y?YIYIY?Y?YIYI5C5DYIYIZ&Z&YSZ&Z&Z&YIYIY?5BY?5B5C5DYIYI5DYIY?YIYIYIY?YI5CYIYIYIYI5CYSYSZ&Z&Z&Z&5BY+X1X1WMWMX1X1VVVVVX5@VUVU5@VUVUVUVRX+VR5@5@5AX+X+5AW6X*X*W65A5B5CW)W)WXWXW)W)WX5BW)W)5BW)WXW)WXWXW)W)WXWXWX5@WXWX5@5AWXWXUCUCUC5A5A5BUCUC5BTNTNTNTZTZ5B5CTZTZ5C5DTN5DTN5ETZTZ5E5FTZTZ5F5GTZTZ5G5HTZ5HTZ5ITZTZ5IV@TZV@V@V@TZTZTZ5HTZTZ5H5ITZTZ5I5JTZ5JTZ5KTZTZ5KUUU4U45K5L5LU9U9U9U4U4U95L5LU35MU35M5NU3U3TNU85NU8TNU8U8U8TNU7TNU7TN5LTNTNU3UU5LUU5LUUU3UUU>UUU>5L5LUUU>U>5LUU5MUUTZTZTZ5MTZ5M5NV@5NV@5OV@5OV@V@V@5OUUV@V@5OUUV@5PV@UUUUUUV@V@V@5OV@UU5OUUUU5OUUUU5O5PY@Y@YD5P5QY@Y@Y@Y@5Q5QYDYDYDYDYDYD5QYD5QYDYD5QYKYDYDY@YD5QYDY*5QY*Y*YKYKY*Y*YK5P5Q5RYKYK5RYKYD5RYDYDYKYK5R5SYKYKYDYDYKYK5R5SYKYK5SYKYKYOYKYOYOYO5RYOYKYK5R5SYKYO5SYO5SYOYKYOYOYOYO5SYO5SYOYOYOYO5S5T5T5UYOYO5U5VZ(5W5WYRZ(5X5XYRZ(Z(5XYLZ(5YYRYRYR5YYRYRYRY-YRY-YRY-YY5WYY5X5XYUYUYUY-Y-YUYUY1Y15WY1Y-5WY-Y-5W5XY-Y-YVYV5XY1YVY1Y1Y1YVY1YVYVY1Y15V5WY1Y15W5X5XYVYVYV5XYVYVYVY1Y1Y15XY1Y1YV5X5XYVYVYV5XY;5YY;Y,5YY,5ZY,Y;Y,Y;5YY;Y,Y;X2X2Y;Y;X2X2Y;Y;X2X2Y;X2Y;Y;5V5WX&X&5WX&W#W#5WW#W#W#X&X&5VW#X&X&X&5VX&X&W#W#X&X&W#W#5U5VX2X25VW#W#W#5V5WWVWVVWVWX&VWX&X&5UWVVWVWVWVWVWWV5TWV5UWVVWWVVWVWWVWV5TW/5TWVVW5UWVWV5UWVVW5UVWVW5UWVVWVWWS5UWSWSVRVRWS5UVRVRVR5UX$5UX$X$5U5V5WX$5W5XX$X$VRX$X$X$5W5XX$X$W'W'W'5XW'W'5X5Y5YX$X$X$5YW'X$W'VRVR5Y5ZW'5ZW'W'VRVRW'W'5Y5ZX$X$VRVRX$X$X$X$X$5YX$X$5YW/W/5YW/W/X$X$W/W/X$X$W/W/X$X$W/W/X$5VX$5W5WW/W/W/X$X$X$5WX$X$5WX$X$X$W/5WX$5W5X5Y5YW/5ZW/X$X$X$5ZX$X$W/W/W'W'X$5YW'W'5YX$W/W/W/5YW/5YW/X$5Y5ZX$X$W/X$W/W/X$X$5Y5ZX$X$5ZX$5ZX$X$X$X$X$X$5ZX$X$X$5ZW/5ZW'W'5Z6#W'W'W/6#W/W/6#6$6%W'W'W/W'6%W/6%6&6'X$X$6'X$6'W'W'W'6'W'6(W'6(W'X$W'X$X$W'6(W'W'6(W'W'6(W'W'X$W'W'W'WV6'WVWV6'W/WVWVW/6'W:W:W'6'W'X$6'W'X$W'W'W'6'6(X$6(X$X$6(6)X$X$X$X$6)X$X$X$X$W'X$X$6(6)W'W'6)6*X$6*X$X$W'W'6*W'W'6*X$X$6*W'X$X$W'W'6*6+W'W'X$X$X$X$X$W'X$X$W'6)X$X$6)6*X$6*6+6,6,6-W'W'6-X$W'W'6-X$W'6.6.X$6/W'W'6/W'W'X$X$W'W'6.W'W'W'6.6/W'W'W'6/W'W'6/60W'W'VRVR6061VRW'61W'WX61WXWP61WXWPWPWX6162WP62WPWPWP62WP63WPWXWPWXWPWX62WPWP62WPWW6363WW64WWWPWPWP64WP64WWWW64WWWWWWWP64656666WWWWWWWXX.WXX.WXWXWX6565WPWPWPWP65WPWPWPWP65WPWPWP65WWWW65WWWW65V&66V&UC66UC6767V&V&V&UC67V?V?6768V?V?6869V?V?UC69V?V?696:V?V?6:6;V?V?UCUC6;6TYTYV&V&TYTY6=TYV&6=TYTY6=TYTYTYV&V&U26=V&V&6=6>V&V&U26>V&V&6>U2TYTY6>6?TYTY6?TY6?6@TPTP6@TYTP6ATP6ATPTPTYTNTYTNTNUUTNUUTNUUTNUUUUUUUU6>TNTNTN6>TNUUTNUUY@6=Y@6>YDYDY7Y7S=S=6=S=SW/W/6>W/6>6?W:W:6?W:6@W:6@W<6A6BW:6BW:W:W:W:W:6BW:W6?W$X-X-W$6?W$6?W$W$6?X-6@X-W$W$W$6@WXWXWX6@WX6@WX6AWXWXWDWXWX6@WXWX6@WXWXWX6@6AW4W4TF6ATF6BTFTKTFTKS6S6S?S?SZSZSB6@SZSZ6@SZSBSZSBSZ6?SZSBSBSB6?SBSB6?SZ6@SZTFTFTF6@S0SOS06@6@6AS0S06ASO6BSO6BT&SOSOSOSOSO6BSOSO6BT&T&SMT&6B6BSM6CSM6CSM6DSMSMSM6DSMS5S5S56DS5S56DSLS5S56D6E6ESQ6FSQ6FSISISISISISQ6F6FSI6G6HSISISI6HSISYSYSYWAWAWA6GWA6G6H6I6IWA6J6KWAWA6KWYWAWY6KWYWYWYWYW7WYW7W7W76IW(6JW(U(U(T/U(T/6IT/T/SL6ISL6JS9S9S96JS9SLSLSLS9S96ISLS9S9SLSLSQSQSQ6H6HSISISISQ6HSQSQSQSKSKSKSQ6GSQSKSQSKSQ6GSQ6GSQSK6GS:6HS:6HSKSKSKS:SKS:S:SK6GSKS:S:6GS:S@6G6HS@S@S@SYS@6HSYSY6HSYS@6HS@S@V)TT6HTTV-V-V-6HT?V-T?6HV-V-V-6H6H6IT?T?6IUET?T?6ISQ6J6KSR6KSRSR6K6LSR6MSR6MSRSRS8S86MS8STS8S8S8S8S8S86LS86L6MSTS8S8S86M6MSTSTSTSTS8STST6L6MT3T3UA6MTITIT=TI6MTITTTT6MV3TIV3TIV3SRSR6L6MS86MS8S8STST6MSTSG6MSGSGT16MT1T16MT26NT2T26NT26O6OT=6P6QT26QT2T2T=T=6Q6RT26RT2T=6RTIT=6ST=6ST=T=V3V36SV36S6T6UT2T26U6VV3T2T2T26V6VV36WV3T=6WT2T26WT=6XT=T26XV3V3SESGSESGSGSGSU6W6WSGSUSUSGSGSG6W6WSUSUSUX>6WXFXF6WXFXFXFT5T56W6XT76XT7T7T7T5T7T7T76WT76XT5T5T56X6XT7T7T7T2T2T26XT2T26XT2T26XT0T06XT2T06YT2T26YT2T0T2T06YT2T2T2T0T2T26X6Y6Y6ZURURUR6ZURUR6ZV37#7$7$UR7%URT27%T2URT2URT27%T2UR7%UR7%URT2URT2T27%T27%T0T0T07%UR7&URV37&URURV3V37&V3UR7&URURURURT0URSG7%SG7&XFXFXD7&7&XF7'7(7(7)XDXDXFXFXD7)XFXF7)XFXFXF7)XFXD7)XDXD7)7*T7T7T1T1T6T6T1T1T6T6T1T1T6T6T1T1T6T6T1T1T6T67%YWYWYWY9Y97%Y9Y9Y9YW7%Y9Y97%Y9YWY9YWY9X1X1X1X)7#7$X)X)X1X1X)X)7#7$X)X)7$X)X)X)X1X17$7%X1X1X17%Y6Y6YB7%7%YB7&YBY6Y6Y67&Y67&YBYB7&YBYBYBY6Y67&YBY6Y6YBYBYBYBY6Y6YBYB7$YB7$7%Y6Y6Y67%Y6Y67%Y6Y6Y6Y)Y.Y)Y)Y)Y)Y)7$Y)7$Y)Y.Y)Y.Y)Y.Y)Y.Y.Y.YJ6ZYWYW6ZYWYWYWYJYJYWYJY5Y56YY.Y.6YY.Y.Y5YWY5Y5Y.Y.6XZ'Y.6X6YZ'6YZ'Y.6Z6ZYNZ%Z%YXYXYNYNYNYX6YYXYN6YYNYNYX6YYXYX6YYI6ZYIYX6ZYXYI6Z7#YZYZYPYP7#YPYP7#7$X1YP7$YPYI7$7%YIYI7%7&YIYI7&YPYPYPX17&X1X1YPYPX1X1YPYPYP7%7%X1X1X1YPX17%X1YPX1X1X1X1YIYIYIYIX1YIYIYI6ZYI7#YI7#YIX1YIYIYIX1YI6ZX1X1YIYIX16ZX1YIX1YIX1X1WRX1X1X1X16XX1X16XX)X1X)X)X)WJW@W@W@WJWJ6V6WW@W@W6W@VBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUCVBVBUCUC6OTNUY6P6PTNTNTNVDVDVD6PVDVD6PTNVDVDTNTNULULTN6OTN6OTNTNULULTNULTN6N6OUL6OULTNTNULULTNTNULULTN6N6N6OTNTNV@6OTZTZV@V@TZTZUPUP6N6OUPUP6OUPUPUPUUUPY0Y06NY0YK6NYKYK6NY)Y0Y)Y0Y0Y06NY0Y06NYKY0Y0YKYKY0Y06M6N6NYKYKYK6N6OYKYK6O6PYKYKY0Y06PY0YK6PYKYKY0Y0YKY06OY0YKY0Y0Y0YKYKYK6NYKYK6N6OYKYKY0Y06OY0YK6OYK6PYKY0YKY0Y06OY06PY)Y)Y0Y0Y)6OY0Y0Y)Y)Y)6OY)6OY0Y0Y06O6PY06P6QY0Y0Y)Y)Y0Y0Y)Y0Y0Y0Y)Y)Y0Y06NY)Y0Y)Y)Y)6NY)6NY0Y0Y0Y)Y)6NY0Y)Y)6NY)Y06NY06OY)Y)6OY)Y)Y)Y)6OY)Y.Y)Y)Y.Y)Y.6NY)Y)6N6OY)Y)6OY)Y.Y.Y)6OY.Y.Y)Y.Y)6NY)Y)Y)Y)Y)Y.Y)Y)Y.6MY)6MY.Y.Y.Y.6MY.6MY.6NY.Y)Y.Y)Y)Y)6MY.Y.6MY.Y.Y.Y.Y)Y.Y.Y0Y)Y0Y0Y)Y)6KY)Y06K6LY)Y)Y)Y)Y.Y)Y.Y)6KY)Y)6KY.Y)Y.Y)6K6KY.6LY.Y)Y)Y)6L6LY.Y.Y.Y)Y)Y)6L6LY.Y.Y.Y.Y.6LY)6L6MY.Y.Y0Y0Y0Y)Y0Y)6LY)Y06LY06MY0Y06M6N6N6OY)Y)6OY)Y)Y)Y)Y)6O6P6PY.Y)6QY.Y.6QY.Y)6QY)6RY.Y.6RY.Y)6RY)6S6SY)Y06TY)Y)6TY)6TY)6U6V6VY)Y0Y0Y)6VY)Y.6V6WY.Y.YK6WYKYKY0Y06WY0Y06WYKYKY0YCY0Y0YCYC6VYCY0Y0Y06VY0Y0YKYK6U6VYKYK6V6WYK6X6X6YY06Z6Z7#YKYK7#7$YKYKYCYC7$7%YCYC7%YKYCYCYC7%YCY.Y.Y.YC7$YCY.Y.Y.7$Y.YK7$YKYKY.Y.7$Y.YK7$YK7%Y.7%Y.7&YK7&YKYKYK7&YKYKYK7&YKYK7&Y.YKYK7&Y.7'7(7(7)YKYKYKYK7)7*7*7+YKYKYKZ)YKYKZ)Z)7*Z)YK7*YKZ)Z)Z)Z)7*Z)YKYKYK7)Z)7*Z)Z)Z)Z)7*Z)7*YKYK7*YKYKYKZ)Z)YKYK7)YKYKYKYKYOYKYKYOYO7(YOYKYKYO7(YKYKYOYOYOYKYOYOY.Y.Y.7&7&Y.YKYKYK7&YKY.YKYKYK7&7&Y.Y.Y.YKYK7&YKYKY.YKY.7%Y.Y.Y.Y.Z'7%Z'7%Z'Y.Z'Y.7%Y.Y.Y.7%Y.7&Y.Z'Y.Y.7%Z'7&Z'7&Z'Z'Z'Z'Z'7&Z'7&Z'Y.Y.Y.YMYMYMY.Y.Y.7%Y.Y.YM7%Y.7%Y.Z'Y.Y.Y.7%Y.Y.Y.7%7%Z'7&7'7'Z'7(Z'YKYKYK7(YKYKYKY.YKYK7'Y.YK7'7(Y.7(Y.7)Y.YK7)YKY.7)Y.Y.Y.YKY.Y.Y.7(Y.7)Y.7)Y.YKYKYKYKYK7)YK7)YKY.7)Y.YK7*7*Y.YK7+Y.Y.7+7,Y.Y.Y.7,YK7,Z)Z)Z)7,Z)7-Y.Y.7-7.Y.Y.Z)Z)7-Z)Z)Z)Y.7-Z)Z)Y.7-Z)Z)7-Y.Z)Z)Y.Y.Z)Z)7,7-Z)Z)Z)Z)Y.Y.Z)Z)7,Z)Y.Y.Z)Z)Y.Y.Z)7+Y.Y.7+Z)7+Z$Z)7,Z)7,Z)7-Z)Z)7-Z$Z$Z$7-Z$7-Z#Z#Z#7-Z#7.Z#7.YOZ)Z)Z)Z)7.Z)YOZ)7.7/Z)Z)7/Z)Z)YO7/YOZ)Z)Z)YOZ)Z)7.YO7.YOYOYOYOZ)YO7.Z)YO7.YOZ)Z)YOYOZ)Z)7-7.Y.7.7/Z$Y.7/Z$Z$7/70Z$Z$Y.70Y.7171Z$Z$Z$7172Y.Z$72Z$Z$Z$YOYA72YA72YAYOYOYA72YOYO7273YO74YO74YO75YAYAZ(75YO7576YA76YAYOYOYAYAYO76YO76YOYO76YAYO77YOYO777878YAYO79YOYOYO7979YAYAYAYMYMZ$Z$YMYMZ$YMYMZ'YMYMZ'Z'76Z'Z'Z'7677Z'Z'77787879YMYM79YMYMYM79Z'7:Z'7:Z'YM7;Z'Z'YM7;YMYMYM7;YM7;Y/Y/Y/7;Y/Y/YMYMZ$Z$Z$Z$Z$7:7:Y27;Y27;77?Z(Z(Z(7?Z(Z(YR7?Z(Z(YRZ(Z(Z(Z(7>7>Z(YRZ(Y/Y/Y/7>Y/Y/Y/Y27=7>Y2Y2Y27>Y2Y27>7?Y2Y2Y/Y/Y/7?Y/7?Y?Y?Y/7?Y/7@Y?Y?7@7AY?Y?7AY?Y/Y/Y2Y27@7AY2Y2Y2Y27A7BY27BY-Y-Y2Y27BY2Y2Y2Y27BY1Y1Y-Y-Y1Y17AY1Y27AY2Y1YGYGZ'Z'Z'7@Z'YGYG7@Y?Y?YGYGY?Y?Y?Y?Y?7?Y?Y?7?7@7@Y?Y4Y47@Y4Y1Y4Y?Y?Y4Y4Y?Y?Y4Y?Y47>Y4Y?Y?Y?Y4Y?Y4Y?Y4Y?Y4Y?7Y?Y1Y?Y?Y1Y17=Y1Y1Y1Y?Y?Y1Y1YIYI7<7=YIYIY?Y?Y?7TZTZ7>7?TZTZ7?7@7@V@7AV@7AV@V@V@TZTZUUUUTNU4TNU9U4U4U9U9TNU9TNU9U47=U9U9U4U37=U37=U3U9U3TNTNTN7=TNU37=U37=U87>U8U7U7TNU7U3UUU3UUU3UUU3UUUUUUU>7;U>7;U>U>V@7;V@7V@TZ7>TZ7?TZV@TZV@7>V@TZV@TZV@TZV@V@7=V@7>UUUU7>7?7?UU7@UUV@V@V@UU7?UUUUUUV@UUUUUUYD7>YDY@7>YDY@Y@YDYDYDY@YD7=Y@Y@Y@YD7=YDY@7=Y@YDYDYDYDYKYD7W07>7?WIW0W07?W0WIW0WIWIY;Y;W+7>7>W+W+W+Y;Y;W+7>Y;Y;7>Y;W0W0WIW0W0W0WIWIW+W+7<7=W+W+7=7>7>W&W&W+W+W+7>W+7>W+W+W+W&W&W&W+W&W9W&W9W9W9W9W2W97;W9W9W+W+7;W+7;W+W9W+VS7;VSVSW+W+7;W+VSW+VSW+W9W9W17:W1W17:W9W1W1W1W9W9W9W2W2W9W9W9W2W9W9W977W9VSW9VS7677W+W+W+VYW+VYW+76W+VY76W+W+W+Y;Y;Y;76VY76VYVYY;Y;VYY;X&75X&X&7576X&X&VYVY767777X&VYVY77X&78X&X&X&VYX&VY7778X&X&X&VYX&X&X&7778X&X&X&78X&X&VYVYX&X&VY7777X&78X&VYX&VYVY7778VYVYVYVYWCVYX&X&77X&X&X&VYX&VY76VY77VWVWX&VWX&76X&X&VWVW76VWX&X&X&7676VWVWVWW:76W:W:76WVW:WVW:76W:W:7677W:W:7778W:7979W/W:7:W/W/7:W/7:W/W:7;W/W/7;W/W:7;W:W:7;W/W:W/W:W:W/W:W/W:W/W/W:WW.W.W.7>W.WHW.W.7=WH7>WHWHWH7>7?WHWH7?7@WHWH7@W.WHW.W.W.WH7?WHW.WWX7>WX7?WX7?WXWXWDWD7?WDWX7?W4W47?W4W4W47?TK7@TK7@TKTFTKSZ7@SB7A7ASZSB7BSB7BSBSBSB7BSBSBSZSZ7BSZSB7BSBSBTF7BTF7CS0SOS0S0S0SOS0S0SOSO7A7BSOSO7BSOS0SOS0SOSO7ASOSOSOSOSO7A7A7BT&T&T&SM7BSMT&7BT&7CT&7CT&7DT&7D7ESMT&SMSMSM7DSMT&SMS5S57D7ES5S5SLSLS5S5S57D7DS5SQ7ES5SQS5SQ7DSQSQSQSQ7D7ESI7ESISQSQSQSISQSISQ7DSQSQ7DSQSQSQSISISYSYWAWAWAWYWAWAWA7BWY7BWYWY7BWYWYWYWAWA7BWAWY7BWYWYWAWA7BWYWAWAWYWYWAWYWAWYW(W(W(WBW(WBWBWB7>U(T/U(SLSLSL7>SL7>SLSLS9S97>7?S97?7@SLSQSQSQ7@SISI7@SISQ7@SQSI7@SKSKSKSKSK7@SKSKSKSQSKSQS:SQS:SQS:SQS:SI7=SI7>SKSKSKS:S:S:S:S@SKSKS@S@SKSKS@S@S@7:S@S@7:SYS@7;S@S@S@SYV)7:V)V)V-V-V-7:7:UET?7;V-V-T?T?V-V-7:T?V-7:7;T?UEUE7;UESRSQSR7;SR7;SRSR7;SQSRSRSR7;SRSRS@S@SRSRS@S@7:S@SR7:SRSRSRS8SRSRS8S8STS8S8S8S8STSTST77ST7778STSTS8S87879S879STSTUAUAUA79UAUA79UAUATITITIT=TIT=TI777879V3SFSRSFSFSRSR78SRS8S8S878STST78STSGSFSGSGT177T17878T279T279T2T1T2T2T2T279T279T2T2T2T=T279T279T2T2T=T=79T=T2T=T2T27879T2T2T=T=79T=T279T2T=T=79T=7:T=TIT=T=T=T=T=7979V37:V37:V37;7X>7:7;X>X>7;7T7T7T7T5T7T5T7T5T7T7T5T57<7=T77=T7T7T2T2T9T9T2T2T9T9T2T0T0T0T0T2T0T0797:T0T0T2T27:T2T07:T0T0URURT2URV3V3URV3T278T2URURV3URURURV3UR77V3V377V3UR77URURV3V3URV3T2URT2UR75UR76UR7677URUR77URT278T2T2T2URT2URT2T276T0T0T0T2T276T276URT2URT2UR76URV3V3UR7676V3UR777778URURSUSUSG78SGSUSUSU77XDXDXDXFXF77XF7778XDXD78XF79XF79XFXDXDXFXDXDXDXFXFXD78XFXF7879XFXF79XF797:XDXD7:7;T7T7T0T0T7T0YWY9YWYWYWY9YWYWY9Y9YWYWY9Y9YWY9X1X1X)X)X1X1X)X)X)X1X)X)X1X1X)X)X1X)X)X)X1X1X)X)X1X1X)X)X1X1X1X)Y6Y6YBYBY6YBY6YBY6YBYBYBY6Y6YBYBY6Y6YBYBY6YBYBYBY6YBYBYBY6YBY6Y6YBYBY6Y6YBYBY6Y6Y6YBY6Y6YBYBY6Y6Y)Y.Y)Y.Y)Y.Y)Y.YJYJYWYWYJYJYWYWY5Y5Y.Y.Y.Y5Y.Y.Y.Y.Y.Z'Z'Z'Y.Z'Y.Y.Y.Z'Z'Z'Y.Z'Z'Z'Y.Z'YNYNZ%Z%YNYXYNYXYXYXYNYNYXYIYXYXYIYIYXYIYXYXYXYIYXYXYXYIYPYPYZYZYPYPYZYPYZYZYZYPYPYPX1X1YPYPYPX1YPYIYPYPYIYPYIYIYPYPYIYIYIYPYIYIYPYPYIYIYPYPYPYIYPYPX1X1YPX1X1X1YPYPX1X1YPX1YPX1YIYIYIX1YIX1YIX1YIYIYIX1YIYIX1X1X1YIX1YIX1X1X1X)X1X)X)X)WJWJW@W@WJWJW@W@UYTNUYUYUYTNUYUYUYTNUYTNVDVDVDTNVDVDTNTNULULTNULTNULTNTNTNULULULTNULULULULULTNULULULTNULULULTNTNULULTNTNV@V@TZV@UPUPUPUUUPUPUUUUUPUPUUUUY0Y0YKY0Y0Y0YKY0Y0Y)Y0Y0Y0Y0YKYKY0YKYKYKY0Y0YKYKY0Y0YKYKY0YKYKYKY0Y0YKY0Y0Y0Y0YKY0Y0YKYKY0Y0YKY0Y0Y0YKY0Y0Y0YKY0YKYKYKY0Y0Y0YKYKY0Y0YKYKY0Y0YKY0Y0Y0YKYKY0Y0YKY0YKY0YKY0Y)Y)Y0Y)Y0Y)Y0Y)Y)Y)Y0Y0Y)Y)Y0Y0Y)Y0Y0Y0Y)Y)Y0Y0Y)Y0Y0Y0Y)Y)Y0Y0Y)Y)Y0Y0Y)Y)Y0Y)Y0Y)Y0Y)Y0Y)Y0Y0Y)Y)Y0Y0Y0Y)Y0Y0Y0Y)Y0Y)Y0Y)Y0Y0Y)Y)Y0Y)Y)Y)Y.Y.Y.Y)Y.Y.Y)Y)Y.Y.Y)Y)Y.Y.Y)Y)Y.Y.Y.Y.Y)Y)Y)Y.Y)Y.Y)Y)Y.Y.Y)Y)Y)Y.Y.Y.Y)Y)Y)Y.Y)Y)Y)Y.Y.Y.Y)Y)Y.Y.Y)Y)Y.Y.Y)Y)Y0Y0Y0Y)Y)Y)Y0Y)Y0Y0Y)Y.Y)Y)Y)Y)Y)Y.Y)Y.Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.Y.Y.Y)Y.Y.Y.Y.Y)Y)Y)Y)Y)Y.Y.Y)Y.Y.Y.Y0Y0Y0Y)Y0Y)Y0Y0Y0Y0Y0Y)Y0Y0Y0Y)Y0Y)Y)Y)Y0Y)Y0Y)Y0Y)Y)Y)Y)Y)Y0Y)Y)Y)Y)Y.Y)Y.Y.Y.Y)Y.Y)Y)Y)Y.Y)Y.Y.Y.Y.Y)Y)Y.Y.Y.Y)Y.Y)Y)Y.Y.Y)Y.Y)Y.Y)Y.Y)Y.Y)Y.Y0Y)Y0Y)Y)Y)Y0Y0Y)Y)Y0Y)Y0Y)Y0Y)Y)Y)Y0Y0Y)Y)Y0Y0Y)Y)Y)Y0Y)Y)Y)Y.Y)Y.Y.Y.Y)Y.Y.Y.Y0Y0YKYKYKY0YKYKY0Y0YKYKY0Y0Y0YCY0YCY0Y0Y0Y0Y0YKY0Y0YKYKY0Y0YKYCY0YCYCYCYKYCYKYCYCYCY0Y0YCYCY0YCY0YCY0YCYCYCYKYKYCYCYKYKYCYCYKYKYCYCYKYCYCYCYCYKYCYCYKYCYCYCYKYKYCY.YCY.YCY.YCY.Y.Y.YKYKYKY.YKYKYKY.YKY.YKY.YKY.Y.Y.YKYKY.YKY.Y.Y.Y.Y.YKY.YKYKYKY.YKYKYKY.Y.YKYKY.Y.YKYKY.Y.YKY.YKY.YKYKY.YKYKYKYKY.YKYKY.Y.YKY.YKYKYKZ)YKYKZ)Z)Z)Z)Z)YKZ)Z)Z)YKYKZ)Z)Z)YKZ)YKZ)Z)Z)Z)YKYKZ)YKZ)YKYKZ)Z)Z)Z)Z)YKZ)Z)Z)YKZ)YKYKYKZ)Z)YKYKYKYOYKYOYOYKYOYOY.Y.YKY.Y.Y.YKYKYKY.YKYKYKY.YKY.YKY.Y.Y.YKYKY.Y.Y.Y.YKY.Y.Z'Z'Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Y.Y.Z'Y.Y.Y.Z'Y.Z'Z'Z'Z'Z'Y.Z'Y.Z'Y.Z'Y.Z'Y.Y.Y.Z'Y.Z'YMY.YMYMY.Z'Z'Z'Y.Z'Z'Z'Y.Y.Y.Z'Y.Y.Y.Z'Z'Z'YMYMZ'Z'YMYMY.Y.Y.Z'Y.Z'Z'Z'YKYKYKY.YKY.Y.Y.YKY.Y.Y.YKY.YKY.YKY.YKY.YKY.YKY.YKYKYKY.YKY.Y.Y.YKY.YKY.YKY.YKY.YKY.YKY.YKYKYKY.YKY.Y.Y.YKY.YKY.Y.Y.YKY.YKY.YKY.Y.Y.Z)Y.Y.Y.Y.Z)Y.Y.Z)Z)Y.Y.Z)Y.YKYKZ)Z)Z)Z)Y.Y.Z)Y.Z)Z)Y.Y.Z)Z)Y.Z)Z)Z)Y.Z)Z)Z)Y.Y.Y.Z)Y.Y.Y.Z)Y.Y.Z)Y.Y.Y.Y.Z)Y.Y.Z)Z)Z)Z)Y3Y3Y.Y.Z)Z)Y.Y.Z)Z)Z)Z$Z)Z$Z$Z$Z)Z$Z)Z$Z)Z$Z)Z$Z)Z$Z)Z$Z$Z$Z#Z$Z#Z$Z)Z#Z#Z#Z#Z#Z)Z#Z)Z#Z)Z)Z)Z)YOZ)YOZ)YOYOYOZ)YOYOZ)Z)YOYOZ)Z)YOYOZ)YOZ)YOZ)Z)Z)YOZ)YOYOYOYOZ)YOYOZ)Z)Z)YOZ)Z)YOYOZ)YOYOYOY.Y.Y.Z$Y.Y.Y.Z$Y.Y.Z$Z$Y.Y.Y.Z$Y.Y.Z$Z$Y.Z$Y.Y.Y.Z$Z$Z$Y.Z$Z$Z$Y.Y.Y.Z$Y.Y.Z$Z$Y.Z$Z$Z$YOYAYAYAYOYAYOYOYAYAYAYOYAYAYOYOYAYAYOYAYOYAYOYAYOYOYOYAYOYAYOYOYAYAZ(Z(YOYAYAYAYOYAYAYAYAYAYOYOYOYAYOYOYOYAYOYOYAYAYOYOYOYAYOYOYOYOYAYAYOYOYAYOYAYAYOYAYAYAYOYOYOYAYAYAYOYOYOYAZ'Z'YMZ'Z'Z'YMYMZ'Z'YMYMZ'Z'YMYMZ'Z'YMZ'YMZ'YMYMZ'Z'YMYMZ'Z'YMYMYMYMYMZ'Z'Z'YMZ'YMZ'YMYMZ'Z'YMZ'Z'Z'YMYMYMYMY/YMYMYMYMY/Y/YMY/Y/Z$Z$Y2Y2Z$Z$Z$Y2Z$Y2Y2Y2Z$Z$Y2Y2Z$Z$Y2Y2Z$Z$Y2Y2Z$Y2Z$Y2Z$Y2Y2Y2Z$Y/Z$Y/Z$Y/Z$Z$Z$Y/Z$Z$Z$Y/Y/Y/Z$Z$Z$Y2Z$Z$Z$Y2Y2Y/Y2Y2Y2Y/Y2Y2Y2Y/Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y2Y2Y/Y/Y/Y2Y/Y/Y2Y2Y/Y/Y2Y/Y/Y/Y/Y2Y/Y/Y2Y2Y/Y2Y2Y2Y/Y2Y2Y2Z$Z$Z$YAYAYAZ$Z$Z$Z$YAYAZ$Z$YAYAZ$YAZ$YAYAY2Y2Y2Y2Y2YAY2YAZ(Z(Z(Y2Z(Z(Z(Z(Y2Y2Y2Y2Y2Z(Y2Z(Z(Z(YRYRZ(YRYRYRYRYRZ(Z(Z(YRYRY/Y/Y2Y2Y/Y/Y/Y2Y/Y/Y2Y2Y2Y/Y2Y2Y/Y/Y2Y2Y/Y2Y2Y2Y/Y/Y/Y?Y/Y/Y?Y?Y/Y?Y/Y?Y/Y?Y/Y/Y?Y?Y2Y2Y?Y2Y2Y2Y2Y?Y2Y2Y/Y/Y2Y2Y/Y2Y2Y2Y2Y2Y2Y-Y2Y2Y-Y-Y2Y-Y-Y-Y2Y2Y-Y-Y2Y2Y2Y1Y-Y1Y-Y-Y2Y2Y2Y1YGYGZ'YGYGYGY?Y?Y?Y?Y1Y1Y?Y?Y1Y1Y?Y1Y1Y1Y?Y?Y4Y4Y4Y4Y1Y4Y4Y?Y4Y4Y4Y?Y?Y?Y4Y?Y4Y?Y?Y?Y1Y1Y1Y?Y1Y?Y1Y?Y1Y1Y?Y?Y?Y1Y?Y?Y1Y1Y?Y1Y1Y1Y?YIY?Y?YIYIY?YIYIYIY?Y?YIYIY?YIYIYIY?YIY?YIY?YIY?Y?Y?YIYIYIY?YIYIYIY?Y?YIYIY?YIYIYIZ&Z&VXVXVXVUVXVXVUVUVXVUVUVUVRVRX+X+VRX+X+X+VRVRVRX+W6W6W6X*W6W6X*X*W6W6X*X*W)W)WXWXW)W)WXW)WXX/WXWXX/X/WXWXX/X/WXWXWXX/WXX/UCTYUCTYUCTYTYTYTYTNTYTYTZTZU3U3TZTZU3U3TNU3TNU3TNU3TNTNU3U3TNTNTZTZU3U3TZTZU3U3TZTZU3U3TZTZTZV@TZTZV@V@TZTZTZV@TZV@V@V@TZV@TZV@TZV@TZV@TZTZTZV@TZTZV@V@TZTZTZUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZTZUUUUTZV@TZV@TZV@TZV@TZV@TZV@U4U4U9U9U4U3U4U3U4U3U9U3TNTNU3U3TNTNU3U3TNTNTNU8TNU8U8U8UUUUU>U>UUUUU>U>V@UUV@UUV@UUV@UUV@UUV@V@V@V@V@UUTZV@V@V@TZV@TZV@TZTZTZV@TZTZTZV@TZV@V@V@TZV@TZV@V@UUV@UUUUUUV@UUUUUUV@V@UUUUV@UUV@UUV@UUV@UUV@UUV@V@UUUUYDYDYDY@YDYDY@Y@YDYDYDY@Y@YDYDYDY@YDYDYDYKYKYDYDYKYKYKYDYDYKYDYKYKYKYDYKYKYKYDYDYKYKYDYDYKYKYDYDYOYOYKYOYKYKYKYHYHYHYKYHYOYOYKYOYKYOYKYOYOYOYOYQYOYQYOYQYOYOYQYQYOYOYQYQYQYQYOYOYQYQYOYOYQYQYQYOYRYRZ(Z(YRYRZ(YRZ(YRZ(Z(YRYRZ(YRYRYRZ(YRZ(YLZ(YLYLYLZ(YLYRY-Y-Y-YYYYYUYUYYYUYYYUYYYUYUYUY1Y1Y-Y1Y1Y1Y-Y1Y-Y1Y-Y-YVYVY1Y1YVYVYVY1Y1Y1YVYVY1Y1YVY1Y1YVYVYVY1Y1YVYVY1Y1Y1YVY1YVYVYVY1YVYVYVY1Y1YVYVY1YVYVYVY;Y;Y,Y;Y,Y;Y,Y,Y,Y,Y,Y;Y,Y,Y,Y;Y;Y;X&X&Y;X&X&X&W#W#X&W#W#W#W#X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&W#W#X&X&WVWVVWVWVWWVVWWVVWWVVWWVWVWVWVW/WVWVVWVWVWWVVWVWWVWVVWWVVWWVVWVWWVWVVWWVVRVRWSVRVRVRWSWSVRVRVRX$VRX$X$X$VRVRVRX$VRX$X$X$VRVRVRX$VRX$VRVRX$VRX$X$VRVRX$X$VRVRX$X$VRVRX$X$VRVRX$X$W'W'X$X$W'X$X$X$W'W'X$X$W'W'X$X$VRVRW'VRVRVRX$X$VRX$VRX$VRX$X$X$X$X$W/W/X$X$W/X$X$X$X$W/W/W/X$W/X$X$W/W/X$X$W/W/X$W/W/W/X$W/X$X$X$X$W/W/X$X$W/W/X$X$W/W/X$X$W/W/X$X$W/W/W/W/X$W/W/W/X$W/W'W'X$X$W'X$X$X$W/W/W/X$W/X$X$X$W/X$X$X$W/W/W/X$W/X$X$X$X$X$W/W/X$X$W/W/X$X$W/X$X$X$W'W'W/W/W/W'W/W/W'W'W/X$W/W/X$X$W/W/X$X$W/W/X$W/W/W/W/X$W/W/W'W'W/W/W/W/W/W'W/W/W/W'W/W'W'W'X$X$X$W/W'X$W'X$W'W'X$X$W'W'X$W'W'X$W'X$W'X$W'W'X$X$W'X$WVWVWVW/W/W/WVWVW/W/WVWVW/W/W:W:W/W/W:W:X$W'X$W'W'W'X$X$X$W'X$X$W'X$X$X$X$W'W'W'W'X$W'X$X$X$X$W'X$X$W'W'X$W'X$X$X$W'X$X$W'W'X$W'X$W'X$X$W'W'X$X$W'W'X$X$W'W'X$X$W'W'X$W'X$W'X$X$W'W'W'X$W'X$X$X$X$X$X$W'X$X$W'W'X$X$W'W'X$X$W'W'X$W'X$W'X$X$X$W'X$X$W'W'X$X$W'X$W'W'X$W'X$X$X$W'X$X$W'W'X$W'W'W'X$X$W'W'X$X$X$W'X$X$W'W'X$W'X$W'X$X$W'W'X$W'W'W'X$W'W'W'W'W'X$X$X$X$W'W'W'W'X$X$X$X$W'W'W'VRW'W'VRVRW'W'VRVRW'VRWXWXWPWPWPWPWPWXWXWPWPWPWXWXWPWPWXWPWXWXWXWXWPWPWPWPWWWWWPWPWWWWWPWWWPWWWPWPWPWWWPWWWWWWWPWPWWWPWPWPWWWWWPWWWWWWWPWPWWWWWPWWWPWWWPWPWPWWWPWPWWWWWPWWWWWWWPWWWWWWWXWXWPWPWXWPWPWPWPWXWPWPWPWWWPWWWPWPWPWWWPWPWWWPWPWPWPWWWPWPWWWWWPWWWWWWV&V&UCV&UCV&UCV&UCV&V&V&UCV&UCUCUCUCV?V?UCUCV?V?UCUCV?V?UCUCV?V?UCUCV?V?UCUCUCV?UCUCV?V?UCUCV&V&UCUCV&V&UCUCV&V&UCV&V&V&V?V&V?V&V?V?U2U2U2V&U2U2V&V&U2U2V&TYV&V&V&V&V&TYV&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2V&V&U2U2TYTYTPTYTYTPTPTPTPTYTPTPTYTYTPTYTYTYTPTYTPTYTPTPUUUUTNUUTNTNUUUUY@YDY@YDY@YDY@YDSXFXFXFXFX>XFXFX>X>XFXFX>XFXFXFT5T5T7T5T7T5T7T7T5T5T7T5T5T5T7T5T5T5T7T5T5T5T7T7T5T5T5T7T5T5T7T7T0T2T0T0T2T2T0T2T0T2T0T0T0T2T0T0URURT2URURV3URURV3URURURV3V3URV3T2URT2URT2T2T2URT2T2T2URT2URURURT2URT2T2T2URT2T2T2T2T0T0T0T2T0T0T2URT2URT2URURURURV3URV3V3V3URURURV3URURURV3URURV3V3URURSGSUSUSUXFXFXDXDXFXFXDXFXDXFXDXFXFXFXDXFXFXFXDXFXDXFXDXDXFXFXDXFXDXFXDXDXFXFXDXDXFXFXDXFXFXFXDXFXDXFXDXDXFXFXDXDT7T0T7T7T0T0T7T7", T = ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers", "Africa/Asmara", "Africa/Bamako", "Africa/Bangui", "Africa/Banjul", "Africa/Bissau", "Africa/Blantyre", "Africa/Brazzaville", "Africa/Bujumbura", "Africa/Cairo", "Africa/Casablanca", "Africa/Ceuta", "Africa/Conakry", "Africa/Dakar", "Africa/Dar_es_Salaam", "Africa/Djibouti", "Africa/Douala", "Africa/El_Aaiun", "Africa/Freetown", "Africa/Gaborone", "Africa/Harare", "Africa/Johannesburg", "Africa/Juba", "Africa/Kampala", "Africa/Khartoum", "Africa/Kigali", "Africa/Kinshasa", "Africa/Lagos", "Africa/Libreville", "Africa/Lome", "Africa/Luanda", "Africa/Lubumbashi", "Africa/Lusaka", "Africa/Malabo", "Africa/Maputo", "Africa/Maseru", "Africa/Mbabane", "Africa/Mogadishu", "Africa/Monrovia", "Africa/Nairobi", "Africa/Ndjamena", "Africa/Niamey", "Africa/Nouakchott", "Africa/Ouagadougou", "Africa/Porto-Novo", "Africa/Sao_Tome", "Africa/Tripoli", "Africa/Tunis", "Africa/Windhoek", "America/Adak", "America/Anchorage", "America/Anguilla", "America/Antigua", "America/Araguaina", "America/Argentina/Buenos_Aires", "America/Argentina/Catamarca", "America/Argentina/Cordoba", "America/Argentina/Jujuy", "America/Argentina/La_Rioja", "America/Argentina/Mendoza", "America/Argentina/Rio_Gallegos", "America/Argentina/Salta", "America/Argentina/San_Juan", "America/Argentina/San_Luis", "America/Argentina/Tucuman", "America/Argentina/Ushuaia", "America/Aruba", "America/Asuncion", "America/Atikokan", "America/Bahia", "America/Bahia_Banderas", "America/Barbados", "America/Belem", "America/Belize", "America/Blanc-Sablon", "America/Boa_Vista", "America/Bogota", "America/Boise", "America/Cambridge_Bay", "America/Campo_Grande", "America/Cancun", "America/Caracas", "America/Cayenne", "America/Cayman", "America/Chicago", "America/Chihuahua", "America/Ciudad_Juarez", "America/Costa_Rica", "America/Coyhaique", "America/Creston", "America/Cuiaba", "America/Curacao", "America/Danmarkshavn", "America/Dawson", "America/Dawson_Creek", "America/Denver", "America/Detroit", "America/Dominica", "America/Edmonton", "America/Eirunepe", "America/El_Salvador", "America/Fort_Nelson", "America/Fortaleza", "America/Glace_Bay", "America/Goose_Bay", "America/Grand_Turk", "America/Guadeloupe", "America/Guatemala", "America/Guayaquil", "America/Guyana", "America/Halifax", "America/Havana", "America/Hermosillo", "America/Indiana/Indianapolis", "America/Indiana/Knox", "America/Indiana/Marengo", "America/Indiana/Petersburg", "America/Indiana/Tell_City", "America/Indiana/Vincennes", "America/Indiana/Winamac", "America/Inuvik", "America/Iqaluit", "America/Jamaica", "America/Juneau", "America/Kentucky/Louisville", "America/Kentucky/Monticello", "America/Kralendijk", "America/La_Paz", "America/Lima", "America/Los_Angeles", "America/Lower_Princes", "America/Maceio", "America/Managua", "America/Manaus", "America/Marigot", "America/Martinique", "America/Matamoros", "America/Mazatlan", "America/Menominee", "America/Merida", "America/Mexico_City", "America/Miquelon", "America/Moncton", "America/Monterrey", "America/Montevideo", "America/Montserrat", "America/Nassau", "America/New_York", "America/Nome", "America/Noronha", "America/North_Dakota/Beulah", "America/North_Dakota/New_Salem", "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Paramaribo", "America/Phoenix", "America/Port-au-Prince", "America/Port_of_Spain", "America/Porto_Velho", "America/Puerto_Rico", "America/Punta_Arenas", "America/Rankin_Inlet", "America/Recife", "America/Regina", "America/Rio_Branco", "America/Santarem", "America/Santiago", "America/Santo_Domingo", "America/Sao_Paulo", "America/Scoresbysund", "America/Sitka", "America/St_Barthelemy", "America/St_Johns", "America/St_Kitts", "America/St_Lucia", "America/St_Thomas", "America/St_Vincent", "America/Swift_Current", "America/Tegucigalpa", "America/Thule", "America/Tijuana", "America/Toronto", "America/Tortola", "America/Vancouver", "America/Whitehorse", "America/Winnipeg", "America/Yakutat", "Antarctica/Casey", "Antarctica/Davis", "Antarctica/DumontDUrville", "Antarctica/Macquarie", "Antarctica/Mawson", "Antarctica/McMurdo", "Antarctica/Rothera", "Antarctica/Syowa", "Antarctica/Troll", "Antarctica/Vostok", "Arctic/Longyearbyen", "Asia/Aden", "Asia/Almaty", "Asia/Amman", "Asia/Anadyr", "Asia/Aqtau", "Asia/Aqtobe", "Asia/Ashgabat", "Asia/Atyrau", "Asia/Baghdad", "Asia/Bahrain", "Asia/Baku", "Asia/Bangkok", "Asia/Barnaul", "Asia/Beirut", "Asia/Bishkek", "Asia/Brunei", "Asia/Chita", "Asia/Colombo", "Asia/Damascus", "Asia/Dhaka", "Asia/Dili", "Asia/Dubai", "Asia/Dushanbe", "Asia/Famagusta", "Asia/Gaza", "Asia/Hebron", "Asia/Ho_Chi_Minh", "Asia/Hong_Kong", "Asia/Hovd", "Asia/Irkutsk", "Asia/Jakarta", "Asia/Jayapura", "Asia/Jerusalem", "Asia/Kabul", "Asia/Kamchatka", "Asia/Karachi", "Asia/Kathmandu", "Asia/Khandyga", "Asia/Kolkata", "Asia/Krasnoyarsk", "Asia/Kuala_Lumpur", "Asia/Kuching", "Asia/Kuwait", "Asia/Macau", "Asia/Magadan", "Asia/Makassar", "Asia/Manila", "Asia/Muscat", "Asia/Nicosia", "Asia/Novokuznetsk", "Asia/Novosibirsk", "Asia/Omsk", "Asia/Oral", "Asia/Phnom_Penh", "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", "Asia/Qostanay", "Asia/Qyzylorda", "Asia/Riyadh", "Asia/Sakhalin", "Asia/Samarkand", "Asia/Seoul", "Asia/Shanghai", "Asia/Singapore", "Asia/Srednekolymsk", "Asia/Taipei", "Asia/Tashkent", "Asia/Tbilisi", "Asia/Tehran", "Asia/Thimphu", "Asia/Tokyo", "Asia/Tomsk", "Asia/Ulaanbaatar", "Asia/Urumqi", "Asia/Ust-Nera", "Asia/Vientiane", "Asia/Vladivostok", "Asia/Yakutsk", "Asia/Yangon", "Asia/Yekaterinburg", "Asia/Yerevan", "Atlantic/Azores", "Atlantic/Bermuda", "Atlantic/Canary", "Atlantic/Cape_Verde", "Atlantic/Faroe", "Atlantic/Madeira", "Atlantic/Reykjavik", "Atlantic/South_Georgia", "Atlantic/St_Helena", "Atlantic/Stanley", "Australia/Adelaide", "Australia/Brisbane", "Australia/Broken_Hill", "Australia/Darwin", "Australia/Eucla", "Australia/Hobart", "Australia/Lord_Howe", "Australia/Melbourne", "Australia/Perth", "Australia/Sydney", "Etc/GMT", "Etc/GMT+1", "Etc/GMT+10", "Etc/GMT+11", "Etc/GMT+12", "Etc/GMT+2", "Etc/GMT+3", "Etc/GMT+4", "Etc/GMT+5", "Etc/GMT+6", "Etc/GMT+7", "Etc/GMT+8", "Etc/GMT+9", "Etc/GMT-1", "Etc/GMT-10", "Etc/GMT-11", "Etc/GMT-12", "Etc/GMT-2", "Etc/GMT-3", "Etc/GMT-4", "Etc/GMT-5", "Etc/GMT-6", "Etc/GMT-7", "Etc/GMT-8", "Etc/GMT-9", "Etc/UTC", "Europe/Amsterdam", "Europe/Andorra", "Europe/Astrakhan", "Europe/Athens", "Europe/Belgrade", "Europe/Berlin", "Europe/Bratislava", "Europe/Brussels", "Europe/Bucharest", "Europe/Budapest", "Europe/Busingen", "Europe/Chisinau", "Europe/Copenhagen", "Europe/Dublin", "Europe/Gibraltar", "Europe/Guernsey", "Europe/Helsinki", "Europe/Isle_of_Man", "Europe/Istanbul", "Europe/Jersey", "Europe/Kaliningrad", "Europe/Kirov", "Europe/Kyiv", "Europe/Lisbon", "Europe/Ljubljana", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", "Europe/Malta", "Europe/Mariehamn", "Europe/Minsk", "Europe/Monaco", "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Podgorica", "Europe/Prague", "Europe/Riga", "Europe/Rome", "Europe/Samara", "Europe/San_Marino", "Europe/Sarajevo", "Europe/Saratov", "Europe/Simferopol", "Europe/Skopje", "Europe/Sofia", "Europe/Stockholm", "Europe/Tallinn", "Europe/Tirane", "Europe/Ulyanovsk", "Europe/Vaduz", "Europe/Vienna", "Europe/Vilnius", "Europe/Volgograd", "Europe/Warsaw", "Europe/Zagreb", "Europe/Zurich", "Indian/Antananarivo", "Indian/Chagos", "Indian/Christmas", "Indian/Cocos", "Indian/Comoro", "Indian/Kerguelen", "Indian/Mahe", "Indian/Maldives", "Indian/Mauritius", "Indian/Mayotte", "Indian/Reunion", "Pacific/Apia", "Pacific/Auckland", "Pacific/Bougainville", "Pacific/Chatham", "Pacific/Chuuk", "Pacific/Easter", "Pacific/Efate", "Pacific/Fakaofo", "Pacific/Fiji", "Pacific/Funafuti", "Pacific/Galapagos", "Pacific/Gambier", "Pacific/Guadalcanal", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", "Pacific/Majuro", "Pacific/Marquesas", "Pacific/Midway", "Pacific/Nauru", "Pacific/Niue", "Pacific/Norfolk", "Pacific/Noumea", "Pacific/Pago_Pago", "Pacific/Palau", "Pacific/Pitcairn", "Pacific/Pohnpei", "Pacific/Port_Moresby", "Pacific/Rarotonga", "Pacific/Saipan", "Pacific/Tahiti", "Pacific/Tarawa", "Pacific/Tongatapu", "Pacific/Wake", "Pacific/Wallis"]; - if (W = +W, !(-90 <= (Y = +Y) && Y <= 90 && -180 <= W && W <= 180)) - throw new RangeError("invalid coordinates"); - if (90 <= Y) - return "Etc/GMT"; - for (var V = -1, S = 48 * (180 + W) / 360.00000000000006, U = 24 * (90 - Y) / 180.00000000000003, Z = 0 | S, $ = 0 | U, K = 96 * $ + 2 * Z, K = 56 * X.charCodeAt(K) + X.charCodeAt(K + 1) - 1995;K + T.length < 3136; ) - K = 56 * X.charCodeAt(K = 8 * (V = V + K + 1) + 4 * ($ = 0 | (U = 2 * (U - $) % 2)) + 2 * (Z = 0 | (S = 2 * (S - Z) % 2)) + 2304) + X.charCodeAt(K + 1) - 1995; - return T[K + T.length - 3136]; - } - typeof module != "undefined" && (module.exports = tzlookup); - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/fbdifflib.js - var require_fbdifflib = __commonJS((exports, module) => { - var floor = Math.floor; - var max = Math.max; - var min = Math.min; - var _calculateRatio = function(matches, length) { - if (length) { - return 2 * matches / length; - } else { - return 1; - } - }; - var _arrayCmp = function(a, b) { - var i, la, lb, _i, _ref, _ref1; - _ref = [a.length, b.length], la = _ref[0], lb = _ref[1]; - for (i = _i = 0, _ref1 = min(la, lb);0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) { - if (a[i] < b[i]) { - return -1; - } - if (a[i] > b[i]) { - return 1; - } - } - return la - lb; - }; - var _has = function(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); - }; - var SequenceMatcher = function() { - function SequenceMatcher2(isjunk, a, b, autojunk) { - this.isjunk = isjunk; - if (a == null) { - a = ""; - } - if (b == null) { - b = ""; - } - this.autojunk = autojunk != null ? autojunk : true; - this.a = this.b = null; - this.setSeqs(a, b); - } - SequenceMatcher2.prototype.setSeqs = function(a, b) { - this.setSeq1(a); - return this.setSeq2(b); - }; - SequenceMatcher2.prototype.setSeq1 = function(a) { - if (a === this.a) { - return; - } - this.a = a; - return this.matchingBlocks = this.opcodes = null; - }; - SequenceMatcher2.prototype.setSeq2 = function(b) { - if (b === this.b) { - return; - } - this.b = b; - this.matchingBlocks = this.opcodes = null; - this.fullbcount = null; - return this._chainB(); - }; - SequenceMatcher2.prototype._chainB = function() { - var b, b2j, elt, i, idxs, indices, isjunk, junk, n, ntest, popular, _i, _j, _len, _len1, _ref; - b = this.b; - this.b2j = b2j = {}; - for (i = _i = 0, _len = b.length;_i < _len; i = ++_i) { - elt = b[i]; - indices = _has(b2j, elt) ? b2j[elt] : b2j[elt] = []; - indices.push(i); - } - junk = {}; - isjunk = this.isjunk; - if (isjunk) { - _ref = Object.keys(b2j); - for (_j = 0, _len1 = _ref.length;_j < _len1; _j++) { - elt = _ref[_j]; - if (isjunk(elt)) { - junk[elt] = true; - delete b2j[elt]; - } - } - } - popular = {}; - n = b.length; - if (this.autojunk && n >= 200) { - ntest = floor(n / 100) + 1; - for (elt in b2j) { - idxs = b2j[elt]; - if (idxs.length > ntest) { - popular[elt] = true; - delete b2j[elt]; - } - } - } - this.isbjunk = function(b2) { - return _has(junk, b2); - }; - return this.isbpopular = function(b2) { - return _has(popular, b2); - }; - }; - SequenceMatcher2.prototype.findLongestMatch = function(alo, ahi, blo, bhi) { - var a, b, b2j, besti, bestj, bestsize, i, isbjunk, j, j2len, k, newj2len, _i, _j, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; - _ref = [this.a, this.b, this.b2j, this.isbjunk], a = _ref[0], b = _ref[1], b2j = _ref[2], isbjunk = _ref[3]; - _ref1 = [alo, blo, 0], besti = _ref1[0], bestj = _ref1[1], bestsize = _ref1[2]; - j2len = {}; - for (i = _i = alo;alo <= ahi ? _i < ahi : _i > ahi; i = alo <= ahi ? ++_i : --_i) { - newj2len = {}; - _ref2 = _has(b2j, a[i]) ? b2j[a[i]] : []; - for (_j = 0, _len = _ref2.length;_j < _len; _j++) { - j = _ref2[_j]; - if (j < blo) { - continue; - } - if (j >= bhi) { - break; - } - k = newj2len[j] = (j2len[j - 1] || 0) + 1; - if (k > bestsize) { - _ref3 = [i - k + 1, j - k + 1, k], besti = _ref3[0], bestj = _ref3[1], bestsize = _ref3[2]; - } - } - j2len = newj2len; - } - while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] === b[bestj - 1]) { - _ref4 = [besti - 1, bestj - 1, bestsize + 1], besti = _ref4[0], bestj = _ref4[1], bestsize = _ref4[2]; - } - while (besti + bestsize < ahi && bestj + bestsize < bhi && !isbjunk(b[bestj + bestsize]) && a[besti + bestsize] === b[bestj + bestsize]) { - bestsize++; - } - while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] === b[bestj - 1]) { - _ref5 = [besti - 1, bestj - 1, bestsize + 1], besti = _ref5[0], bestj = _ref5[1], bestsize = _ref5[2]; - } - while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) && a[besti + bestsize] === b[bestj + bestsize]) { - bestsize++; - } - return [besti, bestj, bestsize]; - }; - SequenceMatcher2.prototype.getMatchingBlocks = function() { - var ahi, alo, bhi, blo, i, i1, i2, j, j1, j2, k, k1, k2, la, lb, matchingBlocks, nonAdjacent, queue, x, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; - if (this.matchingBlocks) { - return this.matchingBlocks; - } - _ref = [this.a.length, this.b.length], la = _ref[0], lb = _ref[1]; - queue = [[0, la, 0, lb]]; - matchingBlocks = []; - while (queue.length) { - _ref1 = queue.pop(), alo = _ref1[0], ahi = _ref1[1], blo = _ref1[2], bhi = _ref1[3]; - _ref2 = x = this.findLongestMatch(alo, ahi, blo, bhi), i = _ref2[0], j = _ref2[1], k = _ref2[2]; - if (k) { - matchingBlocks.push(x); - if (alo < i && blo < j) { - queue.push([alo, i, blo, j]); - } - if (i + k < ahi && j + k < bhi) { - queue.push([i + k, ahi, j + k, bhi]); - } - } - } - matchingBlocks.sort(_arrayCmp); - i1 = j1 = k1 = 0; - nonAdjacent = []; - for (_i = 0, _len = matchingBlocks.length;_i < _len; _i++) { - _ref3 = matchingBlocks[_i], i2 = _ref3[0], j2 = _ref3[1], k2 = _ref3[2]; - if (i1 + k1 === i2 && j1 + k1 === j2) { - k1 += k2; - } else { - if (k1) { - nonAdjacent.push([i1, j1, k1]); - } - _ref4 = [i2, j2, k2], i1 = _ref4[0], j1 = _ref4[1], k1 = _ref4[2]; - } - } - if (k1) { - nonAdjacent.push([i1, j1, k1]); - } - nonAdjacent.push([la, lb, 0]); - return this.matchingBlocks = nonAdjacent; - }; - SequenceMatcher2.prototype.getOpcodes = function() { - var ai, answer, bj, i, j, size, tag, _i, _len, _ref, _ref1, _ref2; - if (this.opcodes) { - return this.opcodes; - } - i = j = 0; - this.opcodes = answer = []; - _ref = this.getMatchingBlocks(); - for (_i = 0, _len = _ref.length;_i < _len; _i++) { - _ref1 = _ref[_i], ai = _ref1[0], bj = _ref1[1], size = _ref1[2]; - tag = ""; - if (i < ai && j < bj) { - tag = "replace"; - } else if (i < ai) { - tag = "delete"; - } else if (j < bj) { - tag = "insert"; - } - if (tag) { - answer.push([tag, i, ai, j, bj]); - } - _ref2 = [ai + size, bj + size], i = _ref2[0], j = _ref2[1]; - if (size) { - answer.push(["equal", ai, i, bj, j]); - } - } - return answer; - }; - SequenceMatcher2.prototype.getGroupedOpcodes = function(n) { - var codes, group, groups, i1, i2, j1, j2, nn, tag, _i, _len, _ref, _ref1, _ref2, _ref3; - if (n == null) { - n = 3; - } - codes = this.getOpcodes(); - if (!codes.length) { - codes = [["equal", 0, 1, 0, 1]]; - } - if (codes[0][0] === "equal") { - _ref = codes[0], tag = _ref[0], i1 = _ref[1], i2 = _ref[2], j1 = _ref[3], j2 = _ref[4]; - codes[0] = [tag, max(i1, i2 - n), i2, max(j1, j2 - n), j2]; - } - if (codes[codes.length - 1][0] === "equal") { - _ref1 = codes[codes.length - 1], tag = _ref1[0], i1 = _ref1[1], i2 = _ref1[2], j1 = _ref1[3], j2 = _ref1[4]; - codes[codes.length - 1] = [tag, i1, min(i2, i1 + n), j1, min(j2, j1 + n)]; - } - nn = n + n; - groups = []; - group = []; - for (_i = 0, _len = codes.length;_i < _len; _i++) { - _ref2 = codes[_i], tag = _ref2[0], i1 = _ref2[1], i2 = _ref2[2], j1 = _ref2[3], j2 = _ref2[4]; - if (tag === "equal" && i2 - i1 > nn) { - group.push([tag, i1, min(i2, i1 + n), j1, min(j2, j1 + n)]); - groups.push(group); - group = []; - _ref3 = [max(i1, i2 - n), max(j1, j2 - n)], i1 = _ref3[0], j1 = _ref3[1]; - } - group.push([tag, i1, i2, j1, j2]); - } - if (group.length && !(group.length === 1 && group[0][0] === "equal")) { - groups.push(group); - } - return groups; - }; - SequenceMatcher2.prototype.ratio = function() { - var match, matches, _i, _len, _ref; - matches = 0; - _ref = this.getMatchingBlocks(); - for (_i = 0, _len = _ref.length;_i < _len; _i++) { - match = _ref[_i]; - matches += match[2]; - } - return _calculateRatio(matches, this.a.length + this.b.length); - }; - SequenceMatcher2.prototype.quickRatio = function() { - var avail, elt, fullbcount, matches, numb, _i, _j, _len, _len1, _ref, _ref1; - if (!this.fullbcount) { - this.fullbcount = fullbcount = {}; - _ref = this.b; - for (_i = 0, _len = _ref.length;_i < _len; _i++) { - elt = _ref[_i]; - fullbcount[elt] = (fullbcount[elt] || 0) + 1; - } - } - fullbcount = this.fullbcount; - avail = {}; - matches = 0; - _ref1 = this.a; - for (_j = 0, _len1 = _ref1.length;_j < _len1; _j++) { - elt = _ref1[_j]; - if (_has(avail, elt)) { - numb = avail[elt]; - } else { - numb = fullbcount[elt] || 0; - } - avail[elt] = numb - 1; - if (numb > 0) { - matches++; - } - } - return _calculateRatio(matches, this.a.length + this.b.length); - }; - SequenceMatcher2.prototype.realQuickRatio = function() { - var la, lb, _ref; - _ref = [this.a.length, this.b.length], la = _ref[0], lb = _ref[1]; - return _calculateRatio(min(la, lb), la + lb); - }; - return SequenceMatcher2; - }(); - module.exports = SequenceMatcher; - }); - - // node_modules/.bun/heap@0.2.7/node_modules/heap/lib/heap.js - var require_heap = __commonJS((exports, module) => { - (function() { - var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup; - floor = Math.floor, min = Math.min; - defaultCmp = function(x, y) { - if (x < y) { - return -1; - } - if (x > y) { - return 1; - } - return 0; - }; - insort = function(a, x, lo, hi, cmp) { - var mid; - if (lo == null) { - lo = 0; - } - if (cmp == null) { - cmp = defaultCmp; - } - if (lo < 0) { - throw new Error("lo must be non-negative"); - } - if (hi == null) { - hi = a.length; - } - while (lo < hi) { - mid = floor((lo + hi) / 2); - if (cmp(x, a[mid]) < 0) { - hi = mid; - } else { - lo = mid + 1; - } - } - return [].splice.apply(a, [lo, lo - lo].concat(x)), x; - }; - heappush = function(array, item, cmp) { - if (cmp == null) { - cmp = defaultCmp; - } - array.push(item); - return _siftdown(array, 0, array.length - 1, cmp); - }; - heappop = function(array, cmp) { - var lastelt, returnitem; - if (cmp == null) { - cmp = defaultCmp; - } - lastelt = array.pop(); - if (array.length) { - returnitem = array[0]; - array[0] = lastelt; - _siftup(array, 0, cmp); - } else { - returnitem = lastelt; - } - return returnitem; - }; - heapreplace = function(array, item, cmp) { - var returnitem; - if (cmp == null) { - cmp = defaultCmp; - } - returnitem = array[0]; - array[0] = item; - _siftup(array, 0, cmp); - return returnitem; - }; - heappushpop = function(array, item, cmp) { - var _ref; - if (cmp == null) { - cmp = defaultCmp; - } - if (array.length && cmp(array[0], item) < 0) { - _ref = [array[0], item], item = _ref[0], array[0] = _ref[1]; - _siftup(array, 0, cmp); - } - return item; - }; - heapify = function(array, cmp) { - var i, _i, _j, _len, _ref, _ref1, _results, _results1; - if (cmp == null) { - cmp = defaultCmp; - } - _ref1 = function() { - _results1 = []; - for (var _j2 = 0, _ref2 = floor(array.length / 2);0 <= _ref2 ? _j2 < _ref2 : _j2 > _ref2; 0 <= _ref2 ? _j2++ : _j2--) { - _results1.push(_j2); - } - return _results1; - }.apply(this).reverse(); - _results = []; - for (_i = 0, _len = _ref1.length;_i < _len; _i++) { - i = _ref1[_i]; - _results.push(_siftup(array, i, cmp)); - } - return _results; - }; - updateItem = function(array, item, cmp) { - var pos; - if (cmp == null) { - cmp = defaultCmp; - } - pos = array.indexOf(item); - if (pos === -1) { - return; - } - _siftdown(array, 0, pos, cmp); - return _siftup(array, pos, cmp); - }; - nlargest = function(array, n, cmp) { - var elem, result, _i, _len, _ref; - if (cmp == null) { - cmp = defaultCmp; - } - result = array.slice(0, n); - if (!result.length) { - return result; - } - heapify(result, cmp); - _ref = array.slice(n); - for (_i = 0, _len = _ref.length;_i < _len; _i++) { - elem = _ref[_i]; - heappushpop(result, elem, cmp); - } - return result.sort(cmp).reverse(); - }; - nsmallest = function(array, n, cmp) { - var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results; - if (cmp == null) { - cmp = defaultCmp; - } - if (n * 10 <= array.length) { - result = array.slice(0, n).sort(cmp); - if (!result.length) { - return result; - } - los = result[result.length - 1]; - _ref = array.slice(n); - for (_i = 0, _len = _ref.length;_i < _len; _i++) { - elem = _ref[_i]; - if (cmp(elem, los) < 0) { - insort(result, elem, 0, null, cmp); - result.pop(); - los = result[result.length - 1]; - } - } - return result; - } - heapify(array, cmp); - _results = []; - for (i = _j = 0, _ref1 = min(n, array.length);0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) { - _results.push(heappop(array, cmp)); - } - return _results; - }; - _siftdown = function(array, startpos, pos, cmp) { - var newitem, parent, parentpos; - if (cmp == null) { - cmp = defaultCmp; - } - newitem = array[pos]; - while (pos > startpos) { - parentpos = pos - 1 >> 1; - parent = array[parentpos]; - if (cmp(newitem, parent) < 0) { - array[pos] = parent; - pos = parentpos; - continue; - } - break; - } - return array[pos] = newitem; - }; - _siftup = function(array, pos, cmp) { - var childpos, endpos, newitem, rightpos, startpos; - if (cmp == null) { - cmp = defaultCmp; - } - endpos = array.length; - startpos = pos; - newitem = array[pos]; - childpos = 2 * pos + 1; - while (childpos < endpos) { - rightpos = childpos + 1; - if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) { - childpos = rightpos; - } - array[pos] = array[childpos]; - pos = childpos; - childpos = 2 * pos + 1; - } - array[pos] = newitem; - return _siftdown(array, startpos, pos, cmp); - }; - Heap = function() { - Heap2.push = heappush; - Heap2.pop = heappop; - Heap2.replace = heapreplace; - Heap2.pushpop = heappushpop; - Heap2.heapify = heapify; - Heap2.updateItem = updateItem; - Heap2.nlargest = nlargest; - Heap2.nsmallest = nsmallest; - function Heap2(cmp) { - this.cmp = cmp != null ? cmp : defaultCmp; - this.nodes = []; - } - Heap2.prototype.push = function(x) { - return heappush(this.nodes, x, this.cmp); - }; - Heap2.prototype.pop = function() { - return heappop(this.nodes, this.cmp); - }; - Heap2.prototype.peek = function() { - return this.nodes[0]; - }; - Heap2.prototype.contains = function(x) { - return this.nodes.indexOf(x) !== -1; - }; - Heap2.prototype.replace = function(x) { - return heapreplace(this.nodes, x, this.cmp); - }; - Heap2.prototype.pushpop = function(x) { - return heappushpop(this.nodes, x, this.cmp); - }; - Heap2.prototype.heapify = function() { - return heapify(this.nodes, this.cmp); - }; - Heap2.prototype.updateItem = function(x) { - return updateItem(this.nodes, x, this.cmp); - }; - Heap2.prototype.clear = function() { - return this.nodes = []; - }; - Heap2.prototype.empty = function() { - return this.nodes.length === 0; - }; - Heap2.prototype.size = function() { - return this.nodes.length; - }; - Heap2.prototype.clone = function() { - var heap; - heap = new Heap2; - heap.nodes = this.nodes.slice(0); - return heap; - }; - Heap2.prototype.toArray = function() { - return this.nodes.slice(0); - }; - Heap2.prototype.insert = Heap2.prototype.push; - Heap2.prototype.top = Heap2.prototype.peek; - Heap2.prototype.front = Heap2.prototype.peek; - Heap2.prototype.has = Heap2.prototype.contains; - Heap2.prototype.copy = Heap2.prototype.clone; - return Heap2; - }(); - (function(root, factory) { - if (typeof define === "function" && define.amd) { - return define([], factory); - } else if (typeof exports === "object") { - return module.exports = factory(); - } else { - return root.Heap = factory(); - } - })(this, function() { - return Heap; - }); - }).call(exports); - }); - - // node_modules/.bun/heap@0.2.7/node_modules/heap/index.js - var require_heap2 = __commonJS((exports, module) => { - module.exports = require_heap(); - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayMap.js - var require__arrayMap = __commonJS((exports, module) => { - function arrayMap(array, iteratee) { - var index = -1, length = array == null ? 0 : array.length, result = Array(length); - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; - } - module.exports = arrayMap; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isArray.js - var require_isArray = __commonJS((exports, module) => { - var isArray = Array.isArray; - module.exports = isArray; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_freeGlobal.js - var require__freeGlobal = __commonJS((exports, module) => { - var freeGlobal = typeof global == "object" && global && global.Object === Object && global; - module.exports = freeGlobal; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_root.js - var require__root = __commonJS((exports, module) => { - var freeGlobal = require__freeGlobal(); - var freeSelf = typeof self == "object" && self && self.Object === Object && self; - var root = freeGlobal || freeSelf || Function("return this")(); - module.exports = root; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Symbol.js - var require__Symbol = __commonJS((exports, module) => { - var root = require__root(); - var Symbol2 = root.Symbol; - module.exports = Symbol2; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getRawTag.js - var require__getRawTag = __commonJS((exports, module) => { - var Symbol2 = require__Symbol(); - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - var nativeObjectToString = objectProto.toString; - var symToStringTag = Symbol2 ? Symbol2.toStringTag : undefined; - function getRawTag(value) { - var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; - try { - value[symToStringTag] = undefined; - var unmasked = true; - } catch (e) {} - var result = nativeObjectToString.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag] = tag; - } else { - delete value[symToStringTag]; - } - } - return result; - } - module.exports = getRawTag; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_objectToString.js - var require__objectToString = __commonJS((exports, module) => { - var objectProto = Object.prototype; - var nativeObjectToString = objectProto.toString; - function objectToString(value) { - return nativeObjectToString.call(value); - } - module.exports = objectToString; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseGetTag.js - var require__baseGetTag = __commonJS((exports, module) => { - var Symbol2 = require__Symbol(); - var getRawTag = require__getRawTag(); - var objectToString = require__objectToString(); - var nullTag = "[object Null]"; - var undefinedTag = "[object Undefined]"; - var symToStringTag = Symbol2 ? Symbol2.toStringTag : undefined; - function baseGetTag(value) { - if (value == null) { - return value === undefined ? undefinedTag : nullTag; - } - return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value); - } - module.exports = baseGetTag; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isObjectLike.js - var require_isObjectLike = __commonJS((exports, module) => { - function isObjectLike(value) { - return value != null && typeof value == "object"; - } - module.exports = isObjectLike; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isSymbol.js - var require_isSymbol = __commonJS((exports, module) => { - var baseGetTag = require__baseGetTag(); - var isObjectLike = require_isObjectLike(); - var symbolTag = "[object Symbol]"; - function isSymbol(value) { - return typeof value == "symbol" || isObjectLike(value) && baseGetTag(value) == symbolTag; - } - module.exports = isSymbol; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isKey.js - var require__isKey = __commonJS((exports, module) => { - var isArray = require_isArray(); - var isSymbol = require_isSymbol(); - var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/; - var reIsPlainProp = /^\w*$/; - function isKey(value, object) { - if (isArray(value)) { - return false; - } - var type = typeof value; - if (type == "number" || type == "symbol" || type == "boolean" || value == null || isSymbol(value)) { - return true; - } - return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object); - } - module.exports = isKey; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isObject.js - var require_isObject = __commonJS((exports, module) => { - function isObject(value) { - var type = typeof value; - return value != null && (type == "object" || type == "function"); - } - module.exports = isObject; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isFunction.js - var require_isFunction = __commonJS((exports, module) => { - var baseGetTag = require__baseGetTag(); - var isObject = require_isObject(); - var asyncTag = "[object AsyncFunction]"; - var funcTag = "[object Function]"; - var genTag = "[object GeneratorFunction]"; - var proxyTag = "[object Proxy]"; - function isFunction(value) { - if (!isObject(value)) { - return false; - } - var tag = baseGetTag(value); - return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; - } - module.exports = isFunction; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_coreJsData.js - var require__coreJsData = __commonJS((exports, module) => { - var root = require__root(); - var coreJsData = root["__core-js_shared__"]; - module.exports = coreJsData; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isMasked.js - var require__isMasked = __commonJS((exports, module) => { - var coreJsData = require__coreJsData(); - var maskSrcKey = function() { - var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ""); - return uid ? "Symbol(src)_1." + uid : ""; - }(); - function isMasked(func) { - return !!maskSrcKey && maskSrcKey in func; - } - module.exports = isMasked; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_toSource.js - var require__toSource = __commonJS((exports, module) => { - var funcProto = Function.prototype; - var funcToString = funcProto.toString; - function toSource(func) { - if (func != null) { - try { - return funcToString.call(func); - } catch (e) {} - try { - return func + ""; - } catch (e) {} - } - return ""; - } - module.exports = toSource; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsNative.js - var require__baseIsNative = __commonJS((exports, module) => { - var isFunction = require_isFunction(); - var isMasked = require__isMasked(); - var isObject = require_isObject(); - var toSource = require__toSource(); - var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; - var reIsHostCtor = /^\[object .+?Constructor\]$/; - var funcProto = Function.prototype; - var objectProto = Object.prototype; - var funcToString = funcProto.toString; - var hasOwnProperty = objectProto.hasOwnProperty; - var reIsNative = RegExp("^" + funcToString.call(hasOwnProperty).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"); - function baseIsNative(value) { - if (!isObject(value) || isMasked(value)) { - return false; - } - var pattern = isFunction(value) ? reIsNative : reIsHostCtor; - return pattern.test(toSource(value)); - } - module.exports = baseIsNative; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getValue.js - var require__getValue = __commonJS((exports, module) => { - function getValue(object, key) { - return object == null ? undefined : object[key]; - } - module.exports = getValue; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getNative.js - var require__getNative = __commonJS((exports, module) => { - var baseIsNative = require__baseIsNative(); - var getValue = require__getValue(); - function getNative(object, key) { - var value = getValue(object, key); - return baseIsNative(value) ? value : undefined; - } - module.exports = getNative; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_nativeCreate.js - var require__nativeCreate = __commonJS((exports, module) => { - var getNative = require__getNative(); - var nativeCreate = getNative(Object, "create"); - module.exports = nativeCreate; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashClear.js - var require__hashClear = __commonJS((exports, module) => { - var nativeCreate = require__nativeCreate(); - function hashClear() { - this.__data__ = nativeCreate ? nativeCreate(null) : {}; - this.size = 0; - } - module.exports = hashClear; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashDelete.js - var require__hashDelete = __commonJS((exports, module) => { - function hashDelete(key) { - var result = this.has(key) && delete this.__data__[key]; - this.size -= result ? 1 : 0; - return result; - } - module.exports = hashDelete; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashGet.js - var require__hashGet = __commonJS((exports, module) => { - var nativeCreate = require__nativeCreate(); - var HASH_UNDEFINED = "__lodash_hash_undefined__"; - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - function hashGet(key) { - var data = this.__data__; - if (nativeCreate) { - var result = data[key]; - return result === HASH_UNDEFINED ? undefined : result; - } - return hasOwnProperty.call(data, key) ? data[key] : undefined; - } - module.exports = hashGet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashHas.js - var require__hashHas = __commonJS((exports, module) => { - var nativeCreate = require__nativeCreate(); - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - function hashHas(key) { - var data = this.__data__; - return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); - } - module.exports = hashHas; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hashSet.js - var require__hashSet = __commonJS((exports, module) => { - var nativeCreate = require__nativeCreate(); - var HASH_UNDEFINED = "__lodash_hash_undefined__"; - function hashSet(key, value) { - var data = this.__data__; - this.size += this.has(key) ? 0 : 1; - data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED : value; - return this; - } - module.exports = hashSet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Hash.js - var require__Hash = __commonJS((exports, module) => { - var hashClear = require__hashClear(); - var hashDelete = require__hashDelete(); - var hashGet = require__hashGet(); - var hashHas = require__hashHas(); - var hashSet = require__hashSet(); - function Hash(entries) { - var index = -1, length = entries == null ? 0 : entries.length; - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - Hash.prototype.clear = hashClear; - Hash.prototype["delete"] = hashDelete; - Hash.prototype.get = hashGet; - Hash.prototype.has = hashHas; - Hash.prototype.set = hashSet; - module.exports = Hash; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheClear.js - var require__listCacheClear = __commonJS((exports, module) => { - function listCacheClear() { - this.__data__ = []; - this.size = 0; - } - module.exports = listCacheClear; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/eq.js - var require_eq = __commonJS((exports, module) => { - function eq(value, other) { - return value === other || value !== value && other !== other; - } - module.exports = eq; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_assocIndexOf.js - var require__assocIndexOf = __commonJS((exports, module) => { - var eq = require_eq(); - function assocIndexOf(array, key) { - var length = array.length; - while (length--) { - if (eq(array[length][0], key)) { - return length; - } - } - return -1; - } - module.exports = assocIndexOf; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheDelete.js - var require__listCacheDelete = __commonJS((exports, module) => { - var assocIndexOf = require__assocIndexOf(); - var arrayProto = Array.prototype; - var splice = arrayProto.splice; - function listCacheDelete(key) { - var data = this.__data__, index = assocIndexOf(data, key); - if (index < 0) { - return false; - } - var lastIndex = data.length - 1; - if (index == lastIndex) { - data.pop(); - } else { - splice.call(data, index, 1); - } - --this.size; - return true; - } - module.exports = listCacheDelete; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheGet.js - var require__listCacheGet = __commonJS((exports, module) => { - var assocIndexOf = require__assocIndexOf(); - function listCacheGet(key) { - var data = this.__data__, index = assocIndexOf(data, key); - return index < 0 ? undefined : data[index][1]; - } - module.exports = listCacheGet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheHas.js - var require__listCacheHas = __commonJS((exports, module) => { - var assocIndexOf = require__assocIndexOf(); - function listCacheHas(key) { - return assocIndexOf(this.__data__, key) > -1; - } - module.exports = listCacheHas; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_listCacheSet.js - var require__listCacheSet = __commonJS((exports, module) => { - var assocIndexOf = require__assocIndexOf(); - function listCacheSet(key, value) { - var data = this.__data__, index = assocIndexOf(data, key); - if (index < 0) { - ++this.size; - data.push([key, value]); - } else { - data[index][1] = value; - } - return this; - } - module.exports = listCacheSet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_ListCache.js - var require__ListCache = __commonJS((exports, module) => { - var listCacheClear = require__listCacheClear(); - var listCacheDelete = require__listCacheDelete(); - var listCacheGet = require__listCacheGet(); - var listCacheHas = require__listCacheHas(); - var listCacheSet = require__listCacheSet(); - function ListCache(entries) { - var index = -1, length = entries == null ? 0 : entries.length; - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - ListCache.prototype.clear = listCacheClear; - ListCache.prototype["delete"] = listCacheDelete; - ListCache.prototype.get = listCacheGet; - ListCache.prototype.has = listCacheHas; - ListCache.prototype.set = listCacheSet; - module.exports = ListCache; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Map.js - var require__Map = __commonJS((exports, module) => { - var getNative = require__getNative(); - var root = require__root(); - var Map = getNative(root, "Map"); - module.exports = Map; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheClear.js - var require__mapCacheClear = __commonJS((exports, module) => { - var Hash = require__Hash(); - var ListCache = require__ListCache(); - var Map = require__Map(); - function mapCacheClear() { - this.size = 0; - this.__data__ = { - hash: new Hash, - map: new (Map || ListCache), - string: new Hash - }; - } - module.exports = mapCacheClear; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isKeyable.js - var require__isKeyable = __commonJS((exports, module) => { - function isKeyable(value) { - var type = typeof value; - return type == "string" || type == "number" || type == "symbol" || type == "boolean" ? value !== "__proto__" : value === null; - } - module.exports = isKeyable; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getMapData.js - var require__getMapData = __commonJS((exports, module) => { - var isKeyable = require__isKeyable(); - function getMapData(map, key) { - var data = map.__data__; - return isKeyable(key) ? data[typeof key == "string" ? "string" : "hash"] : data.map; - } - module.exports = getMapData; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheDelete.js - var require__mapCacheDelete = __commonJS((exports, module) => { - var getMapData = require__getMapData(); - function mapCacheDelete(key) { - var result = getMapData(this, key)["delete"](key); - this.size -= result ? 1 : 0; - return result; - } - module.exports = mapCacheDelete; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheGet.js - var require__mapCacheGet = __commonJS((exports, module) => { - var getMapData = require__getMapData(); - function mapCacheGet(key) { - return getMapData(this, key).get(key); - } - module.exports = mapCacheGet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheHas.js - var require__mapCacheHas = __commonJS((exports, module) => { - var getMapData = require__getMapData(); - function mapCacheHas(key) { - return getMapData(this, key).has(key); - } - module.exports = mapCacheHas; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapCacheSet.js - var require__mapCacheSet = __commonJS((exports, module) => { - var getMapData = require__getMapData(); - function mapCacheSet(key, value) { - var data = getMapData(this, key), size = data.size; - data.set(key, value); - this.size += data.size == size ? 0 : 1; - return this; - } - module.exports = mapCacheSet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_MapCache.js - var require__MapCache = __commonJS((exports, module) => { - var mapCacheClear = require__mapCacheClear(); - var mapCacheDelete = require__mapCacheDelete(); - var mapCacheGet = require__mapCacheGet(); - var mapCacheHas = require__mapCacheHas(); - var mapCacheSet = require__mapCacheSet(); - function MapCache(entries) { - var index = -1, length = entries == null ? 0 : entries.length; - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - MapCache.prototype.clear = mapCacheClear; - MapCache.prototype["delete"] = mapCacheDelete; - MapCache.prototype.get = mapCacheGet; - MapCache.prototype.has = mapCacheHas; - MapCache.prototype.set = mapCacheSet; - module.exports = MapCache; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/memoize.js - var require_memoize = __commonJS((exports, module) => { - var MapCache = require__MapCache(); - var FUNC_ERROR_TEXT = "Expected a function"; - function memoize(func, resolver) { - if (typeof func != "function" || resolver != null && typeof resolver != "function") { - throw new TypeError(FUNC_ERROR_TEXT); - } - var memoized = function() { - var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache; - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result) || cache; - return result; - }; - memoized.cache = new (memoize.Cache || MapCache); - return memoized; - } - memoize.Cache = MapCache; - module.exports = memoize; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_memoizeCapped.js - var require__memoizeCapped = __commonJS((exports, module) => { - var memoize = require_memoize(); - var MAX_MEMOIZE_SIZE = 500; - function memoizeCapped(func) { - var result = memoize(func, function(key) { - if (cache.size === MAX_MEMOIZE_SIZE) { - cache.clear(); - } - return key; - }); - var cache = result.cache; - return result; - } - module.exports = memoizeCapped; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stringToPath.js - var require__stringToPath = __commonJS((exports, module) => { - var memoizeCapped = require__memoizeCapped(); - var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; - var reEscapeChar = /\\(\\)?/g; - var stringToPath = memoizeCapped(function(string) { - var result = []; - if (string.charCodeAt(0) === 46) { - result.push(""); - } - string.replace(rePropName, function(match, number, quote, subString) { - result.push(quote ? subString.replace(reEscapeChar, "$1") : number || match); - }); - return result; - }); - module.exports = stringToPath; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseToString.js - var require__baseToString = __commonJS((exports, module) => { - var Symbol2 = require__Symbol(); - var arrayMap = require__arrayMap(); - var isArray = require_isArray(); - var isSymbol = require_isSymbol(); - var INFINITY = 1 / 0; - var symbolProto = Symbol2 ? Symbol2.prototype : undefined; - var symbolToString = symbolProto ? symbolProto.toString : undefined; - function baseToString(value) { - if (typeof value == "string") { - return value; - } - if (isArray(value)) { - return arrayMap(value, baseToString) + ""; - } - if (isSymbol(value)) { - return symbolToString ? symbolToString.call(value) : ""; - } - var result = value + ""; - return result == "0" && 1 / value == -INFINITY ? "-0" : result; - } - module.exports = baseToString; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/toString.js - var require_toString = __commonJS((exports, module) => { - var baseToString = require__baseToString(); - function toString(value) { - return value == null ? "" : baseToString(value); - } - module.exports = toString; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_castPath.js - var require__castPath = __commonJS((exports, module) => { - var isArray = require_isArray(); - var isKey = require__isKey(); - var stringToPath = require__stringToPath(); - var toString = require_toString(); - function castPath(value, object) { - if (isArray(value)) { - return value; - } - return isKey(value, object) ? [value] : stringToPath(toString(value)); - } - module.exports = castPath; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_toKey.js - var require__toKey = __commonJS((exports, module) => { - var isSymbol = require_isSymbol(); - var INFINITY = 1 / 0; - function toKey(value) { - if (typeof value == "string" || isSymbol(value)) { - return value; - } - var result = value + ""; - return result == "0" && 1 / value == -INFINITY ? "-0" : result; - } - module.exports = toKey; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseGet.js - var require__baseGet = __commonJS((exports, module) => { - var castPath = require__castPath(); - var toKey = require__toKey(); - function baseGet(object, path) { - path = castPath(path, object); - var index = 0, length = path.length; - while (object != null && index < length) { - object = object[toKey(path[index++])]; - } - return index && index == length ? object : undefined; - } - module.exports = baseGet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackClear.js - var require__stackClear = __commonJS((exports, module) => { - var ListCache = require__ListCache(); - function stackClear() { - this.__data__ = new ListCache; - this.size = 0; - } - module.exports = stackClear; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackDelete.js - var require__stackDelete = __commonJS((exports, module) => { - function stackDelete(key) { - var data = this.__data__, result = data["delete"](key); - this.size = data.size; - return result; - } - module.exports = stackDelete; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackGet.js - var require__stackGet = __commonJS((exports, module) => { - function stackGet(key) { - return this.__data__.get(key); - } - module.exports = stackGet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackHas.js - var require__stackHas = __commonJS((exports, module) => { - function stackHas(key) { - return this.__data__.has(key); - } - module.exports = stackHas; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_stackSet.js - var require__stackSet = __commonJS((exports, module) => { - var ListCache = require__ListCache(); - var Map = require__Map(); - var MapCache = require__MapCache(); - var LARGE_ARRAY_SIZE = 200; - function stackSet(key, value) { - var data = this.__data__; - if (data instanceof ListCache) { - var pairs = data.__data__; - if (!Map || pairs.length < LARGE_ARRAY_SIZE - 1) { - pairs.push([key, value]); - this.size = ++data.size; - return this; - } - data = this.__data__ = new MapCache(pairs); - } - data.set(key, value); - this.size = data.size; - return this; - } - module.exports = stackSet; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Stack.js - var require__Stack = __commonJS((exports, module) => { - var ListCache = require__ListCache(); - var stackClear = require__stackClear(); - var stackDelete = require__stackDelete(); - var stackGet = require__stackGet(); - var stackHas = require__stackHas(); - var stackSet = require__stackSet(); - function Stack(entries) { - var data = this.__data__ = new ListCache(entries); - this.size = data.size; - } - Stack.prototype.clear = stackClear; - Stack.prototype["delete"] = stackDelete; - Stack.prototype.get = stackGet; - Stack.prototype.has = stackHas; - Stack.prototype.set = stackSet; - module.exports = Stack; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_setCacheAdd.js - var require__setCacheAdd = __commonJS((exports, module) => { - var HASH_UNDEFINED = "__lodash_hash_undefined__"; - function setCacheAdd(value) { - this.__data__.set(value, HASH_UNDEFINED); - return this; - } - module.exports = setCacheAdd; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_setCacheHas.js - var require__setCacheHas = __commonJS((exports, module) => { - function setCacheHas(value) { - return this.__data__.has(value); - } - module.exports = setCacheHas; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_SetCache.js - var require__SetCache = __commonJS((exports, module) => { - var MapCache = require__MapCache(); - var setCacheAdd = require__setCacheAdd(); - var setCacheHas = require__setCacheHas(); - function SetCache(values) { - var index = -1, length = values == null ? 0 : values.length; - this.__data__ = new MapCache; - while (++index < length) { - this.add(values[index]); - } - } - SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; - SetCache.prototype.has = setCacheHas; - module.exports = SetCache; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arraySome.js - var require__arraySome = __commonJS((exports, module) => { - function arraySome(array, predicate) { - var index = -1, length = array == null ? 0 : array.length; - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; - } - } - return false; - } - module.exports = arraySome; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_cacheHas.js - var require__cacheHas = __commonJS((exports, module) => { - function cacheHas(cache, key) { - return cache.has(key); - } - module.exports = cacheHas; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_equalArrays.js - var require__equalArrays = __commonJS((exports, module) => { - var SetCache = require__SetCache(); - var arraySome = require__arraySome(); - var cacheHas = require__cacheHas(); - var COMPARE_PARTIAL_FLAG = 1; - var COMPARE_UNORDERED_FLAG = 2; - function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length; - if (arrLength != othLength && !(isPartial && othLength > arrLength)) { - return false; - } - var arrStacked = stack.get(array); - var othStacked = stack.get(other); - if (arrStacked && othStacked) { - return arrStacked == other && othStacked == array; - } - var index = -1, result = true, seen = bitmask & COMPARE_UNORDERED_FLAG ? new SetCache : undefined; - stack.set(array, other); - stack.set(other, array); - while (++index < arrLength) { - var arrValue = array[index], othValue = other[index]; - if (customizer) { - var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack); - } - if (compared !== undefined) { - if (compared) { - continue; - } - result = false; - break; - } - if (seen) { - if (!arraySome(other, function(othValue2, othIndex) { - if (!cacheHas(seen, othIndex) && (arrValue === othValue2 || equalFunc(arrValue, othValue2, bitmask, customizer, stack))) { - return seen.push(othIndex); - } - })) { - result = false; - break; - } - } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { - result = false; - break; - } - } - stack["delete"](array); - stack["delete"](other); - return result; - } - module.exports = equalArrays; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Uint8Array.js - var require__Uint8Array = __commonJS((exports, module) => { - var root = require__root(); - var Uint8Array2 = root.Uint8Array; - module.exports = Uint8Array2; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_mapToArray.js - var require__mapToArray = __commonJS((exports, module) => { - function mapToArray(map) { - var index = -1, result = Array(map.size); - map.forEach(function(value, key) { - result[++index] = [key, value]; - }); - return result; - } - module.exports = mapToArray; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_setToArray.js - var require__setToArray = __commonJS((exports, module) => { - function setToArray(set) { - var index = -1, result = Array(set.size); - set.forEach(function(value) { - result[++index] = value; - }); - return result; - } - module.exports = setToArray; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_equalByTag.js - var require__equalByTag = __commonJS((exports, module) => { - var Symbol2 = require__Symbol(); - var Uint8Array2 = require__Uint8Array(); - var eq = require_eq(); - var equalArrays = require__equalArrays(); - var mapToArray = require__mapToArray(); - var setToArray = require__setToArray(); - var COMPARE_PARTIAL_FLAG = 1; - var COMPARE_UNORDERED_FLAG = 2; - var boolTag = "[object Boolean]"; - var dateTag = "[object Date]"; - var errorTag = "[object Error]"; - var mapTag = "[object Map]"; - var numberTag = "[object Number]"; - var regexpTag = "[object RegExp]"; - var setTag = "[object Set]"; - var stringTag = "[object String]"; - var symbolTag = "[object Symbol]"; - var arrayBufferTag = "[object ArrayBuffer]"; - var dataViewTag = "[object DataView]"; - var symbolProto = Symbol2 ? Symbol2.prototype : undefined; - var symbolValueOf = symbolProto ? symbolProto.valueOf : undefined; - function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { - switch (tag) { - case dataViewTag: - if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) { - return false; - } - object = object.buffer; - other = other.buffer; - case arrayBufferTag: - if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array2(object), new Uint8Array2(other))) { - return false; - } - return true; - case boolTag: - case dateTag: - case numberTag: - return eq(+object, +other); - case errorTag: - return object.name == other.name && object.message == other.message; - case regexpTag: - case stringTag: - return object == other + ""; - case mapTag: - var convert = mapToArray; - case setTag: - var isPartial = bitmask & COMPARE_PARTIAL_FLAG; - convert || (convert = setToArray); - if (object.size != other.size && !isPartial) { - return false; - } - var stacked = stack.get(object); - if (stacked) { - return stacked == other; - } - bitmask |= COMPARE_UNORDERED_FLAG; - stack.set(object, other); - var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); - stack["delete"](object); - return result; - case symbolTag: - if (symbolValueOf) { - return symbolValueOf.call(object) == symbolValueOf.call(other); - } - } - return false; - } - module.exports = equalByTag; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayPush.js - var require__arrayPush = __commonJS((exports, module) => { - function arrayPush(array, values) { - var index = -1, length = values.length, offset = array.length; - while (++index < length) { - array[offset + index] = values[index]; - } - return array; - } - module.exports = arrayPush; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseGetAllKeys.js - var require__baseGetAllKeys = __commonJS((exports, module) => { - var arrayPush = require__arrayPush(); - var isArray = require_isArray(); - function baseGetAllKeys(object, keysFunc, symbolsFunc) { - var result = keysFunc(object); - return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); - } - module.exports = baseGetAllKeys; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayFilter.js - var require__arrayFilter = __commonJS((exports, module) => { - function arrayFilter(array, predicate) { - var index = -1, length = array == null ? 0 : array.length, resIndex = 0, result = []; - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result[resIndex++] = value; - } - } - return result; - } - module.exports = arrayFilter; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/stubArray.js - var require_stubArray = __commonJS((exports, module) => { - function stubArray() { - return []; - } - module.exports = stubArray; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getSymbols.js - var require__getSymbols = __commonJS((exports, module) => { - var arrayFilter = require__arrayFilter(); - var stubArray = require_stubArray(); - var objectProto = Object.prototype; - var propertyIsEnumerable = objectProto.propertyIsEnumerable; - var nativeGetSymbols = Object.getOwnPropertySymbols; - var getSymbols = !nativeGetSymbols ? stubArray : function(object) { - if (object == null) { - return []; - } - object = Object(object); - return arrayFilter(nativeGetSymbols(object), function(symbol) { - return propertyIsEnumerable.call(object, symbol); - }); - }; - module.exports = getSymbols; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseTimes.js - var require__baseTimes = __commonJS((exports, module) => { - function baseTimes(n, iteratee) { - var index = -1, result = Array(n); - while (++index < n) { - result[index] = iteratee(index); - } - return result; - } - module.exports = baseTimes; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsArguments.js - var require__baseIsArguments = __commonJS((exports, module) => { - var baseGetTag = require__baseGetTag(); - var isObjectLike = require_isObjectLike(); - var argsTag = "[object Arguments]"; - function baseIsArguments(value) { - return isObjectLike(value) && baseGetTag(value) == argsTag; - } - module.exports = baseIsArguments; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isArguments.js - var require_isArguments = __commonJS((exports, module) => { - var baseIsArguments = require__baseIsArguments(); - var isObjectLike = require_isObjectLike(); - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - var propertyIsEnumerable = objectProto.propertyIsEnumerable; - var isArguments = baseIsArguments(function() { - return arguments; - }()) ? baseIsArguments : function(value) { - return isObjectLike(value) && hasOwnProperty.call(value, "callee") && !propertyIsEnumerable.call(value, "callee"); - }; - module.exports = isArguments; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/stubFalse.js - var require_stubFalse = __commonJS((exports, module) => { - function stubFalse() { - return false; - } - module.exports = stubFalse; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isBuffer.js - var require_isBuffer = __commonJS((exports, module) => { - var root = require__root(); - var stubFalse = require_stubFalse(); - var freeExports = typeof exports == "object" && exports && !exports.nodeType && exports; - var freeModule = freeExports && typeof module == "object" && module && !module.nodeType && module; - var moduleExports = freeModule && freeModule.exports === freeExports; - var Buffer = moduleExports ? root.Buffer : undefined; - var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined; - var isBuffer = nativeIsBuffer || stubFalse; - module.exports = isBuffer; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isIndex.js - var require__isIndex = __commonJS((exports, module) => { - var MAX_SAFE_INTEGER = 9007199254740991; - var reIsUint = /^(?:0|[1-9]\d*)$/; - function isIndex(value, length) { - var type = typeof value; - length = length == null ? MAX_SAFE_INTEGER : length; - return !!length && (type == "number" || type != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); - } - module.exports = isIndex; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isLength.js - var require_isLength = __commonJS((exports, module) => { - var MAX_SAFE_INTEGER = 9007199254740991; - function isLength(value) { - return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - module.exports = isLength; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsTypedArray.js - var require__baseIsTypedArray = __commonJS((exports, module) => { - var baseGetTag = require__baseGetTag(); - var isLength = require_isLength(); - var isObjectLike = require_isObjectLike(); - var argsTag = "[object Arguments]"; - var arrayTag = "[object Array]"; - var boolTag = "[object Boolean]"; - var dateTag = "[object Date]"; - var errorTag = "[object Error]"; - var funcTag = "[object Function]"; - var mapTag = "[object Map]"; - var numberTag = "[object Number]"; - var objectTag = "[object Object]"; - var regexpTag = "[object RegExp]"; - var setTag = "[object Set]"; - var stringTag = "[object String]"; - var weakMapTag = "[object WeakMap]"; - var arrayBufferTag = "[object ArrayBuffer]"; - var dataViewTag = "[object DataView]"; - var float32Tag = "[object Float32Array]"; - var float64Tag = "[object Float64Array]"; - var int8Tag = "[object Int8Array]"; - var int16Tag = "[object Int16Array]"; - var int32Tag = "[object Int32Array]"; - var uint8Tag = "[object Uint8Array]"; - var uint8ClampedTag = "[object Uint8ClampedArray]"; - var uint16Tag = "[object Uint16Array]"; - var uint32Tag = "[object Uint32Array]"; - var typedArrayTags = {}; - typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; - typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; - function baseIsTypedArray(value) { - return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; - } - module.exports = baseIsTypedArray; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseUnary.js - var require__baseUnary = __commonJS((exports, module) => { - function baseUnary(func) { - return function(value) { - return func(value); - }; - } - module.exports = baseUnary; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_nodeUtil.js - var require__nodeUtil = __commonJS((exports, module) => { - var freeGlobal = require__freeGlobal(); - var freeExports = typeof exports == "object" && exports && !exports.nodeType && exports; - var freeModule = freeExports && typeof module == "object" && module && !module.nodeType && module; - var moduleExports = freeModule && freeModule.exports === freeExports; - var freeProcess = moduleExports && freeGlobal.process; - var nodeUtil = function() { - try { - var types = freeModule && freeModule.require && freeModule.require("util").types; - if (types) { - return types; - } - return freeProcess && freeProcess.binding && freeProcess.binding("util"); - } catch (e) {} - }(); - module.exports = nodeUtil; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isTypedArray.js - var require_isTypedArray = __commonJS((exports, module) => { - var baseIsTypedArray = require__baseIsTypedArray(); - var baseUnary = require__baseUnary(); - var nodeUtil = require__nodeUtil(); - var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; - var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; - module.exports = isTypedArray; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_arrayLikeKeys.js - var require__arrayLikeKeys = __commonJS((exports, module) => { - var baseTimes = require__baseTimes(); - var isArguments = require_isArguments(); - var isArray = require_isArray(); - var isBuffer = require_isBuffer(); - var isIndex = require__isIndex(); - var isTypedArray = require_isTypedArray(); - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - function arrayLikeKeys(value, inherited) { - var isArr = isArray(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length; - for (var key in value) { - if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == "length" || isBuff && (key == "offset" || key == "parent") || isType && (key == "buffer" || key == "byteLength" || key == "byteOffset") || isIndex(key, length)))) { - result.push(key); - } - } - return result; - } - module.exports = arrayLikeKeys; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isPrototype.js - var require__isPrototype = __commonJS((exports, module) => { - var objectProto = Object.prototype; - function isPrototype(value) { - var Ctor = value && value.constructor, proto = typeof Ctor == "function" && Ctor.prototype || objectProto; - return value === proto; - } - module.exports = isPrototype; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_overArg.js - var require__overArg = __commonJS((exports, module) => { - function overArg(func, transform) { - return function(arg) { - return func(transform(arg)); - }; - } - module.exports = overArg; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_nativeKeys.js - var require__nativeKeys = __commonJS((exports, module) => { - var overArg = require__overArg(); - var nativeKeys = overArg(Object.keys, Object); - module.exports = nativeKeys; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseKeys.js - var require__baseKeys = __commonJS((exports, module) => { - var isPrototype = require__isPrototype(); - var nativeKeys = require__nativeKeys(); - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - function baseKeys(object) { - if (!isPrototype(object)) { - return nativeKeys(object); - } - var result = []; - for (var key in Object(object)) { - if (hasOwnProperty.call(object, key) && key != "constructor") { - result.push(key); - } - } - return result; - } - module.exports = baseKeys; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/isArrayLike.js - var require_isArrayLike = __commonJS((exports, module) => { - var isFunction = require_isFunction(); - var isLength = require_isLength(); - function isArrayLike(value) { - return value != null && isLength(value.length) && !isFunction(value); - } - module.exports = isArrayLike; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/keys.js - var require_keys = __commonJS((exports, module) => { - var arrayLikeKeys = require__arrayLikeKeys(); - var baseKeys = require__baseKeys(); - var isArrayLike = require_isArrayLike(); - function keys(object) { - return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); - } - module.exports = keys; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getAllKeys.js - var require__getAllKeys = __commonJS((exports, module) => { - var baseGetAllKeys = require__baseGetAllKeys(); - var getSymbols = require__getSymbols(); - var keys = require_keys(); - function getAllKeys(object) { - return baseGetAllKeys(object, keys, getSymbols); - } - module.exports = getAllKeys; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_equalObjects.js - var require__equalObjects = __commonJS((exports, module) => { - var getAllKeys = require__getAllKeys(); - var COMPARE_PARTIAL_FLAG = 1; - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG, objProps = getAllKeys(object), objLength = objProps.length, othProps = getAllKeys(other), othLength = othProps.length; - if (objLength != othLength && !isPartial) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { - return false; - } - } - var objStacked = stack.get(object); - var othStacked = stack.get(other); - if (objStacked && othStacked) { - return objStacked == other && othStacked == object; - } - var result = true; - stack.set(object, other); - stack.set(other, object); - var skipCtor = isPartial; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], othValue = other[key]; - if (customizer) { - var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack); - } - if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) { - result = false; - break; - } - skipCtor || (skipCtor = key == "constructor"); - } - if (result && !skipCtor) { - var objCtor = object.constructor, othCtor = other.constructor; - if (objCtor != othCtor && (("constructor" in object) && ("constructor" in other)) && !(typeof objCtor == "function" && objCtor instanceof objCtor && typeof othCtor == "function" && othCtor instanceof othCtor)) { - result = false; - } - } - stack["delete"](object); - stack["delete"](other); - return result; - } - module.exports = equalObjects; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_DataView.js - var require__DataView = __commonJS((exports, module) => { - var getNative = require__getNative(); - var root = require__root(); - var DataView = getNative(root, "DataView"); - module.exports = DataView; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Promise.js - var require__Promise = __commonJS((exports, module) => { - var getNative = require__getNative(); - var root = require__root(); - var Promise2 = getNative(root, "Promise"); - module.exports = Promise2; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_Set.js - var require__Set = __commonJS((exports, module) => { - var getNative = require__getNative(); - var root = require__root(); - var Set2 = getNative(root, "Set"); - module.exports = Set2; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_WeakMap.js - var require__WeakMap = __commonJS((exports, module) => { - var getNative = require__getNative(); - var root = require__root(); - var WeakMap2 = getNative(root, "WeakMap"); - module.exports = WeakMap2; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getTag.js - var require__getTag = __commonJS((exports, module) => { - var DataView = require__DataView(); - var Map = require__Map(); - var Promise2 = require__Promise(); - var Set2 = require__Set(); - var WeakMap2 = require__WeakMap(); - var baseGetTag = require__baseGetTag(); - var toSource = require__toSource(); - var mapTag = "[object Map]"; - var objectTag = "[object Object]"; - var promiseTag = "[object Promise]"; - var setTag = "[object Set]"; - var weakMapTag = "[object WeakMap]"; - var dataViewTag = "[object DataView]"; - var dataViewCtorString = toSource(DataView); - var mapCtorString = toSource(Map); - var promiseCtorString = toSource(Promise2); - var setCtorString = toSource(Set2); - var weakMapCtorString = toSource(WeakMap2); - var getTag = baseGetTag; - if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag || Map && getTag(new Map) != mapTag || Promise2 && getTag(Promise2.resolve()) != promiseTag || Set2 && getTag(new Set2) != setTag || WeakMap2 && getTag(new WeakMap2) != weakMapTag) { - getTag = function(value) { - var result = baseGetTag(value), Ctor = result == objectTag ? value.constructor : undefined, ctorString = Ctor ? toSource(Ctor) : ""; - if (ctorString) { - switch (ctorString) { - case dataViewCtorString: - return dataViewTag; - case mapCtorString: - return mapTag; - case promiseCtorString: - return promiseTag; - case setCtorString: - return setTag; - case weakMapCtorString: - return weakMapTag; - } - } - return result; - }; - } - module.exports = getTag; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsEqualDeep.js - var require__baseIsEqualDeep = __commonJS((exports, module) => { - var Stack = require__Stack(); - var equalArrays = require__equalArrays(); - var equalByTag = require__equalByTag(); - var equalObjects = require__equalObjects(); - var getTag = require__getTag(); - var isArray = require_isArray(); - var isBuffer = require_isBuffer(); - var isTypedArray = require_isTypedArray(); - var COMPARE_PARTIAL_FLAG = 1; - var argsTag = "[object Arguments]"; - var arrayTag = "[object Array]"; - var objectTag = "[object Object]"; - var objectProto = Object.prototype; - var hasOwnProperty = objectProto.hasOwnProperty; - function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { - var objIsArr = isArray(object), othIsArr = isArray(other), objTag = objIsArr ? arrayTag : getTag(object), othTag = othIsArr ? arrayTag : getTag(other); - objTag = objTag == argsTag ? objectTag : objTag; - othTag = othTag == argsTag ? objectTag : othTag; - var objIsObj = objTag == objectTag, othIsObj = othTag == objectTag, isSameTag = objTag == othTag; - if (isSameTag && isBuffer(object)) { - if (!isBuffer(other)) { - return false; - } - objIsArr = true; - objIsObj = false; - } - if (isSameTag && !objIsObj) { - stack || (stack = new Stack); - return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); - } - if (!(bitmask & COMPARE_PARTIAL_FLAG)) { - var objIsWrapped = objIsObj && hasOwnProperty.call(object, "__wrapped__"), othIsWrapped = othIsObj && hasOwnProperty.call(other, "__wrapped__"); - if (objIsWrapped || othIsWrapped) { - var objUnwrapped = objIsWrapped ? object.value() : object, othUnwrapped = othIsWrapped ? other.value() : other; - stack || (stack = new Stack); - return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); - } - } - if (!isSameTag) { - return false; - } - stack || (stack = new Stack); - return equalObjects(object, other, bitmask, customizer, equalFunc, stack); - } - module.exports = baseIsEqualDeep; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsEqual.js - var require__baseIsEqual = __commonJS((exports, module) => { - var baseIsEqualDeep = require__baseIsEqualDeep(); - var isObjectLike = require_isObjectLike(); - function baseIsEqual(value, other, bitmask, customizer, stack) { - if (value === other) { - return true; - } - if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) { - return value !== value && other !== other; - } - return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); - } - module.exports = baseIsEqual; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIsMatch.js - var require__baseIsMatch = __commonJS((exports, module) => { - var Stack = require__Stack(); - var baseIsEqual = require__baseIsEqual(); - var COMPARE_PARTIAL_FLAG = 1; - var COMPARE_UNORDERED_FLAG = 2; - function baseIsMatch(object, source, matchData, customizer) { - var index = matchData.length, length = index, noCustomizer = !customizer; - if (object == null) { - return !length; - } - object = Object(object); - while (index--) { - var data = matchData[index]; - if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) { - return false; - } - } - while (++index < length) { - data = matchData[index]; - var key = data[0], objValue = object[key], srcValue = data[1]; - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var stack = new Stack; - if (customizer) { - var result = customizer(objValue, srcValue, key, object, source, stack); - } - if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result)) { - return false; - } - } - } - return true; - } - module.exports = baseIsMatch; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_isStrictComparable.js - var require__isStrictComparable = __commonJS((exports, module) => { - var isObject = require_isObject(); - function isStrictComparable(value) { - return value === value && !isObject(value); - } - module.exports = isStrictComparable; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_getMatchData.js - var require__getMatchData = __commonJS((exports, module) => { - var isStrictComparable = require__isStrictComparable(); - var keys = require_keys(); - function getMatchData(object) { - var result = keys(object), length = result.length; - while (length--) { - var key = result[length], value = object[key]; - result[length] = [key, value, isStrictComparable(value)]; - } - return result; - } - module.exports = getMatchData; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_matchesStrictComparable.js - var require__matchesStrictComparable = __commonJS((exports, module) => { - function matchesStrictComparable(key, srcValue) { - return function(object) { - if (object == null) { - return false; - } - return object[key] === srcValue && (srcValue !== undefined || (key in Object(object))); - }; - } - module.exports = matchesStrictComparable; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseMatches.js - var require__baseMatches = __commonJS((exports, module) => { - var baseIsMatch = require__baseIsMatch(); - var getMatchData = require__getMatchData(); - var matchesStrictComparable = require__matchesStrictComparable(); - function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - return matchesStrictComparable(matchData[0][0], matchData[0][1]); - } - return function(object) { - return object === source || baseIsMatch(object, source, matchData); - }; - } - module.exports = baseMatches; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/get.js - var require_get = __commonJS((exports, module) => { - var baseGet = require__baseGet(); - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, path); - return result === undefined ? defaultValue : result; - } - module.exports = get; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseHasIn.js - var require__baseHasIn = __commonJS((exports, module) => { - function baseHasIn(object, key) { - return object != null && key in Object(object); - } - module.exports = baseHasIn; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_hasPath.js - var require__hasPath = __commonJS((exports, module) => { - var castPath = require__castPath(); - var isArguments = require_isArguments(); - var isArray = require_isArray(); - var isIndex = require__isIndex(); - var isLength = require_isLength(); - var toKey = require__toKey(); - function hasPath(object, path, hasFunc) { - path = castPath(path, object); - var index = -1, length = path.length, result = false; - while (++index < length) { - var key = toKey(path[index]); - if (!(result = object != null && hasFunc(object, key))) { - break; - } - object = object[key]; - } - if (result || ++index != length) { - return result; - } - length = object == null ? 0 : object.length; - return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); - } - module.exports = hasPath; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/hasIn.js - var require_hasIn = __commonJS((exports, module) => { - var baseHasIn = require__baseHasIn(); - var hasPath = require__hasPath(); - function hasIn(object, path) { - return object != null && hasPath(object, path, baseHasIn); - } - module.exports = hasIn; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseMatchesProperty.js - var require__baseMatchesProperty = __commonJS((exports, module) => { - var baseIsEqual = require__baseIsEqual(); - var get = require_get(); - var hasIn = require_hasIn(); - var isKey = require__isKey(); - var isStrictComparable = require__isStrictComparable(); - var matchesStrictComparable = require__matchesStrictComparable(); - var toKey = require__toKey(); - var COMPARE_PARTIAL_FLAG = 1; - var COMPARE_UNORDERED_FLAG = 2; - function baseMatchesProperty(path, srcValue) { - if (isKey(path) && isStrictComparable(srcValue)) { - return matchesStrictComparable(toKey(path), srcValue); - } - return function(object) { - var objValue = get(object, path); - return objValue === undefined && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); - }; - } - module.exports = baseMatchesProperty; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/identity.js - var require_identity = __commonJS((exports, module) => { - function identity(value) { - return value; - } - module.exports = identity; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseProperty.js - var require__baseProperty = __commonJS((exports, module) => { - function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; - } - module.exports = baseProperty; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_basePropertyDeep.js - var require__basePropertyDeep = __commonJS((exports, module) => { - var baseGet = require__baseGet(); - function basePropertyDeep(path) { - return function(object) { - return baseGet(object, path); - }; - } - module.exports = basePropertyDeep; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/property.js - var require_property = __commonJS((exports, module) => { - var baseProperty = require__baseProperty(); - var basePropertyDeep = require__basePropertyDeep(); - var isKey = require__isKey(); - var toKey = require__toKey(); - function property(path) { - return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); - } - module.exports = property; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseIteratee.js - var require__baseIteratee = __commonJS((exports, module) => { - var baseMatches = require__baseMatches(); - var baseMatchesProperty = require__baseMatchesProperty(); - var identity = require_identity(); - var isArray = require_isArray(); - var property = require_property(); - function baseIteratee(value) { - if (typeof value == "function") { - return value; - } - if (value == null) { - return identity; - } - if (typeof value == "object") { - return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value); - } - return property(value); - } - module.exports = baseIteratee; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_createBaseFor.js - var require__createBaseFor = __commonJS((exports, module) => { - function createBaseFor(fromRight) { - return function(object, iteratee, keysFunc) { - var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length; - while (length--) { - var key = props[fromRight ? length : ++index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; - } - module.exports = createBaseFor; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseFor.js - var require__baseFor = __commonJS((exports, module) => { - var createBaseFor = require__createBaseFor(); - var baseFor = createBaseFor(); - module.exports = baseFor; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseForOwn.js - var require__baseForOwn = __commonJS((exports, module) => { - var baseFor = require__baseFor(); - var keys = require_keys(); - function baseForOwn(object, iteratee) { - return object && baseFor(object, iteratee, keys); - } - module.exports = baseForOwn; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_createBaseEach.js - var require__createBaseEach = __commonJS((exports, module) => { - var isArrayLike = require_isArrayLike(); - function createBaseEach(eachFunc, fromRight) { - return function(collection, iteratee) { - if (collection == null) { - return collection; - } - if (!isArrayLike(collection)) { - return eachFunc(collection, iteratee); - } - var length = collection.length, index = fromRight ? length : -1, iterable = Object(collection); - while (fromRight ? index-- : ++index < length) { - if (iteratee(iterable[index], index, iterable) === false) { - break; - } - } - return collection; - }; - } - module.exports = createBaseEach; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseEach.js - var require__baseEach = __commonJS((exports, module) => { - var baseForOwn = require__baseForOwn(); - var createBaseEach = require__createBaseEach(); - var baseEach = createBaseEach(baseForOwn); - module.exports = baseEach; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseMap.js - var require__baseMap = __commonJS((exports, module) => { - var baseEach = require__baseEach(); - var isArrayLike = require_isArrayLike(); - function baseMap(collection, iteratee) { - var index = -1, result = isArrayLike(collection) ? Array(collection.length) : []; - baseEach(collection, function(value, key, collection2) { - result[++index] = iteratee(value, key, collection2); - }); - return result; - } - module.exports = baseMap; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseSortBy.js - var require__baseSortBy = __commonJS((exports, module) => { - function baseSortBy(array, comparer) { - var length = array.length; - array.sort(comparer); - while (length--) { - array[length] = array[length].value; - } - return array; - } - module.exports = baseSortBy; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_compareAscending.js - var require__compareAscending = __commonJS((exports, module) => { - var isSymbol = require_isSymbol(); - function compareAscending(value, other) { - if (value !== other) { - var valIsDefined = value !== undefined, valIsNull = value === null, valIsReflexive = value === value, valIsSymbol = isSymbol(value); - var othIsDefined = other !== undefined, othIsNull = other === null, othIsReflexive = other === other, othIsSymbol = isSymbol(other); - if (!othIsNull && !othIsSymbol && !valIsSymbol && value > other || valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol || valIsNull && othIsDefined && othIsReflexive || !valIsDefined && othIsReflexive || !valIsReflexive) { - return 1; - } - if (!valIsNull && !valIsSymbol && !othIsSymbol && value < other || othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol || othIsNull && valIsDefined && valIsReflexive || !othIsDefined && valIsReflexive || !othIsReflexive) { - return -1; - } - } - return 0; - } - module.exports = compareAscending; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_compareMultiple.js - var require__compareMultiple = __commonJS((exports, module) => { - var compareAscending = require__compareAscending(); - function compareMultiple(object, other, orders) { - var index = -1, objCriteria = object.criteria, othCriteria = other.criteria, length = objCriteria.length, ordersLength = orders.length; - while (++index < length) { - var result = compareAscending(objCriteria[index], othCriteria[index]); - if (result) { - if (index >= ordersLength) { - return result; - } - var order = orders[index]; - return result * (order == "desc" ? -1 : 1); - } - } - return object.index - other.index; - } - module.exports = compareMultiple; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/_baseOrderBy.js - var require__baseOrderBy = __commonJS((exports, module) => { - var arrayMap = require__arrayMap(); - var baseGet = require__baseGet(); - var baseIteratee = require__baseIteratee(); - var baseMap = require__baseMap(); - var baseSortBy = require__baseSortBy(); - var baseUnary = require__baseUnary(); - var compareMultiple = require__compareMultiple(); - var identity = require_identity(); - var isArray = require_isArray(); - function baseOrderBy(collection, iteratees, orders) { - if (iteratees.length) { - iteratees = arrayMap(iteratees, function(iteratee) { - if (isArray(iteratee)) { - return function(value) { - return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee); - }; - } - return iteratee; - }); - } else { - iteratees = [identity]; - } - var index = -1; - iteratees = arrayMap(iteratees, baseUnary(baseIteratee)); - var result = baseMap(collection, function(value, key, collection2) { - var criteria = arrayMap(iteratees, function(iteratee) { - return iteratee(value); - }); - return { criteria, index: ++index, value }; - }); - return baseSortBy(result, function(object, other) { - return compareMultiple(object, other, orders); - }); - } - module.exports = baseOrderBy; - }); - - // node_modules/.bun/lodash@4.17.21/node_modules/lodash/orderBy.js - var require_orderBy = __commonJS((exports, module) => { - var baseOrderBy = require__baseOrderBy(); - var isArray = require_isArray(); - function orderBy(collection, iteratees, orders, guard) { - if (collection == null) { - return []; - } - if (!isArray(iteratees)) { - iteratees = iteratees == null ? [] : [iteratees]; - } - orders = guard ? undefined : orders; - if (!isArray(orders)) { - orders = orders == null ? [] : [orders]; - } - return baseOrderBy(collection, iteratees, orders); - } - module.exports = orderBy; - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/native_utils.js - var require_native_utils = __commonJS((exports, module) => { - function _intersect(arr1, arr2) { - if (!arr1 || !arr2 || arr1.length === 0 || arr2.length === 0) - return []; - if (arr1.length < 100 && arr2.length < 100) { - return arr1.filter((item) => arr2.includes(item)); - } - const set = new Set(arr2); - return arr1.filter((item) => set.has(item)); - } - function _intersectWith(arr1, arr2, comparator) { - if (!arr1 || !arr2 || arr1.length === 0 || arr2.length === 0) - return []; - return arr1.filter((a) => arr2.some((b) => comparator(a, b))); - } - function _difference(arr1, arr2) { - if (!arr1) - return []; - if (!arr2 || arr2.length === 0) - return arr1.slice(); - if (arr1.length < 100 && arr2.length < 100) { - return arr1.filter((item) => !arr2.includes(item)); - } - const set = new Set(arr2); - return arr1.filter((item) => !set.has(item)); - } - function _differenceWith(arr1, arr2, comparator) { - if (!arr1) - return []; - if (!arr2 || arr2.length === 0) - return arr1.slice(); - return arr1.filter((a) => !arr2.some((b) => comparator(a, b))); - } - function _uniq(arr) { - if (!arr || arr.length === 0) - return []; - if (arr.length === 1) - return arr.slice(); - return [...new Set(arr)]; - } - function _uniqWith(arr, comparator) { - if (!arr || arr.length === 0) - return []; - if (arr.length === 1) - return arr.slice(); - const result = []; - outer: - for (let i = 0;i < arr.length; i++) { - const current = arr[i]; - for (let j = 0;j < result.length; j++) { - if (comparator(current, result[j])) { - continue outer; - } - } - result.push(current); - } - return result; - } - function _partialRight(func) { - const boundArgs = Array.prototype.slice.call(arguments, 1); - return function() { - const args = Array.prototype.slice.call(arguments); - return func.apply(this, args.concat(boundArgs)); - }; - } - function _forEach(obj, callback) { - if (!obj) - return; - if (Array.isArray(obj)) { - for (let i = 0;i < obj.length; i++) { - callback(obj[i], i); - } - } else { - const keys = Object.keys(obj); - for (let i = 0;i < keys.length; i++) { - callback(obj[keys[i]], keys[i]); - } - } - } - module.exports = { - _intersect, - _intersectWith, - _difference, - _differenceWith, - _uniq, - _uniqWith, - _partialRight, - _forEach, - _isArray: Array.isArray - }; - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/iLeven.js - var require_iLeven = __commonJS((exports, module) => { - var collator; - try { - collator = typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined" ? Intl.Collator("generic", { sensitivity: "base" }) : null; - } catch (err) { - if (typeof console !== undefined) - console.warn("Collator could not be initialized and wouldn't be used"); - } - module.exports = function leven(a, b, options) { - var arr = []; - var charCodeCache = []; - var useCollator = options && collator && options.useCollator; - var subcost = 1; - if (options && options.subcost && typeof options.subcost === "number") - subcost = options.subcost; - if (a === b) { - return 0; - } - var achars = Array.from(a); - var bchars = Array.from(b); - var aLen = achars.length; - var bLen = bchars.length; - if (aLen === 0) { - return bLen; - } - if (bLen === 0) { - return aLen; - } - var bCharCode; - var ret; - var tmp; - var tmp2; - var i = 0; - var j = 0; - while (i < aLen) { - charCodeCache[i] = achars[i].codePointAt(0); - arr[i] = ++i; - } - if (!useCollator) { - while (j < bLen) { - bCharCode = bchars[j].codePointAt(0); - tmp = j++; - ret = j; - for (i = 0;i < aLen; i++) { - tmp2 = bCharCode === charCodeCache[i] ? tmp : tmp + subcost; - tmp = arr[i]; - ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; - } - } - } else { - while (j < bLen) { - bCharCode = bchars[j].codePointAt(0); - tmp = j++; - ret = j; - for (i = 0;i < aLen; i++) { - tmp2 = collator.compare(String.fromCodePoint(bCharCode), String.fromCodePoint(charCodeCache[i])) === 0 ? tmp : tmp + subcost; - tmp = arr[i]; - ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; - } - } - } - return ret; - }; - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/wildcardLeven.js - var require_wildcardLeven = __commonJS((exports, module) => { - var collator; - try { - collator = typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined" ? Intl.Collator("generic", { sensitivity: "base" }) : null; - } catch (err) { - if (typeof console !== undefined) - console.warn("Collator could not be initialized and wouldn't be used"); - } - module.exports = function leven(a, b, options, regLeven) { - var arr = []; - var charCodeCache = []; - var useCollator = options && collator && options.useCollator; - var subcost = 1; - if (options && options.subcost && typeof options.subcost === "number") - subcost = options.subcost; - if (a === b) { - return 0; - } - var aLen = a.length; - var bLen = b.length; - if (aLen === 0) { - return bLen; - } - if (bLen === 0) { - return aLen; - } - function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - } - if (options && options.wildcards && typeof options.wildcards === "string" && options.wildcards.length > 0) { - var wildchar; - var wildcode; - if (options.full_process === false && options.processed !== true) { - wildchar = options.wildcards[0]; - wildcode = wildchar.charCodeAt(0); - var pattern = "[" + escapeRegExp(options.wildcards) + "]"; - a = a.replace(new RegExp(pattern, "g"), wildchar); - b = b.replace(new RegExp(pattern, "g"), wildchar); - if (a === b) - return 0; - } else { - wildchar = options.wildcards[0].toLowerCase(); - wildcode = wildchar.charCodeAt(0); - } - var bCharCode; - var ret; - var tmp; - var tmp2; - var i = 0; - var j = 0; - while (i < aLen) { - charCodeCache[i] = a.charCodeAt(i); - arr[i] = ++i; - } - if (!useCollator) { - while (j < bLen) { - bCharCode = b.charCodeAt(j); - tmp = j++; - ret = j; - for (i = 0;i < aLen; i++) { - tmp2 = bCharCode === charCodeCache[i] || bCharCode === wildcode || charCodeCache[i] === wildcode ? tmp : tmp + subcost; - tmp = arr[i]; - ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; - } - } - } else { - while (j < bLen) { - bCharCode = b.charCodeAt(j); - tmp = j++; - ret = j; - for (i = 0;i < aLen; i++) { - tmp2 = collator.compare(String.fromCharCode(bCharCode), String.fromCharCode(charCodeCache[i])) === 0 || bCharCode === wildcode || charCodeCache[i] === wildcode ? tmp : tmp + subcost; - tmp = arr[i]; - ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; - } - } - } - return ret; - } else { - return regLeven(a, b, options); - } - }; - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/leven.js - var require_leven = __commonJS((exports, module) => { - var collator; - try { - collator = typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined" ? Intl.Collator("generic", { sensitivity: "base" }) : null; - } catch (err) { - if (typeof console !== undefined) - console.warn("Collator could not be initialized and wouldn't be used"); - } - module.exports = function leven(a, b, options) { - var arr = []; - var charCodeCache = []; - var useCollator = options && collator && options.useCollator; - var subcost = 1; - if (options && options.subcost && typeof options.subcost === "number") - subcost = options.subcost; - if (a === b) { - return 0; - } - var aLen = a.length; - var bLen = b.length; - if (aLen === 0) { - return bLen; - } - if (bLen === 0) { - return aLen; - } - var bCharCode; - var ret; - var tmp; - var tmp2; - var i = 0; - var j = 0; - while (i < aLen) { - charCodeCache[i] = a.charCodeAt(i); - arr[i] = ++i; - } - if (!useCollator) { - while (j < bLen) { - bCharCode = b.charCodeAt(j); - tmp = j++; - ret = j; - for (i = 0;i < aLen; i++) { - tmp2 = bCharCode === charCodeCache[i] ? tmp : tmp + subcost; - tmp = arr[i]; - ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; - } - } - } else { - while (j < bLen) { - bCharCode = b.charCodeAt(j); - tmp = j++; - ret = j; - for (i = 0;i < aLen; i++) { - tmp2 = collator.compare(String.fromCharCode(bCharCode), String.fromCharCode(charCodeCache[i])) === 0 ? tmp : tmp + subcost; - tmp = arr[i]; - ret = arr[i] = tmp > ret ? tmp2 > ret ? ret + 1 : tmp2 : tmp2 > tmp ? tmp + 1 : tmp2; - } - } - } - return ret; - }; - }); - - // node_modules/.bun/setimmediate@1.0.5/node_modules/setimmediate/setImmediate.js - var require_setImmediate = __commonJS((exports) => { - (function(global2, undefined2) { - if (global2.setImmediate) { - return; - } - var nextHandle = 1; - var tasksByHandle = {}; - var currentlyRunningATask = false; - var doc = global2.document; - var registerImmediate; - function setImmediate2(callback) { - if (typeof callback !== "function") { - callback = new Function("" + callback); - } - var args = new Array(arguments.length - 1); - for (var i = 0;i < args.length; i++) { - args[i] = arguments[i + 1]; - } - var task = { callback, args }; - tasksByHandle[nextHandle] = task; - registerImmediate(nextHandle); - return nextHandle++; - } - function clearImmediate(handle) { - delete tasksByHandle[handle]; - } - function run(task) { - var callback = task.callback; - var args = task.args; - switch (args.length) { - case 0: - callback(); - break; - case 1: - callback(args[0]); - break; - case 2: - callback(args[0], args[1]); - break; - case 3: - callback(args[0], args[1], args[2]); - break; - default: - callback.apply(undefined2, args); - break; - } - } - function runIfPresent(handle) { - if (currentlyRunningATask) { - setTimeout(runIfPresent, 0, handle); - } else { - var task = tasksByHandle[handle]; - if (task) { - currentlyRunningATask = true; - try { - run(task); - } finally { - clearImmediate(handle); - currentlyRunningATask = false; - } - } - } - } - function installNextTickImplementation() { - registerImmediate = function(handle) { - process.nextTick(function() { - runIfPresent(handle); - }); - }; - } - function canUsePostMessage() { - if (global2.postMessage && !global2.importScripts) { - var postMessageIsAsynchronous = true; - var oldOnMessage = global2.onmessage; - global2.onmessage = function() { - postMessageIsAsynchronous = false; - }; - global2.postMessage("", "*"); - global2.onmessage = oldOnMessage; - return postMessageIsAsynchronous; - } - } - function installPostMessageImplementation() { - var messagePrefix = "setImmediate$" + Math.random() + "$"; - var onGlobalMessage = function(event) { - if (event.source === global2 && typeof event.data === "string" && event.data.indexOf(messagePrefix) === 0) { - runIfPresent(+event.data.slice(messagePrefix.length)); - } - }; - if (global2.addEventListener) { - global2.addEventListener("message", onGlobalMessage, false); - } else { - global2.attachEvent("onmessage", onGlobalMessage); - } - registerImmediate = function(handle) { - global2.postMessage(messagePrefix + handle, "*"); - }; - } - function installMessageChannelImplementation() { - var channel = new MessageChannel; - channel.port1.onmessage = function(event) { - var handle = event.data; - runIfPresent(handle); - }; - registerImmediate = function(handle) { - channel.port2.postMessage(handle); - }; - } - function installReadyStateChangeImplementation() { - var html = doc.documentElement; - registerImmediate = function(handle) { - var script = doc.createElement("script"); - script.onreadystatechange = function() { - runIfPresent(handle); - script.onreadystatechange = null; - html.removeChild(script); - script = null; - }; - html.appendChild(script); - }; - } - function installSetTimeoutImplementation() { - registerImmediate = function(handle) { - setTimeout(runIfPresent, 0, handle); - }; - } - var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global2); - attachTo = attachTo && attachTo.setTimeout ? attachTo : global2; - if ({}.toString.call(global2.process) === "[object process]") { - installNextTickImplementation(); - } else if (canUsePostMessage()) { - installPostMessageImplementation(); - } else if (global2.MessageChannel) { - installMessageChannelImplementation(); - } else if (doc && "onreadystatechange" in doc.createElement("script")) { - installReadyStateChangeImplementation(); - } else { - installSetTimeoutImplementation(); - } - attachTo.setImmediate = setImmediate2; - attachTo.clearImmediate = clearImmediate; - })(typeof self === "undefined" ? typeof global === "undefined" ? exports : global : self); - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/utils.js - var require_utils = __commonJS((exports, module) => { - module.exports = function(_uniq, _uniqWith, _partialRight) { - var module2 = {}; - var wildLeven = require_wildcardLeven(); - var leven = require_leven(); - function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - } - function validate(str) { - if ((typeof str === "string" || str instanceof String) && str.length > 0) - return true; - else - return false; - } - module2.validate = validate; - module2.process_and_sort = function process_and_sort(str) { - if (!validate(str)) - return ""; - return str.match(/\S+/g).sort().join(" ").trim(); - }; - module2.tokenize = function unique_tokens(str, options) { - if (options && options.wildcards && _uniqWith && _partialRight) { - var partWild = _partialRight(wildLeven, options, leven); - var wildCompare = function(a, b) { - return partWild(a, b) === 0; - }; - return _uniqWith(str.match(/\S+/g), wildCompare); - } else - return _uniq(str.match(/\S+/g)); - }; - const alphaNumUnicode = /[^\p{L}\p{N}]/gu; - module2.full_process = function full_process(str, options) { - if (!(str instanceof String) && typeof str !== "string") - return ""; - var processedtext; - if (options && typeof options === "object" && options.wildcards && typeof options.wildcards === "string" && options.wildcards.length > 0) { - var wildcards = options.wildcards.toLowerCase(); - str = str.toLowerCase(); - if (options.force_ascii) { - var pattern = "[^\x00 -|" + escapeRegExp(wildcards) + "]"; - str = str.replace(new RegExp(pattern, "g"), ""); - var wildpattern = "[" + escapeRegExp(wildcards) + "]"; - var wildchar = wildcards[0]; - str = str.replace(new RegExp(wildpattern, "g"), wildchar); - var alphanumPat = "[^A-Za-z0-9" + escapeRegExp(wildcards) + "]"; - str = str.replace(new RegExp(alphanumPat, "g"), " "); - str = str.replace(/_/g, " "); - processedtext = str.trim(); - } else { - var upattern = "[^\\p{L}\\p{N}|" + escapeRegExp(wildcards) + "]"; - str = str.replace(new RegExp(upattern, "gu"), " "); - var wildpattern = "[" + escapeRegExp(wildcards) + "]"; - var wildchar = wildcards[0]; - str = str.replace(new RegExp(wildpattern, "g"), wildchar); - processedtext = str.trim(); - } - } else { - if (options && (options.force_ascii || options === true)) { - str = str.replace(/[^\x00-\x7F]/g, ""); - processedtext = str.replace(/\W|_/g, " ").toLowerCase().trim(); - } else { - processedtext = str.replace(alphaNumUnicode, " ").toLowerCase().trim(); - } - } - if (options && options.collapseWhitespace) { - processedtext = processedtext.replace(/\s+/g, " "); - } - return processedtext; - }; - module2.clone_and_set_option_defaults = function(options) { - if (options && options.isAClone) - return options; - var optclone = { isAClone: true }; - if (options) { - var i, keys = Object.keys(options); - for (i = 0;i < keys.length; i++) { - optclone[keys[i]] = options[keys[i]]; - } - } - if (!(optclone.full_process === false)) - optclone.full_process = true; - if (!(optclone.force_ascii === true)) - optclone.force_ascii = false; - if (!(optclone.normalize === false) && optclone.astral === true) { - optclone.normalize = true; - } - if (!(optclone.collapseWhitespace === false)) - optclone.collapseWhitespace = true; - return optclone; - }; - module2.isCustomFunc = function(func) { - if (typeof func === "function" && (func.name === "token_set_ratio" || func.name === "partial_token_set_ratio" || func.name === "token_sort_ratio" || func.name === "partial_token_sort_ratio" || func.name === "QRatio" || func.name === "WRatio" || func.name === "distance" || func.name === "partial_ratio")) { - return false; - } else { - return true; - } - }; - return module2; - }; - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/lib/process.js - var require_process = __commonJS((exports, module) => { - module.exports = function(_clone_and_set_option_defaults, _isArray, QRatio, extract) { - module = {}; - module.dedupe = function dedupe(contains_dupes, options_p) { - var options = _clone_and_set_option_defaults(options_p); - if (!(_isArray(contains_dupes) || typeof contains_dupes === "object")) { - throw new Error("contains_dupes must be an array or object"); - return; - } - if (Object.keys(contains_dupes).length === 0) { - if (typeof console !== undefined) - console.warn("contains_dupes is empty"); - return []; - } - if (options.limit) { - if (typeof console !== undefined) - console.warn("options.limit will be ignored in dedupe"); - options.limit = 0; - } - if (!options.cutoff || typeof options.cutoff !== "number") { - if (typeof console !== undefined) - console.warn("Using default cutoff of 70"); - options.cutoff = 70; - } - if (!options.scorer) { - options.scorer = QRatio; - if (typeof console !== undefined) - console.log("Using default scorer 'ratio' for dedupe"); - } - var processor; - if (options.processor && typeof options.processor === "function") { - processor = options.processor; - } else - processor = function(x) { - return x; - }; - var uniqueItems = {}; - for (var i in contains_dupes) { - var item = processor(contains_dupes[i]); - if (typeof item !== "string" && item instanceof String === false) { - throw new Error("Each processed item in dedupe must be a string."); - } - var matches = extract(item, contains_dupes, options); - if (options.returnObjects) { - if (matches.length === 1) { - if (options.keepmap) - uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key, matches }; - else - uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key }; - } else { - matches = matches.sort(function(a, b) { - var pa = processor(a.choice); - var pb = processor(b.choice); - var aLen = pa.length; - var bLen = pb.length; - if (aLen === bLen) { - if (pa < pb) - return -1; - else - return 1; - } else - return bLen - aLen; - }); - if (options.keepmap) - uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key, matches }; - else - uniqueItems[processor(matches[0].choice)] = { item: matches[0].choice, key: matches[0].key }; - } - } else { - if (matches.length === 1) { - if (options.keepmap) - uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2], matches]; - else - uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2]]; - } else { - matches = matches.sort(function(a, b) { - var pa = processor(a[0]); - var pb = processor(b[0]); - var aLen = pa.length; - var bLen = pb.length; - if (aLen === bLen) { - if (pa < pb) - return -1; - else - return 1; - } else - return bLen - aLen; - }); - if (options.keepmap) - uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2], matches]; - else - uniqueItems[processor(matches[0][0])] = [matches[0][0], matches[0][2]]; - } - } - } - var uniqueVals = []; - for (var u in uniqueItems) { - uniqueVals.push(uniqueItems[u]); - } - return uniqueVals; - }; - return module; - }; - }); - - // node_modules/.bun/fuzzball@2.2.3/node_modules/fuzzball/fuzzball.js - var require_fuzzball = __commonJS((exports, module) => { - (function() { - var SequenceMatcher = require_fbdifflib(); - var Heap = require_heap2(); - var orderBy = require_orderBy(); - var nativeUtils = require_native_utils(); - var _intersect = nativeUtils._intersect; - var _intersectWith = nativeUtils._intersectWith; - var _difference = nativeUtils._difference; - var _differenceWith = nativeUtils._differenceWith; - var _uniq = nativeUtils._uniq; - var _uniqWith = nativeUtils._uniqWith; - var _partialRight = nativeUtils._partialRight; - var _forEach = nativeUtils._forEach; - var _isArray = nativeUtils._isArray; - var iLeven = require_iLeven(); - var wildleven = require_wildcardLeven(); - var leven = require_leven(); - if (typeof setImmediate !== "function") { - require_setImmediate(); - } - var utils = require_utils()(_uniq, _uniqWith, _partialRight); - var validate = utils.validate; - var process_and_sort = utils.process_and_sort; - var tokenize = utils.tokenize; - var full_process = utils.full_process; - var clone_and_set_option_defaults = utils.clone_and_set_option_defaults; - var isCustomFunc = utils.isCustomFunc; - var processing = require_process()(clone_and_set_option_defaults, _isArray, QRatio, extract); - var dedupe = processing.dedupe; - function distance(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (typeof options.subcost === "undefined") - options.subcost = 1; - if (options.astral) - return iLeven(str1, str2, options); - else - return wildleven(str1, str2, options, leven); - } - function QRatio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - return _ratio(str1, str2, options); - } - function partial_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - return _partial_ratio(str1, str2, options); - } - function token_set_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - return _token_set(str1, str2, options); - } - function partial_token_set_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - options.partial = true; - return _token_set(str1, str2, options); - } - function token_sort_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - if (!options.proc_sorted) { - str1 = process_and_sort(str1); - str2 = process_and_sort(str2); - } - return _ratio(str1, str2, options); - } - function partial_token_sort_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - options.partial = true; - if (!options.proc_sorted) { - str1 = process_and_sort(str1); - str2 = process_and_sort(str2); - } - return _partial_ratio(str1, str2, options); - } - function token_similarity_sort_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - return _token_similarity_sort_ratio(str1, str2, options); - } - function partial_token_similarity_sort_ratio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - options.partial = true; - return _token_similarity_sort_ratio(str1, str2, options); - } - function WRatio(str1, str2, options_p) { - var options = clone_and_set_option_defaults(options_p); - str1 = options.normalize ? str1.normalize() : str1; - str2 = options.normalize ? str2.normalize() : str2; - str1 = options.full_process ? full_process(str1, options) : str1; - str2 = options.full_process ? full_process(str2, options) : str2; - options.full_process = false; - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - var try_partial = true; - var unbase_scale = 0.95; - var partial_scale = 0.9; - var base = _ratio(str1, str2, options); - var len_ratio = Math.max(str1.length, str2.length) / Math.min(str1.length, str2.length); - if (len_ratio < 1.5) - try_partial = false; - if (len_ratio > 8) - partial_scale = 0.6; - if (try_partial) { - var partial = _partial_ratio(str1, str2, options) * partial_scale; - var ptsor = partial_token_sort_ratio(str1, str2, options) * unbase_scale * partial_scale; - var ptser = partial_token_set_ratio(str1, str2, options) * unbase_scale * partial_scale; - return Math.round(Math.max(base, partial, ptsor, ptser)); - } else { - var tsor = token_sort_ratio(str1, str2, options) * unbase_scale; - var tser = token_set_ratio(str1, str2, options) * unbase_scale; - return Math.round(Math.max(base, tsor, tser)); - } - } - function extract(query, choices, options_p) { - var options = clone_and_set_option_defaults(options_p); - var numchoices; - if (_isArray(choices)) { - numchoices = choices.length; - } else if (!(choices instanceof Object)) { - throw new Error("Invalid choices"); - } else - numchoices = Object.keys(choices).length; - if (!choices || numchoices === 0) { - if (typeof console !== undefined) - console.warn("No choices"); - return []; - } - if (options.processor && typeof options.processor !== "function") { - throw new Error("Invalid Processor"); - } - if (!options.processor) - options.processor = function(x) { - return x; - }; - if (options.scorer && typeof options.scorer !== "function") { - throw new Error("Invalid Scorer"); - } - if (!options.scorer) { - options.scorer = QRatio; - } - var isCustom = isCustomFunc(options.scorer); - if (!options.cutoff || typeof options.cutoff !== "number") { - options.cutoff = -1; - } - var pre_processor = function(choice, force_ascii) { - return choice; - }; - if (options.full_process) { - pre_processor = full_process; - if (!isCustom) - options.processed = true; - } - var normalize = false; - if (!isCustom) { - if (options.astral && options.normalize) { - options.normalize = false; - if (String.prototype.normalize) { - normalize = true; - query = query.normalize(); - } else { - if (typeof console !== undefined) - console.warn("Normalization not supported in your environment"); - } - } - query = pre_processor(query, options); - options.full_process = false; - if (query.length === 0) { - if (typeof console !== undefined) - console.warn("Processed query is empty string"); - } - } - var results = []; - var anyblank = false; - var tsort = false; - var tset = false; - if (options.scorer.name === "token_sort_ratio" || options.scorer.name === "partial_token_sort_ratio") { - var proc_sorted_query = process_and_sort(query); - tsort = true; - } else if (options.scorer.name === "token_set_ratio" || options.scorer.name === "partial_token_set_ratio") { - var query_tokens = tokenize(query, options); - tset = true; - } - var result, mychoice, cmpHeap, cmpSort; - if (options.returnObjects) { - cmpHeap = function(a, b) { - return a.score - b.score; - }; - cmpSort = function(a, b) { - return b.score - a.score; - }; - } else { - cmpHeap = function(a, b) { - return a[1] - b[1]; - }; - cmpSort = function(a, b) { - return b[1] - a[1]; - }; - } - _forEach(choices, function(value, key) { - options.tokens = undefined; - options.proc_sorted = false; - if (tsort) { - options.proc_sorted = true; - if (value && value.proc_sorted) - mychoice = value.proc_sorted; - else { - mychoice = pre_processor(normalize ? options.processor(value).normalize() : options.processor(value), options); - mychoice = process_and_sort(mychoice); - } - result = options.scorer(proc_sorted_query, mychoice, options); - } else if (tset) { - mychoice = "x"; - if (value && value.tokens) { - options.tokens = [query_tokens, value.tokens]; - if (options.trySimple) - mychoice = pre_processor(options.processor(value), options); - } else { - mychoice = pre_processor(normalize ? options.processor(value).normalize() : options.processor(value), options); - options.tokens = [query_tokens, tokenize(mychoice, options)]; - } - result = options.scorer(query, mychoice, options); - } else if (isCustom) { - mychoice = options.processor(value); - result = options.scorer(query, mychoice, options); - } else { - mychoice = pre_processor(normalize ? options.processor(value).normalize() : options.processor(value), options); - if (typeof mychoice !== "string" || mychoice.length === 0) - anyblank = true; - result = options.scorer(query, mychoice, options); - } - if (result > options.cutoff) { - if (options.returnObjects) - results.push({ choice: value, score: result, key }); - else - results.push([value, result, key]); - } - }); - if (anyblank) { - if (typeof console !== undefined) - console.log("One or more choices were empty. (post-processing if applied)"); - } - if (options.limit && typeof options.limit === "number" && options.limit > 0 && options.limit < numchoices && !options.unsorted) { - results = Heap.nlargest(results, options.limit, cmpHeap); - } else if (!options.unsorted) { - results = results.sort(cmpSort); - } - return results; - } - function extractAsync(query, choices, options_p, callback) { - var options = clone_and_set_option_defaults(options_p); - var abortController; - if (typeof options_p.abortController === "object") { - abortController = options_p.abortController; - } - var cancelToken; - if (typeof options_p.cancelToken === "object") { - cancelToken = options_p.cancelToken; - } - var loopOffset = 256; - if (typeof options.asyncLoopOffset === "number") { - if (options.asyncLoopOffset < 1) - loopOffset = 1; - else - loopOffset = options.asyncLoopOffset; - } - var isArray = false; - var numchoices; - if (choices && choices.length && _isArray(choices)) { - numchoices = choices.length; - isArray = true; - } else if (!(choices instanceof Object)) { - callback(new Error("Invalid choices")); - return; - } else - numchoices = Object.keys(choices).length; - if (!choices || numchoices === 0) { - if (typeof console !== undefined) - console.warn("No choices"); - callback(null, []); - return; - } - if (options.processor && typeof options.processor !== "function") { - callback(new Error("Invalid Processor")); - return; - } - if (!options.processor) - options.processor = function(x) { - return x; - }; - if (options.scorer && typeof options.scorer !== "function") { - callback(new Error("Invalid Scorer")); - return; - } - if (!options.scorer) { - options.scorer = QRatio; - } - var isCustom = isCustomFunc(options.scorer); - if (!options.cutoff || typeof options.cutoff !== "number") { - options.cutoff = -1; - } - var pre_processor = function(choice, force_ascii) { - return choice; - }; - if (options.full_process) { - pre_processor = full_process; - if (!isCustom) - options.processed = true; - } - var normalize = false; - if (!isCustom) { - if (options.astral && options.normalize) { - options.normalize = false; - if (String.prototype.normalize) { - normalize = true; - query = query.normalize(); - } else { - if (typeof console !== undefined) - console.warn("Normalization not supported in your environment"); - } - } - query = pre_processor(query, options); - options.full_process = false; - if (query.length === 0) { - if (typeof console !== undefined) - console.warn("Processed query is empty string"); - } - } - var results = []; - var anyblank = false; - var tsort = false; - var tset = false; - if (options.scorer.name === "token_sort_ratio" || options.scorer.name === "partial_token_sort_ratio") { - var proc_sorted_query = process_and_sort(query); - tsort = true; - } else if (options.scorer.name === "token_set_ratio" || options.scorer.name === "partial_token_set_ratio") { - var query_tokens = tokenize(query, options); - tset = true; - } - var idx, mychoice, result, cmpHeap, cmpSort; - if (options.returnObjects) { - cmpHeap = function(a, b) { - return a.score - b.score; - }; - cmpSort = function(a, b) { - return b.score - a.score; - }; - } else { - cmpHeap = function(a, b) { - return a[1] - b[1]; - }; - cmpSort = function(a, b) { - return b[1] - a[1]; - }; - } - var keys = Object.keys(choices); - isArray ? searchLoop(0) : searchLoop(keys[0], 0); - function searchLoop(c, i) { - if (isArray || choices.hasOwnProperty(c)) { - options.tokens = undefined; - options.proc_sorted = false; - if (tsort) { - options.proc_sorted = true; - if (choices[c] && choices[c].proc_sorted) - mychoice = choices[c].proc_sorted; - else { - mychoice = pre_processor(normalize ? options.processor(choices[c]).normalize() : options.processor(choices[c]), options); - mychoice = process_and_sort(mychoice); - } - result = options.scorer(proc_sorted_query, mychoice, options); - } else if (tset) { - mychoice = "x"; - if (choices[c] && choices[c].tokens) { - options.tokens = [query_tokens, choices[c].tokens]; - if (options.trySimple) - mychoice = pre_processor(options.processor(choices[c]), options); - } else { - mychoice = pre_processor(normalize ? options.processor(choices[c]).normalize() : options.processor(choices[c]), options); - options.tokens = [query_tokens, tokenize(mychoice, options)]; - } - result = options.scorer(query, mychoice, options); - } else if (isCustom) { - mychoice = options.processor(choices[c]); - result = options.scorer(query, mychoice, options); - } else { - mychoice = pre_processor(normalize ? options.processor(choices[c]).normalize() : options.processor(choices[c]), options); - if (typeof mychoice !== "string" || mychoice.length === 0) - anyblank = true; - result = options.scorer(query, mychoice, options); - } - if (isArray) - idx = parseInt(c); - else - idx = c; - if (result > options.cutoff) { - if (options.returnObjects) - results.push({ choice: choices[c], score: result, key: idx }); - else - results.push([choices[c], result, idx]); - } - } - if (abortController && abortController.signal.aborted === true) { - callback(new Error("aborted")); - return; - } - if (cancelToken && cancelToken.canceled === true) { - callback(new Error("canceled")); - return; - } - if (isArray && c < choices.length - 1) { - if (c % loopOffset === 0) { - setImmediate(function() { - searchLoop(c + 1); - }); - } else { - searchLoop(c + 1); - } - } else if (i < keys.length - 1) { - if (i % loopOffset === 0) { - setImmediate(function() { - searchLoop(keys[i + 1], i + 1); - }); - } else { - searchLoop(keys[i + 1], i + 1); - } - } else { - if (anyblank) { - if (typeof console !== undefined) - console.log("One or more choices were empty. (post-processing if applied)"); - } - if (options.limit && typeof options.limit === "number" && options.limit > 0 && options.limit < numchoices && !options.unsorted) { - results = Heap.nlargest(results, options.limit, cmpHeap); - } else if (!options.unsorted) { - results = results.sort(cmpSort); - } - callback(null, results); - } - } - } - function _cosineSim(v1, v2, options) { - var keysV1 = Object.keys(v1); - var keysV2 = Object.keys(v2); - var intersection = _intersect(keysV1, keysV2); - var prods = intersection.map(function(x) { - return v1[x] * v2[x]; - }); - var numerator = prods.reduce(function(acc, x) { - return acc + x; - }, 0); - var v1Prods = keysV1.map(function(x) { - return Math.pow(v1[x], 2); - }); - var v1sum = v1Prods.reduce(function(acc, x) { - return acc + x; - }, 0); - var v2Prods = keysV2.map(function(x) { - return Math.pow(v2[x], 2); - }); - var v2sum = v2Prods.reduce(function(acc, x) { - return acc + x; - }, 0); - var denominator = Math.sqrt(v1sum) * Math.sqrt(v2sum); - return numerator / denominator; - } - var WILDCARD_KEY = "%*SuperUniqueWildcardKey*%"; - function _getCharacterCounts(str, options) { - var normalString = str; - if (options.astral) { - var charArray = Array.from(normalString); - } else { - var charArray = normalString.split(""); - } - var charCounts = {}; - if (options.wildcards) { - for (var i = 0;i < charArray.length; i++) { - var char = charArray[i]; - if (options.wildcards.indexOf(char) > -1) { - if (charCounts[WILDCARD_KEY]) { - charCounts[WILDCARD_KEY] += 1; - } else { - charCounts[WILDCARD_KEY] = 1; - } - } else if (charCounts[char]) { - charCounts[char] += 1; - } else { - charCounts[char] = 1; - } - } - } else { - for (var i = 0;i < charArray.length; i++) { - var char = charArray[i]; - if (charCounts[char]) { - charCounts[char] += 1; - } else { - charCounts[char] = 1; - } - } - } - return charCounts; - } - function _token_similarity_sort(sorted1, sorted2, options) { - var oldSorted2 = sorted2; - var charCounts1 = sorted1.reduce(function(acc, str) { - acc[str] = _getCharacterCounts(str, options); - return acc; - }, {}); - var charCounts2 = oldSorted2.reduce(function(acc, str) { - acc[str] = _getCharacterCounts(str, options); - return acc; - }, {}); - var newSorted2 = []; - var i = 0; - while (oldSorted2.length && i < sorted1.length) { - var sim = orderBy(oldSorted2, function(x) { - return _cosineSim(charCounts1[sorted1[i]], charCounts2[x]); - }, "desc")[0]; - newSorted2.push(sim); - i++; - oldSorted2 = oldSorted2.filter(function(token) { - return token !== sim; - }); - } - return newSorted2.concat(oldSorted2); - } - function _order_token_lists(str1, tokens1, str2, tokens2) { - var first = tokens1; - var second = tokens2; - if (tokens1.length > tokens2.length) { - first = tokens2; - second = tokens1; - } else if (tokens1.length === tokens2.length) { - if (str1.length > str2.length) { - first = tokens2; - second = tokens1; - } else { - var sortedStrings = [str1, str2].sort(); - if (sortedStrings[0] === str2) { - first = tokens2; - second = tokens1; - } - } - } - return [first, second]; - } - function _token_similarity_sort_ratio(str1, str2, options) { - if (!options.tokens) { - var tokens1 = tokenize(str1, options); - var tokens2 = tokenize(str2, options); - } else { - var tokens1 = options.tokens[0]; - var tokens2 = options.tokens[1]; - } - var sorted1 = tokens1.sort(); - var sorted2 = tokens2.sort(); - var orderedTokenLists = _order_token_lists(str1, sorted1, str2, sorted2); - var first = orderedTokenLists[0]; - var second = orderedTokenLists[1]; - const newSecond = _token_similarity_sort(first, second, options); - if (!options.partial) { - return _ratio(first.join(" "), newSecond.join(" "), options); - } else { - return _partial_ratio(first.join(" "), newSecond.join(" "), options); - } - } - function _token_set(str1, str2, options) { - if (!options.tokens) { - var tokens1 = tokenize(str1, options); - var tokens2 = tokenize(str2, options); - } else { - var tokens1 = options.tokens[0]; - var tokens2 = options.tokens[1]; - } - if (options.wildcards) { - var partWild = _partialRight(wildleven, options, leven); - var wildCompare = function(a, b) { - return partWild(a, b) === 0; - }; - var intersection = _intersectWith(tokens1, tokens2, wildCompare); - var diff1to2 = _differenceWith(tokens1, tokens2, wildCompare); - var diff2to1 = _differenceWith(tokens2, tokens1, wildCompare); - } else { - var intersection = _intersect(tokens1, tokens2); - var diff1to2 = _difference(tokens1, tokens2); - var diff2to1 = _difference(tokens2, tokens1); - } - var sorted_sect = intersection.sort().join(" "); - var sorted_1to2List = diff1to2.sort(); - var sorted_2to1List = diff2to1.sort(); - if (options.sortBySimilarity) { - var orderedTokenLists = _order_token_lists(str1, sorted_1to2List, str2, sorted_2to1List); - var first = orderedTokenLists[0]; - var second = orderedTokenLists[1]; - var sorted_1to2 = first.join(" "); - var sorted_2to1 = _token_similarity_sort(first, second, options).join(" "); - } else { - var sorted_1to2 = sorted_1to2List.join(" "); - var sorted_2to1 = sorted_2to1List.join(" "); - } - var combined_1to2 = sorted_sect + " " + sorted_1to2; - var combined_2to1 = sorted_sect + " " + sorted_2to1; - sorted_sect = sorted_sect.trim(); - combined_1to2 = combined_1to2.trim(); - combined_2to1 = combined_2to1.trim(); - var ratio_func = _ratio; - if (options.partial) { - ratio_func = _partial_ratio; - if (sorted_sect.length > 0) - return 100; - } - var pairwise = [ - ratio_func(sorted_sect, combined_1to2, options), - ratio_func(sorted_sect, combined_2to1, options), - ratio_func(combined_1to2, combined_2to1, options) - ]; - if (options.trySimple) { - pairwise.push(ratio_func(str1, str2, options)); - } - return Math.max.apply(null, pairwise); - } - function _ratio(str1, str2, options) { - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - if (options.ratio_alg && options.ratio_alg === "difflib") { - var m = new SequenceMatcher(null, str1, str2, options.autojunk); - var r = m.ratio(); - return Math.round(100 * r); - } - if (typeof options.subcost === "undefined") - options.subcost = 2; - var levdistance, lensum; - if (options.astral) { - levdistance = iLeven(str1, str2, options); - lensum = Array.from(str1).length + Array.from(str2).length; - } else { - if (!options.wildcards) { - levdistance = leven(str1, str2, options); - lensum = str1.length + str2.length; - } else { - levdistance = wildleven(str1, str2, options, leven); - lensum = str1.length + str2.length; - } - } - return Math.round(100 * ((lensum - levdistance) / lensum)); - } - function _partial_ratio(str1, str2, options) { - if (!validate(str1)) - return 0; - if (!validate(str2)) - return 0; - if (str1.length <= str2.length) { - var shorter = str1; - var longer = str2; - } else { - var shorter = str2; - var longer = str1; - } - var m = new SequenceMatcher(null, shorter, longer, options.autojunk); - var blocks = m.getMatchingBlocks(); - var scores = []; - for (var b = 0;b < blocks.length; b++) { - var long_start = blocks[b][1] - blocks[b][0] > 0 ? blocks[b][1] - blocks[b][0] : 0; - var long_end = long_start + shorter.length; - var long_substr = longer.substring(long_start, long_end); - var r = _ratio(shorter, long_substr, options); - if (r > 99.5) - return 100; - else - scores.push(r); - } - return Math.max.apply(null, scores); - } - if (!Object.keys) { - Object.keys = function() { - var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !{ toString: null }.propertyIsEnumerable("toString"), dontEnums = [ - "toString", - "toLocaleString", - "valueOf", - "hasOwnProperty", - "isPrototypeOf", - "propertyIsEnumerable", - "constructor" - ], dontEnumsLength = dontEnums.length; - return function(obj) { - if (typeof obj !== "object" && (typeof obj !== "function" || obj === null)) { - throw new TypeError("Object.keys called on non-object"); - } - var result = [], prop, i; - for (prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - if (hasDontEnumBug) { - for (i = 0;i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - return result; - }; - }(); - } - var extractAsPromised = undefined; - if (typeof Promise !== "undefined") { - extractAsPromised = function(query, choices, options) { - return new Promise(function(resolve, reject) { - extractAsync(query, choices, options, function(err, response) { - if (err) - reject(err); - else - resolve(response); - }); - }); - }; - } - var fuzzball = { - distance, - ratio: QRatio, - partial_ratio, - token_set_ratio, - token_sort_ratio, - partial_token_set_ratio, - partial_token_sort_ratio, - token_similarity_sort_ratio, - partial_token_similarity_sort_ratio, - WRatio, - full_process, - extract, - extractAsync, - extractAsPromised, - process_and_sort, - unique_tokens: tokenize, - dedupe - }; - module.exports = fuzzball; - })(); - }); - - // node_modules/.bun/country-locale-map@1.9.11/node_modules/country-locale-map/countries-intl.json - var require_countries_intl = __commonJS((exports, module) => { - module.exports = [{ name: "Afghanistan", alpha2: "AF", alpha3: "AFG", numeric: "004", locales: ["ps-AF", "fa-AF", "uz-Arab-AF"], default_locale: "ps-AF", currency: "AFN", latitude: "33.93911", longitude: "67.709953", currency_name: "Afghani", languages: ["ps", "uz", "tk"], capital: "Kabul", emoji: "🇦🇫", emojiU: "U+1F1E6 U+1F1EB", fips: "AF", internet: "AF", continent: "Asia", region: "South Asia" }, { name: "Albania", alpha2: "AL", alpha3: "ALB", numeric: "008", locales: ["sq-AL"], default_locale: "sq-AL", currency: "ALL", latitude: "41.153332", longitude: "20.168331", currency_name: "Lek", languages: ["sq"], capital: "Tirana", emoji: "🇦🇱", emojiU: "U+1F1E6 U+1F1F1", fips: "AL", internet: "AL", continent: "Europe", region: "South East Europe" }, { name: "Algeria", alpha2: "DZ", alpha3: "DZA", numeric: "012", locales: ["ar-DZ", "kab-DZ"], default_locale: "ar-DZ", currency: "DZD", latitude: "28.033886", longitude: "1.659626", currency_name: "Algerian Dinar", languages: ["ar"], capital: "Algiers", emoji: "🇩🇿", emojiU: "U+1F1E9 U+1F1FF", fips: "AG", internet: "DZ", continent: "Africa", region: "Northern Africa" }, { name: "American Samoa", alpha2: "AS", alpha3: "ASM", numeric: "016", locales: ["en-AS"], default_locale: "en-AS", currency: "USD", latitude: "-14.270972", longitude: "-170.132217", currency_name: "US Dollar", languages: ["en", "sm"], capital: "Pago Pago", emoji: "🇦🇸", emojiU: "U+1F1E6 U+1F1F8", fips: "AQ", internet: "AS", continent: "Oceania", region: "Pacific" }, { name: "Andorra", alpha2: "AD", alpha3: "AND", numeric: "020", locales: ["ca"], default_locale: "ca", currency: "EUR", latitude: "42.546245", longitude: "1.601554", currency_name: "Euro", languages: ["ca"], capital: "Andorra la Vella", emoji: "🇦🇩", emojiU: "U+1F1E6 U+1F1E9", fips: "AN", internet: "AD", continent: "Europe", region: "South West Europe" }, { name: "Angola", alpha2: "AO", alpha3: "AGO", numeric: "024", locales: ["pt"], default_locale: "pt", currency: "AOA", latitude: "-11.202692", longitude: "17.873887", currency_name: "Kwanza", languages: ["pt"], capital: "Luanda", emoji: "🇦🇴", emojiU: "U+1F1E6 U+1F1F4", fips: "AO", internet: "AO", continent: "Africa", region: "Southern Africa" }, { name: "Anguilla", alpha2: "AI", alpha3: "AIA", numeric: "660", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "18.220554", longitude: "-63.068615", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "The Valley", emoji: "🇦🇮", emojiU: "U+1F1E6 U+1F1EE", fips: "AV", internet: "AI", continent: "Americas", region: "West Indies" }, { name: "Antarctica", alpha2: "AQ", alpha3: "ATA", numeric: "010", locales: ["en-US"], default_locale: "en-US", currency: "USD", latitude: "-75.250973", longitude: "-0.071389", currency_name: "US Dollar", languages: [], capital: "", emoji: "🇦🇶", emojiU: "U+1F1E6 U+1F1F6", fips: "AY", internet: "AQ", continent: "Antarctica", region: "Antarctica" }, { name: "Antigua and Barbuda", alpha2: "AG", alpha3: "ATG", numeric: "028", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.060816", longitude: "-61.796428", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Saint John's", emoji: "🇦🇬", emojiU: "U+1F1E6 U+1F1EC", fips: "AC", internet: "AG", continent: "Americas", region: "West Indies" }, { name: "Argentina", alpha2: "AR", alpha3: "ARG", numeric: "032", locales: ["es-AR"], default_locale: "es-AR", currency: "ARS", latitude: "-38.416097", longitude: "-63.616672", currency_name: "Argentine Peso", languages: ["es", "gn"], capital: "Buenos Aires", emoji: "🇦🇷", emojiU: "U+1F1E6 U+1F1F7", fips: "AR", internet: "AR", continent: "Americas", region: "South America" }, { name: "Armenia", alpha2: "AM", alpha3: "ARM", numeric: "051", locales: ["hy-AM"], default_locale: "hy-AM", currency: "AMD", latitude: "40.069099", longitude: "45.038189", currency_name: "Armenian Dram", languages: ["hy", "ru"], capital: "Yerevan", emoji: "🇦🇲", emojiU: "U+1F1E6 U+1F1F2", fips: "AM", internet: "AM", continent: "Asia", region: "South West Asia" }, { name: "Aruba", alpha2: "AW", alpha3: "ABW", numeric: "533", locales: ["nl"], default_locale: "nl", currency: "AWG", latitude: "12.52111", longitude: "-69.968338", currency_name: "Aruban Florin", languages: ["nl", "pa"], capital: "Oranjestad", emoji: "🇦🇼", emojiU: "U+1F1E6 U+1F1FC", fips: "AA", internet: "AW", continent: "Americas", region: "West Indies" }, { name: "Australia", alpha2: "AU", alpha3: "AUS", numeric: "036", locales: ["en-AU"], default_locale: "en-AU", currency: "AUD", latitude: "-25.274398", longitude: "133.775136", currency_name: "Australian Dollar", languages: ["en"], capital: "Canberra", emoji: "🇦🇺", emojiU: "U+1F1E6 U+1F1FA", fips: "AS", internet: "AU", continent: "Oceania", region: "Pacific" }, { name: "Austria", alpha2: "AT", alpha3: "AUT", numeric: "040", locales: ["de-AT"], default_locale: "de-AT", currency: "EUR", latitude: "47.516231", longitude: "14.550072", currency_name: "Euro", languages: ["de"], capital: "Vienna", emoji: "🇦🇹", emojiU: "U+1F1E6 U+1F1F9", fips: "AU", internet: "AT", continent: "Europe", region: "Central Europe" }, { name: "Azerbaijan", alpha2: "AZ", alpha3: "AZE", numeric: "031", locales: ["az-Cyrl-AZ", "az-Latn-AZ"], default_locale: "az-Cyrl-AZ", currency: "AZN", latitude: "40.143105", longitude: "47.576927", currency_name: "Azerbaijan Manat", languages: ["az"], capital: "Baku", emoji: "🇦🇿", emojiU: "U+1F1E6 U+1F1FF", fips: "AJ", internet: "AZ", continent: "Asia", region: "South West Asia" }, { name: "Bahamas", alpha2: "BS", alpha3: "BHS", numeric: "044", locales: ["en"], default_locale: "en", currency: "BSD", latitude: "25.03428", longitude: "-77.39628", currency_name: "Bahamian Dollar", languages: ["en"], capital: "Nassau", emoji: "🇧🇸", emojiU: "U+1F1E7 U+1F1F8", fips: "BF", internet: "BS", continent: "Americas", region: "West Indies" }, { name: "Bahrain", alpha2: "BH", alpha3: "BHR", numeric: "048", locales: ["ar-BH"], default_locale: "ar-BH", currency: "BHD", latitude: "25.930414", longitude: "50.637772", currency_name: "Bahraini Dinar", languages: ["ar"], capital: "Manama", emoji: "🇧🇭", emojiU: "U+1F1E7 U+1F1ED", fips: "BA", internet: "BH", continent: "Asia", region: "South West Asia" }, { name: "Bangladesh", alpha2: "BD", alpha3: "BGD", numeric: "050", locales: ["bn-BD"], default_locale: "bn-BD", currency: "BDT", latitude: "23.684994", longitude: "90.356331", currency_name: "Taka", languages: ["bn"], capital: "Dhaka", emoji: "🇧🇩", emojiU: "U+1F1E7 U+1F1E9", fips: "BG", internet: "BD", continent: "Asia", region: "South Asia" }, { name: "Barbados", alpha2: "BB", alpha3: "BRB", numeric: "052", locales: ["en"], default_locale: "en", currency: "BBD", latitude: "13.193887", longitude: "-59.543198", currency_name: "Barbados Dollar", languages: ["en"], capital: "Bridgetown", emoji: "🇧🇧", emojiU: "U+1F1E7 U+1F1E7", fips: "BB", internet: "BB", continent: "Americas", region: "West Indies" }, { name: "Belarus", alpha2: "BY", alpha3: "BLR", numeric: "112", locales: ["be-BY"], default_locale: "be-BY", currency: "BYN", latitude: "53.709807", longitude: "27.953389", currency_name: "Belarusian Ruble", languages: ["be", "ru"], capital: "Minsk", emoji: "🇧🇾", emojiU: "U+1F1E7 U+1F1FE", fips: "BO", internet: "BY", continent: "Europe", region: "Eastern Europe" }, { name: "Belgium", alpha2: "BE", alpha3: "BEL", numeric: "056", locales: ["nl-BE", "en-BE", "fr-BE", "de-BE"], default_locale: "nl-BE", currency: "EUR", latitude: "50.503887", longitude: "4.469936", currency_name: "Euro", languages: ["nl", "fr", "de"], capital: "Brussels", emoji: "🇧🇪", emojiU: "U+1F1E7 U+1F1EA", fips: "BE", internet: "BE", continent: "Europe", region: "Western Europe" }, { name: "Belize", alpha2: "BZ", alpha3: "BLZ", numeric: "084", locales: ["en-BZ"], default_locale: "en-BZ", currency: "BZD", latitude: "17.189877", longitude: "-88.49765", currency_name: "Belize Dollar", languages: ["en", "es"], capital: "Belmopan", emoji: "🇧🇿", emojiU: "U+1F1E7 U+1F1FF", fips: "BH", internet: "BZ", continent: "Americas", region: "Central America" }, { name: "Benin", alpha2: "BJ", alpha3: "BEN", numeric: "204", locales: ["fr-BJ"], default_locale: "fr-BJ", currency: "XOF", latitude: "9.30769", longitude: "2.315834", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Porto-Novo", emoji: "🇧🇯", emojiU: "U+1F1E7 U+1F1EF", fips: "BN", internet: "BJ", continent: "Africa", region: "Western Africa" }, { name: "Bermuda", alpha2: "BM", alpha3: "BMU", numeric: "060", locales: ["en"], default_locale: "en", currency: "BMD", latitude: "32.321384", longitude: "-64.75737", currency_name: "Bermudian Dollar", languages: ["en"], capital: "Hamilton", emoji: "🇧🇲", emojiU: "U+1F1E7 U+1F1F2", fips: "BD", internet: "BM", continent: "Americas", region: "West Indies" }, { name: "Bhutan", alpha2: "BT", alpha3: "BTN", numeric: "064", locales: ["dz"], default_locale: "dz", currency: "BTN", latitude: "27.514162", longitude: "90.433601", currency_name: "Ngultrum", languages: ["dz"], capital: "Thimphu", emoji: "🇧🇹", emojiU: "U+1F1E7 U+1F1F9", fips: "BT", internet: "BT", continent: "Asia", region: "South Asia" }, { name: "Bolivia", alpha2: "BO", alpha3: "BOL", numeric: "068", locales: ["es-BO"], default_locale: "es-BO", currency: "BOB", latitude: "-16.290154", longitude: "-63.588653", currency_name: "Bolivia", languages: ["es", "ay", "qu"], capital: "Sucre", emoji: "🇧🇴", emojiU: "U+1F1E7 U+1F1F4", fips: "BL", internet: "BO", continent: "Americas", region: "South America", alternate_names: ["Plurinational State of Bolivia"] }, { name: "Bonaire", alpha2: "BQ", alpha3: "BES", numeric: "535", locales: ["nl"], default_locale: "nl", currency: "USD", currency_name: "US Dollar", languages: ["nl"], capital: "Kralendijk", emoji: "🇧🇶", emojiU: "U+1F1E7 U+1F1F6", fips: "BQ", internet: "BQ", continent: "Americas", region: "West Indies", alternate_names: ["Bonaire, Sint Eustatius and Saba"] }, { name: "Bosnia and Herzegovina", alpha2: "BA", alpha3: "BIH", numeric: "070", locales: ["bs-BA", "sr-Cyrl-BA", "sr-Latn-BA"], default_locale: "bs-BA", currency: "BAM", latitude: "43.915886", longitude: "17.679076", currency_name: "Convertible Mark", languages: ["bs", "hr", "sr"], capital: "Sarajevo", emoji: "🇧🇦", emojiU: "U+1F1E7 U+1F1E6", fips: "BK", internet: "BA", continent: "Europe", region: "South East Europe" }, { name: "Botswana", alpha2: "BW", alpha3: "BWA", numeric: "072", locales: ["en-BW"], default_locale: "en-BW", currency: "BWP", latitude: "-22.328474", longitude: "24.684866", currency_name: "Pula", languages: ["en", "tn"], capital: "Gaborone", emoji: "🇧🇼", emojiU: "U+1F1E7 U+1F1FC", fips: "BC", internet: "BW", continent: "Africa", region: "Southern Africa" }, { name: "Bouvet Island", alpha2: "BV", alpha3: "BVT", numeric: "074", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "-54.423199", longitude: "3.413194", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "", emoji: "🇧🇻", emojiU: "U+1F1E7 U+1F1FB", fips: "BV", internet: "BV", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "Brazil", alpha2: "BR", alpha3: "BRA", numeric: "076", locales: ["pt-BR"], default_locale: "pt-BR", currency: "BRL", latitude: "-14.235004", longitude: "-51.92528", currency_name: "Brazilian Real", languages: ["pt"], capital: "Brasília", emoji: "🇧🇷", emojiU: "U+1F1E7 U+1F1F7", fips: "BR", internet: "BR", continent: "Americas", region: "South America" }, { name: "British Indian Ocean Territory", alpha2: "IO", alpha3: "IOT", numeric: "086", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-6.343194", longitude: "71.876519", currency_name: "US Dollar", languages: ["en"], capital: "Diego Garcia", emoji: "🇮🇴", emojiU: "U+1F1EE U+1F1F4", fips: "IO", internet: "IO", continent: "Asia", region: "South Asia" }, { name: "Brunei Darussalam", alpha2: "BN", alpha3: "BRN", numeric: "096", locales: ["ms-BN"], default_locale: "ms-BN", currency: "BND", latitude: "4.535277", longitude: "114.727669", currency_name: "Brunei Dollar", languages: ["ms"], capital: "Bandar Seri Begawan", emoji: "🇧🇳", emojiU: "U+1F1E7 U+1F1F3", fips: "BX", internet: "BN", continent: "Asia", region: "South East Asia" }, { name: "Bulgaria", alpha2: "BG", alpha3: "BGR", numeric: "100", locales: ["bg-BG"], default_locale: "bg-BG", currency: "BGN", latitude: "42.733883", longitude: "25.48583", currency_name: "Bulgarian Lev", languages: ["bg"], capital: "Sofia", emoji: "🇧🇬", emojiU: "U+1F1E7 U+1F1EC", fips: "BU", internet: "BG", continent: "Europe", region: "South East Europe" }, { name: "Burkina Faso", alpha2: "BF", alpha3: "BFA", numeric: "854", locales: ["fr-BF"], default_locale: "fr-BF", currency: "XOF", latitude: "12.238333", longitude: "-1.561593", currency_name: "CFA Franc BCEAO", languages: ["fr", "ff"], capital: "Ouagadougou", emoji: "🇧🇫", emojiU: "U+1F1E7 U+1F1EB", fips: "UV", internet: "BF", continent: "Africa", region: "Western Africa" }, { name: "Burundi", alpha2: "BI", alpha3: "BDI", numeric: "108", locales: ["fr-BI"], default_locale: "fr-BI", currency: "BIF", latitude: "-3.373056", longitude: "29.918886", currency_name: "Burundi Franc", languages: ["fr", "rn"], capital: "Bujumbura", emoji: "🇧🇮", emojiU: "U+1F1E7 U+1F1EE", fips: "BY", internet: "BI", continent: "Africa", region: "Central Africa" }, { name: "Cabo Verde", alpha2: "CV", alpha3: "CPV", numeric: "132", locales: ["kea-CV"], default_locale: "kea-CV", currency: "CVE", latitude: "16.002082", longitude: "-24.013197", currency_name: "Cabo Verde Escudo", languages: ["pt"], capital: "Praia", emoji: "🇨🇻", emojiU: "U+1F1E8 U+1F1FB", fips: "CV", internet: "CV", continent: "Africa", region: "Western Africa" }, { name: "Cambodia", alpha2: "KH", alpha3: "KHM", numeric: "116", locales: ["km-KH"], default_locale: "km-KH", currency: "KHR", latitude: "12.565679", longitude: "104.990963", currency_name: "Riel", languages: ["km"], capital: "Phnom Penh", emoji: "🇰🇭", emojiU: "U+1F1F0 U+1F1ED", fips: "CB", internet: "KH", continent: "Asia", region: "South East Asia" }, { name: "Cameroon", alpha2: "CM", alpha3: "CMR", numeric: "120", locales: ["fr-CM"], default_locale: "fr-CM", currency: "XAF", latitude: "7.369722", longitude: "12.354722", currency_name: "CFA Franc BEAC", languages: ["en", "fr"], capital: "Yaoundé", emoji: "🇨🇲", emojiU: "U+1F1E8 U+1F1F2", fips: "CM", internet: "CM", continent: "Africa", region: "Western Africa" }, { name: "Canada", alpha2: "CA", alpha3: "CAN", numeric: "124", locales: ["en-CA", "fr-CA"], default_locale: "en-CA", currency: "CAD", latitude: "56.130366", longitude: "-106.346771", currency_name: "Canadian Dollar", languages: ["en", "fr"], capital: "Ottawa", emoji: "🇨🇦", emojiU: "U+1F1E8 U+1F1E6", fips: "CA", internet: "CA", continent: "Americas", region: "North America" }, { name: "Cayman Islands", alpha2: "KY", alpha3: "CYM", numeric: "136", locales: ["en"], default_locale: "en", currency: "KYD", latitude: "19.513469", longitude: "-80.566956", currency_name: "Cayman Islands Dollar", languages: ["en"], capital: "George Town", emoji: "🇰🇾", emojiU: "U+1F1F0 U+1F1FE", fips: "CJ", internet: "KY", continent: "Americas", region: "West Indies" }, { name: "Central African Republic", alpha2: "CF", alpha3: "CAF", numeric: "140", locales: ["fr-CF", "sg-CF"], default_locale: "fr-CF", currency: "XAF", latitude: "6.611111", longitude: "20.939444", currency_name: "CFA Franc BEAC", languages: ["fr", "sg"], capital: "Bangui", emoji: "🇨🇫", emojiU: "U+1F1E8 U+1F1EB", fips: "CT", internet: "CF", continent: "Africa", region: "Central Africa" }, { name: "Chad", alpha2: "TD", alpha3: "TCD", numeric: "148", locales: ["fr-TD"], default_locale: "fr-TD", currency: "XAF", latitude: "15.454166", longitude: "18.732207", currency_name: "CFA Franc BEAC", languages: ["fr", "ar"], capital: "N'Djamena", emoji: "🇹🇩", emojiU: "U+1F1F9 U+1F1E9", fips: "CD", internet: "TD", continent: "Africa", region: "Central Africa" }, { name: "Chile", alpha2: "CL", alpha3: "CHL", numeric: "152", locales: ["es-CL"], default_locale: "es-CL", currency: "CLP", latitude: "-35.675147", longitude: "-71.542969", currency_name: "Chilean Peso", languages: ["es"], capital: "Santiago", emoji: "🇨🇱", emojiU: "U+1F1E8 U+1F1F1", fips: "CI", internet: "CL", continent: "Americas", region: "South America" }, { name: "China", alpha2: "CN", alpha3: "CHN", numeric: "156", locales: ["zh-CN", "zh-Hans-CN", "ii-CN", "bo-CN"], default_locale: "zh-CN", currency: "CNY", latitude: "35.86166", longitude: "104.195397", currency_name: "Yuan Renminbi", languages: ["zh"], capital: "Beijing", emoji: "🇨🇳", emojiU: "U+1F1E8 U+1F1F3", fips: "CH", internet: "CN", continent: "Asia", region: "East Asia" }, { name: "Christmas Island", alpha2: "CX", alpha3: "CXR", numeric: "162", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-10.447525", longitude: "105.690449", currency_name: "Australian Dollar", languages: ["en"], capital: "Flying Fish Cove", emoji: "🇨🇽", emojiU: "U+1F1E8 U+1F1FD", fips: "KT", internet: "CX", continent: "Asia", region: "South East Asia" }, { name: "Cocos Islands", alpha2: "CC", alpha3: "CCK", numeric: "166", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-12.164165", longitude: "96.870956", currency_name: "Australian Dollar", languages: ["en"], capital: "West Island", emoji: "🇨🇨", emojiU: "U+1F1E8 U+1F1E8", fips: "CK", internet: "CC", continent: "Asia", region: "South East Asia", alternate_names: ["Cocos Keeling Islands"] }, { name: "Colombia", alpha2: "CO", alpha3: "COL", numeric: "170", locales: ["es-CO"], default_locale: "es-CO", currency: "COP", latitude: "4.570868", longitude: "-74.297333", currency_name: "Colombian Peso", languages: ["es"], capital: "Bogotá", emoji: "🇨🇴", emojiU: "U+1F1E8 U+1F1F4", fips: "CO", internet: "CO", continent: "Americas", region: "South America" }, { name: "Comoros", alpha2: "KM", alpha3: "COM", numeric: "174", locales: ["fr-KM"], default_locale: "fr-KM", currency: "KMF", latitude: "-11.875001", longitude: "43.872219", currency_name: "Comorian Franc ", languages: ["ar", "fr"], capital: "Moroni", emoji: "🇰🇲", emojiU: "U+1F1F0 U+1F1F2", fips: "CN", internet: "KM", continent: "Africa", region: "Indian Ocean" }, { name: "Democratic Republic of the Congo", alpha2: "CD", alpha3: "COD", numeric: "180", locales: ["fr-CD"], default_locale: "fr-CD", currency: "CDF", latitude: "-4.038333", longitude: "21.758664", currency_name: "Congolese Franc", languages: ["fr", "ln", "kg", "sw", "lu"], capital: "Kinshasa", emoji: "🇨🇩", emojiU: "U+1F1E8 U+1F1E9", fips: "CG", internet: "ZR", continent: "Africa", region: "Central Africa" }, { name: "Congo", alpha2: "CG", alpha3: "COG", numeric: "178", locales: ["fr-CG"], default_locale: "fr-CG", currency: "XAF", latitude: "-0.228021", longitude: "15.827659", currency_name: "CFA Franc BEAC", languages: ["fr", "ln"], capital: "Brazzaville", emoji: "🇨🇬", emojiU: "U+1F1E8 U+1F1EC", fips: "CF", internet: "CG", continent: "Africa", region: "Central Africa" }, { name: "Cook Islands", alpha2: "CK", alpha3: "COK", numeric: "184", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-21.236736", longitude: "-159.777671", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Avarua", emoji: "🇨🇰", emojiU: "U+1F1E8 U+1F1F0", fips: "CW", internet: "CK", continent: "Oceania", region: "Pacific" }, { name: "Costa Rica", alpha2: "CR", alpha3: "CRI", numeric: "188", locales: ["es-CR"], default_locale: "es-CR", currency: "CRC", latitude: "9.748917", longitude: "-83.753428", currency_name: "Costa Rican Colon", languages: ["es"], capital: "San José", emoji: "🇨🇷", emojiU: "U+1F1E8 U+1F1F7", fips: "CS", internet: "CR", continent: "Americas", region: "Central America" }, { name: "Croatia", alpha2: "HR", alpha3: "HRV", numeric: "191", locales: ["hr-HR"], default_locale: "hr-HR", currency: "EUR", latitude: "45.1", longitude: "15.2", currency_name: "Euro", languages: ["hr"], capital: "Zagreb", emoji: "🇭🇷", emojiU: "U+1F1ED U+1F1F7", fips: "HR", internet: "HR", continent: "Europe", region: "South East Europe" }, { name: "Cuba", alpha2: "CU", alpha3: "CUB", numeric: "192", locales: ["es"], default_locale: "es", currency: "CUC", latitude: "21.521757", longitude: "-77.781167", currency_name: "Peso Convertible", languages: ["es"], capital: "Havana", emoji: "🇨🇺", emojiU: "U+1F1E8 U+1F1FA", fips: "CU", internet: "CU", continent: "Americas", region: "West Indies" }, { name: "Curaçao", alpha2: "CW", alpha3: "CUW", numeric: "531", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "pa", "en"], capital: "Willemstad", emoji: "🇨🇼", emojiU: "U+1F1E8 U+1F1FC", fips: "UC", internet: "CW", continent: "Americas", region: "West Indies" }, { name: "Cyprus", alpha2: "CY", alpha3: "CYP", numeric: "196", locales: ["el-CY"], default_locale: "el-CY", currency: "EUR", latitude: "35.126413", longitude: "33.429859", currency_name: "Euro", languages: ["el", "tr", "hy"], capital: "Nicosia", emoji: "🇨🇾", emojiU: "U+1F1E8 U+1F1FE", fips: "CY", internet: "CY", continent: "Asia", region: "South West Asia" }, { name: "Czechia", alpha2: "CZ", alpha3: "CZE", numeric: "203", locales: ["cs-CZ"], default_locale: "cs-CZ", currency: "CZK", latitude: "49.817492", longitude: "15.472962", currency_name: "Czech Koruna", languages: ["cs", "sk"], capital: "Prague", emoji: "🇨🇿", emojiU: "U+1F1E8 U+1F1FF", fips: "EZ", internet: "CZ", continent: "Europe", region: "Central Europe" }, { name: "Côte d'Ivoire", alpha2: "CI", alpha3: "CIV", numeric: "384", locales: ["fr-CI"], default_locale: "fr-CI", currency: "CZK", latitude: "7.539989", longitude: "-5.54708", currency_name: "Czech Koruna", languages: ["fr"], capital: "Yamoussoukro", emoji: "🇨🇮", emojiU: "U+1F1E8 U+1F1EE", fips: "IV", internet: "CI", continent: "Africa", region: "Western Africa" }, { name: "Denmark", alpha2: "DK", alpha3: "DNK", numeric: "208", locales: ["da-DK"], default_locale: "da-DK", currency: "DKK", latitude: "56.26392", longitude: "9.501785", currency_name: "Danish Krone", languages: ["da"], capital: "Copenhagen", emoji: "🇩🇰", emojiU: "U+1F1E9 U+1F1F0", fips: "DA", internet: "DK", continent: "Europe", region: "Northern Europe" }, { name: "Djibouti", alpha2: "DJ", alpha3: "DJI", numeric: "262", locales: ["fr-DJ", "so-DJ"], default_locale: "fr-DJ", currency: "DJF", latitude: "11.825138", longitude: "42.590275", currency_name: "Djibouti Franc", languages: ["fr", "ar"], capital: "Djibouti", emoji: "🇩🇯", emojiU: "U+1F1E9 U+1F1EF", fips: "DJ", internet: "DJ", continent: "Africa", region: "Eastern Africa" }, { name: "Dominica", alpha2: "DM", alpha3: "DMA", numeric: "212", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "15.414999", longitude: "-61.370976", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Roseau", emoji: "🇩🇲", emojiU: "U+1F1E9 U+1F1F2", fips: "DO", internet: "DM", continent: "Americas", region: "West Indies" }, { name: "Dominican Republic", alpha2: "DO", alpha3: "DOM", numeric: "214", locales: ["es-DO"], default_locale: "es-DO", currency: "DOP", latitude: "18.735693", longitude: "-70.162651", currency_name: "Dominican Peso", languages: ["es"], capital: "Santo Domingo", emoji: "🇩🇴", emojiU: "U+1F1E9 U+1F1F4", fips: "DR", internet: "DO", continent: "Americas", region: "West Indies" }, { name: "Ecuador", alpha2: "EC", alpha3: "ECU", numeric: "218", locales: ["es-EC"], default_locale: "es-EC", currency: "USD", latitude: "-1.831239", longitude: "-78.183406", currency_name: "US Dollar", languages: ["es"], capital: "Quito", emoji: "🇪🇨", emojiU: "U+1F1EA U+1F1E8", fips: "EC", internet: "EC", continent: "Americas", region: "South America" }, { name: "Egypt", alpha2: "EG", alpha3: "EGY", numeric: "818", locales: ["ar-EG"], default_locale: "ar-EG", currency: "EGP", latitude: "26.820553", longitude: "30.802498", currency_name: "Egyptian Pound", languages: ["ar"], capital: "Cairo", emoji: "🇪🇬", emojiU: "U+1F1EA U+1F1EC", fips: "EG", internet: "EG", continent: "Africa", region: "Northern Africa" }, { name: "El Salvador", alpha2: "SV", alpha3: "SLV", numeric: "222", locales: ["es-SV"], default_locale: "es-SV", currency: "USD", latitude: "13.794185", longitude: "-88.89653", currency_name: "US Dollar", languages: ["es"], capital: "San Salvador", emoji: "🇸🇻", emojiU: "U+1F1F8 U+1F1FB", fips: "ES", internet: "SV", continent: "Americas", region: "Central America" }, { name: "Equatorial Guinea", alpha2: "GQ", alpha3: "GNQ", numeric: "226", locales: ["fr-GQ", "es-GQ"], default_locale: "fr-GQ", currency: "XAF", latitude: "1.650801", longitude: "10.267895", currency_name: "CFA Franc BEAC", languages: ["es", "fr"], capital: "Malabo", emoji: "🇬🇶", emojiU: "U+1F1EC U+1F1F6", fips: "EK", internet: "GQ", continent: "Africa", region: "Western Africa" }, { name: "Eritrea", alpha2: "ER", alpha3: "ERI", numeric: "232", locales: ["ti-ER"], default_locale: "ti-ER", currency: "ERN", latitude: "15.179384", longitude: "39.782334", currency_name: "Nakfa", languages: ["ti", "ar", "en"], capital: "Asmara", emoji: "🇪🇷", emojiU: "U+1F1EA U+1F1F7", fips: "ER", internet: "ER", continent: "Africa", region: "Eastern Africa" }, { name: "Estonia", alpha2: "EE", alpha3: "EST", numeric: "233", locales: ["et-EE"], default_locale: "et-EE", currency: "EUR", latitude: "58.595272", longitude: "25.013607", currency_name: "Euro", languages: ["et"], capital: "Tallinn", emoji: "🇪🇪", emojiU: "U+1F1EA U+1F1EA", fips: "EN", internet: "EE", continent: "Europe", region: "Eastern Europe" }, { name: "Eswatini", alpha2: "SZ", alpha3: "SWZ", numeric: "748", locales: ["en"], default_locale: "en", currency: "EUR", latitude: "-26.522503", longitude: "31.465866", currency_name: "Euro", languages: ["en", "ss"], capital: "Lobamba", emoji: "🇸🇿", emojiU: "U+1F1F8 U+1F1FF", fips: "WZ", internet: "SZ", continent: "Africa", region: "Southern Africa" }, { name: "Ethiopia", alpha2: "ET", alpha3: "ETH", numeric: "231", locales: ["am-ET", "om-ET", "so-ET", "ti-ET"], default_locale: "am-ET", currency: "ETB", latitude: "9.145", longitude: "40.489673", currency_name: "Ethiopian Birr", languages: ["am"], capital: "Addis Ababa", emoji: "🇪🇹", emojiU: "U+1F1EA U+1F1F9", fips: "ET", internet: "ET", continent: "Africa", region: "Eastern Africa" }, { name: "Falkland Islands", alpha2: "FK", alpha3: "FLK", numeric: "238", locales: ["en"], default_locale: "en", currency: "DKK", latitude: "-51.796253", longitude: "-59.523613", currency_name: "Danish Krone", languages: ["en"], capital: "Stanley", emoji: "🇫🇰", emojiU: "U+1F1EB U+1F1F0", fips: "FA", internet: "FK", continent: "Americas", region: "South America", alternate_names: ["Malvinas Falkland Islands"] }, { name: "Faroe Islands", alpha2: "FO", alpha3: "FRO", numeric: "234", locales: ["fo-FO"], default_locale: "fo-FO", currency: "DKK", latitude: "61.892635", longitude: "-6.911806", currency_name: "Danish Krone", languages: ["fo"], capital: "Tórshavn", emoji: "🇫🇴", emojiU: "U+1F1EB U+1F1F4", fips: "FO", internet: "FO", continent: "Europe", region: "Northern Europe" }, { name: "Fiji", alpha2: "FJ", alpha3: "FJI", numeric: "242", locales: ["en"], default_locale: "en", currency: "FJD", latitude: "-16.578193", longitude: "179.414413", currency_name: "Fiji Dollar", languages: ["en", "fj", "hi", "ur"], capital: "Suva", emoji: "🇫🇯", emojiU: "U+1F1EB U+1F1EF", fips: "FJ", internet: "FJ", continent: "Oceania", region: "Pacific" }, { name: "Finland", alpha2: "FI", alpha3: "FIN", numeric: "246", locales: ["fi-FI", "sv-FI"], default_locale: "fi-FI", currency: "EUR", latitude: "61.92411", longitude: "25.748151", currency_name: "Euro", languages: ["fi", "sv"], capital: "Helsinki", emoji: "🇫🇮", emojiU: "U+1F1EB U+1F1EE", fips: "FI", internet: "FI", continent: "Europe", region: "Northern Europe" }, { name: "France", alpha2: "FR", alpha3: "FRA", numeric: "250", locales: ["fr-FR"], default_locale: "fr-FR", currency: "EUR", latitude: "46.227638", longitude: "2.213749", currency_name: "Euro", languages: ["fr"], capital: "Paris", emoji: "🇫🇷", emojiU: "U+1F1EB U+1F1F7", fips: "FR", internet: "FR", continent: "Europe", region: "Western Europe" }, { name: "French Guiana", alpha2: "GF", alpha3: "GUF", numeric: "254", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "3.933889", longitude: "-53.125782", currency_name: "Euro", languages: ["fr"], capital: "Cayenne", emoji: "🇬🇫", emojiU: "U+1F1EC U+1F1EB", fips: "FG", internet: "GF", continent: "Americas", region: "South America" }, { name: "French Polynesia", alpha2: "PF", alpha3: "PYF", numeric: "258", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-17.679742", longitude: "-149.406843", currency_name: "CFP Franc", languages: ["fr"], capital: "Papeetē", emoji: "🇵🇫", emojiU: "U+1F1F5 U+1F1EB", fips: "FP", internet: "PF", continent: "Oceania", region: "Pacific" }, { name: "French Southern Territories", alpha2: "TF", alpha3: "ATF", numeric: "260", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-49.280366", longitude: "69.348557", currency_name: "Euro", languages: ["fr"], capital: "Port-aux-Français", emoji: "🇹🇫", emojiU: "U+1F1F9 U+1F1EB", fips: "FS", internet: "--", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Gabon", alpha2: "GA", alpha3: "GAB", numeric: "266", locales: ["fr-GA"], default_locale: "fr-GA", currency: "XAF", latitude: "-0.803689", longitude: "11.609444", currency_name: "CFA Franc BEAC", languages: ["fr"], capital: "Libreville", emoji: "🇬🇦", emojiU: "U+1F1EC U+1F1E6", fips: "GB", internet: "GA", continent: "Africa", region: "Western Africa" }, { name: "Gambia", alpha2: "GM", alpha3: "GMB", numeric: "270", locales: ["en"], default_locale: "en", currency: "GMD", latitude: "13.443182", longitude: "-15.310139", currency_name: "Dalasi", languages: ["en"], capital: "Banjul", emoji: "🇬🇲", emojiU: "U+1F1EC U+1F1F2", fips: "GA", internet: "GM", continent: "Africa", region: "Western Africa" }, { name: "Georgia", alpha2: "GE", alpha3: "GEO", numeric: "268", locales: ["ka-GE"], default_locale: "ka-GE", currency: "GEL", latitude: "42.315407", longitude: "43.356892", currency_name: "Lari", languages: ["ka"], capital: "Tbilisi", emoji: "🇬🇪", emojiU: "U+1F1EC U+1F1EA", fips: "GG", internet: "GE", continent: "Asia", region: "South West Asia" }, { name: "Germany", alpha2: "DE", alpha3: "DEU", numeric: "276", locales: ["de-DE"], default_locale: "de-DE", currency: "EUR", latitude: "51.165691", longitude: "10.451526", currency_name: "Euro", languages: ["de"], capital: "Berlin", emoji: "🇩🇪", emojiU: "U+1F1E9 U+1F1EA", fips: "GM", internet: "DE", continent: "Europe", region: "Western Europe" }, { name: "Ghana", alpha2: "GH", alpha3: "GHA", numeric: "288", locales: ["ak-GH", "ee-GH", "ha-Latn-GH"], default_locale: "ak-GH", currency: "GHS", latitude: "7.946527", longitude: "-1.023194", currency_name: "Ghana Cedi", languages: ["en"], capital: "Accra", emoji: "🇬🇭", emojiU: "U+1F1EC U+1F1ED", fips: "GH", internet: "GH", continent: "Africa", region: "Western Africa" }, { name: "Gibraltar", alpha2: "GI", alpha3: "GIB", numeric: "292", locales: ["en"], default_locale: "en", currency: "GIP", latitude: "36.137741", longitude: "-5.345374", currency_name: "Gibraltar Pound", languages: ["en"], capital: "Gibraltar", emoji: "🇬🇮", emojiU: "U+1F1EC U+1F1EE", fips: "GI", internet: "GI", continent: "Europe", region: "South West Europe" }, { name: "Greece", alpha2: "GR", alpha3: "GRC", numeric: "300", locales: ["el-GR"], default_locale: "el-GR", currency: "EUR", latitude: "39.074208", longitude: "21.824312", currency_name: "Euro", languages: ["el"], capital: "Athens", emoji: "🇬🇷", emojiU: "U+1F1EC U+1F1F7", fips: "GR", internet: "GR", continent: "Europe", region: "South East Europe" }, { name: "Greenland", alpha2: "GL", alpha3: "GRL", numeric: "304", locales: ["kl-GL"], default_locale: "kl-GL", currency: "DKK", latitude: "71.706936", longitude: "-42.604303", currency_name: "Danish Krone", languages: ["kl"], capital: "Nuuk", emoji: "🇬🇱", emojiU: "U+1F1EC U+1F1F1", fips: "GL", internet: "GL", continent: "Americas", region: "North America" }, { name: "Grenada", alpha2: "GD", alpha3: "GRD", numeric: "308", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.262776", longitude: "-61.604171", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "St. George's", emoji: "🇬🇩", emojiU: "U+1F1EC U+1F1E9", fips: "GJ", internet: "GD", continent: "Americas", region: "West Indies" }, { name: "Guadeloupe", alpha2: "GP", alpha3: "GLP", numeric: "312", locales: ["fr-GP"], default_locale: "fr-GP", currency: "EUR", latitude: "16.995971", longitude: "-62.067641", currency_name: "Euro", languages: ["fr"], capital: "Basse-Terre", emoji: "🇬🇵", emojiU: "U+1F1EC U+1F1F5", fips: "GP", internet: "GP", continent: "Americas", region: "West Indies" }, { name: "Guam", alpha2: "GU", alpha3: "GUM", numeric: "316", locales: ["en-GU"], default_locale: "en-GU", currency: "USD", latitude: "13.444304", longitude: "144.793731", currency_name: "US Dollar", languages: ["en", "ch", "es"], capital: "Hagåtña", emoji: "🇬🇺", emojiU: "U+1F1EC U+1F1FA", fips: "GQ", internet: "GU", continent: "Oceania", region: "Pacific" }, { name: "Guatemala", alpha2: "GT", alpha3: "GTM", numeric: "320", locales: ["es-GT"], default_locale: "es-GT", currency: "GTQ", latitude: "15.783471", longitude: "-90.230759", currency_name: "Quetzal", languages: ["es"], capital: "Guatemala City", emoji: "🇬🇹", emojiU: "U+1F1EC U+1F1F9", fips: "GT", internet: "GT", continent: "Americas", region: "Central America" }, { name: "Guernsey", alpha2: "GG", alpha3: "GGY", numeric: "831", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.465691", longitude: "-2.585278", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "St. Peter Port", emoji: "🇬🇬", emojiU: "U+1F1EC U+1F1EC", fips: "GK", internet: "GG", continent: "Europe", region: "Western Europe" }, { name: "Guinea", alpha2: "GN", alpha3: "GIN", numeric: "324", locales: ["fr-GN"], default_locale: "fr-GN", currency: "GNF", latitude: "9.945587", longitude: "-9.696645", currency_name: "Guinean Franc", languages: ["fr", "ff"], capital: "Conakry", emoji: "🇬🇳", emojiU: "U+1F1EC U+1F1F3", fips: "GV", internet: "GN", continent: "Africa", region: "Western Africa" }, { name: "Guinea-Bissau", alpha2: "GW", alpha3: "GNB", numeric: "624", locales: ["pt-GW"], default_locale: "pt-GW", currency: "XOF", latitude: "11.803749", longitude: "-15.180413", currency_name: "CFA Franc BCEAO", languages: ["pt"], capital: "Bissau", emoji: "🇬🇼", emojiU: "U+1F1EC U+1F1FC", fips: "PU", internet: "GW", continent: "Africa", region: "Western Africa" }, { name: "Guyana", alpha2: "GY", alpha3: "GUY", numeric: "328", locales: ["en"], default_locale: "en", currency: "GYD", latitude: "4.860416", longitude: "-58.93018", currency_name: "Guyana Dollar", languages: ["en"], capital: "Georgetown", emoji: "🇬🇾", emojiU: "U+1F1EC U+1F1FE", fips: "GY", internet: "GY", continent: "Americas", region: "South America" }, { name: "Haiti", alpha2: "HT", alpha3: "HTI", numeric: "332", locales: ["fr"], default_locale: "fr", currency: "USD", latitude: "18.971187", longitude: "-72.285215", currency_name: "US Dollar", languages: ["fr", "ht"], capital: "Port-au-Prince", emoji: "🇭🇹", emojiU: "U+1F1ED U+1F1F9", fips: "HA", internet: "HT", continent: "Americas", region: "West Indies" }, { name: "Heard Island and McDonald Islands", alpha2: "HM", alpha3: "HMD", numeric: "334", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-53.08181", longitude: "73.504158", currency_name: "Australian Dollar", languages: ["en"], capital: "", emoji: "🇭🇲", emojiU: "U+1F1ED U+1F1F2", fips: "HM", internet: "HM", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Holy See", alpha2: "VA", alpha3: "VAT", numeric: "336", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "41.902916", longitude: "12.453389", currency_name: "Euro", languages: ["it", "la"], capital: "Vatican City", emoji: "🇻🇦", emojiU: "U+1F1FB U+1F1E6", fips: "VT", internet: "VA", continent: "Europe", region: "Southern Europe" }, { name: "Honduras", alpha2: "HN", alpha3: "HND", numeric: "340", locales: ["es-HN"], default_locale: "es-HN", currency: "HNL", latitude: "15.199999", longitude: "-86.241905", currency_name: "Lempira", languages: ["es"], capital: "Tegucigalpa", emoji: "🇭🇳", emojiU: "U+1F1ED U+1F1F3", fips: "HO", internet: "HN", continent: "Americas", region: "Central America" }, { name: "Hong Kong", alpha2: "HK", alpha3: "HKG", numeric: "344", locales: ["yue-Hant-HK", "zh-Hans-HK", "zh-Hant-HK", "en-HK"], default_locale: "en-HK", currency: "HKD", latitude: "22.396428", longitude: "114.109497", currency_name: "Hong Kong Dollar", languages: ["zh", "en"], capital: "City of Victoria", emoji: "🇭🇰", emojiU: "U+1F1ED U+1F1F0", fips: "HK", internet: "HK", continent: "Asia", region: "East Asia" }, { name: "Hungary", alpha2: "HU", alpha3: "HUN", numeric: "348", locales: ["hu-HU"], default_locale: "hu-HU", currency: "HUF", latitude: "47.162494", longitude: "19.503304", currency_name: "Forint", languages: ["hu"], capital: "Budapest", emoji: "🇭🇺", emojiU: "U+1F1ED U+1F1FA", fips: "HU", internet: "HU", continent: "Europe", region: "Central Europe" }, { name: "Iceland", alpha2: "IS", alpha3: "ISL", numeric: "352", locales: ["is-IS"], default_locale: "is-IS", currency: "ISK", latitude: "64.963051", longitude: "-19.020835", currency_name: "Iceland Krona", languages: ["is"], capital: "Reykjavik", emoji: "🇮🇸", emojiU: "U+1F1EE U+1F1F8", fips: "IC", internet: "IS", continent: "Europe", region: "Northern Europe" }, { name: "India", alpha2: "IN", alpha3: "IND", numeric: "356", locales: ["as-IN", "bn-IN", "en-IN", "gu-IN", "hi-IN", "kn-IN", "kok-IN", "ml-IN", "mr-IN", "ne-IN", "or-IN", "pa-Guru-IN", "ta-IN", "te-IN", "bo-IN", "ur-IN"], default_locale: "hi-IN", currency: "INR", latitude: "20.593684", longitude: "78.96288", currency_name: "Indian Rupee", languages: ["hi", "en"], capital: "New Delhi", emoji: "🇮🇳", emojiU: "U+1F1EE U+1F1F3", fips: "IN", internet: "IN", continent: "Asia", region: "South Asia" }, { name: "Indonesia", alpha2: "ID", alpha3: "IDN", numeric: "360", locales: ["id-ID"], default_locale: "id-ID", currency: "IDR", latitude: "-0.789275", longitude: "113.921327", currency_name: "Rupiah", languages: ["id"], capital: "Jakarta", emoji: "🇮🇩", emojiU: "U+1F1EE U+1F1E9", fips: "ID", internet: "ID", continent: "Asia", region: "South East Asia" }, { name: "Iran", alpha2: "IR", alpha3: "IRN", numeric: "364", locales: ["fa-IR"], default_locale: "fa-IR", currency: "XDR", latitude: "32.427908", longitude: "53.688046", currency_name: "SDR (Special Drawing Right)", languages: ["fa"], capital: "Tehran", emoji: "🇮🇷", emojiU: "U+1F1EE U+1F1F7", fips: "IR", internet: "IR", continent: "Asia", region: "South West Asia", alternate_names: ["Islamic Republic of Iran"] }, { name: "Iraq", alpha2: "IQ", alpha3: "IRQ", numeric: "368", locales: ["ar-IQ"], default_locale: "ar-IQ", currency: "IQD", latitude: "33.223191", longitude: "43.679291", currency_name: "Iraqi Dinar", languages: ["ar", "ku"], capital: "Baghdad", emoji: "🇮🇶", emojiU: "U+1F1EE U+1F1F6", fips: "IZ", internet: "IQ", continent: "Asia", region: "South West Asia" }, { name: "Ireland", alpha2: "IE", alpha3: "IRL", numeric: "372", locales: ["en-IE", "ga-IE"], default_locale: "en-IE", currency: "EUR", latitude: "53.41291", longitude: "-8.24389", currency_name: "Euro", languages: ["ga", "en"], capital: "Dublin", emoji: "🇮🇪", emojiU: "U+1F1EE U+1F1EA", fips: "EI", internet: "IE", continent: "Europe", region: "Western Europe" }, { name: "Isle of Man", alpha2: "IM", alpha3: "IMN", numeric: "833", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "54.236107", longitude: "-4.548056", currency_name: "Pound Sterling", languages: ["en", "gv"], capital: "Douglas", emoji: "🇮🇲", emojiU: "U+1F1EE U+1F1F2", fips: "IM", internet: "IM", continent: "Europe", region: "Western Europe" }, { name: "Israel", alpha2: "IL", alpha3: "ISR", numeric: "376", locales: ["en-IL", "he-IL"], default_locale: "he-IL", currency: "ILS", latitude: "31.046051", longitude: "34.851612", currency_name: "New Israeli Sheqel", languages: ["he", "ar"], capital: "Jerusalem", emoji: "🇮🇱", emojiU: "U+1F1EE U+1F1F1", fips: "IS", internet: "IL", continent: "Asia", region: "South West Asia" }, { name: "Italy", alpha2: "IT", alpha3: "ITA", numeric: "380", locales: ["it-IT"], default_locale: "it-IT", currency: "EUR", latitude: "41.87194", longitude: "12.56738", currency_name: "Euro", languages: ["it"], capital: "Rome", emoji: "🇮🇹", emojiU: "U+1F1EE U+1F1F9", fips: "IT", internet: "IT", continent: "Europe", region: "Southern Europe" }, { name: "Jamaica", alpha2: "JM", alpha3: "JAM", numeric: "388", locales: ["en-JM"], default_locale: "en-JM", currency: "JMD", latitude: "18.109581", longitude: "-77.297508", currency_name: "Jamaican Dollar", languages: ["en"], capital: "Kingston", emoji: "🇯🇲", emojiU: "U+1F1EF U+1F1F2", fips: "JM", internet: "JM", continent: "Americas", region: "West Indies" }, { name: "Japan", alpha2: "JP", alpha3: "JPN", numeric: "392", locales: ["ja-JP"], default_locale: "ja-JP", currency: "JPY", latitude: "36.204824", longitude: "138.252924", currency_name: "Yen", languages: ["ja"], capital: "Tokyo", emoji: "🇯🇵", emojiU: "U+1F1EF U+1F1F5", fips: "JA", internet: "JP", continent: "Asia", region: "East Asia" }, { name: "Jersey", alpha2: "JE", alpha3: "JEY", numeric: "832", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.214439", longitude: "-2.13125", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "Saint Helier", emoji: "🇯🇪", emojiU: "U+1F1EF U+1F1EA", fips: "JE", internet: "JE", continent: "Europe", region: "Western Europe" }, { name: "Jordan", alpha2: "JO", alpha3: "JOR", numeric: "400", locales: ["ar-JO"], default_locale: "ar-JO", currency: "JOD", latitude: "30.585164", longitude: "36.238414", currency_name: "Jordanian Dinar", languages: ["ar"], capital: "Amman", emoji: "🇯🇴", emojiU: "U+1F1EF U+1F1F4", fips: "JO", internet: "JO", continent: "Asia", region: "South West Asia" }, { name: "Kazakhstan", alpha2: "KZ", alpha3: "KAZ", numeric: "398", locales: ["kk-Cyrl-KZ"], default_locale: "kk-Cyrl-KZ", currency: "KZT", latitude: "48.019573", longitude: "66.923684", currency_name: "Tenge", languages: ["kk", "ru"], capital: "Astana", emoji: "🇰🇿", emojiU: "U+1F1F0 U+1F1FF", fips: "KZ", internet: "KZ", continent: "Asia", region: "Central Asia" }, { name: "Kenya", alpha2: "KE", alpha3: "KEN", numeric: "404", locales: ["ebu-KE", "guz-KE", "kln-KE", "kam-KE", "ki-KE", "luo-KE", "luy-KE", "mas-KE", "mer-KE", "om-KE", "saq-KE", "so-KE", "sw-KE", "dav-KE", "teo-KE"], default_locale: "ebu-KE", currency: "KES", latitude: "-0.023559", longitude: "37.906193", currency_name: "Kenyan Shilling", languages: ["en", "sw"], capital: "Nairobi", emoji: "🇰🇪", emojiU: "U+1F1F0 U+1F1EA", fips: "KE", internet: "KE", continent: "Africa", region: "Eastern Africa" }, { name: "Kiribati", alpha2: "KI", alpha3: "KIR", numeric: "296", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-3.370417", longitude: "-168.734039", currency_name: "Australian Dollar", languages: ["en"], capital: "South Tarawa", emoji: "🇰🇮", emojiU: "U+1F1F0 U+1F1EE", fips: "KR", internet: "KI", continent: "Oceania", region: "Pacific" }, { name: "North Korea", alpha2: "KP", alpha3: "PRK", numeric: "408", locales: ["ko"], default_locale: "ko", currency: "KPW", latitude: "40.339852", longitude: "127.510093", currency_name: "North Korean Won", languages: ["ko"], capital: "Pyongyang", emoji: "🇰🇵", emojiU: "U+1F1F0 U+1F1F5", fips: "KN", internet: "KP", continent: "Asia", region: "East Asia", alternate_names: ["Democratic People's Republic of Korea"] }, { name: "South Korea", alpha2: "KR", alpha3: "KOR", numeric: "410", locales: ["ko-KR"], default_locale: "ko-KR", currency: "KRW", latitude: "35.907757", longitude: "127.766922", currency_name: "Won", languages: ["ko"], capital: "Seoul", emoji: "🇰🇷", emojiU: "U+1F1F0 U+1F1F7", fips: "KS", internet: "KR", continent: "Asia", region: "East Asia", alternate_names: ["Republic of Korea"] }, { name: "Kuwait", alpha2: "KW", alpha3: "KWT", numeric: "414", locales: ["ar-KW"], default_locale: "ar-KW", currency: "KWD", latitude: "29.31166", longitude: "47.481766", currency_name: "Kuwaiti Dinar", languages: ["ar"], capital: "Kuwait City", emoji: "🇰🇼", emojiU: "U+1F1F0 U+1F1FC", fips: "KU", internet: "KW", continent: "Asia", region: "South West Asia" }, { name: "Kyrgyzstan", alpha2: "KG", alpha3: "KGZ", numeric: "417", locales: ["ky"], default_locale: "ky", currency: "KGS", latitude: "41.20438", longitude: "74.766098", currency_name: "Som", languages: ["ky", "ru"], capital: "Bishkek", emoji: "🇰🇬", emojiU: "U+1F1F0 U+1F1EC", fips: "KG", internet: "KG", continent: "Asia", region: "Central Asia" }, { name: "Lao People's Democratic Republic", alpha2: "LA", alpha3: "LAO", numeric: "418", locales: ["lo"], default_locale: "lo", currency: "LAK", latitude: "19.85627", longitude: "102.495496", currency_name: "Lao Kip", languages: ["lo"], capital: "Vientiane", emoji: "🇱🇦", emojiU: "U+1F1F1 U+1F1E6", fips: "LA", internet: "LA", continent: "Asia", region: "South East Asia" }, { name: "Latvia", alpha2: "LV", alpha3: "LVA", numeric: "428", locales: ["lv-LV"], default_locale: "lv-LV", currency: "EUR", latitude: "56.879635", longitude: "24.603189", currency_name: "Euro", languages: ["lv"], capital: "Riga", emoji: "🇱🇻", emojiU: "U+1F1F1 U+1F1FB", fips: "LG", internet: "LV", continent: "Europe", region: "Eastern Europe" }, { name: "Lebanon", alpha2: "LB", alpha3: "LBN", numeric: "422", locales: ["ar-LB"], default_locale: "ar-LB", currency: "LBP", latitude: "33.854721", longitude: "35.862285", currency_name: "Lebanese Pound", languages: ["ar", "fr"], capital: "Beirut", emoji: "🇱🇧", emojiU: "U+1F1F1 U+1F1E7", fips: "LE", internet: "LB", continent: "Asia", region: "South West Asia" }, { name: "Lesotho", alpha2: "LS", alpha3: "LSO", numeric: "426", locales: ["en"], default_locale: "en", currency: "ZAR", latitude: "-29.609988", longitude: "28.233608", currency_name: "Rand", languages: ["en", "st"], capital: "Maseru", emoji: "🇱🇸", emojiU: "U+1F1F1 U+1F1F8", fips: "LT", internet: "LS", continent: "Africa", region: "Southern Africa" }, { name: "Liberia", alpha2: "LR", alpha3: "LBR", numeric: "430", locales: ["en"], default_locale: "en", currency: "LRD", latitude: "6.428055", longitude: "-9.429499", currency_name: "Liberian Dollar", languages: ["en"], capital: "Monrovia", emoji: "🇱🇷", emojiU: "U+1F1F1 U+1F1F7", fips: "LI", internet: "LR", continent: "Africa", region: "Western Africa" }, { name: "Libya", alpha2: "LY", alpha3: "LBY", numeric: "434", locales: ["ar-LY"], default_locale: "ar-LY", currency: "LYD", latitude: "26.3351", longitude: "17.228331", currency_name: "Libyan Dinar", languages: ["ar"], capital: "Tripoli", emoji: "🇱🇾", emojiU: "U+1F1F1 U+1F1FE", fips: "LY", internet: "LY", continent: "Africa", region: "Northern Africa" }, { name: "Liechtenstein", alpha2: "LI", alpha3: "LIE", numeric: "438", locales: ["de-LI"], default_locale: "de-LI", currency: "CHF", latitude: "47.166", longitude: "9.555373", currency_name: "Swiss Franc", languages: ["de"], capital: "Vaduz", emoji: "🇱🇮", emojiU: "U+1F1F1 U+1F1EE", fips: "LS", internet: "LI", continent: "Europe", region: "Central Europe" }, { name: "Lithuania", alpha2: "LT", alpha3: "LTU", numeric: "440", locales: ["lt-LT"], default_locale: "lt-LT", currency: "EUR", latitude: "55.169438", longitude: "23.881275", currency_name: "Euro", languages: ["lt"], capital: "Vilnius", emoji: "🇱🇹", emojiU: "U+1F1F1 U+1F1F9", fips: "LH", internet: "LT", continent: "Europe", region: "Eastern Europe" }, { name: "Luxembourg", alpha2: "LU", alpha3: "LUX", numeric: "442", locales: ["fr-LU", "de-LU"], default_locale: "fr-LU", currency: "EUR", latitude: "49.815273", longitude: "6.129583", currency_name: "Euro", languages: ["fr", "de", "lb"], capital: "Luxembourg", emoji: "🇱🇺", emojiU: "U+1F1F1 U+1F1FA", fips: "LU", internet: "LU", continent: "Europe", region: "Western Europe" }, { name: "Macao", alpha2: "MO", alpha3: "MAC", numeric: "446", locales: ["zh-Hans-MO", "zh-Hant-MO"], default_locale: "zh-Hans-MO", currency: "MOP", latitude: "22.198745", longitude: "113.543873", currency_name: "Pataca", languages: ["zh", "pt"], capital: "", emoji: "🇲🇴", emojiU: "U+1F1F2 U+1F1F4", fips: "MC", internet: "MO", continent: "Asia", region: "East Asia" }, { name: "Madagascar", alpha2: "MG", alpha3: "MDG", numeric: "450", locales: ["fr-MG", "mg-MG"], default_locale: "fr-MG", currency: "MGA", latitude: "-18.766947", longitude: "46.869107", currency_name: "Malagasy Ariary", languages: ["fr", "mg"], capital: "Antananarivo", emoji: "🇲🇬", emojiU: "U+1F1F2 U+1F1EC", fips: "MA", internet: "MG", continent: "Africa", region: "Indian Ocean" }, { name: "Malawi", alpha2: "MW", alpha3: "MWI", numeric: "454", locales: ["en"], default_locale: "en", currency: "MWK", latitude: "-13.254308", longitude: "34.301525", currency_name: "Malawi Kwacha", languages: ["en", "ny"], capital: "Lilongwe", emoji: "🇲🇼", emojiU: "U+1F1F2 U+1F1FC", fips: "MI", internet: "MW", continent: "Africa", region: "Southern Africa" }, { name: "Malaysia", alpha2: "MY", alpha3: "MYS", numeric: "458", locales: ["ms-MY"], default_locale: "ms-MY", currency: "MYR", latitude: "4.210484", longitude: "101.975766", currency_name: "Malaysian Ringgit", languages: ["ms"], capital: "Kuala Lumpur", emoji: "🇲🇾", emojiU: "U+1F1F2 U+1F1FE", fips: "MY", internet: "MY", continent: "Asia", region: "South East Asia" }, { name: "Maldives", alpha2: "MV", alpha3: "MDV", numeric: "462", locales: ["dv"], default_locale: "dv", currency: "MVR", latitude: "3.202778", longitude: "73.22068", currency_name: "Rufiyaa", languages: ["dv"], capital: "Malé", emoji: "🇲🇻", emojiU: "U+1F1F2 U+1F1FB", fips: "MV", internet: "MV", continent: "Asia", region: "South Asia" }, { name: "Mali", alpha2: "ML", alpha3: "MLI", numeric: "466", locales: ["bm-ML", "fr-ML", "khq-ML", "ses-ML"], default_locale: "fr-ML", currency: "XOF", latitude: "17.570692", longitude: "-3.996166", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Bamako", emoji: "🇲🇱", emojiU: "U+1F1F2 U+1F1F1", fips: "ML", internet: "ML", continent: "Africa", region: "Western Africa" }, { name: "Malta", alpha2: "MT", alpha3: "MLT", numeric: "470", locales: ["en-MT", "mt-MT"], default_locale: "en-MT", currency: "EUR", latitude: "35.937496", longitude: "14.375416", currency_name: "Euro", languages: ["mt", "en"], capital: "Valletta", emoji: "🇲🇹", emojiU: "U+1F1F2 U+1F1F9", fips: "MT", internet: "MT", continent: "Europe", region: "Southern Europe" }, { name: "Marshall Islands", alpha2: "MH", alpha3: "MHL", numeric: "584", locales: ["en-MH"], default_locale: "en-MH", currency: "USD", latitude: "7.131474", longitude: "171.184478", currency_name: "US Dollar", languages: ["en", "mh"], capital: "Majuro", emoji: "🇲🇭", emojiU: "U+1F1F2 U+1F1ED", fips: "RM", internet: "MH", continent: "Oceania", region: "Pacific" }, { name: "Martinique", alpha2: "MQ", alpha3: "MTQ", numeric: "474", locales: ["fr-MQ"], default_locale: "fr-MQ", currency: "EUR", latitude: "14.641528", longitude: "-61.024174", currency_name: "Euro", languages: ["fr"], capital: "Fort-de-France", emoji: "🇲🇶", emojiU: "U+1F1F2 U+1F1F6", fips: "MB", internet: "MQ", continent: "Americas", region: "West Indies" }, { name: "Mauritania", alpha2: "MR", alpha3: "MRT", numeric: "478", locales: ["ar"], default_locale: "ar", currency: "MRU", latitude: "21.00789", longitude: "-10.940835", currency_name: "Ouguiya", languages: ["ar"], capital: "Nouakchott", emoji: "🇲🇷", emojiU: "U+1F1F2 U+1F1F7", fips: "MR", internet: "MR", continent: "Africa", region: "Western Africa" }, { name: "Mauritius", alpha2: "MU", alpha3: "MUS", numeric: "480", locales: ["en-MU", "mfe-MU"], default_locale: "en-MU", currency: "MUR", latitude: "-20.348404", longitude: "57.552152", currency_name: "Mauritius Rupee", languages: ["en"], capital: "Port Louis", emoji: "🇲🇺", emojiU: "U+1F1F2 U+1F1FA", fips: "MP", internet: "MU", continent: "Africa", region: "Indian Ocean" }, { name: "Mayotte", alpha2: "YT", alpha3: "MYT", numeric: "175", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-12.8275", longitude: "45.166244", currency_name: "Euro", languages: ["fr"], capital: "Mamoudzou", emoji: "🇾🇹", emojiU: "U+1F1FE U+1F1F9", fips: "MF", internet: "YT", continent: "Africa", region: "Indian Ocean" }, { name: "Mexico", alpha2: "MX", alpha3: "MEX", numeric: "484", locales: ["es-MX"], default_locale: "es-MX", currency: "MXN", latitude: "23.634501", longitude: "-102.552784", currency_name: "Mexican Peso", languages: ["es"], capital: "Mexico City", emoji: "🇲🇽", emojiU: "U+1F1F2 U+1F1FD", fips: "MX", internet: "MX", continent: "Americas", region: "Central America" }, { name: "Micronesia", alpha2: "FM", alpha3: "FSM", numeric: "583", locales: ["en"], default_locale: "en", currency: "RUB", latitude: "7.425554", longitude: "150.550812", currency_name: "Russian Ruble", languages: ["en"], capital: "Palikir", emoji: "🇫🇲", emojiU: "U+1F1EB U+1F1F2", fips: "", internet: "FM", continent: "Oceania", region: "Pacific", alternate_names: ["Federated States of Micronesia"] }, { name: "Moldova", alpha2: "MD", alpha3: "MDA", numeric: "498", locales: ["ro-MD", "ru-MD"], default_locale: "ro-MD", currency: "MDL", latitude: "47.411631", longitude: "28.369885", currency_name: "Moldovan Leu", languages: ["ro"], capital: "Chișinău", emoji: "🇲🇩", emojiU: "U+1F1F2 U+1F1E9", fips: "MD", internet: "MD", continent: "Europe", region: "Eastern Europe", alternate_names: ["Republic of Moldova"] }, { name: "Monaco", alpha2: "MC", alpha3: "MCO", numeric: "492", locales: ["fr-MC"], default_locale: "fr-MC", currency: "EUR", latitude: "43.750298", longitude: "7.412841", currency_name: "Euro", languages: ["fr"], capital: "Monaco", emoji: "🇲🇨", emojiU: "U+1F1F2 U+1F1E8", fips: "MN", internet: "MC", continent: "Europe", region: "Western Europe" }, { name: "Mongolia", alpha2: "MN", alpha3: "MNG", numeric: "496", locales: ["mn"], default_locale: "mn", currency: "MNT", latitude: "46.862496", longitude: "103.846656", currency_name: "Tugrik", languages: ["mn"], capital: "Ulan Bator", emoji: "🇲🇳", emojiU: "U+1F1F2 U+1F1F3", fips: "MG", internet: "MN", continent: "Asia", region: "Northern Asia" }, { name: "Montenegro", alpha2: "ME", alpha3: "MNE", numeric: "499", locales: ["sr-Cyrl-ME", "sr-Latn-ME"], default_locale: "sr-Cyrl-ME", currency: "EUR", latitude: "42.708678", longitude: "19.37439", currency_name: "Euro", languages: ["sr", "bs", "sq", "hr"], capital: "Podgorica", emoji: "🇲🇪", emojiU: "U+1F1F2 U+1F1EA", fips: "MJ", internet: "ME", continent: "Europe", region: "South East Europe" }, { name: "Montserrat", alpha2: "MS", alpha3: "MSR", numeric: "500", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "16.742498", longitude: "-62.187366", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Plymouth", emoji: "🇲🇸", emojiU: "U+1F1F2 U+1F1F8", fips: "MH", internet: "MS", continent: "Americas", region: "West Indies" }, { name: "Morocco", alpha2: "MA", alpha3: "MAR", numeric: "504", locales: ["ar-MA", "tzm-Latn-MA", "shi-Latn-MA", "shi-Tfng-MA"], default_locale: "ar-MA", currency: "MAD", latitude: "31.791702", longitude: "-7.09262", currency_name: "Moroccan Dirham", languages: ["ar"], capital: "Rabat", emoji: "🇲🇦", emojiU: "U+1F1F2 U+1F1E6", fips: "MO", internet: "MA", continent: "Africa", region: "Northern Africa" }, { name: "Mozambique", alpha2: "MZ", alpha3: "MOZ", numeric: "508", locales: ["pt-MZ", "seh-MZ"], default_locale: "pt-MZ", currency: "MZN", latitude: "-18.665695", longitude: "35.529562", currency_name: "Mozambique Metical", languages: ["pt"], capital: "Maputo", emoji: "🇲🇿", emojiU: "U+1F1F2 U+1F1FF", fips: "MZ", internet: "MZ", continent: "Africa", region: "Southern Africa" }, { name: "Myanmar", alpha2: "MM", alpha3: "MMR", numeric: "104", locales: ["my-MM"], default_locale: "my-MM", currency: "MMK", latitude: "21.913965", longitude: "95.956223", currency_name: "Kyat", languages: ["my"], capital: "Naypyidaw", emoji: "🇲🇲", emojiU: "U+1F1F2 U+1F1F2", fips: "BM", internet: "MM", continent: "Asia", region: "South East Asia" }, { name: "Namibia", alpha2: "NA", alpha3: "NAM", numeric: "516", locales: ["af-NA", "en-NA", "naq-NA"], default_locale: "en-NA", currency: "ZAR", latitude: "-22.95764", longitude: "18.49041", currency_name: "Rand", languages: ["en", "af"], capital: "Windhoek", emoji: "🇳🇦", emojiU: "U+1F1F3 U+1F1E6", fips: "WA", internet: "NA", continent: "Africa", region: "Southern Africa" }, { name: "Nauru", alpha2: "NR", alpha3: "NRU", numeric: "520", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-0.522778", longitude: "166.931503", currency_name: "Australian Dollar", languages: ["en", "na"], capital: "Yaren", emoji: "🇳🇷", emojiU: "U+1F1F3 U+1F1F7", fips: "NR", internet: "NR", continent: "Oceania", region: "Pacific" }, { name: "Nepal", alpha2: "NP", alpha3: "NPL", numeric: "524", locales: ["ne-NP"], default_locale: "ne-NP", currency: "NPR", latitude: "28.394857", longitude: "84.124008", currency_name: "Nepalese Rupee", languages: ["ne"], capital: "Kathmandu", emoji: "🇳🇵", emojiU: "U+1F1F3 U+1F1F5", fips: "NP", internet: "NP", continent: "Asia", region: "South Asia" }, { name: "Netherlands", alpha2: "NL", alpha3: "NLD", numeric: "528", locales: ["nl-NL"], default_locale: "nl-NL", currency: "EUR", latitude: "52.132633", longitude: "5.291266", currency_name: "Euro", languages: ["nl"], capital: "Amsterdam", emoji: "🇳🇱", emojiU: "U+1F1F3 U+1F1F1", fips: "NL", internet: "NL", continent: "Europe", region: "Western Europe" }, { name: "Netherlands Antilles", alpha2: "AN", alpha3: "ANT", numeric: "530", locales: ["nl-AN"], default_locale: "nl-AN", currency: "ANG", latitude: "12.226079", longitude: "-69.060087", currency_name: "Netherlands Antillean Guilder", fips: "NT", internet: "AN", continent: "Americas", region: "West Indies" }, { name: "New Caledonia", alpha2: "NC", alpha3: "NCL", numeric: "540", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-20.904305", longitude: "165.618042", currency_name: "CFP Franc", languages: ["fr"], capital: "Nouméa", emoji: "🇳🇨", emojiU: "U+1F1F3 U+1F1E8", fips: "NC", internet: "NC", continent: "Oceania", region: "Pacific" }, { name: "New Zealand", alpha2: "NZ", alpha3: "NZL", numeric: "554", locales: ["en-NZ"], default_locale: "en-NZ", currency: "NZD", latitude: "-40.900557", longitude: "174.885971", currency_name: "New Zealand Dollar", languages: ["en", "mi"], capital: "Wellington", emoji: "🇳🇿", emojiU: "U+1F1F3 U+1F1FF", fips: "NZ", internet: "NZ", continent: "Oceania", region: "Pacific" }, { name: "Nicaragua", alpha2: "NI", alpha3: "NIC", numeric: "558", locales: ["es-NI"], default_locale: "es-NI", currency: "NIO", latitude: "12.865416", longitude: "-85.207229", currency_name: "Cordoba Oro", languages: ["es"], capital: "Managua", emoji: "🇳🇮", emojiU: "U+1F1F3 U+1F1EE", fips: "NU", internet: "NI", continent: "Americas", region: "Central America" }, { name: "Niger", alpha2: "NE", alpha3: "NER", numeric: "562", locales: ["fr-NE", "ha-Latn-NE"], default_locale: "fr-NE", currency: "XOF", latitude: "17.607789", longitude: "8.081666", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Niamey", emoji: "🇳🇪", emojiU: "U+1F1F3 U+1F1EA", fips: "NG", internet: "NE", continent: "Africa", region: "Western Africa" }, { name: "Nigeria", alpha2: "NG", alpha3: "NGA", numeric: "566", locales: ["ha-Latn-NG", "ig-NG", "yo-NG"], default_locale: "ha-Latn-NG", currency: "NGN", latitude: "9.081999", longitude: "8.675277", currency_name: "Naira", languages: ["en"], capital: "Abuja", emoji: "🇳🇬", emojiU: "U+1F1F3 U+1F1EC", fips: "NI", internet: "NG", continent: "Africa", region: "Western Africa" }, { name: "Niue", alpha2: "NU", alpha3: "NIU", numeric: "570", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-19.054445", longitude: "-169.867233", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Alofi", emoji: "🇳🇺", emojiU: "U+1F1F3 U+1F1FA", fips: "NE", internet: "NU", continent: "Oceania", region: "Pacific" }, { name: "Norfolk Island", alpha2: "NF", alpha3: "NFK", numeric: "574", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-29.040835", longitude: "167.954712", currency_name: "Australian Dollar", languages: ["en"], capital: "Kingston", emoji: "🇳🇫", emojiU: "U+1F1F3 U+1F1EB", fips: "NF", internet: "NF", continent: "Oceania", region: "Pacific" }, { name: "North Macedonia", alpha2: "MK", alpha3: "MKD", numeric: "807", locales: ["mk-MK"], default_locale: "mk-MK", currency: "MKD", latitude: "41.608635", longitude: "21.745275", currency_name: "Denar", languages: ["mk"], capital: "Skopje", emoji: "🇲🇰", emojiU: "U+1F1F2 U+1F1F0", fips: "MK", internet: "MK", continent: "Europe", region: "South East Europe" }, { name: "Northern Mariana Islands", alpha2: "MP", alpha3: "MNP", numeric: "580", locales: ["en-MP"], default_locale: "en-MP", currency: "USD", latitude: "17.33083", longitude: "145.38469", currency_name: "US Dollar", languages: ["en", "ch"], capital: "Saipan", emoji: "🇲🇵", emojiU: "U+1F1F2 U+1F1F5", fips: "CQ", internet: "MP", continent: "Oceania", region: "Pacific" }, { name: "Norway", alpha2: "NO", alpha3: "NOR", numeric: "578", locales: ["nb-NO", "nn-NO"], default_locale: "nb-NO", currency: "NOK", latitude: "60.472024", longitude: "8.468946", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "Oslo", emoji: "🇳🇴", emojiU: "U+1F1F3 U+1F1F4", fips: "NO", internet: "NO", continent: "Europe", region: "Northern Europe" }, { name: "Oman", alpha2: "OM", alpha3: "OMN", numeric: "512", locales: ["ar-OM"], default_locale: "ar-OM", currency: "OMR", latitude: "21.512583", longitude: "55.923255", currency_name: "Rial Omani", languages: ["ar"], capital: "Muscat", emoji: "🇴🇲", emojiU: "U+1F1F4 U+1F1F2", fips: "MU", internet: "OM", continent: "Asia", region: "South West Asia" }, { name: "Pakistan", alpha2: "PK", alpha3: "PAK", numeric: "586", locales: ["en-PK", "pa-Arab-PK", "ur-PK"], default_locale: "en-PK", currency: "PKR", latitude: "30.375321", longitude: "69.345116", currency_name: "Pakistan Rupee", languages: ["en", "ur"], capital: "Islamabad", emoji: "🇵🇰", emojiU: "U+1F1F5 U+1F1F0", fips: "PK", internet: "PK", continent: "Asia", region: "South Asia" }, { name: "Palau", alpha2: "PW", alpha3: "PLW", numeric: "585", locales: ["en"], default_locale: "en", currency: "USD", latitude: "7.51498", longitude: "134.58252", currency_name: "US Dollar", languages: ["en"], capital: "Ngerulmud", emoji: "🇵🇼", emojiU: "U+1F1F5 U+1F1FC", fips: "PS", internet: "PW", continent: "Oceania", region: "Pacific" }, { name: "Palestine", alpha2: "PS", alpha3: "PSE", numeric: "275", locales: ["ar"], default_locale: "ar", currency: "USD", latitude: "31.952162", longitude: "35.233154", currency_name: "US Dollar", languages: ["ar"], capital: "Ramallah", emoji: "🇵🇸", emojiU: "U+1F1F5 U+1F1F8", fips: "WE", internet: "PS", continent: "Asia", region: "South West Asia", alternate_names: ["State of Palestine"] }, { name: "Panama", alpha2: "PA", alpha3: "PAN", numeric: "591", locales: ["es-PA"], default_locale: "es-PA", currency: "USD", latitude: "8.537981", longitude: "-80.782127", currency_name: "US Dollar", languages: ["es"], capital: "Panama City", emoji: "🇵🇦", emojiU: "U+1F1F5 U+1F1E6", fips: "PM", internet: "PA", continent: "Americas", region: "Central America" }, { name: "Papua New Guinea", alpha2: "PG", alpha3: "PNG", numeric: "598", locales: ["en"], default_locale: "en", currency: "PGK", latitude: "-6.314993", longitude: "143.95555", currency_name: "Kina", languages: ["en"], capital: "Port Moresby", emoji: "🇵🇬", emojiU: "U+1F1F5 U+1F1EC", fips: "PP", internet: "PG", continent: "Oceania", region: "Pacific" }, { name: "Paraguay", alpha2: "PY", alpha3: "PRY", numeric: "600", locales: ["es-PY"], default_locale: "es-PY", currency: "PYG", latitude: "-23.442503", longitude: "-58.443832", currency_name: "Guarani", languages: ["es", "gn"], capital: "Asunción", emoji: "🇵🇾", emojiU: "U+1F1F5 U+1F1FE", fips: "PA", internet: "PY", continent: "Americas", region: "South America" }, { name: "Peru", alpha2: "PE", alpha3: "PER", numeric: "604", locales: ["es-PE"], default_locale: "es-PE", currency: "PEN", latitude: "-9.189967", longitude: "-75.015152", currency_name: "Sol", languages: ["es"], capital: "Lima", emoji: "🇵🇪", emojiU: "U+1F1F5 U+1F1EA", fips: "PE", internet: "PE", continent: "Americas", region: "South America" }, { name: "Philippines", alpha2: "PH", alpha3: "PHL", numeric: "608", locales: ["en-PH", "fil-PH"], default_locale: "en-PH", currency: "PHP", latitude: "12.879721", longitude: "121.774017", currency_name: "Philippine Peso", languages: ["en"], capital: "Manila", emoji: "🇵🇭", emojiU: "U+1F1F5 U+1F1ED", fips: "RP", internet: "PH", continent: "Asia", region: "South East Asia" }, { name: "Pitcairn", alpha2: "PN", alpha3: "PCN", numeric: "612", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-24.703615", longitude: "-127.439308", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Adamstown", emoji: "🇵🇳", emojiU: "U+1F1F5 U+1F1F3", fips: "PC", internet: "PN", continent: "Oceania", region: "Pacific" }, { name: "Poland", alpha2: "PL", alpha3: "POL", numeric: "616", locales: ["pl-PL"], default_locale: "pl-PL", currency: "PLN", latitude: "51.919438", longitude: "19.145136", currency_name: "Zloty", languages: ["pl"], capital: "Warsaw", emoji: "🇵🇱", emojiU: "U+1F1F5 U+1F1F1", fips: "PL", internet: "PL", continent: "Europe", region: "Eastern Europe" }, { name: "Portugal", alpha2: "PT", alpha3: "PRT", numeric: "620", locales: ["pt-PT"], default_locale: "pt-PT", currency: "EUR", latitude: "39.399872", longitude: "-8.224454", currency_name: "Euro", languages: ["pt"], capital: "Lisbon", emoji: "🇵🇹", emojiU: "U+1F1F5 U+1F1F9", fips: "PO", internet: "PT", continent: "Europe", region: "South West Europe" }, { name: "Puerto Rico", alpha2: "PR", alpha3: "PRI", numeric: "630", locales: ["es-PR"], default_locale: "es-PR", currency: "USD", latitude: "18.220833", longitude: "-66.590149", currency_name: "US Dollar", languages: ["es", "en"], capital: "San Juan", emoji: "🇵🇷", emojiU: "U+1F1F5 U+1F1F7", fips: "RQ", internet: "PR", continent: "Americas", region: "West Indies" }, { name: "Qatar", alpha2: "QA", alpha3: "QAT", numeric: "634", locales: ["ar-QA"], default_locale: "ar-QA", currency: "QAR", latitude: "25.354826", longitude: "51.183884", currency_name: "Qatari Rial", languages: ["ar"], capital: "Doha", emoji: "🇶🇦", emojiU: "U+1F1F6 U+1F1E6", fips: "QA", internet: "QA", continent: "Asia", region: "South West Asia" }, { name: "Romania", alpha2: "RO", alpha3: "ROU", numeric: "642", locales: ["ro-RO"], default_locale: "ro-RO", currency: "RON", latitude: "45.943161", longitude: "24.96676", currency_name: "Romanian Leu", languages: ["ro"], capital: "Bucharest", emoji: "🇷🇴", emojiU: "U+1F1F7 U+1F1F4", fips: "RO", internet: "RO", continent: "Europe", region: "South East Europe" }, { name: "Russia", alpha2: "RU", alpha3: "RUS", numeric: "643", locales: ["ru-RU"], default_locale: "ru-RU", currency: "RUB", latitude: "61.52401", longitude: "105.318756", currency_name: "Russian Ruble", languages: ["ru"], capital: "Moscow", emoji: "🇷🇺", emojiU: "U+1F1F7 U+1F1FA", fips: "RS", internet: "RU", continent: "Asia", region: "Northern Asia", alternate_names: ["Russian Federation"] }, { name: "Rwanda", alpha2: "RW", alpha3: "RWA", numeric: "646", locales: ["fr-RW", "rw-RW"], default_locale: "fr-RW", currency: "RWF", latitude: "-1.940278", longitude: "29.873888", currency_name: "Rwanda Franc", languages: ["rw", "en", "fr"], capital: "Kigali", emoji: "🇷🇼", emojiU: "U+1F1F7 U+1F1FC", fips: "RW", internet: "RW", continent: "Africa", region: "Central Africa" }, { name: "Réunion", alpha2: "RE", alpha3: "REU", numeric: "638", locales: ["fr-RE"], default_locale: "fr-RE", currency: "RWF", latitude: "-21.115141", longitude: "55.536384", currency_name: "Rwanda Franc", languages: ["fr"], capital: "Saint-Denis", emoji: "🇷🇪", emojiU: "U+1F1F7 U+1F1EA", fips: "RE", internet: "RE", continent: "Africa", region: "Indian Ocean" }, { name: "Saint Barthélemy", alpha2: "BL", alpha3: "BLM", numeric: "652", locales: ["fr-BL"], default_locale: "fr-BL", currency: "EUR", currency_name: "Euro", languages: ["fr"], capital: "Gustavia", emoji: "🇧🇱", emojiU: "U+1F1E7 U+1F1F1", fips: "TB", internet: "BL", continent: "Americas", region: "West Indies" }, { name: "Saint Helena", alpha2: "SH", alpha3: "SHN", numeric: "654", locales: ["en"], default_locale: "en", currency: "SHP", latitude: "-24.143474", longitude: "-10.030696", currency_name: "Saint Helena Pound", languages: ["en"], capital: "Jamestown", emoji: "🇸🇭", emojiU: "U+1F1F8 U+1F1ED", fips: "SH", internet: "SH", continent: "Atlantic Ocean", region: "South Atlantic Ocean", alternate_names: ["Saint Helena, Ascension and Tristan da Cunha"] }, { name: "Saint Kitts and Nevis", alpha2: "KN", alpha3: "KNA", numeric: "659", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.357822", longitude: "-62.782998", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Basseterre", emoji: "🇰🇳", emojiU: "U+1F1F0 U+1F1F3", fips: "SC", internet: "KN", continent: "Americas", region: "West Indies" }, { name: "Saint Lucia", alpha2: "LC", alpha3: "LCA", numeric: "662", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "13.909444", longitude: "-60.978893", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Castries", emoji: "🇱🇨", emojiU: "U+1F1F1 U+1F1E8", fips: "ST", internet: "LC", continent: "Americas", region: "West Indies" }, { name: "Saint Martin", alpha2: "MF", alpha3: "MAF", numeric: "663", locales: ["fr-MF"], default_locale: "fr-MF", currency: "EUR", currency_name: "Euro", languages: ["en", "fr", "nl"], capital: "Marigot", emoji: "🇲🇫", emojiU: "U+1F1F2 U+1F1EB", fips: "RN", internet: "MF", continent: "Americas", region: "West Indies", alternate_names: ["Saint Martin French part"] }, { name: "Saint Pierre and Miquelon", alpha2: "PM", alpha3: "SPM", numeric: "666", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "46.941936", longitude: "-56.27111", currency_name: "Euro", languages: ["fr"], capital: "Saint-Pierre", emoji: "🇵🇲", emojiU: "U+1F1F5 U+1F1F2", fips: "SB", internet: "PM", continent: "Americas", region: "North America" }, { name: "Saint Vincent and the Grenadines", alpha2: "VC", alpha3: "VCT", numeric: "670", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.984305", longitude: "-61.287228", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Kingstown", emoji: "🇻🇨", emojiU: "U+1F1FB U+1F1E8", fips: "VC", internet: "VC", continent: "Americas", region: "West Indies" }, { name: "Samoa", alpha2: "WS", alpha3: "WSM", numeric: "882", locales: ["sm"], default_locale: "sm", currency: "WST", latitude: "-13.759029", longitude: "-172.104629", currency_name: "Tala", languages: ["sm", "en"], capital: "Apia", emoji: "🇼🇸", emojiU: "U+1F1FC U+1F1F8", fips: "WS", internet: "WS", continent: "Oceania", region: "Pacific" }, { name: "San Marino", alpha2: "SM", alpha3: "SMR", numeric: "674", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "43.94236", longitude: "12.457777", currency_name: "Euro", languages: ["it"], capital: "City of San Marino", emoji: "🇸🇲", emojiU: "U+1F1F8 U+1F1F2", fips: "SM", internet: "SM", continent: "Europe", region: "Southern Europe" }, { name: "Sao Tome and Principe", alpha2: "ST", alpha3: "STP", numeric: "678", locales: ["pt"], default_locale: "pt", currency: "STN", latitude: "0.18636", longitude: "6.613081", currency_name: "Dobra", languages: ["pt"], capital: "São Tomé", emoji: "🇸🇹", emojiU: "U+1F1F8 U+1F1F9", fips: "TP", internet: "ST", continent: "Africa", region: "Western Africa" }, { name: "Saudi Arabia", alpha2: "SA", alpha3: "SAU", numeric: "682", locales: ["ar-SA"], default_locale: "ar-SA", currency: "SAR", latitude: "23.885942", longitude: "45.079162", currency_name: "Saudi Riyal", languages: ["ar"], capital: "Riyadh", emoji: "🇸🇦", emojiU: "U+1F1F8 U+1F1E6", fips: "SA", internet: "SA", continent: "Asia", region: "South West Asia" }, { name: "Senegal", alpha2: "SN", alpha3: "SEN", numeric: "686", locales: ["fr-SN", "ff-SN"], default_locale: "fr-SN", currency: "XOF", latitude: "14.497401", longitude: "-14.452362", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Dakar", emoji: "🇸🇳", emojiU: "U+1F1F8 U+1F1F3", fips: "SG", internet: "SN", continent: "Africa", region: "Western Africa" }, { name: "Serbia", alpha2: "RS", alpha3: "SRB", numeric: "688", locales: ["sr-Cyrl-RS", "sr-Latn-RS"], default_locale: "sr-Cyrl-RS", currency: "RSD", latitude: "44.016521", longitude: "21.005859", currency_name: "Serbian Dinar", languages: ["sr"], capital: "Belgrade", emoji: "🇷🇸", emojiU: "U+1F1F7 U+1F1F8", fips: "RI", internet: "RS", continent: "Europe", region: "South East Europe" }, { name: "Seychelles", alpha2: "SC", alpha3: "SYC", numeric: "690", locales: ["fr"], default_locale: "fr", currency: "SCR", latitude: "-4.679574", longitude: "55.491977", currency_name: "Seychelles Rupee", languages: ["fr", "en"], capital: "Victoria", emoji: "🇸🇨", emojiU: "U+1F1F8 U+1F1E8", fips: "SE", internet: "SC", continent: "Africa", region: "Indian Ocean" }, { name: "Sierra Leone", alpha2: "SL", alpha3: "SLE", numeric: "694", locales: ["en"], default_locale: "en", currency: "SLL", latitude: "8.460555", longitude: "-11.779889", currency_name: "Leone", languages: ["en"], capital: "Freetown", emoji: "🇸🇱", emojiU: "U+1F1F8 U+1F1F1", fips: "SL", internet: "SL", continent: "Africa", region: "Western Africa" }, { name: "Singapore", alpha2: "SG", alpha3: "SGP", numeric: "702", locales: ["zh-Hans-SG", "en-SG"], default_locale: "en-SG", currency: "SGD", latitude: "1.352083", longitude: "103.819836", currency_name: "Singapore Dollar", languages: ["en", "ms", "ta", "zh"], capital: "Singapore", emoji: "🇸🇬", emojiU: "U+1F1F8 U+1F1EC", fips: "SN", internet: "SG", continent: "Asia", region: "South East Asia" }, { name: "Sint Maarten", alpha2: "SX", alpha3: "SXM", numeric: "534", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "en"], capital: "Philipsburg", emoji: "🇸🇽", emojiU: "U+1F1F8 U+1F1FD", fips: "NN", internet: "SX", continent: "Americas", region: "West Indies", alternate_names: ["Sint Maarten Dutch part"] }, { name: "Slovakia", alpha2: "SK", alpha3: "SVK", numeric: "703", locales: ["sk-SK"], default_locale: "sk-SK", currency: "EUR", latitude: "48.669026", longitude: "19.699024", currency_name: "Euro", languages: ["sk"], capital: "Bratislava", emoji: "🇸🇰", emojiU: "U+1F1F8 U+1F1F0", fips: "LO", internet: "SK", continent: "Europe", region: "Central Europe" }, { name: "Slovenia", alpha2: "SI", alpha3: "SVN", numeric: "705", locales: ["sl-SI"], default_locale: "sl-SI", currency: "EUR", latitude: "46.151241", longitude: "14.995463", currency_name: "Euro", languages: ["sl"], capital: "Ljubljana", emoji: "🇸🇮", emojiU: "U+1F1F8 U+1F1EE", fips: "SI", internet: "SI", continent: "Europe", region: "South East Europe" }, { name: "Solomon Islands", alpha2: "SB", alpha3: "SLB", numeric: "090", locales: ["en"], default_locale: "en", currency: "SBD", latitude: "-9.64571", longitude: "160.156194", currency_name: "Solomon Islands Dollar", languages: ["en"], capital: "Honiara", emoji: "🇸🇧", emojiU: "U+1F1F8 U+1F1E7", fips: "BP", internet: "SB", continent: "Oceania", region: "Pacific" }, { name: "Somalia", alpha2: "SO", alpha3: "SOM", numeric: "706", locales: ["so-SO"], default_locale: "so-SO", currency: "SOS", latitude: "5.152149", longitude: "46.199616", currency_name: "Somali Shilling", languages: ["so", "ar"], capital: "Mogadishu", emoji: "🇸🇴", emojiU: "U+1F1F8 U+1F1F4", fips: "SO", internet: "SO", continent: "Africa", region: "Eastern Africa" }, { name: "South Africa", alpha2: "ZA", alpha3: "ZAF", numeric: "710", locales: ["af-ZA", "en-ZA", "zu-ZA"], default_locale: "af-ZA", currency: "ZAR", latitude: "-30.559482", longitude: "22.937506", currency_name: "Rand", languages: ["af", "en", "nr", "st", "ss", "tn", "ts", "ve", "xh", "zu"], capital: "Pretoria", emoji: "🇿🇦", emojiU: "U+1F1FF U+1F1E6", fips: "SF", internet: "ZA", continent: "Africa", region: "Southern Africa" }, { name: "South Georgia and the South Sandwich Islands", alpha2: "GS", alpha3: "SGS", numeric: "239", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-54.429579", longitude: "-36.587909", currency_name: "US Dollar", languages: ["en"], capital: "King Edward Point", emoji: "🇬🇸", emojiU: "U+1F1EC U+1F1F8", fips: "SX", internet: "GS", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "South Sudan", alpha2: "SS", alpha3: "SSD", numeric: "728", locales: ["en"], default_locale: "en", currency: "SSP", currency_name: "South Sudanese Pound", languages: ["en"], capital: "Juba", emoji: "🇸🇸", emojiU: "U+1F1F8 U+1F1F8", fips: "OD", internet: "SS", continent: "Africa", region: "Northern Africa" }, { name: "Spain", alpha2: "ES", alpha3: "ESP", numeric: "724", locales: ["eu-ES", "ca-ES", "gl-ES", "es-ES"], default_locale: "es-ES", currency: "EUR", latitude: "40.463667", longitude: "-3.74922", currency_name: "Euro", languages: ["es", "eu", "ca", "gl", "oc"], capital: "Madrid", emoji: "🇪🇸", emojiU: "U+1F1EA U+1F1F8", fips: "SP", internet: "ES", continent: "Europe", region: "South West Europe" }, { name: "Sri Lanka", alpha2: "LK", alpha3: "LKA", numeric: "144", locales: ["si-LK", "ta-LK"], default_locale: "si-LK", currency: "LKR", latitude: "7.873054", longitude: "80.771797", currency_name: "Sri Lanka Rupee", languages: ["si", "ta"], capital: "Colombo", emoji: "🇱🇰", emojiU: "U+1F1F1 U+1F1F0", fips: "CE", internet: "LK", continent: "Asia", region: "South Asia" }, { name: "Sudan", alpha2: "SD", alpha3: "SDN", numeric: "729", locales: ["ar-SD"], default_locale: "ar-SD", currency: "SDG", latitude: "12.862807", longitude: "30.217636", currency_name: "Sudanese Pound", languages: ["ar", "en"], capital: "Khartoum", emoji: "🇸🇩", emojiU: "U+1F1F8 U+1F1E9", fips: "SU", internet: "SD", continent: "Africa", region: "Northern Africa" }, { name: "Suriname", alpha2: "SR", alpha3: "SUR", numeric: "740", locales: ["nl"], default_locale: "nl", currency: "SRD", latitude: "3.919305", longitude: "-56.027783", currency_name: "Surinam Dollar", languages: ["nl"], capital: "Paramaribo", emoji: "🇸🇷", emojiU: "U+1F1F8 U+1F1F7", fips: "NS", internet: "SR", continent: "Americas", region: "South America" }, { name: "Svalbard and Jan Mayen", alpha2: "SJ", alpha3: "SJM", numeric: "744", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "77.553604", longitude: "23.670272", currency_name: "Norwegian Krone", languages: ["no"], capital: "Longyearbyen", emoji: "🇸🇯", emojiU: "U+1F1F8 U+1F1EF", fips: "SV", internet: "SJ", continent: "Europe", region: "Northern Europe" }, { name: "Sweden", alpha2: "SE", alpha3: "SWE", numeric: "752", locales: ["sv-SE"], default_locale: "sv-SE", currency: "SEK", latitude: "60.128161", longitude: "18.643501", currency_name: "Swedish Krona", languages: ["sv"], capital: "Stockholm", emoji: "🇸🇪", emojiU: "U+1F1F8 U+1F1EA", fips: "SW", internet: "SE", continent: "Europe", region: "Northern Europe" }, { name: "Switzerland", alpha2: "CH", alpha3: "CHE", numeric: "756", locales: ["fr-CH", "de-CH", "it-CH", "rm-CH", "gsw-CH"], default_locale: "fr-CH", currency: "CHF", latitude: "46.818188", longitude: "8.227512", currency_name: "Swiss Franc", languages: ["de", "fr", "it"], capital: "Bern", emoji: "🇨🇭", emojiU: "U+1F1E8 U+1F1ED", fips: "SZ", internet: "CH", continent: "Europe", region: "Central Europe" }, { name: "Syrian Arab Republic", alpha2: "SY", alpha3: "SYR", numeric: "760", locales: ["ar-SY"], default_locale: "ar-SY", currency: "SYP", latitude: "34.802075", longitude: "38.996815", currency_name: "Syrian Pound", languages: ["ar"], capital: "Damascus", emoji: "🇸🇾", emojiU: "U+1F1F8 U+1F1FE", fips: "SY", internet: "SY", continent: "Asia", region: "South West Asia" }, { name: "Taiwan", alpha2: "TW", alpha3: "TWN", numeric: "158", locales: ["zh-Hant-TW"], default_locale: "zh-Hant-TW", currency: "TWD", latitude: "23.69781", longitude: "120.960515", currency_name: "New Taiwan Dollar", languages: ["zh"], capital: "Taipei", emoji: "🇹🇼", emojiU: "U+1F1F9 U+1F1FC", fips: "TW", internet: "TW", continent: "Asia", region: "East Asia", alternate_names: ["Province of China Taiwan"] }, { name: "Tajikistan", alpha2: "TJ", alpha3: "TJK", numeric: "762", locales: ["tg"], default_locale: "tg", currency: "TJS", latitude: "38.861034", longitude: "71.276093", currency_name: "Somoni", languages: ["tg", "ru"], capital: "Dushanbe", emoji: "🇹🇯", emojiU: "U+1F1F9 U+1F1EF", fips: "TI", internet: "TJ", continent: "Asia", region: "Central Asia" }, { name: "Tanzania", alpha2: "TZ", alpha3: "TZA", numeric: "834", locales: ["asa-TZ", "bez-TZ", "lag-TZ", "jmc-TZ", "kde-TZ", "mas-TZ", "rof-TZ", "rwk-TZ", "sw-TZ", "vun-TZ"], default_locale: "asa-TZ", currency: "TZS", latitude: "-6.369028", longitude: "34.888822", currency_name: "Tanzanian Shilling", languages: ["sw", "en"], capital: "Dodoma", emoji: "🇹🇿", emojiU: "U+1F1F9 U+1F1FF", fips: "TZ", internet: "TZ", continent: "Africa", region: "Eastern Africa", alternate_names: ["United Republic of Tanzania"] }, { name: "Thailand", alpha2: "TH", alpha3: "THA", numeric: "764", locales: ["th-TH"], default_locale: "th-TH", currency: "THB", latitude: "15.870032", longitude: "100.992541", currency_name: "Baht", languages: ["th"], capital: "Bangkok", emoji: "🇹🇭", emojiU: "U+1F1F9 U+1F1ED", fips: "TH", internet: "TH", continent: "Asia", region: "South East Asia" }, { name: "Timor-Leste", alpha2: "TL", alpha3: "TLS", numeric: "626", locales: ["pt"], default_locale: "pt", currency: "USD", latitude: "-8.874217", longitude: "125.727539", currency_name: "US Dollar", languages: ["pt"], capital: "Dili", emoji: "🇹🇱", emojiU: "U+1F1F9 U+1F1F1", fips: "TT", internet: "TL", continent: "Asia", region: "South East Asia" }, { name: "Togo", alpha2: "TG", alpha3: "TGO", numeric: "768", locales: ["ee-TG", "fr-TG"], default_locale: "fr-TG", currency: "XOF", latitude: "8.619543", longitude: "0.824782", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Lomé", emoji: "🇹🇬", emojiU: "U+1F1F9 U+1F1EC", fips: "TO", internet: "TG", continent: "Africa", region: "Western Africa" }, { name: "Tokelau", alpha2: "TK", alpha3: "TKL", numeric: "772", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-8.967363", longitude: "-171.855881", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Fakaofo", emoji: "🇹🇰", emojiU: "U+1F1F9 U+1F1F0", fips: "TL", internet: "TK", continent: "Oceania", region: "Pacific" }, { name: "Tonga", alpha2: "TO", alpha3: "TON", numeric: "776", locales: ["to-TO"], default_locale: "to-TO", currency: "TOP", latitude: "-21.178986", longitude: "-175.198242", currency_name: "Pa’anga", languages: ["en", "to"], capital: "Nuku'alofa", emoji: "🇹🇴", emojiU: "U+1F1F9 U+1F1F4", fips: "TN", internet: "TO", continent: "Oceania", region: "Pacific" }, { name: "Trinidad and Tobago", alpha2: "TT", alpha3: "TTO", numeric: "780", locales: ["en-TT"], default_locale: "en-TT", currency: "TTD", latitude: "10.691803", longitude: "-61.222503", currency_name: "Trinidad and Tobago Dollar", languages: ["en"], capital: "Port of Spain", emoji: "🇹🇹", emojiU: "U+1F1F9 U+1F1F9", fips: "TD", internet: "TT", continent: "Americas", region: "West Indies" }, { name: "Tunisia", alpha2: "TN", alpha3: "TUN", numeric: "788", locales: ["ar-TN"], default_locale: "ar-TN", currency: "TND", latitude: "33.886917", longitude: "9.537499", currency_name: "Tunisian Dinar", languages: ["ar"], capital: "Tunis", emoji: "🇹🇳", emojiU: "U+1F1F9 U+1F1F3", fips: "TS", internet: "TN", continent: "Africa", region: "Northern Africa" }, { name: "Turkey", alpha2: "TR", alpha3: "TUR", numeric: "792", locales: ["tr-TR"], default_locale: "tr-TR", currency: "TRY", latitude: "38.963745", longitude: "35.243322", currency_name: "Turkish Lira", languages: ["tr"], capital: "Ankara", emoji: "🇹🇷", emojiU: "U+1F1F9 U+1F1F7", fips: "TU", internet: "TR", continent: "Asia", region: "South West Asia" }, { name: "Turkmenistan", alpha2: "TM", alpha3: "TKM", numeric: "795", locales: ["tk"], default_locale: "tk", currency: "TMT", latitude: "38.969719", longitude: "59.556278", currency_name: "Turkmenistan New Manat", languages: ["tk", "ru"], capital: "Ashgabat", emoji: "🇹🇲", emojiU: "U+1F1F9 U+1F1F2", fips: "TX", internet: "TM", continent: "Asia", region: "Central Asia" }, { name: "Turks and Caicos Islands", alpha2: "TC", alpha3: "TCA", numeric: "796", locales: ["en"], default_locale: "en", currency: "USD", latitude: "21.694025", longitude: "-71.797928", currency_name: "US Dollar", languages: ["en"], capital: "Cockburn Town", emoji: "🇹🇨", emojiU: "U+1F1F9 U+1F1E8", fips: "TK", internet: "TC", continent: "Americas", region: "West Indies" }, { name: "Tuvalu", alpha2: "TV", alpha3: "TUV", numeric: "798", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-7.109535", longitude: "177.64933", currency_name: "Australian Dollar", languages: ["en"], capital: "Funafuti", emoji: "🇹🇻", emojiU: "U+1F1F9 U+1F1FB", fips: "TV", internet: "TV", continent: "Oceania", region: "Pacific" }, { name: "Uganda", alpha2: "UG", alpha3: "UGA", numeric: "800", locales: ["cgg-UG", "lg-UG", "nyn-UG", "xog-UG", "teo-UG"], default_locale: "cgg-UG", currency: "UGX", latitude: "1.373333", longitude: "32.290275", currency_name: "Uganda Shilling", languages: ["en", "sw"], capital: "Kampala", emoji: "🇺🇬", emojiU: "U+1F1FA U+1F1EC", fips: "UG", internet: "UG", continent: "Africa", region: "Eastern Africa" }, { name: "Ukraine", alpha2: "UA", alpha3: "UKR", numeric: "804", locales: ["ru-UA", "uk-UA"], default_locale: "uk-UA", currency: "UAH", latitude: "48.379433", longitude: "31.16558", currency_name: "Hryvnia", languages: ["uk"], capital: "Kyiv", emoji: "🇺🇦", emojiU: "U+1F1FA U+1F1E6", fips: "UP", internet: "UA", continent: "Europe", region: "Eastern Europe" }, { name: "United Arab Emirates", alpha2: "AE", alpha3: "ARE", numeric: "784", locales: ["ar-AE"], default_locale: "ar-AE", currency: "AED", latitude: "23.424076", longitude: "53.847818", currency_name: "UAE Dirham", languages: ["ar"], capital: "Abu Dhabi", emoji: "🇦🇪", emojiU: "U+1F1E6 U+1F1EA", fips: "TC", internet: "AE", continent: "Asia", region: "South West Asia" }, { name: "United Kingdom", alpha2: "GB", alpha3: "GBR", numeric: "826", locales: ["kw-GB", "en-GB", "gv-GB", "cy-GB"], default_locale: "en-GB", currency: "GBP", latitude: "55.378051", longitude: "-3.435973", currency_name: "Pound Sterling", languages: ["en"], capital: "London", emoji: "🇬🇧", emojiU: "U+1F1EC U+1F1E7", fips: "UK", internet: "UK", continent: "Europe", region: "Western Europe", alternate_names: ["United Kingdom of Great Britain and Northern Ireland"] }, { name: "United States Minor Outlying Islands", alpha2: "UM", alpha3: "UMI", numeric: "581", locales: ["en-UM"], default_locale: "en-UM", currency: "USD", currency_name: "US Dollar", languages: ["en"], capital: "", emoji: "🇺🇲", emojiU: "U+1F1FA U+1F1F2", fips: "", internet: "US", continent: "Americas", region: "North America" }, { name: "United States of America", alpha2: "US", alpha3: "USA", numeric: "840", locales: ["chr-US", "en-US", "haw-US", "es-US"], default_locale: "en-US", currency: "USD", latitude: "37.09024", longitude: "-95.712891", currency_name: "US Dollar", languages: ["en"], capital: "Washington D.C.", emoji: "🇺🇸", emojiU: "U+1F1FA U+1F1F8", fips: "US", internet: "US", continent: "Americas", region: "North America", alternate_names: ["United States"] }, { name: "Uruguay", alpha2: "UY", alpha3: "URY", numeric: "858", locales: ["es-UY"], default_locale: "es-UY", currency: "UYU", latitude: "-32.522779", longitude: "-55.765835", currency_name: "Peso Uruguayo", languages: ["es"], capital: "Montevideo", emoji: "🇺🇾", emojiU: "U+1F1FA U+1F1FE", fips: "UY", internet: "UY", continent: "Americas", region: "South America" }, { name: "Uzbekistan", alpha2: "UZ", alpha3: "UZB", numeric: "860", locales: ["uz-Cyrl-UZ", "uz-Latn-UZ"], default_locale: "uz-Cyrl-UZ", currency: "UZS", latitude: "41.377491", longitude: "64.585262", currency_name: "Uzbekistan Sum", languages: ["uz", "ru"], capital: "Tashkent", emoji: "🇺🇿", emojiU: "U+1F1FA U+1F1FF", fips: "UZ", internet: "UZ", continent: "Asia", region: "Central Asia" }, { name: "Vanuatu", alpha2: "VU", alpha3: "VUT", numeric: "548", locales: ["bi"], default_locale: "bi", currency: "VUV", latitude: "-15.376706", longitude: "166.959158", currency_name: "Vatu", languages: ["bi", "en", "fr"], capital: "Port Vila", emoji: "🇻🇺", emojiU: "U+1F1FB U+1F1FA", fips: "NH", internet: "VU", continent: "Oceania", region: "Pacific" }, { name: "Venezuela", alpha2: "VE", alpha3: "VEN", numeric: "862", locales: ["es-VE"], default_locale: "es-VE", currency: "VUV", latitude: "6.42375", longitude: "-66.58973", currency_name: "Vatu", languages: ["es"], capital: "Caracas", emoji: "🇻🇪", emojiU: "U+1F1FB U+1F1EA", fips: "VE", internet: "UE", continent: "Americas", region: "South America", alternate_names: ["Bolivarian Republic of Venezuela"] }, { name: "Viet Nam", alpha2: "VN", alpha3: "VNM", numeric: "704", locales: ["vi-VN"], default_locale: "vi-VN", currency: "VND", latitude: "14.058324", longitude: "108.277199", currency_name: "Dong", languages: ["vi"], capital: "Hanoi", emoji: "🇻🇳", emojiU: "U+1F1FB U+1F1F3", fips: "VN", internet: "VN", continent: "Asia", region: "South East Asia" }, { name: "Virgin Islands (British)", alpha2: "VG", alpha3: "VGB", numeric: "092", locales: ["en"], default_locale: "en", currency: "USD", latitude: "18.420695", longitude: "-64.639968", currency_name: "US Dollar", languages: ["en"], capital: "Road Town", emoji: "🇻🇬", emojiU: "U+1F1FB U+1F1EC", fips: "VI", internet: "VG", continent: "Americas", region: "West Indies" }, { name: "Virgin Islands (U.S.)", alpha2: "VI", alpha3: "VIR", numeric: "850", locales: ["en-VI"], default_locale: "en-VI", currency: "USD", latitude: "18.335765", longitude: "-64.896335", currency_name: "US Dollar", languages: ["en"], capital: "Charlotte Amalie", emoji: "🇻🇮", emojiU: "U+1F1FB U+1F1EE", fips: "VQ", internet: "VI", continent: "Americas", region: "West Indies" }, { name: "Wallis and Futuna", alpha2: "WF", alpha3: "WLF", numeric: "876", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-13.768752", longitude: "-177.156097", currency_name: "CFP Franc", languages: ["fr"], capital: "Mata-Utu", emoji: "🇼🇫", emojiU: "U+1F1FC U+1F1EB", fips: "WF", internet: "WF", continent: "Oceania", region: "Pacific" }, { name: "Western Sahara", alpha2: "EH", alpha3: "ESH", numeric: "732", locales: ["es"], default_locale: "es", currency: "MAD", latitude: "24.215527", longitude: "-12.885834", currency_name: "Moroccan Dirham", languages: ["es"], capital: "El Aaiún", emoji: "🇪🇭", emojiU: "U+1F1EA U+1F1ED", fips: "WI", internet: "EH", continent: "Africa", region: "Northern Africa" }, { name: "Yemen", alpha2: "YE", alpha3: "YEM", numeric: "887", locales: ["ar-YE"], default_locale: "ar-YE", currency: "YER", latitude: "15.552727", longitude: "48.516388", currency_name: "Yemeni Rial", languages: ["ar"], capital: "Sana'a", emoji: "🇾🇪", emojiU: "U+1F1FE U+1F1EA", fips: "YM", internet: "YE", continent: "Asia", region: "South West Asia" }, { name: "Zambia", alpha2: "ZM", alpha3: "ZMB", numeric: "894", locales: ["bem-ZM"], default_locale: "bem-ZM", currency: "ZMW", latitude: "-13.133897", longitude: "27.849332", currency_name: "Zambian Kwacha", languages: ["en"], capital: "Lusaka", emoji: "🇿🇲", emojiU: "U+1F1FF U+1F1F2", fips: "ZA", internet: "ZM", continent: "Africa", region: "Southern Africa" }, { name: "Zimbabwe", alpha2: "ZW", alpha3: "ZWE", numeric: "716", locales: ["en-ZW", "nd-ZW", "sn-ZW"], default_locale: "en-ZW", currency: "ZWL", latitude: "-19.015438", longitude: "29.154857", currency_name: "Zimbabwe Dollar", languages: ["en", "sn", "nd"], capital: "Harare", emoji: "🇿🇼", emojiU: "U+1F1FF U+1F1FC", fips: "ZI", internet: "ZW", continent: "Africa", region: "Southern Africa" }, { name: "Åland Islands", alpha2: "AX", alpha3: "ALA", numeric: "248", locales: ["sv"], default_locale: "sv", currency: "EUR", currency_name: "Euro", languages: ["sv"], capital: "Mariehamn", emoji: "🇦🇽", emojiU: "U+1F1E6 U+1F1FD", fips: "AX", internet: "AX", continent: "Europe", region: "Northern Europe" }, { name: "Kosovo", alpha2: "XK", alpha3: "XKX", numeric: "383", locales: ["sq"], default_locale: "sq", currency: "EUR", latitude: "42.602636", longitude: "20.902977", currency_name: "Euro", languages: ["sq", "sr"], capital: "Pristina", emoji: "🇽🇰", emojiU: "U+1F1FD U+1F1F0", fips: "KV", internet: "XK", continent: "Europe", region: "South East Europe" }]; - }); - - // node_modules/.bun/country-locale-map@1.9.11/node_modules/country-locale-map/countries.json - var require_countries = __commonJS((exports, module) => { - module.exports = [{ name: "Afghanistan", alpha2: "AF", alpha3: "AFG", numeric: "004", locales: ["ps_AF", "fa_AF", "uz_Arab_AF"], default_locale: "ps_AF", currency: "AFN", latitude: "33.93911", longitude: "67.709953", currency_name: "Afghani", languages: ["ps", "uz", "tk"], capital: "Kabul", emoji: "🇦🇫", emojiU: "U+1F1E6 U+1F1EB", fips: "AF", internet: "AF", continent: "Asia", region: "South Asia" }, { name: "Albania", alpha2: "AL", alpha3: "ALB", numeric: "008", locales: ["sq_AL"], default_locale: "sq_AL", currency: "ALL", latitude: "41.153332", longitude: "20.168331", currency_name: "Lek", languages: ["sq"], capital: "Tirana", emoji: "🇦🇱", emojiU: "U+1F1E6 U+1F1F1", fips: "AL", internet: "AL", continent: "Europe", region: "South East Europe" }, { name: "Algeria", alpha2: "DZ", alpha3: "DZA", numeric: "012", locales: ["ar_DZ", "kab_DZ"], default_locale: "ar_DZ", currency: "DZD", latitude: "28.033886", longitude: "1.659626", currency_name: "Algerian Dinar", languages: ["ar"], capital: "Algiers", emoji: "🇩🇿", emojiU: "U+1F1E9 U+1F1FF", fips: "AG", internet: "DZ", continent: "Africa", region: "Northern Africa" }, { name: "American Samoa", alpha2: "AS", alpha3: "ASM", numeric: "016", locales: ["en_AS"], default_locale: "en_AS", currency: "USD", latitude: "-14.270972", longitude: "-170.132217", currency_name: "US Dollar", languages: ["en", "sm"], capital: "Pago Pago", emoji: "🇦🇸", emojiU: "U+1F1E6 U+1F1F8", fips: "AQ", internet: "AS", continent: "Oceania", region: "Pacific" }, { name: "Andorra", alpha2: "AD", alpha3: "AND", numeric: "020", locales: ["ca"], default_locale: "ca", currency: "EUR", latitude: "42.546245", longitude: "1.601554", currency_name: "Euro", languages: ["ca"], capital: "Andorra la Vella", emoji: "🇦🇩", emojiU: "U+1F1E6 U+1F1E9", fips: "AN", internet: "AD", continent: "Europe", region: "South West Europe" }, { name: "Angola", alpha2: "AO", alpha3: "AGO", numeric: "024", locales: ["pt"], default_locale: "pt", currency: "AOA", latitude: "-11.202692", longitude: "17.873887", currency_name: "Kwanza", languages: ["pt"], capital: "Luanda", emoji: "🇦🇴", emojiU: "U+1F1E6 U+1F1F4", fips: "AO", internet: "AO", continent: "Africa", region: "Southern Africa" }, { name: "Anguilla", alpha2: "AI", alpha3: "AIA", numeric: "660", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "18.220554", longitude: "-63.068615", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "The Valley", emoji: "🇦🇮", emojiU: "U+1F1E6 U+1F1EE", fips: "AV", internet: "AI", continent: "Americas", region: "West Indies" }, { name: "Antarctica", alpha2: "AQ", alpha3: "ATA", numeric: "010", locales: ["en_US"], default_locale: "en_US", currency: "USD", latitude: "-75.250973", longitude: "-0.071389", currency_name: "US Dollar", languages: [], capital: "", emoji: "🇦🇶", emojiU: "U+1F1E6 U+1F1F6", fips: "AY", internet: "AQ", continent: "Antarctica", region: "Antarctica" }, { name: "Antigua and Barbuda", alpha2: "AG", alpha3: "ATG", numeric: "028", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.060816", longitude: "-61.796428", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Saint John's", emoji: "🇦🇬", emojiU: "U+1F1E6 U+1F1EC", fips: "AC", internet: "AG", continent: "Americas", region: "West Indies" }, { name: "Argentina", alpha2: "AR", alpha3: "ARG", numeric: "032", locales: ["es_AR"], default_locale: "es_AR", currency: "ARS", latitude: "-38.416097", longitude: "-63.616672", currency_name: "Argentine Peso", languages: ["es", "gn"], capital: "Buenos Aires", emoji: "🇦🇷", emojiU: "U+1F1E6 U+1F1F7", fips: "AR", internet: "AR", continent: "Americas", region: "South America" }, { name: "Armenia", alpha2: "AM", alpha3: "ARM", numeric: "051", locales: ["hy_AM"], default_locale: "hy_AM", currency: "AMD", latitude: "40.069099", longitude: "45.038189", currency_name: "Armenian Dram", languages: ["hy", "ru"], capital: "Yerevan", emoji: "🇦🇲", emojiU: "U+1F1E6 U+1F1F2", fips: "AM", internet: "AM", continent: "Asia", region: "South West Asia" }, { name: "Aruba", alpha2: "AW", alpha3: "ABW", numeric: "533", locales: ["nl"], default_locale: "nl", currency: "AWG", latitude: "12.52111", longitude: "-69.968338", currency_name: "Aruban Florin", languages: ["nl", "pa"], capital: "Oranjestad", emoji: "🇦🇼", emojiU: "U+1F1E6 U+1F1FC", fips: "AA", internet: "AW", continent: "Americas", region: "West Indies" }, { name: "Australia", alpha2: "AU", alpha3: "AUS", numeric: "036", locales: ["en_AU"], default_locale: "en_AU", currency: "AUD", latitude: "-25.274398", longitude: "133.775136", currency_name: "Australian Dollar", languages: ["en"], capital: "Canberra", emoji: "🇦🇺", emojiU: "U+1F1E6 U+1F1FA", fips: "AS", internet: "AU", continent: "Oceania", region: "Pacific" }, { name: "Austria", alpha2: "AT", alpha3: "AUT", numeric: "040", locales: ["de_AT"], default_locale: "de_AT", currency: "EUR", latitude: "47.516231", longitude: "14.550072", currency_name: "Euro", languages: ["de"], capital: "Vienna", emoji: "🇦🇹", emojiU: "U+1F1E6 U+1F1F9", fips: "AU", internet: "AT", continent: "Europe", region: "Central Europe" }, { name: "Azerbaijan", alpha2: "AZ", alpha3: "AZE", numeric: "031", locales: ["az_Cyrl_AZ", "az_Latn_AZ"], default_locale: "az_Cyrl_AZ", currency: "AZN", latitude: "40.143105", longitude: "47.576927", currency_name: "Azerbaijan Manat", languages: ["az"], capital: "Baku", emoji: "🇦🇿", emojiU: "U+1F1E6 U+1F1FF", fips: "AJ", internet: "AZ", continent: "Asia", region: "South West Asia" }, { name: "Bahamas", alpha2: "BS", alpha3: "BHS", numeric: "044", locales: ["en"], default_locale: "en", currency: "BSD", latitude: "25.03428", longitude: "-77.39628", currency_name: "Bahamian Dollar", languages: ["en"], capital: "Nassau", emoji: "🇧🇸", emojiU: "U+1F1E7 U+1F1F8", fips: "BF", internet: "BS", continent: "Americas", region: "West Indies" }, { name: "Bahrain", alpha2: "BH", alpha3: "BHR", numeric: "048", locales: ["ar_BH"], default_locale: "ar_BH", currency: "BHD", latitude: "25.930414", longitude: "50.637772", currency_name: "Bahraini Dinar", languages: ["ar"], capital: "Manama", emoji: "🇧🇭", emojiU: "U+1F1E7 U+1F1ED", fips: "BA", internet: "BH", continent: "Asia", region: "South West Asia" }, { name: "Bangladesh", alpha2: "BD", alpha3: "BGD", numeric: "050", locales: ["bn_BD"], default_locale: "bn_BD", currency: "BDT", latitude: "23.684994", longitude: "90.356331", currency_name: "Taka", languages: ["bn"], capital: "Dhaka", emoji: "🇧🇩", emojiU: "U+1F1E7 U+1F1E9", fips: "BG", internet: "BD", continent: "Asia", region: "South Asia" }, { name: "Barbados", alpha2: "BB", alpha3: "BRB", numeric: "052", locales: ["en"], default_locale: "en", currency: "BBD", latitude: "13.193887", longitude: "-59.543198", currency_name: "Barbados Dollar", languages: ["en"], capital: "Bridgetown", emoji: "🇧🇧", emojiU: "U+1F1E7 U+1F1E7", fips: "BB", internet: "BB", continent: "Americas", region: "West Indies" }, { name: "Belarus", alpha2: "BY", alpha3: "BLR", numeric: "112", locales: ["be_BY"], default_locale: "be_BY", currency: "BYN", latitude: "53.709807", longitude: "27.953389", currency_name: "Belarusian Ruble", languages: ["be", "ru"], capital: "Minsk", emoji: "🇧🇾", emojiU: "U+1F1E7 U+1F1FE", fips: "BO", internet: "BY", continent: "Europe", region: "Eastern Europe" }, { name: "Belgium", alpha2: "BE", alpha3: "BEL", numeric: "056", locales: ["nl_BE", "en_BE", "fr_BE", "de_BE"], default_locale: "nl_BE", currency: "EUR", latitude: "50.503887", longitude: "4.469936", currency_name: "Euro", languages: ["nl", "fr", "de"], capital: "Brussels", emoji: "🇧🇪", emojiU: "U+1F1E7 U+1F1EA", fips: "BE", internet: "BE", continent: "Europe", region: "Western Europe" }, { name: "Belize", alpha2: "BZ", alpha3: "BLZ", numeric: "084", locales: ["en_BZ"], default_locale: "en_BZ", currency: "BZD", latitude: "17.189877", longitude: "-88.49765", currency_name: "Belize Dollar", languages: ["en", "es"], capital: "Belmopan", emoji: "🇧🇿", emojiU: "U+1F1E7 U+1F1FF", fips: "BH", internet: "BZ", continent: "Americas", region: "Central America" }, { name: "Benin", alpha2: "BJ", alpha3: "BEN", numeric: "204", locales: ["fr_BJ"], default_locale: "fr_BJ", currency: "XOF", latitude: "9.30769", longitude: "2.315834", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Porto-Novo", emoji: "🇧🇯", emojiU: "U+1F1E7 U+1F1EF", fips: "BN", internet: "BJ", continent: "Africa", region: "Western Africa" }, { name: "Bermuda", alpha2: "BM", alpha3: "BMU", numeric: "060", locales: ["en"], default_locale: "en", currency: "BMD", latitude: "32.321384", longitude: "-64.75737", currency_name: "Bermudian Dollar", languages: ["en"], capital: "Hamilton", emoji: "🇧🇲", emojiU: "U+1F1E7 U+1F1F2", fips: "BD", internet: "BM", continent: "Americas", region: "West Indies" }, { name: "Bhutan", alpha2: "BT", alpha3: "BTN", numeric: "064", locales: ["dz"], default_locale: "dz", currency: "BTN", latitude: "27.514162", longitude: "90.433601", currency_name: "Ngultrum", languages: ["dz"], capital: "Thimphu", emoji: "🇧🇹", emojiU: "U+1F1E7 U+1F1F9", fips: "BT", internet: "BT", continent: "Asia", region: "South Asia" }, { name: "Bolivia", alpha2: "BO", alpha3: "BOL", numeric: "068", locales: ["es_BO"], default_locale: "es_BO", currency: "BOB", latitude: "-16.290154", longitude: "-63.588653", currency_name: "Bolivia", languages: ["es", "ay", "qu"], capital: "Sucre", emoji: "🇧🇴", emojiU: "U+1F1E7 U+1F1F4", fips: "BL", internet: "BO", continent: "Americas", region: "South America", alternate_names: ["Plurinational State of Bolivia"] }, { name: "Bonaire", alpha2: "BQ", alpha3: "BES", numeric: "535", locales: ["nl"], default_locale: "nl", currency: "USD", currency_name: "US Dollar", languages: ["nl"], capital: "Kralendijk", emoji: "🇧🇶", emojiU: "U+1F1E7 U+1F1F6", fips: "BQ", internet: "BQ", continent: "Americas", region: "West Indies", alternate_names: ["Bonaire, Sint Eustatius and Saba"] }, { name: "Bosnia and Herzegovina", alpha2: "BA", alpha3: "BIH", numeric: "070", locales: ["bs_BA", "sr_Cyrl_BA", "sr_Latn_BA"], default_locale: "bs_BA", currency: "BAM", latitude: "43.915886", longitude: "17.679076", currency_name: "Convertible Mark", languages: ["bs", "hr", "sr"], capital: "Sarajevo", emoji: "🇧🇦", emojiU: "U+1F1E7 U+1F1E6", fips: "BK", internet: "BA", continent: "Europe", region: "South East Europe" }, { name: "Botswana", alpha2: "BW", alpha3: "BWA", numeric: "072", locales: ["en_BW"], default_locale: "en_BW", currency: "BWP", latitude: "-22.328474", longitude: "24.684866", currency_name: "Pula", languages: ["en", "tn"], capital: "Gaborone", emoji: "🇧🇼", emojiU: "U+1F1E7 U+1F1FC", fips: "BC", internet: "BW", continent: "Africa", region: "Southern Africa" }, { name: "Bouvet Island", alpha2: "BV", alpha3: "BVT", numeric: "074", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "-54.423199", longitude: "3.413194", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "", emoji: "🇧🇻", emojiU: "U+1F1E7 U+1F1FB", fips: "BV", internet: "BV", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "Brazil", alpha2: "BR", alpha3: "BRA", numeric: "076", locales: ["pt_BR"], default_locale: "pt_BR", currency: "BRL", latitude: "-14.235004", longitude: "-51.92528", currency_name: "Brazilian Real", languages: ["pt"], capital: "Brasília", emoji: "🇧🇷", emojiU: "U+1F1E7 U+1F1F7", fips: "BR", internet: "BR", continent: "Americas", region: "South America" }, { name: "British Indian Ocean Territory", alpha2: "IO", alpha3: "IOT", numeric: "086", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-6.343194", longitude: "71.876519", currency_name: "US Dollar", languages: ["en"], capital: "Diego Garcia", emoji: "🇮🇴", emojiU: "U+1F1EE U+1F1F4", fips: "IO", internet: "IO", continent: "Asia", region: "South Asia" }, { name: "Brunei Darussalam", alpha2: "BN", alpha3: "BRN", numeric: "096", locales: ["ms_BN"], default_locale: "ms_BN", currency: "BND", latitude: "4.535277", longitude: "114.727669", currency_name: "Brunei Dollar", languages: ["ms"], capital: "Bandar Seri Begawan", emoji: "🇧🇳", emojiU: "U+1F1E7 U+1F1F3", fips: "BX", internet: "BN", continent: "Asia", region: "South East Asia" }, { name: "Bulgaria", alpha2: "BG", alpha3: "BGR", numeric: "100", locales: ["bg_BG"], default_locale: "bg_BG", currency: "BGN", latitude: "42.733883", longitude: "25.48583", currency_name: "Bulgarian Lev", languages: ["bg"], capital: "Sofia", emoji: "🇧🇬", emojiU: "U+1F1E7 U+1F1EC", fips: "BU", internet: "BG", continent: "Europe", region: "South East Europe" }, { name: "Burkina Faso", alpha2: "BF", alpha3: "BFA", numeric: "854", locales: ["fr_BF"], default_locale: "fr_BF", currency: "XOF", latitude: "12.238333", longitude: "-1.561593", currency_name: "CFA Franc BCEAO", languages: ["fr", "ff"], capital: "Ouagadougou", emoji: "🇧🇫", emojiU: "U+1F1E7 U+1F1EB", fips: "UV", internet: "BF", continent: "Africa", region: "Western Africa" }, { name: "Burundi", alpha2: "BI", alpha3: "BDI", numeric: "108", locales: ["fr_BI"], default_locale: "fr_BI", currency: "BIF", latitude: "-3.373056", longitude: "29.918886", currency_name: "Burundi Franc", languages: ["fr", "rn"], capital: "Bujumbura", emoji: "🇧🇮", emojiU: "U+1F1E7 U+1F1EE", fips: "BY", internet: "BI", continent: "Africa", region: "Central Africa" }, { name: "Cabo Verde", alpha2: "CV", alpha3: "CPV", numeric: "132", locales: ["kea_CV"], default_locale: "kea_CV", currency: "CVE", latitude: "16.002082", longitude: "-24.013197", currency_name: "Cabo Verde Escudo", languages: ["pt"], capital: "Praia", emoji: "🇨🇻", emojiU: "U+1F1E8 U+1F1FB", fips: "CV", internet: "CV", continent: "Africa", region: "Western Africa" }, { name: "Cambodia", alpha2: "KH", alpha3: "KHM", numeric: "116", locales: ["km_KH"], default_locale: "km_KH", currency: "KHR", latitude: "12.565679", longitude: "104.990963", currency_name: "Riel", languages: ["km"], capital: "Phnom Penh", emoji: "🇰🇭", emojiU: "U+1F1F0 U+1F1ED", fips: "CB", internet: "KH", continent: "Asia", region: "South East Asia" }, { name: "Cameroon", alpha2: "CM", alpha3: "CMR", numeric: "120", locales: ["fr_CM"], default_locale: "fr_CM", currency: "XAF", latitude: "7.369722", longitude: "12.354722", currency_name: "CFA Franc BEAC", languages: ["en", "fr"], capital: "Yaoundé", emoji: "🇨🇲", emojiU: "U+1F1E8 U+1F1F2", fips: "CM", internet: "CM", continent: "Africa", region: "Western Africa" }, { name: "Canada", alpha2: "CA", alpha3: "CAN", numeric: "124", locales: ["en_CA", "fr_CA"], default_locale: "en_CA", currency: "CAD", latitude: "56.130366", longitude: "-106.346771", currency_name: "Canadian Dollar", languages: ["en", "fr"], capital: "Ottawa", emoji: "🇨🇦", emojiU: "U+1F1E8 U+1F1E6", fips: "CA", internet: "CA", continent: "Americas", region: "North America" }, { name: "Cayman Islands", alpha2: "KY", alpha3: "CYM", numeric: "136", locales: ["en"], default_locale: "en", currency: "KYD", latitude: "19.513469", longitude: "-80.566956", currency_name: "Cayman Islands Dollar", languages: ["en"], capital: "George Town", emoji: "🇰🇾", emojiU: "U+1F1F0 U+1F1FE", fips: "CJ", internet: "KY", continent: "Americas", region: "West Indies" }, { name: "Central African Republic", alpha2: "CF", alpha3: "CAF", numeric: "140", locales: ["fr_CF", "sg_CF"], default_locale: "fr_CF", currency: "XAF", latitude: "6.611111", longitude: "20.939444", currency_name: "CFA Franc BEAC", languages: ["fr", "sg"], capital: "Bangui", emoji: "🇨🇫", emojiU: "U+1F1E8 U+1F1EB", fips: "CT", internet: "CF", continent: "Africa", region: "Central Africa" }, { name: "Chad", alpha2: "TD", alpha3: "TCD", numeric: "148", locales: ["fr_TD"], default_locale: "fr_TD", currency: "XAF", latitude: "15.454166", longitude: "18.732207", currency_name: "CFA Franc BEAC", languages: ["fr", "ar"], capital: "N'Djamena", emoji: "🇹🇩", emojiU: "U+1F1F9 U+1F1E9", fips: "CD", internet: "TD", continent: "Africa", region: "Central Africa" }, { name: "Chile", alpha2: "CL", alpha3: "CHL", numeric: "152", locales: ["es_CL"], default_locale: "es_CL", currency: "CLP", latitude: "-35.675147", longitude: "-71.542969", currency_name: "Chilean Peso", languages: ["es"], capital: "Santiago", emoji: "🇨🇱", emojiU: "U+1F1E8 U+1F1F1", fips: "CI", internet: "CL", continent: "Americas", region: "South America" }, { name: "China", alpha2: "CN", alpha3: "CHN", numeric: "156", locales: ["zh_CN", "zh_Hans_CN", "ii_CN", "bo_CN"], default_locale: "zh_CN", currency: "CNY", latitude: "35.86166", longitude: "104.195397", currency_name: "Yuan Renminbi", languages: ["zh"], capital: "Beijing", emoji: "🇨🇳", emojiU: "U+1F1E8 U+1F1F3", fips: "CH", internet: "CN", continent: "Asia", region: "East Asia" }, { name: "Christmas Island", alpha2: "CX", alpha3: "CXR", numeric: "162", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-10.447525", longitude: "105.690449", currency_name: "Australian Dollar", languages: ["en"], capital: "Flying Fish Cove", emoji: "🇨🇽", emojiU: "U+1F1E8 U+1F1FD", fips: "KT", internet: "CX", continent: "Asia", region: "South East Asia" }, { name: "Cocos Islands", alpha2: "CC", alpha3: "CCK", numeric: "166", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-12.164165", longitude: "96.870956", currency_name: "Australian Dollar", languages: ["en"], capital: "West Island", emoji: "🇨🇨", emojiU: "U+1F1E8 U+1F1E8", fips: "CK", internet: "CC", continent: "Asia", region: "South East Asia", alternate_names: ["Cocos Keeling Islands"] }, { name: "Colombia", alpha2: "CO", alpha3: "COL", numeric: "170", locales: ["es_CO"], default_locale: "es_CO", currency: "COP", latitude: "4.570868", longitude: "-74.297333", currency_name: "Colombian Peso", languages: ["es"], capital: "Bogotá", emoji: "🇨🇴", emojiU: "U+1F1E8 U+1F1F4", fips: "CO", internet: "CO", continent: "Americas", region: "South America" }, { name: "Comoros", alpha2: "KM", alpha3: "COM", numeric: "174", locales: ["fr_KM"], default_locale: "fr_KM", currency: "KMF", latitude: "-11.875001", longitude: "43.872219", currency_name: "Comorian Franc ", languages: ["ar", "fr"], capital: "Moroni", emoji: "🇰🇲", emojiU: "U+1F1F0 U+1F1F2", fips: "CN", internet: "KM", continent: "Africa", region: "Indian Ocean" }, { name: "Democratic Republic of the Congo", alpha2: "CD", alpha3: "COD", numeric: "180", locales: ["fr_CD"], default_locale: "fr_CD", currency: "CDF", latitude: "-4.038333", longitude: "21.758664", currency_name: "Congolese Franc", languages: ["fr", "ln", "kg", "sw", "lu"], capital: "Kinshasa", emoji: "🇨🇩", emojiU: "U+1F1E8 U+1F1E9", fips: "CG", internet: "ZR", continent: "Africa", region: "Central Africa" }, { name: "Congo", alpha2: "CG", alpha3: "COG", numeric: "178", locales: ["fr_CG"], default_locale: "fr_CG", currency: "XAF", latitude: "-0.228021", longitude: "15.827659", currency_name: "CFA Franc BEAC", languages: ["fr", "ln"], capital: "Brazzaville", emoji: "🇨🇬", emojiU: "U+1F1E8 U+1F1EC", fips: "CF", internet: "CG", continent: "Africa", region: "Central Africa" }, { name: "Cook Islands", alpha2: "CK", alpha3: "COK", numeric: "184", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-21.236736", longitude: "-159.777671", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Avarua", emoji: "🇨🇰", emojiU: "U+1F1E8 U+1F1F0", fips: "CW", internet: "CK", continent: "Oceania", region: "Pacific" }, { name: "Costa Rica", alpha2: "CR", alpha3: "CRI", numeric: "188", locales: ["es_CR"], default_locale: "es_CR", currency: "CRC", latitude: "9.748917", longitude: "-83.753428", currency_name: "Costa Rican Colon", languages: ["es"], capital: "San José", emoji: "🇨🇷", emojiU: "U+1F1E8 U+1F1F7", fips: "CS", internet: "CR", continent: "Americas", region: "Central America" }, { name: "Croatia", alpha2: "HR", alpha3: "HRV", numeric: "191", locales: ["hr_HR"], default_locale: "hr_HR", currency: "EUR", latitude: "45.1", longitude: "15.2", currency_name: "Euro", languages: ["hr"], capital: "Zagreb", emoji: "🇭🇷", emojiU: "U+1F1ED U+1F1F7", fips: "HR", internet: "HR", continent: "Europe", region: "South East Europe" }, { name: "Cuba", alpha2: "CU", alpha3: "CUB", numeric: "192", locales: ["es"], default_locale: "es", currency: "CUC", latitude: "21.521757", longitude: "-77.781167", currency_name: "Peso Convertible", languages: ["es"], capital: "Havana", emoji: "🇨🇺", emojiU: "U+1F1E8 U+1F1FA", fips: "CU", internet: "CU", continent: "Americas", region: "West Indies" }, { name: "Curaçao", alpha2: "CW", alpha3: "CUW", numeric: "531", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "pa", "en"], capital: "Willemstad", emoji: "🇨🇼", emojiU: "U+1F1E8 U+1F1FC", fips: "UC", internet: "CW", continent: "Americas", region: "West Indies" }, { name: "Cyprus", alpha2: "CY", alpha3: "CYP", numeric: "196", locales: ["el_CY"], default_locale: "el_CY", currency: "EUR", latitude: "35.126413", longitude: "33.429859", currency_name: "Euro", languages: ["el", "tr", "hy"], capital: "Nicosia", emoji: "🇨🇾", emojiU: "U+1F1E8 U+1F1FE", fips: "CY", internet: "CY", continent: "Asia", region: "South West Asia" }, { name: "Czechia", alpha2: "CZ", alpha3: "CZE", numeric: "203", locales: ["cs_CZ"], default_locale: "cs_CZ", currency: "CZK", latitude: "49.817492", longitude: "15.472962", currency_name: "Czech Koruna", languages: ["cs", "sk"], capital: "Prague", emoji: "🇨🇿", emojiU: "U+1F1E8 U+1F1FF", fips: "EZ", internet: "CZ", continent: "Europe", region: "Central Europe" }, { name: "Côte d'Ivoire", alpha2: "CI", alpha3: "CIV", numeric: "384", locales: ["fr_CI"], default_locale: "fr_CI", currency: "CZK", latitude: "7.539989", longitude: "-5.54708", currency_name: "Czech Koruna", languages: ["fr"], capital: "Yamoussoukro", emoji: "🇨🇮", emojiU: "U+1F1E8 U+1F1EE", fips: "IV", internet: "CI", continent: "Africa", region: "Western Africa" }, { name: "Denmark", alpha2: "DK", alpha3: "DNK", numeric: "208", locales: ["da_DK"], default_locale: "da_DK", currency: "DKK", latitude: "56.26392", longitude: "9.501785", currency_name: "Danish Krone", languages: ["da"], capital: "Copenhagen", emoji: "🇩🇰", emojiU: "U+1F1E9 U+1F1F0", fips: "DA", internet: "DK", continent: "Europe", region: "Northern Europe" }, { name: "Djibouti", alpha2: "DJ", alpha3: "DJI", numeric: "262", locales: ["fr_DJ", "so_DJ"], default_locale: "fr_DJ", currency: "DJF", latitude: "11.825138", longitude: "42.590275", currency_name: "Djibouti Franc", languages: ["fr", "ar"], capital: "Djibouti", emoji: "🇩🇯", emojiU: "U+1F1E9 U+1F1EF", fips: "DJ", internet: "DJ", continent: "Africa", region: "Eastern Africa" }, { name: "Dominica", alpha2: "DM", alpha3: "DMA", numeric: "212", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "15.414999", longitude: "-61.370976", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Roseau", emoji: "🇩🇲", emojiU: "U+1F1E9 U+1F1F2", fips: "DO", internet: "DM", continent: "Americas", region: "West Indies" }, { name: "Dominican Republic", alpha2: "DO", alpha3: "DOM", numeric: "214", locales: ["es_DO"], default_locale: "es_DO", currency: "DOP", latitude: "18.735693", longitude: "-70.162651", currency_name: "Dominican Peso", languages: ["es"], capital: "Santo Domingo", emoji: "🇩🇴", emojiU: "U+1F1E9 U+1F1F4", fips: "DR", internet: "DO", continent: "Americas", region: "West Indies" }, { name: "Ecuador", alpha2: "EC", alpha3: "ECU", numeric: "218", locales: ["es_EC"], default_locale: "es_EC", currency: "USD", latitude: "-1.831239", longitude: "-78.183406", currency_name: "US Dollar", languages: ["es"], capital: "Quito", emoji: "🇪🇨", emojiU: "U+1F1EA U+1F1E8", fips: "EC", internet: "EC", continent: "Americas", region: "South America" }, { name: "Egypt", alpha2: "EG", alpha3: "EGY", numeric: "818", locales: ["ar_EG"], default_locale: "ar_EG", currency: "EGP", latitude: "26.820553", longitude: "30.802498", currency_name: "Egyptian Pound", languages: ["ar"], capital: "Cairo", emoji: "🇪🇬", emojiU: "U+1F1EA U+1F1EC", fips: "EG", internet: "EG", continent: "Africa", region: "Northern Africa" }, { name: "El Salvador", alpha2: "SV", alpha3: "SLV", numeric: "222", locales: ["es_SV"], default_locale: "es_SV", currency: "USD", latitude: "13.794185", longitude: "-88.89653", currency_name: "US Dollar", languages: ["es"], capital: "San Salvador", emoji: "🇸🇻", emojiU: "U+1F1F8 U+1F1FB", fips: "ES", internet: "SV", continent: "Americas", region: "Central America" }, { name: "Equatorial Guinea", alpha2: "GQ", alpha3: "GNQ", numeric: "226", locales: ["fr_GQ", "es_GQ"], default_locale: "fr_GQ", currency: "XAF", latitude: "1.650801", longitude: "10.267895", currency_name: "CFA Franc BEAC", languages: ["es", "fr"], capital: "Malabo", emoji: "🇬🇶", emojiU: "U+1F1EC U+1F1F6", fips: "EK", internet: "GQ", continent: "Africa", region: "Western Africa" }, { name: "Eritrea", alpha2: "ER", alpha3: "ERI", numeric: "232", locales: ["ti_ER"], default_locale: "ti_ER", currency: "ERN", latitude: "15.179384", longitude: "39.782334", currency_name: "Nakfa", languages: ["ti", "ar", "en"], capital: "Asmara", emoji: "🇪🇷", emojiU: "U+1F1EA U+1F1F7", fips: "ER", internet: "ER", continent: "Africa", region: "Eastern Africa" }, { name: "Estonia", alpha2: "EE", alpha3: "EST", numeric: "233", locales: ["et_EE"], default_locale: "et_EE", currency: "EUR", latitude: "58.595272", longitude: "25.013607", currency_name: "Euro", languages: ["et"], capital: "Tallinn", emoji: "🇪🇪", emojiU: "U+1F1EA U+1F1EA", fips: "EN", internet: "EE", continent: "Europe", region: "Eastern Europe" }, { name: "Eswatini", alpha2: "SZ", alpha3: "SWZ", numeric: "748", locales: ["en"], default_locale: "en", currency: "EUR", latitude: "-26.522503", longitude: "31.465866", currency_name: "Euro", languages: ["en", "ss"], capital: "Lobamba", emoji: "🇸🇿", emojiU: "U+1F1F8 U+1F1FF", fips: "WZ", internet: "SZ", continent: "Africa", region: "Southern Africa" }, { name: "Ethiopia", alpha2: "ET", alpha3: "ETH", numeric: "231", locales: ["am_ET", "om_ET", "so_ET", "ti_ET"], default_locale: "am_ET", currency: "ETB", latitude: "9.145", longitude: "40.489673", currency_name: "Ethiopian Birr", languages: ["am"], capital: "Addis Ababa", emoji: "🇪🇹", emojiU: "U+1F1EA U+1F1F9", fips: "ET", internet: "ET", continent: "Africa", region: "Eastern Africa" }, { name: "Falkland Islands", alpha2: "FK", alpha3: "FLK", numeric: "238", locales: ["en"], default_locale: "en", currency: "DKK", latitude: "-51.796253", longitude: "-59.523613", currency_name: "Danish Krone", languages: ["en"], capital: "Stanley", emoji: "🇫🇰", emojiU: "U+1F1EB U+1F1F0", fips: "FA", internet: "FK", continent: "Americas", region: "South America", alternate_names: ["Malvinas Falkland Islands"] }, { name: "Faroe Islands", alpha2: "FO", alpha3: "FRO", numeric: "234", locales: ["fo_FO"], default_locale: "fo_FO", currency: "DKK", latitude: "61.892635", longitude: "-6.911806", currency_name: "Danish Krone", languages: ["fo"], capital: "Tórshavn", emoji: "🇫🇴", emojiU: "U+1F1EB U+1F1F4", fips: "FO", internet: "FO", continent: "Europe", region: "Northern Europe" }, { name: "Fiji", alpha2: "FJ", alpha3: "FJI", numeric: "242", locales: ["en"], default_locale: "en", currency: "FJD", latitude: "-16.578193", longitude: "179.414413", currency_name: "Fiji Dollar", languages: ["en", "fj", "hi", "ur"], capital: "Suva", emoji: "🇫🇯", emojiU: "U+1F1EB U+1F1EF", fips: "FJ", internet: "FJ", continent: "Oceania", region: "Pacific" }, { name: "Finland", alpha2: "FI", alpha3: "FIN", numeric: "246", locales: ["fi_FI", "sv_FI"], default_locale: "fi_FI", currency: "EUR", latitude: "61.92411", longitude: "25.748151", currency_name: "Euro", languages: ["fi", "sv"], capital: "Helsinki", emoji: "🇫🇮", emojiU: "U+1F1EB U+1F1EE", fips: "FI", internet: "FI", continent: "Europe", region: "Northern Europe" }, { name: "France", alpha2: "FR", alpha3: "FRA", numeric: "250", locales: ["fr_FR"], default_locale: "fr_FR", currency: "EUR", latitude: "46.227638", longitude: "2.213749", currency_name: "Euro", languages: ["fr"], capital: "Paris", emoji: "🇫🇷", emojiU: "U+1F1EB U+1F1F7", fips: "FR", internet: "FR", continent: "Europe", region: "Western Europe" }, { name: "French Guiana", alpha2: "GF", alpha3: "GUF", numeric: "254", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "3.933889", longitude: "-53.125782", currency_name: "Euro", languages: ["fr"], capital: "Cayenne", emoji: "🇬🇫", emojiU: "U+1F1EC U+1F1EB", fips: "FG", internet: "GF", continent: "Americas", region: "South America" }, { name: "French Polynesia", alpha2: "PF", alpha3: "PYF", numeric: "258", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-17.679742", longitude: "-149.406843", currency_name: "CFP Franc", languages: ["fr"], capital: "Papeetē", emoji: "🇵🇫", emojiU: "U+1F1F5 U+1F1EB", fips: "FP", internet: "PF", continent: "Oceania", region: "Pacific" }, { name: "French Southern Territories", alpha2: "TF", alpha3: "ATF", numeric: "260", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-49.280366", longitude: "69.348557", currency_name: "Euro", languages: ["fr"], capital: "Port-aux-Français", emoji: "🇹🇫", emojiU: "U+1F1F9 U+1F1EB", fips: "FS", internet: "--", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Gabon", alpha2: "GA", alpha3: "GAB", numeric: "266", locales: ["fr_GA"], default_locale: "fr_GA", currency: "XAF", latitude: "-0.803689", longitude: "11.609444", currency_name: "CFA Franc BEAC", languages: ["fr"], capital: "Libreville", emoji: "🇬🇦", emojiU: "U+1F1EC U+1F1E6", fips: "GB", internet: "GA", continent: "Africa", region: "Western Africa" }, { name: "Gambia", alpha2: "GM", alpha3: "GMB", numeric: "270", locales: ["en"], default_locale: "en", currency: "GMD", latitude: "13.443182", longitude: "-15.310139", currency_name: "Dalasi", languages: ["en"], capital: "Banjul", emoji: "🇬🇲", emojiU: "U+1F1EC U+1F1F2", fips: "GA", internet: "GM", continent: "Africa", region: "Western Africa" }, { name: "Georgia", alpha2: "GE", alpha3: "GEO", numeric: "268", locales: ["ka_GE"], default_locale: "ka_GE", currency: "GEL", latitude: "42.315407", longitude: "43.356892", currency_name: "Lari", languages: ["ka"], capital: "Tbilisi", emoji: "🇬🇪", emojiU: "U+1F1EC U+1F1EA", fips: "GG", internet: "GE", continent: "Asia", region: "South West Asia" }, { name: "Germany", alpha2: "DE", alpha3: "DEU", numeric: "276", locales: ["de_DE"], default_locale: "de_DE", currency: "EUR", latitude: "51.165691", longitude: "10.451526", currency_name: "Euro", languages: ["de"], capital: "Berlin", emoji: "🇩🇪", emojiU: "U+1F1E9 U+1F1EA", fips: "GM", internet: "DE", continent: "Europe", region: "Western Europe" }, { name: "Ghana", alpha2: "GH", alpha3: "GHA", numeric: "288", locales: ["ak_GH", "ee_GH", "ha_Latn_GH"], default_locale: "ak_GH", currency: "GHS", latitude: "7.946527", longitude: "-1.023194", currency_name: "Ghana Cedi", languages: ["en"], capital: "Accra", emoji: "🇬🇭", emojiU: "U+1F1EC U+1F1ED", fips: "GH", internet: "GH", continent: "Africa", region: "Western Africa" }, { name: "Gibraltar", alpha2: "GI", alpha3: "GIB", numeric: "292", locales: ["en"], default_locale: "en", currency: "GIP", latitude: "36.137741", longitude: "-5.345374", currency_name: "Gibraltar Pound", languages: ["en"], capital: "Gibraltar", emoji: "🇬🇮", emojiU: "U+1F1EC U+1F1EE", fips: "GI", internet: "GI", continent: "Europe", region: "South West Europe" }, { name: "Greece", alpha2: "GR", alpha3: "GRC", numeric: "300", locales: ["el_GR"], default_locale: "el_GR", currency: "EUR", latitude: "39.074208", longitude: "21.824312", currency_name: "Euro", languages: ["el"], capital: "Athens", emoji: "🇬🇷", emojiU: "U+1F1EC U+1F1F7", fips: "GR", internet: "GR", continent: "Europe", region: "South East Europe" }, { name: "Greenland", alpha2: "GL", alpha3: "GRL", numeric: "304", locales: ["kl_GL"], default_locale: "kl_GL", currency: "DKK", latitude: "71.706936", longitude: "-42.604303", currency_name: "Danish Krone", languages: ["kl"], capital: "Nuuk", emoji: "🇬🇱", emojiU: "U+1F1EC U+1F1F1", fips: "GL", internet: "GL", continent: "Americas", region: "North America" }, { name: "Grenada", alpha2: "GD", alpha3: "GRD", numeric: "308", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.262776", longitude: "-61.604171", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "St. George's", emoji: "🇬🇩", emojiU: "U+1F1EC U+1F1E9", fips: "GJ", internet: "GD", continent: "Americas", region: "West Indies" }, { name: "Guadeloupe", alpha2: "GP", alpha3: "GLP", numeric: "312", locales: ["fr_GP"], default_locale: "fr_GP", currency: "EUR", latitude: "16.995971", longitude: "-62.067641", currency_name: "Euro", languages: ["fr"], capital: "Basse-Terre", emoji: "🇬🇵", emojiU: "U+1F1EC U+1F1F5", fips: "GP", internet: "GP", continent: "Americas", region: "West Indies" }, { name: "Guam", alpha2: "GU", alpha3: "GUM", numeric: "316", locales: ["en_GU"], default_locale: "en_GU", currency: "USD", latitude: "13.444304", longitude: "144.793731", currency_name: "US Dollar", languages: ["en", "ch", "es"], capital: "Hagåtña", emoji: "🇬🇺", emojiU: "U+1F1EC U+1F1FA", fips: "GQ", internet: "GU", continent: "Oceania", region: "Pacific" }, { name: "Guatemala", alpha2: "GT", alpha3: "GTM", numeric: "320", locales: ["es_GT"], default_locale: "es_GT", currency: "GTQ", latitude: "15.783471", longitude: "-90.230759", currency_name: "Quetzal", languages: ["es"], capital: "Guatemala City", emoji: "🇬🇹", emojiU: "U+1F1EC U+1F1F9", fips: "GT", internet: "GT", continent: "Americas", region: "Central America" }, { name: "Guernsey", alpha2: "GG", alpha3: "GGY", numeric: "831", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.465691", longitude: "-2.585278", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "St. Peter Port", emoji: "🇬🇬", emojiU: "U+1F1EC U+1F1EC", fips: "GK", internet: "GG", continent: "Europe", region: "Western Europe" }, { name: "Guinea", alpha2: "GN", alpha3: "GIN", numeric: "324", locales: ["fr_GN"], default_locale: "fr_GN", currency: "GNF", latitude: "9.945587", longitude: "-9.696645", currency_name: "Guinean Franc", languages: ["fr", "ff"], capital: "Conakry", emoji: "🇬🇳", emojiU: "U+1F1EC U+1F1F3", fips: "GV", internet: "GN", continent: "Africa", region: "Western Africa" }, { name: "Guinea-Bissau", alpha2: "GW", alpha3: "GNB", numeric: "624", locales: ["pt_GW"], default_locale: "pt_GW", currency: "XOF", latitude: "11.803749", longitude: "-15.180413", currency_name: "CFA Franc BCEAO", languages: ["pt"], capital: "Bissau", emoji: "🇬🇼", emojiU: "U+1F1EC U+1F1FC", fips: "PU", internet: "GW", continent: "Africa", region: "Western Africa" }, { name: "Guyana", alpha2: "GY", alpha3: "GUY", numeric: "328", locales: ["en"], default_locale: "en", currency: "GYD", latitude: "4.860416", longitude: "-58.93018", currency_name: "Guyana Dollar", languages: ["en"], capital: "Georgetown", emoji: "🇬🇾", emojiU: "U+1F1EC U+1F1FE", fips: "GY", internet: "GY", continent: "Americas", region: "South America" }, { name: "Haiti", alpha2: "HT", alpha3: "HTI", numeric: "332", locales: ["fr"], default_locale: "fr", currency: "USD", latitude: "18.971187", longitude: "-72.285215", currency_name: "US Dollar", languages: ["fr", "ht"], capital: "Port-au-Prince", emoji: "🇭🇹", emojiU: "U+1F1ED U+1F1F9", fips: "HA", internet: "HT", continent: "Americas", region: "West Indies" }, { name: "Heard Island and McDonald Islands", alpha2: "HM", alpha3: "HMD", numeric: "334", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-53.08181", longitude: "73.504158", currency_name: "Australian Dollar", languages: ["en"], capital: "", emoji: "🇭🇲", emojiU: "U+1F1ED U+1F1F2", fips: "HM", internet: "HM", continent: "Indian Ocean", region: "Southern Indian Ocean" }, { name: "Holy See", alpha2: "VA", alpha3: "VAT", numeric: "336", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "41.902916", longitude: "12.453389", currency_name: "Euro", languages: ["it", "la"], capital: "Vatican City", emoji: "🇻🇦", emojiU: "U+1F1FB U+1F1E6", fips: "VT", internet: "VA", continent: "Europe", region: "Southern Europe" }, { name: "Honduras", alpha2: "HN", alpha3: "HND", numeric: "340", locales: ["es_HN"], default_locale: "es_HN", currency: "HNL", latitude: "15.199999", longitude: "-86.241905", currency_name: "Lempira", languages: ["es"], capital: "Tegucigalpa", emoji: "🇭🇳", emojiU: "U+1F1ED U+1F1F3", fips: "HO", internet: "HN", continent: "Americas", region: "Central America" }, { name: "Hong Kong", alpha2: "HK", alpha3: "HKG", numeric: "344", locales: ["yue_Hant_HK", "zh_Hans_HK", "zh_Hant_HK", "en_HK"], default_locale: "en_HK", currency: "HKD", latitude: "22.396428", longitude: "114.109497", currency_name: "Hong Kong Dollar", languages: ["zh", "en"], capital: "City of Victoria", emoji: "🇭🇰", emojiU: "U+1F1ED U+1F1F0", fips: "HK", internet: "HK", continent: "Asia", region: "East Asia" }, { name: "Hungary", alpha2: "HU", alpha3: "HUN", numeric: "348", locales: ["hu_HU"], default_locale: "hu_HU", currency: "HUF", latitude: "47.162494", longitude: "19.503304", currency_name: "Forint", languages: ["hu"], capital: "Budapest", emoji: "🇭🇺", emojiU: "U+1F1ED U+1F1FA", fips: "HU", internet: "HU", continent: "Europe", region: "Central Europe" }, { name: "Iceland", alpha2: "IS", alpha3: "ISL", numeric: "352", locales: ["is_IS"], default_locale: "is_IS", currency: "ISK", latitude: "64.963051", longitude: "-19.020835", currency_name: "Iceland Krona", languages: ["is"], capital: "Reykjavik", emoji: "🇮🇸", emojiU: "U+1F1EE U+1F1F8", fips: "IC", internet: "IS", continent: "Europe", region: "Northern Europe" }, { name: "India", alpha2: "IN", alpha3: "IND", numeric: "356", locales: ["as_IN", "bn_IN", "en_IN", "gu_IN", "hi_IN", "kn_IN", "kok_IN", "ml_IN", "mr_IN", "ne_IN", "or_IN", "pa_Guru_IN", "ta_IN", "te_IN", "bo_IN", "ur_IN"], default_locale: "hi_IN", currency: "INR", latitude: "20.593684", longitude: "78.96288", currency_name: "Indian Rupee", languages: ["hi", "en"], capital: "New Delhi", emoji: "🇮🇳", emojiU: "U+1F1EE U+1F1F3", fips: "IN", internet: "IN", continent: "Asia", region: "South Asia" }, { name: "Indonesia", alpha2: "ID", alpha3: "IDN", numeric: "360", locales: ["id_ID"], default_locale: "id_ID", currency: "IDR", latitude: "-0.789275", longitude: "113.921327", currency_name: "Rupiah", languages: ["id"], capital: "Jakarta", emoji: "🇮🇩", emojiU: "U+1F1EE U+1F1E9", fips: "ID", internet: "ID", continent: "Asia", region: "South East Asia" }, { name: "Iran", alpha2: "IR", alpha3: "IRN", numeric: "364", locales: ["fa_IR"], default_locale: "fa_IR", currency: "XDR", latitude: "32.427908", longitude: "53.688046", currency_name: "SDR (Special Drawing Right)", languages: ["fa"], capital: "Tehran", emoji: "🇮🇷", emojiU: "U+1F1EE U+1F1F7", fips: "IR", internet: "IR", continent: "Asia", region: "South West Asia", alternate_names: ["Islamic Republic of Iran"] }, { name: "Iraq", alpha2: "IQ", alpha3: "IRQ", numeric: "368", locales: ["ar_IQ"], default_locale: "ar_IQ", currency: "IQD", latitude: "33.223191", longitude: "43.679291", currency_name: "Iraqi Dinar", languages: ["ar", "ku"], capital: "Baghdad", emoji: "🇮🇶", emojiU: "U+1F1EE U+1F1F6", fips: "IZ", internet: "IQ", continent: "Asia", region: "South West Asia" }, { name: "Ireland", alpha2: "IE", alpha3: "IRL", numeric: "372", locales: ["en_IE", "ga_IE"], default_locale: "en_IE", currency: "EUR", latitude: "53.41291", longitude: "-8.24389", currency_name: "Euro", languages: ["ga", "en"], capital: "Dublin", emoji: "🇮🇪", emojiU: "U+1F1EE U+1F1EA", fips: "EI", internet: "IE", continent: "Europe", region: "Western Europe" }, { name: "Isle of Man", alpha2: "IM", alpha3: "IMN", numeric: "833", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "54.236107", longitude: "-4.548056", currency_name: "Pound Sterling", languages: ["en", "gv"], capital: "Douglas", emoji: "🇮🇲", emojiU: "U+1F1EE U+1F1F2", fips: "IM", internet: "IM", continent: "Europe", region: "Western Europe" }, { name: "Israel", alpha2: "IL", alpha3: "ISR", numeric: "376", locales: ["en_IL", "he_IL"], default_locale: "he_IL", currency: "ILS", latitude: "31.046051", longitude: "34.851612", currency_name: "New Israeli Sheqel", languages: ["he", "ar"], capital: "Jerusalem", emoji: "🇮🇱", emojiU: "U+1F1EE U+1F1F1", fips: "IS", internet: "IL", continent: "Asia", region: "South West Asia" }, { name: "Italy", alpha2: "IT", alpha3: "ITA", numeric: "380", locales: ["it_IT"], default_locale: "it_IT", currency: "EUR", latitude: "41.87194", longitude: "12.56738", currency_name: "Euro", languages: ["it"], capital: "Rome", emoji: "🇮🇹", emojiU: "U+1F1EE U+1F1F9", fips: "IT", internet: "IT", continent: "Europe", region: "Southern Europe" }, { name: "Jamaica", alpha2: "JM", alpha3: "JAM", numeric: "388", locales: ["en_JM"], default_locale: "en_JM", currency: "JMD", latitude: "18.109581", longitude: "-77.297508", currency_name: "Jamaican Dollar", languages: ["en"], capital: "Kingston", emoji: "🇯🇲", emojiU: "U+1F1EF U+1F1F2", fips: "JM", internet: "JM", continent: "Americas", region: "West Indies" }, { name: "Japan", alpha2: "JP", alpha3: "JPN", numeric: "392", locales: ["ja_JP"], default_locale: "ja_JP", currency: "JPY", latitude: "36.204824", longitude: "138.252924", currency_name: "Yen", languages: ["ja"], capital: "Tokyo", emoji: "🇯🇵", emojiU: "U+1F1EF U+1F1F5", fips: "JA", internet: "JP", continent: "Asia", region: "East Asia" }, { name: "Jersey", alpha2: "JE", alpha3: "JEY", numeric: "832", locales: ["en"], default_locale: "en", currency: "GBP", latitude: "49.214439", longitude: "-2.13125", currency_name: "Pound Sterling", languages: ["en", "fr"], capital: "Saint Helier", emoji: "🇯🇪", emojiU: "U+1F1EF U+1F1EA", fips: "JE", internet: "JE", continent: "Europe", region: "Western Europe" }, { name: "Jordan", alpha2: "JO", alpha3: "JOR", numeric: "400", locales: ["ar_JO"], default_locale: "ar_JO", currency: "JOD", latitude: "30.585164", longitude: "36.238414", currency_name: "Jordanian Dinar", languages: ["ar"], capital: "Amman", emoji: "🇯🇴", emojiU: "U+1F1EF U+1F1F4", fips: "JO", internet: "JO", continent: "Asia", region: "South West Asia" }, { name: "Kazakhstan", alpha2: "KZ", alpha3: "KAZ", numeric: "398", locales: ["kk_Cyrl_KZ"], default_locale: "kk_Cyrl_KZ", currency: "KZT", latitude: "48.019573", longitude: "66.923684", currency_name: "Tenge", languages: ["kk", "ru"], capital: "Astana", emoji: "🇰🇿", emojiU: "U+1F1F0 U+1F1FF", fips: "KZ", internet: "KZ", continent: "Asia", region: "Central Asia" }, { name: "Kenya", alpha2: "KE", alpha3: "KEN", numeric: "404", locales: ["ebu_KE", "guz_KE", "kln_KE", "kam_KE", "ki_KE", "luo_KE", "luy_KE", "mas_KE", "mer_KE", "om_KE", "saq_KE", "so_KE", "sw_KE", "dav_KE", "teo_KE"], default_locale: "ebu_KE", currency: "KES", latitude: "-0.023559", longitude: "37.906193", currency_name: "Kenyan Shilling", languages: ["en", "sw"], capital: "Nairobi", emoji: "🇰🇪", emojiU: "U+1F1F0 U+1F1EA", fips: "KE", internet: "KE", continent: "Africa", region: "Eastern Africa" }, { name: "Kiribati", alpha2: "KI", alpha3: "KIR", numeric: "296", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-3.370417", longitude: "-168.734039", currency_name: "Australian Dollar", languages: ["en"], capital: "South Tarawa", emoji: "🇰🇮", emojiU: "U+1F1F0 U+1F1EE", fips: "KR", internet: "KI", continent: "Oceania", region: "Pacific" }, { name: "North Korea", alpha2: "KP", alpha3: "PRK", numeric: "408", locales: ["ko"], default_locale: "ko", currency: "KPW", latitude: "40.339852", longitude: "127.510093", currency_name: "North Korean Won", languages: ["ko"], capital: "Pyongyang", emoji: "🇰🇵", emojiU: "U+1F1F0 U+1F1F5", fips: "KN", internet: "KP", continent: "Asia", region: "East Asia", alternate_names: ["Democratic People's Republic of Korea"] }, { name: "South Korea", alpha2: "KR", alpha3: "KOR", numeric: "410", locales: ["ko_KR"], default_locale: "ko_KR", currency: "KRW", latitude: "35.907757", longitude: "127.766922", currency_name: "Won", languages: ["ko"], capital: "Seoul", emoji: "🇰🇷", emojiU: "U+1F1F0 U+1F1F7", fips: "KS", internet: "KR", continent: "Asia", region: "East Asia", alternate_names: ["Republic of Korea"] }, { name: "Kuwait", alpha2: "KW", alpha3: "KWT", numeric: "414", locales: ["ar_KW"], default_locale: "ar_KW", currency: "KWD", latitude: "29.31166", longitude: "47.481766", currency_name: "Kuwaiti Dinar", languages: ["ar"], capital: "Kuwait City", emoji: "🇰🇼", emojiU: "U+1F1F0 U+1F1FC", fips: "KU", internet: "KW", continent: "Asia", region: "South West Asia" }, { name: "Kyrgyzstan", alpha2: "KG", alpha3: "KGZ", numeric: "417", locales: ["ky"], default_locale: "ky", currency: "KGS", latitude: "41.20438", longitude: "74.766098", currency_name: "Som", languages: ["ky", "ru"], capital: "Bishkek", emoji: "🇰🇬", emojiU: "U+1F1F0 U+1F1EC", fips: "KG", internet: "KG", continent: "Asia", region: "Central Asia" }, { name: "Lao People's Democratic Republic", alpha2: "LA", alpha3: "LAO", numeric: "418", locales: ["lo"], default_locale: "lo", currency: "LAK", latitude: "19.85627", longitude: "102.495496", currency_name: "Lao Kip", languages: ["lo"], capital: "Vientiane", emoji: "🇱🇦", emojiU: "U+1F1F1 U+1F1E6", fips: "LA", internet: "LA", continent: "Asia", region: "South East Asia" }, { name: "Latvia", alpha2: "LV", alpha3: "LVA", numeric: "428", locales: ["lv_LV"], default_locale: "lv_LV", currency: "EUR", latitude: "56.879635", longitude: "24.603189", currency_name: "Euro", languages: ["lv"], capital: "Riga", emoji: "🇱🇻", emojiU: "U+1F1F1 U+1F1FB", fips: "LG", internet: "LV", continent: "Europe", region: "Eastern Europe" }, { name: "Lebanon", alpha2: "LB", alpha3: "LBN", numeric: "422", locales: ["ar_LB"], default_locale: "ar_LB", currency: "LBP", latitude: "33.854721", longitude: "35.862285", currency_name: "Lebanese Pound", languages: ["ar", "fr"], capital: "Beirut", emoji: "🇱🇧", emojiU: "U+1F1F1 U+1F1E7", fips: "LE", internet: "LB", continent: "Asia", region: "South West Asia" }, { name: "Lesotho", alpha2: "LS", alpha3: "LSO", numeric: "426", locales: ["en"], default_locale: "en", currency: "ZAR", latitude: "-29.609988", longitude: "28.233608", currency_name: "Rand", languages: ["en", "st"], capital: "Maseru", emoji: "🇱🇸", emojiU: "U+1F1F1 U+1F1F8", fips: "LT", internet: "LS", continent: "Africa", region: "Southern Africa" }, { name: "Liberia", alpha2: "LR", alpha3: "LBR", numeric: "430", locales: ["en"], default_locale: "en", currency: "LRD", latitude: "6.428055", longitude: "-9.429499", currency_name: "Liberian Dollar", languages: ["en"], capital: "Monrovia", emoji: "🇱🇷", emojiU: "U+1F1F1 U+1F1F7", fips: "LI", internet: "LR", continent: "Africa", region: "Western Africa" }, { name: "Libya", alpha2: "LY", alpha3: "LBY", numeric: "434", locales: ["ar_LY"], default_locale: "ar_LY", currency: "LYD", latitude: "26.3351", longitude: "17.228331", currency_name: "Libyan Dinar", languages: ["ar"], capital: "Tripoli", emoji: "🇱🇾", emojiU: "U+1F1F1 U+1F1FE", fips: "LY", internet: "LY", continent: "Africa", region: "Northern Africa" }, { name: "Liechtenstein", alpha2: "LI", alpha3: "LIE", numeric: "438", locales: ["de_LI"], default_locale: "de_LI", currency: "CHF", latitude: "47.166", longitude: "9.555373", currency_name: "Swiss Franc", languages: ["de"], capital: "Vaduz", emoji: "🇱🇮", emojiU: "U+1F1F1 U+1F1EE", fips: "LS", internet: "LI", continent: "Europe", region: "Central Europe" }, { name: "Lithuania", alpha2: "LT", alpha3: "LTU", numeric: "440", locales: ["lt_LT"], default_locale: "lt_LT", currency: "EUR", latitude: "55.169438", longitude: "23.881275", currency_name: "Euro", languages: ["lt"], capital: "Vilnius", emoji: "🇱🇹", emojiU: "U+1F1F1 U+1F1F9", fips: "LH", internet: "LT", continent: "Europe", region: "Eastern Europe" }, { name: "Luxembourg", alpha2: "LU", alpha3: "LUX", numeric: "442", locales: ["fr_LU", "de_LU"], default_locale: "fr_LU", currency: "EUR", latitude: "49.815273", longitude: "6.129583", currency_name: "Euro", languages: ["fr", "de", "lb"], capital: "Luxembourg", emoji: "🇱🇺", emojiU: "U+1F1F1 U+1F1FA", fips: "LU", internet: "LU", continent: "Europe", region: "Western Europe" }, { name: "Macao", alpha2: "MO", alpha3: "MAC", numeric: "446", locales: ["zh_Hans_MO", "zh_Hant_MO"], default_locale: "zh_Hans_MO", currency: "MOP", latitude: "22.198745", longitude: "113.543873", currency_name: "Pataca", languages: ["zh", "pt"], capital: "", emoji: "🇲🇴", emojiU: "U+1F1F2 U+1F1F4", fips: "MC", internet: "MO", continent: "Asia", region: "East Asia" }, { name: "Madagascar", alpha2: "MG", alpha3: "MDG", numeric: "450", locales: ["fr_MG", "mg_MG"], default_locale: "fr_MG", currency: "MGA", latitude: "-18.766947", longitude: "46.869107", currency_name: "Malagasy Ariary", languages: ["fr", "mg"], capital: "Antananarivo", emoji: "🇲🇬", emojiU: "U+1F1F2 U+1F1EC", fips: "MA", internet: "MG", continent: "Africa", region: "Indian Ocean" }, { name: "Malawi", alpha2: "MW", alpha3: "MWI", numeric: "454", locales: ["en"], default_locale: "en", currency: "MWK", latitude: "-13.254308", longitude: "34.301525", currency_name: "Malawi Kwacha", languages: ["en", "ny"], capital: "Lilongwe", emoji: "🇲🇼", emojiU: "U+1F1F2 U+1F1FC", fips: "MI", internet: "MW", continent: "Africa", region: "Southern Africa" }, { name: "Malaysia", alpha2: "MY", alpha3: "MYS", numeric: "458", locales: ["ms_MY"], default_locale: "ms_MY", currency: "MYR", latitude: "4.210484", longitude: "101.975766", currency_name: "Malaysian Ringgit", languages: ["ms"], capital: "Kuala Lumpur", emoji: "🇲🇾", emojiU: "U+1F1F2 U+1F1FE", fips: "MY", internet: "MY", continent: "Asia", region: "South East Asia" }, { name: "Maldives", alpha2: "MV", alpha3: "MDV", numeric: "462", locales: ["dv"], default_locale: "dv", currency: "MVR", latitude: "3.202778", longitude: "73.22068", currency_name: "Rufiyaa", languages: ["dv"], capital: "Malé", emoji: "🇲🇻", emojiU: "U+1F1F2 U+1F1FB", fips: "MV", internet: "MV", continent: "Asia", region: "South Asia" }, { name: "Mali", alpha2: "ML", alpha3: "MLI", numeric: "466", locales: ["bm_ML", "fr_ML", "khq_ML", "ses_ML"], default_locale: "fr_ML", currency: "XOF", latitude: "17.570692", longitude: "-3.996166", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Bamako", emoji: "🇲🇱", emojiU: "U+1F1F2 U+1F1F1", fips: "ML", internet: "ML", continent: "Africa", region: "Western Africa" }, { name: "Malta", alpha2: "MT", alpha3: "MLT", numeric: "470", locales: ["en_MT", "mt_MT"], default_locale: "en_MT", currency: "EUR", latitude: "35.937496", longitude: "14.375416", currency_name: "Euro", languages: ["mt", "en"], capital: "Valletta", emoji: "🇲🇹", emojiU: "U+1F1F2 U+1F1F9", fips: "MT", internet: "MT", continent: "Europe", region: "Southern Europe" }, { name: "Marshall Islands", alpha2: "MH", alpha3: "MHL", numeric: "584", locales: ["en_MH"], default_locale: "en_MH", currency: "USD", latitude: "7.131474", longitude: "171.184478", currency_name: "US Dollar", languages: ["en", "mh"], capital: "Majuro", emoji: "🇲🇭", emojiU: "U+1F1F2 U+1F1ED", fips: "RM", internet: "MH", continent: "Oceania", region: "Pacific" }, { name: "Martinique", alpha2: "MQ", alpha3: "MTQ", numeric: "474", locales: ["fr_MQ"], default_locale: "fr_MQ", currency: "EUR", latitude: "14.641528", longitude: "-61.024174", currency_name: "Euro", languages: ["fr"], capital: "Fort-de-France", emoji: "🇲🇶", emojiU: "U+1F1F2 U+1F1F6", fips: "MB", internet: "MQ", continent: "Americas", region: "West Indies" }, { name: "Mauritania", alpha2: "MR", alpha3: "MRT", numeric: "478", locales: ["ar"], default_locale: "ar", currency: "MRU", latitude: "21.00789", longitude: "-10.940835", currency_name: "Ouguiya", languages: ["ar"], capital: "Nouakchott", emoji: "🇲🇷", emojiU: "U+1F1F2 U+1F1F7", fips: "MR", internet: "MR", continent: "Africa", region: "Western Africa" }, { name: "Mauritius", alpha2: "MU", alpha3: "MUS", numeric: "480", locales: ["en_MU", "mfe_MU"], default_locale: "en_MU", currency: "MUR", latitude: "-20.348404", longitude: "57.552152", currency_name: "Mauritius Rupee", languages: ["en"], capital: "Port Louis", emoji: "🇲🇺", emojiU: "U+1F1F2 U+1F1FA", fips: "MP", internet: "MU", continent: "Africa", region: "Indian Ocean" }, { name: "Mayotte", alpha2: "YT", alpha3: "MYT", numeric: "175", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "-12.8275", longitude: "45.166244", currency_name: "Euro", languages: ["fr"], capital: "Mamoudzou", emoji: "🇾🇹", emojiU: "U+1F1FE U+1F1F9", fips: "MF", internet: "YT", continent: "Africa", region: "Indian Ocean" }, { name: "Mexico", alpha2: "MX", alpha3: "MEX", numeric: "484", locales: ["es_MX"], default_locale: "es_MX", currency: "MXN", latitude: "23.634501", longitude: "-102.552784", currency_name: "Mexican Peso", languages: ["es"], capital: "Mexico City", emoji: "🇲🇽", emojiU: "U+1F1F2 U+1F1FD", fips: "MX", internet: "MX", continent: "Americas", region: "Central America" }, { name: "Micronesia", alpha2: "FM", alpha3: "FSM", numeric: "583", locales: ["en"], default_locale: "en", currency: "RUB", latitude: "7.425554", longitude: "150.550812", currency_name: "Russian Ruble", languages: ["en"], capital: "Palikir", emoji: "🇫🇲", emojiU: "U+1F1EB U+1F1F2", fips: "", internet: "FM", continent: "Oceania", region: "Pacific", alternate_names: ["Federated States of Micronesia"] }, { name: "Moldova", alpha2: "MD", alpha3: "MDA", numeric: "498", locales: ["ro_MD", "ru_MD"], default_locale: "ro_MD", currency: "MDL", latitude: "47.411631", longitude: "28.369885", currency_name: "Moldovan Leu", languages: ["ro"], capital: "Chișinău", emoji: "🇲🇩", emojiU: "U+1F1F2 U+1F1E9", fips: "MD", internet: "MD", continent: "Europe", region: "Eastern Europe", alternate_names: ["Republic of Moldova"] }, { name: "Monaco", alpha2: "MC", alpha3: "MCO", numeric: "492", locales: ["fr_MC"], default_locale: "fr_MC", currency: "EUR", latitude: "43.750298", longitude: "7.412841", currency_name: "Euro", languages: ["fr"], capital: "Monaco", emoji: "🇲🇨", emojiU: "U+1F1F2 U+1F1E8", fips: "MN", internet: "MC", continent: "Europe", region: "Western Europe" }, { name: "Mongolia", alpha2: "MN", alpha3: "MNG", numeric: "496", locales: ["mn"], default_locale: "mn", currency: "MNT", latitude: "46.862496", longitude: "103.846656", currency_name: "Tugrik", languages: ["mn"], capital: "Ulan Bator", emoji: "🇲🇳", emojiU: "U+1F1F2 U+1F1F3", fips: "MG", internet: "MN", continent: "Asia", region: "Northern Asia" }, { name: "Montenegro", alpha2: "ME", alpha3: "MNE", numeric: "499", locales: ["sr_Cyrl_ME", "sr_Latn_ME"], default_locale: "sr_Cyrl_ME", currency: "EUR", latitude: "42.708678", longitude: "19.37439", currency_name: "Euro", languages: ["sr", "bs", "sq", "hr"], capital: "Podgorica", emoji: "🇲🇪", emojiU: "U+1F1F2 U+1F1EA", fips: "MJ", internet: "ME", continent: "Europe", region: "South East Europe" }, { name: "Montserrat", alpha2: "MS", alpha3: "MSR", numeric: "500", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "16.742498", longitude: "-62.187366", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Plymouth", emoji: "🇲🇸", emojiU: "U+1F1F2 U+1F1F8", fips: "MH", internet: "MS", continent: "Americas", region: "West Indies" }, { name: "Morocco", alpha2: "MA", alpha3: "MAR", numeric: "504", locales: ["ar_MA", "tzm_Latn_MA", "shi_Latn_MA", "shi_Tfng_MA"], default_locale: "ar_MA", currency: "MAD", latitude: "31.791702", longitude: "-7.09262", currency_name: "Moroccan Dirham", languages: ["ar"], capital: "Rabat", emoji: "🇲🇦", emojiU: "U+1F1F2 U+1F1E6", fips: "MO", internet: "MA", continent: "Africa", region: "Northern Africa" }, { name: "Mozambique", alpha2: "MZ", alpha3: "MOZ", numeric: "508", locales: ["pt_MZ", "seh_MZ"], default_locale: "pt_MZ", currency: "MZN", latitude: "-18.665695", longitude: "35.529562", currency_name: "Mozambique Metical", languages: ["pt"], capital: "Maputo", emoji: "🇲🇿", emojiU: "U+1F1F2 U+1F1FF", fips: "MZ", internet: "MZ", continent: "Africa", region: "Southern Africa" }, { name: "Myanmar", alpha2: "MM", alpha3: "MMR", numeric: "104", locales: ["my_MM"], default_locale: "my_MM", currency: "MMK", latitude: "21.913965", longitude: "95.956223", currency_name: "Kyat", languages: ["my"], capital: "Naypyidaw", emoji: "🇲🇲", emojiU: "U+1F1F2 U+1F1F2", fips: "BM", internet: "MM", continent: "Asia", region: "South East Asia" }, { name: "Namibia", alpha2: "NA", alpha3: "NAM", numeric: "516", locales: ["af_NA", "en_NA", "naq_NA"], default_locale: "en_NA", currency: "ZAR", latitude: "-22.95764", longitude: "18.49041", currency_name: "Rand", languages: ["en", "af"], capital: "Windhoek", emoji: "🇳🇦", emojiU: "U+1F1F3 U+1F1E6", fips: "WA", internet: "NA", continent: "Africa", region: "Southern Africa" }, { name: "Nauru", alpha2: "NR", alpha3: "NRU", numeric: "520", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-0.522778", longitude: "166.931503", currency_name: "Australian Dollar", languages: ["en", "na"], capital: "Yaren", emoji: "🇳🇷", emojiU: "U+1F1F3 U+1F1F7", fips: "NR", internet: "NR", continent: "Oceania", region: "Pacific" }, { name: "Nepal", alpha2: "NP", alpha3: "NPL", numeric: "524", locales: ["ne_NP"], default_locale: "ne_NP", currency: "NPR", latitude: "28.394857", longitude: "84.124008", currency_name: "Nepalese Rupee", languages: ["ne"], capital: "Kathmandu", emoji: "🇳🇵", emojiU: "U+1F1F3 U+1F1F5", fips: "NP", internet: "NP", continent: "Asia", region: "South Asia" }, { name: "Netherlands", alpha2: "NL", alpha3: "NLD", numeric: "528", locales: ["nl_NL"], default_locale: "nl_NL", currency: "EUR", latitude: "52.132633", longitude: "5.291266", currency_name: "Euro", languages: ["nl"], capital: "Amsterdam", emoji: "🇳🇱", emojiU: "U+1F1F3 U+1F1F1", fips: "NL", internet: "NL", continent: "Europe", region: "Western Europe" }, { name: "Netherlands Antilles", alpha2: "AN", alpha3: "ANT", numeric: "530", locales: ["nl_AN"], default_locale: "nl_AN", currency: "ANG", latitude: "12.226079", longitude: "-69.060087", currency_name: "Netherlands Antillean Guilder", fips: "NT", internet: "AN", continent: "Americas", region: "West Indies" }, { name: "New Caledonia", alpha2: "NC", alpha3: "NCL", numeric: "540", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-20.904305", longitude: "165.618042", currency_name: "CFP Franc", languages: ["fr"], capital: "Nouméa", emoji: "🇳🇨", emojiU: "U+1F1F3 U+1F1E8", fips: "NC", internet: "NC", continent: "Oceania", region: "Pacific" }, { name: "New Zealand", alpha2: "NZ", alpha3: "NZL", numeric: "554", locales: ["en_NZ"], default_locale: "en_NZ", currency: "NZD", latitude: "-40.900557", longitude: "174.885971", currency_name: "New Zealand Dollar", languages: ["en", "mi"], capital: "Wellington", emoji: "🇳🇿", emojiU: "U+1F1F3 U+1F1FF", fips: "NZ", internet: "NZ", continent: "Oceania", region: "Pacific" }, { name: "Nicaragua", alpha2: "NI", alpha3: "NIC", numeric: "558", locales: ["es_NI"], default_locale: "es_NI", currency: "NIO", latitude: "12.865416", longitude: "-85.207229", currency_name: "Cordoba Oro", languages: ["es"], capital: "Managua", emoji: "🇳🇮", emojiU: "U+1F1F3 U+1F1EE", fips: "NU", internet: "NI", continent: "Americas", region: "Central America" }, { name: "Niger", alpha2: "NE", alpha3: "NER", numeric: "562", locales: ["fr_NE", "ha_Latn_NE"], default_locale: "fr_NE", currency: "XOF", latitude: "17.607789", longitude: "8.081666", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Niamey", emoji: "🇳🇪", emojiU: "U+1F1F3 U+1F1EA", fips: "NG", internet: "NE", continent: "Africa", region: "Western Africa" }, { name: "Nigeria", alpha2: "NG", alpha3: "NGA", numeric: "566", locales: ["en_NG", "ha_Latn_NG", "ig_NG", "yo_NG"], default_locale: "en_NG", currency: "NGN", latitude: "9.081999", longitude: "8.675277", currency_name: "Naira", languages: ["en"], capital: "Abuja", emoji: "🇳🇬", emojiU: "U+1F1F3 U+1F1EC", fips: "NI", internet: "NG", continent: "Africa", region: "Western Africa" }, { name: "Niue", alpha2: "NU", alpha3: "NIU", numeric: "570", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-19.054445", longitude: "-169.867233", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Alofi", emoji: "🇳🇺", emojiU: "U+1F1F3 U+1F1FA", fips: "NE", internet: "NU", continent: "Oceania", region: "Pacific" }, { name: "Norfolk Island", alpha2: "NF", alpha3: "NFK", numeric: "574", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-29.040835", longitude: "167.954712", currency_name: "Australian Dollar", languages: ["en"], capital: "Kingston", emoji: "🇳🇫", emojiU: "U+1F1F3 U+1F1EB", fips: "NF", internet: "NF", continent: "Oceania", region: "Pacific" }, { name: "North Macedonia", alpha2: "MK", alpha3: "MKD", numeric: "807", locales: ["mk_MK"], default_locale: "mk_MK", currency: "MKD", latitude: "41.608635", longitude: "21.745275", currency_name: "Denar", languages: ["mk"], capital: "Skopje", emoji: "🇲🇰", emojiU: "U+1F1F2 U+1F1F0", fips: "MK", internet: "MK", continent: "Europe", region: "South East Europe" }, { name: "Northern Mariana Islands", alpha2: "MP", alpha3: "MNP", numeric: "580", locales: ["en_MP"], default_locale: "en_MP", currency: "USD", latitude: "17.33083", longitude: "145.38469", currency_name: "US Dollar", languages: ["en", "ch"], capital: "Saipan", emoji: "🇲🇵", emojiU: "U+1F1F2 U+1F1F5", fips: "CQ", internet: "MP", continent: "Oceania", region: "Pacific" }, { name: "Norway", alpha2: "NO", alpha3: "NOR", numeric: "578", locales: ["nb_NO", "nn_NO"], default_locale: "nb_NO", currency: "NOK", latitude: "60.472024", longitude: "8.468946", currency_name: "Norwegian Krone", languages: ["no", "nb", "nn"], capital: "Oslo", emoji: "🇳🇴", emojiU: "U+1F1F3 U+1F1F4", fips: "NO", internet: "NO", continent: "Europe", region: "Northern Europe" }, { name: "Oman", alpha2: "OM", alpha3: "OMN", numeric: "512", locales: ["ar_OM"], default_locale: "ar_OM", currency: "OMR", latitude: "21.512583", longitude: "55.923255", currency_name: "Rial Omani", languages: ["ar"], capital: "Muscat", emoji: "🇴🇲", emojiU: "U+1F1F4 U+1F1F2", fips: "MU", internet: "OM", continent: "Asia", region: "South West Asia" }, { name: "Pakistan", alpha2: "PK", alpha3: "PAK", numeric: "586", locales: ["en_PK", "pa_Arab_PK", "ur_PK"], default_locale: "en_PK", currency: "PKR", latitude: "30.375321", longitude: "69.345116", currency_name: "Pakistan Rupee", languages: ["en", "ur"], capital: "Islamabad", emoji: "🇵🇰", emojiU: "U+1F1F5 U+1F1F0", fips: "PK", internet: "PK", continent: "Asia", region: "South Asia" }, { name: "Palau", alpha2: "PW", alpha3: "PLW", numeric: "585", locales: ["en"], default_locale: "en", currency: "USD", latitude: "7.51498", longitude: "134.58252", currency_name: "US Dollar", languages: ["en"], capital: "Ngerulmud", emoji: "🇵🇼", emojiU: "U+1F1F5 U+1F1FC", fips: "PS", internet: "PW", continent: "Oceania", region: "Pacific" }, { name: "Palestine", alpha2: "PS", alpha3: "PSE", numeric: "275", locales: ["ar"], default_locale: "ar", currency: "USD", latitude: "31.952162", longitude: "35.233154", currency_name: "US Dollar", languages: ["ar"], capital: "Ramallah", emoji: "🇵🇸", emojiU: "U+1F1F5 U+1F1F8", fips: "WE", internet: "PS", continent: "Asia", region: "South West Asia", alternate_names: ["State of Palestine"] }, { name: "Panama", alpha2: "PA", alpha3: "PAN", numeric: "591", locales: ["es_PA"], default_locale: "es_PA", currency: "USD", latitude: "8.537981", longitude: "-80.782127", currency_name: "US Dollar", languages: ["es"], capital: "Panama City", emoji: "🇵🇦", emojiU: "U+1F1F5 U+1F1E6", fips: "PM", internet: "PA", continent: "Americas", region: "Central America" }, { name: "Papua New Guinea", alpha2: "PG", alpha3: "PNG", numeric: "598", locales: ["en"], default_locale: "en", currency: "PGK", latitude: "-6.314993", longitude: "143.95555", currency_name: "Kina", languages: ["en"], capital: "Port Moresby", emoji: "🇵🇬", emojiU: "U+1F1F5 U+1F1EC", fips: "PP", internet: "PG", continent: "Oceania", region: "Pacific" }, { name: "Paraguay", alpha2: "PY", alpha3: "PRY", numeric: "600", locales: ["es_PY"], default_locale: "es_PY", currency: "PYG", latitude: "-23.442503", longitude: "-58.443832", currency_name: "Guarani", languages: ["es", "gn"], capital: "Asunción", emoji: "🇵🇾", emojiU: "U+1F1F5 U+1F1FE", fips: "PA", internet: "PY", continent: "Americas", region: "South America" }, { name: "Peru", alpha2: "PE", alpha3: "PER", numeric: "604", locales: ["es_PE"], default_locale: "es_PE", currency: "PEN", latitude: "-9.189967", longitude: "-75.015152", currency_name: "Sol", languages: ["es"], capital: "Lima", emoji: "🇵🇪", emojiU: "U+1F1F5 U+1F1EA", fips: "PE", internet: "PE", continent: "Americas", region: "South America" }, { name: "Philippines", alpha2: "PH", alpha3: "PHL", numeric: "608", locales: ["en_PH", "fil_PH"], default_locale: "en_PH", currency: "PHP", latitude: "12.879721", longitude: "121.774017", currency_name: "Philippine Peso", languages: ["en"], capital: "Manila", emoji: "🇵🇭", emojiU: "U+1F1F5 U+1F1ED", fips: "RP", internet: "PH", continent: "Asia", region: "South East Asia" }, { name: "Pitcairn", alpha2: "PN", alpha3: "PCN", numeric: "612", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-24.703615", longitude: "-127.439308", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Adamstown", emoji: "🇵🇳", emojiU: "U+1F1F5 U+1F1F3", fips: "PC", internet: "PN", continent: "Oceania", region: "Pacific" }, { name: "Poland", alpha2: "PL", alpha3: "POL", numeric: "616", locales: ["pl_PL"], default_locale: "pl_PL", currency: "PLN", latitude: "51.919438", longitude: "19.145136", currency_name: "Zloty", languages: ["pl"], capital: "Warsaw", emoji: "🇵🇱", emojiU: "U+1F1F5 U+1F1F1", fips: "PL", internet: "PL", continent: "Europe", region: "Eastern Europe" }, { name: "Portugal", alpha2: "PT", alpha3: "PRT", numeric: "620", locales: ["pt_PT"], default_locale: "pt_PT", currency: "EUR", latitude: "39.399872", longitude: "-8.224454", currency_name: "Euro", languages: ["pt"], capital: "Lisbon", emoji: "🇵🇹", emojiU: "U+1F1F5 U+1F1F9", fips: "PO", internet: "PT", continent: "Europe", region: "South West Europe" }, { name: "Puerto Rico", alpha2: "PR", alpha3: "PRI", numeric: "630", locales: ["es_PR"], default_locale: "es_PR", currency: "USD", latitude: "18.220833", longitude: "-66.590149", currency_name: "US Dollar", languages: ["es", "en"], capital: "San Juan", emoji: "🇵🇷", emojiU: "U+1F1F5 U+1F1F7", fips: "RQ", internet: "PR", continent: "Americas", region: "West Indies" }, { name: "Qatar", alpha2: "QA", alpha3: "QAT", numeric: "634", locales: ["ar_QA"], default_locale: "ar_QA", currency: "QAR", latitude: "25.354826", longitude: "51.183884", currency_name: "Qatari Rial", languages: ["ar"], capital: "Doha", emoji: "🇶🇦", emojiU: "U+1F1F6 U+1F1E6", fips: "QA", internet: "QA", continent: "Asia", region: "South West Asia" }, { name: "Romania", alpha2: "RO", alpha3: "ROU", numeric: "642", locales: ["ro_RO"], default_locale: "ro_RO", currency: "RON", latitude: "45.943161", longitude: "24.96676", currency_name: "Romanian Leu", languages: ["ro"], capital: "Bucharest", emoji: "🇷🇴", emojiU: "U+1F1F7 U+1F1F4", fips: "RO", internet: "RO", continent: "Europe", region: "South East Europe" }, { name: "Russia", alpha2: "RU", alpha3: "RUS", numeric: "643", locales: ["ru_RU"], default_locale: "ru_RU", currency: "RUB", latitude: "61.52401", longitude: "105.318756", currency_name: "Russian Ruble", languages: ["ru"], capital: "Moscow", emoji: "🇷🇺", emojiU: "U+1F1F7 U+1F1FA", fips: "RS", internet: "RU", continent: "Asia", region: "Northern Asia", alternate_names: ["Russian Federation"] }, { name: "Rwanda", alpha2: "RW", alpha3: "RWA", numeric: "646", locales: ["fr_RW", "rw_RW"], default_locale: "fr_RW", currency: "RWF", latitude: "-1.940278", longitude: "29.873888", currency_name: "Rwanda Franc", languages: ["rw", "en", "fr"], capital: "Kigali", emoji: "🇷🇼", emojiU: "U+1F1F7 U+1F1FC", fips: "RW", internet: "RW", continent: "Africa", region: "Central Africa" }, { name: "Réunion", alpha2: "RE", alpha3: "REU", numeric: "638", locales: ["fr_RE"], default_locale: "fr_RE", currency: "RWF", latitude: "-21.115141", longitude: "55.536384", currency_name: "Rwanda Franc", languages: ["fr"], capital: "Saint-Denis", emoji: "🇷🇪", emojiU: "U+1F1F7 U+1F1EA", fips: "RE", internet: "RE", continent: "Africa", region: "Indian Ocean" }, { name: "Saint Barthélemy", alpha2: "BL", alpha3: "BLM", numeric: "652", locales: ["fr_BL"], default_locale: "fr_BL", currency: "EUR", currency_name: "Euro", languages: ["fr"], capital: "Gustavia", emoji: "🇧🇱", emojiU: "U+1F1E7 U+1F1F1", fips: "TB", internet: "BL", continent: "Americas", region: "West Indies" }, { name: "Saint Helena", alpha2: "SH", alpha3: "SHN", numeric: "654", locales: ["en"], default_locale: "en", currency: "SHP", latitude: "-24.143474", longitude: "-10.030696", currency_name: "Saint Helena Pound", languages: ["en"], capital: "Jamestown", emoji: "🇸🇭", emojiU: "U+1F1F8 U+1F1ED", fips: "SH", internet: "SH", continent: "Atlantic Ocean", region: "South Atlantic Ocean", alternate_names: ["Saint Helena, Ascension and Tristan da Cunha"] }, { name: "Saint Kitts and Nevis", alpha2: "KN", alpha3: "KNA", numeric: "659", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "17.357822", longitude: "-62.782998", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Basseterre", emoji: "🇰🇳", emojiU: "U+1F1F0 U+1F1F3", fips: "SC", internet: "KN", continent: "Americas", region: "West Indies" }, { name: "Saint Lucia", alpha2: "LC", alpha3: "LCA", numeric: "662", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "13.909444", longitude: "-60.978893", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Castries", emoji: "🇱🇨", emojiU: "U+1F1F1 U+1F1E8", fips: "ST", internet: "LC", continent: "Americas", region: "West Indies" }, { name: "Saint Martin", alpha2: "MF", alpha3: "MAF", numeric: "663", locales: ["fr_MF"], default_locale: "fr_MF", currency: "EUR", currency_name: "Euro", languages: ["en", "fr", "nl"], capital: "Marigot", emoji: "🇲🇫", emojiU: "U+1F1F2 U+1F1EB", fips: "RN", internet: "MF", continent: "Americas", region: "West Indies", alternate_names: ["Saint Martin French part"] }, { name: "Saint Pierre and Miquelon", alpha2: "PM", alpha3: "SPM", numeric: "666", locales: ["fr"], default_locale: "fr", currency: "EUR", latitude: "46.941936", longitude: "-56.27111", currency_name: "Euro", languages: ["fr"], capital: "Saint-Pierre", emoji: "🇵🇲", emojiU: "U+1F1F5 U+1F1F2", fips: "SB", internet: "PM", continent: "Americas", region: "North America" }, { name: "Saint Vincent and the Grenadines", alpha2: "VC", alpha3: "VCT", numeric: "670", locales: ["en"], default_locale: "en", currency: "XCD", latitude: "12.984305", longitude: "-61.287228", currency_name: "East Caribbean Dollar", languages: ["en"], capital: "Kingstown", emoji: "🇻🇨", emojiU: "U+1F1FB U+1F1E8", fips: "VC", internet: "VC", continent: "Americas", region: "West Indies" }, { name: "Samoa", alpha2: "WS", alpha3: "WSM", numeric: "882", locales: ["sm"], default_locale: "sm", currency: "WST", latitude: "-13.759029", longitude: "-172.104629", currency_name: "Tala", languages: ["sm", "en"], capital: "Apia", emoji: "🇼🇸", emojiU: "U+1F1FC U+1F1F8", fips: "WS", internet: "WS", continent: "Oceania", region: "Pacific" }, { name: "San Marino", alpha2: "SM", alpha3: "SMR", numeric: "674", locales: ["it"], default_locale: "it", currency: "EUR", latitude: "43.94236", longitude: "12.457777", currency_name: "Euro", languages: ["it"], capital: "City of San Marino", emoji: "🇸🇲", emojiU: "U+1F1F8 U+1F1F2", fips: "SM", internet: "SM", continent: "Europe", region: "Southern Europe" }, { name: "Sao Tome and Principe", alpha2: "ST", alpha3: "STP", numeric: "678", locales: ["pt"], default_locale: "pt", currency: "STN", latitude: "0.18636", longitude: "6.613081", currency_name: "Dobra", languages: ["pt"], capital: "São Tomé", emoji: "🇸🇹", emojiU: "U+1F1F8 U+1F1F9", fips: "TP", internet: "ST", continent: "Africa", region: "Western Africa" }, { name: "Saudi Arabia", alpha2: "SA", alpha3: "SAU", numeric: "682", locales: ["ar_SA"], default_locale: "ar_SA", currency: "SAR", latitude: "23.885942", longitude: "45.079162", currency_name: "Saudi Riyal", languages: ["ar"], capital: "Riyadh", emoji: "🇸🇦", emojiU: "U+1F1F8 U+1F1E6", fips: "SA", internet: "SA", continent: "Asia", region: "South West Asia" }, { name: "Senegal", alpha2: "SN", alpha3: "SEN", numeric: "686", locales: ["fr_SN", "ff_SN"], default_locale: "fr_SN", currency: "XOF", latitude: "14.497401", longitude: "-14.452362", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Dakar", emoji: "🇸🇳", emojiU: "U+1F1F8 U+1F1F3", fips: "SG", internet: "SN", continent: "Africa", region: "Western Africa" }, { name: "Serbia", alpha2: "RS", alpha3: "SRB", numeric: "688", locales: ["sr_Cyrl_RS", "sr_Latn_RS"], default_locale: "sr_Cyrl_RS", currency: "RSD", latitude: "44.016521", longitude: "21.005859", currency_name: "Serbian Dinar", languages: ["sr"], capital: "Belgrade", emoji: "🇷🇸", emojiU: "U+1F1F7 U+1F1F8", fips: "RI", internet: "RS", continent: "Europe", region: "South East Europe" }, { name: "Seychelles", alpha2: "SC", alpha3: "SYC", numeric: "690", locales: ["fr"], default_locale: "fr", currency: "SCR", latitude: "-4.679574", longitude: "55.491977", currency_name: "Seychelles Rupee", languages: ["fr", "en"], capital: "Victoria", emoji: "🇸🇨", emojiU: "U+1F1F8 U+1F1E8", fips: "SE", internet: "SC", continent: "Africa", region: "Indian Ocean" }, { name: "Sierra Leone", alpha2: "SL", alpha3: "SLE", numeric: "694", locales: ["en"], default_locale: "en", currency: "SLL", latitude: "8.460555", longitude: "-11.779889", currency_name: "Leone", languages: ["en"], capital: "Freetown", emoji: "🇸🇱", emojiU: "U+1F1F8 U+1F1F1", fips: "SL", internet: "SL", continent: "Africa", region: "Western Africa" }, { name: "Singapore", alpha2: "SG", alpha3: "SGP", numeric: "702", locales: ["zh_Hans_SG", "en_SG"], default_locale: "en_SG", currency: "SGD", latitude: "1.352083", longitude: "103.819836", currency_name: "Singapore Dollar", languages: ["en", "ms", "ta", "zh"], capital: "Singapore", emoji: "🇸🇬", emojiU: "U+1F1F8 U+1F1EC", fips: "SN", internet: "SG", continent: "Asia", region: "South East Asia" }, { name: "Sint Maarten", alpha2: "SX", alpha3: "SXM", numeric: "534", locales: ["nl"], default_locale: "nl", currency: "ANG", currency_name: "Netherlands Antillean Guilder", languages: ["nl", "en"], capital: "Philipsburg", emoji: "🇸🇽", emojiU: "U+1F1F8 U+1F1FD", fips: "NN", internet: "SX", continent: "Americas", region: "West Indies", alternate_names: ["Sint Maarten Dutch part"] }, { name: "Slovakia", alpha2: "SK", alpha3: "SVK", numeric: "703", locales: ["sk_SK"], default_locale: "sk_SK", currency: "EUR", latitude: "48.669026", longitude: "19.699024", currency_name: "Euro", languages: ["sk"], capital: "Bratislava", emoji: "🇸🇰", emojiU: "U+1F1F8 U+1F1F0", fips: "LO", internet: "SK", continent: "Europe", region: "Central Europe" }, { name: "Slovenia", alpha2: "SI", alpha3: "SVN", numeric: "705", locales: ["sl_SI"], default_locale: "sl_SI", currency: "EUR", latitude: "46.151241", longitude: "14.995463", currency_name: "Euro", languages: ["sl"], capital: "Ljubljana", emoji: "🇸🇮", emojiU: "U+1F1F8 U+1F1EE", fips: "SI", internet: "SI", continent: "Europe", region: "South East Europe" }, { name: "Solomon Islands", alpha2: "SB", alpha3: "SLB", numeric: "090", locales: ["en"], default_locale: "en", currency: "SBD", latitude: "-9.64571", longitude: "160.156194", currency_name: "Solomon Islands Dollar", languages: ["en"], capital: "Honiara", emoji: "🇸🇧", emojiU: "U+1F1F8 U+1F1E7", fips: "BP", internet: "SB", continent: "Oceania", region: "Pacific" }, { name: "Somalia", alpha2: "SO", alpha3: "SOM", numeric: "706", locales: ["so_SO"], default_locale: "so_SO", currency: "SOS", latitude: "5.152149", longitude: "46.199616", currency_name: "Somali Shilling", languages: ["so", "ar"], capital: "Mogadishu", emoji: "🇸🇴", emojiU: "U+1F1F8 U+1F1F4", fips: "SO", internet: "SO", continent: "Africa", region: "Eastern Africa" }, { name: "South Africa", alpha2: "ZA", alpha3: "ZAF", numeric: "710", locales: ["af_ZA", "en_ZA", "zu_ZA"], default_locale: "af_ZA", currency: "ZAR", latitude: "-30.559482", longitude: "22.937506", currency_name: "Rand", languages: ["af", "en", "nr", "st", "ss", "tn", "ts", "ve", "xh", "zu"], capital: "Pretoria", emoji: "🇿🇦", emojiU: "U+1F1FF U+1F1E6", fips: "SF", internet: "ZA", continent: "Africa", region: "Southern Africa" }, { name: "South Georgia and the South Sandwich Islands", alpha2: "GS", alpha3: "SGS", numeric: "239", locales: ["en"], default_locale: "en", currency: "USD", latitude: "-54.429579", longitude: "-36.587909", currency_name: "US Dollar", languages: ["en"], capital: "King Edward Point", emoji: "🇬🇸", emojiU: "U+1F1EC U+1F1F8", fips: "SX", internet: "GS", continent: "Atlantic Ocean", region: "South Atlantic Ocean" }, { name: "South Sudan", alpha2: "SS", alpha3: "SSD", numeric: "728", locales: ["en"], default_locale: "en", currency: "SSP", currency_name: "South Sudanese Pound", languages: ["en"], capital: "Juba", emoji: "🇸🇸", emojiU: "U+1F1F8 U+1F1F8", fips: "OD", internet: "SS", continent: "Africa", region: "Northern Africa" }, { name: "Spain", alpha2: "ES", alpha3: "ESP", numeric: "724", locales: ["eu_ES", "ca_ES", "gl_ES", "es_ES"], default_locale: "es_ES", currency: "EUR", latitude: "40.463667", longitude: "-3.74922", currency_name: "Euro", languages: ["es", "eu", "ca", "gl", "oc"], capital: "Madrid", emoji: "🇪🇸", emojiU: "U+1F1EA U+1F1F8", fips: "SP", internet: "ES", continent: "Europe", region: "South West Europe" }, { name: "Sri Lanka", alpha2: "LK", alpha3: "LKA", numeric: "144", locales: ["si_LK", "ta_LK"], default_locale: "si_LK", currency: "LKR", latitude: "7.873054", longitude: "80.771797", currency_name: "Sri Lanka Rupee", languages: ["si", "ta"], capital: "Colombo", emoji: "🇱🇰", emojiU: "U+1F1F1 U+1F1F0", fips: "CE", internet: "LK", continent: "Asia", region: "South Asia" }, { name: "Sudan", alpha2: "SD", alpha3: "SDN", numeric: "729", locales: ["ar_SD"], default_locale: "ar_SD", currency: "SDG", latitude: "12.862807", longitude: "30.217636", currency_name: "Sudanese Pound", languages: ["ar", "en"], capital: "Khartoum", emoji: "🇸🇩", emojiU: "U+1F1F8 U+1F1E9", fips: "SU", internet: "SD", continent: "Africa", region: "Northern Africa" }, { name: "Suriname", alpha2: "SR", alpha3: "SUR", numeric: "740", locales: ["nl"], default_locale: "nl", currency: "SRD", latitude: "3.919305", longitude: "-56.027783", currency_name: "Surinam Dollar", languages: ["nl"], capital: "Paramaribo", emoji: "🇸🇷", emojiU: "U+1F1F8 U+1F1F7", fips: "NS", internet: "SR", continent: "Americas", region: "South America" }, { name: "Svalbard and Jan Mayen", alpha2: "SJ", alpha3: "SJM", numeric: "744", locales: ["no"], default_locale: "no", currency: "NOK", latitude: "77.553604", longitude: "23.670272", currency_name: "Norwegian Krone", languages: ["no"], capital: "Longyearbyen", emoji: "🇸🇯", emojiU: "U+1F1F8 U+1F1EF", fips: "SV", internet: "SJ", continent: "Europe", region: "Northern Europe" }, { name: "Sweden", alpha2: "SE", alpha3: "SWE", numeric: "752", locales: ["sv_SE"], default_locale: "sv_SE", currency: "SEK", latitude: "60.128161", longitude: "18.643501", currency_name: "Swedish Krona", languages: ["sv"], capital: "Stockholm", emoji: "🇸🇪", emojiU: "U+1F1F8 U+1F1EA", fips: "SW", internet: "SE", continent: "Europe", region: "Northern Europe" }, { name: "Switzerland", alpha2: "CH", alpha3: "CHE", numeric: "756", locales: ["fr_CH", "de_CH", "it_CH", "rm_CH", "gsw_CH"], default_locale: "fr_CH", currency: "CHF", latitude: "46.818188", longitude: "8.227512", currency_name: "Swiss Franc", languages: ["de", "fr", "it"], capital: "Bern", emoji: "🇨🇭", emojiU: "U+1F1E8 U+1F1ED", fips: "SZ", internet: "CH", continent: "Europe", region: "Central Europe" }, { name: "Syrian Arab Republic", alpha2: "SY", alpha3: "SYR", numeric: "760", locales: ["ar_SY"], default_locale: "ar_SY", currency: "SYP", latitude: "34.802075", longitude: "38.996815", currency_name: "Syrian Pound", languages: ["ar"], capital: "Damascus", emoji: "🇸🇾", emojiU: "U+1F1F8 U+1F1FE", fips: "SY", internet: "SY", continent: "Asia", region: "South West Asia" }, { name: "Taiwan", alpha2: "TW", alpha3: "TWN", numeric: "158", locales: ["zh_Hant_TW"], default_locale: "zh_Hant_TW", currency: "TWD", latitude: "23.69781", longitude: "120.960515", currency_name: "New Taiwan Dollar", languages: ["zh"], capital: "Taipei", emoji: "🇹🇼", emojiU: "U+1F1F9 U+1F1FC", fips: "TW", internet: "TW", continent: "Asia", region: "East Asia", alternate_names: ["Province of China Taiwan"] }, { name: "Tajikistan", alpha2: "TJ", alpha3: "TJK", numeric: "762", locales: ["tg"], default_locale: "tg", currency: "TJS", latitude: "38.861034", longitude: "71.276093", currency_name: "Somoni", languages: ["tg", "ru"], capital: "Dushanbe", emoji: "🇹🇯", emojiU: "U+1F1F9 U+1F1EF", fips: "TI", internet: "TJ", continent: "Asia", region: "Central Asia" }, { name: "Tanzania", alpha2: "TZ", alpha3: "TZA", numeric: "834", locales: ["asa_TZ", "bez_TZ", "lag_TZ", "jmc_TZ", "kde_TZ", "mas_TZ", "rof_TZ", "rwk_TZ", "sw_TZ", "vun_TZ"], default_locale: "asa_TZ", currency: "TZS", latitude: "-6.369028", longitude: "34.888822", currency_name: "Tanzanian Shilling", languages: ["sw", "en"], capital: "Dodoma", emoji: "🇹🇿", emojiU: "U+1F1F9 U+1F1FF", fips: "TZ", internet: "TZ", continent: "Africa", region: "Eastern Africa", alternate_names: ["United Republic of Tanzania"] }, { name: "Thailand", alpha2: "TH", alpha3: "THA", numeric: "764", locales: ["th_TH"], default_locale: "th_TH", currency: "THB", latitude: "15.870032", longitude: "100.992541", currency_name: "Baht", languages: ["th"], capital: "Bangkok", emoji: "🇹🇭", emojiU: "U+1F1F9 U+1F1ED", fips: "TH", internet: "TH", continent: "Asia", region: "South East Asia" }, { name: "Timor-Leste", alpha2: "TL", alpha3: "TLS", numeric: "626", locales: ["pt"], default_locale: "pt", currency: "USD", latitude: "-8.874217", longitude: "125.727539", currency_name: "US Dollar", languages: ["pt"], capital: "Dili", emoji: "🇹🇱", emojiU: "U+1F1F9 U+1F1F1", fips: "TT", internet: "TL", continent: "Asia", region: "South East Asia" }, { name: "Togo", alpha2: "TG", alpha3: "TGO", numeric: "768", locales: ["ee_TG", "fr_TG"], default_locale: "fr_TG", currency: "XOF", latitude: "8.619543", longitude: "0.824782", currency_name: "CFA Franc BCEAO", languages: ["fr"], capital: "Lomé", emoji: "🇹🇬", emojiU: "U+1F1F9 U+1F1EC", fips: "TO", internet: "TG", continent: "Africa", region: "Western Africa" }, { name: "Tokelau", alpha2: "TK", alpha3: "TKL", numeric: "772", locales: ["en"], default_locale: "en", currency: "NZD", latitude: "-8.967363", longitude: "-171.855881", currency_name: "New Zealand Dollar", languages: ["en"], capital: "Fakaofo", emoji: "🇹🇰", emojiU: "U+1F1F9 U+1F1F0", fips: "TL", internet: "TK", continent: "Oceania", region: "Pacific" }, { name: "Tonga", alpha2: "TO", alpha3: "TON", numeric: "776", locales: ["to_TO"], default_locale: "to_TO", currency: "TOP", latitude: "-21.178986", longitude: "-175.198242", currency_name: "Pa’anga", languages: ["en", "to"], capital: "Nuku'alofa", emoji: "🇹🇴", emojiU: "U+1F1F9 U+1F1F4", fips: "TN", internet: "TO", continent: "Oceania", region: "Pacific" }, { name: "Trinidad and Tobago", alpha2: "TT", alpha3: "TTO", numeric: "780", locales: ["en_TT"], default_locale: "en_TT", currency: "TTD", latitude: "10.691803", longitude: "-61.222503", currency_name: "Trinidad and Tobago Dollar", languages: ["en"], capital: "Port of Spain", emoji: "🇹🇹", emojiU: "U+1F1F9 U+1F1F9", fips: "TD", internet: "TT", continent: "Americas", region: "West Indies" }, { name: "Tunisia", alpha2: "TN", alpha3: "TUN", numeric: "788", locales: ["ar_TN"], default_locale: "ar_TN", currency: "TND", latitude: "33.886917", longitude: "9.537499", currency_name: "Tunisian Dinar", languages: ["ar"], capital: "Tunis", emoji: "🇹🇳", emojiU: "U+1F1F9 U+1F1F3", fips: "TS", internet: "TN", continent: "Africa", region: "Northern Africa" }, { name: "Turkey", alpha2: "TR", alpha3: "TUR", numeric: "792", locales: ["tr_TR"], default_locale: "tr_TR", currency: "TRY", latitude: "38.963745", longitude: "35.243322", currency_name: "Turkish Lira", languages: ["tr"], capital: "Ankara", emoji: "🇹🇷", emojiU: "U+1F1F9 U+1F1F7", fips: "TU", internet: "TR", continent: "Asia", region: "South West Asia" }, { name: "Turkmenistan", alpha2: "TM", alpha3: "TKM", numeric: "795", locales: ["tk"], default_locale: "tk", currency: "TMT", latitude: "38.969719", longitude: "59.556278", currency_name: "Turkmenistan New Manat", languages: ["tk", "ru"], capital: "Ashgabat", emoji: "🇹🇲", emojiU: "U+1F1F9 U+1F1F2", fips: "TX", internet: "TM", continent: "Asia", region: "Central Asia" }, { name: "Turks and Caicos Islands", alpha2: "TC", alpha3: "TCA", numeric: "796", locales: ["en"], default_locale: "en", currency: "USD", latitude: "21.694025", longitude: "-71.797928", currency_name: "US Dollar", languages: ["en"], capital: "Cockburn Town", emoji: "🇹🇨", emojiU: "U+1F1F9 U+1F1E8", fips: "TK", internet: "TC", continent: "Americas", region: "West Indies" }, { name: "Tuvalu", alpha2: "TV", alpha3: "TUV", numeric: "798", locales: ["en"], default_locale: "en", currency: "AUD", latitude: "-7.109535", longitude: "177.64933", currency_name: "Australian Dollar", languages: ["en"], capital: "Funafuti", emoji: "🇹🇻", emojiU: "U+1F1F9 U+1F1FB", fips: "TV", internet: "TV", continent: "Oceania", region: "Pacific" }, { name: "Uganda", alpha2: "UG", alpha3: "UGA", numeric: "800", locales: ["cgg_UG", "lg_UG", "nyn_UG", "xog_UG", "teo_UG"], default_locale: "cgg_UG", currency: "UGX", latitude: "1.373333", longitude: "32.290275", currency_name: "Uganda Shilling", languages: ["en", "sw"], capital: "Kampala", emoji: "🇺🇬", emojiU: "U+1F1FA U+1F1EC", fips: "UG", internet: "UG", continent: "Africa", region: "Eastern Africa" }, { name: "Ukraine", alpha2: "UA", alpha3: "UKR", numeric: "804", locales: ["ru_UA", "uk_UA"], default_locale: "uk_UA", currency: "UAH", latitude: "48.379433", longitude: "31.16558", currency_name: "Hryvnia", languages: ["uk"], capital: "Kyiv", emoji: "🇺🇦", emojiU: "U+1F1FA U+1F1E6", fips: "UP", internet: "UA", continent: "Europe", region: "Eastern Europe" }, { name: "United Arab Emirates", alpha2: "AE", alpha3: "ARE", numeric: "784", locales: ["ar_AE"], default_locale: "ar_AE", currency: "AED", latitude: "23.424076", longitude: "53.847818", currency_name: "UAE Dirham", languages: ["ar"], capital: "Abu Dhabi", emoji: "🇦🇪", emojiU: "U+1F1E6 U+1F1EA", fips: "TC", internet: "AE", continent: "Asia", region: "South West Asia" }, { name: "United Kingdom", alpha2: "GB", alpha3: "GBR", numeric: "826", locales: ["kw_GB", "en_GB", "gv_GB", "cy_GB"], default_locale: "en_GB", currency: "GBP", latitude: "55.378051", longitude: "-3.435973", currency_name: "Pound Sterling", languages: ["en"], capital: "London", emoji: "🇬🇧", emojiU: "U+1F1EC U+1F1E7", fips: "UK", internet: "UK", continent: "Europe", region: "Western Europe", alternate_names: ["United Kingdom of Great Britain and Northern Ireland"] }, { name: "United States Minor Outlying Islands", alpha2: "UM", alpha3: "UMI", numeric: "581", locales: ["en_UM"], default_locale: "en_UM", currency: "USD", currency_name: "US Dollar", languages: ["en"], capital: "", emoji: "🇺🇲", emojiU: "U+1F1FA U+1F1F2", fips: "", internet: "US", continent: "Americas", region: "North America" }, { name: "United States of America", alpha2: "US", alpha3: "USA", numeric: "840", locales: ["chr_US", "en_US", "haw_US", "es_US"], default_locale: "en_US", currency: "USD", latitude: "37.09024", longitude: "-95.712891", currency_name: "US Dollar", languages: ["en"], capital: "Washington D.C.", emoji: "🇺🇸", emojiU: "U+1F1FA U+1F1F8", fips: "US", internet: "US", continent: "Americas", region: "North America", alternate_names: ["United States"] }, { name: "Uruguay", alpha2: "UY", alpha3: "URY", numeric: "858", locales: ["es_UY"], default_locale: "es_UY", currency: "UYU", latitude: "-32.522779", longitude: "-55.765835", currency_name: "Peso Uruguayo", languages: ["es"], capital: "Montevideo", emoji: "🇺🇾", emojiU: "U+1F1FA U+1F1FE", fips: "UY", internet: "UY", continent: "Americas", region: "South America" }, { name: "Uzbekistan", alpha2: "UZ", alpha3: "UZB", numeric: "860", locales: ["uz_Cyrl_UZ", "uz_Latn_UZ"], default_locale: "uz_Cyrl_UZ", currency: "UZS", latitude: "41.377491", longitude: "64.585262", currency_name: "Uzbekistan Sum", languages: ["uz", "ru"], capital: "Tashkent", emoji: "🇺🇿", emojiU: "U+1F1FA U+1F1FF", fips: "UZ", internet: "UZ", continent: "Asia", region: "Central Asia" }, { name: "Vanuatu", alpha2: "VU", alpha3: "VUT", numeric: "548", locales: ["bi"], default_locale: "bi", currency: "VUV", latitude: "-15.376706", longitude: "166.959158", currency_name: "Vatu", languages: ["bi", "en", "fr"], capital: "Port Vila", emoji: "🇻🇺", emojiU: "U+1F1FB U+1F1FA", fips: "NH", internet: "VU", continent: "Oceania", region: "Pacific" }, { name: "Venezuela", alpha2: "VE", alpha3: "VEN", numeric: "862", locales: ["es_VE"], default_locale: "es_VE", currency: "VUV", latitude: "6.42375", longitude: "-66.58973", currency_name: "Vatu", languages: ["es"], capital: "Caracas", emoji: "🇻🇪", emojiU: "U+1F1FB U+1F1EA", fips: "VE", internet: "UE", continent: "Americas", region: "South America", alternate_names: ["Bolivarian Republic of Venezuela"] }, { name: "Viet Nam", alpha2: "VN", alpha3: "VNM", numeric: "704", locales: ["vi_VN"], default_locale: "vi_VN", currency: "VND", latitude: "14.058324", longitude: "108.277199", currency_name: "Dong", languages: ["vi"], capital: "Hanoi", emoji: "🇻🇳", emojiU: "U+1F1FB U+1F1F3", fips: "VN", internet: "VN", continent: "Asia", region: "South East Asia" }, { name: "Virgin Islands (British)", alpha2: "VG", alpha3: "VGB", numeric: "092", locales: ["en"], default_locale: "en", currency: "USD", latitude: "18.420695", longitude: "-64.639968", currency_name: "US Dollar", languages: ["en"], capital: "Road Town", emoji: "🇻🇬", emojiU: "U+1F1FB U+1F1EC", fips: "VI", internet: "VG", continent: "Americas", region: "West Indies" }, { name: "Virgin Islands (U.S.)", alpha2: "VI", alpha3: "VIR", numeric: "850", locales: ["en_VI"], default_locale: "en_VI", currency: "USD", latitude: "18.335765", longitude: "-64.896335", currency_name: "US Dollar", languages: ["en"], capital: "Charlotte Amalie", emoji: "🇻🇮", emojiU: "U+1F1FB U+1F1EE", fips: "VQ", internet: "VI", continent: "Americas", region: "West Indies" }, { name: "Wallis and Futuna", alpha2: "WF", alpha3: "WLF", numeric: "876", locales: ["fr"], default_locale: "fr", currency: "XPF", latitude: "-13.768752", longitude: "-177.156097", currency_name: "CFP Franc", languages: ["fr"], capital: "Mata-Utu", emoji: "🇼🇫", emojiU: "U+1F1FC U+1F1EB", fips: "WF", internet: "WF", continent: "Oceania", region: "Pacific" }, { name: "Western Sahara", alpha2: "EH", alpha3: "ESH", numeric: "732", locales: ["es"], default_locale: "es", currency: "MAD", latitude: "24.215527", longitude: "-12.885834", currency_name: "Moroccan Dirham", languages: ["es"], capital: "El Aaiún", emoji: "🇪🇭", emojiU: "U+1F1EA U+1F1ED", fips: "WI", internet: "EH", continent: "Africa", region: "Northern Africa" }, { name: "Yemen", alpha2: "YE", alpha3: "YEM", numeric: "887", locales: ["ar_YE"], default_locale: "ar_YE", currency: "YER", latitude: "15.552727", longitude: "48.516388", currency_name: "Yemeni Rial", languages: ["ar"], capital: "Sana'a", emoji: "🇾🇪", emojiU: "U+1F1FE U+1F1EA", fips: "YM", internet: "YE", continent: "Asia", region: "South West Asia" }, { name: "Zambia", alpha2: "ZM", alpha3: "ZMB", numeric: "894", locales: ["bem_ZM"], default_locale: "bem_ZM", currency: "ZMW", latitude: "-13.133897", longitude: "27.849332", currency_name: "Zambian Kwacha", languages: ["en"], capital: "Lusaka", emoji: "🇿🇲", emojiU: "U+1F1FF U+1F1F2", fips: "ZA", internet: "ZM", continent: "Africa", region: "Southern Africa" }, { name: "Zimbabwe", alpha2: "ZW", alpha3: "ZWE", numeric: "716", locales: ["en_ZW", "nd_ZW", "sn_ZW"], default_locale: "en_ZW", currency: "ZWL", latitude: "-19.015438", longitude: "29.154857", currency_name: "Zimbabwe Dollar", languages: ["en", "sn", "nd"], capital: "Harare", emoji: "🇿🇼", emojiU: "U+1F1FF U+1F1FC", fips: "ZI", internet: "ZW", continent: "Africa", region: "Southern Africa" }, { name: "Åland Islands", alpha2: "AX", alpha3: "ALA", numeric: "248", locales: ["sv"], default_locale: "sv", currency: "EUR", currency_name: "Euro", languages: ["sv"], capital: "Mariehamn", emoji: "🇦🇽", emojiU: "U+1F1E6 U+1F1FD", fips: "AX", internet: "AX", continent: "Europe", region: "Northern Europe" }, { name: "Kosovo", alpha2: "XK", alpha3: "XKX", numeric: "383", locales: ["sq-XK"], default_locale: "sq-XK", currency: "EUR", latitude: "42.602636", longitude: "20.902977", currency_name: "Euro", languages: ["sq", "sr"], capital: "Pristina", emoji: "🇽🇰", emojiU: "U+1F1FD U+1F1F0", fips: "KV", internet: "XK", continent: "Europe", region: "South East Europe" }]; - }); - - // node_modules/.bun/country-locale-map@1.9.11/node_modules/country-locale-map/index.js - var require_country_locale_map = __commonJS((exports, module) => { - var fuzz = require_fuzzball(); - function CLM() { - var clm = {}; - var countries; - if (typeof process !== "undefined" && process && process.env && process.env.CLM_MODE == "INTL") { - countries = require_countries_intl(); - } else { - countries = require_countries(); - } - var countryByAlpha2Code = {}; - var countryByAlpha3Code = {}; - var countryByNumericCode = {}; - var countryByName = {}; - var countryNames = []; - var countriesByContinent = {}; - for (let i = 0;i < countries.length; ++i) { - countryByAlpha2Code[countries[i]["alpha2"]] = countries[i]; - countryByAlpha3Code[countries[i]["alpha3"]] = countries[i]; - countryByNumericCode[countries[i]["numeric"]] = countries[i]; - countryByName[countries[i]["name"]] = countries[i]; - countryNames.push(countries[i]["name"]); - countriesByContinent[countries[i]["continent"]] = [ - ...countriesByContinent[countries[i]["continent"]] || [], - countries[i] - ]; - if (countries[i]["alternate_names"]) { - for (let j = 0;j < countries[i]["alternate_names"].length; ++j) { - countryByName[countries[i]["alternate_names"][j]] = countries[i]; - countryNames.push(countries[i]["alternate_names"][j]); - } - } - } - Object.freeze(countryByAlpha2Code); - Object.freeze(countryByAlpha3Code); - Object.freeze(countryByNumericCode); - Object.freeze(countryByName); - Object.freeze(countryNames); - Object.freeze(countriesByContinent); - clm.getAllCountries = function() { - return countries; - }; - clm.getAlpha3ByAlpha2 = function(alpha2) { - if (countryByAlpha2Code.hasOwnProperty(alpha2)) - return countryByAlpha2Code[alpha2].alpha3; - else - return; - }; - clm.getLocaleByAlpha2 = function(alpha2) { - if (countryByAlpha2Code.hasOwnProperty(alpha2)) - return countryByAlpha2Code[alpha2].default_locale; - else - return; - }; - clm.getCountryNameByAlpha2 = function(alpha2) { - if (countryByAlpha2Code.hasOwnProperty(alpha2)) - return countryByAlpha2Code[alpha2].name; - else - return; - }; - clm.getNumericByAlpha2 = function(alpha2) { - if (countryByAlpha2Code.hasOwnProperty(alpha2)) - return countryByAlpha2Code[alpha2].numeric; - else - return; - }; - clm.getCurrencyByAlpha2 = function(alpha2) { - if (countryByAlpha2Code.hasOwnProperty(alpha2)) - return countryByAlpha2Code[alpha2].currency; - else - return; - }; - clm.getCountryByAlpha2 = function(alpha2) { - return countryByAlpha2Code.hasOwnProperty(alpha2) ? countryByAlpha2Code[alpha2] : undefined; - }; - clm.getAlpha2ByAlpha3 = function(alpha3) { - if (countryByAlpha3Code.hasOwnProperty(alpha3)) - return countryByAlpha3Code[alpha3].alpha2; - else - return; - }; - clm.getLocaleByAlpha3 = function(alpha3) { - if (countryByAlpha3Code.hasOwnProperty(alpha3)) - return countryByAlpha3Code[alpha3].default_locale; - else - return; - }; - clm.getCountryNameByAlpha3 = function(alpha3) { - if (countryByAlpha3Code.hasOwnProperty(alpha3)) - return countryByAlpha3Code[alpha3].name; - else - return; - }; - clm.getNumericByAlpha3 = function(alpha3) { - if (countryByAlpha3Code.hasOwnProperty(alpha3)) - return countryByAlpha3Code[alpha3].numeric; - else - return; - }; - clm.getCurrencyByAlpha3 = function(alpha3) { - if (countryByAlpha3Code.hasOwnProperty(alpha3)) - return countryByAlpha3Code[alpha3].currency; - else - return; - }; - clm.getCountryByAlpha3 = function(alpha3) { - return countryByAlpha3Code.hasOwnProperty(alpha3) ? countryByAlpha3Code[alpha3] : undefined; - }; - clm.getAlpha2ByNumeric = function(numeric) { - if (countryByNumericCode.hasOwnProperty(numeric)) - return countryByNumericCode[numeric].alpha2; - else - return; - }; - clm.getAlpha3ByNumeric = function(numeric) { - if (countryByNumericCode.hasOwnProperty(numeric)) - return countryByNumericCode[numeric].alpha3; - else - return; - }; - clm.getLocaleByNumeric = function(numeric) { - if (countryByNumericCode.hasOwnProperty(numeric)) - return countryByNumericCode[numeric].default_locale; - else - return; - }; - clm.getCountryNameByNumeric = function(numeric) { - if (countryByNumericCode.hasOwnProperty(numeric)) - return countryByNumericCode[numeric].name; - else - return; - }; - clm.getCurrencyByNumeric = function(numeric) { - if (countryByNumericCode.hasOwnProperty(numeric)) - return countryByNumericCode[numeric].currency; - else - return; - }; - clm.getCountryByNumeric = function(numeric) { - return countryByNumericCode.hasOwnProperty(numeric) ? countryByNumericCode[numeric] : undefined; - }; - clm.getAlpha2ByName = function(name, fuzzy) { - if (countryByName.hasOwnProperty(name)) { - return countryByName[name].alpha2; - } else if (fuzzy) { - let match = getClosestMatch(name); - if (match) { - return countryByName[match].alpha2; - } - } - return; - }; - clm.getAlpha3ByName = function(name, fuzzy) { - if (countryByName.hasOwnProperty(name)) { - return countryByName[name].alpha3; - } else if (fuzzy) { - let match = getClosestMatch(name); - if (match) { - return countryByName[match].alpha3; - } - } - return; - }; - clm.getLocaleByName = function(name, fuzzy) { - if (countryByName.hasOwnProperty(name)) { - return countryByName[name].default_locale; - } else if (fuzzy) { - let match = getClosestMatch(name); - if (match) { - return countryByName[match].default_locale; - } - } - return; - }; - clm.getNumericByName = function(name, fuzzy) { - if (countryByName.hasOwnProperty(name)) { - return countryByName[name].numeric; - } else if (fuzzy) { - let match = getClosestMatch(name); - if (match) { - return countryByName[match].numeric; - } - } - return; - }; - clm.getCurrencyByName = function(name, fuzzy) { - if (countryByName.hasOwnProperty(name)) { - return countryByName[name].currency; - } else if (fuzzy) { - let match = getClosestMatch(name); - if (match) { - return countryByName[match].currency; - } - } - return; - }; - clm.getCountryByName = function(name, fuzzy) { - if (countryByName.hasOwnProperty(name)) { - return countryByName[name]; - } else if (fuzzy) { - let match = getClosestMatch(name); - if (match) { - return countryByName[match]; - } - } - return; - }; - clm.getCountriesByContinent = function(name) { - if (countriesByContinent.hasOwnProperty(name)) { - return countriesByContinent[name]; - } - return; - }; - function getClosestMatch(name) { - let result = fuzz.extract(name, countryNames); - if (result[0][1] >= 60) { - return result[0][0]; - } - return; - } - return clm; - } - module.exports = CLM(); - }); - - // src/main.ts - var exports_main = {}; - __export(exports_main, { - startApp: () => startApp, - parseCap: () => parseCap, - getNearestExit: () => getNearestExit - }); - // ../edge-apps-library/dist/utils/theme.js - var DEFAULT_THEME_COLORS = { - primary: "#972EFF", - secondary: "#454BD2", - tertiary: "#FFFFFF", - background: "#C9CDD0" - }; - function getPrimaryColor(accentColor) { - if (!accentColor || accentColor.toLowerCase() === "#ffffff") { - return DEFAULT_THEME_COLORS.primary; - } - return accentColor; - } - function getSecondaryColor(theme, lightColor, darkColor) { - const defaultSecondary = "#adafbe"; - if (theme === "light") { - return !lightColor || lightColor.toLowerCase() === "#ffffff" ? defaultSecondary : lightColor; - } - if (theme === "dark") { - return !darkColor || darkColor.toLowerCase() === "#ffffff" ? defaultSecondary : darkColor; - } - return DEFAULT_THEME_COLORS.secondary; - } - function getThemeColors() { - const settings = screenly.settings; - const primary = getPrimaryColor(settings.screenly_color_accent); - const secondary = getSecondaryColor(settings.theme, settings.screenly_color_light, settings.screenly_color_dark); - return { - primary, - secondary, - tertiary: DEFAULT_THEME_COLORS.tertiary, - background: DEFAULT_THEME_COLORS.background - }; - } - function applyThemeColors(colors) { - document.documentElement.style.setProperty("--theme-color-primary", colors.primary); - document.documentElement.style.setProperty("--theme-color-secondary", colors.secondary); - document.documentElement.style.setProperty("--theme-color-tertiary", colors.tertiary); - document.documentElement.style.setProperty("--theme-color-background", colors.background); - } - function setupTheme() { - const colors = getThemeColors(); - applyThemeColors(colors); - return colors; - } - // ../edge-apps-library/dist/utils/locale.js - var import_tz_lookup = __toESM(require_tz(), 1); - var import_country_locale_map = __toESM(require_country_locale_map(), 1); - // ../edge-apps-library/dist/utils/metadata.js - function getMetadata() { - return screenly.metadata; - } - // ../edge-apps-library/dist/utils/settings.js - function getSettings() { - return screenly.settings; - } - function getCorsProxyUrl() { - return screenly.cors_proxy_url; - } - function signalReady() { - screenly.signalReadyForRendering(); - } - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/util.js - var nameStartChar = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; - var nameChar = nameStartChar + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; - var nameRegexp = "[" + nameStartChar + "][" + nameChar + "]*"; - var regexName = new RegExp("^" + nameRegexp + "$"); - function getAllMatches(string, regex) { - const matches = []; - let match = regex.exec(string); - while (match) { - const allmatches = []; - allmatches.startIndex = regex.lastIndex - match[0].length; - const len = match.length; - for (let index = 0;index < len; index++) { - allmatches.push(match[index]); - } - matches.push(allmatches); - match = regex.exec(string); - } - return matches; - } - var isName = function(string) { - const match = regexName.exec(string); - return !(match === null || typeof match === "undefined"); - }; - function isExist(v) { - return typeof v !== "undefined"; - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/validator.js - var defaultOptions = { - allowBooleanAttributes: false, - unpairedTags: [] - }; - function validate(xmlData, options) { - options = Object.assign({}, defaultOptions, options); - const tags = []; - let tagFound = false; - let reachedRoot = false; - if (xmlData[0] === "\uFEFF") { - xmlData = xmlData.substr(1); - } - for (let i = 0;i < xmlData.length; i++) { - if (xmlData[i] === "<" && xmlData[i + 1] === "?") { - i += 2; - i = readPI(xmlData, i); - if (i.err) - return i; - } else if (xmlData[i] === "<") { - let tagStartPos = i; - i++; - if (xmlData[i] === "!") { - i = readCommentAndCDATA(xmlData, i); - continue; - } else { - let closingTag = false; - if (xmlData[i] === "/") { - closingTag = true; - i++; - } - let tagName = ""; - for (;i < xmlData.length && xmlData[i] !== ">" && xmlData[i] !== " " && xmlData[i] !== "\t" && xmlData[i] !== ` -` && xmlData[i] !== "\r"; i++) { - tagName += xmlData[i]; - } - tagName = tagName.trim(); - if (tagName[tagName.length - 1] === "/") { - tagName = tagName.substring(0, tagName.length - 1); - i--; - } - if (!validateTagName(tagName)) { - let msg; - if (tagName.trim().length === 0) { - msg = "Invalid space after '<'."; - } else { - msg = "Tag '" + tagName + "' is an invalid name."; - } - return getErrorObject("InvalidTag", msg, getLineNumberForPosition(xmlData, i)); - } - const result = readAttributeStr(xmlData, i); - if (result === false) { - return getErrorObject("InvalidAttr", "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i)); - } - let attrStr = result.value; - i = result.index; - if (attrStr[attrStr.length - 1] === "/") { - const attrStrStart = i - attrStr.length; - attrStr = attrStr.substring(0, attrStr.length - 1); - const isValid = validateAttributeString(attrStr, options); - if (isValid === true) { - tagFound = true; - } else { - return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line)); - } - } else if (closingTag) { - if (!result.tagClosed) { - return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i)); - } else if (attrStr.trim().length > 0) { - return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos)); - } else if (tags.length === 0) { - return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos)); - } else { - const otg = tags.pop(); - if (tagName !== otg.tagName) { - let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos); - return getErrorObject("InvalidTag", "Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ", col " + openPos.col + ") instead of closing tag '" + tagName + "'.", getLineNumberForPosition(xmlData, tagStartPos)); - } - if (tags.length == 0) { - reachedRoot = true; - } - } - } else { - const isValid = validateAttributeString(attrStr, options); - if (isValid !== true) { - return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line)); - } - if (reachedRoot === true) { - return getErrorObject("InvalidXml", "Multiple possible root nodes found.", getLineNumberForPosition(xmlData, i)); - } else if (options.unpairedTags.indexOf(tagName) !== -1) {} else { - tags.push({ tagName, tagStartPos }); - } - tagFound = true; - } - for (i++;i < xmlData.length; i++) { - if (xmlData[i] === "<") { - if (xmlData[i + 1] === "!") { - i++; - i = readCommentAndCDATA(xmlData, i); - continue; - } else if (xmlData[i + 1] === "?") { - i = readPI(xmlData, ++i); - if (i.err) - return i; - } else { - break; - } - } else if (xmlData[i] === "&") { - const afterAmp = validateAmpersand(xmlData, i); - if (afterAmp == -1) - return getErrorObject("InvalidChar", "char '&' is not expected.", getLineNumberForPosition(xmlData, i)); - i = afterAmp; - } else { - if (reachedRoot === true && !isWhiteSpace(xmlData[i])) { - return getErrorObject("InvalidXml", "Extra text at the end", getLineNumberForPosition(xmlData, i)); - } - } - } - if (xmlData[i] === "<") { - i--; - } - } - } else { - if (isWhiteSpace(xmlData[i])) { - continue; - } - return getErrorObject("InvalidChar", "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i)); - } - } - if (!tagFound) { - return getErrorObject("InvalidXml", "Start tag expected.", 1); - } else if (tags.length == 1) { - return getErrorObject("InvalidTag", "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos)); - } else if (tags.length > 0) { - return getErrorObject("InvalidXml", "Invalid '" + JSON.stringify(tags.map((t) => t.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 }); - } - return true; - } - function isWhiteSpace(char) { - return char === " " || char === "\t" || char === ` -` || char === "\r"; - } - function readPI(xmlData, i) { - const start = i; - for (;i < xmlData.length; i++) { - if (xmlData[i] == "?" || xmlData[i] == " ") { - const tagname = xmlData.substr(start, i - start); - if (i > 5 && tagname === "xml") { - return getErrorObject("InvalidXml", "XML declaration allowed only at the start of the document.", getLineNumberForPosition(xmlData, i)); - } else if (xmlData[i] == "?" && xmlData[i + 1] == ">") { - i++; - break; - } else { - continue; - } - } - } - return i; - } - function readCommentAndCDATA(xmlData, i) { - if (xmlData.length > i + 5 && xmlData[i + 1] === "-" && xmlData[i + 2] === "-") { - for (i += 3;i < xmlData.length; i++) { - if (xmlData[i] === "-" && xmlData[i + 1] === "-" && xmlData[i + 2] === ">") { - i += 2; - break; - } - } - } else if (xmlData.length > i + 8 && xmlData[i + 1] === "D" && xmlData[i + 2] === "O" && xmlData[i + 3] === "C" && xmlData[i + 4] === "T" && xmlData[i + 5] === "Y" && xmlData[i + 6] === "P" && xmlData[i + 7] === "E") { - let angleBracketsCount = 1; - for (i += 8;i < xmlData.length; i++) { - if (xmlData[i] === "<") { - angleBracketsCount++; - } else if (xmlData[i] === ">") { - angleBracketsCount--; - if (angleBracketsCount === 0) { - break; - } - } - } - } else if (xmlData.length > i + 9 && xmlData[i + 1] === "[" && xmlData[i + 2] === "C" && xmlData[i + 3] === "D" && xmlData[i + 4] === "A" && xmlData[i + 5] === "T" && xmlData[i + 6] === "A" && xmlData[i + 7] === "[") { - for (i += 8;i < xmlData.length; i++) { - if (xmlData[i] === "]" && xmlData[i + 1] === "]" && xmlData[i + 2] === ">") { - i += 2; - break; - } - } - } - return i; - } - var doubleQuote = '"'; - var singleQuote = "'"; - function readAttributeStr(xmlData, i) { - let attrStr = ""; - let startChar = ""; - let tagClosed = false; - for (;i < xmlData.length; i++) { - if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) { - if (startChar === "") { - startChar = xmlData[i]; - } else if (startChar !== xmlData[i]) {} else { - startChar = ""; - } - } else if (xmlData[i] === ">") { - if (startChar === "") { - tagClosed = true; - break; - } - } - attrStr += xmlData[i]; - } - if (startChar !== "") { - return false; - } - return { - value: attrStr, - index: i, - tagClosed - }; - } - var validAttrStrRegxp = new RegExp(`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['"])(([\\s\\S])*?)\\5)?`, "g"); - function validateAttributeString(attrStr, options) { - const matches = getAllMatches(attrStr, validAttrStrRegxp); - const attrNames = {}; - for (let i = 0;i < matches.length; i++) { - if (matches[i][1].length === 0) { - return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i])); - } else if (matches[i][3] !== undefined && matches[i][4] === undefined) { - return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i])); - } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) { - return getErrorObject("InvalidAttr", "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i])); - } - const attrName = matches[i][2]; - if (!validateAttrName(attrName)) { - return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i])); - } - if (!attrNames.hasOwnProperty(attrName)) { - attrNames[attrName] = 1; - } else { - return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i])); - } - } - return true; - } - function validateNumberAmpersand(xmlData, i) { - let re = /\d/; - if (xmlData[i] === "x") { - i++; - re = /[\da-fA-F]/; - } - for (;i < xmlData.length; i++) { - if (xmlData[i] === ";") - return i; - if (!xmlData[i].match(re)) - break; - } - return -1; - } - function validateAmpersand(xmlData, i) { - i++; - if (xmlData[i] === ";") - return -1; - if (xmlData[i] === "#") { - i++; - return validateNumberAmpersand(xmlData, i); - } - let count = 0; - for (;i < xmlData.length; i++, count++) { - if (xmlData[i].match(/\w/) && count < 20) - continue; - if (xmlData[i] === ";") - break; - return -1; - } - return i; - } - function getErrorObject(code, message, lineNumber) { - return { - err: { - code, - msg: message, - line: lineNumber.line || lineNumber, - col: lineNumber.col - } - }; - } - function validateAttrName(attrName) { - return isName(attrName); - } - function validateTagName(tagname) { - return isName(tagname); - } - function getLineNumberForPosition(xmlData, index) { - const lines = xmlData.substring(0, index).split(/\r?\n/); - return { - line: lines.length, - col: lines[lines.length - 1].length + 1 - }; - } - function getPositionFromMatch(match) { - return match.startIndex + match[1].length; - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js - var defaultOptions2 = { - preserveOrder: false, - attributeNamePrefix: "@_", - attributesGroupName: false, - textNodeName: "#text", - ignoreAttributes: true, - removeNSPrefix: false, - allowBooleanAttributes: false, - parseTagValue: true, - parseAttributeValue: false, - trimValues: true, - cdataPropName: false, - numberParseOptions: { - hex: true, - leadingZeros: true, - eNotation: true - }, - tagValueProcessor: function(tagName, val) { - return val; - }, - attributeValueProcessor: function(attrName, val) { - return val; - }, - stopNodes: [], - alwaysCreateTextNode: false, - isArray: () => false, - commentPropName: false, - unpairedTags: [], - processEntities: true, - htmlEntities: false, - ignoreDeclaration: false, - ignorePiTags: false, - transformTagName: false, - transformAttributeName: false, - updateTag: function(tagName, jPath, attrs) { - return tagName; - }, - captureMetaData: false - }; - var buildOptions = function(options) { - return Object.assign({}, defaultOptions2, options); - }; - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js - var METADATA_SYMBOL; - if (typeof Symbol !== "function") { - METADATA_SYMBOL = "@@xmlMetadata"; - } else { - METADATA_SYMBOL = Symbol("XML Node Metadata"); - } - - class XmlNode { - constructor(tagname) { - this.tagname = tagname; - this.child = []; - this[":@"] = {}; - } - add(key, val) { - if (key === "__proto__") - key = "#__proto__"; - this.child.push({ [key]: val }); - } - addChild(node, startIndex) { - if (node.tagname === "__proto__") - node.tagname = "#__proto__"; - if (node[":@"] && Object.keys(node[":@"]).length > 0) { - this.child.push({ [node.tagname]: node.child, [":@"]: node[":@"] }); - } else { - this.child.push({ [node.tagname]: node.child }); - } - if (startIndex !== undefined) { - this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex }; - } - } - static getMetaDataSymbol() { - return METADATA_SYMBOL; - } - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js - class DocTypeReader { - constructor(processEntities) { - this.suppressValidationErr = !processEntities; - } - readDocType(xmlData, i) { - const entities = {}; - if (xmlData[i + 3] === "O" && xmlData[i + 4] === "C" && xmlData[i + 5] === "T" && xmlData[i + 6] === "Y" && xmlData[i + 7] === "P" && xmlData[i + 8] === "E") { - i = i + 9; - let angleBracketsCount = 1; - let hasBody = false, comment = false; - let exp = ""; - for (;i < xmlData.length; i++) { - if (xmlData[i] === "<" && !comment) { - if (hasBody && hasSeq(xmlData, "!ENTITY", i)) { - i += 7; - let entityName, val; - [entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr); - if (val.indexOf("&") === -1) - entities[entityName] = { - regx: RegExp(`&${entityName};`, "g"), - val - }; - } else if (hasBody && hasSeq(xmlData, "!ELEMENT", i)) { - i += 8; - const { index } = this.readElementExp(xmlData, i + 1); - i = index; - } else if (hasBody && hasSeq(xmlData, "!ATTLIST", i)) { - i += 8; - } else if (hasBody && hasSeq(xmlData, "!NOTATION", i)) { - i += 9; - const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr); - i = index; - } else if (hasSeq(xmlData, "!--", i)) - comment = true; - else - throw new Error(`Invalid DOCTYPE`); - angleBracketsCount++; - exp = ""; - } else if (xmlData[i] === ">") { - if (comment) { - if (xmlData[i - 1] === "-" && xmlData[i - 2] === "-") { - comment = false; - angleBracketsCount--; - } - } else { - angleBracketsCount--; - } - if (angleBracketsCount === 0) { - break; - } - } else if (xmlData[i] === "[") { - hasBody = true; - } else { - exp += xmlData[i]; - } - } - if (angleBracketsCount !== 0) { - throw new Error(`Unclosed DOCTYPE`); - } - } else { - throw new Error(`Invalid Tag instead of DOCTYPE`); - } - return { entities, i }; - } - readEntityExp(xmlData, i) { - i = skipWhitespace(xmlData, i); - let entityName = ""; - while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") { - entityName += xmlData[i]; - i++; - } - validateEntityName(entityName); - i = skipWhitespace(xmlData, i); - if (!this.suppressValidationErr) { - if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") { - throw new Error("External entities are not supported"); - } else if (xmlData[i] === "%") { - throw new Error("Parameter entities are not supported"); - } - } - let entityValue = ""; - [i, entityValue] = this.readIdentifierVal(xmlData, i, "entity"); - i--; - return [entityName, entityValue, i]; - } - readNotationExp(xmlData, i) { - i = skipWhitespace(xmlData, i); - let notationName = ""; - while (i < xmlData.length && !/\s/.test(xmlData[i])) { - notationName += xmlData[i]; - i++; - } - !this.suppressValidationErr && validateEntityName(notationName); - i = skipWhitespace(xmlData, i); - const identifierType = xmlData.substring(i, i + 6).toUpperCase(); - if (!this.suppressValidationErr && identifierType !== "SYSTEM" && identifierType !== "PUBLIC") { - throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`); - } - i += identifierType.length; - i = skipWhitespace(xmlData, i); - let publicIdentifier = null; - let systemIdentifier = null; - if (identifierType === "PUBLIC") { - [i, publicIdentifier] = this.readIdentifierVal(xmlData, i, "publicIdentifier"); - i = skipWhitespace(xmlData, i); - if (xmlData[i] === '"' || xmlData[i] === "'") { - [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier"); - } - } else if (identifierType === "SYSTEM") { - [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier"); - if (!this.suppressValidationErr && !systemIdentifier) { - throw new Error("Missing mandatory system identifier for SYSTEM notation"); - } - } - return { notationName, publicIdentifier, systemIdentifier, index: --i }; - } - readIdentifierVal(xmlData, i, type) { - let identifierVal = ""; - const startChar = xmlData[i]; - if (startChar !== '"' && startChar !== "'") { - throw new Error(`Expected quoted string, found "${startChar}"`); - } - i++; - while (i < xmlData.length && xmlData[i] !== startChar) { - identifierVal += xmlData[i]; - i++; - } - if (xmlData[i] !== startChar) { - throw new Error(`Unterminated ${type} value`); - } - i++; - return [i, identifierVal]; - } - readElementExp(xmlData, i) { - i = skipWhitespace(xmlData, i); - let elementName = ""; - while (i < xmlData.length && !/\s/.test(xmlData[i])) { - elementName += xmlData[i]; - i++; - } - if (!this.suppressValidationErr && !isName(elementName)) { - throw new Error(`Invalid element name: "${elementName}"`); - } - i = skipWhitespace(xmlData, i); - let contentModel = ""; - if (xmlData[i] === "E" && hasSeq(xmlData, "MPTY", i)) - i += 4; - else if (xmlData[i] === "A" && hasSeq(xmlData, "NY", i)) - i += 2; - else if (xmlData[i] === "(") { - i++; - while (i < xmlData.length && xmlData[i] !== ")") { - contentModel += xmlData[i]; - i++; - } - if (xmlData[i] !== ")") { - throw new Error("Unterminated content model"); - } - } else if (!this.suppressValidationErr) { - throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`); - } - return { - elementName, - contentModel: contentModel.trim(), - index: i - }; - } - readAttlistExp(xmlData, i) { - i = skipWhitespace(xmlData, i); - let elementName = ""; - while (i < xmlData.length && !/\s/.test(xmlData[i])) { - elementName += xmlData[i]; - i++; - } - validateEntityName(elementName); - i = skipWhitespace(xmlData, i); - let attributeName = ""; - while (i < xmlData.length && !/\s/.test(xmlData[i])) { - attributeName += xmlData[i]; - i++; - } - if (!validateEntityName(attributeName)) { - throw new Error(`Invalid attribute name: "${attributeName}"`); - } - i = skipWhitespace(xmlData, i); - let attributeType = ""; - if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") { - attributeType = "NOTATION"; - i += 8; - i = skipWhitespace(xmlData, i); - if (xmlData[i] !== "(") { - throw new Error(`Expected '(', found "${xmlData[i]}"`); - } - i++; - let allowedNotations = []; - while (i < xmlData.length && xmlData[i] !== ")") { - let notation = ""; - while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") { - notation += xmlData[i]; - i++; - } - notation = notation.trim(); - if (!validateEntityName(notation)) { - throw new Error(`Invalid notation name: "${notation}"`); - } - allowedNotations.push(notation); - if (xmlData[i] === "|") { - i++; - i = skipWhitespace(xmlData, i); - } - } - if (xmlData[i] !== ")") { - throw new Error("Unterminated list of notations"); - } - i++; - attributeType += " (" + allowedNotations.join("|") + ")"; - } else { - while (i < xmlData.length && !/\s/.test(xmlData[i])) { - attributeType += xmlData[i]; - i++; - } - const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"]; - if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) { - throw new Error(`Invalid attribute type: "${attributeType}"`); - } - } - i = skipWhitespace(xmlData, i); - let defaultValue = ""; - if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") { - defaultValue = "#REQUIRED"; - i += 8; - } else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") { - defaultValue = "#IMPLIED"; - i += 7; - } else { - [i, defaultValue] = this.readIdentifierVal(xmlData, i, "ATTLIST"); - } - return { - elementName, - attributeName, - attributeType, - defaultValue, - index: i - }; - } - } - var skipWhitespace = (data, index) => { - while (index < data.length && /\s/.test(data[index])) { - index++; - } - return index; - }; - function hasSeq(data, seq, i) { - for (let j = 0;j < seq.length; j++) { - if (seq[j] !== data[i + j + 1]) - return false; - } - return true; - } - function validateEntityName(name) { - if (isName(name)) - return name; - else - throw new Error(`Invalid entity name ${name}`); - } - - // node_modules/.bun/strnum@2.1.1/node_modules/strnum/strnum.js - var hexRegex = /^[-+]?0x[a-fA-F0-9]+$/; - var numRegex = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/; - var consider = { - hex: true, - leadingZeros: true, - decimalPoint: ".", - eNotation: true - }; - function toNumber(str, options = {}) { - options = Object.assign({}, consider, options); - if (!str || typeof str !== "string") - return str; - let trimmedStr = str.trim(); - if (options.skipLike !== undefined && options.skipLike.test(trimmedStr)) - return str; - else if (str === "0") - return 0; - else if (options.hex && hexRegex.test(trimmedStr)) { - return parse_int(trimmedStr, 16); - } else if (trimmedStr.search(/.+[eE].+/) !== -1) { - return resolveEnotation(str, trimmedStr, options); - } else { - const match = numRegex.exec(trimmedStr); - if (match) { - const sign = match[1] || ""; - const leadingZeros = match[2]; - let numTrimmedByZeros = trimZeros(match[3]); - const decimalAdjacentToLeadingZeros = sign ? str[leadingZeros.length + 1] === "." : str[leadingZeros.length] === "."; - if (!options.leadingZeros && (leadingZeros.length > 1 || leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros)) { - return str; - } else { - const num = Number(trimmedStr); - const parsedStr = String(num); - if (num === 0) - return num; - if (parsedStr.search(/[eE]/) !== -1) { - if (options.eNotation) - return num; - else - return str; - } else if (trimmedStr.indexOf(".") !== -1) { - if (parsedStr === "0") - return num; - else if (parsedStr === numTrimmedByZeros) - return num; - else if (parsedStr === `${sign}${numTrimmedByZeros}`) - return num; - else - return str; - } - let n = leadingZeros ? numTrimmedByZeros : trimmedStr; - if (leadingZeros) { - return n === parsedStr || sign + n === parsedStr ? num : str; - } else { - return n === parsedStr || n === sign + parsedStr ? num : str; - } - } - } else { - return str; - } - } - } - var eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/; - function resolveEnotation(str, trimmedStr, options) { - if (!options.eNotation) - return str; - const notation = trimmedStr.match(eNotationRegx); - if (notation) { - let sign = notation[1] || ""; - const eChar = notation[3].indexOf("e") === -1 ? "E" : "e"; - const leadingZeros = notation[2]; - const eAdjacentToLeadingZeros = sign ? str[leadingZeros.length + 1] === eChar : str[leadingZeros.length] === eChar; - if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) - return str; - else if (leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) { - return Number(trimmedStr); - } else if (options.leadingZeros && !eAdjacentToLeadingZeros) { - trimmedStr = (notation[1] || "") + notation[3]; - return Number(trimmedStr); - } else - return str; - } else { - return str; - } - } - function trimZeros(numStr) { - if (numStr && numStr.indexOf(".") !== -1) { - numStr = numStr.replace(/0+$/, ""); - if (numStr === ".") - numStr = "0"; - else if (numStr[0] === ".") - numStr = "0" + numStr; - else if (numStr[numStr.length - 1] === ".") - numStr = numStr.substring(0, numStr.length - 1); - return numStr; - } - return numStr; - } - function parse_int(numStr, base) { - if (parseInt) - return parseInt(numStr, base); - else if (Number.parseInt) - return Number.parseInt(numStr, base); - else if (window && window.parseInt) - return window.parseInt(numStr, base); - else - throw new Error("parseInt, Number.parseInt, window.parseInt are not supported"); - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/ignoreAttributes.js - function getIgnoreAttributesFn(ignoreAttributes) { - if (typeof ignoreAttributes === "function") { - return ignoreAttributes; - } - if (Array.isArray(ignoreAttributes)) { - return (attrName) => { - for (const pattern of ignoreAttributes) { - if (typeof pattern === "string" && attrName === pattern) { - return true; - } - if (pattern instanceof RegExp && pattern.test(attrName)) { - return true; - } - } - }; - } - return () => false; - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js - class OrderedObjParser { - constructor(options) { - this.options = options; - this.currentNode = null; - this.tagsNodeStack = []; - this.docTypeEntities = {}; - this.lastEntities = { - apos: { regex: /&(apos|#39|#x27);/g, val: "'" }, - gt: { regex: /&(gt|#62|#x3E);/g, val: ">" }, - lt: { regex: /&(lt|#60|#x3C);/g, val: "<" }, - quot: { regex: /&(quot|#34|#x22);/g, val: '"' } - }; - this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" }; - this.htmlEntities = { - space: { regex: /&(nbsp|#160);/g, val: " " }, - cent: { regex: /&(cent|#162);/g, val: "¢" }, - pound: { regex: /&(pound|#163);/g, val: "£" }, - yen: { regex: /&(yen|#165);/g, val: "¥" }, - euro: { regex: /&(euro|#8364);/g, val: "€" }, - copyright: { regex: /&(copy|#169);/g, val: "©" }, - reg: { regex: /&(reg|#174);/g, val: "®" }, - inr: { regex: /&(inr|#8377);/g, val: "₹" }, - num_dec: { regex: /&#([0-9]{1,7});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) }, - num_hex: { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) } - }; - this.addExternalEntities = addExternalEntities; - this.parseXml = parseXml; - this.parseTextData = parseTextData; - this.resolveNameSpace = resolveNameSpace; - this.buildAttributesMap = buildAttributesMap; - this.isItStopNode = isItStopNode; - this.replaceEntitiesValue = replaceEntitiesValue; - this.readStopNodeData = readStopNodeData; - this.saveTextToParentTag = saveTextToParentTag; - this.addChild = addChild; - this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes); - if (this.options.stopNodes && this.options.stopNodes.length > 0) { - this.stopNodesExact = new Set; - this.stopNodesWildcard = new Set; - for (let i = 0;i < this.options.stopNodes.length; i++) { - const stopNodeExp = this.options.stopNodes[i]; - if (typeof stopNodeExp !== "string") - continue; - if (stopNodeExp.startsWith("*.")) { - this.stopNodesWildcard.add(stopNodeExp.substring(2)); - } else { - this.stopNodesExact.add(stopNodeExp); - } - } - } - } - } - function addExternalEntities(externalEntities) { - const entKeys = Object.keys(externalEntities); - for (let i = 0;i < entKeys.length; i++) { - const ent = entKeys[i]; - this.lastEntities[ent] = { - regex: new RegExp("&" + ent + ";", "g"), - val: externalEntities[ent] - }; - } - } - function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) { - if (val !== undefined) { - if (this.options.trimValues && !dontTrim) { - val = val.trim(); - } - if (val.length > 0) { - if (!escapeEntities) - val = this.replaceEntitiesValue(val); - const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode); - if (newval === null || newval === undefined) { - return val; - } else if (typeof newval !== typeof val || newval !== val) { - return newval; - } else if (this.options.trimValues) { - return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions); - } else { - const trimmedVal = val.trim(); - if (trimmedVal === val) { - return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions); - } else { - return val; - } - } - } - } - } - function resolveNameSpace(tagname) { - if (this.options.removeNSPrefix) { - const tags = tagname.split(":"); - const prefix = tagname.charAt(0) === "/" ? "/" : ""; - if (tags[0] === "xmlns") { - return ""; - } - if (tags.length === 2) { - tagname = prefix + tags[1]; - } - } - return tagname; - } - var attrsRegx = new RegExp(`([^\\s=]+)\\s*(=\\s*(['"])([\\s\\S]*?)\\3)?`, "gm"); - function buildAttributesMap(attrStr, jPath, tagName) { - if (this.options.ignoreAttributes !== true && typeof attrStr === "string") { - const matches = getAllMatches(attrStr, attrsRegx); - const len = matches.length; - const attrs = {}; - for (let i = 0;i < len; i++) { - const attrName = this.resolveNameSpace(matches[i][1]); - if (this.ignoreAttributesFn(attrName, jPath)) { - continue; - } - let oldVal = matches[i][4]; - let aName = this.options.attributeNamePrefix + attrName; - if (attrName.length) { - if (this.options.transformAttributeName) { - aName = this.options.transformAttributeName(aName); - } - if (aName === "__proto__") - aName = "#__proto__"; - if (oldVal !== undefined) { - if (this.options.trimValues) { - oldVal = oldVal.trim(); - } - oldVal = this.replaceEntitiesValue(oldVal); - const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath); - if (newVal === null || newVal === undefined) { - attrs[aName] = oldVal; - } else if (typeof newVal !== typeof oldVal || newVal !== oldVal) { - attrs[aName] = newVal; - } else { - attrs[aName] = parseValue(oldVal, this.options.parseAttributeValue, this.options.numberParseOptions); - } - } else if (this.options.allowBooleanAttributes) { - attrs[aName] = true; - } - } - } - if (!Object.keys(attrs).length) { - return; - } - if (this.options.attributesGroupName) { - const attrCollection = {}; - attrCollection[this.options.attributesGroupName] = attrs; - return attrCollection; - } - return attrs; - } - } - var parseXml = function(xmlData) { - xmlData = xmlData.replace(/\r\n?/g, ` -`); - const xmlObj = new XmlNode("!xml"); - let currentNode = xmlObj; - let textData = ""; - let jPath = ""; - const docTypeReader = new DocTypeReader(this.options.processEntities); - for (let i = 0;i < xmlData.length; i++) { - const ch = xmlData[i]; - if (ch === "<") { - if (xmlData[i + 1] === "/") { - const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed."); - let tagName = xmlData.substring(i + 2, closeIndex).trim(); - if (this.options.removeNSPrefix) { - const colonIndex = tagName.indexOf(":"); - if (colonIndex !== -1) { - tagName = tagName.substr(colonIndex + 1); - } - } - if (this.options.transformTagName) { - tagName = this.options.transformTagName(tagName); - } - if (currentNode) { - textData = this.saveTextToParentTag(textData, currentNode, jPath); - } - const lastTagName = jPath.substring(jPath.lastIndexOf(".") + 1); - if (tagName && this.options.unpairedTags.indexOf(tagName) !== -1) { - throw new Error(`Unpaired tag can not be used as closing tag: `); - } - let propIndex = 0; - if (lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1) { - propIndex = jPath.lastIndexOf(".", jPath.lastIndexOf(".") - 1); - this.tagsNodeStack.pop(); - } else { - propIndex = jPath.lastIndexOf("."); - } - jPath = jPath.substring(0, propIndex); - currentNode = this.tagsNodeStack.pop(); - textData = ""; - i = closeIndex; - } else if (xmlData[i + 1] === "?") { - let tagData = readTagExp(xmlData, i, false, "?>"); - if (!tagData) - throw new Error("Pi Tag is not closed."); - textData = this.saveTextToParentTag(textData, currentNode, jPath); - if (this.options.ignoreDeclaration && tagData.tagName === "?xml" || this.options.ignorePiTags) {} else { - const childNode = new XmlNode(tagData.tagName); - childNode.add(this.options.textNodeName, ""); - if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) { - childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName); - } - this.addChild(currentNode, childNode, jPath, i); - } - i = tagData.closeIndex + 1; - } else if (xmlData.substr(i + 1, 3) === "!--") { - const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed."); - if (this.options.commentPropName) { - const comment = xmlData.substring(i + 4, endIndex - 2); - textData = this.saveTextToParentTag(textData, currentNode, jPath); - currentNode.add(this.options.commentPropName, [{ [this.options.textNodeName]: comment }]); - } - i = endIndex; - } else if (xmlData.substr(i + 1, 2) === "!D") { - const result = docTypeReader.readDocType(xmlData, i); - this.docTypeEntities = result.entities; - i = result.i; - } else if (xmlData.substr(i + 1, 2) === "![") { - const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2; - const tagExp = xmlData.substring(i + 9, closeIndex); - textData = this.saveTextToParentTag(textData, currentNode, jPath); - let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true); - if (val == undefined) - val = ""; - if (this.options.cdataPropName) { - currentNode.add(this.options.cdataPropName, [{ [this.options.textNodeName]: tagExp }]); - } else { - currentNode.add(this.options.textNodeName, val); - } - i = closeIndex + 2; - } else { - let result = readTagExp(xmlData, i, this.options.removeNSPrefix); - let tagName = result.tagName; - const rawTagName = result.rawTagName; - let tagExp = result.tagExp; - let attrExpPresent = result.attrExpPresent; - let closeIndex = result.closeIndex; - if (this.options.transformTagName) { - tagName = this.options.transformTagName(tagName); - } - if (currentNode && textData) { - if (currentNode.tagname !== "!xml") { - textData = this.saveTextToParentTag(textData, currentNode, jPath, false); - } - } - const lastTag = currentNode; - if (lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1) { - currentNode = this.tagsNodeStack.pop(); - jPath = jPath.substring(0, jPath.lastIndexOf(".")); - } - if (tagName !== xmlObj.tagname) { - jPath += jPath ? "." + tagName : tagName; - } - const startIndex = i; - if (this.isItStopNode(this.stopNodesExact, this.stopNodesWildcard, jPath, tagName)) { - let tagContent = ""; - if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) { - if (tagName[tagName.length - 1] === "/") { - tagName = tagName.substr(0, tagName.length - 1); - jPath = jPath.substr(0, jPath.length - 1); - tagExp = tagName; - } else { - tagExp = tagExp.substr(0, tagExp.length - 1); - } - i = result.closeIndex; - } else if (this.options.unpairedTags.indexOf(tagName) !== -1) { - i = result.closeIndex; - } else { - const result2 = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1); - if (!result2) - throw new Error(`Unexpected end of ${rawTagName}`); - i = result2.i; - tagContent = result2.tagContent; - } - const childNode = new XmlNode(tagName); - if (tagName !== tagExp && attrExpPresent) { - childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); - } - if (tagContent) { - tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true); - } - jPath = jPath.substr(0, jPath.lastIndexOf(".")); - childNode.add(this.options.textNodeName, tagContent); - this.addChild(currentNode, childNode, jPath, startIndex); - } else { - if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) { - if (tagName[tagName.length - 1] === "/") { - tagName = tagName.substr(0, tagName.length - 1); - jPath = jPath.substr(0, jPath.length - 1); - tagExp = tagName; - } else { - tagExp = tagExp.substr(0, tagExp.length - 1); - } - if (this.options.transformTagName) { - tagName = this.options.transformTagName(tagName); - } - const childNode = new XmlNode(tagName); - if (tagName !== tagExp && attrExpPresent) { - childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); - } - this.addChild(currentNode, childNode, jPath, startIndex); - jPath = jPath.substr(0, jPath.lastIndexOf(".")); - } else { - const childNode = new XmlNode(tagName); - this.tagsNodeStack.push(currentNode); - if (tagName !== tagExp && attrExpPresent) { - childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); - } - this.addChild(currentNode, childNode, jPath, startIndex); - currentNode = childNode; - } - textData = ""; - i = closeIndex; - } - } - } else { - textData += xmlData[i]; - } - } - return xmlObj.child; - }; - function addChild(currentNode, childNode, jPath, startIndex) { - if (!this.options.captureMetaData) - startIndex = undefined; - const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"]); - if (result === false) {} else if (typeof result === "string") { - childNode.tagname = result; - currentNode.addChild(childNode, startIndex); - } else { - currentNode.addChild(childNode, startIndex); - } - } - var replaceEntitiesValue = function(val) { - if (this.options.processEntities) { - for (let entityName in this.docTypeEntities) { - const entity = this.docTypeEntities[entityName]; - val = val.replace(entity.regx, entity.val); - } - for (let entityName in this.lastEntities) { - const entity = this.lastEntities[entityName]; - val = val.replace(entity.regex, entity.val); - } - if (this.options.htmlEntities) { - for (let entityName in this.htmlEntities) { - const entity = this.htmlEntities[entityName]; - val = val.replace(entity.regex, entity.val); - } - } - val = val.replace(this.ampEntity.regex, this.ampEntity.val); - } - return val; - }; - function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) { - if (textData) { - if (isLeafNode === undefined) - isLeafNode = currentNode.child.length === 0; - textData = this.parseTextData(textData, currentNode.tagname, jPath, false, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false, isLeafNode); - if (textData !== undefined && textData !== "") - currentNode.add(this.options.textNodeName, textData); - textData = ""; - } - return textData; - } - function isItStopNode(stopNodesExact, stopNodesWildcard, jPath, currentTagName) { - if (stopNodesWildcard && stopNodesWildcard.has(currentTagName)) - return true; - if (stopNodesExact && stopNodesExact.has(jPath)) - return true; - return false; - } - function tagExpWithClosingIndex(xmlData, i, closingChar = ">") { - let attrBoundary; - let tagExp = ""; - for (let index = i;index < xmlData.length; index++) { - let ch = xmlData[index]; - if (attrBoundary) { - if (ch === attrBoundary) - attrBoundary = ""; - } else if (ch === '"' || ch === "'") { - attrBoundary = ch; - } else if (ch === closingChar[0]) { - if (closingChar[1]) { - if (xmlData[index + 1] === closingChar[1]) { - return { - data: tagExp, - index - }; - } - } else { - return { - data: tagExp, - index - }; - } - } else if (ch === "\t") { - ch = " "; - } - tagExp += ch; - } - } - function findClosingIndex(xmlData, str, i, errMsg) { - const closingIndex = xmlData.indexOf(str, i); - if (closingIndex === -1) { - throw new Error(errMsg); - } else { - return closingIndex + str.length - 1; - } - } - function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") { - const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar); - if (!result) - return; - let tagExp = result.data; - const closeIndex = result.index; - const separatorIndex = tagExp.search(/\s/); - let tagName = tagExp; - let attrExpPresent = true; - if (separatorIndex !== -1) { - tagName = tagExp.substring(0, separatorIndex); - tagExp = tagExp.substring(separatorIndex + 1).trimStart(); - } - const rawTagName = tagName; - if (removeNSPrefix) { - const colonIndex = tagName.indexOf(":"); - if (colonIndex !== -1) { - tagName = tagName.substr(colonIndex + 1); - attrExpPresent = tagName !== result.data.substr(colonIndex + 1); - } - } - return { - tagName, - tagExp, - closeIndex, - attrExpPresent, - rawTagName - }; - } - function readStopNodeData(xmlData, tagName, i) { - const startIndex = i; - let openTagCount = 1; - for (;i < xmlData.length; i++) { - if (xmlData[i] === "<") { - if (xmlData[i + 1] === "/") { - const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`); - let closeTagName = xmlData.substring(i + 2, closeIndex).trim(); - if (closeTagName === tagName) { - openTagCount--; - if (openTagCount === 0) { - return { - tagContent: xmlData.substring(startIndex, i), - i: closeIndex - }; - } - } - i = closeIndex; - } else if (xmlData[i + 1] === "?") { - const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed."); - i = closeIndex; - } else if (xmlData.substr(i + 1, 3) === "!--") { - const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed."); - i = closeIndex; - } else if (xmlData.substr(i + 1, 2) === "![") { - const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2; - i = closeIndex; - } else { - const tagData = readTagExp(xmlData, i, ">"); - if (tagData) { - const openTagName = tagData && tagData.tagName; - if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") { - openTagCount++; - } - i = tagData.closeIndex; - } - } - } - } - } - function parseValue(val, shouldParse, options) { - if (shouldParse && typeof val === "string") { - const newval = val.trim(); - if (newval === "true") - return true; - else if (newval === "false") - return false; - else - return toNumber(val, options); - } else { - if (isExist(val)) { - return val; - } else { - return ""; - } - } - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/node2json.js - var METADATA_SYMBOL2 = XmlNode.getMetaDataSymbol(); - function prettify(node, options) { - return compress(node, options); - } - function compress(arr, options, jPath) { - let text; - const compressedObj = {}; - for (let i = 0;i < arr.length; i++) { - const tagObj = arr[i]; - const property = propName(tagObj); - let newJpath = ""; - if (jPath === undefined) - newJpath = property; - else - newJpath = jPath + "." + property; - if (property === options.textNodeName) { - if (text === undefined) - text = tagObj[property]; - else - text += "" + tagObj[property]; - } else if (property === undefined) { - continue; - } else if (tagObj[property]) { - let val = compress(tagObj[property], options, newJpath); - const isLeaf = isLeafTag(val, options); - if (tagObj[METADATA_SYMBOL2] !== undefined) { - val[METADATA_SYMBOL2] = tagObj[METADATA_SYMBOL2]; - } - if (tagObj[":@"]) { - assignAttributes(val, tagObj[":@"], newJpath, options); - } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) { - val = val[options.textNodeName]; - } else if (Object.keys(val).length === 0) { - if (options.alwaysCreateTextNode) - val[options.textNodeName] = ""; - else - val = ""; - } - if (compressedObj[property] !== undefined && compressedObj.hasOwnProperty(property)) { - if (!Array.isArray(compressedObj[property])) { - compressedObj[property] = [compressedObj[property]]; - } - compressedObj[property].push(val); - } else { - if (options.isArray(property, newJpath, isLeaf)) { - compressedObj[property] = [val]; - } else { - compressedObj[property] = val; - } - } - } - } - if (typeof text === "string") { - if (text.length > 0) - compressedObj[options.textNodeName] = text; - } else if (text !== undefined) - compressedObj[options.textNodeName] = text; - return compressedObj; - } - function propName(obj) { - const keys = Object.keys(obj); - for (let i = 0;i < keys.length; i++) { - const key = keys[i]; - if (key !== ":@") - return key; - } - } - function assignAttributes(obj, attrMap, jpath, options) { - if (attrMap) { - const keys = Object.keys(attrMap); - const len = keys.length; - for (let i = 0;i < len; i++) { - const atrrName = keys[i]; - if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) { - obj[atrrName] = [attrMap[atrrName]]; - } else { - obj[atrrName] = attrMap[atrrName]; - } - } - } - } - function isLeafTag(obj, options) { - const { textNodeName } = options; - const propCount = Object.keys(obj).length; - if (propCount === 0) { - return true; - } - if (propCount === 1 && (obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)) { - return true; - } - return false; - } - - // node_modules/.bun/fast-xml-parser@5.3.2/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js - class XMLParser { - constructor(options) { - this.externalEntities = {}; - this.options = buildOptions(options); - } - parse(xmlData, validationOption) { - if (typeof xmlData !== "string" && xmlData.toString) { - xmlData = xmlData.toString(); - } else if (typeof xmlData !== "string") { - throw new Error("XML data is accepted in String or Bytes[] form."); - } - if (validationOption) { - if (validationOption === true) - validationOption = {}; - const result = validate(xmlData, validationOption); - if (result !== true) { - throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`); - } - } - const orderedObjParser = new OrderedObjParser(this.options); - orderedObjParser.addExternalEntities(this.externalEntities); - const orderedResult = orderedObjParser.parseXml(xmlData); - if (this.options.preserveOrder || orderedResult === undefined) - return orderedResult; - else - return prettify(orderedResult, this.options); - } - addEntity(key, value) { - if (value.indexOf("&") !== -1) { - throw new Error("Entity value can't have '&'"); - } else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) { - throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for ' '"); - } else if (value === "&") { - throw new Error("An entity with value '&' is not permitted"); - } else { - this.externalEntities[key] = value; - } - } - static getMetaDataSymbol() { - return XmlNode.getMetaDataSymbol(); - } - } - - // src/main.ts - function parseCap(xml) { - const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_" }); - const json = parser.parse(xml); - const alertsJson = json.alert ? Array.isArray(json.alert) ? json.alert : [json.alert] : []; - const alerts = []; - alertsJson.forEach((a) => { - const infosJson = a.info ? Array.isArray(a.info) ? a.info : [a.info] : []; - const infos = infosJson.map((info) => { - const resourcesJson = info.resource ? Array.isArray(info.resource) ? info.resource : [info.resource] : []; - const areasJson = info.area ? Array.isArray(info.area) ? info.area : [info.area] : []; - return { - language: info.language || "", - category: info.category, - event: info.event, - responseType: info.responseType, - urgency: info.urgency, - severity: info.severity, - certainty: info.certainty, - audience: info.audience, - effective: info.effective, - onset: info.onset, - expires: info.expires, - senderName: info.senderName, - headline: info.headline, - description: info.description, - instruction: info.instruction, - web: info.web, - contact: info.contact, - parameter: info.parameter, - eventCode: info.eventCode, - resources: resourcesJson.map((res) => { - return { - resourceDesc: res.resourceDesc, - mimeType: res.mimeType || res["mimeType"], - size: res.size, - uri: res.uri, - derefUri: res.derefUri, - digest: res.digest, - url: res.uri || res.resourceDesc || "" - }; - }), - areas: areasJson.map((area) => ({ - areaDesc: area.areaDesc || "", - polygon: area.polygon, - circle: area.circle, - geocode: area.geocode, - altitude: area.altitude, - ceiling: area.ceiling - })) - }; - }); - alerts.push({ - identifier: a.identifier || "", - sender: a.sender || "", - sent: a.sent || "", - status: a.status, - msgType: a.msgType, - source: a.source, - scope: a.scope, - restriction: a.restriction, - addresses: a.addresses, - code: a.code, - note: a.note, - references: a.references, - incidents: a.incidents, - infos - }); - }); - return alerts; - } - function getNearestExit(tags) { - for (const tag of tags) { - const lower = tag.toLowerCase(); - if (lower.startsWith("exit:")) { - return tag.slice(5).trim(); - } - if (lower.startsWith("exit-")) { - return tag.slice(5).trim(); - } - } - return; - } - async function fetchCapData(feedUrl, offlineMode) { - if (offlineMode) { - return localStorage.getItem("cap_last"); - } - try { - const cors = getCorsProxyUrl(); - let url = feedUrl; - if (feedUrl && feedUrl.match(/^https?:/)) { - url = `${cors}/${feedUrl}`; - } - const response = await fetch(url); - if (!response.ok) - throw new Error(`HTTP ${response.status}`); - const text = await response.text(); - localStorage.setItem("cap_last", text); - return text; - } catch (err) { - console.warn("CAP fetch failed", err); - return localStorage.getItem("cap_last"); - } - } - function highlightKeywords(text) { - const keywords = [ - "DO NOT", - "DON'T", - "IMMEDIATELY", - "IMMEDIATE", - "NOW", - "MOVE TO", - "EVACUATE", - "CALL", - "WARNING", - "DANGER", - "SHELTER", - "TAKE COVER", - "AVOID", - "STAY", - "SEEK", - "TURN AROUND", - "GET TO", - "LEAVE" - ]; - let result = text; - keywords.forEach((keyword) => { - const regex = new RegExp(`\\b(${keyword})\\b`, "gi"); - result = result.replace(regex, '$1'); - }); - return result; - } - function splitIntoSentences(text) { - return text.split(/(?<=[.!?])\s+/).map((s) => s.trim()).filter((s) => s.length > 0); - } - function proxyUrl(url) { - if (url && url.match(/^https?:/)) { - const cors = getCorsProxyUrl(); - return `${cors}/${url}`; - } - return url; - } - function renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) { - const container = document.getElementById("alerts"); - if (!container) - return; - container.innerHTML = ""; - const slice = alerts.slice(0, maxAlerts); - slice.forEach((alert) => { - const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0]; - if (!info) - return; - const card = document.createElement("div"); - card.className = "alert-card w-full h-full bg-white flex flex-col overflow-y-auto"; - if (alert.status) { - const statusBanner = document.createElement("div"); - let baseClasses = "w-full text-center font-black uppercase tracking-[0.15em] flex-shrink-0 status-stripe-pattern status-banner-text py-[2.5vh] px-[4vw] text-white "; - let statusText = alert.status.toUpperCase(); - if (alert.status === "Exercise") { - statusText = "EXERCISE - THIS IS A DRILL"; - baseClasses += "status-banner-blue"; - } else if (alert.status === "Test") { - statusText = "TEST - NOT A REAL EMERGENCY"; - baseClasses += "status-banner-gray"; - } else if (alert.status === "System") { - statusText = "SYSTEM TEST"; - baseClasses += "status-banner-gray"; - } else if (alert.status === "Draft") { - statusText = "DRAFT - NOT ACTIVE"; - baseClasses += "status-banner-orange"; - } else if (alert.status === "Actual") { - statusText = "ACTUAL EMERGENCY"; - baseClasses += "status-banner-red status-actual-pulse"; - } - statusBanner.className = baseClasses; - statusBanner.textContent = statusText; - card.appendChild(statusBanner); - } - const headerRow = document.createElement("div"); - headerRow.className = "flex items-center justify-between gap-[2vw] mx-[5vw] mt-[2vh] mb-[1.5vh]"; - const header = document.createElement("h2"); - header.className = "text-red-600 font-black uppercase leading-none event-title-text"; - header.textContent = info.event || alert.identifier; - headerRow.appendChild(header); - const meta = document.createElement("div"); - meta.className = "severity-badge inline-block text-white rounded-xl font-black uppercase tracking-wider flex-shrink-0 severity-badge-text px-[4vw] py-[2vh]"; - meta.textContent = `${info.urgency || ""} ${info.severity || ""} ${info.certainty || ""}`.trim(); - headerRow.appendChild(meta); - card.appendChild(headerRow); - if (info.headline) { - const headline = document.createElement("h3"); - headline.className = "font-extrabold leading-tight flex-shrink-0 headline-text text-gray-900 "; - headline.className += "mx-[5vw] mb-[1.5vh]"; - headline.textContent = info.headline; - card.appendChild(headline); - } - if (info.description) { - const desc = document.createElement("p"); - desc.className = "font-semibold leading-snug body-text text-gray-800 "; - desc.className += "mx-[5vw] mb-[2vh]"; - desc.textContent = info.description; - card.appendChild(desc); - } - if (info.instruction) { - let instr = info.instruction; - if (nearestExit) { - if (instr.includes("{{closest_exit}}") || instr.includes("[[closest_exit]]")) { - instr = instr.replace(/\{\{closest_exit\}\}/g, nearestExit).replace(/\[\[closest_exit\]\]/g, nearestExit); - } else { - instr += ` - -Nearest exit: ${nearestExit}`; - } - } - const instructionBox = document.createElement("div"); - instructionBox.className = "instruction-box rounded-xl flex-shrink-0 px-[4vw] py-[2.5vh] mx-[5vw] mb-[2vh]"; - const sentences = splitIntoSentences(instr); - if (sentences.length > 2) { - const ul = document.createElement("ul"); - ul.className = "instruction-text leading-snug text-gray-900"; - sentences.forEach((sentence) => { - const li = document.createElement("li"); - li.className = "mb-[1vh] flex items-start"; - const bullet = document.createElement("span"); - bullet.className = "mr-[2vw] flex-shrink-0 font-black text-orange-600"; - bullet.textContent = "•"; - const content = document.createElement("span"); - content.className = "flex-1 font-extrabold"; - content.innerHTML = highlightKeywords(sentence); - li.appendChild(bullet); - li.appendChild(content); - ul.appendChild(li); - }); - instructionBox.appendChild(ul); - } else { - const instP = document.createElement("p"); - instP.className = "instruction-text font-extrabold leading-snug whitespace-pre-line text-gray-900"; - instP.innerHTML = highlightKeywords(instr); - instructionBox.appendChild(instP); - } - card.appendChild(instructionBox); - } - info.resources.forEach((res) => { - if (res.mimeType && res.mimeType.startsWith("image")) { - const imgWrapper = document.createElement("div"); - imgWrapper.className = "mx-[5vw] my-[2vh]"; - const img = document.createElement("img"); - img.className = "max-w-full max-h-[20vh] rounded-2xl object-contain shadow-lg"; - img.src = proxyUrl(res.url); - imgWrapper.appendChild(img); - card.appendChild(imgWrapper); - } else if (res.mimeType && res.mimeType.startsWith("audio") && playAudio) { - const proxiedUrl = proxyUrl(res.url); - console.log("Creating audio player for:", res.url, "-> proxied:", proxiedUrl, "MIME type:", res.mimeType); - const audio = document.createElement("audio"); - audio.className = "w-[90vw] flex-shrink-0 mx-[5vw] my-[2vh] rounded-xl"; - audio.style.height = "clamp(3rem, 5vh, 10rem)"; - audio.src = proxiedUrl; - audio.controls = true; - audio.autoplay = true; - audio.preload = "auto"; - audio.crossOrigin = "anonymous"; - card.appendChild(audio); - audio.addEventListener("loadeddata", () => { - console.log("Audio loaded successfully:", proxiedUrl); - }); - audio.addEventListener("error", (e) => { - console.error("Audio load error:", proxiedUrl, e); - }); - audio.play().catch((err) => { - console.warn("Autoplay blocked or failed:", err.message, "- Click play button to start audio"); - }); - } - }); - container.appendChild(card); - }); - } - async function startApp() { - setupTheme(); - let settings2 = {}; - let metadata2 = {}; - try { - settings2 = getSettings(); - localStorage.setItem("screenly_settings", JSON.stringify(settings2)); - } catch (_) { - const cached = localStorage.getItem("screenly_settings"); - settings2 = cached ? JSON.parse(cached) : {}; - } - try { - metadata2 = getMetadata(); - localStorage.setItem("screenly_metadata", JSON.stringify(metadata2)); - } catch (_) { - const cachedMeta = localStorage.getItem("screenly_metadata"); - metadata2 = cachedMeta ? JSON.parse(cachedMeta) : {}; - } - const feedUrl = settings2.cap_feed_url || ""; - const interval = parseInt(settings2.refresh_interval || "5", 10); - const lang = settings2.language || "en"; - const maxAlerts = parseInt(settings2.max_alerts || "3", 10); - const playAudio = (settings2.audio_alert || "false") === "true"; - const offlineMode = (settings2.offline_mode || "false") === "true"; - const testMode = (settings2.test_mode || "false") === "true"; - const demoMode = (settings2.demo_mode || "false") === "true"; - console.log("CAP Settings:", { - playAudio, - audio_alert: settings2.audio_alert, - demoMode, - testMode, - maxAlerts - }); - const tags = metadata2.tags || []; - const nearestExit = getNearestExit(tags); - async function update() { - let xml; - if (testMode) { - try { - const resp = await fetch("static/test.cap"); - xml = resp.ok ? await resp.text() : null; - } catch (_) { - xml = null; - } - } else if (demoMode && !feedUrl) { - const demoFiles = [ - "static/demo-1-tornado.cap", - "static/demo-2-fire.cap", - "static/demo-3-flood.cap", - "static/demo-4-earthquake.cap", - "static/demo-5-hazmat.cap", - "static/demo-6-shooter.cap" - ]; - const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)]; - try { - const resp = await fetch(randomFile); - xml = resp.ok ? await resp.text() : null; - } catch (_) { - xml = null; - } - } else { - xml = await fetchCapData(feedUrl, offlineMode); - } - if (xml) { - const alerts = parseCap(xml); - renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio); - } else { - console.warn("No CAP data available"); - } - } - await update(); - signalReady(); - setInterval(update, interval * 60 * 1000); - } - if (typeof window !== "undefined" && typeof document !== "undefined") { - window.onload = function() { - startApp(); - }; - } -})(); diff --git a/edge-apps/cap-alerting/static/style.css b/edge-apps/cap-alerting/static/style.css deleted file mode 100644 index 2ddf63983..000000000 --- a/edge-apps/cap-alerting/static/style.css +++ /dev/null @@ -1,2 +0,0 @@ -/*! tailwindcss v4.1.17 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-600:oklch(57.7% .245 27.325);--color-red-800:oklch(44.4% .177 26.899);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-600:oklch(68.1% .162 75.834);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-800:oklch(42.4% .199 265.638);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--font-weight-semibold:600;--font-weight-extrabold:800;--font-weight-black:900;--tracking-wider:.05em;--leading-tight:1.25;--leading-snug:1.375;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.relative{position:relative}.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing)*0)}.mx-\[5vw\]{margin-inline:5vw}.my-\[2vh\]{margin-block:2vh}.my-\[3vh\]{margin-block:3vh}.mt-\[2vh\]{margin-top:2vh}.mt-\[4vh\]{margin-top:4vh}.mr-\[2vw\]{margin-right:2vw}.mb-\[1\.5vh\]{margin-bottom:1.5vh}.mb-\[1vh\]{margin-bottom:1vh}.mb-\[2vh\]{margin-bottom:2vh}.mb-\[3vh\]{margin-bottom:3vh}.flex{display:flex}.inline-block{display:inline-block}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[20vh\]{max-height:20vh}.w-\[90vw\]{width:90vw}.w-full{width:100%}.max-w-full{max-width:100%}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.flex-col{flex-direction:column}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-\[2vw\]{gap:2vw}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border-b-\[1vh\]{border-bottom-style:var(--tw-border-style);border-bottom-width:1vh}.border-l-\[1vw\]{border-left-style:var(--tw-border-style);border-left-width:1vw}.border-blue-800{border-color:var(--color-blue-800)}.border-gray-900{border-color:var(--color-gray-900)}.border-orange-600{border-color:var(--color-orange-600)}.border-red-800{border-color:var(--color-red-800)}.border-yellow-600{border-color:var(--color-yellow-600)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-600{background-color:var(--color-orange-600)}.bg-red-600{background-color:var(--color-red-600)}.bg-white{background-color:var(--color-white)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-400{background-color:var(--color-yellow-400)}.object-contain{object-fit:contain}.p-0{padding:calc(var(--spacing)*0)}.px-\[4vw\]{padding-inline:4vw}.py-\[2\.5vh\]{padding-block:2.5vh}.py-\[2vh\]{padding-block:2vh}.py-\[3vh\]{padding-block:3vh}.text-center{text-align:center}.leading-none{--tw-leading:1;line-height:1}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-pre-line{white-space:pre-line}.text-black{color:var(--color-black)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-orange-600{color:var(--color-orange-600)}.text-red-600{color:var(--color-red-600)}.text-red-800{color:var(--color-red-800)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}@keyframes pulse-banner{0%,to{opacity:1;transform:scale(1)}50%{opacity:.95;transform:scale(1.005)}}.status-actual-pulse{animation:1.5s ease-in-out infinite pulse-banner}@media (prefers-reduced-motion:reduce){.status-actual-pulse{animation:none}}.status-stripe-pattern{background-image:repeating-linear-gradient(135deg,#0000,#0000 2rem,#ffffff14 2rem 4rem)}.status-banner-text{text-shadow:0 2px 4px #0003;font-size:clamp(1.25rem,3.5vmin,6rem);line-height:1.1}.event-title-text{text-shadow:0 2px 8px #dc262626;letter-spacing:-.02em;font-size:clamp(2rem,5vmin,10rem);line-height:1}.severity-badge-text{text-shadow:0 1px 2px #00000026;font-size:clamp(1rem,2vmin,4rem);line-height:1.1}.headline-text{letter-spacing:-.01em;font-size:clamp(1.5rem,3.5vmin,7rem);line-height:1.2}.body-text{font-size:clamp(1.125rem,2.5vmin,5rem);line-height:1.35}.instruction-text{font-size:clamp(1.25rem,3vmin,6rem);line-height:1.4}.instruction-text strong{color:#991b1b;font-weight:900}.alert-card{box-shadow:0 8px 32px #0000001f,0 2px 8px #00000014}.instruction-box{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background:linear-gradient(135deg,#fef9c3 0%,#fef3c7 100%);box-shadow:inset 4px 0 #eab308,0 4px 12px #0000000d}.severity-badge{background:linear-gradient(135deg,#f97316 0%,#ea580c 100%);box-shadow:0 4px 12px #f9731640}.status-banner-blue{background:linear-gradient(135deg,#2563eb 0%,#1d4ed8 100%)}.status-banner-red{background:linear-gradient(135deg,#dc2626 0%,#b91c1c 100%)}.status-banner-orange{background:linear-gradient(135deg,#f97316 0%,#ea580c 100%)}.status-banner-gray{background:linear-gradient(135deg,#4b5563 0%,#374151 100%)}@media (min-width:1920px) and (min-height:1080px){.instruction-text{font-size:clamp(2rem,3.5vmin,7rem)}.event-title-text{font-size:clamp(3rem,6vmin,12rem)}}@media (min-width:3840px) and (min-height:2160px){.instruction-text{font-size:clamp(2.5rem,3.5vmin,7rem)}.event-title-text{font-size:clamp(4rem,6vmin,12rem)}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000} \ No newline at end of file From 3f0057f4a821ec0caed4cb2bd280a8a42ef68dbc Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 10:52:00 -0800 Subject: [PATCH 06/26] chore(cap-alerting): change formatting rules --- edge-apps/cap-alerting/index.html | 28 ++--- edge-apps/cap-alerting/package.json | 1 + edge-apps/cap-alerting/src/fetcher.test.ts | 130 +++++++++++++-------- edge-apps/cap-alerting/src/fetcher.ts | 19 +-- edge-apps/cap-alerting/src/input.css | 47 ++++++-- edge-apps/cap-alerting/src/main.ts | 94 +++++++++++---- 6 files changed, 215 insertions(+), 104 deletions(-) diff --git a/edge-apps/cap-alerting/index.html b/edge-apps/cap-alerting/index.html index 163c82387..3ea1df1d4 100644 --- a/edge-apps/cap-alerting/index.html +++ b/edge-apps/cap-alerting/index.html @@ -1,16 +1,16 @@ - + - - - CAP Alerting - Screenly Edge App - - - - -
-
-
- - - + + + CAP Alerting - Screenly Edge App + + + + +
+
+
+ + + diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index f6a9dbf02..1e7092408 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -21,6 +21,7 @@ "fast-xml-parser": "^5.3.2", "offline-geocode-city": "^1.0.2" }, + "prettier": "../edge-apps-library/.prettierrc.json", "devDependencies": { "@eslint/js": "^9.39.1", "@screenly/edge-apps": "workspace:../edge-apps-library", diff --git a/edge-apps/cap-alerting/src/fetcher.test.ts b/edge-apps/cap-alerting/src/fetcher.test.ts index a314eb8ff..a1afb9e7e 100644 --- a/edge-apps/cap-alerting/src/fetcher.test.ts +++ b/edge-apps/cap-alerting/src/fetcher.test.ts @@ -33,7 +33,7 @@ const waitFor = async (condition: () => boolean, timeout = 5000) => { if (Date.now() - start > timeout) { throw new Error('Timeout waiting for condition') } - await new Promise(resolve => setTimeout(resolve, 50)) + await new Promise((resolve) => setTimeout(resolve, 50)) } } @@ -42,11 +42,16 @@ describe('CAPFetcher', () => { // Setup mocks global.localStorage = localStorageMock as any global.fetch = mockFetch as any - global.window = { setInterval, clearInterval, setTimeout, clearTimeout } as any - + global.window = { + setInterval, + clearInterval, + setTimeout, + clearTimeout, + } as any + // Clear localStorage before each test localStorageMock.clear() - + // Reset fetch mock mockFetch.mockReset() }) @@ -74,7 +79,8 @@ describe('CAPFetcher', () => { describe('Caching', () => { it('should save valid data to cache', async () => { - const mockData = 'TEST' + const mockData = + 'TEST' mockFetch.mockResolvedValueOnce({ ok: true, text: async () => mockData, @@ -96,7 +102,9 @@ describe('CAPFetcher', () => { expect(cached).toBe(mockData) // Check that metadata was saved - const meta = JSON.parse(localStorageMock.getItem('cap_feed_cache_meta') || '{}') + const meta = JSON.parse( + localStorageMock.getItem('cap_feed_cache_meta') || '{}', + ) expect(meta.isValid).toBe(true) expect(meta.timestamp).toBeDefined() @@ -150,11 +158,12 @@ describe('CAPFetcher', () => { }) it('should use atomic write for cache', async () => { - const mockData = 'TEST' - + const mockData = + 'TEST' + // Set up existing cache localStorageMock.setItem('cap_feed_cache', 'old data') - + mockFetch.mockResolvedValueOnce({ ok: true, text: async () => mockData, @@ -182,15 +191,19 @@ describe('CAPFetcher', () => { }) it('should load from cache if initial fetch fails', async () => { - const cachedData = 'CACHED' - + const cachedData = + 'CACHED' + // Set up cache localStorageMock.setItem('cap_feed_cache', cachedData) - localStorageMock.setItem('cap_feed_cache_meta', JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: true, - })) + localStorageMock.setItem( + 'cap_feed_cache_meta', + JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: true, + }), + ) mockFetch.mockRejectedValue(new Error('Network error')) @@ -215,8 +228,9 @@ describe('CAPFetcher', () => { describe('Retry Logic', () => { it('should retry on fetch failure', async () => { - const mockData = 'TEST' - + const mockData = + 'TEST' + // Fail first two attempts, succeed on third mockFetch .mockRejectedValueOnce(new Error('Network error')) @@ -245,15 +259,19 @@ describe('CAPFetcher', () => { }) it('should fall back to cache after all retries exhausted', async () => { - const cachedData = 'CACHED' - + const cachedData = + 'CACHED' + // Set up cache localStorageMock.setItem('cap_feed_cache', cachedData) - localStorageMock.setItem('cap_feed_cache_meta', JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: true, - })) + localStorageMock.setItem( + 'cap_feed_cache_meta', + JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: true, + }), + ) mockFetch.mockRejectedValue(new Error('Network error')) @@ -286,13 +304,13 @@ describe('CAPFetcher', () => { const updateCallback = mock() const startTime = Date.now() - + fetcher.start(updateCallback) await waitFor(() => mockFetch.mock.calls.length >= 3, 3000) const elapsed = Date.now() - startTime - + // Should take at least 100ms (1st retry) + 200ms (2nd retry) = 300ms // With jitter, it could be slightly less, so check for at least 200ms expect(elapsed).toBeGreaterThan(200) @@ -342,7 +360,9 @@ describe('CAPFetcher', () => { await waitFor(() => mockFetch.mock.calls.length > 0) - expect(mockFetch.mock.calls[0][0]).toBe('https://proxy.com/https://example.com/feed.xml') + expect(mockFetch.mock.calls[0][0]).toBe( + 'https://proxy.com/https://example.com/feed.xml', + ) fetcher.stop() }) @@ -374,7 +394,8 @@ describe('CAPFetcher', () => { describe('Stats', () => { it('should track statistics', async () => { - const mockData = 'TEST' + const mockData = + 'TEST' mockFetch.mockResolvedValueOnce({ ok: true, text: async () => mockData, @@ -386,7 +407,7 @@ describe('CAPFetcher', () => { }) const updateCallback = mock() - + let initialStats = fetcher.getStats() expect(initialStats.isRunning).toBe(false) expect(initialStats.lastSuccessfulFetch).toBe(0) @@ -428,7 +449,8 @@ describe('CAPFetcher', () => { describe('Start and Stop', () => { it('should not start if already running', async () => { - const mockData = 'TEST' + const mockData = + 'TEST' mockFetch.mockResolvedValue({ ok: true, text: async () => mockData, @@ -440,7 +462,7 @@ describe('CAPFetcher', () => { }) const updateCallback = mock() - + fetcher.start(updateCallback) await waitFor(() => fetcher.getStats().isRunning) @@ -448,7 +470,7 @@ describe('CAPFetcher', () => { const consoleWarn = mock() const originalWarn = console.warn console.warn = consoleWarn - + fetcher.start(updateCallback) expect(consoleWarn.mock.calls.length).toBeGreaterThan(0) @@ -457,7 +479,8 @@ describe('CAPFetcher', () => { }) it('should stop properly', async () => { - const mockData = 'TEST' + const mockData = + 'TEST' mockFetch.mockResolvedValue({ ok: true, text: async () => mockData, @@ -481,7 +504,8 @@ describe('CAPFetcher', () => { describe('Force Refresh', () => { it('should force an immediate refresh', async () => { - const mockData = 'TEST' + const mockData = + 'TEST' mockFetch.mockResolvedValue({ ok: true, text: async () => mockData, @@ -493,7 +517,7 @@ describe('CAPFetcher', () => { }) const updateCallback = mock() - + await fetcher.forceRefresh(updateCallback) expect(mockFetch.mock.calls.length).toBe(1) @@ -526,7 +550,8 @@ describe('CAPFetcher', () => { throw new Error('QuotaExceededError') }) - const mockData = 'TEST' + const mockData = + 'TEST' mockFetch.mockResolvedValueOnce({ ok: true, text: async () => mockData, @@ -553,10 +578,14 @@ describe('CAPFetcher', () => { it('should handle corrupted cache metadata', async () => { // Set up corrupted metadata - localStorageMock.setItem('cap_feed_cache', '') + localStorageMock.setItem( + 'cap_feed_cache', + '', + ) localStorageMock.setItem('cap_feed_cache_meta', 'invalid json') - const mockData = 'NEW' + const mockData = + 'NEW' mockFetch.mockResolvedValueOnce({ ok: true, text: async () => mockData, @@ -580,14 +609,21 @@ describe('CAPFetcher', () => { it('should return null when cache has invalid flag', async () => { // Set up cache with invalid flag - localStorageMock.setItem('cap_feed_cache', '') - localStorageMock.setItem('cap_feed_cache_meta', JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: false, - })) - - const mockData = 'NEW' + localStorageMock.setItem( + 'cap_feed_cache', + '', + ) + localStorageMock.setItem( + 'cap_feed_cache_meta', + JSON.stringify({ + data: '', + timestamp: Date.now(), + isValid: false, + }), + ) + + const mockData = + 'NEW' mockFetch.mockResolvedValueOnce({ ok: true, text: async () => mockData, diff --git a/edge-apps/cap-alerting/src/fetcher.ts b/edge-apps/cap-alerting/src/fetcher.ts index 1349cf04c..fe2d63fc5 100644 --- a/edge-apps/cap-alerting/src/fetcher.ts +++ b/edge-apps/cap-alerting/src/fetcher.ts @@ -202,7 +202,7 @@ export class CAPFetcher { for (let attempt = 0; attempt < this.config.maxRetries; attempt++) { try { const data = await this.fetchData() - + if (data) { // Successfully fetched - save to cache const saved = this.saveToCacheWithValidation(data) @@ -228,7 +228,7 @@ export class CAPFetcher { // All retries exhausted console.error(`Failed to fetch after ${this.config.maxRetries} attempts`) - + // Return cached data as fallback const cached = this.getCachedData() if (cached) { @@ -245,9 +245,9 @@ export class CAPFetcher { private calculateBackoffDelay(attempt: number): number { const exponentialDelay = Math.min( this.config.initialRetryDelay * Math.pow(2, attempt), - this.config.maxRetryDelay + this.config.maxRetryDelay, ) - + // Add jitter (±25% randomness) to prevent thundering herd const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1) return Math.floor(exponentialDelay + jitter) @@ -283,7 +283,7 @@ export class CAPFetcher { signal: controller.signal, cache: 'no-cache', headers: { - 'Accept': 'application/xml, text/xml, */*', + Accept: 'application/xml, text/xml, */*', }, }) @@ -302,14 +302,14 @@ export class CAPFetcher { return text } catch (err) { clearTimeout(timeoutId) - + if (err instanceof Error) { if (err.name === 'AbortError') { throw new Error('Fetch timeout after 15 seconds') } throw err } - + throw new Error('Unknown fetch error') } } @@ -338,11 +338,12 @@ export class CAPFetcher { /** * Force a refresh (useful for debugging) */ - public async forceRefresh(onUpdate: (data: string | null) => void): Promise { + public async forceRefresh( + onUpdate: (data: string | null) => void, + ): Promise { const data = await this.fetchWithRetry() if (data) { onUpdate(data) } } } - diff --git a/edge-apps/cap-alerting/src/input.css b/edge-apps/cap-alerting/src/input.css index 08cf91422..1458625cc 100644 --- a/edge-apps/cap-alerting/src/input.css +++ b/edge-apps/cap-alerting/src/input.css @@ -1,10 +1,11 @@ -@import "tailwindcss"; +@import 'tailwindcss'; /* Modern Digital Signage Design - Viewport-based sizing */ /* Custom animations */ @keyframes pulse-banner { - 0%, 100% { + 0%, + 100% { opacity: 1; transform: scale(1); } @@ -80,33 +81,57 @@ /* Modern card styling */ .alert-card { - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08); + box-shadow: + 0 8px 32px rgba(0, 0, 0, 0.12), + 0 2px 8px rgba(0, 0, 0, 0.08); } /* Modern instruction box */ .instruction-box { - box-shadow: inset 4px 0 0 0 rgb(234, 179, 8), 0 4px 12px rgba(0, 0, 0, 0.05); + box-shadow: + inset 4px 0 0 0 rgb(234, 179, 8), + 0 4px 12px rgba(0, 0, 0, 0.05); backdrop-filter: blur(10px); - background: linear-gradient(135deg, rgb(254, 249, 195) 0%, rgb(254, 243, 199) 100%); + background: linear-gradient( + 135deg, + rgb(254, 249, 195) 0%, + rgb(254, 243, 199) 100% + ); } /* Severity badge modern styling */ .severity-badge { box-shadow: 0 4px 12px rgba(249, 115, 22, 0.25); - background: linear-gradient(135deg, rgb(249, 115, 22) 0%, rgb(234, 88, 12) 100%); + background: linear-gradient( + 135deg, + rgb(249, 115, 22) 0%, + rgb(234, 88, 12) 100% + ); } /* Status banner gradients */ .status-banner-blue { - background: linear-gradient(135deg, rgb(37, 99, 235) 0%, rgb(29, 78, 216) 100%); + background: linear-gradient( + 135deg, + rgb(37, 99, 235) 0%, + rgb(29, 78, 216) 100% + ); } .status-banner-red { - background: linear-gradient(135deg, rgb(220, 38, 38) 0%, rgb(185, 28, 28) 100%); + background: linear-gradient( + 135deg, + rgb(220, 38, 38) 0%, + rgb(185, 28, 28) 100% + ); } .status-banner-orange { - background: linear-gradient(135deg, rgb(249, 115, 22) 0%, rgb(234, 88, 12) 100%); + background: linear-gradient( + 135deg, + rgb(249, 115, 22) 0%, + rgb(234, 88, 12) 100% + ); } .status-banner-gray { @@ -118,7 +143,7 @@ .instruction-text { font-size: clamp(2rem, 3.5vmin, 7rem); } - + .event-title-text { font-size: clamp(3rem, 6vmin, 12rem); } @@ -128,7 +153,7 @@ .instruction-text { font-size: clamp(2.5rem, 3.5vmin, 7rem); } - + .event-title-text { font-size: clamp(4rem, 6vmin, 12rem); } diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index adb123c5a..5cd1402a2 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -70,9 +70,16 @@ interface CAPAlert { } export function parseCap(xml: string): CAPAlert[] { - const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) + const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: '@_', + }) const json: any = parser.parse(xml) - const alertsJson = json.alert ? (Array.isArray(json.alert) ? json.alert : [json.alert]) : [] + const alertsJson = json.alert + ? Array.isArray(json.alert) + ? json.alert + : [json.alert] + : [] const alerts: CAPAlert[] = [] @@ -85,7 +92,11 @@ export function parseCap(xml: string): CAPAlert[] { ? info.resource : [info.resource] : [] - const areasJson = info.area ? (Array.isArray(info.area) ? info.area : [info.area]) : [] + const areasJson = info.area + ? Array.isArray(info.area) + ? info.area + : [info.area] + : [] return { language: info.language || '', @@ -163,7 +174,10 @@ export function getNearestExit(tags: string[]): string | undefined { return undefined } -async function fetchCapData(feedUrl: string, offlineMode: boolean): Promise { +async function fetchCapData( + feedUrl: string, + offlineMode: boolean, +): Promise { if (offlineMode) { return localStorage.getItem('cap_last') } @@ -212,7 +226,10 @@ function highlightKeywords(text: string): string { let result = text keywords.forEach((keyword) => { const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') - result = result.replace(regex, '$1') + result = result.replace( + regex, + '$1', + ) }) return result @@ -238,7 +255,7 @@ function renderAlerts( nearestExit: string | undefined, lang: string, maxAlerts: number, - playAudio: boolean + playAudio: boolean, ): void { const container = document.getElementById('alerts') if (!container) return @@ -251,7 +268,8 @@ function renderAlerts( if (!info) return const card = document.createElement('div') - card.className = 'alert-card w-full h-full bg-white flex flex-col overflow-y-auto' + card.className = + 'alert-card w-full h-full bg-white flex flex-col overflow-y-auto' if (alert.status) { const statusBanner = document.createElement('div') @@ -283,24 +301,28 @@ function renderAlerts( } const headerRow = document.createElement('div') - headerRow.className = 'flex items-center justify-between gap-[2vw] mx-[5vw] mt-[2vh] mb-[1.5vh]' + headerRow.className = + 'flex items-center justify-between gap-[2vw] mx-[5vw] mt-[2vh] mb-[1.5vh]' const header = document.createElement('h2') - header.className = 'text-red-600 font-black uppercase leading-none event-title-text' + header.className = + 'text-red-600 font-black uppercase leading-none event-title-text' header.textContent = info.event || alert.identifier headerRow.appendChild(header) const meta = document.createElement('div') meta.className = 'severity-badge inline-block text-white rounded-xl font-black uppercase tracking-wider flex-shrink-0 severity-badge-text px-[4vw] py-[2vh]' - meta.textContent = `${info.urgency || ''} ${info.severity || ''} ${info.certainty || ''}`.trim() + meta.textContent = + `${info.urgency || ''} ${info.severity || ''} ${info.certainty || ''}`.trim() headerRow.appendChild(meta) card.appendChild(headerRow) if (info.headline) { const headline = document.createElement('h3') - headline.className = 'font-extrabold leading-tight flex-shrink-0 headline-text text-gray-900 ' + headline.className = + 'font-extrabold leading-tight flex-shrink-0 headline-text text-gray-900 ' headline.className += 'mx-[5vw] mb-[1.5vh]' headline.textContent = info.headline card.appendChild(headline) @@ -317,8 +339,13 @@ function renderAlerts( if (info.instruction) { let instr = info.instruction if (nearestExit) { - if (instr.includes('{{closest_exit}}') || instr.includes('[[closest_exit]]')) { - instr = instr.replace(/\{\{closest_exit\}\}/g, nearestExit).replace(/\[\[closest_exit\]\]/g, nearestExit) + if ( + instr.includes('{{closest_exit}}') || + instr.includes('[[closest_exit]]') + ) { + instr = instr + .replace(/\{\{closest_exit\}\}/g, nearestExit) + .replace(/\[\[closest_exit\]\]/g, nearestExit) } else { instr += `\n\nNearest exit: ${nearestExit}` } @@ -349,7 +376,8 @@ function renderAlerts( instructionBox.appendChild(ul) } else { const instP = document.createElement('p') - instP.className = 'instruction-text font-extrabold leading-snug whitespace-pre-line text-gray-900' + instP.className = + 'instruction-text font-extrabold leading-snug whitespace-pre-line text-gray-900' instP.innerHTML = highlightKeywords(instr) instructionBox.appendChild(instP) } @@ -362,13 +390,25 @@ function renderAlerts( const imgWrapper = document.createElement('div') imgWrapper.className = 'mx-[5vw] my-[2vh]' const img = document.createElement('img') - img.className = 'max-w-full max-h-[20vh] rounded-2xl object-contain shadow-lg' + img.className = + 'max-w-full max-h-[20vh] rounded-2xl object-contain shadow-lg' img.src = proxyUrl(res.url) imgWrapper.appendChild(img) card.appendChild(imgWrapper) - } else if (res.mimeType && res.mimeType.startsWith('audio') && playAudio) { + } else if ( + res.mimeType && + res.mimeType.startsWith('audio') && + playAudio + ) { const proxiedUrl = proxyUrl(res.url) - console.log('Creating audio player for:', res.url, '-> proxied:', proxiedUrl, 'MIME type:', res.mimeType) + console.log( + 'Creating audio player for:', + res.url, + '-> proxied:', + proxiedUrl, + 'MIME type:', + res.mimeType, + ) const audio = document.createElement('audio') audio.className = 'w-[90vw] flex-shrink-0 mx-[5vw] my-[2vh] rounded-xl' audio.style.height = 'clamp(3rem, 5vh, 10rem)' @@ -378,17 +418,21 @@ function renderAlerts( audio.preload = 'auto' audio.crossOrigin = 'anonymous' card.appendChild(audio) - + audio.addEventListener('loadeddata', () => { console.log('Audio loaded successfully:', proxiedUrl) }) - + audio.addEventListener('error', (e) => { console.error('Audio load error:', proxiedUrl, e) }) - + audio.play().catch((err) => { - console.warn('Autoplay blocked or failed:', err.message, '- Click play button to start audio') + console.warn( + 'Autoplay blocked or failed:', + err.message, + '- Click play button to start audio', + ) }) } }) @@ -408,7 +452,9 @@ export async function startApp(): Promise { localStorage.setItem('screenly_settings', JSON.stringify(settings)) } catch (_) { const cached = localStorage.getItem('screenly_settings') - settings = cached ? (JSON.parse(cached) as Partial>) : {} + settings = cached + ? (JSON.parse(cached) as Partial>) + : {} } try { @@ -416,7 +462,9 @@ export async function startApp(): Promise { localStorage.setItem('screenly_metadata', JSON.stringify(metadata)) } catch (_) { const cachedMeta = localStorage.getItem('screenly_metadata') - metadata = cachedMeta ? (JSON.parse(cachedMeta) as Partial>) : {} + metadata = cachedMeta + ? (JSON.parse(cachedMeta) as Partial>) + : {} } const feedUrl: string = (settings.cap_feed_url as string) || '' From b7335197762a7a3de13d9e748abaa4b10e5f5957 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 10:54:29 -0800 Subject: [PATCH 07/26] chore(cap-alerting): add a `package.json` script for only checking formatting rules --- edge-apps/cap-alerting/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 1e7092408..8ff6539ed 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -12,7 +12,9 @@ "test:watch": "bun test --watch", "type-check": "tsc --noEmit", "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.ts index.test.ts src/fetcher.ts src/fetcher.test.ts src/main.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", - "format": "prettier --write src/ README.md index.html", + "format:common": "prettier src/ README.md index.html", + "format": "bun run format:common --write", + "format:check": "bun run format:common --check", "deploy": "bun run build && screenly edge-app deploy" }, "dependencies": { From 28f31e4764d39e39646916263f8be7a5c6995ec4 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 11:01:05 -0800 Subject: [PATCH 08/26] chore(cap-alerting): fix failures in CI checks - Include `cap-alerting/` in `.gitignore` - Have the unit tests be run via `bun run test:unit` instead --- .prettierignore | 1 + edge-apps/cap-alerting/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 788b8769f..0d26a3ab5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ **/*.min.js +edge-apps/cap-alerting/ edge-apps/edge-apps-library/ edge-apps/powerbi-legacy/ edge-apps/powerbi-legacy/** diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 8ff6539ed..85b3d7157 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -8,7 +8,7 @@ "build": "bun run build:css && bun run build:js", "build:css": "bunx @tailwindcss/cli -i ./src/input.css -o ./static/style.css --minify", "build:js": "bun build src/main.ts --outdir static/js --target browser --format iife", - "test": "bun test", + "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.ts index.test.ts src/fetcher.ts src/fetcher.test.ts src/main.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", From e8893c1fdbd0e473dc61214b0694d60d523a5a93 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 12:25:26 -0800 Subject: [PATCH 09/26] refactor: add watch mode for build scripts in cap-alerting - Extract common build flags into shared scripts to reduce duplication - Add build:css:common and build:js:common scripts - Add watch mode for CSS and JS builds - Add npm-run-all2 dependency for parallel task execution --- edge-apps/cap-alerting/bun.lock | 39 +++++++++++++++++++++++------ edge-apps/cap-alerting/package.json | 16 +++++++++--- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/edge-apps/cap-alerting/bun.lock b/edge-apps/cap-alerting/bun.lock index 495c6b57d..0789acb0d 100644 --- a/edge-apps/cap-alerting/bun.lock +++ b/edge-apps/cap-alerting/bun.lock @@ -20,6 +20,7 @@ "bun-types": "^1.3.3", "eslint": "^9.39.1", "jiti": "^2.6.1", + "npm-run-all2": "^8.0.4", "prettier": "^3.7.4", "tailwindcss": "^4.1.17", "typescript": "^5.9.3", @@ -259,7 +260,7 @@ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], @@ -427,7 +428,7 @@ "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], @@ -437,6 +438,8 @@ "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + "json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "", {}, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="], + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], @@ -487,6 +490,8 @@ "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], + "memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], @@ -509,6 +514,10 @@ "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], + "npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="], + + "npm-run-all2": ["npm-run-all2@8.0.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "picomatch": "^4.0.2", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA=="], + "offline-geocode-city": ["offline-geocode-city@1.0.2", "", { "dependencies": { "@jsheaven/easybuild": "^1.2.9", "chokidar": "^3.5.3", "csv-parse": "^5.3.10", "lz-ts": "^1.1.2", "s2-geometry": "^1.2.10" } }, "sha512-6q9XvgYpvOr7kLzi/K2P1GZ36FajNHEI4cFphNcZ6tPxR0kBROzy6CorTn+yU7en3wrDkDTfcn1sPCAKA569xA=="], "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], @@ -527,7 +536,9 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], @@ -543,6 +554,8 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="], + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], @@ -569,6 +582,8 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -623,7 +638,7 @@ "whatwg-url": ["whatwg-url@15.1.0", "", { "dependencies": { "tr46": "^6.0.0", "webidl-conversions": "^8.0.0" } }, "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g=="], - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], @@ -665,11 +680,15 @@ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "gzip-size/duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], @@ -677,8 +696,14 @@ "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], } } diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 85b3d7157..b3cca02db 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -4,10 +4,17 @@ "private": true, "type": "module", "scripts": { - "dev": "bun run build && screenly edge-app run", - "build": "bun run build:css && bun run build:js", - "build:css": "bunx @tailwindcss/cli -i ./src/input.css -o ./static/style.css --minify", - "build:js": "bun build src/main.ts --outdir static/js --target browser --format iife", + "generate-mock-data": "screenly edge-app run --generate-mock-data", + "predev": "bun run generate-mock-data", + "dev": "run-p build:css:dev build:js:dev edge-app-server", + "edge-app-server": "screenly edge-app run", + "build": "run-p build:css:prod build:js:prod", + "build:css:common": "bunx @tailwindcss/cli -i ./src/input.css -o ./static/style.css", + "build:css:prod": "bun run build:css:common -- --minify", + "build:css:dev": "bun run build:css:common -- --watch", + "build:js:common": "bun build src/main.ts --outdir static/js --target browser --format iife", + "build:js:prod": "bun run build:js:common", + "build:js:dev": "bun run build:js:common -- --watch", "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", @@ -34,6 +41,7 @@ "bun-types": "^1.3.3", "eslint": "^9.39.1", "jiti": "^2.6.1", + "npm-run-all2": "^8.0.4", "prettier": "^3.7.4", "tailwindcss": "^4.1.17", "typescript": "^5.9.3", From 3c80359fa30084fa222565b5a25eb29c77d33854 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 12:33:35 -0800 Subject: [PATCH 10/26] chore(cap-alerting): reorganize fields in manifest files --- edge-apps/cap-alerting/screenly.yml | 44 +++++++++++++------------- edge-apps/cap-alerting/screenly_qc.yml | 44 +++++++++++++------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml index 68bfaa004..b5851adb4 100644 --- a/edge-apps/cap-alerting/screenly.yml +++ b/edge-apps/cap-alerting/screenly.yml @@ -3,55 +3,55 @@ syntax: manifest_v1 description: Display CAP emergency alerts on Screenly screens. Supports offline mode and uses screen tags (e.g., exit:North Lobby) to highlight the nearest exit. icon: https://playground.srly.io/edge-apps/cap-alerting/static/cap-icon.svg author: Screenly, Inc. -ready_signal: true entrypoint: type: file +ready_signal: true settings: + audio_alert: + type: string + default_value: 'false' + title: Play Audio Alerts + optional: true + help_text: Play audio from CAP resources when available (true/false). cap_feed_url: type: string + default_value: '' title: CAP Feed URL optional: true - default_value: '' help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. - refresh_interval: + demo_mode: type: string - title: Refresh Interval (minutes) + default_value: 'false' + title: Demo Mode optional: true - default_value: '5' - help_text: Time in minutes between feed updates. + help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). language: type: string + default_value: en title: Default Language optional: true - default_value: en help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). max_alerts: type: string + default_value: '3' title: Maximum Alerts optional: true - default_value: '3' help_text: Maximum number of alerts to display simultaneously. - audio_alert: - type: string - title: Play Audio Alerts - optional: true - default_value: 'false' - help_text: Play audio from CAP resources when available (true/false). offline_mode: type: string + default_value: 'false' title: Offline Mode optional: true - default_value: 'false' help_text: When enabled, avoid network fetches and use cached data (true/false). + refresh_interval: + type: string + default_value: '5' + title: Refresh Interval (minutes) + optional: true + help_text: Time in minutes between feed updates. test_mode: type: string + default_value: 'false' title: Test Mode optional: true - default_value: 'false' help_text: Use bundled test CAP file for testing (true/false). - demo_mode: - type: string - title: Demo Mode - optional: true - default_value: 'false' - help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index 68bfaa004..b5851adb4 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -3,55 +3,55 @@ syntax: manifest_v1 description: Display CAP emergency alerts on Screenly screens. Supports offline mode and uses screen tags (e.g., exit:North Lobby) to highlight the nearest exit. icon: https://playground.srly.io/edge-apps/cap-alerting/static/cap-icon.svg author: Screenly, Inc. -ready_signal: true entrypoint: type: file +ready_signal: true settings: + audio_alert: + type: string + default_value: 'false' + title: Play Audio Alerts + optional: true + help_text: Play audio from CAP resources when available (true/false). cap_feed_url: type: string + default_value: '' title: CAP Feed URL optional: true - default_value: '' help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. - refresh_interval: + demo_mode: type: string - title: Refresh Interval (minutes) + default_value: 'false' + title: Demo Mode optional: true - default_value: '5' - help_text: Time in minutes between feed updates. + help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). language: type: string + default_value: en title: Default Language optional: true - default_value: en help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). max_alerts: type: string + default_value: '3' title: Maximum Alerts optional: true - default_value: '3' help_text: Maximum number of alerts to display simultaneously. - audio_alert: - type: string - title: Play Audio Alerts - optional: true - default_value: 'false' - help_text: Play audio from CAP resources when available (true/false). offline_mode: type: string + default_value: 'false' title: Offline Mode optional: true - default_value: 'false' help_text: When enabled, avoid network fetches and use cached data (true/false). + refresh_interval: + type: string + default_value: '5' + title: Refresh Interval (minutes) + optional: true + help_text: Time in minutes between feed updates. test_mode: type: string + default_value: 'false' title: Test Mode optional: true - default_value: 'false' help_text: Use bundled test CAP file for testing (true/false). - demo_mode: - type: string - title: Demo Mode - optional: true - default_value: 'false' - help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). From 09d1a677535e546dd5ce7edc44dddee6756aa505 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 14:57:07 -0800 Subject: [PATCH 11/26] fix: improve Anywhere screen detection - Update isAnywhereScreen to check for empty string or undefined - Use isAnywhereScreen in cap-alerting demo mode logic --- edge-apps/cap-alerting/.ignore | 1 + edge-apps/cap-alerting/src/main.ts | 10 +++++++++- .../src/utils/metadata.test.ts | 20 +++++++++++++++++++ .../edge-apps-library/src/utils/metadata.ts | 10 ++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 edge-apps/cap-alerting/.ignore diff --git a/edge-apps/cap-alerting/.ignore b/edge-apps/cap-alerting/.ignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/edge-apps/cap-alerting/.ignore @@ -0,0 +1 @@ +node_modules/ diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 5cd1402a2..37b6a91a3 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -5,10 +5,14 @@ import { getTags, getSettings, getCorsProxyUrl, + isAnywhereScreen, } from '@screenly/edge-apps' import { XMLParser } from 'fast-xml-parser' +const DEMO_BASE_URL = + 'https://raw.githubusercontent.com/Screenly/Playground/refs/heads/master/edge-apps/cap-alerting' + interface CAPResource { resourceDesc?: string mimeType: string @@ -498,7 +502,7 @@ export async function startApp(): Promise { xml = null } } else if (demoMode && !feedUrl) { - const demoFiles = [ + const localDemoFiles = [ 'static/demo-1-tornado.cap', 'static/demo-2-fire.cap', 'static/demo-3-flood.cap', @@ -506,6 +510,10 @@ export async function startApp(): Promise { 'static/demo-5-hazmat.cap', 'static/demo-6-shooter.cap', ] + const remoteDemoFiles = localDemoFiles.map( + (file) => `${DEMO_BASE_URL}/${file}`, + ) + const demoFiles = isAnywhereScreen() ? remoteDemoFiles : localDemoFiles const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)] try { const resp = await fetch(randomFile) diff --git a/edge-apps/edge-apps-library/src/utils/metadata.test.ts b/edge-apps/edge-apps-library/src/utils/metadata.test.ts index c570d8cab..70761b98e 100644 --- a/edge-apps/edge-apps-library/src/utils/metadata.test.ts +++ b/edge-apps/edge-apps-library/src/utils/metadata.test.ts @@ -9,6 +9,7 @@ import { getScreenlyVersion, getTags, hasTag, + isAnywhereScreen, } from './metadata' import { setupScreenlyMock, resetScreenlyMock } from '../test/mock' @@ -100,4 +101,23 @@ describe('metadata utilities', () => { expect(hasTag('other')).toBe(false) }) }) + + describe('isAnywhereScreen', () => { + test('should return false when hardware is not empty', () => { + expect(isAnywhereScreen()).toBe(false) + }) + + test('should return true when hardware is empty string', () => { + setupScreenlyMock({ + coordinates: [37.3861, -122.0839], + hostname: 'test-host', + location: 'Test Location', + hardware: '', + screenly_version: '1.2.3', + screen_name: 'Main Screen', + tags: [], + }) + expect(isAnywhereScreen()).toBe(true) + }) + }) }) diff --git a/edge-apps/edge-apps-library/src/utils/metadata.ts b/edge-apps/edge-apps-library/src/utils/metadata.ts index 79317f3ad..96030628b 100644 --- a/edge-apps/edge-apps-library/src/utils/metadata.ts +++ b/edge-apps/edge-apps-library/src/utils/metadata.ts @@ -63,3 +63,13 @@ export function getTags(): string[] { export function hasTag(tag: string): boolean { return screenly.metadata.tags.includes(tag) } + +/** + * Check if the device is an Anywhere screen + */ +export function isAnywhereScreen(): boolean { + return ( + screenly.metadata.hardware === '' || + screenly.metadata.hardware === undefined + ) +} From b6ab84b55f0cf48062996baa4deb1c9aa2a5088f Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 15:14:55 -0800 Subject: [PATCH 12/26] chore(cap-alerting): refactor interface declarations into a separate file --- edge-apps/cap-alerting/src/main.ts | 61 +------------------------ edge-apps/cap-alerting/src/types/cap.ts | 59 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 edge-apps/cap-alerting/src/types/cap.ts diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 37b6a91a3..38187c90f 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -8,71 +8,12 @@ import { isAnywhereScreen, } from '@screenly/edge-apps' +import { CAPResource, CAPArea, CAPInfo, CAPAlert } from './types/cap.js' import { XMLParser } from 'fast-xml-parser' const DEMO_BASE_URL = 'https://raw.githubusercontent.com/Screenly/Playground/refs/heads/master/edge-apps/cap-alerting' -interface CAPResource { - resourceDesc?: string - mimeType: string - size?: number - uri?: string - derefUri?: string - digest?: string - url: string -} - -interface CAPArea { - areaDesc: string - polygon?: string | string[] - circle?: string | string[] - geocode?: any - altitude?: number - ceiling?: number -} - -interface CAPInfo { - language: string - category?: string | string[] - event?: string - responseType?: string | string[] - urgency?: string - severity?: string - certainty?: string - audience?: string - effective?: string - onset?: string - expires?: string - senderName?: string - headline?: string - description?: string - instruction?: string - web?: string - contact?: string - parameter?: any - eventCode?: any - resources: CAPResource[] - areas: CAPArea[] -} - -interface CAPAlert { - identifier: string - sender: string - sent: string - status?: string - msgType?: string - source?: string - scope?: string - restriction?: string - addresses?: string - code?: string | string[] - note?: string - references?: string - incidents?: string - infos: CAPInfo[] -} - export function parseCap(xml: string): CAPAlert[] { const parser = new XMLParser({ ignoreAttributes: false, diff --git a/edge-apps/cap-alerting/src/types/cap.ts b/edge-apps/cap-alerting/src/types/cap.ts new file mode 100644 index 000000000..dcc2217e0 --- /dev/null +++ b/edge-apps/cap-alerting/src/types/cap.ts @@ -0,0 +1,59 @@ +export interface CAPResource { + resourceDesc?: string + mimeType: string + size?: number + uri?: string + derefUri?: string + digest?: string + url: string +} + +export interface CAPArea { + areaDesc: string + polygon?: string | string[] + circle?: string | string[] + geocode?: any + altitude?: number + ceiling?: number +} + +export interface CAPInfo { + language: string + category?: string | string[] + event?: string + responseType?: string | string[] + urgency?: string + severity?: string + certainty?: string + audience?: string + effective?: string + onset?: string + expires?: string + senderName?: string + headline?: string + description?: string + instruction?: string + web?: string + contact?: string + parameter?: any + eventCode?: any + resources: CAPResource[] + areas: CAPArea[] +} + +export interface CAPAlert { + identifier: string + sender: string + sent: string + status?: string + msgType?: string + source?: string + scope?: string + restriction?: string + addresses?: string + code?: string | string[] + note?: string + references?: string + incidents?: string + infos: CAPInfo[] +} From 0d0f179758afbee2306f47a6e3c5dd00cf36b301 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 26 Dec 2025 16:04:36 -0800 Subject: [PATCH 13/26] refactor: organize cap-alerting and improve code quality - Extract CAP type definitions into separate types/cap.ts file - Use getTags() helper instead of direct metadata access --- edge-apps/cap-alerting/src/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 38187c90f..e6af243f0 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -8,7 +8,7 @@ import { isAnywhereScreen, } from '@screenly/edge-apps' -import { CAPResource, CAPArea, CAPInfo, CAPAlert } from './types/cap.js' +import { CAPInfo, CAPAlert } from './types/cap.js' import { XMLParser } from 'fast-xml-parser' const DEMO_BASE_URL = @@ -429,7 +429,7 @@ export async function startApp(): Promise { maxAlerts, }) - const tags: string[] = metadata.tags || [] + const tags: string[] = getTags() const nearestExit = getNearestExit(tags) async function update() { From a573831a49a931260ce0987aa728e9285119be96 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 08:27:34 -0800 Subject: [PATCH 14/26] refactor(cap-alerting): consolidate demo_mode and test_mode into mode setting - Replace separate demo_mode and test_mode boolean settings with single mode enumeration - Mode supports three values: production, demo, test - Add CAPMode type for type-safe mode handling - Update screenly.yml and screenly_qc.yml with mode setting as select type - Update src/main.ts and index.ts to use CAPMode type --- edge-apps/cap-alerting/index.ts | 11 +++++----- edge-apps/cap-alerting/screenly.yml | 29 +++++++++++++++---------- edge-apps/cap-alerting/screenly_qc.yml | 29 +++++++++++++++---------- edge-apps/cap-alerting/src/main.ts | 7 +++--- edge-apps/cap-alerting/src/types/cap.ts | 2 ++ 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/edge-apps/cap-alerting/index.ts b/edge-apps/cap-alerting/index.ts index 98be87330..2e18d3eb2 100644 --- a/edge-apps/cap-alerting/index.ts +++ b/edge-apps/cap-alerting/index.ts @@ -169,7 +169,7 @@ function migrateLegacyCache(): void { try { const oldCache = localStorage.getItem('cap_last') const newCache = localStorage.getItem('cap_feed_cache') - + if (oldCache && !newCache) { console.log('Migrating legacy cache to new format') localStorage.setItem('cap_feed_cache', oldCache) @@ -304,7 +304,7 @@ function renderAlerts( // Use actual CAP status and msgType fields per CAP v1.2 spec const statusBannerInfo = getStatusBannerInfo(alert.status, alert.msgType) - + // Only show context banner for Exercise/Test/System/Update/Cancel if (statusBannerInfo) { const statusBanner = document.createElement('div') @@ -436,8 +436,9 @@ export async function startApp(): Promise { const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) const playAudio = ((settings.audio_alert as string) || 'false') === 'true' const offlineMode = ((settings.offline_mode as string) || 'false') === 'true' - const testMode = ((settings.test_mode as string) || 'false') === 'true' - const demoMode = ((settings.demo_mode as string) || 'false') === 'true' + const mode = (settings.mode as string) || 'production' + const testMode = mode === 'test' + const demoMode = mode === 'demo' const tags: string[] = metadata.tags || [] const nearestExit = getNearestExit(tags) @@ -511,7 +512,7 @@ export async function startApp(): Promise { // Start the fetcher - it will handle initial load and periodic updates fetcher.start((xml) => { handleUpdate(xml) - + // Signal ready after first update if (!offlineMode) { signalReady() diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml index b5851adb4..eeb30e964 100644 --- a/edge-apps/cap-alerting/screenly.yml +++ b/edge-apps/cap-alerting/screenly.yml @@ -19,12 +19,6 @@ settings: title: CAP Feed URL optional: true help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. - demo_mode: - type: string - default_value: 'false' - title: Demo Mode - optional: true - help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). language: type: string default_value: en @@ -37,6 +31,23 @@ settings: title: Maximum Alerts optional: true help_text: Maximum number of alerts to display simultaneously. + mode: + type: string + default_value: production + title: Mode + optional: true + help_text: + properties: + help_text: Select the operation mode for the app. + options: + - label: Production + value: production + - label: Demo + value: demo + - label: Test + value: test + type: select + schema_version: 1 offline_mode: type: string default_value: 'false' @@ -49,9 +60,3 @@ settings: title: Refresh Interval (minutes) optional: true help_text: Time in minutes between feed updates. - test_mode: - type: string - default_value: 'false' - title: Test Mode - optional: true - help_text: Use bundled test CAP file for testing (true/false). diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index b5851adb4..e37ecce08 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -19,12 +19,6 @@ settings: title: CAP Feed URL optional: true help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. - demo_mode: - type: string - default_value: 'false' - title: Demo Mode - optional: true - help_text: Enable demo mode with realistic CAP alerts (randomly selected). Only active when test_mode is disabled and no feed URL is set (true/false). language: type: string default_value: en @@ -37,6 +31,23 @@ settings: title: Maximum Alerts optional: true help_text: Maximum number of alerts to display simultaneously. + mode: + type: string + default_value: production + title: Mode + optional: true + help_text: + properties: + help_text: Select the operation mode for the app. + options: + - label: Production + value: production + - label: Demo + value: demo + - label: Test + value: test + type: select + schema_version: 1 offline_mode: type: string default_value: 'false' @@ -49,9 +60,3 @@ settings: title: Refresh Interval (minutes) optional: true help_text: Time in minutes between feed updates. - test_mode: - type: string - default_value: 'false' - title: Test Mode - optional: true - help_text: Use bundled test CAP file for testing (true/false). diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index e6af243f0..4dedb700e 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -8,7 +8,7 @@ import { isAnywhereScreen, } from '@screenly/edge-apps' -import { CAPInfo, CAPAlert } from './types/cap.js' +import { CAPInfo, CAPAlert, CAPMode } from './types/cap.js' import { XMLParser } from 'fast-xml-parser' const DEMO_BASE_URL = @@ -418,8 +418,9 @@ export async function startApp(): Promise { const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) const playAudio = ((settings.audio_alert as string) || 'false') === 'true' const offlineMode = ((settings.offline_mode as string) || 'false') === 'true' - const testMode = ((settings.test_mode as string) || 'false') === 'true' - const demoMode = ((settings.demo_mode as string) || 'false') === 'true' + const mode = (settings.mode as CAPMode) || 'production' + const testMode = mode === 'test' + const demoMode = mode === 'demo' console.log('CAP Settings:', { playAudio, diff --git a/edge-apps/cap-alerting/src/types/cap.ts b/edge-apps/cap-alerting/src/types/cap.ts index dcc2217e0..e1d8fea9a 100644 --- a/edge-apps/cap-alerting/src/types/cap.ts +++ b/edge-apps/cap-alerting/src/types/cap.ts @@ -57,3 +57,5 @@ export interface CAPAlert { incidents?: string infos: CAPInfo[] } + +export type CAPMode = 'test' | 'demo' | 'production' From 437a3bd5d3c2947ebac928b502b6b30d5fd1f2f3 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 09:07:18 -0800 Subject: [PATCH 15/26] refactor(cap-alerting): remove unused legacy code and orphaned fetcher - Delete legacy index.ts entry point - Delete orphaned CAPFetcher implementation (fetcher.ts) - Delete comprehensive CAPFetcher test suite (fetcher.test.ts) - Update index.test.ts to use modern @screenly/edge-apps imports - Update package.json lint script to reflect active files only --- edge-apps/cap-alerting/index.test.ts | 2 +- edge-apps/cap-alerting/index.ts | 547 ----------------- edge-apps/cap-alerting/package.json | 2 +- edge-apps/cap-alerting/src/fetcher.test.ts | 648 --------------------- edge-apps/cap-alerting/src/fetcher.ts | 349 ----------- 5 files changed, 2 insertions(+), 1546 deletions(-) delete mode 100644 edge-apps/cap-alerting/index.ts delete mode 100644 edge-apps/cap-alerting/src/fetcher.test.ts delete mode 100644 edge-apps/cap-alerting/src/fetcher.ts diff --git a/edge-apps/cap-alerting/index.test.ts b/edge-apps/cap-alerting/index.test.ts index ff7c2d120..0836a87ef 100644 --- a/edge-apps/cap-alerting/index.test.ts +++ b/edge-apps/cap-alerting/index.test.ts @@ -7,7 +7,7 @@ const mockSetupTheme = mock() const mockSignalReady = mock() const mockGetTags = mock() -mock.module('../edge-apps-library/src/index', () => ({ +mock.module('@screenly/edge-apps', () => ({ getSettings: () => mockGetSettings(), getMetadata: () => mockGetMetadata(), getCorsProxyUrl: () => mockGetCorsProxyUrl(), diff --git a/edge-apps/cap-alerting/index.ts b/edge-apps/cap-alerting/index.ts deleted file mode 100644 index 2e18d3eb2..000000000 --- a/edge-apps/cap-alerting/index.ts +++ /dev/null @@ -1,547 +0,0 @@ -import { - setupTheme, - signalReady, - getMetadata, - getTags, - getSettings, - getCorsProxyUrl, -} from '../edge-apps-library/src/index' - -import { XMLParser } from 'fast-xml-parser' -import { CAPFetcher } from './src/fetcher' - -interface CAPResource { - resourceDesc?: string - mimeType: string - size?: number - uri?: string - derefUri?: string - digest?: string - url: string -} - -interface CAPArea { - areaDesc: string - polygon?: string | string[] - circle?: string | string[] - geocode?: any - altitude?: number - ceiling?: number -} - -interface CAPInfo { - language: string - category?: string | string[] - event?: string - responseType?: string | string[] - urgency?: string - severity?: string - certainty?: string - audience?: string - effective?: string - onset?: string - expires?: string - senderName?: string - headline?: string - description?: string - instruction?: string - web?: string - contact?: string - parameter?: any - eventCode?: any - resources: CAPResource[] - areas: CAPArea[] -} - -interface CAPAlert { - identifier: string - sender: string - sent: string - status?: string - msgType?: string - source?: string - scope?: string - restriction?: string - addresses?: string - code?: string | string[] - note?: string - references?: string - incidents?: string - infos: CAPInfo[] -} - -export function parseCap(xml: string): CAPAlert[] { - const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) - const json: any = parser.parse(xml) - const alertsJson = json.alert ? (Array.isArray(json.alert) ? json.alert : [json.alert]) : [] - - const alerts: CAPAlert[] = [] - - alertsJson.forEach((a: any) => { - const infosJson = a.info ? (Array.isArray(a.info) ? a.info : [a.info]) : [] - - const infos: CAPInfo[] = infosJson.map((info: any) => { - const resourcesJson = info.resource - ? Array.isArray(info.resource) - ? info.resource - : [info.resource] - : [] - const areasJson = info.area ? (Array.isArray(info.area) ? info.area : [info.area]) : [] - - return { - language: info.language || '', - category: info.category, - event: info.event, - responseType: info.responseType, - urgency: info.urgency, - severity: info.severity, - certainty: info.certainty, - audience: info.audience, - effective: info.effective, - onset: info.onset, - expires: info.expires, - senderName: info.senderName, - headline: info.headline, - description: info.description, - instruction: info.instruction, - web: info.web, - contact: info.contact, - parameter: info.parameter, - eventCode: info.eventCode, - resources: resourcesJson.map((res: any) => { - return { - resourceDesc: res.resourceDesc, - mimeType: res.mimeType || res['mimeType'], - size: res.size, - uri: res.uri, - derefUri: res.derefUri, - digest: res.digest, - url: res.uri || res.resourceDesc || '', - } - }), - areas: areasJson.map((area: any) => ({ - areaDesc: area.areaDesc || '', - polygon: area.polygon, - circle: area.circle, - geocode: area.geocode, - altitude: area.altitude, - ceiling: area.ceiling, - })), - } - }) - - alerts.push({ - identifier: a.identifier || '', - sender: a.sender || '', - sent: a.sent || '', - status: a.status, - msgType: a.msgType, - source: a.source, - scope: a.scope, - restriction: a.restriction, - addresses: a.addresses, - code: a.code, - note: a.note, - references: a.references, - incidents: a.incidents, - infos, - }) - }) - - return alerts -} - -export function getNearestExit(tags: string[]): string | undefined { - for (const tag of tags) { - const lower = tag.toLowerCase() - if (lower.startsWith('exit:')) { - return tag.slice(5).trim() - } - if (lower.startsWith('exit-')) { - return tag.slice(5).trim() - } - } - return undefined -} - -// Legacy cache migration - convert old cache to new format -function migrateLegacyCache(): void { - try { - const oldCache = localStorage.getItem('cap_last') - const newCache = localStorage.getItem('cap_feed_cache') - - if (oldCache && !newCache) { - console.log('Migrating legacy cache to new format') - localStorage.setItem('cap_feed_cache', oldCache) - localStorage.setItem('cap_feed_cache_meta', JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: true, - })) - localStorage.removeItem('cap_last') - } - } catch (err) { - console.error('Failed to migrate legacy cache:', err) - } -} - -function highlightKeywords(text: string): string { - const keywords = [ - 'DO NOT', - 'DON\'T', - 'DO NOT', - 'IMMEDIATELY', - 'IMMEDIATE', - 'NOW', - 'MOVE TO', - 'EVACUATE', - 'CALL', - 'WARNING', - 'DANGER', - 'SHELTER', - 'TAKE COVER', - 'AVOID', - 'STAY', - 'SEEK', - ] - - let result = text - keywords.forEach((keyword) => { - const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') - result = result.replace(regex, '$1') - }) - - return result -} - -function splitIntoSentences(text: string): string[] { - return text - .split(/(?<=[.!?])\s+/) - .map((s) => s.trim()) - .filter((s) => s.length > 0) -} - -function getStatusBannerInfo(status?: string, msgType?: string): { text: string; classes: string } | null { - const statusLower = (status || '').toLowerCase() - const msgTypeLower = (msgType || '').toLowerCase() - - // Per CAP spec: status = Actual | Exercise | System | Test | Draft - if (statusLower === 'exercise') { - return { - text: 'EXERCISE - THIS IS A DRILL', - classes: 'bg-blue-600 text-white border-blue-800', - } - } - if (statusLower === 'test') { - return { - text: 'TEST MESSAGE - NOT AN ACTUAL ALERT', - classes: 'bg-gray-800 text-white border-gray-900', - } - } - if (statusLower === 'system') { - return { - text: 'SYSTEM MESSAGE', - classes: 'bg-gray-800 text-white border-gray-900', - } - } - if (statusLower === 'draft') { - return null // Don't show banner for drafts - } - - // Per CAP spec: msgType = Alert | Update | Cancel | Ack | Error - if (msgTypeLower === 'update') { - return { - text: 'UPDATED ALERT', - classes: 'bg-orange-600 text-white border-orange-600', - } - } - if (msgTypeLower === 'cancel') { - return { - text: 'ALERT CANCELLED', - classes: 'bg-gray-800 text-white border-gray-900', - } - } - - // For Actual alerts with msgType=Alert, no context banner needed - return null -} - -function getSeverityClasses(severity?: string, urgency?: string): string { - const sev = (severity || '').toLowerCase() - const urg = (urgency || '').toLowerCase() - - if (sev === 'extreme' || urg === 'immediate') { - return 'bg-red-600 text-white border-red-800 status-actual-pulse' - } - if (sev === 'severe') { - return 'bg-orange-500 text-white border-yellow-600' - } - if (sev === 'moderate') { - return 'bg-yellow-400 text-black border-yellow-600' - } - return 'bg-blue-600 text-white border-blue-800' -} - -function renderAlerts( - alerts: CAPAlert[], - nearestExit: string | undefined, - lang: string, - maxAlerts: number, - playAudio: boolean -): void { - const container = document.getElementById('alerts') - if (!container) return - - container.innerHTML = '' - const slice = alerts.slice(0, maxAlerts) - - slice.forEach((alert) => { - const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0] - if (!info) return - - const card = document.createElement('div') - card.className = 'alert-card w-[90vw] mx-[5vw] my-[3vh] bg-gray-100 rounded-xl overflow-hidden flex flex-col' - - // Use actual CAP status and msgType fields per CAP v1.2 spec - const statusBannerInfo = getStatusBannerInfo(alert.status, alert.msgType) - - // Only show context banner for Exercise/Test/System/Update/Cancel - if (statusBannerInfo) { - const statusBanner = document.createElement('div') - statusBanner.className = `${statusBannerInfo.classes} py-[2vh] px-[4vw] border-b-[1vh] status-stripe-pattern` - const statusText = document.createElement('div') - statusText.className = 'status-banner-text font-black uppercase tracking-[0.15em] text-center leading-none' - statusText.textContent = statusBannerInfo.text - statusBanner.appendChild(statusText) - card.appendChild(statusBanner) - } - - const contentWrapper = document.createElement('div') - contentWrapper.className = 'px-[4vw] py-[3vh] flex-1 overflow-y-auto' - - const eventTitle = document.createElement('h1') - eventTitle.className = 'event-title-text font-black text-red-600 uppercase leading-tight mb-[2vh]' - eventTitle.textContent = info.event || alert.identifier - contentWrapper.appendChild(eventTitle) - - const metaParts: string[] = [] - if (info.urgency) metaParts.push(info.urgency.toUpperCase()) - if (info.severity) metaParts.push(info.severity.toUpperCase()) - if (info.certainty) metaParts.push(info.certainty.toUpperCase()) - - if (metaParts.length > 0) { - const badge = document.createElement('div') - badge.className = `severity-badge-text inline-block bg-orange-500 text-white font-extrabold uppercase px-[4vw] py-[2vh] rounded-lg mb-[3vh] tracking-wider leading-none` - badge.textContent = metaParts.join(' ') - contentWrapper.appendChild(badge) - } - - // Only show description if it adds new information beyond event name and context - // Per digital signage best practices: avoid redundancy - if (info.description) { - const desc = document.createElement('p') - desc.className = 'body-text text-black leading-snug mb-[3vh]' - desc.textContent = info.description - contentWrapper.appendChild(desc) - } - - if (info.instruction) { - let instr = info.instruction - if (nearestExit) { - if (instr.includes('{{closest_exit}}') || instr.includes('[[closest_exit]]')) { - instr = instr - .replace(/\{\{closest_exit\}\}/g, nearestExit) - .replace(/\[\[closest_exit\]\]/g, nearestExit) - } else { - instr += `\n\nNearest exit: ${nearestExit}` - } - } - - const instructionBox = document.createElement('div') - instructionBox.className = 'bg-yellow-100 border-l-[1vw] border-yellow-600 px-[4vw] py-[3vh] rounded-lg' - - const sentences = splitIntoSentences(instr) - - if (sentences.length > 2) { - const ul = document.createElement('ul') - ul.className = 'instruction-text text-black leading-snug' - sentences.forEach((sentence) => { - const li = document.createElement('li') - li.className = 'mb-[2vh] flex' - li.innerHTML = `•${highlightKeywords(sentence)}` - ul.appendChild(li) - }) - instructionBox.appendChild(ul) - } else { - const instP = document.createElement('p') - instP.className = 'instruction-text text-black leading-snug whitespace-pre-line' - instP.innerHTML = highlightKeywords(instr) - instructionBox.appendChild(instP) - } - - contentWrapper.appendChild(instructionBox) - } - - info.resources.forEach((res) => { - if (res.mimeType && res.mimeType.startsWith('image')) { - const imgWrapper = document.createElement('div') - imgWrapper.className = 'mt-[4vh] flex justify-center' - const img = document.createElement('img') - img.src = res.url - img.className = 'max-w-full max-h-[20vh] object-contain rounded-lg' - imgWrapper.appendChild(img) - contentWrapper.appendChild(imgWrapper) - } else if (res.mimeType && res.mimeType.startsWith('audio') && playAudio) { - const audio = document.createElement('audio') - audio.src = res.url - audio.controls = true - audio.className = 'w-full mt-[4vh]' - contentWrapper.appendChild(audio) - audio.play().catch(() => { - /* autoplay blocked */ - }) - } - }) - - card.appendChild(contentWrapper) - container.appendChild(card) - }) -} - -export async function startApp(): Promise { - setupTheme() - migrateLegacyCache() - - let settings: Partial> = {} - let metadata: Partial> = {} - - try { - settings = getSettings() - localStorage.setItem('screenly_settings', JSON.stringify(settings)) - } catch (_) { - const cached = localStorage.getItem('screenly_settings') - settings = cached ? (JSON.parse(cached) as Partial>) : {} - } - - try { - metadata = getMetadata() - localStorage.setItem('screenly_metadata', JSON.stringify(metadata)) - } catch (_) { - const cachedMeta = localStorage.getItem('screenly_metadata') - metadata = cachedMeta ? (JSON.parse(cachedMeta) as Partial>) : {} - } - - const feedUrl: string = (settings.cap_feed_url as string) || '' - const lang = (settings.language as string) || 'en' - const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) - const playAudio = ((settings.audio_alert as string) || 'false') === 'true' - const offlineMode = ((settings.offline_mode as string) || 'false') === 'true' - const mode = (settings.mode as string) || 'production' - const testMode = mode === 'test' - const demoMode = mode === 'demo' - - const tags: string[] = metadata.tags || [] - const nearestExit = getNearestExit(tags) - - let fetcher: CAPFetcher | null = null - - function handleUpdate(xml: string | null) { - if (xml) { - try { - const alerts = parseCap(xml) - renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) - } catch (err) { - console.error('Failed to parse or render alerts:', err) - // Don't crash, keep displaying previous content - } - } else { - console.warn('No CAP data available') - } - } - - // For test mode and demo mode, use static files - if (testMode) { - try { - const resp = await fetch('static/test.cap') - const xml = resp.ok ? await resp.text() : null - handleUpdate(xml) - signalReady() - } catch (err) { - console.error('Failed to load test data:', err) - signalReady() - } - } else if (demoMode && !feedUrl) { - // For demo mode without feed URL, rotate through demo files - const demoFiles = [ - 'static/demo-1-tornado.cap', - 'static/demo-2-fire.cap', - 'static/demo-3-flood.cap', - 'static/demo-4-earthquake.cap', - 'static/demo-5-hazmat.cap', - 'static/demo-6-shooter.cap', - ] - let currentIndex = 0 - - const loadDemoFile = async () => { - try { - const resp = await fetch(demoFiles[currentIndex]) - const xml = resp.ok ? await resp.text() : null - handleUpdate(xml) - currentIndex = (currentIndex + 1) % demoFiles.length - } catch (err) { - console.error('Failed to load demo file:', err) - } - } - - await loadDemoFile() - signalReady() - - // Rotate demo files every 30 seconds - setInterval(loadDemoFile, 30000) - } else if (!offlineMode && feedUrl) { - // Use the robust fetcher for live feeds - fetcher = new CAPFetcher({ - feedUrl, - refreshInterval: 30, // Hardcoded to 30 seconds as requested - maxRetries: 5, - initialRetryDelay: 1000, - maxRetryDelay: 30000, - corsProxyUrl: getCorsProxyUrl(), - }) - - // Start the fetcher - it will handle initial load and periodic updates - fetcher.start((xml) => { - handleUpdate(xml) - - // Signal ready after first update - if (!offlineMode) { - signalReady() - } - }) - - // Log stats periodically for debugging - setInterval(() => { - if (fetcher) { - const stats = fetcher.getStats() - console.log('Fetcher stats:', stats) - } - }, 60000) // Log every minute - } else if (offlineMode) { - // In offline mode, just load from cache - const cached = localStorage.getItem('cap_feed_cache') - handleUpdate(cached) - signalReady() - } else { - console.warn('No feed URL configured and not in demo mode') - signalReady() - } - - // Add a global reference for debugging - if (typeof window !== 'undefined') { - ;(window as any).capFetcher = fetcher - } -} - -if (typeof window !== 'undefined' && typeof document !== 'undefined') { - startApp() -} diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index b3cca02db..5364d2878 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -18,7 +18,7 @@ "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", - "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.ts index.test.ts src/fetcher.ts src/fetcher.test.ts src/main.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.test.ts src/main.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", diff --git a/edge-apps/cap-alerting/src/fetcher.test.ts b/edge-apps/cap-alerting/src/fetcher.test.ts deleted file mode 100644 index a1afb9e7e..000000000 --- a/edge-apps/cap-alerting/src/fetcher.test.ts +++ /dev/null @@ -1,648 +0,0 @@ -/** - * Unit tests for CAPFetcher - */ - -import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test' -import { CAPFetcher } from './fetcher' - -// Mock localStorage -const localStorageMock = (() => { - let store: Record = {} - - return { - getItem: (key: string) => store[key] || null, - setItem: (key: string, value: string) => { - store[key] = value - }, - removeItem: (key: string) => { - delete store[key] - }, - clear: () => { - store = {} - }, - } -})() - -// Mock fetch -const mockFetch = mock() - -// Helper to wait for a condition -const waitFor = async (condition: () => boolean, timeout = 5000) => { - const start = Date.now() - while (!condition()) { - if (Date.now() - start > timeout) { - throw new Error('Timeout waiting for condition') - } - await new Promise((resolve) => setTimeout(resolve, 50)) - } -} - -describe('CAPFetcher', () => { - beforeEach(() => { - // Setup mocks - global.localStorage = localStorageMock as any - global.fetch = mockFetch as any - global.window = { - setInterval, - clearInterval, - setTimeout, - clearTimeout, - } as any - - // Clear localStorage before each test - localStorageMock.clear() - - // Reset fetch mock - mockFetch.mockReset() - }) - - afterEach(() => { - mockFetch.mockRestore() - }) - - describe('Constructor', () => { - it('should create a fetcher with default config', () => { - const fetcher = new CAPFetcher({}) - expect(fetcher).toBeDefined() - expect(fetcher.getStats().isRunning).toBe(false) - }) - - it('should create a fetcher with custom config', () => { - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - refreshInterval: 60, - maxRetries: 3, - }) - expect(fetcher).toBeDefined() - }) - }) - - describe('Caching', () => { - it('should save valid data to cache', async () => { - const mockData = - 'TEST' - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - // Wait for the fetch to complete - await waitFor(() => updateCallback.mock.calls.length > 0) - - // Check that data was cached - const cached = localStorageMock.getItem('cap_feed_cache') - expect(cached).toBe(mockData) - - // Check that metadata was saved - const meta = JSON.parse( - localStorageMock.getItem('cap_feed_cache_meta') || '{}', - ) - expect(meta.isValid).toBe(true) - expect(meta.timestamp).toBeDefined() - - fetcher.stop() - }) - - it('should not cache empty data', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => '', - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => mockFetch.mock.calls.length > 0) - - // Check that nothing was cached - const cached = localStorageMock.getItem('cap_feed_cache') - expect(cached).toBeNull() - - fetcher.stop() - }) - - it('should not cache invalid XML data', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => 'This is not XML', - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => mockFetch.mock.calls.length > 0) - - // Check that data was not cached - const cached = localStorageMock.getItem('cap_feed_cache') - expect(cached).toBeNull() - - fetcher.stop() - }) - - it('should use atomic write for cache', async () => { - const mockData = - 'TEST' - - // Set up existing cache - localStorageMock.setItem('cap_feed_cache', 'old data') - - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0) - - // Verify cache was updated - const cached = localStorageMock.getItem('cap_feed_cache') - expect(cached).toBe(mockData) - - // Verify temp keys were cleaned up - const tempCache = localStorageMock.getItem('cap_feed_cache_temp') - expect(tempCache).toBeNull() - - fetcher.stop() - }) - - it('should load from cache if initial fetch fails', async () => { - const cachedData = - 'CACHED' - - // Set up cache - localStorageMock.setItem('cap_feed_cache', cachedData) - localStorageMock.setItem( - 'cap_feed_cache_meta', - JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: true, - }), - ) - - mockFetch.mockRejectedValue(new Error('Network error')) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 2, - initialRetryDelay: 100, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0, 3000) - - // Should have called with cached data - expect(updateCallback.mock.calls[0][0]).toBe(cachedData) - - fetcher.stop() - }) - }) - - describe('Retry Logic', () => { - it('should retry on fetch failure', async () => { - const mockData = - 'TEST' - - // Fail first two attempts, succeed on third - mockFetch - .mockRejectedValueOnce(new Error('Network error')) - .mockRejectedValueOnce(new Error('Network error')) - .mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 3, - initialRetryDelay: 100, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0, 3000) - - expect(mockFetch.mock.calls.length).toBe(3) - expect(updateCallback.mock.calls[0][0]).toBe(mockData) - - fetcher.stop() - }) - - it('should fall back to cache after all retries exhausted', async () => { - const cachedData = - 'CACHED' - - // Set up cache - localStorageMock.setItem('cap_feed_cache', cachedData) - localStorageMock.setItem( - 'cap_feed_cache_meta', - JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: true, - }), - ) - - mockFetch.mockRejectedValue(new Error('Network error')) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 2, - initialRetryDelay: 100, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0, 3000) - - expect(updateCallback.mock.calls[0][0]).toBe(cachedData) - - fetcher.stop() - }) - - it('should use exponential backoff', async () => { - mockFetch.mockRejectedValue(new Error('Network error')) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 3, - initialRetryDelay: 100, - }) - - const updateCallback = mock() - const startTime = Date.now() - - fetcher.start(updateCallback) - - await waitFor(() => mockFetch.mock.calls.length >= 3, 3000) - - const elapsed = Date.now() - startTime - - // Should take at least 100ms (1st retry) + 200ms (2nd retry) = 300ms - // With jitter, it could be slightly less, so check for at least 200ms - expect(elapsed).toBeGreaterThan(200) - - fetcher.stop() - }) - }) - - describe('HTTP Handling', () => { - it('should handle HTTP errors', async () => { - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 404, - statusText: 'Not Found', - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 1, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0, 2000) - - // Should call with null since no cache exists - expect(updateCallback.mock.calls[0][0]).toBeNull() - - fetcher.stop() - }) - - it('should add CORS proxy for http/https URLs', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => '', - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => mockFetch.mock.calls.length > 0) - - expect(mockFetch.mock.calls[0][0]).toBe( - 'https://proxy.com/https://example.com/feed.xml', - ) - - fetcher.stop() - }) - - it('should handle empty response', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => '', - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 1, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => mockFetch.mock.calls.length > 0) - - // Should not cache or call callback with empty data - const cached = localStorageMock.getItem('cap_feed_cache') - expect(cached).toBeNull() - - fetcher.stop() - }) - }) - - describe('Stats', () => { - it('should track statistics', async () => { - const mockData = - 'TEST' - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - - let initialStats = fetcher.getStats() - expect(initialStats.isRunning).toBe(false) - expect(initialStats.lastSuccessfulFetch).toBe(0) - expect(initialStats.consecutiveFailures).toBe(0) - - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0) - - const stats = fetcher.getStats() - expect(stats.isRunning).toBe(true) - expect(stats.lastSuccessfulFetch).toBeGreaterThan(0) - expect(stats.cacheAge).toBeLessThan(5) // Should be very recent - - fetcher.stop() - }) - - it('should track consecutive failures', async () => { - mockFetch.mockRejectedValue(new Error('Network error')) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - maxRetries: 2, - initialRetryDelay: 100, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => mockFetch.mock.calls.length >= 2, 2000) - - const stats = fetcher.getStats() - expect(stats.consecutiveFailures).toBeGreaterThan(0) - - fetcher.stop() - }) - }) - - describe('Start and Stop', () => { - it('should not start if already running', async () => { - const mockData = - 'TEST' - mockFetch.mockResolvedValue({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - - fetcher.start(updateCallback) - await waitFor(() => fetcher.getStats().isRunning) - - // Try to start again - should log warning - const consoleWarn = mock() - const originalWarn = console.warn - console.warn = consoleWarn - - fetcher.start(updateCallback) - expect(consoleWarn.mock.calls.length).toBeGreaterThan(0) - - console.warn = originalWarn - fetcher.stop() - }) - - it('should stop properly', async () => { - const mockData = - 'TEST' - mockFetch.mockResolvedValue({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => fetcher.getStats().isRunning) - - fetcher.stop() - - expect(fetcher.getStats().isRunning).toBe(false) - }) - }) - - describe('Force Refresh', () => { - it('should force an immediate refresh', async () => { - const mockData = - 'TEST' - mockFetch.mockResolvedValue({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - - await fetcher.forceRefresh(updateCallback) - - expect(mockFetch.mock.calls.length).toBe(1) - expect(updateCallback.mock.calls[0][0]).toBe(mockData) - }) - }) - - describe('Edge Cases', () => { - it('should handle missing feed URL', async () => { - const fetcher = new CAPFetcher({ - feedUrl: '', - corsProxyUrl: 'https://proxy.com', - maxRetries: 1, - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0, 2000) - - expect(updateCallback.mock.calls[0][0]).toBeNull() - - fetcher.stop() - }) - - it('should handle localStorage errors gracefully', async () => { - // Mock localStorage to throw errors - const originalSetItem = localStorageMock.setItem - localStorageMock.setItem = mock(() => { - throw new Error('QuotaExceededError') - }) - - const mockData = - 'TEST' - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0) - - // Should still return data even though caching failed - expect(updateCallback.mock.calls[0][0]).toBe(mockData) - - // Restore original - localStorageMock.setItem = originalSetItem - - fetcher.stop() - }) - - it('should handle corrupted cache metadata', async () => { - // Set up corrupted metadata - localStorageMock.setItem( - 'cap_feed_cache', - '', - ) - localStorageMock.setItem('cap_feed_cache_meta', 'invalid json') - - const mockData = - 'NEW' - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0) - - // Should fetch fresh data instead of using corrupted cache - expect(updateCallback.mock.calls[0][0]).toBe(mockData) - - fetcher.stop() - }) - - it('should return null when cache has invalid flag', async () => { - // Set up cache with invalid flag - localStorageMock.setItem( - 'cap_feed_cache', - '', - ) - localStorageMock.setItem( - 'cap_feed_cache_meta', - JSON.stringify({ - data: '', - timestamp: Date.now(), - isValid: false, - }), - ) - - const mockData = - 'NEW' - mockFetch.mockResolvedValueOnce({ - ok: true, - text: async () => mockData, - }) - - const fetcher = new CAPFetcher({ - feedUrl: 'https://example.com/feed.xml', - corsProxyUrl: 'https://proxy.com', - }) - - const updateCallback = mock() - fetcher.start(updateCallback) - - await waitFor(() => updateCallback.mock.calls.length > 0) - - // Should fetch fresh data - expect(updateCallback.mock.calls[0][0]).toBe(mockData) - - fetcher.stop() - }) - }) -}) diff --git a/edge-apps/cap-alerting/src/fetcher.ts b/edge-apps/cap-alerting/src/fetcher.ts deleted file mode 100644 index fe2d63fc5..000000000 --- a/edge-apps/cap-alerting/src/fetcher.ts +++ /dev/null @@ -1,349 +0,0 @@ -/** - * Robust CAP feed fetcher with exponential backoff, caching, and periodic reloading - */ - -interface FetcherConfig { - feedUrl: string - refreshInterval: number // in seconds, default 30 - maxRetries: number // default 5 - initialRetryDelay: number // in ms, default 1000 - maxRetryDelay: number // in ms, default 30000 - corsProxyUrl: string -} - -interface CacheEntry { - data: string - timestamp: number - isValid: boolean -} - -const CACHE_KEY = 'cap_feed_cache' -const CACHE_METADATA_KEY = 'cap_feed_cache_meta' - -export class CAPFetcher { - private config: FetcherConfig - private intervalId: number | null = null - private isRunning: boolean = false - private lastSuccessfulFetch: number = 0 - private consecutiveFailures: number = 0 - - constructor(config: Partial) { - this.config = { - feedUrl: config.feedUrl || '', - refreshInterval: config.refreshInterval || 30, - maxRetries: config.maxRetries || 5, - initialRetryDelay: config.initialRetryDelay || 1000, - maxRetryDelay: config.maxRetryDelay || 30000, - corsProxyUrl: config.corsProxyUrl || '', - } - } - - /** - * Start the periodic fetching - */ - public start(onUpdate: (data: string | null) => void): void { - if (this.isRunning) { - console.warn('CAPFetcher is already running') - return - } - - this.isRunning = true - - // Initial fetch - this.fetchWithRetry().then((data) => { - if (data) { - onUpdate(data) - } else { - // If initial fetch fails, try to load from cache - const cached = this.getCachedData() - if (cached) { - console.log('Using cached data for initial load') - onUpdate(cached) - } else { - onUpdate(null) - } - } - }) - - // Set up periodic fetching - this.intervalId = window.setInterval(() => { - this.fetchWithRetry().then((data) => { - if (data) { - onUpdate(data) - } - // If fetch fails, we keep displaying the cached data without calling onUpdate - }) - }, this.config.refreshInterval * 1000) - } - - /** - * Stop the periodic fetching - */ - public stop(): void { - if (this.intervalId !== null) { - clearInterval(this.intervalId) - this.intervalId = null - } - this.isRunning = false - } - - /** - * Get cached data from localStorage - */ - private getCachedData(): string | null { - try { - const cached = localStorage.getItem(CACHE_KEY) - if (!cached) return null - - const meta = this.getCacheMetadata() - if (!meta || !meta.isValid) { - console.warn('Cache metadata indicates invalid data') - return null - } - - return cached - } catch (err) { - console.error('Error reading from cache:', err) - return null - } - } - - /** - * Get cache metadata - */ - private getCacheMetadata(): CacheEntry | null { - try { - const metaStr = localStorage.getItem(CACHE_METADATA_KEY) - if (!metaStr) return null - return JSON.parse(metaStr) as CacheEntry - } catch (err) { - console.error('Error reading cache metadata:', err) - return null - } - } - - /** - * Save data to cache with validation - */ - private saveToCacheWithValidation(data: string): boolean { - try { - // Validate the data before saving - if (!data || data.trim().length === 0) { - console.warn('Refusing to cache empty data') - return false - } - - // Basic XML validation - check if it looks like valid CAP XML - if (!data.includes(' { - for (let attempt = 0; attempt < this.config.maxRetries; attempt++) { - try { - const data = await this.fetchData() - - if (data) { - // Successfully fetched - save to cache - const saved = this.saveToCacheWithValidation(data) - if (saved) { - return data - } else { - console.warn('Failed to cache data, but returning it anyway') - return data - } - } - } catch (err) { - console.error(`Fetch attempt ${attempt + 1} failed:`, err) - this.consecutiveFailures++ - - // If this isn't the last attempt, wait before retrying - if (attempt < this.config.maxRetries - 1) { - const delay = this.calculateBackoffDelay(attempt) - console.log(`Retrying in ${delay}ms...`) - await this.sleep(delay) - } - } - } - - // All retries exhausted - console.error(`Failed to fetch after ${this.config.maxRetries} attempts`) - - // Return cached data as fallback - const cached = this.getCachedData() - if (cached) { - console.log('Falling back to cached data') - return cached - } - - return null - } - - /** - * Calculate exponential backoff delay with jitter - */ - private calculateBackoffDelay(attempt: number): number { - const exponentialDelay = Math.min( - this.config.initialRetryDelay * Math.pow(2, attempt), - this.config.maxRetryDelay, - ) - - // Add jitter (±25% randomness) to prevent thundering herd - const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1) - return Math.floor(exponentialDelay + jitter) - } - - /** - * Sleep utility - */ - private sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)) - } - - /** - * Perform the actual fetch - */ - private async fetchData(): Promise { - if (!this.config.feedUrl) { - throw new Error('Feed URL is not configured') - } - - let url = this.config.feedUrl - - // Add CORS proxy if needed - if (this.config.feedUrl.match(/^https?:/)) { - url = `${this.config.corsProxyUrl}/${this.config.feedUrl}` - } - - const controller = new AbortController() - const timeoutId = setTimeout(() => controller.abort(), 15000) // 15 second timeout - - try { - const response = await fetch(url, { - signal: controller.signal, - cache: 'no-cache', - headers: { - Accept: 'application/xml, text/xml, */*', - }, - }) - - clearTimeout(timeoutId) - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - - const text = await response.text() - - if (!text || text.trim().length === 0) { - throw new Error('Received empty response') - } - - return text - } catch (err) { - clearTimeout(timeoutId) - - if (err instanceof Error) { - if (err.name === 'AbortError') { - throw new Error('Fetch timeout after 15 seconds') - } - throw err - } - - throw new Error('Unknown fetch error') - } - } - - /** - * Get statistics about the fetcher's performance - */ - public getStats() { - return { - isRunning: this.isRunning, - lastSuccessfulFetch: this.lastSuccessfulFetch, - consecutiveFailures: this.consecutiveFailures, - cacheAge: this.getCacheAge(), - } - } - - /** - * Get cache age in seconds - */ - private getCacheAge(): number | null { - const meta = this.getCacheMetadata() - if (!meta) return null - return Math.floor((Date.now() - meta.timestamp) / 1000) - } - - /** - * Force a refresh (useful for debugging) - */ - public async forceRefresh( - onUpdate: (data: string | null) => void, - ): Promise { - const data = await this.fetchWithRetry() - if (data) { - onUpdate(data) - } - } -} From 04ad7632701c3f685f6f446c7789ae5f3bf86ceb Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 10:25:46 -0800 Subject: [PATCH 16/26] refactor(cap-alerting): extract fetching logic into new CAPFetcher class - Create src/fetcher.ts with CAPFetcher class - Extract test, demo, and live data fetching logic - Create comprehensive test suite in src/fetcher.test.ts - Update main.ts to use CAPFetcher instead of inline logic - Remove DEMO_BASE_URL, fetchCapData, and related inline fetch code - Update package.json lint script to include fetcher files --- edge-apps/cap-alerting/package.json | 3 +- edge-apps/cap-alerting/src/fetcher.test.ts | 425 +++++++++++++++++++++ edge-apps/cap-alerting/src/fetcher.ts | 121 ++++++ edge-apps/cap-alerting/src/main.ts | 73 +--- 4 files changed, 557 insertions(+), 65 deletions(-) create mode 100644 edge-apps/cap-alerting/src/fetcher.test.ts create mode 100644 edge-apps/cap-alerting/src/fetcher.ts diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 5364d2878..715f34910 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -15,10 +15,11 @@ "build:js:common": "bun build src/main.ts --outdir static/js --target browser --format iife", "build:js:prod": "bun run build:js:common", "build:js:dev": "bun run build:js:common -- --watch", + "test": "bun test", "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", - "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.test.ts src/main.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.test.ts src/main.ts src/fetcher.ts src/fetcher.test.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", diff --git a/edge-apps/cap-alerting/src/fetcher.test.ts b/edge-apps/cap-alerting/src/fetcher.test.ts new file mode 100644 index 000000000..5803a87b2 --- /dev/null +++ b/edge-apps/cap-alerting/src/fetcher.test.ts @@ -0,0 +1,425 @@ +import { describe, it, expect, beforeEach, mock } from 'bun:test' +import { CAPFetcher } from './fetcher' + +// Mock the @screenly/edge-apps module +const mockGetCorsProxyUrl = mock() +const mockIsAnywhereScreen = mock() + +mock.module('@screenly/edge-apps', () => ({ + getCorsProxyUrl: () => mockGetCorsProxyUrl(), + isAnywhereScreen: () => mockIsAnywhereScreen(), + setupTheme: () => {}, + signalReady: () => {}, + getMetadata: () => ({}), + getTags: () => [], + getSettings: () => ({}), +})) + +// Mock localStorage +const localStorageMock = (() => { + let store: Record = {} + + return { + getItem: (key: string) => store[key] || null, + setItem: (key: string, value: string) => { + store[key] = value + }, + removeItem: (key: string) => { + delete store[key] + }, + clear: () => { + store = {} + }, + } +})() + +// Mock fetch +const mockFetch = mock() + +describe('CAPFetcher', () => { + beforeEach(() => { + // Setup mocks + global.localStorage = localStorageMock as any + global.fetch = mockFetch as any + + // Clear localStorage + localStorageMock.clear() + + // Reset mocks + mockFetch.mockReset() + mockGetCorsProxyUrl.mockReset() + mockIsAnywhereScreen.mockReset() + + // Default mock implementations + mockGetCorsProxyUrl.mockReturnValue('https://cors-proxy.example.com') + mockIsAnywhereScreen.mockReturnValue(false) + }) + + describe('Test Mode', () => { + it('should fetch test data from static/test.cap', async () => { + const testData = + 'TEST' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => testData, + }) + + const fetcher = new CAPFetcher({ + testMode: true, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(testData) + expect(mockFetch.mock.calls[0][0]).toBe('static/test.cap') + }) + + it('should return null if test file not found', async () => { + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 404, + }) + + const fetcher = new CAPFetcher({ + testMode: true, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBeNull() + }) + + it('should handle fetch errors in test mode', async () => { + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + const fetcher = new CAPFetcher({ + testMode: true, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBeNull() + }) + }) + + describe('Demo Mode', () => { + it('should fetch random demo file on local screen', async () => { + const demoData = + 'DEMO' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => demoData, + }) + + mockIsAnywhereScreen.mockReturnValueOnce(false) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: true, + feedUrl: '', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(demoData) + // Should fetch from static directory + const url = mockFetch.mock.calls[0][0] as string + expect(url).toMatch(/^static\/demo-/) + }) + + it('should fetch from remote URL on Anywhere screen', async () => { + const demoData = + 'REMOTE-DEMO' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => demoData, + }) + + mockIsAnywhereScreen.mockReturnValueOnce(true) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: true, + feedUrl: '', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(demoData) + // Should fetch from GitHub remote + const url = mockFetch.mock.calls[0][0] as string + expect(url).toContain( + 'https://raw.githubusercontent.com/Screenly/Playground', + ) + }) + + it('should return null if demo file not found', async () => { + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 404, + }) + + mockIsAnywhereScreen.mockReturnValueOnce(false) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: true, + feedUrl: '', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBeNull() + }) + + it('should not enter demo mode if feed URL is provided', async () => { + const liveData = + 'LIVE' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => liveData, + }) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: true, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(liveData) + // Should not fetch from static demo files + const url = mockFetch.mock.calls[0][0] as string + expect(url).not.toMatch(/^static\/demo-/) + expect(url).toContain('https://cors-proxy.example.com') + }) + }) + + describe('Live Mode', () => { + it('should fetch live data with CORS proxy', async () => { + const liveData = + 'LIVE' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => liveData, + }) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(liveData) + expect(mockFetch.mock.calls[0][0]).toBe( + 'https://cors-proxy.example.com/https://example.com/feed.xml', + ) + }) + + it('should cache successful fetches', async () => { + const liveData = + 'CACHED' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => liveData, + }) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + await fetcher.fetch() + + expect(localStorageMock.getItem('cap_last')).toBe(liveData) + }) + + it('should return cached data on fetch failure', async () => { + const cachedData = + 'CACHED' + + // Set up cache + localStorageMock.setItem('cap_last', cachedData) + + // Mock fetch to fail + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(cachedData) + }) + + it('should return null if fetch fails and no cache exists', async () => { + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBeNull() + }) + + it('should handle HTTP errors', async () => { + const cachedData = + 'CACHED' + + // Set up cache + localStorageMock.setItem('cap_last', cachedData) + + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 404, + }) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + // Should return cached data + expect(result).toBe(cachedData) + }) + + it('should not use CORS proxy for non-HTTP URLs', async () => { + const liveData = + 'LOCAL' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => liveData, + }) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'file:///local/path/feed.xml', + offlineMode: false, + }) + + await fetcher.fetch() + + // Should not add CORS proxy for non-HTTP URLs + expect(mockFetch.mock.calls[0][0]).toBe('file:///local/path/feed.xml') + }) + }) + + describe('Offline Mode', () => { + it('should return cached data in offline mode', async () => { + const cachedData = + 'OFFLINE' + + // Set up cache + localStorageMock.setItem('cap_last', cachedData) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: true, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(cachedData) + // Should not attempt any fetch + expect(mockFetch.mock.calls.length).toBe(0) + }) + + it('should return null in offline mode with no cache', async () => { + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: 'https://example.com/feed.xml', + offlineMode: true, + }) + + const result = await fetcher.fetch() + + expect(result).toBeNull() + }) + }) + + describe('Edge Cases', () => { + it('should handle missing feed URL in live mode', async () => { + const cachedData = '' + + localStorageMock.setItem('cap_last', cachedData) + + const fetcher = new CAPFetcher({ + testMode: false, + demoMode: false, + feedUrl: '', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(cachedData) + }) + + it('should prioritize testMode over demoMode', async () => { + const testData = + 'TEST' + + mockFetch.mockResolvedValueOnce({ + ok: true, + text: async () => testData, + }) + + const fetcher = new CAPFetcher({ + testMode: true, + demoMode: true, + feedUrl: 'https://example.com/feed.xml', + offlineMode: false, + }) + + const result = await fetcher.fetch() + + expect(result).toBe(testData) + // Should fetch from test file, not demo file + const url = mockFetch.mock.calls[0][0] as string + expect(url).toBe('static/test.cap') + }) + }) +}) diff --git a/edge-apps/cap-alerting/src/fetcher.ts b/edge-apps/cap-alerting/src/fetcher.ts new file mode 100644 index 000000000..52968673a --- /dev/null +++ b/edge-apps/cap-alerting/src/fetcher.ts @@ -0,0 +1,121 @@ +import { getCorsProxyUrl, isAnywhereScreen } from '@screenly/edge-apps' + +const DEMO_BASE_URL = + 'https://raw.githubusercontent.com/Screenly/Playground/refs/heads/master/edge-apps/cap-alerting' + +export interface FetcherConfig { + testMode: boolean + demoMode: boolean + feedUrl: string + offlineMode: boolean +} + +/** + * Fetches CAP (Common Alerting Protocol) data based on the app mode. + * Supports test mode (static test file), demo mode (rotating demo files), + * and production mode (live feed with fallback to localStorage cache). + */ +export class CAPFetcher { + private config: FetcherConfig + + constructor(config: FetcherConfig) { + this.config = config + } + + /** + * Fetch CAP data based on configured mode + */ + async fetch(): Promise { + if (this.config.testMode) { + return this.fetchTestData() + } + + if (this.config.demoMode && !this.config.feedUrl) { + return this.fetchDemoData() + } + + return this.fetchLiveData() + } + + /** + * Fetch test data from static test file + */ + private async fetchTestData(): Promise { + try { + const resp = await fetch('static/test.cap') + return resp.ok ? await resp.text() : null + } catch (err) { + console.warn('Failed to load test data:', err) + return null + } + } + + /** + * Fetch demo data - randomly selects from available demo files + */ + private async fetchDemoData(): Promise { + const localDemoFiles = [ + 'static/demo-1-tornado.cap', + 'static/demo-2-fire.cap', + 'static/demo-3-flood.cap', + 'static/demo-4-earthquake.cap', + 'static/demo-5-hazmat.cap', + 'static/demo-6-shooter.cap', + ] + + const remoteDemoFiles = localDemoFiles.map( + (file) => `${DEMO_BASE_URL}/${file}`, + ) + + const demoFiles = isAnywhereScreen() ? remoteDemoFiles : localDemoFiles + const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)] + + try { + const resp = await fetch(randomFile) + return resp.ok ? await resp.text() : null + } catch (err) { + console.warn('Failed to load demo data:', err) + return null + } + } + + /** + * Fetch live CAP data with fallback to localStorage cache + */ + private async fetchLiveData(): Promise { + // If in offline mode, return cached data + if (this.config.offlineMode) { + return localStorage.getItem('cap_last') + } + + // No feed URL configured + if (!this.config.feedUrl) { + console.warn('No feed URL configured') + return localStorage.getItem('cap_last') + } + + try { + const cors = getCorsProxyUrl() + let url = this.config.feedUrl + + // Add CORS proxy for HTTP(S) URLs + if (this.config.feedUrl.match(/^https?:/)) { + url = `${cors}/${this.config.feedUrl}` + } + + const response = await fetch(url) + if (!response.ok) { + throw new Error(`HTTP ${response.status}`) + } + + const text = await response.text() + // Cache the successful fetch + localStorage.setItem('cap_last', text) + return text + } catch (err) { + console.warn('CAP fetch failed, falling back to cache:', err) + // Return cached data on failure + return localStorage.getItem('cap_last') + } + } +} diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 4dedb700e..9e79aad9e 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -5,15 +5,12 @@ import { getTags, getSettings, getCorsProxyUrl, - isAnywhereScreen, } from '@screenly/edge-apps' import { CAPInfo, CAPAlert, CAPMode } from './types/cap.js' +import { CAPFetcher } from './fetcher.js' import { XMLParser } from 'fast-xml-parser' -const DEMO_BASE_URL = - 'https://raw.githubusercontent.com/Screenly/Playground/refs/heads/master/edge-apps/cap-alerting' - export function parseCap(xml: string): CAPAlert[] { const parser = new XMLParser({ ignoreAttributes: false, @@ -119,33 +116,6 @@ export function getNearestExit(tags: string[]): string | undefined { return undefined } -async function fetchCapData( - feedUrl: string, - offlineMode: boolean, -): Promise { - if (offlineMode) { - return localStorage.getItem('cap_last') - } - - try { - const cors = getCorsProxyUrl() - let url = feedUrl - if (feedUrl && feedUrl.match(/^https?:/)) { - url = `${cors}/${feedUrl}` - } - - const response = await fetch(url) - if (!response.ok) throw new Error(`HTTP ${response.status}`) - - const text = await response.text() - localStorage.setItem('cap_last', text) - return text - } catch (err) { - console.warn('CAP fetch failed', err) - return localStorage.getItem('cap_last') - } -} - function highlightKeywords(text: string): string { const keywords = [ 'DO NOT', @@ -433,40 +403,15 @@ export async function startApp(): Promise { const tags: string[] = getTags() const nearestExit = getNearestExit(tags) - async function update() { - let xml: string | null - - if (testMode) { - try { - const resp = await fetch('static/test.cap') - xml = resp.ok ? await resp.text() : null - } catch (_) { - xml = null - } - } else if (demoMode && !feedUrl) { - const localDemoFiles = [ - 'static/demo-1-tornado.cap', - 'static/demo-2-fire.cap', - 'static/demo-3-flood.cap', - 'static/demo-4-earthquake.cap', - 'static/demo-5-hazmat.cap', - 'static/demo-6-shooter.cap', - ] - const remoteDemoFiles = localDemoFiles.map( - (file) => `${DEMO_BASE_URL}/${file}`, - ) - const demoFiles = isAnywhereScreen() ? remoteDemoFiles : localDemoFiles - const randomFile = demoFiles[Math.floor(Math.random() * demoFiles.length)] - try { - const resp = await fetch(randomFile) - xml = resp.ok ? await resp.text() : null - } catch (_) { - xml = null - } - } else { - xml = await fetchCapData(feedUrl, offlineMode) - } + const fetcher = new CAPFetcher({ + testMode, + demoMode, + feedUrl, + offlineMode, + }) + async function update() { + const xml = await fetcher.fetch() if (xml) { const alerts = parseCap(xml) renderAlerts(alerts, nearestExit, lang, maxAlerts, playAudio) From b3f186aebf07b3b219f67fdecae52458eb3979f1 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 10:37:47 -0800 Subject: [PATCH 17/26] chore(cap-alerting): remove `.js` extension from imports --- edge-apps/cap-alerting/src/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 9e79aad9e..6ef43875f 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -7,8 +7,8 @@ import { getCorsProxyUrl, } from '@screenly/edge-apps' -import { CAPInfo, CAPAlert, CAPMode } from './types/cap.js' -import { CAPFetcher } from './fetcher.js' +import { CAPInfo, CAPAlert, CAPMode } from './types/cap' +import { CAPFetcher } from './fetcher' import { XMLParser } from 'fast-xml-parser' export function parseCap(xml: string): CAPAlert[] { From efc6251417b926faef0d72bbd0c74dcac96a771f Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 11:09:05 -0800 Subject: [PATCH 18/26] refactor(cap-alerting): extract parsing logic into dedicated parser module - Create src/parser.ts with parseCap function - Rename index.test.ts to src/parser.test.ts with CAP parsing tests - Update src/main.ts to import parseCap from parser module - Update package.json lint script to reference new parser files --- edge-apps/cap-alerting/package.json | 2 +- edge-apps/cap-alerting/screenly_qc.yml | 12 +-- edge-apps/cap-alerting/src/main.ts | 96 +------------------ .../{index.test.ts => src/parser.test.ts} | 41 ++++++-- edge-apps/cap-alerting/src/parser.ts | 94 ++++++++++++++++++ 5 files changed, 134 insertions(+), 111 deletions(-) rename edge-apps/cap-alerting/{index.test.ts => src/parser.test.ts} (98%) create mode 100644 edge-apps/cap-alerting/src/parser.ts diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 715f34910..88aae79d5 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -19,7 +19,7 @@ "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", - "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck index.test.ts src/main.ts src/fetcher.ts src/fetcher.test.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck src/main.ts src/parser.ts src/parser.test.ts src/fetcher.ts src/fetcher.test.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index e37ecce08..eeb30e964 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -40,12 +40,12 @@ settings: properties: help_text: Select the operation mode for the app. options: - - label: Production - value: production - - label: Demo - value: demo - - label: Test - value: test + - label: Production + value: production + - label: Demo + value: demo + - label: Test + value: test type: select schema_version: 1 offline_mode: diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 6ef43875f..fc9b010c9 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -7,101 +7,9 @@ import { getCorsProxyUrl, } from '@screenly/edge-apps' -import { CAPInfo, CAPAlert, CAPMode } from './types/cap' +import { CAPAlert, CAPMode } from './types/cap' +import { parseCap } from './parser' import { CAPFetcher } from './fetcher' -import { XMLParser } from 'fast-xml-parser' - -export function parseCap(xml: string): CAPAlert[] { - const parser = new XMLParser({ - ignoreAttributes: false, - attributeNamePrefix: '@_', - }) - const json: any = parser.parse(xml) - const alertsJson = json.alert - ? Array.isArray(json.alert) - ? json.alert - : [json.alert] - : [] - - const alerts: CAPAlert[] = [] - - alertsJson.forEach((a: any) => { - const infosJson = a.info ? (Array.isArray(a.info) ? a.info : [a.info]) : [] - - const infos: CAPInfo[] = infosJson.map((info: any) => { - const resourcesJson = info.resource - ? Array.isArray(info.resource) - ? info.resource - : [info.resource] - : [] - const areasJson = info.area - ? Array.isArray(info.area) - ? info.area - : [info.area] - : [] - - return { - language: info.language || '', - category: info.category, - event: info.event, - responseType: info.responseType, - urgency: info.urgency, - severity: info.severity, - certainty: info.certainty, - audience: info.audience, - effective: info.effective, - onset: info.onset, - expires: info.expires, - senderName: info.senderName, - headline: info.headline, - description: info.description, - instruction: info.instruction, - web: info.web, - contact: info.contact, - parameter: info.parameter, - eventCode: info.eventCode, - resources: resourcesJson.map((res: any) => { - return { - resourceDesc: res.resourceDesc, - mimeType: res.mimeType || res['mimeType'], - size: res.size, - uri: res.uri, - derefUri: res.derefUri, - digest: res.digest, - url: res.uri || res.resourceDesc || '', - } - }), - areas: areasJson.map((area: any) => ({ - areaDesc: area.areaDesc || '', - polygon: area.polygon, - circle: area.circle, - geocode: area.geocode, - altitude: area.altitude, - ceiling: area.ceiling, - })), - } - }) - - alerts.push({ - identifier: a.identifier || '', - sender: a.sender || '', - sent: a.sent || '', - status: a.status, - msgType: a.msgType, - source: a.source, - scope: a.scope, - restriction: a.restriction, - addresses: a.addresses, - code: a.code, - note: a.note, - references: a.references, - incidents: a.incidents, - infos, - }) - }) - - return alerts -} export function getNearestExit(tags: string[]): string | undefined { for (const tag of tags) { diff --git a/edge-apps/cap-alerting/index.test.ts b/edge-apps/cap-alerting/src/parser.test.ts similarity index 98% rename from edge-apps/cap-alerting/index.test.ts rename to edge-apps/cap-alerting/src/parser.test.ts index 0836a87ef..5b50e56f3 100644 --- a/edge-apps/cap-alerting/index.test.ts +++ b/edge-apps/cap-alerting/src/parser.test.ts @@ -16,7 +16,8 @@ mock.module('@screenly/edge-apps', () => ({ getTags: () => mockGetTags(), })) -import { parseCap, getNearestExit } from './src/main' +import { getNearestExit } from './main' +import { parseCap } from './parser' import { XMLParser } from 'fast-xml-parser' describe('CAP v1.2 Parser', () => { @@ -82,7 +83,9 @@ describe('CAP v1.2 Parser', () => { const alerts = parseCap(xml) expect(alerts[0].source).toBe('Weather Service') expect(alerts[0].note).toBe('This is a test note') - expect(alerts[0].references).toBe('KSTO@NWS.NOAA.GOV,KSTO1055887200,2003-06-17T14:00:00-07:00') + expect(alerts[0].references).toBe( + 'KSTO@NWS.NOAA.GOV,KSTO1055887200,2003-06-17T14:00:00-07:00', + ) expect(alerts[0].incidents).toBe('incident1,incident2') }) @@ -232,8 +235,12 @@ describe('CAP v1.2 Parser', () => { expect(info.expires).toBe('2024-01-15T22:00:00-00:00') expect(info.senderName).toBe('National Weather Service') expect(info.headline).toBe('Wildfire Warning Issued') - expect(info.description).toBe('A rapidly spreading wildfire has been detected.') - expect(info.instruction).toBe('Evacuate immediately to designated shelters.') + expect(info.description).toBe( + 'A rapidly spreading wildfire has been detected.', + ) + expect(info.instruction).toBe( + 'Evacuate immediately to designated shelters.', + ) expect(info.web).toBe('http://www.example.com/wildfire') expect(info.contact).toBe('1-800-EMERGENCY') }) @@ -401,7 +408,13 @@ describe('CAP v1.2 Parser', () => { }) describe('Certainty Values', () => { - const certainties = ['Observed', 'Likely', 'Possible', 'Unlikely', 'Unknown'] + const certainties = [ + 'Observed', + 'Likely', + 'Possible', + 'Unlikely', + 'Unknown', + ] certainties.forEach((certainty) => { it(`should parse certainty: ${certainty}`, () => { @@ -554,7 +567,9 @@ describe('CAP v1.2 Parser', () => { const alerts = parseCap(xml) expect(alerts[0].infos[0].areas).toHaveLength(1) - expect(alerts[0].infos[0].areas[0].areaDesc).toBe('Downtown Metropolitan Area') + expect(alerts[0].infos[0].areas[0].areaDesc).toBe( + 'Downtown Metropolitan Area', + ) }) it('should parse area with polygon', () => { @@ -582,7 +597,9 @@ describe('CAP v1.2 Parser', () => { const alerts = parseCap(xml) const area = alerts[0].infos[0].areas[0] expect(area.areaDesc).toBe('Storm Path') - expect(area.polygon).toBe('38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14') + expect(area.polygon).toBe( + '38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14', + ) }) it('should parse area with multiple polygons', () => { @@ -610,7 +627,9 @@ describe('CAP v1.2 Parser', () => { const alerts = parseCap(xml) const area = alerts[0].infos[0].areas[0] - const polygons = Array.isArray(area.polygon) ? area.polygon : [area.polygon] + const polygons = Array.isArray(area.polygon) + ? area.polygon + : [area.polygon] expect(polygons).toHaveLength(2) }) @@ -856,7 +875,10 @@ describe('CAP v1.2 Parser', () => { ` - const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' }) + const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: '@_', + }) const json: any = parser.parse(xml) const alerts = json.feed?.alert ? Array.isArray(json.feed.alert) @@ -1855,4 +1877,3 @@ describe('CAP v1.2 Parser', () => { }) }) }) - diff --git a/edge-apps/cap-alerting/src/parser.ts b/edge-apps/cap-alerting/src/parser.ts new file mode 100644 index 000000000..a3e43e426 --- /dev/null +++ b/edge-apps/cap-alerting/src/parser.ts @@ -0,0 +1,94 @@ +import { CAPInfo, CAPAlert } from './types/cap.js' +import { XMLParser } from 'fast-xml-parser' + +export function parseCap(xml: string): CAPAlert[] { + const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: '@_', + }) + const json: any = parser.parse(xml) + const alertsJson = json.alert + ? Array.isArray(json.alert) + ? json.alert + : [json.alert] + : [] + + const alerts: CAPAlert[] = [] + + alertsJson.forEach((a: any) => { + const infosJson = a.info ? (Array.isArray(a.info) ? a.info : [a.info]) : [] + + const infos: CAPInfo[] = infosJson.map((info: any) => { + const resourcesJson = info.resource + ? Array.isArray(info.resource) + ? info.resource + : [info.resource] + : [] + const areasJson = info.area + ? Array.isArray(info.area) + ? info.area + : [info.area] + : [] + + return { + language: info.language || '', + category: info.category, + event: info.event, + responseType: info.responseType, + urgency: info.urgency, + severity: info.severity, + certainty: info.certainty, + audience: info.audience, + effective: info.effective, + onset: info.onset, + expires: info.expires, + senderName: info.senderName, + headline: info.headline, + description: info.description, + instruction: info.instruction, + web: info.web, + contact: info.contact, + parameter: info.parameter, + eventCode: info.eventCode, + resources: resourcesJson.map((res: any) => { + return { + resourceDesc: res.resourceDesc, + mimeType: res.mimeType || res['mimeType'], + size: res.size, + uri: res.uri, + derefUri: res.derefUri, + digest: res.digest, + url: res.uri || res.resourceDesc || '', + } + }), + areas: areasJson.map((area: any) => ({ + areaDesc: area.areaDesc || '', + polygon: area.polygon, + circle: area.circle, + geocode: area.geocode, + altitude: area.altitude, + ceiling: area.ceiling, + })), + } + }) + + alerts.push({ + identifier: a.identifier || '', + sender: a.sender || '', + sent: a.sent || '', + status: a.status, + msgType: a.msgType, + source: a.source, + scope: a.scope, + restriction: a.restriction, + addresses: a.addresses, + code: a.code, + note: a.note, + references: a.references, + incidents: a.incidents, + infos, + }) + }) + + return alerts +} From 86fb6b69b78ba067e078c7f3403f1742aa51963a Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 13:26:41 -0800 Subject: [PATCH 19/26] chore(cap-alerting): use `getSettingWithDefault` in `main.ts` --- edge-apps/cap-alerting/src/main.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index fc9b010c9..a5635f7ea 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -5,6 +5,7 @@ import { getTags, getSettings, getCorsProxyUrl, + getSettingWithDefault, } from '@screenly/edge-apps' import { CAPAlert, CAPMode } from './types/cap' @@ -290,13 +291,13 @@ export async function startApp(): Promise { : {} } - const feedUrl: string = (settings.cap_feed_url as string) || '' - const interval = parseInt((settings.refresh_interval as string) || '5', 10) - const lang = (settings.language as string) || 'en' - const maxAlerts = parseInt((settings.max_alerts as string) || '3', 10) - const playAudio = ((settings.audio_alert as string) || 'false') === 'true' - const offlineMode = ((settings.offline_mode as string) || 'false') === 'true' - const mode = (settings.mode as CAPMode) || 'production' + const feedUrl = getSettingWithDefault('cap_feed_url', '') + const interval = getSettingWithDefault('refresh_interval', 5) + const lang = getSettingWithDefault('language', 'en') + const maxAlerts = getSettingWithDefault('max_alerts', 3) + const playAudio = getSettingWithDefault('audio_alert', false) + const offlineMode = getSettingWithDefault('offline_mode', false) + const mode = getSettingWithDefault('mode', 'production') const testMode = mode === 'test' const demoMode = mode === 'demo' From ac1470691604d7cc05e3963065a60254986bf535 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 15:43:37 -0800 Subject: [PATCH 20/26] refactor: extract utility functions and improve settings handling - Use getSettingWithDefault for all settings parsing in main.ts - Extract getNearestExit, highlightKeywords, splitIntoSentences, and proxyUrl to separate files - Create render.ts for rendering utilities - Create utils.ts for shared utility functions - Add abbreviation support to splitIntoSentences for better sentence parsing - Move getNearestExit tests to utils.test.ts - Add comprehensive unit tests for splitIntoSentences and proxyUrl - Update package.json lint script to use wildcard pattern for TypeScript files - Fix type-check errors by replacing replaceAll with replace --- edge-apps/cap-alerting/package.json | 2 +- edge-apps/cap-alerting/src/main.ts | 65 +--------- edge-apps/cap-alerting/src/parser.test.ts | 39 ------ edge-apps/cap-alerting/src/render.ts | 33 +++++ edge-apps/cap-alerting/src/utils.test.ts | 149 ++++++++++++++++++++++ edge-apps/cap-alerting/src/utils.ts | 74 +++++++++++ 6 files changed, 259 insertions(+), 103 deletions(-) create mode 100644 edge-apps/cap-alerting/src/render.ts create mode 100644 edge-apps/cap-alerting/src/utils.test.ts create mode 100644 edge-apps/cap-alerting/src/utils.ts diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 88aae79d5..8892dc065 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -19,7 +19,7 @@ "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", - "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck src/main.ts src/parser.ts src/parser.test.ts src/fetcher.ts src/fetcher.test.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck src/**/*.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index a5635f7ea..877f87149 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -4,75 +4,14 @@ import { getMetadata, getTags, getSettings, - getCorsProxyUrl, getSettingWithDefault, } from '@screenly/edge-apps' import { CAPAlert, CAPMode } from './types/cap' import { parseCap } from './parser' import { CAPFetcher } from './fetcher' - -export function getNearestExit(tags: string[]): string | undefined { - for (const tag of tags) { - const lower = tag.toLowerCase() - if (lower.startsWith('exit:')) { - return tag.slice(5).trim() - } - if (lower.startsWith('exit-')) { - return tag.slice(5).trim() - } - } - return undefined -} - -function highlightKeywords(text: string): string { - const keywords = [ - 'DO NOT', - "DON'T", - 'IMMEDIATELY', - 'IMMEDIATE', - 'NOW', - 'MOVE TO', - 'EVACUATE', - 'CALL', - 'WARNING', - 'DANGER', - 'SHELTER', - 'TAKE COVER', - 'AVOID', - 'STAY', - 'SEEK', - 'TURN AROUND', - 'GET TO', - 'LEAVE', - ] - - let result = text - keywords.forEach((keyword) => { - const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') - result = result.replace( - regex, - '$1', - ) - }) - - return result -} - -function splitIntoSentences(text: string): string[] { - return text - .split(/(?<=[.!?])\s+/) - .map((s) => s.trim()) - .filter((s) => s.length > 0) -} - -function proxyUrl(url: string): string { - if (url && url.match(/^https?:/)) { - const cors = getCorsProxyUrl() - return `${cors}/${url}` - } - return url -} +import { getNearestExit, splitIntoSentences, proxyUrl } from './utils' +import { highlightKeywords } from './render' function renderAlerts( alerts: CAPAlert[], diff --git a/edge-apps/cap-alerting/src/parser.test.ts b/edge-apps/cap-alerting/src/parser.test.ts index 5b50e56f3..bfec6c6f4 100644 --- a/edge-apps/cap-alerting/src/parser.test.ts +++ b/edge-apps/cap-alerting/src/parser.test.ts @@ -16,7 +16,6 @@ mock.module('@screenly/edge-apps', () => ({ getTags: () => mockGetTags(), })) -import { getNearestExit } from './main' import { parseCap } from './parser' import { XMLParser } from 'fast-xml-parser' @@ -891,44 +890,6 @@ describe('CAP v1.2 Parser', () => { }) }) - describe('Nearest Exit Functionality', () => { - it('should extract exit from tag with colon', () => { - const tags = ['exit:North Lobby', 'location:Building A'] - const exit = getNearestExit(tags) - expect(exit).toBe('North Lobby') - }) - - it('should extract exit from tag with dash', () => { - const tags = ['exit-South Stairwell', 'floor:3'] - const exit = getNearestExit(tags) - expect(exit).toBe('South Stairwell') - }) - - it('should be case-insensitive', () => { - const tags = ['EXIT:West Door', 'Exit-East Door'] - const exit = getNearestExit(tags) - expect(exit).toBe('West Door') - }) - - it('should return first exit tag found', () => { - const tags = ['exit:First Exit', 'exit:Second Exit'] - const exit = getNearestExit(tags) - expect(exit).toBe('First Exit') - }) - - it('should return undefined if no exit tag found', () => { - const tags = ['location:Building A', 'floor:3'] - const exit = getNearestExit(tags) - expect(exit).toBeUndefined() - }) - - it('should trim whitespace from exit description', () => { - const tags = ['exit: Main Entrance '] - const exit = getNearestExit(tags) - expect(exit).toBe('Main Entrance') - }) - }) - describe('Edge Cases', () => { it('should handle empty alert', () => { const xml = ` diff --git a/edge-apps/cap-alerting/src/render.ts b/edge-apps/cap-alerting/src/render.ts new file mode 100644 index 000000000..bb1e487dc --- /dev/null +++ b/edge-apps/cap-alerting/src/render.ts @@ -0,0 +1,33 @@ +export function highlightKeywords(text: string): string { + const keywords = [ + 'DO NOT', + "DON'T", + 'IMMEDIATELY', + 'IMMEDIATE', + 'NOW', + 'MOVE TO', + 'EVACUATE', + 'CALL', + 'WARNING', + 'DANGER', + 'SHELTER', + 'TAKE COVER', + 'AVOID', + 'STAY', + 'SEEK', + 'TURN AROUND', + 'GET TO', + 'LEAVE', + ] + + let result = text + keywords.forEach((keyword) => { + const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') + result = result.replace( + regex, + '$1', + ) + }) + + return result +} diff --git a/edge-apps/cap-alerting/src/utils.test.ts b/edge-apps/cap-alerting/src/utils.test.ts new file mode 100644 index 000000000..349150cd0 --- /dev/null +++ b/edge-apps/cap-alerting/src/utils.test.ts @@ -0,0 +1,149 @@ +import { describe, it, expect, mock } from 'bun:test' + +// Mock the @screenly/edge-apps module +mock.module('@screenly/edge-apps', () => ({ + getCorsProxyUrl: () => 'http://localhost:8080', + isAnywhereScreen: () => false, +})) + +import { getNearestExit, splitIntoSentences, proxyUrl } from './utils' + +describe('Utils', () => { + describe('Nearest Exit Functionality', () => { + it('should extract exit from tag with colon', () => { + const tags = ['exit:North Lobby', 'location:Building A'] + const exit = getNearestExit(tags) + expect(exit).toBe('North Lobby') + }) + + it('should extract exit from tag with dash', () => { + const tags = ['exit-South Stairwell', 'floor:3'] + const exit = getNearestExit(tags) + expect(exit).toBe('South Stairwell') + }) + + it('should be case-insensitive', () => { + const tags = ['EXIT:West Door', 'Exit-East Door'] + const exit = getNearestExit(tags) + expect(exit).toBe('West Door') + }) + + it('should return first exit tag found', () => { + const tags = ['exit:First Exit', 'exit:Second Exit'] + const exit = getNearestExit(tags) + expect(exit).toBe('First Exit') + }) + + it('should return undefined if no exit tag found', () => { + const tags = ['location:Building A', 'floor:3'] + const exit = getNearestExit(tags) + expect(exit).toBeUndefined() + }) + + it('should trim whitespace from exit description', () => { + const tags = ['exit: Main Entrance '] + const exit = getNearestExit(tags) + expect(exit).toBe('Main Entrance') + }) + }) + + describe('Split Into Sentences', () => { + it('should split text on periods', () => { + const text = 'First sentence. Second sentence.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First sentence.', 'Second sentence.']) + }) + + it('should split text on exclamation marks', () => { + const text = 'First sentence! Second sentence!' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First sentence!', 'Second sentence!']) + }) + + it('should split text on question marks', () => { + const text = 'First question? Second question?' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First question?', 'Second question?']) + }) + + it('should split on mixed punctuation', () => { + const text = 'Sentence one. Question two? Exclamation three!' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual([ + 'Sentence one.', + 'Question two?', + 'Exclamation three!', + ]) + }) + + it('should handle single sentence without punctuation', () => { + const text = 'Single sentence' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['Single sentence']) + }) + + it('should handle empty string', () => { + const text = '' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual([]) + }) + + it('should handle string with only whitespace', () => { + const text = ' ' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual([]) + }) + + it('should trim whitespace from sentences', () => { + const text = ' First sentence. Second sentence. ' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First sentence.', 'Second sentence.']) + }) + + it('should handle multiple spaces between sentences', () => { + const text = 'First. Second. Third.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First.', 'Second.', 'Third.']) + }) + + it('should handle newlines and tabs', () => { + const text = 'First.\n\nSecond.\tThird.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First.', 'Second.', 'Third.']) + }) + + it('should preserve periods in abbreviations within a sentence', () => { + const text = 'Dr. Smith said hello. Then he left.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['Dr. Smith said hello.', 'Then he left.']) + }) + + it('should handle trailing punctuation', () => { + const text = 'Only one sentence.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['Only one sentence.']) + }) + }) + + describe('Proxy URL', () => { + it('should proxy URLs with http protocol', () => { + const url = 'http://example.com/image.png' + const result = proxyUrl(url) + expect(result).toBe('http://localhost:8080/http://example.com/image.png') + }) + + it('should proxy URLs with https protocol', () => { + const url = 'https://example.com/video.mp4' + const result = proxyUrl(url) + expect(result).toBe('http://localhost:8080/https://example.com/video.mp4') + }) + + it('should proxy a complete link with path and query parameters', () => { + const url = 'https://api.example.com/v1/media?id=12345&format=json' + const result = proxyUrl(url) + expect(result).toBe( + 'http://localhost:8080/https://api.example.com/v1/media?id=12345&format=json', + ) + }) + }) +}) diff --git a/edge-apps/cap-alerting/src/utils.ts b/edge-apps/cap-alerting/src/utils.ts new file mode 100644 index 000000000..5aec087a5 --- /dev/null +++ b/edge-apps/cap-alerting/src/utils.ts @@ -0,0 +1,74 @@ +import { getCorsProxyUrl } from '@screenly/edge-apps' + +export function getNearestExit(tags: string[]): string | undefined { + for (const tag of tags) { + const lower = tag.toLowerCase() + if (lower.startsWith('exit:')) { + return tag.slice(5).trim() + } + if (lower.startsWith('exit-')) { + return tag.slice(5).trim() + } + } + return undefined +} + +export function splitIntoSentences(text: string): string[] { + // Replace common abbreviations with placeholders to protect them from splitting + let processed = text + const abbreviations = [ + 'Dr.', + 'Mr.', + 'Mrs.', + 'Ms.', + 'Prof.', + 'Sr.', + 'Jr.', + 'Inc.', + 'Ltd.', + 'Corp.', + 'Co.', + 'St.', + 'Ave.', + 'Blvd.', + 'etc.', + 'vs.', + 'e.g.', + 'i.e.', + 'U.S.', + 'U.K.', + ] + + const placeholders = new Map() + abbreviations.forEach((abbr, index) => { + const placeholder = `__ABBR_${index}__` + placeholders.set(placeholder, abbr) + processed = processed.replace( + new RegExp(abbr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), + placeholder, + ) + }) + + // Split sentences + const sentences = processed + .split(/(?<=[.!?])\s+/) + .map((s) => s.trim()) + .filter((s) => s.length > 0) + + // Restore abbreviations + return sentences.map((s) => { + let result = s + placeholders.forEach((abbr, placeholder) => { + result = result.replace(new RegExp(placeholder, 'g'), abbr) + }) + return result + }) +} + +export function proxyUrl(url: string): string { + if (url && url.match(/^https?:/)) { + const cors = getCorsProxyUrl() + return `${cors}/${url}` + } + return url +} From 9f4a6595f0d352f1cf30c2775ea4cc949d34956c Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 15:43:37 -0800 Subject: [PATCH 21/26] refactor: rename audio_alert setting to mute_sound with inverted logic - Rename audio_alert to mute_sound in both screenly.yml and screenly_qc.yml - Update help_text to use boolean type structure for toggle switch display - Invert logic in main.ts: playAudio = !getSettingWithDefault(mute_sound, false) --- edge-apps/cap-alerting/package.json | 2 +- edge-apps/cap-alerting/screenly.yml | 16 ++- edge-apps/cap-alerting/screenly_qc.yml | 16 ++- edge-apps/cap-alerting/src/main.ts | 75 +---------- edge-apps/cap-alerting/src/parser.test.ts | 39 ------ edge-apps/cap-alerting/src/render.ts | 33 +++++ edge-apps/cap-alerting/src/utils.test.ts | 149 ++++++++++++++++++++++ edge-apps/cap-alerting/src/utils.ts | 74 +++++++++++ 8 files changed, 280 insertions(+), 124 deletions(-) create mode 100644 edge-apps/cap-alerting/src/render.ts create mode 100644 edge-apps/cap-alerting/src/utils.test.ts create mode 100644 edge-apps/cap-alerting/src/utils.ts diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 88aae79d5..8892dc065 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -19,7 +19,7 @@ "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", - "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck src/main.ts src/parser.ts src/parser.test.ts src/fetcher.ts src/fetcher.test.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck src/**/*.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml index eeb30e964..488670f55 100644 --- a/edge-apps/cap-alerting/screenly.yml +++ b/edge-apps/cap-alerting/screenly.yml @@ -7,12 +7,6 @@ entrypoint: type: file ready_signal: true settings: - audio_alert: - type: string - default_value: 'false' - title: Play Audio Alerts - optional: true - help_text: Play audio from CAP resources when available (true/false). cap_feed_url: type: string default_value: '' @@ -48,6 +42,16 @@ settings: value: test type: select schema_version: 1 + mute_sound: + type: string + default_value: 'false' + title: Mute Sound + optional: true + help_text: + properties: + help_text: Mute audio alerts from CAP resources. Not available for Screenly Anywhere. + type: boolean + schema_version: 1 offline_mode: type: string default_value: 'false' diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index eeb30e964..488670f55 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -7,12 +7,6 @@ entrypoint: type: file ready_signal: true settings: - audio_alert: - type: string - default_value: 'false' - title: Play Audio Alerts - optional: true - help_text: Play audio from CAP resources when available (true/false). cap_feed_url: type: string default_value: '' @@ -48,6 +42,16 @@ settings: value: test type: select schema_version: 1 + mute_sound: + type: string + default_value: 'false' + title: Mute Sound + optional: true + help_text: + properties: + help_text: Mute audio alerts from CAP resources. Not available for Screenly Anywhere. + type: boolean + schema_version: 1 offline_mode: type: string default_value: 'false' diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index a5635f7ea..116ccfa0d 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -4,75 +4,14 @@ import { getMetadata, getTags, getSettings, - getCorsProxyUrl, getSettingWithDefault, } from '@screenly/edge-apps' import { CAPAlert, CAPMode } from './types/cap' import { parseCap } from './parser' import { CAPFetcher } from './fetcher' - -export function getNearestExit(tags: string[]): string | undefined { - for (const tag of tags) { - const lower = tag.toLowerCase() - if (lower.startsWith('exit:')) { - return tag.slice(5).trim() - } - if (lower.startsWith('exit-')) { - return tag.slice(5).trim() - } - } - return undefined -} - -function highlightKeywords(text: string): string { - const keywords = [ - 'DO NOT', - "DON'T", - 'IMMEDIATELY', - 'IMMEDIATE', - 'NOW', - 'MOVE TO', - 'EVACUATE', - 'CALL', - 'WARNING', - 'DANGER', - 'SHELTER', - 'TAKE COVER', - 'AVOID', - 'STAY', - 'SEEK', - 'TURN AROUND', - 'GET TO', - 'LEAVE', - ] - - let result = text - keywords.forEach((keyword) => { - const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') - result = result.replace( - regex, - '$1', - ) - }) - - return result -} - -function splitIntoSentences(text: string): string[] { - return text - .split(/(?<=[.!?])\s+/) - .map((s) => s.trim()) - .filter((s) => s.length > 0) -} - -function proxyUrl(url: string): string { - if (url && url.match(/^https?:/)) { - const cors = getCorsProxyUrl() - return `${cors}/${url}` - } - return url -} +import { getNearestExit, splitIntoSentences, proxyUrl } from './utils' +import { highlightKeywords } from './render' function renderAlerts( alerts: CAPAlert[], @@ -295,20 +234,12 @@ export async function startApp(): Promise { const interval = getSettingWithDefault('refresh_interval', 5) const lang = getSettingWithDefault('language', 'en') const maxAlerts = getSettingWithDefault('max_alerts', 3) - const playAudio = getSettingWithDefault('audio_alert', false) + const playAudio = !getSettingWithDefault('mute_sound', false) const offlineMode = getSettingWithDefault('offline_mode', false) const mode = getSettingWithDefault('mode', 'production') const testMode = mode === 'test' const demoMode = mode === 'demo' - console.log('CAP Settings:', { - playAudio, - audio_alert: settings.audio_alert, - demoMode, - testMode, - maxAlerts, - }) - const tags: string[] = getTags() const nearestExit = getNearestExit(tags) diff --git a/edge-apps/cap-alerting/src/parser.test.ts b/edge-apps/cap-alerting/src/parser.test.ts index 5b50e56f3..bfec6c6f4 100644 --- a/edge-apps/cap-alerting/src/parser.test.ts +++ b/edge-apps/cap-alerting/src/parser.test.ts @@ -16,7 +16,6 @@ mock.module('@screenly/edge-apps', () => ({ getTags: () => mockGetTags(), })) -import { getNearestExit } from './main' import { parseCap } from './parser' import { XMLParser } from 'fast-xml-parser' @@ -891,44 +890,6 @@ describe('CAP v1.2 Parser', () => { }) }) - describe('Nearest Exit Functionality', () => { - it('should extract exit from tag with colon', () => { - const tags = ['exit:North Lobby', 'location:Building A'] - const exit = getNearestExit(tags) - expect(exit).toBe('North Lobby') - }) - - it('should extract exit from tag with dash', () => { - const tags = ['exit-South Stairwell', 'floor:3'] - const exit = getNearestExit(tags) - expect(exit).toBe('South Stairwell') - }) - - it('should be case-insensitive', () => { - const tags = ['EXIT:West Door', 'Exit-East Door'] - const exit = getNearestExit(tags) - expect(exit).toBe('West Door') - }) - - it('should return first exit tag found', () => { - const tags = ['exit:First Exit', 'exit:Second Exit'] - const exit = getNearestExit(tags) - expect(exit).toBe('First Exit') - }) - - it('should return undefined if no exit tag found', () => { - const tags = ['location:Building A', 'floor:3'] - const exit = getNearestExit(tags) - expect(exit).toBeUndefined() - }) - - it('should trim whitespace from exit description', () => { - const tags = ['exit: Main Entrance '] - const exit = getNearestExit(tags) - expect(exit).toBe('Main Entrance') - }) - }) - describe('Edge Cases', () => { it('should handle empty alert', () => { const xml = ` diff --git a/edge-apps/cap-alerting/src/render.ts b/edge-apps/cap-alerting/src/render.ts new file mode 100644 index 000000000..bb1e487dc --- /dev/null +++ b/edge-apps/cap-alerting/src/render.ts @@ -0,0 +1,33 @@ +export function highlightKeywords(text: string): string { + const keywords = [ + 'DO NOT', + "DON'T", + 'IMMEDIATELY', + 'IMMEDIATE', + 'NOW', + 'MOVE TO', + 'EVACUATE', + 'CALL', + 'WARNING', + 'DANGER', + 'SHELTER', + 'TAKE COVER', + 'AVOID', + 'STAY', + 'SEEK', + 'TURN AROUND', + 'GET TO', + 'LEAVE', + ] + + let result = text + keywords.forEach((keyword) => { + const regex = new RegExp(`\\b(${keyword})\\b`, 'gi') + result = result.replace( + regex, + '$1', + ) + }) + + return result +} diff --git a/edge-apps/cap-alerting/src/utils.test.ts b/edge-apps/cap-alerting/src/utils.test.ts new file mode 100644 index 000000000..349150cd0 --- /dev/null +++ b/edge-apps/cap-alerting/src/utils.test.ts @@ -0,0 +1,149 @@ +import { describe, it, expect, mock } from 'bun:test' + +// Mock the @screenly/edge-apps module +mock.module('@screenly/edge-apps', () => ({ + getCorsProxyUrl: () => 'http://localhost:8080', + isAnywhereScreen: () => false, +})) + +import { getNearestExit, splitIntoSentences, proxyUrl } from './utils' + +describe('Utils', () => { + describe('Nearest Exit Functionality', () => { + it('should extract exit from tag with colon', () => { + const tags = ['exit:North Lobby', 'location:Building A'] + const exit = getNearestExit(tags) + expect(exit).toBe('North Lobby') + }) + + it('should extract exit from tag with dash', () => { + const tags = ['exit-South Stairwell', 'floor:3'] + const exit = getNearestExit(tags) + expect(exit).toBe('South Stairwell') + }) + + it('should be case-insensitive', () => { + const tags = ['EXIT:West Door', 'Exit-East Door'] + const exit = getNearestExit(tags) + expect(exit).toBe('West Door') + }) + + it('should return first exit tag found', () => { + const tags = ['exit:First Exit', 'exit:Second Exit'] + const exit = getNearestExit(tags) + expect(exit).toBe('First Exit') + }) + + it('should return undefined if no exit tag found', () => { + const tags = ['location:Building A', 'floor:3'] + const exit = getNearestExit(tags) + expect(exit).toBeUndefined() + }) + + it('should trim whitespace from exit description', () => { + const tags = ['exit: Main Entrance '] + const exit = getNearestExit(tags) + expect(exit).toBe('Main Entrance') + }) + }) + + describe('Split Into Sentences', () => { + it('should split text on periods', () => { + const text = 'First sentence. Second sentence.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First sentence.', 'Second sentence.']) + }) + + it('should split text on exclamation marks', () => { + const text = 'First sentence! Second sentence!' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First sentence!', 'Second sentence!']) + }) + + it('should split text on question marks', () => { + const text = 'First question? Second question?' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First question?', 'Second question?']) + }) + + it('should split on mixed punctuation', () => { + const text = 'Sentence one. Question two? Exclamation three!' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual([ + 'Sentence one.', + 'Question two?', + 'Exclamation three!', + ]) + }) + + it('should handle single sentence without punctuation', () => { + const text = 'Single sentence' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['Single sentence']) + }) + + it('should handle empty string', () => { + const text = '' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual([]) + }) + + it('should handle string with only whitespace', () => { + const text = ' ' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual([]) + }) + + it('should trim whitespace from sentences', () => { + const text = ' First sentence. Second sentence. ' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First sentence.', 'Second sentence.']) + }) + + it('should handle multiple spaces between sentences', () => { + const text = 'First. Second. Third.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First.', 'Second.', 'Third.']) + }) + + it('should handle newlines and tabs', () => { + const text = 'First.\n\nSecond.\tThird.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['First.', 'Second.', 'Third.']) + }) + + it('should preserve periods in abbreviations within a sentence', () => { + const text = 'Dr. Smith said hello. Then he left.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['Dr. Smith said hello.', 'Then he left.']) + }) + + it('should handle trailing punctuation', () => { + const text = 'Only one sentence.' + const sentences = splitIntoSentences(text) + expect(sentences).toEqual(['Only one sentence.']) + }) + }) + + describe('Proxy URL', () => { + it('should proxy URLs with http protocol', () => { + const url = 'http://example.com/image.png' + const result = proxyUrl(url) + expect(result).toBe('http://localhost:8080/http://example.com/image.png') + }) + + it('should proxy URLs with https protocol', () => { + const url = 'https://example.com/video.mp4' + const result = proxyUrl(url) + expect(result).toBe('http://localhost:8080/https://example.com/video.mp4') + }) + + it('should proxy a complete link with path and query parameters', () => { + const url = 'https://api.example.com/v1/media?id=12345&format=json' + const result = proxyUrl(url) + expect(result).toBe( + 'http://localhost:8080/https://api.example.com/v1/media?id=12345&format=json', + ) + }) + }) +}) diff --git a/edge-apps/cap-alerting/src/utils.ts b/edge-apps/cap-alerting/src/utils.ts new file mode 100644 index 000000000..5aec087a5 --- /dev/null +++ b/edge-apps/cap-alerting/src/utils.ts @@ -0,0 +1,74 @@ +import { getCorsProxyUrl } from '@screenly/edge-apps' + +export function getNearestExit(tags: string[]): string | undefined { + for (const tag of tags) { + const lower = tag.toLowerCase() + if (lower.startsWith('exit:')) { + return tag.slice(5).trim() + } + if (lower.startsWith('exit-')) { + return tag.slice(5).trim() + } + } + return undefined +} + +export function splitIntoSentences(text: string): string[] { + // Replace common abbreviations with placeholders to protect them from splitting + let processed = text + const abbreviations = [ + 'Dr.', + 'Mr.', + 'Mrs.', + 'Ms.', + 'Prof.', + 'Sr.', + 'Jr.', + 'Inc.', + 'Ltd.', + 'Corp.', + 'Co.', + 'St.', + 'Ave.', + 'Blvd.', + 'etc.', + 'vs.', + 'e.g.', + 'i.e.', + 'U.S.', + 'U.K.', + ] + + const placeholders = new Map() + abbreviations.forEach((abbr, index) => { + const placeholder = `__ABBR_${index}__` + placeholders.set(placeholder, abbr) + processed = processed.replace( + new RegExp(abbr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), + placeholder, + ) + }) + + // Split sentences + const sentences = processed + .split(/(?<=[.!?])\s+/) + .map((s) => s.trim()) + .filter((s) => s.length > 0) + + // Restore abbreviations + return sentences.map((s) => { + let result = s + placeholders.forEach((abbr, placeholder) => { + result = result.replace(new RegExp(placeholder, 'g'), abbr) + }) + return result + }) +} + +export function proxyUrl(url: string): string { + if (url && url.match(/^https?:/)) { + const cors = getCorsProxyUrl() + return `${cors}/${url}` + } + return url +} From fa3f2712efc22880bb3aa5eb29a8d91f762cb031 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sat, 27 Dec 2025 18:44:02 -0800 Subject: [PATCH 22/26] refactor: standardize manifest help_text structure - Update all settings to use structured help_text format - Add type information (string, number, boolean, select) to all settings - Remove (true/false) notation from boolean settings - Ensures consistent UI presentation for all setting types --- edge-apps/cap-alerting/screenly.yml | 30 +++++++++++++++++++++----- edge-apps/cap-alerting/screenly_qc.yml | 30 +++++++++++++++++++++----- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml index 488670f55..9a5aa5660 100644 --- a/edge-apps/cap-alerting/screenly.yml +++ b/edge-apps/cap-alerting/screenly.yml @@ -12,19 +12,31 @@ settings: default_value: '' title: CAP Feed URL optional: true - help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. + help_text: + properties: + help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. + type: string + schema_version: 1 language: type: string default_value: en title: Default Language optional: true - help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). + help_text: + properties: + help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). + type: string + schema_version: 1 max_alerts: type: string default_value: '3' title: Maximum Alerts optional: true - help_text: Maximum number of alerts to display simultaneously. + help_text: + properties: + help_text: Maximum number of alerts to display simultaneously. + type: number + schema_version: 1 mode: type: string default_value: production @@ -57,10 +69,18 @@ settings: default_value: 'false' title: Offline Mode optional: true - help_text: When enabled, avoid network fetches and use cached data (true/false). + help_text: + properties: + help_text: When enabled, avoid network fetches and use cached data. + type: boolean + schema_version: 1 refresh_interval: type: string default_value: '5' title: Refresh Interval (minutes) optional: true - help_text: Time in minutes between feed updates. + help_text: + properties: + help_text: Time in minutes between feed updates. + type: number + schema_version: 1 diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index 488670f55..9a5aa5660 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -12,19 +12,31 @@ settings: default_value: '' title: CAP Feed URL optional: true - help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. + help_text: + properties: + help_text: URL or relative path to a CAP XML feed. Leave blank to use demo mode. + type: string + schema_version: 1 language: type: string default_value: en title: Default Language optional: true - help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). + help_text: + properties: + help_text: Choose the preferred language when multiple info blocks exist (e.g., en, es, fr). + type: string + schema_version: 1 max_alerts: type: string default_value: '3' title: Maximum Alerts optional: true - help_text: Maximum number of alerts to display simultaneously. + help_text: + properties: + help_text: Maximum number of alerts to display simultaneously. + type: number + schema_version: 1 mode: type: string default_value: production @@ -57,10 +69,18 @@ settings: default_value: 'false' title: Offline Mode optional: true - help_text: When enabled, avoid network fetches and use cached data (true/false). + help_text: + properties: + help_text: When enabled, avoid network fetches and use cached data. + type: boolean + schema_version: 1 refresh_interval: type: string default_value: '5' title: Refresh Interval (minutes) optional: true - help_text: Time in minutes between feed updates. + help_text: + properties: + help_text: Time in minutes between feed updates. + type: number + schema_version: 1 From 721f048495a4a4d571643257df2f8b2ccd3c8361 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sun, 28 Dec 2025 13:35:43 -0800 Subject: [PATCH 23/26] chore: start a local CORS proxy server when `bun run dev` runs --- edge-apps/cap-alerting/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 8892dc065..6e4062fc6 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -6,7 +6,8 @@ "scripts": { "generate-mock-data": "screenly edge-app run --generate-mock-data", "predev": "bun run generate-mock-data", - "dev": "run-p build:css:dev build:js:dev edge-app-server", + "dev": "run-p build:css:dev build:js:dev edge-app-server cors-proxy-server", + "cors-proxy-server": "bun ../blueprint/scripts/cors-proxy-server.ts", "edge-app-server": "screenly edge-app run", "build": "run-p build:css:prod build:js:prod", "build:css:common": "bunx @tailwindcss/cli -i ./src/input.css -o ./static/style.css", From 2d4da432290a352631700fee581e28c6a9142fed Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Sun, 28 Dec 2025 15:24:50 -0800 Subject: [PATCH 24/26] chore(cap-alerting): set the default max alters to infinity --- edge-apps/cap-alerting/screenly.yml | 2 +- edge-apps/cap-alerting/screenly_qc.yml | 2 +- edge-apps/cap-alerting/src/main.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/edge-apps/cap-alerting/screenly.yml b/edge-apps/cap-alerting/screenly.yml index 9a5aa5660..93c60385c 100644 --- a/edge-apps/cap-alerting/screenly.yml +++ b/edge-apps/cap-alerting/screenly.yml @@ -29,7 +29,7 @@ settings: schema_version: 1 max_alerts: type: string - default_value: '3' + default_value: Infinity title: Maximum Alerts optional: true help_text: diff --git a/edge-apps/cap-alerting/screenly_qc.yml b/edge-apps/cap-alerting/screenly_qc.yml index 9a5aa5660..93c60385c 100644 --- a/edge-apps/cap-alerting/screenly_qc.yml +++ b/edge-apps/cap-alerting/screenly_qc.yml @@ -29,7 +29,7 @@ settings: schema_version: 1 max_alerts: type: string - default_value: '3' + default_value: Infinity title: Maximum Alerts optional: true help_text: diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index 116ccfa0d..e29751c7d 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -24,7 +24,7 @@ function renderAlerts( if (!container) return container.innerHTML = '' - const slice = alerts.slice(0, maxAlerts) + const slice = maxAlerts === Infinity ? alerts : alerts.slice(0, maxAlerts) slice.forEach((alert) => { const info = alert.infos.find((i) => i.language === lang) ?? alert.infos[0] @@ -233,7 +233,7 @@ export async function startApp(): Promise { const feedUrl = getSettingWithDefault('cap_feed_url', '') const interval = getSettingWithDefault('refresh_interval', 5) const lang = getSettingWithDefault('language', 'en') - const maxAlerts = getSettingWithDefault('max_alerts', 3) + const maxAlerts = getSettingWithDefault('max_alerts', Infinity) const playAudio = !getSettingWithDefault('mute_sound', false) const offlineMode = getSettingWithDefault('offline_mode', false) const mode = getSettingWithDefault('mode', 'production') From 5e1f8308596d6a39fdf69fc6981d7bbfb73059e1 Mon Sep 17 00:00:00 2001 From: nicomiguelino Date: Fri, 2 Jan 2026 14:47:01 -0800 Subject: [PATCH 25/26] chore(cap-alerting): add a `prepare` hook in `package.json` --- edge-apps/cap-alerting/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 6e4062fc6..19e0b9a2d 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -24,7 +24,8 @@ "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", - "deploy": "bun run build && screenly edge-app deploy" + "deploy": "bun run build && screenly edge-app deploy", + "prepare": "cd ../edge-apps-library && bun install && bun run build" }, "dependencies": { "@photostructure/tz-lookup": "^11.3.0", From 303c30cb036985fda11af0650d1f846be485da73 Mon Sep 17 00:00:00 2001 From: Nico Miguelino Date: Fri, 2 Jan 2026 16:48:31 -0800 Subject: [PATCH 26/26] chore(cap-alerting): use `eslint` for linting code instead of `tsc` --- edge-apps/cap-alerting/package.json | 2 +- edge-apps/cap-alerting/src/fetcher.test.ts | 4 ++-- edge-apps/cap-alerting/src/main.ts | 4 ++-- edge-apps/cap-alerting/src/parser.test.ts | 4 ++-- edge-apps/cap-alerting/src/parser.ts | 10 +++++----- edge-apps/cap-alerting/src/types/cap.ts | 6 +++--- edge-apps/edge-apps-library/eslint.config.ts | 8 +++++++- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/edge-apps/cap-alerting/package.json b/edge-apps/cap-alerting/package.json index 19e0b9a2d..97d22de1b 100644 --- a/edge-apps/cap-alerting/package.json +++ b/edge-apps/cap-alerting/package.json @@ -20,7 +20,7 @@ "test:unit": "bun test", "test:watch": "bun test --watch", "type-check": "tsc --noEmit", - "lint": "bash -c 'OUTPUT=$(tsc --noEmit --skipLibCheck src/**/*.ts 2>&1 | grep -v edge-apps-library); if [ -n \"$OUTPUT\" ]; then echo \"$OUTPUT\"; exit 1; fi'", + "lint": "edge-apps-scripts lint --fix", "format:common": "prettier src/ README.md index.html", "format": "bun run format:common --write", "format:check": "bun run format:common --check", diff --git a/edge-apps/cap-alerting/src/fetcher.test.ts b/edge-apps/cap-alerting/src/fetcher.test.ts index 5803a87b2..0b1383055 100644 --- a/edge-apps/cap-alerting/src/fetcher.test.ts +++ b/edge-apps/cap-alerting/src/fetcher.test.ts @@ -39,8 +39,8 @@ const mockFetch = mock() describe('CAPFetcher', () => { beforeEach(() => { // Setup mocks - global.localStorage = localStorageMock as any - global.fetch = mockFetch as any + global.localStorage = localStorageMock as unknown + global.fetch = mockFetch as unknown // Clear localStorage localStorageMock.clear() diff --git a/edge-apps/cap-alerting/src/main.ts b/edge-apps/cap-alerting/src/main.ts index e29751c7d..e2f91c242 100644 --- a/edge-apps/cap-alerting/src/main.ts +++ b/edge-apps/cap-alerting/src/main.ts @@ -213,7 +213,7 @@ export async function startApp(): Promise { try { settings = getSettings() localStorage.setItem('screenly_settings', JSON.stringify(settings)) - } catch (_) { + } catch { const cached = localStorage.getItem('screenly_settings') settings = cached ? (JSON.parse(cached) as Partial>) @@ -223,7 +223,7 @@ export async function startApp(): Promise { try { metadata = getMetadata() localStorage.setItem('screenly_metadata', JSON.stringify(metadata)) - } catch (_) { + } catch { const cachedMeta = localStorage.getItem('screenly_metadata') metadata = cachedMeta ? (JSON.parse(cachedMeta) as Partial>) diff --git a/edge-apps/cap-alerting/src/parser.test.ts b/edge-apps/cap-alerting/src/parser.test.ts index bfec6c6f4..bb3df531a 100644 --- a/edge-apps/cap-alerting/src/parser.test.ts +++ b/edge-apps/cap-alerting/src/parser.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test' +import { describe, it, expect, mock } from 'bun:test' const mockGetSettings = mock() const mockGetMetadata = mock() @@ -878,7 +878,7 @@ describe('CAP v1.2 Parser', () => { ignoreAttributes: false, attributeNamePrefix: '@_', }) - const json: any = parser.parse(xml) + const json = parser.parse(xml) const alerts = json.feed?.alert ? Array.isArray(json.feed.alert) ? json.feed.alert diff --git a/edge-apps/cap-alerting/src/parser.ts b/edge-apps/cap-alerting/src/parser.ts index a3e43e426..8340f9649 100644 --- a/edge-apps/cap-alerting/src/parser.ts +++ b/edge-apps/cap-alerting/src/parser.ts @@ -6,7 +6,7 @@ export function parseCap(xml: string): CAPAlert[] { ignoreAttributes: false, attributeNamePrefix: '@_', }) - const json: any = parser.parse(xml) + const json = parser.parse(xml) const alertsJson = json.alert ? Array.isArray(json.alert) ? json.alert @@ -15,10 +15,10 @@ export function parseCap(xml: string): CAPAlert[] { const alerts: CAPAlert[] = [] - alertsJson.forEach((a: any) => { + alertsJson.forEach((a: CAPAlert) => { const infosJson = a.info ? (Array.isArray(a.info) ? a.info : [a.info]) : [] - const infos: CAPInfo[] = infosJson.map((info: any) => { + const infos: CAPInfo[] = infosJson.map((info: CAPInfo) => { const resourcesJson = info.resource ? Array.isArray(info.resource) ? info.resource @@ -50,7 +50,7 @@ export function parseCap(xml: string): CAPAlert[] { contact: info.contact, parameter: info.parameter, eventCode: info.eventCode, - resources: resourcesJson.map((res: any) => { + resources: resourcesJson.map((res: Record) => { return { resourceDesc: res.resourceDesc, mimeType: res.mimeType || res['mimeType'], @@ -61,7 +61,7 @@ export function parseCap(xml: string): CAPAlert[] { url: res.uri || res.resourceDesc || '', } }), - areas: areasJson.map((area: any) => ({ + areas: areasJson.map((area: Record) => ({ areaDesc: area.areaDesc || '', polygon: area.polygon, circle: area.circle, diff --git a/edge-apps/cap-alerting/src/types/cap.ts b/edge-apps/cap-alerting/src/types/cap.ts index e1d8fea9a..9769f4c24 100644 --- a/edge-apps/cap-alerting/src/types/cap.ts +++ b/edge-apps/cap-alerting/src/types/cap.ts @@ -12,7 +12,7 @@ export interface CAPArea { areaDesc: string polygon?: string | string[] circle?: string | string[] - geocode?: any + geocode?: unknown altitude?: number ceiling?: number } @@ -35,8 +35,8 @@ export interface CAPInfo { instruction?: string web?: string contact?: string - parameter?: any - eventCode?: any + parameter?: unknown + eventCode?: unknown resources: CAPResource[] areas: CAPArea[] } diff --git a/edge-apps/edge-apps-library/eslint.config.ts b/edge-apps/edge-apps-library/eslint.config.ts index 3fae6ac5d..57c5c231c 100644 --- a/edge-apps/edge-apps-library/eslint.config.ts +++ b/edge-apps/edge-apps-library/eslint.config.ts @@ -5,5 +5,11 @@ import tseslint from 'typescript-eslint' export default defineConfig( eslint.configs.recommended, tseslint.configs.recommended, - globalIgnores(['dist/', 'node_modules/', 'static/js/', 'build/']), + globalIgnores([ + 'dist/', + 'node_modules/', + 'static/js/', + 'build/', + 'tailwind.config.js', + ]), )