[
  {
    "path": ".editorconfig",
    "content": "# editorconfig.org\nroot = true\n\n[*]\nindent_size = 2\nindent_style = space\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".env.example",
    "content": "NUXT_PUBLIC_PREVIEW_MODE=false\nNUXT_PUBLIC_SLUG_DEFAULT_LENGTH=5\nNUXT_SITE_TOKEN=SinkCool\nNUXT_REDIRECT_STATUS_CODE=308\nNUXT_LINK_CACHE_TTL=60\nNUXT_REDIRECT_WITH_QUERY=false\nNUXT_HOME_URL=\"https://sink.cool\"\nNUXT_CF_ACCOUNT_ID=123456\nNUXT_CF_API_TOKEN=CloudflareAPIToken\nNUXT_DATASET=sink\nNUXT_AI_MODEL=\"@cf/meta/llama-3-8b-instruct\"\nNUXT_AI_PROMPT=\"You are a URL shortening assistant......\"\nNUXT_DISABLE_AUTO_BACKUP=false\nNUXT_NOT_FOUND_REDIRECT=/your-own-404-page\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: miantiao-me\nbuy_me_a_coffee: miantiao\n"
  },
  {
    "path": ".github/workflows/issue-assistant.yml",
    "content": "name: issue-assistant\n\non:\n  issues:\n    types: [opened]\n\njobs:\n  issue-assistant:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      issues: write\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n\n      - name: Set up Bun\n        uses: oven-sh/setup-bun@v2\n\n      - name: Install opencode\n        run: curl -fsSL https://opencode.ai/install | bash\n\n      - name: Analyze issue\n        env:\n          OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          OPENCODE_PERMISSION: |\n            {\n              \"bash\": {\n                \"*\": \"deny\",\n                \"gh issue*\": \"allow\"\n              },\n              \"webfetch\": \"deny\"\n            }\n        run: |\n          opencode run -m ${{ secrets.OPENCODE_MODEL }} \"A new issue #${{ github.event.issue.number }} has been created. Lookup this issue, then do the following:\n\n          1. Read README.md and all files under docs/ directory to see if any documentation answers the issue.\n          2. Search existing issues (excluding #${{ github.event.issue.number }}) for potential duplicates by similarity in title, description, error messages, or functionality.\n\n          Based on your findings, post AT MOST ONE comment on #${{ github.event.issue.number }}. Combine all relevant info into a single comment. If nothing relevant found, do not comment at all.\n\n          Comment format example:\n          '👋 Here are some resources that may help:\n\n          **📖 From docs:** [brief explanation with relevant docs content]\n\n          **🔗 Related issues:**\n\n          - #123: [brief similarity]\n\n          Hope this helps! Feel free to provide more details if these don't address your case.'\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Nuxt dev/build outputs\n.output\n.data\n.nuxt\n.nitro\n.cache\ndist\n\n# Node dependencies\nnode_modules\n\n# Logs\n./logs\n*.log\n\n# Misc\n.DS_Store\n.fleet\n.idea\n\n# Local env files\n.env\n.env.*\n!.env.example\n.wrangler\nsite\ncache\npublic/world.json\n.dev.*\n.kiro/steering/locale.md\n\n.claude\n.opencode\n.agents\nopencode.json\n"
  },
  {
    "path": ".mcp.json",
    "content": "{\n  \"mcpServers\": {\n    \"shadcn-vue\": {\n      \"command\": \"npx\",\n      \"args\": [\"shadcn-vue@latest\", \"mcp\"]\n    },\n    \"nuxt\": {\n      \"type\": \"http\",\n      \"url\": \"https://nuxt.com/mcp\"\n    }\n  }\n}\n"
  },
  {
    "path": ".node-version",
    "content": "22\n"
  },
  {
    "path": ".npmrc",
    "content": "shamefully-hoist=false\nstrict-peer-dependencies=true\nauto-install-peers=true\nprefer-frozen-lockfile=true\nsave-dev=true\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"vue.volar\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  // Disable the default formatter, use eslint instead\n  \"prettier.enable\": false,\n  \"editor.formatOnSave\": false,\n\n  // Auto fix\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.eslint\": \"explicit\",\n    \"source.organizeImports\": \"never\"\n  },\n\n  // Silent the stylistic rules in you IDE, but still auto fix them\n  \"eslint.rules.customizations\": [\n    { \"rule\": \"style/*\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"format/*\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*-indent\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*-spacing\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*-spaces\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*-order\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*-dangle\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*-newline\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*quotes\", \"severity\": \"off\", \"fixable\": true },\n    { \"rule\": \"*semi\", \"severity\": \"off\", \"fixable\": true }\n  ],\n\n  // Enable eslint for all supported languages\n  \"eslint.validate\": [\n    \"javascript\",\n    \"javascriptreact\",\n    \"typescript\",\n    \"typescriptreact\",\n    \"vue\",\n    \"html\",\n    \"markdown\",\n    \"json\",\n    \"jsonc\",\n    \"yaml\",\n    \"toml\",\n    \"xml\",\n    \"gql\",\n    \"graphql\",\n    \"astro\",\n    \"svelte\",\n    \"css\",\n    \"less\",\n    \"scss\",\n    \"pcss\",\n    \"postcss\"\n  ],\n  \"i18n-ally.dirStructure\": \"auto\",\n  \"i18n-ally.localesPaths\": [\n    \"i18n/locales\"\n  ],\n  \"i18n-ally.extract.keygenStyle\": \"camelCase\",\n  \"i18n-ally.keystyle\": \"nested\",\n  \"i18n-ally.enabledParsers\": [\n    \"json\"\n  ],\n  \"i18n-ally.namespace\": true,\n  \"i18n-ally.sortKeys\": true,\n  \"i18n-ally.sourceLanguage\": \"en-US\",\n  \"i18n-ally.displayLanguage\": \"en-US\",\n  \"i18n-ally.parsers.typescript.compilerOptions\": {\n    \"moduleResolution\": \"node\"\n  },\n  \"i18n-ally.enabledFrameworks\": [\"vue\"]\n}\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# Repository Guidelines\n\nGuidelines for agentic coding agents operating in the Sink codebase.\n\n## Project Overview\n\nSink is a link shortener with analytics, running 100% on Cloudflare. Uses Nuxt 4 frontend and Cloudflare Workers backend.\n\n**All documentation and comments must be in English.**\n\n## Project Structure\n\n```\napp/                    # Nuxt 4 application (main app layer)\n  ├── components/       # Vue components (PascalCase)\n  │   └── ui/           # shadcn-vue components (DO NOT EDIT - auto-generated)\n  ├── composables/      # Vue composables (camelCase, use* prefix)\n  ├── pages/            # File-based routing\n  ├── types/            # TypeScript types (re-exports from shared/)\n  ├── utils/            # Utility functions\n  └── lib/              # Shared helpers\nlayers/dashboard/       # Dashboard layer (extends app/)\n  └── app/components/dashboard/  # Dashboard-specific components\nshared/                 # Shared code (client + server)\n  ├── schemas/          # Zod validation schemas\n  └── types/            # Shared TypeScript types\nserver/                 # Nitro server (Cloudflare Workers)\n  ├── api/              # API endpoints (method suffix: create.post.ts)\n  └── utils/            # Server utilities (auto-imported)\ntests/                  # Vitest tests (Cloudflare Workers pool)\n```\n\n## Commands\n\nUse **pnpm** (v10+) with **Node.js 22+**.\n\n```bash\n# Development\npnpm dev                  # Start dev server (port 7465)\npnpm build                # Production build\npnpm preview              # Worker preview via wrangler\npnpm lint:fix             # ESLint with auto-fix (ALWAYS run before commit)\npnpm types:check          # TypeScript type check\n\n# Testing (Vitest + @cloudflare/vitest-pool-workers)\npnpm vitest               # Watch mode\npnpm vitest run           # CI mode (run once)\npnpm vitest tests/api/link.spec.ts       # Single test file\npnpm vitest -t \"creates new link\"        # Match test name pattern\n\n# Deployment\npnpm deploy:pages         # Deploy to Cloudflare Pages\npnpm deploy:worker        # Deploy to Cloudflare Workers\n```\n\n## Code Style\n\nUses `@antfu/eslint-config` with `eslint-plugin-better-tailwindcss`. Run `pnpm lint:fix` before committing.\n\n**Formatting**: 2-space indent | Single quotes | No semicolons | Trailing commas\n\n### TypeScript\n\n- Use TypeScript everywhere; prefer `interface` for objects, `type` for unions/aliases\n- Avoid `any`; use proper types or `unknown`\n- Use Zod for runtime validation in `shared/schemas/`\n- Export types with `export type` for type-only exports\n\n```typescript\n// shared/schemas/link.ts - shared validation\nexport const LinkSchema = z.object({\n  id: z.string().trim().max(26),\n  url: z.string().trim().url().max(2048),\n  slug: z.string().trim().max(2048).regex(slugRegex),\n})\nexport type Link = z.infer<typeof LinkSchema>\n```\n\n### Vue Components\n\nUse `<script setup lang=\"ts\">` always. Files: PascalCase (`LinkEditor.vue`).\n\n```vue\n<script setup lang=\"ts\">\nimport type { Link } from '@/types'\nimport { Copy } from 'lucide-vue-next'\n\nconst props = defineProps<{ link: Link }>()\nconst emit = defineEmits<{ update: [link: Link] }>()\n</script>\n\n<template>\n  <div>{{ props.link.slug }}</div>\n</template>\n```\n\n### Imports\n\n- **Prefer Nuxt auto-imports**: `ref`, `computed`, `useFetch`, `useState`, `useRuntimeConfig`, etc.\n- **Explicit imports for**: external libs, types (`import type { Link } from '@/types'`), icons (`import { Copy } from 'lucide-vue-next'`)\n- **Server utils are auto-imported**: Functions in `server/utils/` are available globally in server code\n\n### Naming Conventions\n\n| Item           | Convention       | Example            |\n| -------------- | ---------------- | ------------------ |\n| Components     | PascalCase       | `LinkEditor.vue`   |\n| Composables    | `use` prefix     | `useAuthToken()`   |\n| API routes     | method suffix    | `create.post.ts`   |\n| Directories    | kebab-case       | `dashboard/links/` |\n| Functions/vars | camelCase        | `getLink`          |\n| Constants      | UPPER_SNAKE_CASE | `TOKEN_KEY`        |\n\n### Error Handling\n\n```typescript\n// Server API - use createError for HTTP errors\nexport default eventHandler(async (event) => {\n  const link = await readValidatedBody(event, LinkSchema.parse)\n  if (existingLink) {\n    throw createError({ status: 409, statusText: 'Link already exists' })\n  }\n})\n```\n\n## Cloudflare Bindings\n\nAccess via destructuring `event.context`:\n\n```typescript\nconst { cloudflare } = event.context\nconst { KV, ANALYTICS, AI, R2 } = cloudflare.env\n```\n\n| Binding     | Type             | Purpose                      |\n| ----------- | ---------------- | ---------------------------- |\n| `KV`        | Workers KV       | Link storage (`link:{slug}`) |\n| `ANALYTICS` | Analytics Engine | Click tracking & analytics   |\n| `AI`        | Workers AI       | AI-powered slug generation   |\n| `R2`        | R2 Bucket        | Image uploads & backup       |\n\n## Testing Patterns\n\nTests use `@cloudflare/vitest-pool-workers` with real Cloudflare bindings (single worker, shared storage).\n\n```typescript\nimport { generateMock } from '@anatine/zod-mock'\nimport { describe, expect, it } from 'vitest'\nimport { fetchWithAuth, postJson } from '../utils'\n\ndescribe.sequential('/api/link/create', () => {\n  it('creates new link with valid data', async () => {\n    const response = await postJson('/api/link/create', { url: 'https://example.com', slug: 'test' })\n    expect(response.status).toBe(201)\n  })\n})\n```\n\n**Test utilities** (`tests/utils.ts`):\n\n- `fetchWithAuth(path, options)` - GET with auth header\n- `postJson(path, body, withAuth?)` - POST JSON with optional auth\n- `putJson(path, body, withAuth?)` - PUT JSON with optional auth\n- `fetch(path, options)` - Raw fetch without auth\n\nUse `describe.sequential` for tests that share state (most API tests).\n\n## UI Components\n\n- Use shadcn-vue from `app/components/ui/` - **Never edit** (auto-generated)\n- Use `ResponsiveModal` for mobile-optimized dialogs\n- Use Tailwind CSS v4 for styling\n- Use static English for `aria-label` (no `$t()` translations)\n- Icons from `lucide-vue-next`\n\n## Commits\n\nFollow Conventional Commits: `feat:`, `fix:`, `docs:`, `chore:`, `refactor:`\n\n## Pre-commit\n\n`simple-git-hooks` runs `lint-staged` on commit, auto-runs `eslint --fix` on staged files.\n\n## API Route Patterns\n\nAPI routes use method suffix convention:\n\n- `create.post.ts` → `POST /api/link/create`\n- `query.get.ts` → `GET /api/link/query`\n- `edit.put.ts` → `PUT /api/link/edit`\n\nServer utils in `server/utils/` are auto-imported:\n\n- `getLink(event, slug)` - Fetch link from KV\n- `putLink(event, link)` - Store link in KV\n- `deleteLink(event, slug)` - Remove link from KV\n- `normalizeSlug(event, slug)` - Case normalization\n- `buildShortLink(event, slug)` - Construct full URL\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n- Demonstrating empathy and kindness toward other people\n- Being respectful of differing opinions, viewpoints, and experiences\n- Giving and gracefully accepting constructive feedback\n- Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n- Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n- Trolling, insulting or derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official email address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n<chi@miantiao.me>.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "README.md",
    "content": "# ⚡ Sink\n\n**A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.**\n\n<a href=\"https://trendshift.io/repositories/10421\" target=\"_blank\">\n  <img\n    src=\"https://trendshift.io/api/badge/repositories/10421\"\n    alt=\"miantiao-me/Sink | Trendshift\"\n    width=\"250\"\n    height=\"55\"\n  />\n</a>\n<a href=\"https://news.ycombinator.com/item?id=40843683\" target=\"_blank\">\n  <img\n    src=\"https://hackernews-badge.vercel.app/api?id=40843683\"\n    alt=\"Featured on Hacker News\"\n    width=\"250\"\n    height=\"55\"\n  />\n</a>\n<a href=\"https://hellogithub.com/repository/57771fd91d1542c7a470959b677a9944\" target=\"_blank\">\n  <img\n    src=\"https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=57771fd91d1542c7a470959b677a9944&claim_uid=qi74Zp23wYKeAVB&theme=neutral\"\n    alt=\"Featured｜HelloGitHub\"\n    width=\"250\"\n    height=\"55\"\n  />\n</a>\n<a href=\"https://www.uneed.best/tool/sink\" target=\"_blank\">\n  <img\n    src=\"https://www.uneed.best/POTW1.png\"\n    alt=\"Uneed Badge\"\n    width=\"250\"\n    height=\"55\"\n  />\n</a>\n\n[<img src=\"https://devin.ai/assets/deepwiki-badge.png\" alt=\"DeepWiki\" height=\"20\"/>](https://deepwiki.com/miantiao-me/Sink)\n![Cloudflare](https://img.shields.io/badge/Cloudflare-F69652?style=flat&logo=cloudflare&logoColor=white)\n![Nuxt](https://img.shields.io/badge/Nuxt-00DC82?style=flat&logo=nuxtdotjs&logoColor=white)\n![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-06B6D4?style=flat&logo=tailwindcss&logoColor=white)\n![shadcn/ui](https://img.shields.io/badge/shadcn/ui-000000?style=flat&logo=shadcnui&logoColor=white)\n\n![Hero](./public/image.png)\n\n---\n\n## ✨ Features\n\n- **🔗 URL Shortening:** Compress your URLs to their minimal length.\n- **📈 Analytics:** Monitor link analytics and gather insightful statistics.\n- **☁️ Serverless:** Deploy without the need for traditional servers.\n- **🎨 Customizable Slug:** Support for personalized slugs and case sensitivity.\n- **🪄 AI Slug:** Leverage AI to generate slugs.\n- **⏰ Link Expiration:** Set expiration dates for your links.\n- **📱 Device Routing:** Redirect iOS/Android users to different URLs (App Store links).\n- **🖼️ OpenGraph Preview:** Custom social media previews with title, description, and image.\n- **📊 Real-time Analytics:** Live 3D globe visualization and real-time event logs.\n- **🔲 QR Code:** Generate QR codes for your short links.\n- **📦 Import/Export:** Bulk migration via JSON/CSV files.\n- **🌍 Multi-language:** Full i18n support for the dashboard.\n- **🌙 Dark Mode:** Light, dark, and system theme support.\n\n## 🪧 Demo\n\nExperience the demo at [Sink.Cool](https://sink.cool/dashboard). Log in using the Site Token below:\n\n```txt\nSite Token: SinkCool\n```\n\n<details>\n  <summary><b>Screenshots</b></summary>\n  <img alt=\"Analytics\" src=\"./docs/images/sink.cool_dashboard.png\"/>\n  <img alt=\"Links\" src=\"./docs/images/sink.cool_dashboard_links.png\"/>\n  <img alt=\"Link Analytics\" src=\"./docs/images/sink.cool_dashboard_link_slug.png\"/>\n</details>\n\n## 🧱 Technologies Used\n\n- **Framework**: [Nuxt](https://nuxt.com/)\n- **Database**: [Cloudflare Workers KV](https://developers.cloudflare.com/kv/)\n- **Analytics Engine**: [Cloudflare Workers Analytics Engine](https://developers.cloudflare.com/analytics/)\n- **UI Components**: [shadcn-vue](https://www.shadcn-vue.com/)\n- **Styling:** [Tailwind CSS](https://tailwindcss.com/)\n- **Deployment**: [Cloudflare](https://www.cloudflare.com/)\n\n## 🚗 Roadmap [WIP]\n\nWe welcome your contributions and PRs.\n\n- [x] Browser Extension - [Sink Tool](https://github.com/zhuzhuyule/sink-extension)\n- [x] Chrome Extension - [Sink Quick Shorten](https://chromewebstore.google.com/detail/sink-quick-shorten/emlojomjpenjgkaphajcokijobpkejih)\n- [x] Raycast Extension - [Raycast-Sink](https://github.com/foru17/raycast-sink)\n- [x] Apple Shortcuts - [Sink Shortcuts](https://s.search1api.com/sink001)\n- [x] iOS App - [Sink](https://apps.apple.com/app/id6745417598)\n- [ ] Enhanced Link Management (with Cloudflare D1)\n- [ ] Analytics Enhancements (Support for merging filter conditions)\n- [ ] Dashboard Performance Optimization (Infinite loading)\n- [ ] Units Test\n\n## 🏗️ Deployment\n\n> Video tutorial: [Watch here](https://www.youtube.com/watch?v=MkU23U2VE9E)\n\nWe currently support deployment to [Cloudflare Workers](./docs/deployment/workers.md) (recommended) and [Cloudflare Pages](./docs/deployment/pages.md).\n\n## ⚒️ Configuration\n\n[Configuration Docs](./docs/configuration.md)\n\n## 🔌 API\n\n[API Docs](./docs/api.md)\n\n## 🤖 AI Skills\n\nInstall Sink AI Skills for enhanced coding assistance:\n\n```bash\nnpx skills add miantiao-me/sink\n```\n\n## 🧰 MCP\n\nWe currently do not support native MCP Server, but we have OpenAPI documentation, and you can use the following method to support MCP.\n\n> Replace the domain name in `OPENAPI_SPEC_URL` with your own domain name.\n>\n> The `API_KEY` is the same as the `NUXT_SITE_TOKEN` in the environment variables.\n\n```json\n{\n  \"mcpServers\": {\n    \"sink\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"mcp-openapi-proxy\"\n      ],\n      \"env\": {\n        \"OPENAPI_SPEC_URL\": \"https://sink.cool/_docs/openapi.json\",\n        \"API_KEY\": \"SinkCool\",\n        \"TOOL_WHITELIST\": \"/api/link\"\n      }\n    }\n  }\n}\n```\n\n## 🙋🏻 FAQs\n\n[FAQs](./docs/faqs.md)\n\n## 💖 Credits\n\n1. [**Cloudflare**](https://www.cloudflare.com/)\n2. [**NuxtHub**](https://hub.nuxt.com/)\n3. [**Astroship**](https://astroship.web3templates.com/)\n4. [**Tailark**](https://tailark.com/)\n\n## ☕ Sponsor\n\n1. [Follow Me on X(Twitter)](https://404.li/x).\n2. [Become a sponsor to on GitHub](https://github.com/sponsors/miantiao-me).\n"
  },
  {
    "path": "app/app.config.ts",
    "content": "export default defineAppConfig({\n  title: 'Sink',\n  github: 'https://github.com/miantiao-me/sink',\n  coffee: 'https://sink.cool/coffee',\n  twitter: 'https://sink.cool/x',\n  telegram: 'https://sink.cool/telegram',\n  description: 'A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.',\n  image: 'https://sink.cool/banner.png',\n  previewTTL: 300, // 5 minutes\n  slugRegex: /^[a-z0-9]+(?:-[a-z0-9]+)*$/i,\n  reserveSlug: [\n    'dashboard',\n  ],\n})\n"
  },
  {
    "path": "app/app.vue",
    "content": "<script setup lang=\"ts\">\nimport 'vue-sonner/style.css'\n\nconst { title, description, image } = useAppConfig()\nconst route = useRoute()\n\nuseSeoMeta({\n  title: `${title} - Link Shortener with Analytics`,\n  description,\n  ogType: 'website',\n  ogTitle: title,\n  ogSiteName: title,\n  ogDescription: description,\n  ogImage: image,\n  twitterTitle: title,\n  twitterDescription: description,\n  twitterImage: image,\n  twitterCard: 'summary_large_image',\n})\n\nuseHead({\n  htmlAttrs: {\n    lang: 'en',\n  },\n  meta: [\n    {\n      name: 'viewport',\n      content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no',\n    },\n  ],\n  link: [\n    {\n      rel: 'canonical',\n      href: computed(() => `https://sink.cool${route.path}`),\n    },\n    {\n      rel: 'icon',\n      type: 'image/png',\n      href: '/icon-192.png',\n    },\n    {\n      rel: 'apple-touch-icon',\n      href: '/apple-touch-icon.png',\n    },\n  ],\n})\n</script>\n\n<template>\n  <NuxtLayout>\n    <NuxtLoadingIndicator color=\"#000\" />\n    <NuxtPage />\n    <Toaster position=\"top-center\" rich-colors />\n  </NuxtLayout>\n</template>\n"
  },
  {
    "path": "app/assets/css/tailwind.css",
    "content": "@import 'tailwindcss';\n@import 'tw-animate-css';\n\n@source \"../../../layers\";\n\n@custom-variant dark (&:is(.dark *));\n\n@theme inline {\n  --radius-sm: calc(var(--radius) - 4px);\n  --radius-md: calc(var(--radius) - 2px);\n  --radius-lg: var(--radius);\n  --radius-xl: calc(var(--radius) + 4px);\n  --color-background: var(--background);\n  --color-foreground: var(--foreground);\n  --color-card: var(--card);\n  --color-card-foreground: var(--card-foreground);\n  --color-popover: var(--popover);\n  --color-popover-foreground: var(--popover-foreground);\n  --color-primary: var(--primary);\n  --color-primary-foreground: var(--primary-foreground);\n  --color-secondary: var(--secondary);\n  --color-secondary-foreground: var(--secondary-foreground);\n  --color-muted: var(--muted);\n  --color-muted-foreground: var(--muted-foreground);\n  --color-accent: var(--accent);\n  --color-accent-foreground: var(--accent-foreground);\n  --color-destructive: var(--destructive);\n  --color-border: var(--border);\n  --color-input: var(--input);\n  --color-ring: var(--ring);\n  --color-chart-1: var(--chart-1);\n  --color-chart-2: var(--chart-2);\n  --color-chart-3: var(--chart-3);\n  --color-chart-4: var(--chart-4);\n  --color-chart-5: var(--chart-5);\n  --color-sidebar: var(--sidebar);\n  --color-sidebar-foreground: var(--sidebar-foreground);\n  --color-sidebar-primary: var(--sidebar-primary);\n  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n  --color-sidebar-accent: var(--sidebar-accent);\n  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n  --color-sidebar-border: var(--sidebar-border);\n  --color-sidebar-ring: var(--sidebar-ring);\n}\n\n:root {\n  --radius: 0.625rem;\n  --background: oklch(1 0 0);\n  --foreground: oklch(0.141 0.005 285.823);\n  --card: oklch(1 0 0);\n  --card-foreground: oklch(0.141 0.005 285.823);\n  --popover: oklch(1 0 0);\n  --popover-foreground: oklch(0.141 0.005 285.823);\n  --primary: oklch(0.21 0.006 285.885);\n  --primary-foreground: oklch(0.985 0 0);\n  --secondary: oklch(0.967 0.001 286.375);\n  --secondary-foreground: oklch(0.21 0.006 285.885);\n  --muted: oklch(0.967 0.001 286.375);\n  --muted-foreground: oklch(0.552 0.016 285.938);\n  --accent: oklch(0.967 0.001 286.375);\n  --accent-foreground: oklch(0.21 0.006 285.885);\n  --destructive: oklch(0.577 0.245 27.325);\n  --border: oklch(0.92 0.004 286.32);\n  --input: oklch(0.92 0.004 286.32);\n  --ring: oklch(0.705 0.015 286.067);\n  --chart-1: oklch(0.871 0.15 154.449);\n  --chart-2: oklch(0.723 0.219 149.579);\n  --chart-3: oklch(0.627 0.194 149.214);\n  --chart-4: oklch(0.527 0.154 150.069);\n  --chart-5: oklch(0.448 0.119 151.328);\n  --sidebar: oklch(0.985 0 0);\n  --sidebar-foreground: oklch(0.141 0.005 285.823);\n  --sidebar-primary: oklch(0.21 0.006 285.885);\n  --sidebar-primary-foreground: oklch(0.985 0 0);\n  --sidebar-accent: oklch(0.967 0.001 286.375);\n  --sidebar-accent-foreground: oklch(0.21 0.006 285.885);\n  --sidebar-border: oklch(0.92 0.004 286.32);\n  --sidebar-ring: oklch(0.705 0.015 286.067);\n}\n\n.dark {\n  --background: oklch(0.141 0.005 285.823);\n  --foreground: oklch(0.985 0 0);\n  --card: oklch(0.21 0.006 285.885);\n  --card-foreground: oklch(0.985 0 0);\n  --popover: oklch(0.21 0.006 285.885);\n  --popover-foreground: oklch(0.985 0 0);\n  --primary: oklch(0.92 0.004 286.32);\n  --primary-foreground: oklch(0.21 0.006 285.885);\n  --secondary: oklch(0.274 0.006 286.033);\n  --secondary-foreground: oklch(0.985 0 0);\n  --muted: oklch(0.274 0.006 286.033);\n  --muted-foreground: oklch(0.705 0.015 286.067);\n  --accent: oklch(0.274 0.006 286.033);\n  --accent-foreground: oklch(0.985 0 0);\n  --destructive: oklch(0.704 0.191 22.216);\n  --border: oklch(1 0 0 / 10%);\n  --input: oklch(1 0 0 / 15%);\n  --ring: oklch(0.552 0.016 285.938);\n  --chart-1: oklch(0.871 0.15 154.449);\n  --chart-2: oklch(0.723 0.219 149.579);\n  --chart-3: oklch(0.627 0.194 149.214);\n  --chart-4: oklch(0.527 0.154 150.069);\n  --chart-5: oklch(0.448 0.119 151.328);\n  --sidebar: oklch(0.21 0.006 285.885);\n  --sidebar-foreground: oklch(0.985 0 0);\n  --sidebar-primary: oklch(0.488 0.243 264.376);\n  --sidebar-primary-foreground: oklch(0.985 0 0);\n  --sidebar-accent: oklch(0.274 0.006 286.033);\n  --sidebar-accent-foreground: oklch(0.985 0 0);\n  --sidebar-border: oklch(1 0 0 / 10%);\n  --sidebar-ring: oklch(0.552 0.016 285.938);\n}\n\n@layer base {\n  * {\n    @apply border-border outline-ring/50;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n\n  button:not(:disabled),\n  [role='button']:not(:disabled),\n  [role='menuitem']:not(:disabled),\n  [role='tab']:not(:disabled),\n  [role='option']:not(:disabled),\n  [role='switch']:not(:disabled),\n  [role='checkbox']:not(:disabled),\n  [role='radio']:not(:disabled),\n  summary,\n  label {\n    @apply cursor-pointer;\n  }\n}\n"
  },
  {
    "path": "app/components/ResponsiveModal.vue",
    "content": "<script setup lang=\"ts\">\nimport { useMediaQuery } from '@vueuse/core'\n\ndefineOptions({ inheritAttrs: false })\n\nwithDefaults(defineProps<{\n  title: string\n  description?: string\n  contentClass?: string\n}>(), {\n  contentClass: '',\n})\n\nconst slots = defineSlots<{\n  trigger?: () => any\n  default?: () => any\n  footer?: () => any\n}>()\n\nconst open = defineModel<boolean>('open', { default: false })\nconst isDesktop = useMediaQuery('(min-width: 640px)')\n</script>\n\n<template>\n  <!-- Desktop: Dialog -->\n  <Dialog v-if=\"isDesktop\" v-model:open=\"open\">\n    <DialogTrigger v-if=\"slots.trigger\" as-child>\n      <slot name=\"trigger\" />\n    </DialogTrigger>\n    <DialogContent\n      class=\"\n        max-h-[90svh] max-w-[95svw] grid-rows-[auto_minmax(0,1fr)_auto]\n        md:max-w-lg\n      \" :class=\"[\n        contentClass,\n      ]\"\n    >\n      <DialogHeader>\n        <DialogTitle>{{ title }}</DialogTitle>\n        <DialogDescription v-if=\"description\">\n          {{ description }}\n        </DialogDescription>\n      </DialogHeader>\n      <div class=\"overflow-y-auto\">\n        <slot />\n      </div>\n      <DialogFooter v-if=\"slots.footer\">\n        <slot name=\"footer\" />\n      </DialogFooter>\n    </DialogContent>\n  </Dialog>\n\n  <!-- Mobile: Drawer -->\n  <Drawer v-else v-model:open=\"open\">\n    <DrawerTrigger v-if=\"slots.trigger\" as-child>\n      <slot name=\"trigger\" />\n    </DrawerTrigger>\n    <DrawerContent class=\"max-h-[90svh]\">\n      <DrawerHeader>\n        <DrawerTitle>{{ title }}</DrawerTitle>\n        <DrawerDescription v-if=\"description\">\n          {{ description }}\n        </DrawerDescription>\n      </DrawerHeader>\n      <div class=\"flex flex-1 flex-col items-center overflow-y-auto px-4\">\n        <slot />\n      </div>\n      <DrawerFooter v-if=\"slots.footer\" class=\"flex-row justify-end gap-2\">\n        <slot name=\"footer\" />\n      </DrawerFooter>\n    </DrawerContent>\n  </Drawer>\n</template>\n"
  },
  {
    "path": "app/components/SwitchLanguage.vue",
    "content": "<script setup lang=\"ts\">\nimport { Languages } from 'lucide-vue-next'\n\nconst { setLocale, locales } = useI18n()\n</script>\n\n<template>\n  <DropdownMenu>\n    <DropdownMenuTrigger as-child>\n      <Button variant=\"ghost\">\n        <Languages class=\"h-5 w-5\" />\n        <span class=\"sr-only\">{{ $t('theme.toggle') }}</span>\n      </Button>\n    </DropdownMenuTrigger>\n    <DropdownMenuContent\n      align=\"end\"\n      class=\"min-w-min\"\n    >\n      <DropdownMenuItem\n        v-for=\"locale in locales\"\n        :key=\"locale.code\"\n        class=\"cursor-pointer\"\n        @click=\"setLocale(locale.code)\"\n      >\n        <span class=\"mr-1\">\n          {{ locale.emoji }}\n        </span>\n        {{ locale.name }}\n      </DropdownMenuItem>\n    </DropdownMenuContent>\n  </DropdownMenu>\n</template>\n"
  },
  {
    "path": "app/components/SwitchTheme.vue",
    "content": "<script setup lang=\"ts\">\nimport { Laptop, Moon, Sun } from 'lucide-vue-next'\n\nconst colorMode = useColorMode()\n</script>\n\n<template>\n  <DropdownMenu>\n    <DropdownMenuTrigger as-child>\n      <Button variant=\"ghost\">\n        <Sun\n          aria-hidden=\"true\"\n          class=\"\n            absolute h-5 w-5 scale-100 transition-[transform,opacity]\n            dark:scale-0\n          \"\n        />\n        <Moon\n          aria-hidden=\"true\"\n          class=\"\n            h-5 w-5 scale-0 transition-[transform,opacity]\n            dark:scale-100\n          \"\n        />\n        <span class=\"sr-only\">{{ $t('theme.toggle') }}</span>\n      </Button>\n    </DropdownMenuTrigger>\n    <DropdownMenuContent\n      align=\"end\"\n      class=\"min-w-min\"\n    >\n      <DropdownMenuItem\n        class=\"cursor-pointer\"\n        @click=\"colorMode.preference = 'light'\"\n      >\n        <Sun aria-hidden=\"true\" class=\"mr-1 h-4 w-4\" />\n        {{ $t('theme.light') }}\n      </DropdownMenuItem>\n      <DropdownMenuItem\n        class=\"cursor-pointer\"\n        @click=\"colorMode.preference = 'dark'\"\n      >\n        <Moon aria-hidden=\"true\" class=\"mr-1 h-4 w-4\" />\n        {{ $t('theme.dark') }}\n      </DropdownMenuItem>\n      <DropdownMenuItem\n        class=\"cursor-pointer\"\n        @click=\"colorMode.preference = 'system'\"\n      >\n        <Laptop aria-hidden=\"true\" class=\"mr-1 h-4 w-4\" />\n        {{ $t('theme.system') }}\n      </DropdownMenuItem>\n    </DropdownMenuContent>\n  </DropdownMenu>\n</template>\n"
  },
  {
    "path": "app/components/home/Cta.vue",
    "content": "<script setup lang=\"ts\">\nconst { github } = useAppConfig()\n</script>\n\n<template>\n  <section class=\"py-16\">\n    <div class=\"mx-auto max-w-6xl px-6\">\n      <div\n        class=\"\n          mx-auto max-w-3xl rounded-xl border px-6 py-10\n          md:py-12\n        \"\n      >\n        <div class=\"text-center\">\n          <h2\n            class=\"text-3xl font-semibold text-balance\"\n          >\n            {{ $t('home.cta.title') }}\n          </h2>\n          <p class=\"mt-4 text-muted-foreground\">\n            {{ $t('home.cta.description') }}\n          </p>\n\n          <div class=\"mt-8 flex flex-wrap justify-center gap-4\">\n            <Button\n              as-child\n              size=\"lg\"\n            >\n              <a\n                :href=\"`${github}?tab=readme-ov-file#%EF%B8%8F-deployment`\"\n                target=\"_blank\"\n                :title=\"$t('home.cta.button')\"\n              >\n                {{ $t('home.cta.button') }}\n              </a>\n            </Button>\n\n            <Button\n              as-child\n              size=\"lg\"\n              variant=\"outline\"\n            >\n              <NuxtLink to=\"/dashboard\" :title=\"$t('dashboard.title')\">\n                {{ $t('dashboard.title') }}\n              </NuxtLink>\n            </Button>\n          </div>\n        </div>\n      </div>\n    </div>\n  </section>\n</template>\n"
  },
  {
    "path": "app/components/home/Features.vue",
    "content": "<script setup lang=\"ts\">\nimport { AreaChart, FileJson, Globe, Hourglass, Languages, Link, Paintbrush, QrCode, ServerOff, Share2, Smartphone, Sparkles } from 'lucide-vue-next'\n\nconst { t } = useI18n()\nconst features = computed(() => [\n  {\n    title: t('home.features.url_shortening.title'),\n    description: t('home.features.url_shortening.description'),\n    icon: Link,\n  },\n  {\n    title: t('home.features.analytics.title'),\n    description: t('home.features.analytics.description'),\n    icon: AreaChart,\n  },\n  {\n    title: t('home.features.serverless.title'),\n    description: t('home.features.serverless.description'),\n    icon: ServerOff,\n  },\n  {\n    title: t('home.features.customizable_slug.title'),\n    description: t('home.features.customizable_slug.description'),\n    icon: Paintbrush,\n  },\n  {\n    title: t('home.features.ai_slug.title'),\n    description: t('home.features.ai_slug.description'),\n    icon: Sparkles,\n  },\n  {\n    title: t('home.features.link_expiration.title'),\n    description: t('home.features.link_expiration.description'),\n    icon: Hourglass,\n  },\n  {\n    title: t('home.features.device_routing.title'),\n    description: t('home.features.device_routing.description'),\n    icon: Smartphone,\n  },\n  {\n    title: t('home.features.og_preview.title'),\n    description: t('home.features.og_preview.description'),\n    icon: Share2,\n  },\n  {\n    title: t('home.features.realtime_analytics.title'),\n    description: t('home.features.realtime_analytics.description'),\n    icon: Globe,\n  },\n  {\n    title: t('home.features.qr_code.title'),\n    description: t('home.features.qr_code.description'),\n    icon: QrCode,\n  },\n  {\n    title: t('home.features.import_export.title'),\n    description: t('home.features.import_export.description'),\n    icon: FileJson,\n  },\n  {\n    title: t('home.features.multi_language.title'),\n    description: t('home.features.multi_language.description'),\n    icon: Languages,\n  },\n])\n</script>\n\n<template>\n  <section\n    class=\"\n      py-12\n      md:py-20\n    \"\n  >\n    <div\n      class=\"\n        mx-auto max-w-6xl space-y-8 px-6\n        md:space-y-12\n      \"\n    >\n      <div\n        class=\"relative z-10 mx-auto max-w-xl space-y-4 text-center\"\n      >\n        <h2\n          class=\"text-3xl font-semibold text-balance\"\n        >\n          {{ $t('home.features.title') }}\n        </h2>\n        <p class=\"text-muted-foreground\">\n          {{ $t('home.features.subtitle') }}\n        </p>\n      </div>\n\n      <div\n        class=\"\n          relative mx-auto grid gap-3\n          sm:grid-cols-2\n          lg:grid-cols-3\n        \"\n      >\n        <div\n          v-for=\"item in features\"\n          :key=\"item.title\"\n          class=\"space-y-3 rounded-xl border p-6\"\n        >\n          <div class=\"flex items-center gap-2\">\n            <component\n              :is=\"item.icon\"\n              class=\"size-4\"\n            />\n            <h3 class=\"text-sm font-medium\">\n              {{ item.title }}\n            </h3>\n          </div>\n          <p class=\"text-sm text-muted-foreground\">\n            {{ item.description }}\n          </p>\n        </div>\n      </div>\n    </div>\n  </section>\n</template>\n"
  },
  {
    "path": "app/components/home/Hero.vue",
    "content": "<script setup lang=\"ts\">\nimport { ArrowRight } from 'lucide-vue-next'\nimport { GitHubIcon, XIcon } from 'vue3-simple-icons'\nimport heroUrl from '@/assets/images/hero.svg?url'\n\nconst { title, description, github, twitter } = useAppConfig()\n</script>\n\n<template>\n  <section>\n    <div\n      class=\"\n        py-16\n        md:py-24\n      \"\n    >\n      <div\n        class=\"\n          mx-auto flex max-w-6xl flex-col items-center gap-12 px-6\n          lg:flex-row lg:justify-between\n        \"\n      >\n        <div\n          class=\"\n            max-w-lg text-center\n            lg:text-left\n          \"\n        >\n          <!-- Twitter Follow Badge -->\n          <a\n            :href=\"twitter\"\n            target=\"_blank\"\n            rel=\"noopener\"\n            :title=\"$t('home.twitter.follow')\"\n            class=\"\n              mx-auto mb-8 inline-flex w-fit items-center gap-2 rounded-full\n              border p-1 pr-3\n              lg:mx-0\n            \"\n          >\n            <span\n              class=\"\n                flex items-center gap-1.5 rounded-full bg-muted px-2 py-1\n                text-xs\n              \"\n            >\n              <XIcon aria-hidden=\"true\" class=\"size-3\" />\n            </span>\n            <span class=\"text-sm\">{{ $t('home.twitter.follow') }}</span>\n            <span class=\"block h-4 w-px bg-border\" />\n            <ArrowRight aria-hidden=\"true\" class=\"size-4\" />\n          </a>\n\n          <h1\n            class=\"\n              text-4xl font-medium text-balance\n              md:text-5xl\n              xl:text-6xl\n            \"\n          >\n            {{ title }}\n          </h1>\n          <p class=\"mt-6 text-lg text-pretty text-muted-foreground\">\n            {{ description }}\n          </p>\n\n          <div\n            class=\"\n              mt-10 flex flex-col items-center justify-center gap-2\n              sm:flex-row\n              lg:justify-start\n            \"\n          >\n            <Button\n              as-child\n              size=\"lg\"\n              class=\"px-5 text-base\"\n            >\n              <NuxtLink to=\"/dashboard\">\n                <span class=\"text-nowrap\">{{ $t('dashboard.title') }}</span>\n              </NuxtLink>\n            </Button>\n            <Button\n              as-child\n              size=\"lg\"\n              variant=\"ghost\"\n              class=\"px-5 text-base\"\n            >\n              <a\n                :href=\"github\"\n                target=\"_blank\"\n                :title=\"$t('layouts.footer.social.github')\"\n                class=\"flex items-center gap-1.5\"\n              >\n                <GitHubIcon aria-hidden=\"true\" class=\"size-5\" />\n                <span class=\"text-nowrap\">{{ $t('home.hero.github_repo') }}</span>\n              </a>\n            </Button>\n          </div>\n        </div>\n\n        <object\n          type=\"image/svg+xml\"\n          :data=\"heroUrl\"\n          class=\"\n            hidden aspect-square w-96 shrink-0\n            md:block\n            lg:w-[420px]\n          \"\n          aria-label=\"Link sharing illustration\"\n          suppressHydrationWarning\n        />\n      </div>\n    </div>\n  </section>\n</template>\n"
  },
  {
    "path": "app/components/home/Logos.vue",
    "content": "<template>\n  <section class=\"bg-background py-12\">\n    <div class=\"mx-auto max-w-6xl px-6\">\n      <div\n        class=\"\n          flex items-center justify-center gap-x-12 gap-y-6\n          md:gap-x-24\n        \"\n      >\n        <img\n          class=\"\n            h-20 w-auto\n            md:h-32\n            lg:h-44\n          \"\n          alt=\"Cloudflare\"\n          src=\"@/assets/images/cloudflare.png\"\n          fetchpriority=\"high\"\n          width=\"350\"\n          height=\"176\"\n        >\n        <img\n          class=\"\n            h-20 w-auto\n            md:h-32\n            lg:h-44\n          \"\n          alt=\"Nuxt.js\"\n          src=\"@/assets/images/nuxtjs.png\"\n          fetchpriority=\"high\"\n          width=\"350\"\n          height=\"176\"\n        >\n      </div>\n    </div>\n  </section>\n</template>\n"
  },
  {
    "path": "app/components/home/Stats.vue",
    "content": "<script setup lang=\"ts\">\nconst { stats } = useGithubStats()\n</script>\n\n<template>\n  <section\n    class=\"\n      py-12\n      md:py-20\n    \"\n  >\n    <div\n      class=\"\n        mx-auto max-w-6xl space-y-8 px-6\n        md:space-y-12\n      \"\n    >\n      <div class=\"relative z-10 mx-auto max-w-xl space-y-4 text-center\">\n        <h2\n          class=\"text-3xl font-semibold\"\n        >\n          {{ $t('home.stats.title') }}\n        </h2>\n        <p class=\"text-muted-foreground\">\n          {{ $t('home.stats.subtitle') }}\n        </p>\n      </div>\n\n      <div\n        class=\"\n          grid gap-0 divide-y\n          md:grid-cols-2 md:gap-2 md:divide-x md:divide-y-0\n        \"\n      >\n        <div\n          class=\"\n            space-y-4 pb-6 text-center\n            md:pb-0\n          \"\n        >\n          <ClientOnly>\n            <template #fallback>\n              <Skeleton class=\"mx-auto h-12 w-24\" />\n            </template>\n            <div class=\"text-5xl font-bold tabular-nums\">\n              {{ stats.stars }}\n            </div>\n          </ClientOnly>\n          <p class=\"text-muted-foreground\">\n            {{ $t('home.stats.stars') }}\n          </p>\n        </div>\n        <div\n          class=\"\n            space-y-4 pt-6 text-center\n            md:pt-0\n          \"\n        >\n          <ClientOnly>\n            <template #fallback>\n              <Skeleton class=\"mx-auto h-12 w-24\" />\n            </template>\n            <div class=\"text-5xl font-bold tabular-nums\">\n              {{ stats.forks }}\n            </div>\n          </ClientOnly>\n          <p class=\"text-muted-foreground\">\n            {{ $t('home.stats.forks') }}\n          </p>\n        </div>\n      </div>\n    </div>\n  </section>\n</template>\n"
  },
  {
    "path": "app/components/home/Testimonials.vue",
    "content": "<script setup lang=\"ts\">\nimport testimonials from '@/data/testimonials.json'\n\ninterface Testimonial {\n  id: string\n  name: string\n  username: string\n  content: string\n  url: string\n  verified: boolean\n  date: string\n}\n\nconst typedTestimonials = testimonials as Testimonial[]\n</script>\n\n<template>\n  <section>\n    <div\n      class=\"\n        py-12\n        md:py-16\n      \"\n    >\n      <div class=\"mx-auto max-w-6xl px-6\">\n        <div class=\"text-center\">\n          <h2 class=\"text-3xl font-semibold\">\n            {{ $t('home.testimonials.title') }}\n          </h2>\n          <p class=\"mt-4 text-muted-foreground\">\n            {{ $t('home.testimonials.subtitle') }}\n          </p>\n        </div>\n        <div\n          class=\"\n            mt-8 columns-1 gap-3 space-y-3\n            md:mt-12 md:columns-3\n          \"\n        >\n          <a\n            v-for=\"testimonial in typedTestimonials\"\n            :key=\"testimonial.id\"\n            :href=\"testimonial.url\"\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            class=\"block break-inside-avoid\"\n          >\n            <Card\n              class=\"\n                transition-shadow\n                hover:shadow-md\n              \"\n            >\n              <CardContent class=\"grid grid-cols-[auto_1fr] gap-3 px-4 py-0\">\n                <Avatar class=\"size-9\">\n                  <AvatarImage\n                    :alt=\"testimonial.name\"\n                    :src=\"`https://unavatar.webp.se/x/${testimonial.username}`\"\n                    loading=\"lazy\"\n                    referrerpolicy=\"no-referrer\"\n                  />\n                  <AvatarFallback>{{ testimonial.name.slice(0, 2) }}</AvatarFallback>\n                </Avatar>\n\n                <div>\n                  <div class=\"flex items-center gap-1\">\n                    <h3 class=\"font-medium\">\n                      {{ testimonial.name }}\n                    </h3>\n                    <svg\n                      v-if=\"testimonial.verified\"\n                      class=\"size-4 text-blue-500\"\n                      viewBox=\"0 0 22 22\"\n                      fill=\"currentColor\"\n                    >\n                      <path d=\"M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z\" />\n                    </svg>\n                  </div>\n\n                  <span\n                    class=\"block text-sm tracking-wide text-muted-foreground\"\n                  >\n                    @{{ testimonial.username }}\n                  </span>\n\n                  <blockquote class=\"mt-3\">\n                    <p class=\"text-foreground\">\n                      {{ testimonial.content }}\n                    </p>\n                  </blockquote>\n                </div>\n              </CardContent>\n            </Card>\n          </a>\n        </div>\n      </div>\n    </div>\n  </section>\n</template>\n"
  },
  {
    "path": "app/components/ui/accordion/Accordion.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AccordionRootEmits, AccordionRootProps } from \"reka-ui\"\nimport {\n  AccordionRoot,\n  useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<AccordionRootProps>()\nconst emits = defineEmits<AccordionRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <AccordionRoot v-slot=\"slotProps\" data-slot=\"accordion\" v-bind=\"forwarded\">\n    <slot v-bind=\"slotProps\" />\n  </AccordionRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/accordion/AccordionContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AccordionContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { AccordionContent } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<AccordionContentProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AccordionContent\n    data-slot=\"accordion-content\"\n    v-bind=\"delegatedProps\"\n    class=\"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm\"\n  >\n    <div :class=\"cn('pt-0 pb-4', props.class)\">\n      <slot />\n    </div>\n  </AccordionContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/accordion/AccordionItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AccordionItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { AccordionItem, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<AccordionItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <AccordionItem\n    v-slot=\"slotProps\"\n    data-slot=\"accordion-item\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('border-b last:border-b-0', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </AccordionItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/accordion/AccordionTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AccordionTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronDown } from \"lucide-vue-next\"\nimport {\n  AccordionHeader,\n  AccordionTrigger,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<AccordionTriggerProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AccordionHeader class=\"flex\">\n    <AccordionTrigger\n      data-slot=\"accordion-trigger\"\n      v-bind=\"delegatedProps\"\n      :class=\"\n        cn(\n          'focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180',\n          props.class,\n        )\n      \"\n    >\n      <slot />\n      <slot name=\"icon\">\n        <ChevronDown\n          class=\"text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200\"\n        />\n      </slot>\n    </AccordionTrigger>\n  </AccordionHeader>\n</template>\n"
  },
  {
    "path": "app/components/ui/accordion/index.ts",
    "content": "export { default as Accordion } from \"./Accordion.vue\"\nexport { default as AccordionContent } from \"./AccordionContent.vue\"\nexport { default as AccordionItem } from \"./AccordionItem.vue\"\nexport { default as AccordionTrigger } from \"./AccordionTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/alert/Alert.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { AlertVariants } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { alertVariants } from \".\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  variant?: AlertVariants[\"variant\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"alert\"\n    :class=\"cn(alertVariants({ variant }), props.class)\"\n    role=\"alert\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert/AlertDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"alert-description\"\n    :class=\"cn('text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert/AlertTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"alert-title\"\n    :class=\"cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as Alert } from \"./Alert.vue\"\nexport { default as AlertDescription } from \"./AlertDescription.vue\"\nexport { default as AlertTitle } from \"./AlertTitle.vue\"\n\nexport const alertVariants = cva(\n  \"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-card text-card-foreground\",\n        destructive:\n          \"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\n\nexport type AlertVariants = VariantProps<typeof alertVariants>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialog.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogEmits, AlertDialogProps } from \"reka-ui\"\nimport { AlertDialogRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<AlertDialogProps>()\nconst emits = defineEmits<AlertDialogEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <AlertDialogRoot v-slot=\"slotProps\" data-slot=\"alert-dialog\" v-bind=\"forwarded\">\n    <slot v-bind=\"slotProps\" />\n  </AlertDialogRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogAction.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogActionProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { AlertDialogAction } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AlertDialogAction v-bind=\"delegatedProps\" :class=\"cn(buttonVariants(), props.class)\">\n    <slot />\n  </AlertDialogAction>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogCancel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogCancelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { AlertDialogCancel } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AlertDialogCancel\n    v-bind=\"delegatedProps\"\n    :class=\"cn(\n      buttonVariants({ variant: 'outline' }),\n      'mt-2 sm:mt-0',\n      props.class,\n    )\"\n  >\n    <slot />\n  </AlertDialogCancel>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogContentEmits, AlertDialogContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  AlertDialogContent,\n  AlertDialogOverlay,\n  AlertDialogPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<AlertDialogContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <AlertDialogPortal>\n    <AlertDialogOverlay\n      data-slot=\"alert-dialog-overlay\"\n      class=\"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80\"\n    />\n    <AlertDialogContent\n      data-slot=\"alert-dialog-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"\n        cn(\n          'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',\n          props.class,\n        )\n      \"\n    >\n      <slot />\n    </AlertDialogContent>\n  </AlertDialogPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogDescriptionProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  AlertDialogDescription,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AlertDialogDescription\n    data-slot=\"alert-dialog-description\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('text-muted-foreground text-sm', props.class)\"\n  >\n    <slot />\n  </AlertDialogDescription>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"alert-dialog-footer\"\n    :class=\"\n      cn(\n        'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"alert-dialog-header\"\n    :class=\"cn('flex flex-col gap-2 text-center sm:text-left', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogTitleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { AlertDialogTitle } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AlertDialogTitle\n    data-slot=\"alert-dialog-title\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('text-lg font-semibold', props.class)\"\n  >\n    <slot />\n  </AlertDialogTitle>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/AlertDialogTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AlertDialogTriggerProps } from \"reka-ui\"\nimport { AlertDialogTrigger } from \"reka-ui\"\n\nconst props = defineProps<AlertDialogTriggerProps>()\n</script>\n\n<template>\n  <AlertDialogTrigger data-slot=\"alert-dialog-trigger\" v-bind=\"props\">\n    <slot />\n  </AlertDialogTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/alert-dialog/index.ts",
    "content": "export { default as AlertDialog } from \"./AlertDialog.vue\"\nexport { default as AlertDialogAction } from \"./AlertDialogAction.vue\"\nexport { default as AlertDialogCancel } from \"./AlertDialogCancel.vue\"\nexport { default as AlertDialogContent } from \"./AlertDialogContent.vue\"\nexport { default as AlertDialogDescription } from \"./AlertDialogDescription.vue\"\nexport { default as AlertDialogFooter } from \"./AlertDialogFooter.vue\"\nexport { default as AlertDialogHeader } from \"./AlertDialogHeader.vue\"\nexport { default as AlertDialogTitle } from \"./AlertDialogTitle.vue\"\nexport { default as AlertDialogTrigger } from \"./AlertDialogTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/aspect-ratio/AspectRatio.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AspectRatioProps } from \"reka-ui\"\nimport { AspectRatio } from \"reka-ui\"\n\nconst props = defineProps<AspectRatioProps>()\n</script>\n\n<template>\n  <AspectRatio\n    v-slot=\"slotProps\"\n    data-slot=\"aspect-ratio\"\n    v-bind=\"props\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </AspectRatio>\n</template>\n"
  },
  {
    "path": "app/components/ui/aspect-ratio/index.ts",
    "content": "export { default as AspectRatio } from \"./AspectRatio.vue\"\n"
  },
  {
    "path": "app/components/ui/avatar/Avatar.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { AvatarRoot } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <AvatarRoot\n    data-slot=\"avatar\"\n    :class=\"cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', props.class)\"\n  >\n    <slot />\n  </AvatarRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/avatar/AvatarFallback.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AvatarFallbackProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { AvatarFallback } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<AvatarFallbackProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <AvatarFallback\n    data-slot=\"avatar-fallback\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-muted flex size-full items-center justify-center rounded-full', props.class)\"\n  >\n    <slot />\n  </AvatarFallback>\n</template>\n"
  },
  {
    "path": "app/components/ui/avatar/AvatarImage.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AvatarImageProps } from \"reka-ui\"\nimport { AvatarImage } from \"reka-ui\"\n\nconst props = defineProps<AvatarImageProps>()\n</script>\n\n<template>\n  <AvatarImage\n    data-slot=\"avatar-image\"\n    v-bind=\"props\"\n    class=\"aspect-square size-full\"\n  >\n    <slot />\n  </AvatarImage>\n</template>\n"
  },
  {
    "path": "app/components/ui/avatar/index.ts",
    "content": "export { default as Avatar } from \"./Avatar.vue\"\nexport { default as AvatarFallback } from \"./AvatarFallback.vue\"\nexport { default as AvatarImage } from \"./AvatarImage.vue\"\n"
  },
  {
    "path": "app/components/ui/badge/Badge.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { BadgeVariants } from \".\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { badgeVariants } from \".\"\n\nconst props = defineProps<PrimitiveProps & {\n  variant?: BadgeVariants[\"variant\"]\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"badge\"\n    :class=\"cn(badgeVariants({ variant }), props.class)\"\n    v-bind=\"delegatedProps\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/badge/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as Badge } from \"./Badge.vue\"\n\nexport const badgeVariants = cva(\n  \"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n        secondary:\n          \"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n        destructive:\n         \"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n        outline:\n          \"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\nexport type BadgeVariants = VariantProps<typeof badgeVariants>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/Breadcrumb.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <nav\n    aria-label=\"breadcrumb\"\n    data-slot=\"breadcrumb\"\n    :class=\"props.class\"\n  >\n    <slot />\n  </nav>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/BreadcrumbEllipsis.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { MoreHorizontal } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"breadcrumb-ellipsis\"\n    role=\"presentation\"\n    aria-hidden=\"true\"\n    :class=\"cn('flex size-9 items-center justify-center', props.class)\"\n  >\n    <slot>\n      <MoreHorizontal class=\"size-4\" />\n    </slot>\n    <span class=\"sr-only\">More</span>\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/BreadcrumbItem.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <li\n    data-slot=\"breadcrumb-item\"\n    :class=\"cn('inline-flex items-center gap-1.5', props.class)\"\n  >\n    <slot />\n  </li>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/BreadcrumbLink.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  as: \"a\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"breadcrumb-link\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"cn('hover:text-foreground transition-colors', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/BreadcrumbList.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <ol\n    data-slot=\"breadcrumb-list\"\n    :class=\"cn('text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5', props.class)\"\n  >\n    <slot />\n  </ol>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/BreadcrumbPage.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"breadcrumb-page\"\n    role=\"link\"\n    aria-disabled=\"true\"\n    aria-current=\"page\"\n    :class=\"cn('text-foreground font-normal', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/BreadcrumbSeparator.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { ChevronRight } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <li\n    data-slot=\"breadcrumb-separator\"\n    role=\"presentation\"\n    aria-hidden=\"true\"\n    :class=\"cn('[&>svg]:size-3.5', props.class)\"\n  >\n    <slot>\n      <ChevronRight />\n    </slot>\n  </li>\n</template>\n"
  },
  {
    "path": "app/components/ui/breadcrumb/index.ts",
    "content": "export { default as Breadcrumb } from \"./Breadcrumb.vue\"\nexport { default as BreadcrumbEllipsis } from \"./BreadcrumbEllipsis.vue\"\nexport { default as BreadcrumbItem } from \"./BreadcrumbItem.vue\"\nexport { default as BreadcrumbLink } from \"./BreadcrumbLink.vue\"\nexport { default as BreadcrumbList } from \"./BreadcrumbList.vue\"\nexport { default as BreadcrumbPage } from \"./BreadcrumbPage.vue\"\nexport { default as BreadcrumbSeparator } from \"./BreadcrumbSeparator.vue\"\n"
  },
  {
    "path": "app/components/ui/button/Button.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from \".\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \".\"\n\ninterface Props extends PrimitiveProps {\n  variant?: ButtonVariants[\"variant\"]\n  size?: ButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  as: \"button\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"button\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"cn(buttonVariants({ variant, size }), props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/button/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as Button } from \"./Button.vue\"\n\nexport const buttonVariants = cva(\n  \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n        outline:\n          \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost:\n          \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        \"default\": \"h-9 px-4 py-2 has-[>svg]:px-3\",\n        \"sm\": \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n        \"lg\": \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n        \"icon\": \"size-9\",\n        \"icon-sm\": \"size-8\",\n        \"icon-lg\": \"size-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n)\nexport type ButtonVariants = VariantProps<typeof buttonVariants>\n"
  },
  {
    "path": "app/components/ui/button-group/ButtonGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonGroupVariants } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonGroupVariants } from \".\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  orientation?: ButtonGroupVariants[\"orientation\"]\n}>()\n</script>\n\n<template>\n  <div\n    role=\"group\"\n    data-slot=\"button-group\"\n    :data-orientation=\"props.orientation\"\n    :class=\"cn(buttonGroupVariants({ orientation: props.orientation }), props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/button-group/ButtonGroupSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { cn } from \"@/lib/utils\"\nimport { Separator } from '@/components/ui/separator'\n\nconst props = withDefaults(defineProps<SeparatorProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  orientation: \"vertical\",\n})\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <Separator\n    data-slot=\"button-group-separator\"\n    v-bind=\"delegatedProps\"\n    :orientation=\"props.orientation\"\n    :class=\"cn(\n      'bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto',\n      props.class,\n    )\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/button-group/ButtonGroupText.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonGroupVariants } from \".\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ninterface Props extends PrimitiveProps {\n  class?: HTMLAttributes[\"class\"]\n  orientation?: ButtonGroupVariants[\"orientation\"]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  as: \"div\",\n})\n</script>\n\n<template>\n  <Primitive\n    role=\"group\"\n    data-slot=\"button-group\"\n    :data-orientation=\"props.orientation\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"cn('bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*=\\'size-\\'])]:size-4', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/button-group/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as ButtonGroup } from \"./ButtonGroup.vue\"\nexport { default as ButtonGroupSeparator } from \"./ButtonGroupSeparator.vue\"\nexport { default as ButtonGroupText } from \"./ButtonGroupText.vue\"\n\nexport const buttonGroupVariants = cva(\n  \"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2\",\n  {\n    variants: {\n      orientation: {\n        horizontal:\n          \"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none\",\n        vertical:\n          \"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none\",\n      },\n    },\n    defaultVariants: {\n      orientation: \"horizontal\",\n    },\n  },\n)\n\nexport type ButtonGroupVariants = VariantProps<typeof buttonGroupVariants>\n"
  },
  {
    "path": "app/components/ui/calendar/Calendar.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarRootEmits, CalendarRootProps, DateValue } from \"reka-ui\"\nimport type { HTMLAttributes, Ref } from \"vue\"\nimport type { LayoutTypes } from \".\"\nimport { getLocalTimeZone, today } from \"@internationalized/date\"\nimport { createReusableTemplate, reactiveOmit, useVModel } from \"@vueuse/core\"\nimport { CalendarRoot, useDateFormatter, useForwardPropsEmits } from \"reka-ui\"\nimport { createYear, createYearRange, toDate } from \"reka-ui/date\"\nimport { computed, toRaw } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'\nimport { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNextButton, CalendarPrevButton } from \".\"\n\nconst props = withDefaults(defineProps<CalendarRootProps & { class?: HTMLAttributes[\"class\"], layout?: LayoutTypes, yearRange?: DateValue[] }>(), {\n  modelValue: undefined,\n  layout: undefined,\n})\nconst emits = defineEmits<CalendarRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"layout\", \"placeholder\")\n\nconst placeholder = useVModel(props, \"placeholder\", emits, {\n  passive: true,\n  defaultValue: props.defaultPlaceholder ?? today(getLocalTimeZone()),\n}) as Ref<DateValue>\n\nconst formatter = useDateFormatter(props.locale ?? \"en\")\n\nconst yearRange = computed(() => {\n  return props.yearRange ?? createYearRange({\n    start: props?.minValue ?? (toRaw(props.placeholder) ?? props.defaultPlaceholder ?? today(getLocalTimeZone()))\n      .cycle(\"year\", -100),\n\n    end: props?.maxValue ?? (toRaw(props.placeholder) ?? props.defaultPlaceholder ?? today(getLocalTimeZone()))\n      .cycle(\"year\", 10),\n  })\n})\n\nconst [DefineMonthTemplate, ReuseMonthTemplate] = createReusableTemplate<{ date: DateValue }>()\nconst [DefineYearTemplate, ReuseYearTemplate] = createReusableTemplate<{ date: DateValue }>()\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DefineMonthTemplate v-slot=\"{ date }\">\n    <div class=\"**:data-[slot=native-select-icon]:right-1\">\n      <div class=\"relative\">\n        <div class=\"absolute inset-0 flex h-full items-center text-sm pl-2 pointer-events-none\">\n          {{ formatter.custom(toDate(date), { month: 'short' }) }}\n        </div>\n        <NativeSelect\n          class=\"text-xs h-8 pr-6 pl-2 text-transparent relative\"\n          @change=\"(e: Event) => {\n            placeholder = placeholder.set({\n              month: Number((e?.target as any)?.value),\n            })\n          }\"\n        >\n          <NativeSelectOption v-for=\"(month) in createYear({ dateObj: date })\" :key=\"month.toString()\" :value=\"month.month\" :selected=\"date.month === month.month\">\n            {{ formatter.custom(toDate(month), { month: 'short' }) }}\n          </NativeSelectOption>\n        </NativeSelect>\n      </div>\n    </div>\n  </DefineMonthTemplate>\n\n  <DefineYearTemplate v-slot=\"{ date }\">\n    <div class=\"**:data-[slot=native-select-icon]:right-1\">\n      <div class=\"relative\">\n        <div class=\"absolute inset-0 flex h-full items-center text-sm pl-2 pointer-events-none\">\n          {{ formatter.custom(toDate(date), { year: 'numeric' }) }}\n        </div>\n        <NativeSelect\n          class=\"text-xs h-8 pr-6 pl-2 text-transparent relative\"\n          @change=\"(e: Event) => {\n            placeholder = placeholder.set({\n              year: Number((e?.target as any)?.value),\n            })\n          }\"\n        >\n          <NativeSelectOption v-for=\"(year) in yearRange\" :key=\"year.toString()\" :value=\"year.year\" :selected=\"date.year === year.year\">\n            {{ formatter.custom(toDate(year), { year: 'numeric' }) }}\n          </NativeSelectOption>\n        </NativeSelect>\n      </div>\n    </div>\n  </DefineYearTemplate>\n\n  <CalendarRoot\n    v-slot=\"{ grid, weekDays, date }\"\n    v-bind=\"forwarded\"\n    v-model:placeholder=\"placeholder\"\n    data-slot=\"calendar\"\n    :class=\"cn('p-3', props.class)\"\n  >\n    <CalendarHeader class=\"pt-0\">\n      <nav class=\"flex items-center gap-1 absolute top-0 inset-x-0 justify-between\">\n        <CalendarPrevButton>\n          <slot name=\"calendar-prev-icon\" />\n        </CalendarPrevButton>\n        <CalendarNextButton>\n          <slot name=\"calendar-next-icon\" />\n        </CalendarNextButton>\n      </nav>\n\n      <slot name=\"calendar-heading\" :date=\"date\" :month=\"ReuseMonthTemplate\" :year=\"ReuseYearTemplate\">\n        <template v-if=\"layout === 'month-and-year'\">\n          <div class=\"flex items-center justify-center gap-1\">\n            <ReuseMonthTemplate :date=\"date\" />\n            <ReuseYearTemplate :date=\"date\" />\n          </div>\n        </template>\n        <template v-else-if=\"layout === 'month-only'\">\n          <div class=\"flex items-center justify-center gap-1\">\n            <ReuseMonthTemplate :date=\"date\" />\n            {{ formatter.custom(toDate(date), { year: 'numeric' }) }}\n          </div>\n        </template>\n        <template v-else-if=\"layout === 'year-only'\">\n          <div class=\"flex items-center justify-center gap-1\">\n            {{ formatter.custom(toDate(date), { month: 'short' }) }}\n            <ReuseYearTemplate :date=\"date\" />\n          </div>\n        </template>\n        <template v-else>\n          <CalendarHeading />\n        </template>\n      </slot>\n    </CalendarHeader>\n\n    <div class=\"flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0\">\n      <CalendarGrid v-for=\"month in grid\" :key=\"month.value.toString()\">\n        <CalendarGridHead>\n          <CalendarGridRow>\n            <CalendarHeadCell\n              v-for=\"day in weekDays\" :key=\"day\"\n            >\n              {{ day }}\n            </CalendarHeadCell>\n          </CalendarGridRow>\n        </CalendarGridHead>\n        <CalendarGridBody>\n          <CalendarGridRow v-for=\"(weekDates, index) in month.rows\" :key=\"`weekDate-${index}`\" class=\"mt-2 w-full\">\n            <CalendarCell\n              v-for=\"weekDate in weekDates\"\n              :key=\"weekDate.toString()\"\n              :date=\"weekDate\"\n            >\n              <CalendarCellTrigger\n                :day=\"weekDate\"\n                :month=\"month.value\"\n              />\n            </CalendarCell>\n          </CalendarGridRow>\n        </CalendarGridBody>\n      </CalendarGrid>\n    </div>\n  </CalendarRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarCell.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarCellProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarCell, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CalendarCellProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarCell\n    data-slot=\"calendar-cell\"\n    :class=\"cn('relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </CalendarCell>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarCellTrigger.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarCellTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarCellTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<CalendarCellTriggerProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  as: \"button\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarCellTrigger\n    data-slot=\"calendar-cell-trigger\"\n    :class=\"cn(\n      buttonVariants({ variant: 'ghost' }),\n      'size-8 p-0 font-normal aria-selected:opacity-100 cursor-default',\n      '[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',\n      // Selected\n      'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground',\n      // Disabled\n      'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',\n      // Unavailable\n      'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',\n      // Outside months\n      'data-[outside-view]:text-muted-foreground',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </CalendarCellTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarGrid.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarGridProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarGrid, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CalendarGridProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarGrid\n    data-slot=\"calendar-grid\"\n    :class=\"cn('w-full border-collapse space-x-1', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </CalendarGrid>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarGridBody.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarGridBodyProps } from \"reka-ui\"\nimport { CalendarGridBody } from \"reka-ui\"\n\nconst props = defineProps<CalendarGridBodyProps>()\n</script>\n\n<template>\n  <CalendarGridBody\n    data-slot=\"calendar-grid-body\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </CalendarGridBody>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarGridHead.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarGridHeadProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { CalendarGridHead } from \"reka-ui\"\n\nconst props = defineProps<CalendarGridHeadProps & { class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <CalendarGridHead\n    data-slot=\"calendar-grid-head\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </CalendarGridHead>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarGridRow.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarGridRowProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarGridRow, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CalendarGridRowProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarGridRow\n    data-slot=\"calendar-grid-row\"\n    :class=\"cn('flex', props.class)\" v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </CalendarGridRow>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarHeadCell.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarHeadCellProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarHeadCell, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CalendarHeadCellProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarHeadCell\n    data-slot=\"calendar-head-cell\"\n    :class=\"cn('text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem]', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </CalendarHeadCell>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarHeader.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarHeaderProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarHeader, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CalendarHeaderProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarHeader\n    data-slot=\"calendar-header\"\n    :class=\"cn('flex justify-center pt-1 relative items-center w-full px-8', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </CalendarHeader>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarHeading.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarHeadingProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CalendarHeading, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CalendarHeadingProps & { class?: HTMLAttributes[\"class\"] }>()\n\ndefineSlots<{\n  default: (props: { headingValue: string }) => any\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarHeading\n    v-slot=\"{ headingValue }\"\n    data-slot=\"calendar-heading\"\n    :class=\"cn('text-sm font-medium', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot :heading-value>\n      {{ headingValue }}\n    </slot>\n  </CalendarHeading>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarNextButton.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarNextProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRight } from \"lucide-vue-next\"\nimport { CalendarNext, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = defineProps<CalendarNextProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarNext\n    data-slot=\"calendar-next-button\"\n    :class=\"cn(\n      buttonVariants({ variant: 'outline' }),\n      'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot>\n      <ChevronRight class=\"size-4\" />\n    </slot>\n  </CalendarNext>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/CalendarPrevButton.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { CalendarPrevProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronLeft } from \"lucide-vue-next\"\nimport { CalendarPrev, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = defineProps<CalendarPrevProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <CalendarPrev\n    data-slot=\"calendar-prev-button\"\n    :class=\"cn(\n      buttonVariants({ variant: 'outline' }),\n      'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot>\n      <ChevronLeft class=\"size-4\" />\n    </slot>\n  </CalendarPrev>\n</template>\n"
  },
  {
    "path": "app/components/ui/calendar/index.ts",
    "content": "export { default as Calendar } from \"./Calendar.vue\"\nexport { default as CalendarCell } from \"./CalendarCell.vue\"\nexport { default as CalendarCellTrigger } from \"./CalendarCellTrigger.vue\"\nexport { default as CalendarGrid } from \"./CalendarGrid.vue\"\nexport { default as CalendarGridBody } from \"./CalendarGridBody.vue\"\nexport { default as CalendarGridHead } from \"./CalendarGridHead.vue\"\nexport { default as CalendarGridRow } from \"./CalendarGridRow.vue\"\nexport { default as CalendarHeadCell } from \"./CalendarHeadCell.vue\"\nexport { default as CalendarHeader } from \"./CalendarHeader.vue\"\nexport { default as CalendarHeading } from \"./CalendarHeading.vue\"\nexport { default as CalendarNextButton } from \"./CalendarNextButton.vue\"\nexport { default as CalendarPrevButton } from \"./CalendarPrevButton.vue\"\n\nexport type LayoutTypes = \"month-and-year\" | \"month-only\" | \"year-only\" | undefined\n"
  },
  {
    "path": "app/components/ui/card/Card.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"card\"\n    :class=\"\n      cn(\n        'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/CardAction.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"card-action\"\n    :class=\"cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/CardContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"card-content\"\n    :class=\"cn('px-6', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/CardDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <p\n    data-slot=\"card-description\"\n    :class=\"cn('text-muted-foreground text-sm', props.class)\"\n  >\n    <slot />\n  </p>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/CardFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"card-footer\"\n    :class=\"cn('flex items-center px-6 [.border-t]:pt-6', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/CardHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"card-header\"\n    :class=\"cn('@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/CardTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <h3\n    data-slot=\"card-title\"\n    :class=\"cn('leading-none font-semibold', props.class)\"\n  >\n    <slot />\n  </h3>\n</template>\n"
  },
  {
    "path": "app/components/ui/card/index.ts",
    "content": "export { default as Card } from \"./Card.vue\"\nexport { default as CardAction } from \"./CardAction.vue\"\nexport { default as CardContent } from \"./CardContent.vue\"\nexport { default as CardDescription } from \"./CardDescription.vue\"\nexport { default as CardFooter } from \"./CardFooter.vue\"\nexport { default as CardHeader } from \"./CardHeader.vue\"\nexport { default as CardTitle } from \"./CardTitle.vue\"\n"
  },
  {
    "path": "app/components/ui/carousel/Carousel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CarouselEmits, CarouselProps, WithClassAsProps } from \"./interface\"\nimport { cn } from \"@/lib/utils\"\nimport { useProvideCarousel } from \"./useCarousel\"\n\nconst props = withDefaults(defineProps<CarouselProps & WithClassAsProps>(), {\n  orientation: \"horizontal\",\n})\n\nconst emits = defineEmits<CarouselEmits>()\n\nconst { canScrollNext, canScrollPrev, carouselApi, carouselRef, orientation, scrollNext, scrollPrev } = useProvideCarousel(props, emits)\n\ndefineExpose({\n  canScrollNext,\n  canScrollPrev,\n  carouselApi,\n  carouselRef,\n  orientation,\n  scrollNext,\n  scrollPrev,\n})\n\nfunction onKeyDown(event: KeyboardEvent) {\n  const prevKey = props.orientation === \"vertical\" ? \"ArrowUp\" : \"ArrowLeft\"\n  const nextKey = props.orientation === \"vertical\" ? \"ArrowDown\" : \"ArrowRight\"\n\n  if (event.key === prevKey) {\n    event.preventDefault()\n    scrollPrev()\n\n    return\n  }\n\n  if (event.key === nextKey) {\n    event.preventDefault()\n    scrollNext()\n  }\n}\n</script>\n\n<template>\n  <div\n    data-slot=\"carousel\"\n    :class=\"cn('relative', props.class)\"\n    role=\"region\"\n    aria-roledescription=\"carousel\"\n    tabindex=\"0\"\n    @keydown=\"onKeyDown\"\n  >\n    <slot :can-scroll-next :can-scroll-prev :carousel-api :carousel-ref :orientation :scroll-next :scroll-prev />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/carousel/CarouselContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from \"./interface\"\nimport { cn } from \"@/lib/utils\"\nimport { useCarousel } from \"./useCarousel\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<WithClassAsProps>()\n\nconst { carouselRef, orientation } = useCarousel()\n</script>\n\n<template>\n  <div\n    ref=\"carouselRef\"\n    data-slot=\"carousel-content\"\n    class=\"overflow-hidden\"\n  >\n    <div\n      :class=\"\n        cn(\n          'flex',\n          orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\n          props.class,\n        )\"\n      v-bind=\"$attrs\"\n    >\n      <slot />\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/carousel/CarouselItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from \"./interface\"\nimport { cn } from \"@/lib/utils\"\nimport { useCarousel } from \"./useCarousel\"\n\nconst props = defineProps<WithClassAsProps>()\n\nconst { orientation } = useCarousel()\n</script>\n\n<template>\n  <div\n    data-slot=\"carousel-item\"\n    role=\"group\"\n    aria-roledescription=\"slide\"\n    :class=\"cn(\n      'min-w-0 shrink-0 grow-0 basis-full',\n      orientation === 'horizontal' ? 'pl-4' : 'pt-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/carousel/CarouselNext.vue",
    "content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from \"./interface\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { ArrowRight } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from '@/components/ui/button'\nimport { useCarousel } from \"./useCarousel\"\n\nconst props = withDefaults(defineProps<{\n  variant?: ButtonVariants[\"variant\"]\n  size?: ButtonVariants[\"size\"]\n}\n& WithClassAsProps>(), {\n  variant: \"outline\",\n  size: \"icon\",\n})\n\nconst { orientation, canScrollNext, scrollNext } = useCarousel()\n</script>\n\n<template>\n  <Button\n    data-slot=\"carousel-next\"\n    :disabled=\"!canScrollNext\"\n    :class=\"cn(\n      'absolute size-8 rounded-full',\n      orientation === 'horizontal'\n        ? 'top-1/2 -right-12 -translate-y-1/2'\n        : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\n      props.class,\n    )\"\n    :variant=\"variant\"\n    :size=\"size\"\n    @click=\"scrollNext\"\n  >\n    <slot>\n      <ArrowRight />\n      <span class=\"sr-only\">Next Slide</span>\n    </slot>\n  </Button>\n</template>\n"
  },
  {
    "path": "app/components/ui/carousel/CarouselPrevious.vue",
    "content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from \"./interface\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { ArrowLeft } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from '@/components/ui/button'\nimport { useCarousel } from \"./useCarousel\"\n\nconst props = withDefaults(defineProps<{\n  variant?: ButtonVariants[\"variant\"]\n  size?: ButtonVariants[\"size\"]\n}\n& WithClassAsProps>(), {\n  variant: \"outline\",\n  size: \"icon\",\n})\n\nconst { orientation, canScrollPrev, scrollPrev } = useCarousel()\n</script>\n\n<template>\n  <Button\n    data-slot=\"carousel-previous\"\n    :disabled=\"!canScrollPrev\"\n    :class=\"cn(\n      'absolute size-8 rounded-full',\n      orientation === 'horizontal'\n        ? 'top-1/2 -left-12 -translate-y-1/2'\n        : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\n      props.class,\n    )\"\n    :variant=\"variant\"\n    :size=\"size\"\n    @click=\"scrollPrev\"\n  >\n    <slot>\n      <ArrowLeft />\n      <span class=\"sr-only\">Previous Slide</span>\n    </slot>\n  </Button>\n</template>\n"
  },
  {
    "path": "app/components/ui/carousel/index.ts",
    "content": "export { default as Carousel } from \"./Carousel.vue\"\nexport { default as CarouselContent } from \"./CarouselContent.vue\"\nexport { default as CarouselItem } from \"./CarouselItem.vue\"\nexport { default as CarouselNext } from \"./CarouselNext.vue\"\nexport { default as CarouselPrevious } from \"./CarouselPrevious.vue\"\nexport type {\n  UnwrapRefCarouselApi as CarouselApi,\n} from \"./interface\"\n\nexport { useCarousel } from \"./useCarousel\"\n"
  },
  {
    "path": "app/components/ui/carousel/interface.ts",
    "content": "import type useEmblaCarousel from \"embla-carousel-vue\"\nimport type {\n  EmblaCarouselVueType,\n} from \"embla-carousel-vue\"\nimport type { HTMLAttributes, UnwrapRef } from \"vue\"\n\ntype CarouselApi = EmblaCarouselVueType[1]\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>\ntype CarouselOptions = UseCarouselParameters[0]\ntype CarouselPlugin = UseCarouselParameters[1]\n\nexport type UnwrapRefCarouselApi = UnwrapRef<CarouselApi>\n\nexport interface CarouselProps {\n  opts?: CarouselOptions\n  plugins?: CarouselPlugin\n  orientation?: \"horizontal\" | \"vertical\"\n}\n\nexport interface CarouselEmits {\n  (e: \"init-api\", payload: UnwrapRefCarouselApi): void\n}\n\nexport interface WithClassAsProps {\n  class?: HTMLAttributes[\"class\"]\n}\n"
  },
  {
    "path": "app/components/ui/carousel/useCarousel.ts",
    "content": "import type { UnwrapRefCarouselApi as CarouselApi, CarouselEmits, CarouselProps } from \"./interface\"\nimport { createInjectionState } from \"@vueuse/core\"\nimport emblaCarouselVue from \"embla-carousel-vue\"\nimport { onMounted, ref } from \"vue\"\n\nconst [useProvideCarousel, useInjectCarousel] = createInjectionState(\n  ({\n    opts,\n    orientation,\n    plugins,\n  }: CarouselProps, emits: CarouselEmits) => {\n    const [emblaNode, emblaApi] = emblaCarouselVue({\n      ...opts,\n      axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n    }, plugins)\n\n    function scrollPrev() {\n      emblaApi.value?.scrollPrev()\n    }\n    function scrollNext() {\n      emblaApi.value?.scrollNext()\n    }\n\n    const canScrollNext = ref(false)\n    const canScrollPrev = ref(false)\n\n    function onSelect(api: CarouselApi) {\n      canScrollNext.value = api?.canScrollNext() || false\n      canScrollPrev.value = api?.canScrollPrev() || false\n    }\n\n    onMounted(() => {\n      if (!emblaApi.value)\n        return\n\n      emblaApi.value?.on(\"init\", onSelect)\n      emblaApi.value?.on(\"reInit\", onSelect)\n      emblaApi.value?.on(\"select\", onSelect)\n\n      emits(\"init-api\", emblaApi.value)\n    })\n\n    return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, orientation }\n  },\n)\n\nfunction useCarousel() {\n  const carouselState = useInjectCarousel()\n\n  if (!carouselState)\n    throw new Error(\"useCarousel must be used within a <Carousel />\")\n\n  return carouselState\n}\n\nexport { useCarousel, useProvideCarousel }\n"
  },
  {
    "path": "app/components/ui/chart/ChartContainer.vue",
    "content": "<script lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { ChartConfig } from \".\"\nimport { useId } from \"reka-ui\"\nimport { computed, toRefs } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { provideChartContext } from \".\"\nimport ChartStyle from \"./ChartStyle.vue\"\n</script>\n\n<script setup lang=\"ts\">\nconst props = defineProps<{\n  id?: HTMLAttributes[\"id\"]\n  class?: HTMLAttributes[\"class\"]\n  config: ChartConfig\n  cursor?: boolean\n}>()\n\ndefineSlots<{\n  default: {\n    id: string\n    config: ChartConfig\n  }\n}>()\n\nconst { config } = toRefs(props)\nconst uniqueId = useId()\nconst chartId = computed(() => `chart-${props.id || uniqueId.replace(/:/g, \"\")}`)\n\nprovideChartContext({\n  id: uniqueId,\n  config,\n})\n</script>\n\n<template>\n  <div\n    data-slot=\"chart\"\n    :data-chart=\"chartId\"\n    :class=\"cn(\n      `[&_.tick_text]:!fill-muted-foreground [&_.tick_line]:!stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex flex-col aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden [&_[data-vis-xy-container]]:h-full [&_[data-vis-single-container]]:h-full h-full [&_[data-vis-xy-container]]:w-full [&_[data-vis-single-container]]:w-full w-full `,\n      props.class,\n    )\"\n    :style=\"{\n      '--vis-tooltip-padding': '0px',\n      '--vis-tooltip-background-color': 'transparent',\n      '--vis-tooltip-border-color': 'transparent',\n      '--vis-tooltip-text-color': 'none',\n      '--vis-tooltip-shadow-color': 'none',\n      '--vis-tooltip-backdrop-filter': 'none',\n      '--vis-crosshair-circle-stroke-color': '#0000',\n      '--vis-crosshair-line-stroke-width': cursor ? '1px' : '0px',\n      '--vis-font-family': 'var(--font-sans)',\n    }\"\n  >\n    <slot :id=\"uniqueId\" :config=\"config\" />\n    <ChartStyle :id=\"chartId\" />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/chart/ChartLegendContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { computed, onMounted, ref } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { useChart } from \".\"\n\nconst props = withDefaults(defineProps<{\n  hideIcon?: boolean\n  nameKey?: string\n  verticalAlign?: \"bottom\" | \"top\"\n  // payload?: any[]\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  verticalAlign: \"bottom\",\n})\n\nconst { id, config } = useChart()\n\nconst payload = computed(() => Object.entries(config.value).map(([key, value]) => {\n  return {\n    key: props.nameKey || key,\n    itemConfig: config.value[key],\n  }\n}))\n\nconst containerSelector = ref(\"\")\nonMounted(() => {\n  containerSelector.value = `[data-chart=\"chart-${id}\"]>[data-vis-xy-container]`\n})\n</script>\n\n<template>\n  <div\n    v-if=\"containerSelector\"\n    :class=\"cn(\n      'flex items-center justify-center gap-4',\n      verticalAlign === 'top' ? 'pb-3' : 'pt-3',\n      props.class,\n    )\"\n  >\n    <div\n      v-for=\"{ key, itemConfig } in payload\"\n      :key=\"key\"\n      :class=\"cn(\n        '[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3',\n      )\"\n    >\n      <component :is=\"itemConfig.icon\" v-if=\"itemConfig?.icon\" />\n      <div\n        v-else\n        class=\"h-2 w-2 shrink-0 rounded-[2px]\"\n        :style=\"{\n          backgroundColor: itemConfig?.color,\n        }\"\n      />\n\n      {{ itemConfig?.label }}\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/chart/ChartStyle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { THEMES, useChart } from \".\"\n\ndefineProps<{\n  id?: HTMLAttributes[\"id\"]\n}>()\n\nconst { config } = useChart()\n\nconst colorConfig = computed(() => {\n  return Object.entries(config.value).filter(\n    ([, config]) => config.theme || config.color,\n  )\n})\n</script>\n\n<template>\n  <Primitive\n    v-if=\"colorConfig.length\"\n    as=\"style\"\n  >\n    {{ Object.entries(THEMES)\n      .map(\n        ([theme, prefix]) => `\n${prefix} [data-chart=${id}] {\n${colorConfig\n  .map(([key, itemConfig]) => {\n    const color\n      = itemConfig.theme?.[theme as keyof typeof itemConfig.theme]\n      || itemConfig.color\n    return color ? `  --color-${key}: ${color};` : null\n  })\n        .join(\"\\n\")}\n}\n`,\n      )\n      .join(\"\\n\") }}\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/chart/ChartTooltipContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { ChartConfig } from \".\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<{\n  hideLabel?: boolean\n  hideIndicator?: boolean\n  indicator?: \"line\" | \"dot\" | \"dashed\"\n  nameKey?: string\n  labelKey?: string\n  labelFormatter?: (d: number | Date) => string\n  payload?: Record<string, any>\n  config?: ChartConfig\n  class?: HTMLAttributes[\"class\"]\n  color?: string\n  x?: number | Date\n}>(), {\n  payload: () => ({}),\n  config: () => ({}),\n  indicator: \"dot\",\n})\n\n// TODO: currently we use `createElement` and `render` to render the\n// const chartContext = useChart(null)\n\nconst payload = computed(() => {\n  return Object.entries(props.payload).map(([key, value]) => {\n    // const key = `${props.nameKey || item.name || item.dataKey || \"value\"}`\n    const itemConfig = props.config[key]\n    const indicatorColor = props.config[key]?.color ?? props.payload.fill\n\n    return { key, value, itemConfig, indicatorColor }\n  }).filter(i => i.itemConfig)\n})\n\nconst nestLabel = computed(() => Object.keys(props.payload).length === 1 && props.indicator !== \"dot\")\nconst tooltipLabel = computed(() => {\n  if (props.hideLabel)\n    return null\n  if (props.labelFormatter && props.x !== undefined) {\n    return props.labelFormatter(props.x)\n  }\n  return props.labelKey ? props.config[props.labelKey]?.label || props.payload[props.labelKey] : props.x\n})\n</script>\n\n<template>\n  <div\n    :class=\"cn(\n      'border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl',\n      props.class,\n    )\"\n  >\n    <slot>\n      <div v-if=\"!nestLabel && tooltipLabel\" class=\"font-medium\">\n        {{ tooltipLabel }}\n      </div>\n      <div class=\"grid gap-1.5\">\n        <div\n          v-for=\"{ value, itemConfig, indicatorColor, key } in payload\"\n          :key=\"key\"\n          :class=\"\n            cn('[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5',\n               indicator === 'dot' && 'items-center')\"\n        >\n          <component :is=\"itemConfig.icon\" v-if=\"itemConfig?.icon\" />\n          <template v-else-if=\"!hideIndicator\">\n            <div\n              :class=\"cn(\n                'shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)',\n                {\n                  'h-2.5 w-2.5': indicator === 'dot',\n                  'w-1': indicator === 'line',\n                  'w-0 border-[1.5px] border-dashed bg-transparent':\n                    indicator === 'dashed',\n                  'my-0.5': nestLabel && indicator === 'dashed',\n                },\n              )\"\n              :style=\"{\n                '--color-bg': indicatorColor,\n                '--color-border': indicatorColor,\n              }\"\n            />\n          </template>\n\n          <div :class=\"cn('flex flex-1 justify-between leading-none', nestLabel ? 'items-end' : 'items-center')\">\n            <div class=\"grid gap-1.5\">\n              <div v-if=\"nestLabel\" class=\"font-medium\">\n                {{ tooltipLabel }}\n              </div>\n              <span class=\"text-muted-foreground\">\n                {{ itemConfig?.label || value }}\n              </span>\n            </div>\n            <span v-if=\"value\" class=\"text-foreground font-mono font-medium tabular-nums\">\n              {{ value.toLocaleString() }}\n            </span>\n          </div>\n        </div>\n      </div>\n    </slot>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/chart/index.ts",
    "content": "import type { Component, Ref } from \"vue\"\nimport { createContext } from \"reka-ui\"\n\nexport { default as ChartContainer } from \"./ChartContainer.vue\"\nexport { default as ChartLegendContent } from \"./ChartLegendContent.vue\"\nexport { default as ChartTooltipContent } from \"./ChartTooltipContent.vue\"\nexport { componentToString } from \"./utils\"\n\n// Format: { THEME_NAME: CSS_SELECTOR }\nexport const THEMES = { light: \"\", dark: \".dark\" } as const\n\nexport type ChartConfig = {\n  [k in string]: {\n    label?: string | Component\n    icon?: string | Component\n  } & (\n    | { color?: string, theme?: never }\n    | { color?: never, theme: Record<keyof typeof THEMES, string> }\n  )\n}\n\ninterface ChartContextProps {\n  id: string\n  config: Ref<ChartConfig>\n}\n\nexport const [useChart, provideChartContext] = createContext<ChartContextProps>(\"Chart\")\n\nexport { VisCrosshair as ChartCrosshair, VisTooltip as ChartTooltip } from \"@unovis/vue\"\n"
  },
  {
    "path": "app/components/ui/chart/utils.ts",
    "content": "import type { ChartConfig } from \".\"\nimport { isClient } from \"@vueuse/core\"\nimport { useId } from \"reka-ui\"\nimport { h, render } from \"vue\"\n\n// Simple cache using a Map to store serialized object keys\nconst cache = new Map<string, string>()\n\n// Convert object to a consistent string key\nfunction serializeKey(key: Record<string, any>): string {\n  return JSON.stringify(key, Object.keys(key).sort())\n}\n\ninterface Constructor<P = any> {\n  __isFragment?: never\n  __isTeleport?: never\n  __isSuspense?: never\n  new (...args: any[]): {\n    $props: P\n  }\n}\n\nexport function componentToString<P>(config: ChartConfig, component: Constructor<P>, props?: P) {\n  if (!isClient)\n    return\n\n  // This function will be called once during mount lifecycle\n  const id = useId()\n\n  // https://unovis.dev/docs/auxiliary/Crosshair#component-props\n  return (_data: any, x: number | Date) => {\n    const data = \"data\" in _data ? _data.data : _data\n    const serializedKey = `${id}-${serializeKey(data)}`\n    const cachedContent = cache.get(serializedKey)\n    if (cachedContent)\n      return cachedContent\n\n    const vnode = h<unknown>(component, { ...props, payload: data, config, x })\n    const div = document.createElement(\"div\")\n    render(vnode, div)\n    cache.set(serializedKey, div.innerHTML)\n    return div.innerHTML\n  }\n}\n"
  },
  {
    "path": "app/components/ui/checkbox/Checkbox.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CheckboxRootEmits, CheckboxRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Check } from \"lucide-vue-next\"\nimport { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<CheckboxRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<CheckboxRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <CheckboxRoot\n    v-slot=\"slotProps\"\n    data-slot=\"checkbox\"\n    v-bind=\"forwarded\"\n    :class=\"\n      cn('peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',\n         props.class)\"\n  >\n    <CheckboxIndicator\n      data-slot=\"checkbox-indicator\"\n      class=\"grid place-content-center text-current transition-none\"\n    >\n      <slot v-bind=\"slotProps\">\n        <Check class=\"size-3.5\" />\n      </slot>\n    </CheckboxIndicator>\n  </CheckboxRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/checkbox/index.ts",
    "content": "export { default as Checkbox } from \"./Checkbox.vue\"\n"
  },
  {
    "path": "app/components/ui/collapsible/Collapsible.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CollapsibleRootEmits, CollapsibleRootProps } from \"reka-ui\"\nimport { CollapsibleRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<CollapsibleRootProps>()\nconst emits = defineEmits<CollapsibleRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <CollapsibleRoot\n    v-slot=\"slotProps\"\n    data-slot=\"collapsible\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </CollapsibleRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/collapsible/CollapsibleContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CollapsibleContentProps } from \"reka-ui\"\nimport { CollapsibleContent } from \"reka-ui\"\n\nconst props = defineProps<CollapsibleContentProps>()\n</script>\n\n<template>\n  <CollapsibleContent\n    data-slot=\"collapsible-content\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </CollapsibleContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/collapsible/CollapsibleTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CollapsibleTriggerProps } from \"reka-ui\"\nimport { CollapsibleTrigger } from \"reka-ui\"\n\nconst props = defineProps<CollapsibleTriggerProps>()\n</script>\n\n<template>\n  <CollapsibleTrigger\n    data-slot=\"collapsible-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </CollapsibleTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/collapsible/index.ts",
    "content": "export { default as Collapsible } from \"./Collapsible.vue\"\nexport { default as CollapsibleContent } from \"./CollapsibleContent.vue\"\nexport { default as CollapsibleTrigger } from \"./CollapsibleTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/combobox/Combobox.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxRootEmits, ComboboxRootProps } from \"reka-ui\"\nimport { ComboboxRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<ComboboxRootProps>()\nconst emits = defineEmits<ComboboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <ComboboxRoot\n    v-slot=\"slotProps\"\n    data-slot=\"combobox\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </ComboboxRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxAnchor.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxAnchorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxAnchor, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ComboboxAnchor\n    data-slot=\"combobox-anchor\"\n    v-bind=\"forwarded\"\n    :class=\"cn('w-[200px]', props.class)\"\n  >\n    <slot />\n  </ComboboxAnchor>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxEmpty.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxEmptyProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxEmpty } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ComboboxEmpty\n    data-slot=\"combobox-empty\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('py-6 text-center text-sm', props.class)\"\n  >\n    <slot />\n  </ComboboxEmpty>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxGroupProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxGroup, ComboboxLabel } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxGroupProps & {\n  class?: HTMLAttributes[\"class\"]\n  heading?: string\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ComboboxGroup\n    data-slot=\"combobox-group\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('overflow-hidden p-1 text-foreground', props.class)\"\n  >\n    <ComboboxLabel v-if=\"heading\" class=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">\n      {{ heading }}\n    </ComboboxLabel>\n    <slot />\n  </ComboboxGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxInputEmits, ComboboxInputProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SearchIcon } from \"lucide-vue-next\"\nimport { ComboboxInput, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<ComboboxInputProps & {\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst emits = defineEmits<ComboboxInputEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <div\n    data-slot=\"command-input-wrapper\"\n    class=\"flex h-9 items-center gap-2 border-b px-3\"\n  >\n    <SearchIcon class=\"size-4 shrink-0 opacity-50\" />\n    <ComboboxInput\n      data-slot=\"command-input\"\n      :class=\"cn(\n        'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',\n        props.class,\n      )\"\n\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n    >\n      <slot />\n    </ComboboxInput>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxItemEmits, ComboboxItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxItem, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<ComboboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ComboboxItem\n    data-slot=\"combobox-item\"\n    v-bind=\"forwarded\"\n    :class=\"cn('data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4', props.class)\"\n  >\n    <slot />\n  </ComboboxItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxItemIndicator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxItemIndicatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxItemIndicator, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxItemIndicatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ComboboxItemIndicator\n    data-slot=\"combobox-item-indicator\"\n    v-bind=\"forwarded\"\n    :class=\"cn('ml-auto', props.class)\"\n  >\n    <slot />\n  </ComboboxItemIndicator>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxList.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxContent, ComboboxPortal, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  position: \"popper\",\n  align: \"center\",\n  sideOffset: 4,\n})\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ComboboxPortal>\n    <ComboboxContent\n      data-slot=\"combobox-list\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground origin-(--reka-combobox-content-transform-origin) overflow-hidden shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)\"\n    >\n      <slot />\n    </ComboboxContent>\n  </ComboboxPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxSeparator } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ComboboxSeparator\n    data-slot=\"combobox-separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-border -mx-1 h-px', props.class)\"\n  >\n    <slot />\n  </ComboboxSeparator>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ComboboxTrigger\n    data-slot=\"combobox-trigger\"\n    v-bind=\"forwarded\"\n    :class=\"cn('', props.class)\"\n    tabindex=\"0\"\n  >\n    <slot />\n  </ComboboxTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/ComboboxViewport.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ComboboxViewportProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ComboboxViewport, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ComboboxViewportProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ComboboxViewport\n    data-slot=\"combobox-viewport\"\n    v-bind=\"forwarded\"\n    :class=\"cn('max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto', props.class)\"\n  >\n    <slot />\n  </ComboboxViewport>\n</template>\n"
  },
  {
    "path": "app/components/ui/combobox/index.ts",
    "content": "export { default as Combobox } from \"./Combobox.vue\"\nexport { default as ComboboxAnchor } from \"./ComboboxAnchor.vue\"\nexport { default as ComboboxEmpty } from \"./ComboboxEmpty.vue\"\nexport { default as ComboboxGroup } from \"./ComboboxGroup.vue\"\nexport { default as ComboboxInput } from \"./ComboboxInput.vue\"\nexport { default as ComboboxItem } from \"./ComboboxItem.vue\"\nexport { default as ComboboxItemIndicator } from \"./ComboboxItemIndicator.vue\"\nexport { default as ComboboxList } from \"./ComboboxList.vue\"\nexport { default as ComboboxSeparator } from \"./ComboboxSeparator.vue\"\nexport { default as ComboboxViewport } from \"./ComboboxViewport.vue\"\n\nexport { ComboboxCancel, ComboboxTrigger } from \"reka-ui\"\n"
  },
  {
    "path": "app/components/ui/command/Command.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ListboxRootEmits, ListboxRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ListboxRoot, useFilter, useForwardPropsEmits } from \"reka-ui\"\nimport { reactive, ref, watch } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { provideCommandContext } from \".\"\n\nconst props = withDefaults(defineProps<ListboxRootProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  modelValue: \"\",\n})\n\nconst emits = defineEmits<ListboxRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n\nconst allItems = ref<Map<string, string>>(new Map())\nconst allGroups = ref<Map<string, Set<string>>>(new Map())\n\nconst { contains } = useFilter({ sensitivity: \"base\" })\nconst filterState = reactive({\n  search: \"\",\n  filtered: {\n    /** The count of all visible items. */\n    count: 0,\n    /** Map from visible item id to its search score. */\n    items: new Map() as Map<string, number>,\n    /** Set of groups with at least one visible item. */\n    groups: new Set() as Set<string>,\n  },\n})\n\nfunction filterItems() {\n  if (!filterState.search) {\n    filterState.filtered.count = allItems.value.size\n    // Do nothing, each item will know to show itself because search is empty\n    return\n  }\n\n  // Reset the groups\n  filterState.filtered.groups = new Set()\n  let itemCount = 0\n\n  // Check which items should be included\n  for (const [id, value] of allItems.value) {\n    const score = contains(value, filterState.search)\n    filterState.filtered.items.set(id, score ? 1 : 0)\n    if (score)\n      itemCount++\n  }\n\n  // Check which groups have at least 1 item shown\n  for (const [groupId, group] of allGroups.value) {\n    for (const itemId of group) {\n      if (filterState.filtered.items.get(itemId)! > 0) {\n        filterState.filtered.groups.add(groupId)\n        break\n      }\n    }\n  }\n\n  filterState.filtered.count = itemCount\n}\n\nwatch(() => filterState.search, () => {\n  filterItems()\n})\n\nprovideCommandContext({\n  allItems,\n  allGroups,\n  filterState,\n})\n</script>\n\n<template>\n  <ListboxRoot\n    data-slot=\"command\"\n    v-bind=\"forwarded\"\n    :class=\"cn('bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', props.class)\"\n  >\n    <slot />\n  </ListboxRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandDialog.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogRootEmits, DialogRootProps } from \"reka-ui\"\nimport { useForwardPropsEmits } from \"reka-ui\"\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'\nimport Command from \"./Command.vue\"\n\nconst props = withDefaults(defineProps<DialogRootProps & {\n  title?: string\n  description?: string\n}>(), {\n  title: \"Command Palette\",\n  description: \"Search for a command to run...\",\n})\nconst emits = defineEmits<DialogRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <Dialog v-slot=\"slotProps\" v-bind=\"forwarded\">\n    <DialogContent class=\"overflow-hidden p-0 \">\n      <DialogHeader class=\"sr-only\">\n        <DialogTitle>{{ title }}</DialogTitle>\n        <DialogDescription>{{ description }}</DialogDescription>\n      </DialogHeader>\n      <Command>\n        <slot v-bind=\"slotProps\" />\n      </Command>\n    </DialogContent>\n  </Dialog>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandEmpty.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { useCommand } from \".\"\n\nconst props = defineProps<PrimitiveProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst { filterState } = useCommand()\nconst isRender = computed(() => !!filterState.search && filterState.filtered.count === 0,\n)\n</script>\n\n<template>\n  <Primitive\n    v-if=\"isRender\"\n    data-slot=\"command-empty\"\n    v-bind=\"delegatedProps\" :class=\"cn('py-6 text-center text-sm', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ListboxGroupProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ListboxGroup, ListboxGroupLabel, useId } from \"reka-ui\"\nimport { computed, onMounted, onUnmounted } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { provideCommandGroupContext, useCommand } from \".\"\n\nconst props = defineProps<ListboxGroupProps & {\n  class?: HTMLAttributes[\"class\"]\n  heading?: string\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst { allGroups, filterState } = useCommand()\nconst id = useId()\n\nconst isRender = computed(() => !filterState.search ? true : filterState.filtered.groups.has(id))\n\nprovideCommandGroupContext({ id })\nonMounted(() => {\n  if (!allGroups.value.has(id))\n    allGroups.value.set(id, new Set())\n})\nonUnmounted(() => {\n  allGroups.value.delete(id)\n})\n</script>\n\n<template>\n  <ListboxGroup\n    v-bind=\"delegatedProps\"\n    :id=\"id\"\n    data-slot=\"command-group\"\n    :class=\"cn('text-foreground overflow-hidden p-1', props.class)\"\n    :hidden=\"isRender ? undefined : true\"\n  >\n    <ListboxGroupLabel v-if=\"heading\" data-slot=\"command-group-heading\" class=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">\n      {{ heading }}\n    </ListboxGroupLabel>\n    <slot />\n  </ListboxGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ListboxFilterProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Search } from \"lucide-vue-next\"\nimport { ListboxFilter, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { useCommand } from \".\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<ListboxFilterProps & {\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n\nconst { filterState } = useCommand()\n</script>\n\n<template>\n  <div\n    data-slot=\"command-input-wrapper\"\n    class=\"flex h-9 items-center gap-2 border-b px-3\"\n  >\n    <Search class=\"size-4 shrink-0 opacity-50\" />\n    <ListboxFilter\n      v-bind=\"{ ...forwardedProps, ...$attrs }\"\n      v-model=\"filterState.search\"\n      data-slot=\"command-input\"\n      auto-focus\n      :class=\"cn('placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50', props.class)\"\n    />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ListboxItemEmits, ListboxItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit, useCurrentElement } from \"@vueuse/core\"\nimport { ListboxItem, useForwardPropsEmits, useId } from \"reka-ui\"\nimport { computed, onMounted, onUnmounted, ref } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { useCommand, useCommandGroup } from \".\"\n\nconst props = defineProps<ListboxItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<ListboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n\nconst id = useId()\nconst { filterState, allItems, allGroups } = useCommand()\nconst groupContext = useCommandGroup()\n\nconst isRender = computed(() => {\n  if (!filterState.search) {\n    return true\n  }\n  else {\n    const filteredCurrentItem = filterState.filtered.items.get(id)\n    // If the filtered items is undefined means not in the all times map yet\n    // Do the first render to add into the map\n    if (filteredCurrentItem === undefined) {\n      return true\n    }\n\n    // Check with filter\n    return filteredCurrentItem > 0\n  }\n})\n\nconst itemRef = ref()\nconst currentElement = useCurrentElement(itemRef)\nonMounted(() => {\n  if (!(currentElement.value instanceof HTMLElement))\n    return\n\n  // textValue to perform filter\n  allItems.value.set(id, currentElement.value.textContent ?? (props.value?.toString() ?? \"\"))\n\n  const groupId = groupContext?.id\n  if (groupId) {\n    if (!allGroups.value.has(groupId)) {\n      allGroups.value.set(groupId, new Set([id]))\n    }\n    else {\n      allGroups.value.get(groupId)?.add(id)\n    }\n  }\n})\nonUnmounted(() => {\n  allItems.value.delete(id)\n})\n</script>\n\n<template>\n  <ListboxItem\n    v-if=\"isRender\"\n    v-bind=\"forwarded\"\n    :id=\"id\"\n    ref=\"itemRef\"\n    data-slot=\"command-item\"\n    :class=\"cn('data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4', props.class)\"\n    @select=\"() => {\n      filterState.search = ''\n    }\"\n  >\n    <slot />\n  </ListboxItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandList.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ListboxContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ListboxContent, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ListboxContentProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ListboxContent\n    data-slot=\"command-list\"\n    v-bind=\"forwarded\"\n    :class=\"cn('max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto', props.class)\"\n  >\n    <div role=\"presentation\">\n      <slot />\n    </div>\n  </ListboxContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Separator } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <Separator\n    data-slot=\"command-separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-border -mx-1 h-px', props.class)\"\n  >\n    <slot />\n  </Separator>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/CommandShortcut.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"command-shortcut\"\n    :class=\"cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/command/index.ts",
    "content": "import type { Ref } from \"vue\"\nimport { createContext } from \"reka-ui\"\n\nexport { default as Command } from \"./Command.vue\"\nexport { default as CommandDialog } from \"./CommandDialog.vue\"\nexport { default as CommandEmpty } from \"./CommandEmpty.vue\"\nexport { default as CommandGroup } from \"./CommandGroup.vue\"\nexport { default as CommandInput } from \"./CommandInput.vue\"\nexport { default as CommandItem } from \"./CommandItem.vue\"\nexport { default as CommandList } from \"./CommandList.vue\"\nexport { default as CommandSeparator } from \"./CommandSeparator.vue\"\nexport { default as CommandShortcut } from \"./CommandShortcut.vue\"\n\nexport const [useCommand, provideCommandContext] = createContext<{\n  allItems: Ref<Map<string, string>>\n  allGroups: Ref<Map<string, Set<string>>>\n  filterState: {\n    search: string\n    filtered: { count: number, items: Map<string, number>, groups: Set<string> }\n  }\n}>(\"Command\")\n\nexport const [useCommandGroup, provideCommandGroupContext] = createContext<{\n  id?: string\n}>(\"CommandGroup\")\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenu.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuRootEmits, ContextMenuRootProps } from \"reka-ui\"\nimport { ContextMenuRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<ContextMenuRootProps>()\nconst emits = defineEmits<ContextMenuRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <ContextMenuRoot\n    data-slot=\"context-menu\"\n    v-bind=\"forwarded\"\n  >\n    <slot />\n  </ContextMenuRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuCheckboxItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuCheckboxItemEmits, ContextMenuCheckboxItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Check } from \"lucide-vue-next\"\nimport {\n  ContextMenuCheckboxItem,\n  ContextMenuItemIndicator,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<ContextMenuCheckboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ContextMenuCheckboxItem\n    data-slot=\"context-menu-checkbox-item\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n      <ContextMenuItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Check class=\"size-4\" />\n        </slot>\n      </ContextMenuItemIndicator>\n    </span>\n    <slot />\n  </ContextMenuCheckboxItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuContentEmits, ContextMenuContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  ContextMenuContent,\n  ContextMenuPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<ContextMenuContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ContextMenuPortal>\n    <ContextMenuContent\n      data-slot=\"context-menu-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"cn(\n        'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-context-menu-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',\n        props.class,\n      )\"\n    >\n      <slot />\n    </ContextMenuContent>\n  </ContextMenuPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuGroupProps } from \"reka-ui\"\nimport { ContextMenuGroup } from \"reka-ui\"\n\nconst props = defineProps<ContextMenuGroupProps>()\n</script>\n\n<template>\n  <ContextMenuGroup\n    data-slot=\"context-menu-group\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </ContextMenuGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuItemEmits, ContextMenuItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  ContextMenuItem,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<ContextMenuItemProps & {\n  class?: HTMLAttributes[\"class\"]\n  inset?: boolean\n  variant?: \"default\" | \"destructive\"\n}>(), {\n  variant: \"default\",\n})\nconst emits = defineEmits<ContextMenuItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ContextMenuItem\n    data-slot=\"context-menu-item\"\n    :data-inset=\"inset ? '' : undefined\"\n    :data-variant=\"variant\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </ContextMenuItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuLabel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuLabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ContextMenuLabel } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ContextMenuLabel\n    data-slot=\"context-menu-label\"\n    :data-inset=\"inset ? '' : undefined\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', props.class)\"\n  >\n    <slot />\n  </ContextMenuLabel>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuPortal.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuPortalProps } from \"reka-ui\"\nimport { ContextMenuPortal } from \"reka-ui\"\n\nconst props = defineProps<ContextMenuPortalProps>()\n</script>\n\n<template>\n  <ContextMenuPortal\n    data-slot=\"context-menu-portal\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </ContextMenuPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuRadioGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuRadioGroupEmits, ContextMenuRadioGroupProps } from \"reka-ui\"\nimport {\n  ContextMenuRadioGroup,\n  useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<ContextMenuRadioGroupProps>()\nconst emits = defineEmits<ContextMenuRadioGroupEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <ContextMenuRadioGroup\n    data-slot=\"context-menu-radio-group\"\n    v-bind=\"forwarded\"\n  >\n    <slot />\n  </ContextMenuRadioGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuRadioItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuRadioItemEmits, ContextMenuRadioItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Circle } from \"lucide-vue-next\"\nimport {\n  ContextMenuItemIndicator,\n  ContextMenuRadioItem,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<ContextMenuRadioItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ContextMenuRadioItem\n    data-slot=\"context-menu-radio-item\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n      <ContextMenuItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Circle class=\"size-2 fill-current\" />\n        </slot>\n      </ContextMenuItemIndicator>\n    </span>\n    <slot />\n  </ContextMenuRadioItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  ContextMenuSeparator,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ContextMenuSeparator\n    data-slot=\"context-menu-separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-border -mx-1 my-1 h-px', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuShortcut.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"context-menu-shortcut\"\n    :class=\"cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuSub.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuSubEmits, ContextMenuSubProps } from \"reka-ui\"\nimport {\n  ContextMenuSub,\n  useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<ContextMenuSubProps>()\nconst emits = defineEmits<ContextMenuSubEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <ContextMenuSub\n    data-slot=\"context-menu-sub\"\n    v-bind=\"forwarded\"\n  >\n    <slot />\n  </ContextMenuSub>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuSubContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubContentEmits, DropdownMenuSubContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  ContextMenuSubContent,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DropdownMenuSubContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ContextMenuSubContent\n    data-slot=\"context-menu-sub-content\"\n    v-bind=\"forwarded\"\n    :class=\"\n      cn(\n        'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--reka-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </ContextMenuSubContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuSubTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuSubTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRight } from \"lucide-vue-next\"\nimport {\n  ContextMenuSubTrigger,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ContextMenuSubTrigger\n    data-slot=\"context-menu-sub-trigger\"\n    :data-inset=\"inset ? '' : undefined\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <slot />\n    <ChevronRight class=\"ml-auto\" />\n  </ContextMenuSubTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/ContextMenuTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContextMenuTriggerProps } from \"reka-ui\"\nimport { ContextMenuTrigger, useForwardProps } from \"reka-ui\"\n\nconst props = defineProps<ContextMenuTriggerProps>()\n\nconst forwardedProps = useForwardProps(props)\n</script>\n\n<template>\n  <ContextMenuTrigger\n    data-slot=\"context-menu-trigger\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </ContextMenuTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/context-menu/index.ts",
    "content": "export { default as ContextMenu } from \"./ContextMenu.vue\"\nexport { default as ContextMenuCheckboxItem } from \"./ContextMenuCheckboxItem.vue\"\nexport { default as ContextMenuContent } from \"./ContextMenuContent.vue\"\nexport { default as ContextMenuGroup } from \"./ContextMenuGroup.vue\"\nexport { default as ContextMenuItem } from \"./ContextMenuItem.vue\"\nexport { default as ContextMenuLabel } from \"./ContextMenuLabel.vue\"\nexport { default as ContextMenuRadioGroup } from \"./ContextMenuRadioGroup.vue\"\nexport { default as ContextMenuRadioItem } from \"./ContextMenuRadioItem.vue\"\nexport { default as ContextMenuSeparator } from \"./ContextMenuSeparator.vue\"\nexport { default as ContextMenuShortcut } from \"./ContextMenuShortcut.vue\"\nexport { default as ContextMenuSub } from \"./ContextMenuSub.vue\"\nexport { default as ContextMenuSubContent } from \"./ContextMenuSubContent.vue\"\nexport { default as ContextMenuSubTrigger } from \"./ContextMenuSubTrigger.vue\"\nexport { default as ContextMenuTrigger } from \"./ContextMenuTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/dialog/Dialog.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogRootEmits, DialogRootProps } from \"reka-ui\"\nimport { DialogRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<DialogRootProps>()\nconst emits = defineEmits<DialogRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DialogRoot\n    v-slot=\"slotProps\"\n    data-slot=\"dialog\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </DialogRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogClose.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogCloseProps } from \"reka-ui\"\nimport { DialogClose } from \"reka-ui\"\n\nconst props = defineProps<DialogCloseProps>()\n</script>\n\n<template>\n  <DialogClose\n    data-slot=\"dialog-close\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DialogClose>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogContentEmits, DialogContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { X } from \"lucide-vue-next\"\nimport {\n  DialogClose,\n  DialogContent,\n  DialogPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport DialogOverlay from \"./DialogOverlay.vue\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<DialogContentProps & { class?: HTMLAttributes[\"class\"], showCloseButton?: boolean }>(), {\n  showCloseButton: true,\n})\nconst emits = defineEmits<DialogContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DialogPortal>\n    <DialogOverlay />\n    <DialogContent\n      data-slot=\"dialog-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"\n        cn(\n          'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',\n          props.class,\n        )\"\n    >\n      <slot />\n\n      <DialogClose\n        v-if=\"showCloseButton\"\n        data-slot=\"dialog-close\"\n        class=\"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n      >\n        <X />\n        <span class=\"sr-only\">Close</span>\n      </DialogClose>\n    </DialogContent>\n  </DialogPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogDescriptionProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogDescription, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <DialogDescription\n    data-slot=\"dialog-description\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('text-muted-foreground text-sm', props.class)\"\n  >\n    <slot />\n  </DialogDescription>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <div\n    data-slot=\"dialog-footer\"\n    :class=\"cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"dialog-header\"\n    :class=\"cn('flex flex-col gap-2 text-center sm:text-left', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogOverlay.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogOverlayProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogOverlay } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogOverlayProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DialogOverlay\n    data-slot=\"dialog-overlay\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)\"\n  >\n    <slot />\n  </DialogOverlay>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogScrollContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogContentEmits, DialogContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { X } from \"lucide-vue-next\"\nimport {\n  DialogClose,\n  DialogContent,\n  DialogOverlay,\n  DialogPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<DialogContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DialogContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DialogPortal>\n    <DialogOverlay\n      class=\"fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\"\n    >\n      <DialogContent\n        :class=\"\n          cn(\n            'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',\n            props.class,\n          )\n        \"\n        v-bind=\"{ ...$attrs, ...forwarded }\"\n        @pointer-down-outside=\"(event) => {\n          const originalEvent = event.detail.originalEvent;\n          const target = originalEvent.target as HTMLElement;\n          if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) {\n            event.preventDefault();\n          }\n        }\"\n      >\n        <slot />\n\n        <DialogClose\n          class=\"absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary\"\n        >\n          <X class=\"w-4 h-4\" />\n          <span class=\"sr-only\">Close</span>\n        </DialogClose>\n      </DialogContent>\n    </DialogOverlay>\n  </DialogPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogTitleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogTitle, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogTitleProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <DialogTitle\n    data-slot=\"dialog-title\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('text-lg leading-none font-semibold', props.class)\"\n  >\n    <slot />\n  </DialogTitle>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/DialogTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogTriggerProps } from \"reka-ui\"\nimport { DialogTrigger } from \"reka-ui\"\n\nconst props = defineProps<DialogTriggerProps>()\n</script>\n\n<template>\n  <DialogTrigger\n    data-slot=\"dialog-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DialogTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/dialog/index.ts",
    "content": "export { default as Dialog } from \"./Dialog.vue\"\nexport { default as DialogClose } from \"./DialogClose.vue\"\nexport { default as DialogContent } from \"./DialogContent.vue\"\nexport { default as DialogDescription } from \"./DialogDescription.vue\"\nexport { default as DialogFooter } from \"./DialogFooter.vue\"\nexport { default as DialogHeader } from \"./DialogHeader.vue\"\nexport { default as DialogOverlay } from \"./DialogOverlay.vue\"\nexport { default as DialogScrollContent } from \"./DialogScrollContent.vue\"\nexport { default as DialogTitle } from \"./DialogTitle.vue\"\nexport { default as DialogTrigger } from \"./DialogTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/drawer/Drawer.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DrawerRootEmits, DrawerRootProps } from \"vaul-vue\"\nimport { useForwardPropsEmits } from \"reka-ui\"\nimport { DrawerRoot } from \"vaul-vue\"\n\nconst props = withDefaults(defineProps<DrawerRootProps>(), {\n  shouldScaleBackground: true,\n})\n\nconst emits = defineEmits<DrawerRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DrawerRoot\n    v-slot=\"slotProps\"\n    data-slot=\"drawer\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </DrawerRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerClose.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DrawerCloseProps } from \"vaul-vue\"\nimport { DrawerClose } from \"vaul-vue\"\n\nconst props = defineProps<DrawerCloseProps>()\n</script>\n\n<template>\n  <DrawerClose\n    data-slot=\"drawer-close\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DrawerClose>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerContent.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DialogContentEmits, DialogContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { useForwardPropsEmits } from \"reka-ui\"\nimport { DrawerContent, DrawerPortal } from \"vaul-vue\"\nimport { cn } from \"@/lib/utils\"\nimport DrawerOverlay from \"./DrawerOverlay.vue\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<DialogContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DialogContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DrawerPortal>\n    <DrawerOverlay />\n    <DrawerContent\n      data-slot=\"drawer-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"cn(\n        'group/drawer-content bg-background fixed z-50 flex h-auto flex-col',\n        'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg',\n        'data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg',\n        'data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:sm:max-w-sm',\n        'data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:sm:max-w-sm',\n        props.class,\n      )\"\n    >\n      <div class=\"bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n      <slot />\n    </DrawerContent>\n  </DrawerPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerDescription.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DrawerDescriptionProps } from \"vaul-vue\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DrawerDescription } from \"vaul-vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DrawerDescriptionProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DrawerDescription\n    data-slot=\"drawer-description\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('text-muted-foreground text-sm', props.class)\"\n  >\n    <slot />\n  </DrawerDescription>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerFooter.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"drawer-footer\"\n    :class=\"cn('mt-auto flex flex-col gap-2 p-4', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerHeader.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"drawer-header\"\n    :class=\"cn('flex flex-col gap-1.5 p-4', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerOverlay.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DialogOverlayProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DrawerOverlay } from \"vaul-vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogOverlayProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DrawerOverlay\n    data-slot=\"drawer-overlay\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerTitle.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DrawerTitleProps } from \"vaul-vue\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DrawerTitle } from \"vaul-vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DrawerTitleProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DrawerTitle\n    data-slot=\"drawer-title\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('text-foreground font-semibold', props.class)\"\n  >\n    <slot />\n  </DrawerTitle>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/DrawerTrigger.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { DrawerTriggerProps } from \"vaul-vue\"\nimport { DrawerTrigger } from \"vaul-vue\"\n\nconst props = defineProps<DrawerTriggerProps>()\n</script>\n\n<template>\n  <DrawerTrigger\n    data-slot=\"drawer-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DrawerTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/drawer/index.ts",
    "content": "export { default as Drawer } from \"./Drawer.vue\"\nexport { default as DrawerClose } from \"./DrawerClose.vue\"\nexport { default as DrawerContent } from \"./DrawerContent.vue\"\nexport { default as DrawerDescription } from \"./DrawerDescription.vue\"\nexport { default as DrawerFooter } from \"./DrawerFooter.vue\"\nexport { default as DrawerHeader } from \"./DrawerHeader.vue\"\nexport { default as DrawerOverlay } from \"./DrawerOverlay.vue\"\nexport { default as DrawerTitle } from \"./DrawerTitle.vue\"\nexport { default as DrawerTrigger } from \"./DrawerTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenu.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuRootEmits, DropdownMenuRootProps } from \"reka-ui\"\nimport { DropdownMenuRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuRootProps>()\nconst emits = defineEmits<DropdownMenuRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DropdownMenuRoot\n    v-slot=\"slotProps\"\n    data-slot=\"dropdown-menu\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </DropdownMenuRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Check } from \"lucide-vue-next\"\nimport {\n  DropdownMenuCheckboxItem,\n  DropdownMenuItemIndicator,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DropdownMenuCheckboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DropdownMenuCheckboxItem\n    data-slot=\"dropdown-menu-checkbox-item\"\n    v-bind=\"forwarded\"\n    :class=\" cn(\n      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n      <DropdownMenuItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Check class=\"size-4\" />\n        </slot>\n      </DropdownMenuItemIndicator>\n    </span>\n    <slot />\n  </DropdownMenuCheckboxItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuContentEmits, DropdownMenuContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  DropdownMenuContent,\n  DropdownMenuPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<DropdownMenuContentProps & { class?: HTMLAttributes[\"class\"] }>(),\n  {\n    sideOffset: 4,\n  },\n)\nconst emits = defineEmits<DropdownMenuContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DropdownMenuPortal>\n    <DropdownMenuContent\n      data-slot=\"dropdown-menu-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-dropdown-menu-content-available-height) min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', props.class)\"\n    >\n      <slot />\n    </DropdownMenuContent>\n  </DropdownMenuPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuGroupProps } from \"reka-ui\"\nimport { DropdownMenuGroup } from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuGroupProps>()\n</script>\n\n<template>\n  <DropdownMenuGroup\n    data-slot=\"dropdown-menu-group\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DropdownMenuGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DropdownMenuItem, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<DropdownMenuItemProps & {\n  class?: HTMLAttributes[\"class\"]\n  inset?: boolean\n  variant?: \"default\" | \"destructive\"\n}>(), {\n  variant: \"default\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"inset\", \"variant\", \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <DropdownMenuItem\n    data-slot=\"dropdown-menu-item\"\n    :data-inset=\"inset ? '' : undefined\"\n    :data-variant=\"variant\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4', props.class)\"\n  >\n    <slot />\n  </DropdownMenuItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuLabel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuLabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DropdownMenuLabel, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <DropdownMenuLabel\n    data-slot=\"dropdown-menu-label\"\n    :data-inset=\"inset ? '' : undefined\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', props.class)\"\n  >\n    <slot />\n  </DropdownMenuLabel>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuRadioGroupEmits, DropdownMenuRadioGroupProps } from \"reka-ui\"\nimport {\n  DropdownMenuRadioGroup,\n  useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuRadioGroupProps>()\nconst emits = defineEmits<DropdownMenuRadioGroupEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DropdownMenuRadioGroup\n    data-slot=\"dropdown-menu-radio-group\"\n    v-bind=\"forwarded\"\n  >\n    <slot />\n  </DropdownMenuRadioGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuRadioItemEmits, DropdownMenuRadioItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Circle } from \"lucide-vue-next\"\nimport {\n  DropdownMenuItemIndicator,\n  DropdownMenuRadioItem,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuRadioItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst emits = defineEmits<DropdownMenuRadioItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DropdownMenuRadioItem\n    data-slot=\"dropdown-menu-radio-item\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n      <DropdownMenuItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Circle class=\"size-2 fill-current\" />\n        </slot>\n      </DropdownMenuItemIndicator>\n    </span>\n    <slot />\n  </DropdownMenuRadioItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  DropdownMenuSeparator,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSeparatorProps & {\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DropdownMenuSeparator\n    data-slot=\"dropdown-menu-separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-border -mx-1 my-1 h-px', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuShortcut.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"dropdown-menu-shortcut\"\n    :class=\"cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuSub.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubEmits, DropdownMenuSubProps } from \"reka-ui\"\nimport {\n  DropdownMenuSub,\n  useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuSubProps>()\nconst emits = defineEmits<DropdownMenuSubEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DropdownMenuSub v-slot=\"slotProps\" data-slot=\"dropdown-menu-sub\" v-bind=\"forwarded\">\n    <slot v-bind=\"slotProps\" />\n  </DropdownMenuSub>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuSubContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubContentEmits, DropdownMenuSubContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  DropdownMenuSubContent,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DropdownMenuSubContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DropdownMenuSubContent\n    data-slot=\"dropdown-menu-sub-content\"\n    v-bind=\"forwarded\"\n    :class=\"cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg', props.class)\"\n  >\n    <slot />\n  </DropdownMenuSubContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRight } from \"lucide-vue-next\"\nimport {\n  DropdownMenuSubTrigger,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSubTriggerProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <DropdownMenuSubTrigger\n    data-slot=\"dropdown-menu-sub-trigger\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',\n      props.class,\n    )\"\n  >\n    <slot />\n    <ChevronRight class=\"ml-auto size-4\" />\n  </DropdownMenuSubTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/DropdownMenuTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DropdownMenuTriggerProps } from \"reka-ui\"\nimport { DropdownMenuTrigger, useForwardProps } from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuTriggerProps>()\n\nconst forwardedProps = useForwardProps(props)\n</script>\n\n<template>\n  <DropdownMenuTrigger\n    data-slot=\"dropdown-menu-trigger\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </DropdownMenuTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/dropdown-menu/index.ts",
    "content": "export { default as DropdownMenu } from \"./DropdownMenu.vue\"\n\nexport { default as DropdownMenuCheckboxItem } from \"./DropdownMenuCheckboxItem.vue\"\nexport { default as DropdownMenuContent } from \"./DropdownMenuContent.vue\"\nexport { default as DropdownMenuGroup } from \"./DropdownMenuGroup.vue\"\nexport { default as DropdownMenuItem } from \"./DropdownMenuItem.vue\"\nexport { default as DropdownMenuLabel } from \"./DropdownMenuLabel.vue\"\nexport { default as DropdownMenuRadioGroup } from \"./DropdownMenuRadioGroup.vue\"\nexport { default as DropdownMenuRadioItem } from \"./DropdownMenuRadioItem.vue\"\nexport { default as DropdownMenuSeparator } from \"./DropdownMenuSeparator.vue\"\nexport { default as DropdownMenuShortcut } from \"./DropdownMenuShortcut.vue\"\nexport { default as DropdownMenuSub } from \"./DropdownMenuSub.vue\"\nexport { default as DropdownMenuSubContent } from \"./DropdownMenuSubContent.vue\"\nexport { default as DropdownMenuSubTrigger } from \"./DropdownMenuSubTrigger.vue\"\nexport { default as DropdownMenuTrigger } from \"./DropdownMenuTrigger.vue\"\nexport { DropdownMenuPortal } from \"reka-ui\"\n"
  },
  {
    "path": "app/components/ui/empty/Empty.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"empty\"\n    :class=\"cn(\n      'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/empty/EmptyContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"empty-content\"\n    :class=\"cn(\n      'flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/empty/EmptyDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\ndefineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <p\n    data-slot=\"empty-description\"\n    :class=\"cn(\n      'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',\n      $attrs.class ?? '',\n    )\"\n  >\n    <slot />\n  </p>\n</template>\n"
  },
  {
    "path": "app/components/ui/empty/EmptyHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"empty-header\"\n    :class=\"cn(\n      'flex max-w-sm flex-col items-center gap-2 text-center',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/empty/EmptyMedia.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { EmptyMediaVariants } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { emptyMediaVariants } from \".\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  variant?: EmptyMediaVariants[\"variant\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"empty-icon\"\n    :data-variant=\"variant\"\n    :class=\"cn(emptyMediaVariants({ variant }), props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/empty/EmptyTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"empty-title\"\n    :class=\"cn('text-lg font-medium tracking-tight', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/empty/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as Empty } from \"./Empty.vue\"\nexport { default as EmptyContent } from \"./EmptyContent.vue\"\nexport { default as EmptyDescription } from \"./EmptyDescription.vue\"\nexport { default as EmptyHeader } from \"./EmptyHeader.vue\"\nexport { default as EmptyMedia } from \"./EmptyMedia.vue\"\nexport { default as EmptyTitle } from \"./EmptyTitle.vue\"\n\nexport const emptyMediaVariants = cva(\n  \"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        icon: \"bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\n\nexport type EmptyMediaVariants = VariantProps<typeof emptyMediaVariants>\n"
  },
  {
    "path": "app/components/ui/field/Field.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { FieldVariants } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { fieldVariants } from \".\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  orientation?: FieldVariants[\"orientation\"]\n}>()\n</script>\n\n<template>\n  <div\n    role=\"group\"\n    data-slot=\"field\"\n    :data-orientation=\"orientation\"\n    :class=\"cn(\n      fieldVariants({ orientation }),\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"field-content\"\n    :class=\"cn(\n      'group/field-content flex flex-1 flex-col gap-1.5 leading-snug',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <p\n    data-slot=\"field-description\"\n    :class=\"cn(\n      'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance',\n      'last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5',\n      '[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </p>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldError.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  errors?: Array<string | { message: string | undefined } | undefined>\n}>()\n\nconst content = computed(() => {\n  if (!props.errors || props.errors.length === 0)\n    return null\n\n  const uniqueErrors = [\n    ...new Map(\n      props.errors\n        .filter(Boolean)\n        .map((error) => {\n          const message = typeof error === \"string\" ? error : error?.message\n          return [message, error]\n        }),\n    ).values(),\n  ]\n\n  if (uniqueErrors.length === 1 && uniqueErrors[0]) {\n    return typeof uniqueErrors[0] === \"string\" ? uniqueErrors[0] : uniqueErrors[0].message\n  }\n\n  return uniqueErrors.map(error => typeof error === \"string\" ? error : error?.message)\n})\n</script>\n\n<template>\n  <div\n    v-if=\"$slots.default || content\"\n    role=\"alert\"\n    data-slot=\"field-error\"\n    :class=\"cn('text-destructive text-sm font-normal', props.class)\"\n  >\n    <slot v-if=\"$slots.default\" />\n\n    <template v-else-if=\"typeof content === 'string'\">\n      {{ content }}\n    </template>\n\n    <ul v-else-if=\"Array.isArray(content)\" class=\"ml-4 flex list-disc flex-col gap-1\">\n      <li v-for=\"(error, index) in content\" :key=\"index\">\n        {{ error }}\n      </li>\n    </ul>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"field-group\"\n    :class=\"cn(\n      'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldLabel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Label } from '@/components/ui/label'\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Label\n    data-slot=\"field-label\"\n    :class=\"cn(\n      'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',\n      'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4',\n      'has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10',\n      props.class,\n    )\"\n  >\n    <slot />\n  </Label>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldLegend.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  variant?: \"legend\" | \"label\"\n}>()\n</script>\n\n<template>\n  <legend\n    data-slot=\"field-legend\"\n    :data-variant=\"variant\"\n    :class=\"cn(\n      'mb-3 font-medium',\n      'data-[variant=legend]:text-base',\n      'data-[variant=label]:text-sm',\n      props.class,\n    )\"\n  >\n    <slot />\n  </legend>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Separator } from '@/components/ui/separator'\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"field-separator\"\n    :data-content=\"!!$slots.default\"\n    :class=\"cn(\n      'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',\n      props.class,\n    )\"\n  >\n    <Separator class=\"absolute inset-0 top-1/2\" />\n    <span\n      v-if=\"$slots.default\"\n      class=\"bg-background text-muted-foreground relative mx-auto block w-fit px-2\"\n      data-slot=\"field-separator-content\"\n    >\n      <slot />\n    </span>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldSet.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <fieldset\n    data-slot=\"field-set\"\n    :class=\"cn(\n      'flex flex-col gap-6',\n      'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',\n      props.class,\n    )\"\n  >\n    <slot />\n  </fieldset>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/FieldTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"field-label\"\n    :class=\"cn(\n      'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/field/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport const fieldVariants = cva(\n  \"group/field flex w-full gap-3 data-[invalid=true]:text-destructive\",\n  {\n    variants: {\n      orientation: {\n        vertical: [\"flex-col [&>*]:w-full [&>.sr-only]:w-auto\"],\n        horizontal: [\n          \"flex-row items-center\",\n          \"[&>[data-slot=field-label]]:flex-auto\",\n          \"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n        ],\n        responsive: [\n          \"flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto\",\n          \"@md/field-group:[&>[data-slot=field-label]]:flex-auto\",\n          \"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n        ],\n      },\n    },\n    defaultVariants: {\n      orientation: \"vertical\",\n    },\n  },\n)\n\nexport type FieldVariants = VariantProps<typeof fieldVariants>\n\nexport { default as Field } from \"./Field.vue\"\nexport { default as FieldContent } from \"./FieldContent.vue\"\nexport { default as FieldDescription } from \"./FieldDescription.vue\"\nexport { default as FieldError } from \"./FieldError.vue\"\nexport { default as FieldGroup } from \"./FieldGroup.vue\"\nexport { default as FieldLabel } from \"./FieldLabel.vue\"\nexport { default as FieldLegend } from \"./FieldLegend.vue\"\nexport { default as FieldSeparator } from \"./FieldSeparator.vue\"\nexport { default as FieldSet } from \"./FieldSet.vue\"\nexport { default as FieldTitle } from \"./FieldTitle.vue\"\n"
  },
  {
    "path": "app/components/ui/hover-card/HoverCard.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HoverCardRootEmits, HoverCardRootProps } from \"reka-ui\"\nimport { HoverCardRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<HoverCardRootProps>()\nconst emits = defineEmits<HoverCardRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <HoverCardRoot\n    v-slot=\"slotProps\"\n    data-slot=\"hover-card\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </HoverCardRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/hover-card/HoverCardContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HoverCardContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  HoverCardContent,\n  HoverCardPortal,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<HoverCardContentProps & { class?: HTMLAttributes[\"class\"] }>(),\n  {\n    sideOffset: 4,\n  },\n)\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <HoverCardPortal>\n    <HoverCardContent\n      data-slot=\"hover-card-content\"\n      v-bind=\"{ ...$attrs, ...forwardedProps }\"\n      :class=\"\n        cn(\n          'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border p-4 shadow-md outline-hidden',\n          props.class,\n        )\n      \"\n    >\n      <slot />\n    </HoverCardContent>\n  </HoverCardPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/hover-card/HoverCardTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HoverCardTriggerProps } from \"reka-ui\"\nimport { HoverCardTrigger } from \"reka-ui\"\n\nconst props = defineProps<HoverCardTriggerProps>()\n</script>\n\n<template>\n  <HoverCardTrigger\n    data-slot=\"hover-card-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </HoverCardTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/hover-card/index.ts",
    "content": "export { default as HoverCard } from \"./HoverCard.vue\"\nexport { default as HoverCardContent } from \"./HoverCardContent.vue\"\nexport { default as HoverCardTrigger } from \"./HoverCardTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/input/Input.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { useVModel } from \"@vueuse/core\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  defaultValue?: string | number\n  modelValue?: string | number\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst emits = defineEmits<{\n  (e: \"update:modelValue\", payload: string | number): void\n}>()\n\nconst modelValue = useVModel(props, \"modelValue\", emits, {\n  passive: true,\n  defaultValue: props.defaultValue,\n})\n</script>\n\n<template>\n  <input\n    v-model=\"modelValue\"\n    data-slot=\"input\"\n    :class=\"cn(\n      'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n      'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n      'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n      props.class,\n    )\"\n  >\n</template>\n"
  },
  {
    "path": "app/components/ui/input/index.ts",
    "content": "export { default as Input } from \"./Input.vue\"\n"
  },
  {
    "path": "app/components/ui/input-group/InputGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"input-group\"\n    role=\"group\"\n    :class=\"cn(\n      'group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none',\n      'h-9 min-w-0 has-[>textarea]:h-auto',\n\n      // Variants based on alignment.\n      'has-[>[data-align=inline-start]]:[&>input]:pl-2',\n      'has-[>[data-align=inline-end]]:[&>input]:pr-2',\n      'has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3',\n      'has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3',\n\n      // Focus state.\n      'has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]',\n\n      // Error state.\n      'has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40',\n\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-group/InputGroupAddon.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { InputGroupVariants } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { inputGroupAddonVariants } from \".\"\n\nconst props = withDefaults(defineProps<{\n  align?: InputGroupVariants[\"align\"]\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  align: \"inline-start\",\n})\n\nfunction handleInputGroupAddonClick(e: MouseEvent) {\n  const currentTarget = e.currentTarget as HTMLElement | null\n  const target = e.target as HTMLElement | null\n  if (target && target.closest(\"button\")) {\n    return\n  }\n  if (currentTarget && currentTarget?.parentElement) {\n    currentTarget.parentElement?.querySelector(\"input\")?.focus()\n  }\n}\n</script>\n\n<template>\n  <div\n    role=\"group\"\n    data-slot=\"input-group-addon\"\n    :data-align=\"props.align\"\n    :class=\"cn(inputGroupAddonVariants({ align: props.align }), props.class)\"\n    @click=\"handleInputGroupAddonClick\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-group/InputGroupButton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { InputGroupButtonProps } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from '@/components/ui/button'\nimport { inputGroupButtonVariants } from \".\"\n\nconst props = withDefaults(defineProps<InputGroupButtonProps>(), {\n  size: \"xs\",\n  variant: \"ghost\",\n})\n</script>\n\n<template>\n  <Button\n    :data-size=\"props.size\"\n    :variant=\"props.variant\"\n    :class=\"cn(inputGroupButtonVariants({ size: props.size }), props.class)\"\n  >\n    <slot />\n  </Button>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-group/InputGroupInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Input } from '@/components/ui/input'\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Input\n    data-slot=\"input-group-control\"\n    :class=\"cn(\n      'flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',\n      props.class,\n    )\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/input-group/InputGroupText.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    :class=\"cn(\n      'text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-group/InputGroupTextarea.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Textarea } from '@/components/ui/textarea'\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Textarea\n    data-slot=\"input-group-control\"\n    :class=\"cn(\n      'flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent',\n      props.class,\n    )\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/input-group/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { cva } from \"class-variance-authority\"\n\nexport { default as InputGroup } from \"./InputGroup.vue\"\nexport { default as InputGroupAddon } from \"./InputGroupAddon.vue\"\nexport { default as InputGroupButton } from \"./InputGroupButton.vue\"\nexport { default as InputGroupInput } from \"./InputGroupInput.vue\"\nexport { default as InputGroupText } from \"./InputGroupText.vue\"\nexport { default as InputGroupTextarea } from \"./InputGroupTextarea.vue\"\n\nexport const inputGroupAddonVariants = cva(\n  \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n  {\n    variants: {\n      align: {\n        \"inline-start\":\n          \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n        \"inline-end\":\n          \"order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]\",\n        \"block-start\":\n          \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n        \"block-end\":\n          \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\",\n      },\n    },\n    defaultVariants: {\n      align: \"inline-start\",\n    },\n  },\n)\n\nexport type InputGroupVariants = VariantProps<typeof inputGroupAddonVariants>\n\nexport const inputGroupButtonVariants = cva(\n  \"text-sm shadow-none flex gap-2 items-center\",\n  {\n    variants: {\n      size: {\n        \"xs\": \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n        \"sm\": \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n        \"icon-xs\": \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n        \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\",\n      },\n    },\n    defaultVariants: {\n      size: \"xs\",\n    },\n  },\n)\n\nexport type InputGroupButtonVariants = VariantProps<typeof inputGroupButtonVariants>\n\nexport interface InputGroupButtonProps {\n  variant?: ButtonVariants[\"variant\"]\n  size?: InputGroupButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n}\n"
  },
  {
    "path": "app/components/ui/input-otp/InputOTP.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { OTPInputEmits, OTPInputProps } from \"vue-input-otp\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { useForwardPropsEmits } from \"reka-ui\"\nimport { OTPInput } from \"vue-input-otp\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<OTPInputProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst emits = defineEmits<OTPInputEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <OTPInput\n    v-slot=\"slotProps\"\n    v-bind=\"forwarded\"\n    :container-class=\"cn('flex items-center gap-2 has-disabled:opacity-50', props.class)\"\n    data-slot=\"input-otp\"\n    class=\"disabled:cursor-not-allowed\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </OTPInput>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-otp/InputOTPGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <div\n    data-slot=\"input-otp-group\"\n    v-bind=\"forwarded\"\n    :class=\"cn('flex items-center', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-otp/InputOTPSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { MinusIcon } from \"lucide-vue-next\"\nimport { useForwardProps } from \"reka-ui\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n\nconst forwarded = useForwardProps(props)\n</script>\n\n<template>\n  <div\n    data-slot=\"input-otp-separator\"\n    role=\"separator\"\n    v-bind=\"forwarded\"\n  >\n    <slot>\n      <MinusIcon />\n    </slot>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-otp/InputOTPSlot.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { useForwardProps } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { useVueOTPContext } from \"vue-input-otp\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ index: number, class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n\nconst context = useVueOTPContext()\n\nconst slot = computed(() => context?.value.slots[props.index])\n</script>\n\n<template>\n  <div\n    v-bind=\"forwarded\"\n    data-slot=\"input-otp-slot\"\n    :data-active=\"slot?.isActive\"\n    :class=\"cn('data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]', props.class)\"\n  >\n    {{ slot?.char }}\n    <div v-if=\"slot?.hasFakeCaret\" class=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n      <div class=\"animate-caret-blink bg-foreground h-4 w-px duration-1000\" />\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/input-otp/index.ts",
    "content": "export { default as InputOTP } from \"./InputOTP.vue\"\nexport { default as InputOTPGroup } from \"./InputOTPGroup.vue\"\nexport { default as InputOTPSeparator } from \"./InputOTPSeparator.vue\"\nexport { default as InputOTPSlot } from \"./InputOTPSlot.vue\"\n"
  },
  {
    "path": "app/components/ui/item/Item.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ItemVariants } from \".\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { itemVariants } from \".\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  variant?: ItemVariants[\"variant\"]\n  size?: ItemVariants[\"size\"]\n}>(), {\n  as: \"div\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"item\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"cn(itemVariants({ variant, size }), props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemActions.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"item-actions\"\n    :class=\"cn('flex items-center gap-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"item-content\"\n    :class=\"cn('flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <p\n    data-slot=\"item-description\"\n    :class=\"cn(\n      'text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance',\n      '[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </p>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"item-footer\"\n    :class=\"cn('flex basis-full items-center justify-between gap-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    role=\"list\"\n    data-slot=\"item-group\"\n    :class=\"cn('group/item-group flex flex-col', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"item-header\"\n    :class=\"cn('flex basis-full items-center justify-between gap-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemMedia.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { ItemMediaVariants } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { itemMediaVariants } from \".\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  variant?: ItemMediaVariants[\"variant\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"item-media\"\n    :data-variant=\"props.variant\"\n    :class=\"cn(itemMediaVariants({ variant }), props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Separator } from '@/components/ui/separator'\n\nconst props = defineProps<\n  SeparatorProps & { class?: HTMLAttributes[\"class\"] }\n>()\n</script>\n\n<template>\n  <Separator\n    data-slot=\"item-separator\"\n    orientation=\"horizontal\"\n    :class=\"cn('my-0', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/item/ItemTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"item-title\"\n    :class=\"cn('flex w-fit items-center gap-2 text-sm leading-snug font-medium', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/item/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as Item } from \"./Item.vue\"\nexport { default as ItemActions } from \"./ItemActions.vue\"\nexport { default as ItemContent } from \"./ItemContent.vue\"\nexport { default as ItemDescription } from \"./ItemDescription.vue\"\nexport { default as ItemFooter } from \"./ItemFooter.vue\"\nexport { default as ItemGroup } from \"./ItemGroup.vue\"\nexport { default as ItemHeader } from \"./ItemHeader.vue\"\nexport { default as ItemMedia } from \"./ItemMedia.vue\"\nexport { default as ItemSeparator } from \"./ItemSeparator.vue\"\nexport { default as ItemTitle } from \"./ItemTitle.vue\"\n\nexport const itemVariants = cva(\n  \"group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        outline: \"border-border\",\n        muted: \"bg-muted/50\",\n      },\n      size: {\n        default: \"p-4 gap-4 \",\n        sm: \"py-3 px-4 gap-2.5\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n)\n\nexport const itemMediaVariants = cva(\n  \"flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        icon: \"size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4\",\n        image:\n          \"size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\n\nexport type ItemVariants = VariantProps<typeof itemVariants>\nexport type ItemMediaVariants = VariantProps<typeof itemMediaVariants>\n"
  },
  {
    "path": "app/components/ui/kbd/Kbd.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <kbd\n    :class=\"cn(\n      'bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none',\n      '[&_svg:not([class*=\\'size-\\'])]:size-3',\n      '[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10',\n      props.class,\n    )\"\n  >\n    <slot />\n  </kbd>\n</template>\n"
  },
  {
    "path": "app/components/ui/kbd/KbdGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <kbd\n    data-slot=\"kbd-group\"\n    :class=\"cn('inline-flex items-center gap-1', props.class)\"\n  >\n    <slot />\n  </kbd>\n</template>\n"
  },
  {
    "path": "app/components/ui/kbd/index.ts",
    "content": "export { default as Kbd } from \"./Kbd.vue\"\nexport { default as KbdGroup } from \"./KbdGroup.vue\"\n"
  },
  {
    "path": "app/components/ui/label/Label.vue",
    "content": "<script setup lang=\"ts\">\nimport type { LabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Label } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<LabelProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <Label\n    data-slot=\"label\"\n    v-bind=\"delegatedProps\"\n    :class=\"\n      cn(\n        'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </Label>\n</template>\n"
  },
  {
    "path": "app/components/ui/label/index.ts",
    "content": "export { default as Label } from \"./Label.vue\"\n"
  },
  {
    "path": "app/components/ui/menubar/Menubar.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarRootEmits, MenubarRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  MenubarRoot,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<MenubarRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <MenubarRoot\n    v-slot=\"slotProps\"\n    data-slot=\"menubar\"\n    v-bind=\"forwarded\"\n    :class=\"\n      cn(\n        'bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs',\n        props.class,\n      )\n    \"\n  >\n    <slot v-bind=\"slotProps\" />\n  </MenubarRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarCheckboxItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarCheckboxItemEmits, MenubarCheckboxItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Check } from \"lucide-vue-next\"\nimport {\n  MenubarCheckboxItem,\n  MenubarItemIndicator,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarCheckboxItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<MenubarCheckboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <MenubarCheckboxItem\n    data-slot=\"menubar-checkbox-item\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n      <MenubarItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Check class=\"size-4\" />\n        </slot>\n      </MenubarItemIndicator>\n    </span>\n    <slot />\n  </MenubarCheckboxItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  MenubarContent,\n  MenubarPortal,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<MenubarContentProps & { class?: HTMLAttributes[\"class\"] }>(),\n  {\n    align: \"start\",\n    alignOffset: -4,\n    sideOffset: 8,\n  },\n)\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <MenubarPortal>\n    <MenubarContent\n      data-slot=\"menubar-content\"\n      v-bind=\"{ ...$attrs, ...forwardedProps }\"\n      :class=\"\n        cn(\n          'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[12rem] origin-(--reka-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-md',\n          props.class,\n        )\n      \"\n    >\n      <slot />\n    </MenubarContent>\n  </MenubarPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarGroupProps } from \"reka-ui\"\nimport { MenubarGroup } from \"reka-ui\"\n\nconst props = defineProps<MenubarGroupProps>()\n</script>\n\n<template>\n  <MenubarGroup\n    data-slot=\"menubar-group\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </MenubarGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarItemEmits, MenubarItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  MenubarItem,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarItemProps & {\n  class?: HTMLAttributes[\"class\"]\n  inset?: boolean\n  variant?: \"default\" | \"destructive\"\n}>()\n\nconst emits = defineEmits<MenubarItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\", \"variant\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <MenubarItem\n    data-slot=\"menubar-item\"\n    :data-inset=\"inset ? '' : undefined\"\n    :data-variant=\"variant\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <slot />\n  </MenubarItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarLabel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarLabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { MenubarLabel } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarLabelProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\")\n</script>\n\n<template>\n  <MenubarLabel\n    :data-inset=\"inset ? '' : undefined\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', props.class)\"\n  >\n    <slot />\n  </MenubarLabel>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarMenu.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarMenuProps } from \"reka-ui\"\nimport { MenubarMenu } from \"reka-ui\"\n\nconst props = defineProps<MenubarMenuProps>()\n</script>\n\n<template>\n  <MenubarMenu\n    data-slot=\"menubar-menu\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </MenubarMenu>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarRadioGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarRadioGroupEmits, MenubarRadioGroupProps } from \"reka-ui\"\nimport {\n  MenubarRadioGroup,\n  useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<MenubarRadioGroupProps>()\nconst emits = defineEmits<MenubarRadioGroupEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <MenubarRadioGroup\n    data-slot=\"menubar-radio-group\"\n    v-bind=\"forwarded\"\n  >\n    <slot />\n  </MenubarRadioGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarRadioItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarRadioItemEmits, MenubarRadioItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Circle } from \"lucide-vue-next\"\nimport {\n  MenubarItemIndicator,\n  MenubarRadioItem,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarRadioItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<MenubarRadioItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <MenubarRadioItem\n    data-slot=\"menubar-radio-item\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n      <MenubarItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Circle class=\"size-2 fill-current\" />\n        </slot>\n      </MenubarItemIndicator>\n    </span>\n    <slot />\n  </MenubarRadioItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { MenubarSeparator, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarSeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <MenubarSeparator\n    data-slot=\"menubar-separator\"\n    :class=\" cn('bg-border -mx-1 my-1 h-px', props.class)\"\n    v-bind=\"forwardedProps\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarShortcut.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"menubar-shortcut\"\n    :class=\"cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarSub.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarSubEmits } from \"reka-ui\"\nimport { MenubarSub, useForwardPropsEmits } from \"reka-ui\"\n\ninterface MenubarSubRootProps {\n  defaultOpen?: boolean\n  open?: boolean\n}\n\nconst props = defineProps<MenubarSubRootProps>()\nconst emits = defineEmits<MenubarSubEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <MenubarSub\n    v-slot=\"slotProps\"\n    data-slot=\"menubar-sub\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </MenubarSub>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarSubContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarSubContentEmits, MenubarSubContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  MenubarPortal,\n  MenubarSubContent,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<MenubarSubContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<MenubarSubContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <MenubarPortal>\n    <MenubarSubContent\n      data-slot=\"menubar-sub-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"\n        cn(\n          'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--reka-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',\n          props.class,\n        )\n      \"\n    >\n      <slot />\n    </MenubarSubContent>\n  </MenubarPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarSubTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarSubTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRight } from \"lucide-vue-next\"\nimport { MenubarSubTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarSubTriggerProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <MenubarSubTrigger\n    data-slot=\"menubar-sub-trigger\"\n    :data-inset=\"inset ? '' : undefined\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn(\n      'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8',\n      props.class,\n    )\"\n  >\n    <slot />\n    <ChevronRight class=\"ml-auto size-4\" />\n  </MenubarSubTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/MenubarTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MenubarTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { MenubarTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<MenubarTriggerProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <MenubarTrigger\n    data-slot=\"menubar-trigger\"\n    v-bind=\"forwardedProps\"\n    :class=\"\n      cn(\n        'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </MenubarTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/menubar/index.ts",
    "content": "export { default as Menubar } from \"./Menubar.vue\"\nexport { default as MenubarCheckboxItem } from \"./MenubarCheckboxItem.vue\"\nexport { default as MenubarContent } from \"./MenubarContent.vue\"\nexport { default as MenubarGroup } from \"./MenubarGroup.vue\"\nexport { default as MenubarItem } from \"./MenubarItem.vue\"\nexport { default as MenubarLabel } from \"./MenubarLabel.vue\"\nexport { default as MenubarMenu } from \"./MenubarMenu.vue\"\nexport { default as MenubarRadioGroup } from \"./MenubarRadioGroup.vue\"\nexport { default as MenubarRadioItem } from \"./MenubarRadioItem.vue\"\nexport { default as MenubarSeparator } from \"./MenubarSeparator.vue\"\nexport { default as MenubarShortcut } from \"./MenubarShortcut.vue\"\nexport { default as MenubarSub } from \"./MenubarSub.vue\"\nexport { default as MenubarSubContent } from \"./MenubarSubContent.vue\"\nexport { default as MenubarSubTrigger } from \"./MenubarSubTrigger.vue\"\nexport { default as MenubarTrigger } from \"./MenubarTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/native-select/NativeSelect.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AcceptableValue } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit, useVModel } from \"@vueuse/core\"\nimport { ChevronDownIcon } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = defineProps<{ modelValue?: AcceptableValue | AcceptableValue[], class?: HTMLAttributes[\"class\"] }>()\n\nconst emit = defineEmits<{\n  \"update:modelValue\": AcceptableValue\n}>()\n\nconst modelValue = useVModel(props, \"modelValue\", emit, {\n  passive: true,\n  defaultValue: \"\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <div\n    class=\"group/native-select relative w-fit has-[select:disabled]:opacity-50\"\n    data-slot=\"native-select-wrapper\"\n  >\n    <select\n      v-bind=\"{ ...$attrs, ...delegatedProps }\"\n      v-model=\"modelValue\"\n      data-slot=\"native-select\"\n      :class=\"cn(\n        'border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed',\n        'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n        'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n        props.class,\n      )\"\n    >\n      <slot />\n    </select>\n    <ChevronDownIcon\n      class=\"text-muted-foreground pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 opacity-50 select-none\"\n      aria-hidden=\"true\"\n      data-slot=\"native-select-icon\"\n    />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/native-select/NativeSelectOptGroup.vue",
    "content": "<!-- @fallthroughAttributes true -->\n<!-- @strictTemplates true -->\n\n<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <optgroup data-slot=\"native-select-optgroup\" :class=\"cn('bg-popover text-popover-foreground', props.class)\">\n    <slot />\n  </optgroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/native-select/NativeSelectOption.vue",
    "content": "<!-- @fallthroughAttributes true -->\n<!-- @strictTemplates true -->\n\n<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <option data-slot=\"native-select-option\" :class=\"cn('bg-popover text-popover-foreground', props.class)\">\n    <slot />\n  </option>\n</template>\n"
  },
  {
    "path": "app/components/ui/native-select/index.ts",
    "content": "export { default as NativeSelect } from \"./NativeSelect.vue\"\nexport { default as NativeSelectOptGroup } from \"./NativeSelectOptGroup.vue\"\nexport { default as NativeSelectOption } from \"./NativeSelectOption.vue\"\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenu.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuRootEmits, NavigationMenuRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  NavigationMenuRoot,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport NavigationMenuViewport from \"./NavigationMenuViewport.vue\"\n\nconst props = withDefaults(defineProps<NavigationMenuRootProps & {\n  class?: HTMLAttributes[\"class\"]\n  viewport?: boolean\n}>(), {\n  viewport: true,\n})\nconst emits = defineEmits<NavigationMenuRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"viewport\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <NavigationMenuRoot\n    v-slot=\"slotProps\"\n    data-slot=\"navigation-menu\"\n    :data-viewport=\"viewport\"\n    v-bind=\"forwarded\"\n    :class=\"cn('group/navigation-menu relative flex max-w-max flex-1 items-center justify-center', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n    <NavigationMenuViewport v-if=\"viewport\" />\n  </NavigationMenuRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuContentEmits, NavigationMenuContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  NavigationMenuContent,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NavigationMenuContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<NavigationMenuContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <NavigationMenuContent\n    data-slot=\"navigation-menu-content\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto',\n      'group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none',\n      props.class,\n    )\"\n  >\n    <slot />\n  </NavigationMenuContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuIndicator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuIndicatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { NavigationMenuIndicator, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NavigationMenuIndicatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <NavigationMenuIndicator\n    data-slot=\"navigation-menu-indicator\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden', props.class)\"\n  >\n    <div class=\"bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\" />\n  </NavigationMenuIndicator>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { NavigationMenuItem } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NavigationMenuItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <NavigationMenuItem\n    data-slot=\"navigation-menu-item\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('relative', props.class)\"\n  >\n    <slot />\n  </NavigationMenuItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuLink.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuLinkEmits, NavigationMenuLinkProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  NavigationMenuLink,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NavigationMenuLinkProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<NavigationMenuLinkEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <NavigationMenuLink\n    data-slot=\"navigation-menu-link\"\n    v-bind=\"forwarded\"\n    :class=\"cn('data-active:focus:bg-accent data-active:hover:bg-accent data-active:bg-accent/50 data-active:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 [&_svg:not([class*=\\'size-\\'])]:size-4', props.class)\"\n  >\n    <slot />\n  </NavigationMenuLink>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuList.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuListProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { NavigationMenuList, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NavigationMenuListProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <NavigationMenuList\n    data-slot=\"navigation-menu-list\"\n    v-bind=\"forwardedProps\"\n    :class=\"\n      cn(\n        'group flex flex-1 list-none items-center justify-center gap-1',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </NavigationMenuList>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronDown } from \"lucide-vue-next\"\nimport {\n  NavigationMenuTrigger,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { navigationMenuTriggerStyle } from \".\"\n\nconst props = defineProps<NavigationMenuTriggerProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <NavigationMenuTrigger\n    data-slot=\"navigation-menu-trigger\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn(navigationMenuTriggerStyle(), 'group', props.class)\"\n  >\n    <slot />\n    <ChevronDown\n      class=\"relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180\"\n      aria-hidden=\"true\"\n    />\n  </NavigationMenuTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/NavigationMenuViewport.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NavigationMenuViewportProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  NavigationMenuViewport,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NavigationMenuViewportProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <div class=\"absolute top-full left-0 isolate z-50 flex justify-center\">\n    <NavigationMenuViewport\n      data-slot=\"navigation-menu-viewport\"\n      v-bind=\"forwardedProps\"\n      :class=\"\n        cn(\n          'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--reka-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--reka-navigation-menu-viewport-width)] left-[var(--reka-navigation-menu-viewport-left)]',\n          props.class,\n        )\n      \"\n    />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/navigation-menu/index.ts",
    "content": "import { cva } from \"class-variance-authority\"\n\nexport { default as NavigationMenu } from \"./NavigationMenu.vue\"\nexport { default as NavigationMenuContent } from \"./NavigationMenuContent.vue\"\nexport { default as NavigationMenuIndicator } from \"./NavigationMenuIndicator.vue\"\nexport { default as NavigationMenuItem } from \"./NavigationMenuItem.vue\"\nexport { default as NavigationMenuLink } from \"./NavigationMenuLink.vue\"\nexport { default as NavigationMenuList } from \"./NavigationMenuList.vue\"\nexport { default as NavigationMenuTrigger } from \"./NavigationMenuTrigger.vue\"\nexport { default as NavigationMenuViewport } from \"./NavigationMenuViewport.vue\"\n\nexport const navigationMenuTriggerStyle = cva(\n  \"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1\",\n)\n"
  },
  {
    "path": "app/components/ui/number-field/NumberField.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NumberFieldRootEmits, NumberFieldRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { NumberFieldRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NumberFieldRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<NumberFieldRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <NumberFieldRoot v-slot=\"slotProps\" v-bind=\"forwarded\" :class=\"cn('grid gap-1.5', props.class)\">\n    <slot v-bind=\"slotProps\" />\n  </NumberFieldRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/number-field/NumberFieldContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div :class=\"cn('relative [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5 [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5', props.class)\">\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/number-field/NumberFieldDecrement.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NumberFieldDecrementProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Minus } from \"lucide-vue-next\"\nimport { NumberFieldDecrement, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NumberFieldDecrementProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <NumberFieldDecrement data-slot=\"decrement\" v-bind=\"forwarded\" :class=\"cn('absolute top-1/2 -translate-y-1/2 left-0 p-3 disabled:cursor-not-allowed disabled:opacity-20', props.class)\">\n    <slot>\n      <Minus class=\"h-4 w-4\" />\n    </slot>\n  </NumberFieldDecrement>\n</template>\n"
  },
  {
    "path": "app/components/ui/number-field/NumberFieldIncrement.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NumberFieldIncrementProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Plus } from \"lucide-vue-next\"\nimport { NumberFieldIncrement, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<NumberFieldIncrementProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <NumberFieldIncrement data-slot=\"increment\" v-bind=\"forwarded\" :class=\"cn('absolute top-1/2 -translate-y-1/2 right-0 disabled:cursor-not-allowed disabled:opacity-20 p-3', props.class)\">\n    <slot>\n      <Plus class=\"h-4 w-4\" />\n    </slot>\n  </NumberFieldIncrement>\n</template>\n"
  },
  {
    "path": "app/components/ui/number-field/NumberFieldInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { NumberFieldInput } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <NumberFieldInput\n    data-slot=\"input\"\n    :class=\"cn('flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-sm text-center shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/number-field/index.ts",
    "content": "export { default as NumberField } from \"./NumberField.vue\"\nexport { default as NumberFieldContent } from \"./NumberFieldContent.vue\"\nexport { default as NumberFieldDecrement } from \"./NumberFieldDecrement.vue\"\nexport { default as NumberFieldIncrement } from \"./NumberFieldIncrement.vue\"\nexport { default as NumberFieldInput } from \"./NumberFieldInput.vue\"\n"
  },
  {
    "path": "app/components/ui/pagination/Pagination.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationRootEmits, PaginationRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { PaginationRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PaginationRootProps & {\n  class?: HTMLAttributes[\"class\"]\n}>()\nconst emits = defineEmits<PaginationRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <PaginationRoot\n    v-slot=\"slotProps\"\n    data-slot=\"pagination\"\n    v-bind=\"forwarded\"\n    :class=\"cn('mx-auto flex w-full justify-center', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </PaginationRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationListProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { PaginationList } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PaginationListProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <PaginationList\n    v-slot=\"slotProps\"\n    data-slot=\"pagination-content\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('flex flex-row items-center gap-1', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </PaginationList>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationEllipsis.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationEllipsisProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { MoreHorizontal } from \"lucide-vue-next\"\nimport { PaginationEllipsis } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PaginationEllipsisProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <PaginationEllipsis\n    data-slot=\"pagination-ellipsis\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('flex size-9 items-center justify-center', props.class)\"\n  >\n    <slot>\n      <MoreHorizontal class=\"size-4\" />\n      <span class=\"sr-only\">More pages</span>\n    </slot>\n  </PaginationEllipsis>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationFirst.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationFirstProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronLeftIcon } from \"lucide-vue-next\"\nimport { PaginationFirst, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<PaginationFirstProps & {\n  size?: ButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  size: \"default\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\")\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <PaginationFirst\n    data-slot=\"pagination-first\"\n    :class=\"cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)\"\n    v-bind=\"forwarded\"\n  >\n    <slot>\n      <ChevronLeftIcon />\n      <span class=\"hidden sm:block\">First</span>\n    </slot>\n  </PaginationFirst>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationListItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { PaginationListItem } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<PaginationListItemProps & {\n  size?: ButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n  isActive?: boolean\n}>(), {\n  size: \"icon\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\", \"isActive\")\n</script>\n\n<template>\n  <PaginationListItem\n    data-slot=\"pagination-item\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn(\n      buttonVariants({\n        variant: isActive ? 'outline' : 'ghost',\n        size,\n      }),\n      props.class)\"\n  >\n    <slot />\n  </PaginationListItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationLast.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationLastProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRightIcon } from \"lucide-vue-next\"\nimport { PaginationLast, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<PaginationLastProps & {\n  size?: ButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  size: \"default\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\")\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <PaginationLast\n    data-slot=\"pagination-last\"\n    :class=\"cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)\"\n    v-bind=\"forwarded\"\n  >\n    <slot>\n      <span class=\"hidden sm:block\">Last</span>\n      <ChevronRightIcon />\n    </slot>\n  </PaginationLast>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationNext.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationNextProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRightIcon } from \"lucide-vue-next\"\nimport { PaginationNext, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<PaginationNextProps & {\n  size?: ButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  size: \"default\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\")\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <PaginationNext\n    data-slot=\"pagination-next\"\n    :class=\"cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)\"\n    v-bind=\"forwarded\"\n  >\n    <slot>\n      <span class=\"hidden sm:block\">Next</span>\n      <ChevronRightIcon />\n    </slot>\n  </PaginationNext>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/PaginationPrevious.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PaginationPrevProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from '@/components/ui/button'\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronLeftIcon } from \"lucide-vue-next\"\nimport { PaginationPrev, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<PaginationPrevProps & {\n  size?: ButtonVariants[\"size\"]\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  size: \"default\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\")\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <PaginationPrev\n    data-slot=\"pagination-previous\"\n    :class=\"cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)\"\n    v-bind=\"forwarded\"\n  >\n    <slot>\n      <ChevronLeftIcon />\n      <span class=\"hidden sm:block\">Previous</span>\n    </slot>\n  </PaginationPrev>\n</template>\n"
  },
  {
    "path": "app/components/ui/pagination/index.ts",
    "content": "export { default as Pagination } from \"./Pagination.vue\"\nexport { default as PaginationContent } from \"./PaginationContent.vue\"\nexport { default as PaginationEllipsis } from \"./PaginationEllipsis.vue\"\nexport { default as PaginationFirst } from \"./PaginationFirst.vue\"\nexport { default as PaginationItem } from \"./PaginationItem.vue\"\nexport { default as PaginationLast } from \"./PaginationLast.vue\"\nexport { default as PaginationNext } from \"./PaginationNext.vue\"\nexport { default as PaginationPrevious } from \"./PaginationPrevious.vue\"\n"
  },
  {
    "path": "app/components/ui/pin-input/PinInput.vue",
    "content": "<script setup lang=\"ts\" generic=\"Type extends 'text' | 'number' = 'text'\">\nimport type { PinInputRootEmits, PinInputRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { PinInputRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<PinInputRootProps<Type> & { class?: HTMLAttributes[\"class\"] }>(), {\n  otp: true,\n})\nconst emits = defineEmits<PinInputRootEmits<Type>>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <PinInputRoot\n    :otp=\"props.otp\"\n    data-slot=\"pin-input\"\n    v-bind=\"forwarded\" :class=\"cn('flex items-center gap-2 has-disabled:opacity-50 disabled:cursor-not-allowed', props.class)\"\n  >\n    <slot />\n  </PinInputRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/pin-input/PinInputGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Primitive, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PrimitiveProps & { class?: HTMLAttributes[\"class\"] }>()\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"pin-input-group\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('flex items-center', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/pin-input/PinInputSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport { Minus } from \"lucide-vue-next\"\nimport { Primitive, useForwardProps } from \"reka-ui\"\n\nconst props = defineProps<PrimitiveProps>()\nconst forwardedProps = useForwardProps(props)\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"pin-input-separator\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot>\n      <Minus />\n    </slot>\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/pin-input/PinInputSlot.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PinInputInputProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { PinInputInput, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PinInputInputProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <PinInputInput\n    data-slot=\"pin-input-slot\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('border-input focus:border-ring focus:ring-ring/50 focus:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:focus:aria-invalid:ring-destructive/40 aria-invalid:border-destructive focus:aria-invalid:border-destructive relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none text-center first:rounded-l-md first:border-l last:rounded-r-md focus:z-10 focus:ring-[3px]', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/pin-input/index.ts",
    "content": "export { default as PinInput } from \"./PinInput.vue\"\nexport { default as PinInputGroup } from \"./PinInputGroup.vue\"\nexport { default as PinInputSeparator } from \"./PinInputSeparator.vue\"\nexport { default as PinInputSlot } from \"./PinInputSlot.vue\"\n"
  },
  {
    "path": "app/components/ui/popover/Popover.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PopoverRootEmits, PopoverRootProps } from \"reka-ui\"\nimport { PopoverRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<PopoverRootProps>()\nconst emits = defineEmits<PopoverRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <PopoverRoot\n    v-slot=\"slotProps\"\n    data-slot=\"popover\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </PopoverRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/popover/PopoverAnchor.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PopoverAnchorProps } from \"reka-ui\"\nimport { PopoverAnchor } from \"reka-ui\"\n\nconst props = defineProps<PopoverAnchorProps>()\n</script>\n\n<template>\n  <PopoverAnchor\n    data-slot=\"popover-anchor\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </PopoverAnchor>\n</template>\n"
  },
  {
    "path": "app/components/ui/popover/PopoverContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PopoverContentEmits, PopoverContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  PopoverContent,\n  PopoverPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<PopoverContentProps & { class?: HTMLAttributes[\"class\"] }>(),\n  {\n    align: \"center\",\n    sideOffset: 4,\n  },\n)\nconst emits = defineEmits<PopoverContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <PopoverPortal>\n    <PopoverContent\n      data-slot=\"popover-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"\n        cn(\n          'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md origin-(--reka-popover-content-transform-origin) outline-hidden',\n          props.class,\n        )\n      \"\n    >\n      <slot />\n    </PopoverContent>\n  </PopoverPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/popover/PopoverTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PopoverTriggerProps } from \"reka-ui\"\nimport { PopoverTrigger } from \"reka-ui\"\n\nconst props = defineProps<PopoverTriggerProps>()\n</script>\n\n<template>\n  <PopoverTrigger\n    data-slot=\"popover-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </PopoverTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/popover/index.ts",
    "content": "export { default as Popover } from \"./Popover.vue\"\nexport { default as PopoverAnchor } from \"./PopoverAnchor.vue\"\nexport { default as PopoverContent } from \"./PopoverContent.vue\"\nexport { default as PopoverTrigger } from \"./PopoverTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/progress/Progress.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ProgressRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  ProgressIndicator,\n  ProgressRoot,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(\n  defineProps<ProgressRootProps & { class?: HTMLAttributes[\"class\"] }>(),\n  {\n    modelValue: 0,\n  },\n)\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ProgressRoot\n    data-slot=\"progress\"\n    v-bind=\"delegatedProps\"\n    :class=\"\n      cn(\n        'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full',\n        props.class,\n      )\n    \"\n  >\n    <ProgressIndicator\n      data-slot=\"progress-indicator\"\n      class=\"bg-primary h-full w-full flex-1 transition-all\"\n      :style=\"`transform: translateX(-${100 - (props.modelValue ?? 0)}%);`\"\n    />\n  </ProgressRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/progress/index.ts",
    "content": "export { default as Progress } from \"./Progress.vue\"\n"
  },
  {
    "path": "app/components/ui/radio-group/RadioGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { RadioGroupRootEmits, RadioGroupRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RadioGroupRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RadioGroupRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<RadioGroupRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <RadioGroupRoot\n    v-slot=\"slotProps\"\n    data-slot=\"radio-group\"\n    :class=\"cn('grid gap-3', props.class)\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </RadioGroupRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/radio-group/RadioGroupItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { RadioGroupItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { CircleIcon } from \"lucide-vue-next\"\nimport {\n  RadioGroupIndicator,\n  RadioGroupItem,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RadioGroupItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RadioGroupItem\n    data-slot=\"radio-group-item\"\n    v-bind=\"forwardedProps\"\n    :class=\"\n      cn(\n        'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',\n        props.class,\n      )\n    \"\n  >\n    <RadioGroupIndicator\n      data-slot=\"radio-group-indicator\"\n      class=\"relative flex items-center justify-center\"\n    >\n      <slot>\n        <CircleIcon class=\"fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2\" />\n      </slot>\n    </RadioGroupIndicator>\n  </RadioGroupItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/radio-group/index.ts",
    "content": "export { default as RadioGroup } from \"./RadioGroup.vue\"\nexport { default as RadioGroupItem } from \"./RadioGroupItem.vue\"\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendar.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarRootEmits, RangeCalendarRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { RangeCalendarCell, RangeCalendarCellTrigger, RangeCalendarGrid, RangeCalendarGridBody, RangeCalendarGridHead, RangeCalendarGridRow, RangeCalendarHeadCell, RangeCalendarHeader, RangeCalendarHeading, RangeCalendarNextButton, RangeCalendarPrevButton } from \".\"\n\nconst props = defineProps<RangeCalendarRootProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst emits = defineEmits<RangeCalendarRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <RangeCalendarRoot\n    v-slot=\"{ grid, weekDays }\"\n    data-slot=\"range-calendar\"\n    :class=\"cn('p-3', props.class)\"\n    v-bind=\"forwarded\"\n  >\n    <RangeCalendarHeader>\n      <RangeCalendarHeading />\n\n      <div class=\"flex items-center gap-1\">\n        <RangeCalendarPrevButton />\n        <RangeCalendarNextButton />\n      </div>\n    </RangeCalendarHeader>\n\n    <div class=\"flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0\">\n      <RangeCalendarGrid v-for=\"month in grid\" :key=\"month.value.toString()\">\n        <RangeCalendarGridHead>\n          <RangeCalendarGridRow>\n            <RangeCalendarHeadCell\n              v-for=\"day in weekDays\" :key=\"day\"\n            >\n              {{ day }}\n            </RangeCalendarHeadCell>\n          </RangeCalendarGridRow>\n        </RangeCalendarGridHead>\n        <RangeCalendarGridBody>\n          <RangeCalendarGridRow v-for=\"(weekDates, index) in month.rows\" :key=\"`weekDate-${index}`\" class=\"mt-2 w-full\">\n            <RangeCalendarCell\n              v-for=\"weekDate in weekDates\"\n              :key=\"weekDate.toString()\"\n              :date=\"weekDate\"\n            >\n              <RangeCalendarCellTrigger\n                :day=\"weekDate\"\n                :month=\"month.value\"\n              />\n            </RangeCalendarCell>\n          </RangeCalendarGridRow>\n        </RangeCalendarGridBody>\n      </RangeCalendarGrid>\n    </div>\n  </RangeCalendarRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarCell.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarCellProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarCell, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RangeCalendarCellProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarCell\n    data-slot=\"range-calendar-cell\"\n    :class=\"cn('relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:bg-accent first:[&:has([data-selected])]:rounded-l-md last:[&:has([data-selected])]:rounded-r-md [&:has([data-selected][data-selection-end])]:rounded-r-md [&:has([data-selected][data-selection-start])]:rounded-l-md', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </RangeCalendarCell>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarCellTrigger.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarCellTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarCellTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = withDefaults(defineProps<RangeCalendarCellTriggerProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  as: \"button\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarCellTrigger\n    data-slot=\"range-calendar-trigger\"\n    :class=\"cn(\n      buttonVariants({ variant: 'ghost' }),\n      'h-8 w-8 p-0 font-normal data-[selected]:opacity-100',\n      '[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',\n      // Selection Start\n      'data-[selection-start]:bg-primary data-[selection-start]:text-primary-foreground data-[selection-start]:hover:bg-primary data-[selection-start]:hover:text-primary-foreground data-[selection-start]:focus:bg-primary data-[selection-start]:focus:text-primary-foreground',\n      // Selection End\n      'data-[selection-end]:bg-primary data-[selection-end]:text-primary-foreground data-[selection-end]:hover:bg-primary data-[selection-end]:hover:text-primary-foreground data-[selection-end]:focus:bg-primary data-[selection-end]:focus:text-primary-foreground',\n      // Outside months\n      'data-[outside-view]:text-muted-foreground',\n      // Disabled\n      'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',\n      // Unavailable\n      'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </RangeCalendarCellTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarGrid.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarGridProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarGrid, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RangeCalendarGridProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarGrid\n    data-slot=\"range-calendar-grid\"\n    :class=\"cn('w-full border-collapse space-x-1', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </RangeCalendarGrid>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarGridBody.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarGridBodyProps } from \"reka-ui\"\nimport { RangeCalendarGridBody } from \"reka-ui\"\n\nconst props = defineProps<RangeCalendarGridBodyProps>()\n</script>\n\n<template>\n  <RangeCalendarGridBody\n    data-slot=\"range-calendar-grid-body\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </RangeCalendarGridBody>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarGridHead.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarGridHeadProps } from \"reka-ui\"\nimport { RangeCalendarGridHead } from \"reka-ui\"\n\nconst props = defineProps<RangeCalendarGridHeadProps>()\n</script>\n\n<template>\n  <RangeCalendarGridHead\n    data-slot=\"range-calendar-grid-head\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </RangeCalendarGridHead>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarGridRow.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarGridRowProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarGridRow, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RangeCalendarGridRowProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarGridRow\n    data-slot=\"range-calendar-grid-row\"\n    :class=\"cn('flex', props.class)\" v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </RangeCalendarGridRow>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarHeadCell.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarHeadCellProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarHeadCell, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RangeCalendarHeadCellProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarHeadCell\n    data-slot=\"range-calendar-head-cell\"\n    :class=\"cn('w-8 rounded-md text-[0.8rem] font-normal text-muted-foreground', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </RangeCalendarHeadCell>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarHeader.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarHeaderProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarHeader, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RangeCalendarHeaderProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarHeader\n    data-slot=\"range-calendar-header\"\n    :class=\"cn('flex justify-center pt-1 relative items-center w-full', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </RangeCalendarHeader>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarHeading.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarHeadingProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RangeCalendarHeading, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<RangeCalendarHeadingProps & { class?: HTMLAttributes[\"class\"] }>()\n\ndefineSlots<{\n  default: (props: { headingValue: string }) => any\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarHeading\n    v-slot=\"{ headingValue }\"\n    data-slot=\"range-calendar-heading\"\n    :class=\"cn('text-sm font-medium', props.class)\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot :heading-value>\n      {{ headingValue }}\n    </slot>\n  </RangeCalendarHeading>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarNextButton.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarNextProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronRight } from \"lucide-vue-next\"\nimport { RangeCalendarNext, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = defineProps<RangeCalendarNextProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarNext\n    data-slot=\"range-calendar-next-button\"\n    :class=\"cn(\n      buttonVariants({ variant: 'outline' }),\n      'absolute right-1',\n      'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot>\n      <ChevronRight class=\"size-4\" />\n    </slot>\n  </RangeCalendarNext>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/RangeCalendarPrevButton.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { RangeCalendarPrevProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronLeft } from \"lucide-vue-next\"\nimport { RangeCalendarPrev, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from '@/components/ui/button'\n\nconst props = defineProps<RangeCalendarPrevProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <RangeCalendarPrev\n    data-slot=\"range-calendar-prev-button\"\n    :class=\"cn(\n      buttonVariants({ variant: 'outline' }),\n      'absolute left-1',\n      'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot>\n      <ChevronLeft class=\"size-4\" />\n    </slot>\n  </RangeCalendarPrev>\n</template>\n"
  },
  {
    "path": "app/components/ui/range-calendar/index.ts",
    "content": "export { default as RangeCalendar } from \"./RangeCalendar.vue\"\nexport { default as RangeCalendarCell } from \"./RangeCalendarCell.vue\"\nexport { default as RangeCalendarCellTrigger } from \"./RangeCalendarCellTrigger.vue\"\nexport { default as RangeCalendarGrid } from \"./RangeCalendarGrid.vue\"\nexport { default as RangeCalendarGridBody } from \"./RangeCalendarGridBody.vue\"\nexport { default as RangeCalendarGridHead } from \"./RangeCalendarGridHead.vue\"\nexport { default as RangeCalendarGridRow } from \"./RangeCalendarGridRow.vue\"\nexport { default as RangeCalendarHeadCell } from \"./RangeCalendarHeadCell.vue\"\nexport { default as RangeCalendarHeader } from \"./RangeCalendarHeader.vue\"\nexport { default as RangeCalendarHeading } from \"./RangeCalendarHeading.vue\"\nexport { default as RangeCalendarNextButton } from \"./RangeCalendarNextButton.vue\"\nexport { default as RangeCalendarPrevButton } from \"./RangeCalendarPrevButton.vue\"\n"
  },
  {
    "path": "app/components/ui/resizable/ResizableHandle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SplitterResizeHandleEmits, SplitterResizeHandleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { GripVertical } from \"lucide-vue-next\"\nimport { SplitterResizeHandle, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SplitterResizeHandleProps & { class?: HTMLAttributes[\"class\"], withHandle?: boolean }>()\nconst emits = defineEmits<SplitterResizeHandleEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"withHandle\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <SplitterResizeHandle\n    data-slot=\"resizable-handle\"\n    v-bind=\"forwarded\"\n    :class=\"cn('bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:left-0 data-[orientation=vertical]:after:h-1 data-[orientation=vertical]:after:w-full data-[orientation=vertical]:after:-translate-y-1/2 data-[orientation=vertical]:after:translate-x-0 [&[data-orientation=vertical]>div]:rotate-90', props.class)\"\n  >\n    <template v-if=\"props.withHandle\">\n      <div class=\"bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border\">\n        <slot>\n          <GripVertical class=\"size-2.5\" />\n        </slot>\n      </div>\n    </template>\n  </SplitterResizeHandle>\n</template>\n"
  },
  {
    "path": "app/components/ui/resizable/ResizablePanel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SplitterPanelEmits, SplitterPanelProps } from \"reka-ui\"\nimport { SplitterPanel, useForwardExpose, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<SplitterPanelProps>()\nconst emits = defineEmits<SplitterPanelEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\nconst { forwardRef } = useForwardExpose()\n</script>\n\n<template>\n  <SplitterPanel\n    :ref=\"forwardRef\"\n    v-slot=\"slotProps\"\n    data-slot=\"resizable-panel\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </SplitterPanel>\n</template>\n"
  },
  {
    "path": "app/components/ui/resizable/ResizablePanelGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SplitterGroupEmits, SplitterGroupProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SplitterGroup, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SplitterGroupProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<SplitterGroupEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <SplitterGroup\n    v-slot=\"slotProps\"\n    data-slot=\"resizable-panel-group\"\n    v-bind=\"forwarded\"\n    :class=\"cn('flex h-full w-full data-[orientation=vertical]:flex-col', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </SplitterGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/resizable/index.ts",
    "content": "export { default as ResizableHandle } from \"./ResizableHandle.vue\"\nexport { default as ResizablePanel } from \"./ResizablePanel.vue\"\nexport { default as ResizablePanelGroup } from \"./ResizablePanelGroup.vue\"\n"
  },
  {
    "path": "app/components/ui/scroll-area/ScrollArea.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ScrollAreaRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  ScrollAreaCorner,\n  ScrollAreaRoot,\n  ScrollAreaViewport,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport ScrollBar from \"./ScrollBar.vue\"\n\nconst props = defineProps<ScrollAreaRootProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ScrollAreaRoot\n    data-slot=\"scroll-area\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('relative', props.class)\"\n  >\n    <ScrollAreaViewport\n      data-slot=\"scroll-area-viewport\"\n      class=\"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1\"\n    >\n      <slot />\n    </ScrollAreaViewport>\n    <ScrollBar />\n    <ScrollAreaCorner />\n  </ScrollAreaRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/scroll-area/ScrollBar.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ScrollAreaScrollbarProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ScrollAreaScrollbar, ScrollAreaThumb } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<ScrollAreaScrollbarProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  orientation: \"vertical\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <ScrollAreaScrollbar\n    data-slot=\"scroll-area-scrollbar\"\n    v-bind=\"delegatedProps\"\n    :class=\"\n      cn('flex touch-none p-px transition-colors select-none',\n         orientation === 'vertical'\n           && 'h-full w-2.5 border-l border-l-transparent',\n         orientation === 'horizontal'\n           && 'h-2.5 flex-col border-t border-t-transparent',\n         props.class)\"\n  >\n    <ScrollAreaThumb\n      data-slot=\"scroll-area-thumb\"\n      class=\"bg-border relative flex-1 rounded-full\"\n    />\n  </ScrollAreaScrollbar>\n</template>\n"
  },
  {
    "path": "app/components/ui/scroll-area/index.ts",
    "content": "export { default as ScrollArea } from \"./ScrollArea.vue\"\nexport { default as ScrollBar } from \"./ScrollBar.vue\"\n"
  },
  {
    "path": "app/components/ui/select/Select.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectRootEmits, SelectRootProps } from \"reka-ui\"\nimport { SelectRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<SelectRootProps>()\nconst emits = defineEmits<SelectRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <SelectRoot\n    v-slot=\"slotProps\"\n    data-slot=\"select\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </SelectRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectContentEmits, SelectContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  SelectContent,\n  SelectPortal,\n  SelectViewport,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { SelectScrollDownButton, SelectScrollUpButton } from \".\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<SelectContentProps & { class?: HTMLAttributes[\"class\"] }>(),\n  {\n    position: \"popper\",\n  },\n)\nconst emits = defineEmits<SelectContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <SelectPortal>\n    <SelectContent\n      data-slot=\"select-content\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n      :class=\"cn(\n        'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--reka-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md',\n        position === 'popper'\n          && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n        props.class,\n      )\n      \"\n    >\n      <SelectScrollUpButton />\n      <SelectViewport :class=\"cn('p-1', position === 'popper' && 'h-[var(--reka-select-trigger-height)] w-full min-w-[var(--reka-select-trigger-width)] scroll-my-1')\">\n        <slot />\n      </SelectViewport>\n      <SelectScrollDownButton />\n    </SelectContent>\n  </SelectPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectGroupProps } from \"reka-ui\"\nimport { SelectGroup } from \"reka-ui\"\n\nconst props = defineProps<SelectGroupProps>()\n</script>\n\n<template>\n  <SelectGroup\n    data-slot=\"select-group\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </SelectGroup>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Check } from \"lucide-vue-next\"\nimport {\n  SelectItem,\n  SelectItemIndicator,\n  SelectItemText,\n  useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <SelectItem\n    data-slot=\"select-item\"\n    v-bind=\"forwardedProps\"\n    :class=\"\n      cn(\n        'focus:bg-accent focus:text-accent-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n        props.class,\n      )\n    \"\n  >\n    <span class=\"absolute right-2 flex size-3.5 items-center justify-center\">\n      <SelectItemIndicator>\n        <slot name=\"indicator-icon\">\n          <Check class=\"size-4\" />\n        </slot>\n      </SelectItemIndicator>\n    </span>\n\n    <SelectItemText>\n      <slot />\n    </SelectItemText>\n  </SelectItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectItemText.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectItemTextProps } from \"reka-ui\"\nimport { SelectItemText } from \"reka-ui\"\n\nconst props = defineProps<SelectItemTextProps>()\n</script>\n\n<template>\n  <SelectItemText\n    data-slot=\"select-item-text\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </SelectItemText>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectLabel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectLabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { SelectLabel } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectLabelProps & { class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <SelectLabel\n    data-slot=\"select-label\"\n    :class=\"cn('text-muted-foreground px-2 py-1.5 text-xs', props.class)\"\n  >\n    <slot />\n  </SelectLabel>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectScrollDownButton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectScrollDownButtonProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronDown } from \"lucide-vue-next\"\nimport { SelectScrollDownButton, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectScrollDownButtonProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <SelectScrollDownButton\n    data-slot=\"select-scroll-down-button\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('flex cursor-default items-center justify-center py-1', props.class)\"\n  >\n    <slot>\n      <ChevronDown class=\"size-4\" />\n    </slot>\n  </SelectScrollDownButton>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectScrollUpButton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectScrollUpButtonProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronUp } from \"lucide-vue-next\"\nimport { SelectScrollUpButton, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectScrollUpButtonProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <SelectScrollUpButton\n    data-slot=\"select-scroll-up-button\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn('flex cursor-default items-center justify-center py-1', props.class)\"\n  >\n    <slot>\n      <ChevronUp class=\"size-4\" />\n    </slot>\n  </SelectScrollUpButton>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SelectSeparator } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <SelectSeparator\n    data-slot=\"select-separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-border pointer-events-none -mx-1 my-1 h-px', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ChevronDown } from \"lucide-vue-next\"\nimport { SelectIcon, SelectTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(\n  defineProps<SelectTriggerProps & { class?: HTMLAttributes[\"class\"], size?: \"sm\" | \"default\" }>(),\n  { size: \"default\" },\n)\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <SelectTrigger\n    data-slot=\"select-trigger\"\n    :data-size=\"size\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn(\n      'border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n  >\n    <slot />\n    <SelectIcon as-child>\n      <ChevronDown class=\"size-4 opacity-50\" />\n    </SelectIcon>\n  </SelectTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/SelectValue.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SelectValueProps } from \"reka-ui\"\nimport { SelectValue } from \"reka-ui\"\n\nconst props = defineProps<SelectValueProps>()\n</script>\n\n<template>\n  <SelectValue\n    data-slot=\"select-value\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </SelectValue>\n</template>\n"
  },
  {
    "path": "app/components/ui/select/index.ts",
    "content": "export { default as Select } from \"./Select.vue\"\nexport { default as SelectContent } from \"./SelectContent.vue\"\nexport { default as SelectGroup } from \"./SelectGroup.vue\"\nexport { default as SelectItem } from \"./SelectItem.vue\"\nexport { default as SelectItemText } from \"./SelectItemText.vue\"\nexport { default as SelectLabel } from \"./SelectLabel.vue\"\nexport { default as SelectScrollDownButton } from \"./SelectScrollDownButton.vue\"\nexport { default as SelectScrollUpButton } from \"./SelectScrollUpButton.vue\"\nexport { default as SelectSeparator } from \"./SelectSeparator.vue\"\nexport { default as SelectTrigger } from \"./SelectTrigger.vue\"\nexport { default as SelectValue } from \"./SelectValue.vue\"\n"
  },
  {
    "path": "app/components/ui/separator/Separator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Separator } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<\n  SeparatorProps & { class?: HTMLAttributes[\"class\"] }\n>(), {\n  orientation: \"horizontal\",\n  decorative: true,\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <Separator\n    data-slot=\"separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"\n      cn(\n        'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',\n        props.class,\n      )\n    \"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/separator/index.ts",
    "content": "export { default as Separator } from \"./Separator.vue\"\n"
  },
  {
    "path": "app/components/ui/sheet/Sheet.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogRootEmits, DialogRootProps } from \"reka-ui\"\nimport { DialogRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<DialogRootProps>()\nconst emits = defineEmits<DialogRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <DialogRoot\n    v-slot=\"slotProps\"\n    data-slot=\"sheet\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </DialogRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetClose.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogCloseProps } from \"reka-ui\"\nimport { DialogClose } from \"reka-ui\"\n\nconst props = defineProps<DialogCloseProps>()\n</script>\n\n<template>\n  <DialogClose\n    data-slot=\"sheet-close\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DialogClose>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogContentEmits, DialogContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { X } from \"lucide-vue-next\"\nimport {\n  DialogClose,\n  DialogContent,\n  DialogPortal,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport SheetOverlay from \"./SheetOverlay.vue\"\n\ninterface SheetContentProps extends DialogContentProps {\n  class?: HTMLAttributes[\"class\"]\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n}\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<SheetContentProps>(), {\n  side: \"right\",\n})\nconst emits = defineEmits<DialogContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"side\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <DialogPortal>\n    <SheetOverlay />\n    <DialogContent\n      data-slot=\"sheet-content\"\n      :class=\"cn(\n        'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',\n        side === 'right'\n          && 'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',\n        side === 'left'\n          && 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',\n        side === 'top'\n          && 'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b',\n        side === 'bottom'\n          && 'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t',\n        props.class)\"\n      v-bind=\"{ ...$attrs, ...forwarded }\"\n    >\n      <slot />\n\n      <DialogClose\n        class=\"ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none\"\n      >\n        <X class=\"size-4\" />\n        <span class=\"sr-only\">Close</span>\n      </DialogClose>\n    </DialogContent>\n  </DialogPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetDescription.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogDescriptionProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogDescription } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DialogDescription\n    data-slot=\"sheet-description\"\n    :class=\"cn('text-muted-foreground text-sm', props.class)\"\n    v-bind=\"delegatedProps\"\n  >\n    <slot />\n  </DialogDescription>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sheet-footer\"\n    :class=\"cn('mt-auto flex flex-col gap-2 p-4', props.class)\n    \"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{ class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sheet-header\"\n    :class=\"cn('flex flex-col gap-1.5 p-4', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetOverlay.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogOverlayProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogOverlay } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogOverlayProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DialogOverlay\n    data-slot=\"sheet-overlay\"\n    :class=\"cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)\"\n    v-bind=\"delegatedProps\"\n  >\n    <slot />\n  </DialogOverlay>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetTitle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogTitleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogTitle } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogTitleProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <DialogTitle\n    data-slot=\"sheet-title\"\n    :class=\"cn('text-foreground font-semibold', props.class)\"\n    v-bind=\"delegatedProps\"\n  >\n    <slot />\n  </DialogTitle>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/SheetTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DialogTriggerProps } from \"reka-ui\"\nimport { DialogTrigger } from \"reka-ui\"\n\nconst props = defineProps<DialogTriggerProps>()\n</script>\n\n<template>\n  <DialogTrigger\n    data-slot=\"sheet-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </DialogTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/sheet/index.ts",
    "content": "export { default as Sheet } from \"./Sheet.vue\"\nexport { default as SheetClose } from \"./SheetClose.vue\"\nexport { default as SheetContent } from \"./SheetContent.vue\"\nexport { default as SheetDescription } from \"./SheetDescription.vue\"\nexport { default as SheetFooter } from \"./SheetFooter.vue\"\nexport { default as SheetHeader } from \"./SheetHeader.vue\"\nexport { default as SheetTitle } from \"./SheetTitle.vue\"\nexport { default as SheetTrigger } from \"./SheetTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/sidebar/Sidebar.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SidebarProps } from \".\"\nimport { cn } from \"@/lib/utils\"\nimport { Sheet, SheetContent } from '@/components/ui/sheet'\nimport SheetDescription from '@/components/ui/sheet/SheetDescription.vue'\nimport SheetHeader from '@/components/ui/sheet/SheetHeader.vue'\nimport SheetTitle from '@/components/ui/sheet/SheetTitle.vue'\nimport { SIDEBAR_WIDTH_MOBILE, useSidebar } from \"./utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<SidebarProps>(), {\n  side: \"left\",\n  variant: \"sidebar\",\n  collapsible: \"offcanvas\",\n})\n\nconst { isMobile, state, openMobile, setOpenMobile } = useSidebar()\n</script>\n\n<template>\n  <div\n    v-if=\"collapsible === 'none'\"\n    data-slot=\"sidebar\"\n    :class=\"cn('bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col', props.class)\"\n    v-bind=\"$attrs\"\n  >\n    <slot />\n  </div>\n\n  <Sheet v-else-if=\"isMobile\" :open=\"openMobile\" v-bind=\"$attrs\" @update:open=\"setOpenMobile\">\n    <SheetContent\n      data-sidebar=\"sidebar\"\n      data-slot=\"sidebar\"\n      data-mobile=\"true\"\n      :side=\"side\"\n      class=\"bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden\"\n      :style=\"{\n        '--sidebar-width': SIDEBAR_WIDTH_MOBILE,\n      }\"\n    >\n      <SheetHeader class=\"sr-only\">\n        <SheetTitle>Sidebar</SheetTitle>\n        <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n      </SheetHeader>\n      <div class=\"flex h-full w-full flex-col\">\n        <slot />\n      </div>\n    </SheetContent>\n  </Sheet>\n\n  <div\n    v-else\n    class=\"group peer text-sidebar-foreground hidden md:block\"\n    data-slot=\"sidebar\"\n    :data-state=\"state\"\n    :data-collapsible=\"state === 'collapsed' ? collapsible : ''\"\n    :data-variant=\"variant\"\n    :data-side=\"side\"\n  >\n    <!-- This is what handles the sidebar gap on desktop  -->\n    <div\n      :class=\"cn(\n        'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',\n        'group-data-[collapsible=offcanvas]:w-0',\n        'group-data-[side=right]:rotate-180',\n        variant === 'floating' || variant === 'inset'\n          ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'\n          : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',\n      )\"\n    />\n    <div\n      :class=\"cn(\n        'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',\n        side === 'left'\n          ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'\n          : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',\n        // Adjust the padding for floating and inset variants.\n        variant === 'floating' || variant === 'inset'\n          ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'\n          : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n        props.class,\n      )\"\n      v-bind=\"$attrs\"\n    >\n      <div\n        data-sidebar=\"sidebar\"\n        class=\"bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm\"\n      >\n        <slot />\n      </div>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-content\"\n    data-sidebar=\"content\"\n    :class=\"cn('flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-footer\"\n    data-sidebar=\"footer\"\n    :class=\"cn('flex flex-col gap-2 p-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-group\"\n    data-sidebar=\"group\"\n    :class=\"cn('relative flex w-full min-w-0 flex-col p-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarGroupAction.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"sidebar-group-action\"\n    data-sidebar=\"group-action\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"cn(\n      'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n      'after:absolute after:-inset-2 md:after:hidden',\n      'group-data-[collapsible=icon]:hidden',\n      props.class,\n    )\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarGroupContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-group-content\"\n    data-sidebar=\"group-content\"\n    :class=\"cn('w-full text-sm', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarGroupLabel.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"sidebar-group-label\"\n    data-sidebar=\"group-label\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"cn(\n      'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n      'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',\n      props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-header\"\n    data-sidebar=\"header\"\n    :class=\"cn('flex flex-col gap-2 p-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Input } from '@/components/ui/input'\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Input\n    data-slot=\"sidebar-input\"\n    data-sidebar=\"input\"\n    :class=\"cn(\n      'bg-background h-8 w-full shadow-none',\n      props.class,\n    )\"\n  >\n    <slot />\n  </Input>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarInset.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <main\n    data-slot=\"sidebar-inset\"\n    :class=\"cn(\n      'bg-background relative flex w-full flex-1 flex-col',\n      'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',\n      props.class,\n    )\"\n  >\n    <slot />\n  </main>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenu.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <ul\n    data-slot=\"sidebar-menu\"\n    data-sidebar=\"menu\"\n    :class=\"cn('flex w-full min-w-0 flex-col gap-1', props.class)\"\n  >\n    <slot />\n  </ul>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuAction.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  showOnHover?: boolean\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  as: \"button\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"sidebar-menu-action\"\n    data-sidebar=\"menu-action\"\n    :class=\"cn(\n      'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n      'after:absolute after:-inset-2 md:after:hidden',\n      'peer-data-[size=sm]/menu-button:top-1',\n      'peer-data-[size=default]/menu-button:top-1.5',\n      'peer-data-[size=lg]/menu-button:top-2.5',\n      'group-data-[collapsible=icon]:hidden',\n      showOnHover\n        && 'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',\n      props.class,\n    )\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuBadge.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-menu-badge\"\n    data-sidebar=\"menu-badge\"\n    :class=\"cn(\n      'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',\n      'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',\n      'peer-data-[size=sm]/menu-button:top-1',\n      'peer-data-[size=default]/menu-button:top-1.5',\n      'peer-data-[size=lg]/menu-button:top-2.5',\n      'group-data-[collapsible=icon]:hidden',\n      props.class,\n    )\"\n  >\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuButton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Component } from \"vue\"\nimport type { SidebarMenuButtonProps } from \"./SidebarMenuButtonChild.vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport SidebarMenuButtonChild from \"./SidebarMenuButtonChild.vue\"\nimport { useSidebar } from \"./utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<SidebarMenuButtonProps & {\n  tooltip?: string | Component\n}>(), {\n  as: \"button\",\n  variant: \"default\",\n  size: \"default\",\n})\n\nconst { isMobile, state } = useSidebar()\n\nconst delegatedProps = reactiveOmit(props, \"tooltip\")\n</script>\n\n<template>\n  <SidebarMenuButtonChild v-if=\"!tooltip\" v-bind=\"{ ...delegatedProps, ...$attrs }\">\n    <slot />\n  </SidebarMenuButtonChild>\n\n  <Tooltip v-else>\n    <TooltipTrigger as-child>\n      <SidebarMenuButtonChild v-bind=\"{ ...delegatedProps, ...$attrs }\">\n        <slot />\n      </SidebarMenuButtonChild>\n    </TooltipTrigger>\n    <TooltipContent\n      side=\"right\"\n      align=\"center\"\n      :hidden=\"state !== 'collapsed' || isMobile\"\n    >\n      <template v-if=\"typeof tooltip === 'string'\">\n        {{ tooltip }}\n      </template>\n      <component :is=\"tooltip\" v-else />\n    </TooltipContent>\n  </Tooltip>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuButtonChild.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { SidebarMenuButtonVariants } from \".\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { sidebarMenuButtonVariants } from \".\"\n\nexport interface SidebarMenuButtonProps extends PrimitiveProps {\n  variant?: SidebarMenuButtonVariants[\"variant\"]\n  size?: SidebarMenuButtonVariants[\"size\"]\n  isActive?: boolean\n  class?: HTMLAttributes[\"class\"]\n}\n\nconst props = withDefaults(defineProps<SidebarMenuButtonProps>(), {\n  as: \"button\",\n  variant: \"default\",\n  size: \"default\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"sidebar-menu-button\"\n    data-sidebar=\"menu-button\"\n    :data-size=\"size\"\n    :data-active=\"isActive\"\n    :class=\"cn(sidebarMenuButtonVariants({ variant, size }), props.class)\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    v-bind=\"$attrs\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <li\n    data-slot=\"sidebar-menu-item\"\n    data-sidebar=\"menu-item\"\n    :class=\"cn('group/menu-item relative', props.class)\"\n  >\n    <slot />\n  </li>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuSkeleton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Skeleton } from '@/components/ui/skeleton'\n\nconst props = defineProps<{\n  showIcon?: boolean\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst width = computed(() => {\n  return `${Math.floor(Math.random() * 40) + 50}%`\n})\n</script>\n\n<template>\n  <div\n    data-slot=\"sidebar-menu-skeleton\"\n    data-sidebar=\"menu-skeleton\"\n    :class=\"cn('flex h-8 items-center gap-2 rounded-md px-2', props.class)\"\n  >\n    <Skeleton\n      v-if=\"showIcon\"\n      class=\"size-4 rounded-md\"\n      data-sidebar=\"menu-skeleton-icon\"\n    />\n\n    <Skeleton\n      class=\"h-4 max-w-(--skeleton-width) flex-1\"\n      data-sidebar=\"menu-skeleton-text\"\n      :style=\"{ '--skeleton-width': width }\"\n    />\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuSub.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <ul\n    data-slot=\"sidebar-menu-sub\"\n    data-sidebar=\"menu-badge\"\n    :class=\"cn(\n      'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',\n      'group-data-[collapsible=icon]:hidden',\n      props.class,\n    )\"\n  >\n    <slot />\n  </ul>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuSubButton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  size?: \"sm\" | \"md\"\n  isActive?: boolean\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  as: \"a\",\n  size: \"md\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"sidebar-menu-sub-button\"\n    data-sidebar=\"menu-sub-button\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :data-size=\"size\"\n    :data-active=\"isActive\"\n    :class=\"cn(\n      'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',\n      'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',\n      size === 'sm' && 'text-xs',\n      size === 'md' && 'text-sm',\n      'group-data-[collapsible=icon]:hidden',\n      props.class,\n    )\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarMenuSubItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <li\n    data-slot=\"sidebar-menu-sub-item\"\n    data-sidebar=\"menu-sub-item\"\n    :class=\"cn('group/menu-sub-item relative', props.class)\"\n  >\n    <slot />\n  </li>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarProvider.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes, Ref } from \"vue\"\nimport { defaultDocument, useEventListener, useMediaQuery, useVModel } from \"@vueuse/core\"\nimport { TooltipProvider } from \"reka-ui\"\nimport { computed, ref } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { provideSidebarContext, SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON } from \"./utils\"\n\nconst props = withDefaults(defineProps<{\n  defaultOpen?: boolean\n  open?: boolean\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  defaultOpen: !defaultDocument?.cookie.includes(`${SIDEBAR_COOKIE_NAME}=false`),\n  open: undefined,\n})\n\nconst emits = defineEmits<{\n  \"update:open\": [open: boolean]\n}>()\n\nconst isMobile = useMediaQuery(\"(max-width: 768px)\")\nconst openMobile = ref(false)\n\nconst open = useVModel(props, \"open\", emits, {\n  defaultValue: props.defaultOpen ?? false,\n  passive: (props.open === undefined) as false,\n}) as Ref<boolean>\n\nfunction setOpen(value: boolean) {\n  open.value = value // emits('update:open', value)\n\n  // This sets the cookie to keep the sidebar state.\n  document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n}\n\nfunction setOpenMobile(value: boolean) {\n  openMobile.value = value\n}\n\n// Helper to toggle the sidebar.\nfunction toggleSidebar() {\n  return isMobile.value ? setOpenMobile(!openMobile.value) : setOpen(!open.value)\n}\n\nuseEventListener(\"keydown\", (event: KeyboardEvent) => {\n  if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n    event.preventDefault()\n    toggleSidebar()\n  }\n})\n\n// We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n// This makes it easier to style the sidebar with Tailwind classes.\nconst state = computed(() => open.value ? \"expanded\" : \"collapsed\")\n\nprovideSidebarContext({\n  state,\n  open,\n  setOpen,\n  isMobile,\n  openMobile,\n  setOpenMobile,\n  toggleSidebar,\n})\n</script>\n\n<template>\n  <TooltipProvider :delay-duration=\"0\">\n    <div\n      data-slot=\"sidebar-wrapper\"\n      :style=\"{\n        '--sidebar-width': SIDEBAR_WIDTH,\n        '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,\n      }\"\n      :class=\"cn('group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full', props.class)\"\n      v-bind=\"$attrs\"\n    >\n      <slot />\n    </div>\n  </TooltipProvider>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarRail.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { useSidebar } from \"./utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst { toggleSidebar } = useSidebar()\n</script>\n\n<template>\n  <button\n    data-sidebar=\"rail\"\n    data-slot=\"sidebar-rail\"\n    aria-label=\"Toggle Sidebar\"\n    :tabindex=\"-1\"\n    title=\"Toggle Sidebar\"\n    :class=\"cn(\n      'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex',\n      'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',\n      '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',\n      'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',\n      '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',\n      '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',\n      props.class,\n    )\"\n    @click=\"toggleSidebar\"\n  >\n    <slot />\n  </button>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarSeparator.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { Separator } from '@/components/ui/separator'\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Separator\n    data-slot=\"sidebar-separator\"\n    data-sidebar=\"separator\"\n    :class=\"cn('bg-sidebar-border mx-2 w-auto', props.class)\"\n  >\n    <slot />\n  </Separator>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/SidebarTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { PanelLeft } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from '@/components/ui/button'\nimport { useSidebar } from \"./utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n\nconst { toggleSidebar } = useSidebar()\n</script>\n\n<template>\n  <Button\n    data-sidebar=\"trigger\"\n    data-slot=\"sidebar-trigger\"\n    variant=\"ghost\"\n    size=\"icon\"\n    :class=\"cn('h-7 w-7', props.class)\"\n    @click=\"toggleSidebar\"\n  >\n    <PanelLeft />\n    <span class=\"sr-only\">Toggle Sidebar</span>\n  </Button>\n</template>\n"
  },
  {
    "path": "app/components/ui/sidebar/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport type { HTMLAttributes } from \"vue\"\nimport { cva } from \"class-variance-authority\"\n\nexport interface SidebarProps {\n  side?: \"left\" | \"right\"\n  variant?: \"sidebar\" | \"floating\" | \"inset\"\n  collapsible?: \"offcanvas\" | \"icon\" | \"none\"\n  class?: HTMLAttributes[\"class\"]\n}\n\nexport { default as Sidebar } from \"./Sidebar.vue\"\nexport { default as SidebarContent } from \"./SidebarContent.vue\"\nexport { default as SidebarFooter } from \"./SidebarFooter.vue\"\nexport { default as SidebarGroup } from \"./SidebarGroup.vue\"\nexport { default as SidebarGroupAction } from \"./SidebarGroupAction.vue\"\nexport { default as SidebarGroupContent } from \"./SidebarGroupContent.vue\"\nexport { default as SidebarGroupLabel } from \"./SidebarGroupLabel.vue\"\nexport { default as SidebarHeader } from \"./SidebarHeader.vue\"\nexport { default as SidebarInput } from \"./SidebarInput.vue\"\nexport { default as SidebarInset } from \"./SidebarInset.vue\"\nexport { default as SidebarMenu } from \"./SidebarMenu.vue\"\nexport { default as SidebarMenuAction } from \"./SidebarMenuAction.vue\"\nexport { default as SidebarMenuBadge } from \"./SidebarMenuBadge.vue\"\nexport { default as SidebarMenuButton } from \"./SidebarMenuButton.vue\"\nexport { default as SidebarMenuItem } from \"./SidebarMenuItem.vue\"\nexport { default as SidebarMenuSkeleton } from \"./SidebarMenuSkeleton.vue\"\nexport { default as SidebarMenuSub } from \"./SidebarMenuSub.vue\"\nexport { default as SidebarMenuSubButton } from \"./SidebarMenuSubButton.vue\"\nexport { default as SidebarMenuSubItem } from \"./SidebarMenuSubItem.vue\"\nexport { default as SidebarProvider } from \"./SidebarProvider.vue\"\nexport { default as SidebarRail } from \"./SidebarRail.vue\"\nexport { default as SidebarSeparator } from \"./SidebarSeparator.vue\"\nexport { default as SidebarTrigger } from \"./SidebarTrigger.vue\"\n\nexport { useSidebar } from \"./utils\"\n\nexport const sidebarMenuButtonVariants = cva(\n  \"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        default: \"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\",\n        outline:\n          \"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]\",\n      },\n      size: {\n        default: \"h-8 text-sm\",\n        sm: \"h-7 text-xs\",\n        lg: \"h-12 text-sm group-data-[collapsible=icon]:p-0!\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n)\n\nexport type SidebarMenuButtonVariants = VariantProps<typeof sidebarMenuButtonVariants>\n"
  },
  {
    "path": "app/components/ui/sidebar/utils.ts",
    "content": "import type { ComputedRef, Ref } from \"vue\"\nimport { createContext } from \"reka-ui\"\n\nexport const SIDEBAR_COOKIE_NAME = \"sidebar_state\"\nexport const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\nexport const SIDEBAR_WIDTH = \"16rem\"\nexport const SIDEBAR_WIDTH_MOBILE = \"18rem\"\nexport const SIDEBAR_WIDTH_ICON = \"3rem\"\nexport const SIDEBAR_KEYBOARD_SHORTCUT = \"b\"\n\nexport const [useSidebar, provideSidebarContext] = createContext<{\n  state: ComputedRef<\"expanded\" | \"collapsed\">\n  open: Ref<boolean>\n  setOpen: (value: boolean) => void\n  isMobile: Ref<boolean>\n  openMobile: Ref<boolean>\n  setOpenMobile: (value: boolean) => void\n  toggleSidebar: () => void\n}>(\"Sidebar\")\n"
  },
  {
    "path": "app/components/ui/skeleton/Skeleton.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\ninterface SkeletonProps {\n  class?: HTMLAttributes[\"class\"]\n}\n\nconst props = defineProps<SkeletonProps>()\n</script>\n\n<template>\n  <div\n    data-slot=\"skeleton\"\n    :class=\"cn('animate-pulse rounded-md bg-primary/10', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/skeleton/index.ts",
    "content": "export { default as Skeleton } from \"./Skeleton.vue\"\n"
  },
  {
    "path": "app/components/ui/slider/Slider.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SliderRootEmits, SliderRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SliderRange, SliderRoot, SliderThumb, SliderTrack, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SliderRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<SliderRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <SliderRoot\n    v-slot=\"{ modelValue }\"\n    data-slot=\"slider\"\n    :class=\"cn(\n      'relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col',\n      props.class,\n    )\"\n    v-bind=\"forwarded\"\n  >\n    <SliderTrack\n      data-slot=\"slider-track\"\n      class=\"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5\"\n    >\n      <SliderRange\n        data-slot=\"slider-range\"\n        class=\"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\"\n      />\n    </SliderTrack>\n\n    <SliderThumb\n      v-for=\"(_, key) in modelValue\"\n      :key=\"key\"\n      data-slot=\"slider-thumb\"\n      class=\"bg-white border-primary ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50\"\n    />\n  </SliderRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/slider/index.ts",
    "content": "export { default as Slider } from \"./Slider.vue\"\n"
  },
  {
    "path": "app/components/ui/sonner/Sonner.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { ToasterProps } from \"vue-sonner\"\nimport { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, XIcon } from \"lucide-vue-next\"\nimport { Toaster as Sonner } from \"vue-sonner\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<ToasterProps>()\n</script>\n\n<template>\n  <Sonner\n    :class=\"cn('toaster group', props.class)\"\n    :style=\"{\n      '--normal-bg': 'var(--popover)',\n      '--normal-text': 'var(--popover-foreground)',\n      '--normal-border': 'var(--border)',\n      '--border-radius': 'var(--radius)',\n    }\"\n    v-bind=\"props\"\n  >\n    <template #success-icon>\n      <CircleCheckIcon class=\"size-4\" />\n    </template>\n    <template #info-icon>\n      <InfoIcon class=\"size-4\" />\n    </template>\n    <template #warning-icon>\n      <TriangleAlertIcon class=\"size-4\" />\n    </template>\n    <template #error-icon>\n      <OctagonXIcon class=\"size-4\" />\n    </template>\n    <template #loading-icon>\n      <div>\n        <Loader2Icon class=\"size-4 animate-spin\" />\n      </div>\n    </template>\n    <template #close-icon>\n      <XIcon class=\"size-4\" />\n    </template>\n  </Sonner>\n</template>\n"
  },
  {
    "path": "app/components/ui/sonner/index.ts",
    "content": "export { default as Toaster } from \"./Sonner.vue\"\n"
  },
  {
    "path": "app/components/ui/spinner/Spinner.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { Loader2Icon } from \"lucide-vue-next\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <Loader2Icon\n    role=\"status\"\n    aria-label=\"Loading\"\n    :class=\"cn('size-4 animate-spin', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/spinner/index.ts",
    "content": "export { default as Spinner } from \"./Spinner.vue\"\n"
  },
  {
    "path": "app/components/ui/stepper/Stepper.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperRootEmits, StepperRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<StepperRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <StepperRoot\n    v-slot=\"slotProps\"\n    :class=\"cn(\n      'flex gap-2',\n      props.class,\n    )\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </StepperRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/StepperDescription.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperDescriptionProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperDescription, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperDescriptionProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <StepperDescription v-slot=\"slotProps\" v-bind=\"forwarded\" :class=\"cn('text-xs text-muted-foreground', props.class)\">\n    <slot v-bind=\"slotProps\" />\n  </StepperDescription>\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/StepperIndicator.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperIndicatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperIndicator, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperIndicatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <StepperIndicator\n    v-slot=\"slotProps\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'inline-flex items-center justify-center rounded-full text-muted-foreground/50 w-8 h-8',\n      // Disabled\n      'group-data-[disabled]:text-muted-foreground group-data-[disabled]:opacity-50',\n      // Active\n      'group-data-[state=active]:bg-primary group-data-[state=active]:text-primary-foreground',\n      // Completed\n      'group-data-[state=completed]:bg-accent group-data-[state=completed]:text-accent-foreground',\n      props.class,\n    )\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </StepperIndicator>\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/StepperItem.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperItem, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <StepperItem\n    v-slot=\"slotProps\"\n    v-bind=\"forwarded\"\n    :class=\"cn('flex items-center gap-2 group data-[disabled]:pointer-events-none', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </StepperItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/StepperSeparator.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperSeparator, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperSeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <StepperSeparator\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'bg-muted',\n      // Disabled\n      'group-data-[disabled]:bg-muted group-data-[disabled]:opacity-50',\n      // Completed\n      'group-data-[state=completed]:bg-accent',\n      props.class,\n    )\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/StepperTitle.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperTitleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperTitle, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperTitleProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <StepperTitle v-bind=\"forwarded\" :class=\"cn('text-md font-semibold whitespace-nowrap', props.class)\">\n    <slot />\n  </StepperTitle>\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/StepperTrigger.vue",
    "content": "<script lang=\"ts\" setup>\nimport type { StepperTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { StepperTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<StepperTriggerProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <StepperTrigger\n    v-bind=\"forwarded\"\n    :class=\"cn('p-1 flex flex-col items-center text-center gap-1 rounded-md', props.class)\"\n  >\n    <slot />\n  </StepperTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/stepper/index.ts",
    "content": "export { default as Stepper } from \"./Stepper.vue\"\nexport { default as StepperDescription } from \"./StepperDescription.vue\"\nexport { default as StepperIndicator } from \"./StepperIndicator.vue\"\nexport { default as StepperItem } from \"./StepperItem.vue\"\nexport { default as StepperSeparator } from \"./StepperSeparator.vue\"\nexport { default as StepperTitle } from \"./StepperTitle.vue\"\nexport { default as StepperTrigger } from \"./StepperTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/switch/Switch.vue",
    "content": "<script setup lang=\"ts\">\nimport type { SwitchRootEmits, SwitchRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n  SwitchRoot,\n  SwitchThumb,\n  useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SwitchRootProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst emits = defineEmits<SwitchRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <SwitchRoot\n    v-slot=\"slotProps\"\n    data-slot=\"switch\"\n    v-bind=\"forwarded\"\n    :class=\"cn(\n      'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',\n      props.class,\n    )\"\n  >\n    <SwitchThumb\n      data-slot=\"switch-thumb\"\n      :class=\"cn('bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0')\"\n    >\n      <slot name=\"thumb\" v-bind=\"slotProps\" />\n    </SwitchThumb>\n  </SwitchRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/switch/index.ts",
    "content": "export { default as Switch } from \"./Switch.vue\"\n"
  },
  {
    "path": "app/components/ui/table/Table.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <div data-slot=\"table-container\" class=\"relative w-full overflow-auto\">\n    <table data-slot=\"table\" :class=\"cn('w-full caption-bottom text-sm', props.class)\">\n      <slot />\n    </table>\n  </div>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableBody.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <tbody\n    data-slot=\"table-body\"\n    :class=\"cn('[&_tr:last-child]:border-0', props.class)\"\n  >\n    <slot />\n  </tbody>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableCaption.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <caption\n    data-slot=\"table-caption\"\n    :class=\"cn('text-muted-foreground mt-4 text-sm', props.class)\"\n  >\n    <slot />\n  </caption>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableCell.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <td\n    data-slot=\"table-cell\"\n    :class=\"\n      cn(\n        'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </td>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableEmpty.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { cn } from \"@/lib/utils\"\nimport TableCell from \"./TableCell.vue\"\nimport TableRow from \"./TableRow.vue\"\n\nconst props = withDefaults(defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  colspan?: number\n}>(), {\n  colspan: 1,\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <TableRow>\n    <TableCell\n      :class=\"\n        cn(\n          'p-4 whitespace-nowrap align-middle text-sm text-foreground',\n          props.class,\n        )\n      \"\n      v-bind=\"delegatedProps\"\n    >\n      <div class=\"flex items-center justify-center py-10\">\n        <slot />\n      </div>\n    </TableCell>\n  </TableRow>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableFooter.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <tfoot\n    data-slot=\"table-footer\"\n    :class=\"cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', props.class)\"\n  >\n    <slot />\n  </tfoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableHead.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <th\n    data-slot=\"table-head\"\n    :class=\"cn('text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', props.class)\"\n  >\n    <slot />\n  </th>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <thead\n    data-slot=\"table-header\"\n    :class=\"cn('[&_tr]:border-b', props.class)\"\n  >\n    <slot />\n  </thead>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/TableRow.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <tr\n    data-slot=\"table-row\"\n    :class=\"cn('hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors', props.class)\"\n  >\n    <slot />\n  </tr>\n</template>\n"
  },
  {
    "path": "app/components/ui/table/index.ts",
    "content": "export { default as Table } from \"./Table.vue\"\nexport { default as TableBody } from \"./TableBody.vue\"\nexport { default as TableCaption } from \"./TableCaption.vue\"\nexport { default as TableCell } from \"./TableCell.vue\"\nexport { default as TableEmpty } from \"./TableEmpty.vue\"\nexport { default as TableFooter } from \"./TableFooter.vue\"\nexport { default as TableHead } from \"./TableHead.vue\"\nexport { default as TableHeader } from \"./TableHeader.vue\"\nexport { default as TableRow } from \"./TableRow.vue\"\n"
  },
  {
    "path": "app/components/ui/table/utils.ts",
    "content": "import type { Updater } from \"@tanstack/vue-table\"\n\nimport type { Ref } from \"vue\"\nimport { isFunction } from \"@tanstack/vue-table\"\n\nexport function valueUpdater<T>(updaterOrValue: Updater<T>, ref: Ref<T>) {\n  ref.value = isFunction(updaterOrValue)\n    ? updaterOrValue(ref.value)\n    : updaterOrValue\n}\n"
  },
  {
    "path": "app/components/ui/tabs/Tabs.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TabsRootEmits, TabsRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TabsRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TabsRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<TabsRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <TabsRoot\n    v-slot=\"slotProps\"\n    data-slot=\"tabs\"\n    v-bind=\"forwarded\"\n    :class=\"cn('flex flex-col gap-2', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </TabsRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/tabs/TabsContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TabsContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TabsContent } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TabsContentProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <TabsContent\n    data-slot=\"tabs-content\"\n    :class=\"cn('flex-1 outline-none', props.class)\"\n    v-bind=\"delegatedProps\"\n  >\n    <slot />\n  </TabsContent>\n</template>\n"
  },
  {
    "path": "app/components/ui/tabs/TabsList.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TabsListProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TabsList } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TabsListProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n  <TabsList\n    data-slot=\"tabs-list\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn(\n      'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]',\n      props.class,\n    )\"\n  >\n    <slot />\n  </TabsList>\n</template>\n"
  },
  {
    "path": "app/components/ui/tabs/TabsTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TabsTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TabsTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TabsTriggerProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <TabsTrigger\n    data-slot=\"tabs-trigger\"\n    :class=\"cn(\n      'data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n      props.class,\n    )\"\n    v-bind=\"forwardedProps\"\n  >\n    <slot />\n  </TabsTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/tabs/index.ts",
    "content": "export { default as Tabs } from \"./Tabs.vue\"\nexport { default as TabsContent } from \"./TabsContent.vue\"\nexport { default as TabsList } from \"./TabsList.vue\"\nexport { default as TabsTrigger } from \"./TabsTrigger.vue\"\n"
  },
  {
    "path": "app/components/ui/tags-input/TagsInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TagsInputRootEmits, TagsInputRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TagsInputRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TagsInputRootProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<TagsInputRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <TagsInputRoot\n    v-slot=\"slotProps\" v-bind=\"forwarded\" :class=\"cn(\n      'flex flex-wrap gap-2 items-center rounded-md border border-input bg-background px-2 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none',\n      'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]',\n      'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n      props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </TagsInputRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/tags-input/TagsInputInput.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TagsInputInputProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TagsInputInput, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TagsInputInputProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <TagsInputInput v-bind=\"forwardedProps\" :class=\"cn('text-sm min-h-5 focus:outline-none flex-1 bg-transparent px-1', props.class)\" />\n</template>\n"
  },
  {
    "path": "app/components/ui/tags-input/TagsInputItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TagsInputItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\n\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TagsInputItem, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TagsInputItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <TagsInputItem v-bind=\"forwardedProps\" :class=\"cn('flex h-5 items-center rounded-md bg-secondary data-[state=active]:ring-ring data-[state=active]:ring-2 data-[state=active]:ring-offset-2 ring-offset-background', props.class)\">\n    <slot />\n  </TagsInputItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/tags-input/TagsInputItemDelete.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TagsInputItemDeleteProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { X } from \"lucide-vue-next\"\nimport { TagsInputItemDelete, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TagsInputItemDeleteProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <TagsInputItemDelete v-bind=\"forwardedProps\" :class=\"cn('flex rounded bg-transparent mr-1', props.class)\">\n    <slot>\n      <X class=\"w-4 h-4\" />\n    </slot>\n  </TagsInputItemDelete>\n</template>\n"
  },
  {
    "path": "app/components/ui/tags-input/TagsInputItemText.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TagsInputItemTextProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TagsInputItemText, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<TagsInputItemTextProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <TagsInputItemText v-bind=\"forwardedProps\" :class=\"cn('py-0.5 px-2 text-sm rounded bg-transparent', props.class)\" />\n</template>\n"
  },
  {
    "path": "app/components/ui/tags-input/index.ts",
    "content": "export { default as TagsInput } from \"./TagsInput.vue\"\nexport { default as TagsInputInput } from \"./TagsInputInput.vue\"\nexport { default as TagsInputItem } from \"./TagsInputItem.vue\"\nexport { default as TagsInputItemDelete } from \"./TagsInputItemDelete.vue\"\nexport { default as TagsInputItemText } from \"./TagsInputItemText.vue\"\n"
  },
  {
    "path": "app/components/ui/textarea/Textarea.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { useVModel } from \"@vueuse/core\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  defaultValue?: string | number\n  modelValue?: string | number\n}>()\n\nconst emits = defineEmits<{\n  (e: \"update:modelValue\", payload: string | number): void\n}>()\n\nconst modelValue = useVModel(props, \"modelValue\", emits, {\n  passive: true,\n  defaultValue: props.defaultValue,\n})\n</script>\n\n<template>\n  <textarea\n    v-model=\"modelValue\"\n    data-slot=\"textarea\"\n    :class=\"cn('border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', props.class)\"\n  />\n</template>\n"
  },
  {
    "path": "app/components/ui/textarea/index.ts",
    "content": "export { default as Textarea } from \"./Textarea.vue\"\n"
  },
  {
    "path": "app/components/ui/toggle/Toggle.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ToggleEmits, ToggleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ToggleVariants } from \".\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { Toggle, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { toggleVariants } from \".\"\n\nconst props = withDefaults(defineProps<ToggleProps & {\n  class?: HTMLAttributes[\"class\"]\n  variant?: ToggleVariants[\"variant\"]\n  size?: ToggleVariants[\"size\"]\n}>(), {\n  variant: \"default\",\n  size: \"default\",\n  disabled: false,\n})\n\nconst emits = defineEmits<ToggleEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\", \"variant\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <Toggle\n    v-slot=\"slotProps\"\n    data-slot=\"toggle\"\n    v-bind=\"forwarded\"\n    :class=\"cn(toggleVariants({ variant, size }), props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </Toggle>\n</template>\n"
  },
  {
    "path": "app/components/ui/toggle/index.ts",
    "content": "import type { VariantProps } from \"class-variance-authority\"\nimport { cva } from \"class-variance-authority\"\n\nexport { default as Toggle } from \"./Toggle.vue\"\n\nexport const toggleVariants = cva(\n  \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        outline:\n          \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\",\n      },\n      size: {\n        default: \"h-9 px-2 min-w-9\",\n        sm: \"h-8 px-1.5 min-w-8\",\n        lg: \"h-10 px-2.5 min-w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n)\n\nexport type ToggleVariants = VariantProps<typeof toggleVariants>\n"
  },
  {
    "path": "app/components/ui/toggle-group/ToggleGroup.vue",
    "content": "<script setup lang=\"ts\">\nimport type { VariantProps } from \"class-variance-authority\"\nimport type { ToggleGroupRootEmits, ToggleGroupRootProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { toggleVariants } from '@/components/ui/toggle'\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ToggleGroupRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { provide } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\ntype ToggleGroupVariants = VariantProps<typeof toggleVariants>\n\nconst props = withDefaults(defineProps<ToggleGroupRootProps & {\n  class?: HTMLAttributes[\"class\"]\n  variant?: ToggleGroupVariants[\"variant\"]\n  size?: ToggleGroupVariants[\"size\"]\n  spacing?: number\n}>(), {\n  spacing: 0,\n})\n\nconst emits = defineEmits<ToggleGroupRootEmits>()\n\nprovide(\"toggleGroup\", {\n  variant: props.variant,\n  size: props.size,\n  spacing: props.spacing,\n})\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\", \"variant\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ToggleGroupRoot\n    v-slot=\"slotProps\"\n    data-slot=\"toggle-group\"\n    :data-size=\"size\"\n    :data-variant=\"variant\"\n    :data-spacing=\"spacing\"\n    :style=\"{\n      '--gap': spacing,\n    }\"\n    v-bind=\"forwarded\"\n    :class=\"cn('group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-xs', props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </ToggleGroupRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/toggle-group/ToggleGroupItem.vue",
    "content": "<script setup lang=\"ts\">\nimport type { VariantProps } from \"class-variance-authority\"\nimport type { ToggleGroupItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { ToggleGroupItem, useForwardProps } from \"reka-ui\"\nimport { inject } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { toggleVariants } from '@/components/ui/toggle'\n\ntype ToggleGroupVariants = VariantProps<typeof toggleVariants> & {\n  spacing?: number\n}\n\nconst props = defineProps<ToggleGroupItemProps & {\n  class?: HTMLAttributes[\"class\"]\n  variant?: ToggleGroupVariants[\"variant\"]\n  size?: ToggleGroupVariants[\"size\"]\n}>()\n\nconst context = inject<ToggleGroupVariants>(\"toggleGroup\")\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\", \"variant\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ToggleGroupItem\n    v-slot=\"slotProps\"\n    data-slot=\"toggle-group-item\"\n    :data-variant=\"context?.variant || variant\"\n    :data-size=\"context?.size || size\"\n    :data-spacing=\"context?.spacing\"\n    v-bind=\"forwardedProps\"\n    :class=\"cn(\n      toggleVariants({\n        variant: context?.variant || variant,\n        size: context?.size || size,\n      }),\n      'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10',\n      'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l',\n      props.class)\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </ToggleGroupItem>\n</template>\n"
  },
  {
    "path": "app/components/ui/toggle-group/index.ts",
    "content": "export { default as ToggleGroup } from \"./ToggleGroup.vue\"\nexport { default as ToggleGroupItem } from \"./ToggleGroupItem.vue\"\n"
  },
  {
    "path": "app/components/ui/tooltip/Tooltip.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TooltipRootEmits, TooltipRootProps } from \"reka-ui\"\nimport { TooltipRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<TooltipRootProps>()\nconst emits = defineEmits<TooltipRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <TooltipRoot\n    v-slot=\"slotProps\"\n    data-slot=\"tooltip\"\n    v-bind=\"forwarded\"\n  >\n    <slot v-bind=\"slotProps\" />\n  </TooltipRoot>\n</template>\n"
  },
  {
    "path": "app/components/ui/tooltip/TooltipContent.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TooltipContentEmits, TooltipContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { TooltipArrow, TooltipContent, TooltipPortal, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<TooltipContentProps & { class?: HTMLAttributes[\"class\"] }>(), {\n  sideOffset: 4,\n})\n\nconst emits = defineEmits<TooltipContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <TooltipPortal>\n    <TooltipContent\n      data-slot=\"tooltip-content\"\n      v-bind=\"{ ...forwarded, ...$attrs }\"\n      :class=\"cn('bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance', props.class)\"\n    >\n      <slot />\n\n      <TooltipArrow class=\"bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]\" />\n    </TooltipContent>\n  </TooltipPortal>\n</template>\n"
  },
  {
    "path": "app/components/ui/tooltip/TooltipProvider.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TooltipProviderProps } from \"reka-ui\"\nimport { TooltipProvider } from \"reka-ui\"\n\nconst props = withDefaults(defineProps<TooltipProviderProps>(), {\n  delayDuration: 0,\n})\n</script>\n\n<template>\n  <TooltipProvider v-bind=\"props\">\n    <slot />\n  </TooltipProvider>\n</template>\n"
  },
  {
    "path": "app/components/ui/tooltip/TooltipTrigger.vue",
    "content": "<script setup lang=\"ts\">\nimport type { TooltipTriggerProps } from \"reka-ui\"\nimport { TooltipTrigger } from \"reka-ui\"\n\nconst props = defineProps<TooltipTriggerProps>()\n</script>\n\n<template>\n  <TooltipTrigger\n    data-slot=\"tooltip-trigger\"\n    v-bind=\"props\"\n  >\n    <slot />\n  </TooltipTrigger>\n</template>\n"
  },
  {
    "path": "app/components/ui/tooltip/index.ts",
    "content": "export { default as Tooltip } from \"./Tooltip.vue\"\nexport { default as TooltipContent } from \"./TooltipContent.vue\"\nexport { default as TooltipProvider } from \"./TooltipProvider.vue\"\nexport { default as TooltipTrigger } from \"./TooltipTrigger.vue\"\n"
  },
  {
    "path": "app/composables/useAuthToken.ts",
    "content": "const TOKEN_KEY = 'SinkSiteToken'\n\nexport function useAuthToken() {\n  function getToken() {\n    if (import.meta.client) {\n      return localStorage.getItem(TOKEN_KEY)\n    }\n    return null\n  }\n\n  function setToken(token: string) {\n    localStorage.setItem(TOKEN_KEY, token)\n  }\n\n  function removeToken() {\n    localStorage.removeItem(TOKEN_KEY)\n  }\n\n  return { getToken, setToken, removeToken }\n}\n"
  },
  {
    "path": "app/composables/useGithubStats.ts",
    "content": "import { useAppConfig, useFetch } from '#imports'\nimport { computed } from 'vue'\n\nexport function useGithubStats() {\n  const { github } = useAppConfig()\n  const repo = github.replace('https://github.com/', '')\n\n  const { data, status } = useFetch(\n    `https://api.github.com/repos/${repo}`,\n    {\n      key: 'github-stats',\n      server: false,\n      lazy: true,\n      dedupe: 'defer',\n      transform: (res: { stargazers_count: number, forks_count: number }) => ({\n        stars: res.stargazers_count,\n        forks: res.forks_count,\n      }),\n      getCachedData: (key, nuxtApp) => nuxtApp.payload?.data?.[key] ?? nuxtApp.static?.data?.[key],\n      onResponseError: ({ response }) => {\n        // Silently handle GitHub API errors (rate limit, network issues, etc.)\n        console.warn(`[useGithubStats] GitHub API error: ${response.status}`)\n      },\n    },\n  )\n\n  const formattedStats = computed(() => ({\n    stars: data.value?.stars?.toLocaleString() ?? '6,000',\n    forks: data.value?.forks?.toLocaleString() ?? '4,000',\n  }))\n\n  return { stats: formattedStats, status }\n}\n"
  },
  {
    "path": "app/composables/useTrafficEventBus.ts",
    "content": "import type { TrafficEventParams } from '@/types'\nimport { useEventBus } from '@vueuse/core'\n\nconst trafficEventBus = useEventBus<TrafficEventParams>(Symbol('traffic'))\n\n/**\n * Traffic event bus for globe arc/ripple animations.\n * Wraps global event bus for testability and explicit dependency.\n */\nexport function useTrafficEventBus() {\n  return trafficEventBus\n}\n"
  },
  {
    "path": "app/data/testimonials.json",
    "content": "[\n  {\n    \"id\": \"1953003326317920422\",\n    \"name\": \"OpenAlternative\",\n    \"username\": \"ossalternative\",\n    \"content\": \"⭐ Sink has just reached 5,000 stars on GitHub! Huge congrats! 🎉 Simple, speedy, and secure link shortener with analytics\",\n    \"url\": \"https://x.com/ossalternative/status/1953003326317920422\",\n    \"verified\": false,\n    \"date\": \"2025-08-06T08:00:52.000Z\"\n  },\n  {\n    \"id\": \"1857623546606080350\",\n    \"name\": \"toobug\",\n    \"username\": \"TooooooBug\",\n    \"content\": \"我就说多翻翻能翻到好东西吧。Sink这个短链系统（）用的网站图标来自，这个网站提供了按email 用户名 域名来返回对象图标/头像的功能，可以直接引用，很方便。\",\n    \"url\": \"https://x.com/TooooooBug/status/1857623546606080350\",\n    \"verified\": false,\n    \"date\": \"2024-11-16T03:15:41.000Z\"\n  },\n  {\n    \"id\": \"1876990358250246628\",\n    \"name\": \"Ian Howells\",\n    \"username\": \"ianhowells\",\n    \"content\": \"Link shortener, run entirely on CF w/ analytics. You know.. if having redirects you can change the target URL on later is a thing you're interested in.\",\n    \"url\": \"https://x.com/ianhowells/status/1876990358250246628\",\n    \"verified\": false,\n    \"date\": \"2025-01-08T13:52:28.000Z\"\n  },\n  {\n    \"id\": \"1961700003459862799\",\n    \"name\": \"Wey Gu 古思为\",\n    \"username\": \"wey_gu\",\n    \"content\": \"再次感慨、感谢面条哥哥 @miantiao_me 的 做的太好了。 同时感恩 cloudflare 的完备、慷慨的服务。\",\n    \"url\": \"https://x.com/wey_gu/status/1961700003459862799\",\n    \"verified\": true,\n    \"date\": \"2025-08-30T07:58:22.000Z\"\n  },\n  {\n    \"id\": \"1808150012058390969\",\n    \"name\": \"Vincent Yang\",\n    \"username\": \"m1ssuo\",\n    \"content\": \"买了个新域名部署了一下 @ccbikai 的 Sink，体验不错。\",\n    \"url\": \"https://x.com/m1ssuo/status/1808150012058390969\",\n    \"verified\": true,\n    \"date\": \"2024-07-02T14:45:31.000Z\"\n  },\n  {\n    \"id\": \"1794746047136411723\",\n    \"name\": \"Moran🫆\",\n    \"username\": \"NoPeople404\",\n    \"content\": \"Sink - 基于 Cloudflare 带访问统计的短链系统\",\n    \"url\": \"https://x.com/NoPeople404/status/1794746047136411723\",\n    \"verified\": false,\n    \"date\": \"2024-05-26T15:02:57.000Z\"\n  },\n  {\n    \"id\": \"1990813013247492308\",\n    \"name\": \"xmok\",\n    \"username\": \"xmok_\",\n    \"content\": \"sink is a pretty cool link shortener by @0xKaiBi. what's special? - it works with cloudflare workers (almost free!) - has a really cool globe for realtime analysis (see it below) last week, i made the @raycast extension available on windows: list, create, view link details 🔥.\",\n    \"url\": \"https://x.com/xmok_/status/1990813013247492308\",\n    \"verified\": false,\n    \"date\": \"2025-11-18T16:03:04.000Z\"\n  },\n  {\n    \"id\": \"1931199560489251052\",\n    \"name\": \"Fox@MkSaaS.com\",\n    \"username\": \"indie_maker_fox\",\n    \"content\": \"🎉 Sink A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.\",\n    \"url\": \"https://x.com/indie_maker_fox/status/1931199560489251052\",\n    \"verified\": true,\n    \"date\": \"2025-06-07T04:00:29.000Z\"\n  },\n  {\n    \"id\": \"1817702576629985685\",\n    \"name\": \"Tw93\",\n    \"username\": \"HiTw93\",\n    \"content\": \"#工程师工具 逛 GitHub 发现一款不错的短链接开源工具 Sink，有访问数据分析功能，使用尚简单、快速、安全，可以直接部署在 Cloudflare 上运行，有需要的小伙伴可以部署一个自用玩玩。 🤖\",\n    \"url\": \"https://x.com/HiTw93/status/1817702576629985685\",\n    \"verified\": true,\n    \"date\": \"2024-07-28T23:24:00.000Z\"\n  },\n  {\n    \"id\": \"1944683470627741966\",\n    \"name\": \"allentown\",\n    \"username\": \"allentown521\",\n    \"content\": \"🏆 Daily Winner! Sink IntroductionSink is a simple, speedy, and secure link shortener designed for individuals and businesses seeking efficien... From 👉:\",\n    \"url\": \"https://x.com/allentown521/status/1944683470627741966\",\n    \"verified\": false,\n    \"date\": \"2025-07-14T09:00:44.000Z\"\n  },\n  {\n    \"id\": \"1795169172873413116\",\n    \"name\": \"GitHubGPT\",\n    \"username\": \"GitHubGPT\",\n    \"content\": \"📛 Sink 🧠 Sink is a fast, secure Cloudflare link shortener with analytics. It provides URL shortening, tracking, serverless deployment, custom and AI slugs, and link expiration. 🛠️ @ccbikai 💻 Vue ⭐ 518 🍴 150 🔗\",\n    \"url\": \"https://x.com/GitHubGPT/status/1795169172873413116\",\n    \"verified\": true,\n    \"date\": \"2024-05-27T19:04:18.000Z\"\n  },\n  {\n    \"id\": \"1925250262870237555\",\n    \"name\": \"Michael Hoffmann\",\n    \"username\": \"Mokkapps\",\n    \"content\": \"🛠️ Sink 👉🏻 A simple, speedy, secure link shortener with analytics. 👉🏻 Source code is open-source and built with Nuxt.\",\n    \"url\": \"https://x.com/Mokkapps/status/1925250262870237555\",\n    \"verified\": false,\n    \"date\": \"2025-05-21T18:00:06.000Z\"\n  },\n  {\n    \"id\": \"1796478331522781460\",\n    \"name\": \"Luo说不啰嗦\",\n    \"username\": \"LuoSays\",\n    \"content\": \"分享一个优秀的开源项目：Sink - 基于 Cloudflare 构建的短网址系统。 特性： 1. 自带访问统计 2. 自定义 slug（还能 AI 生成） 4. 可设置链接有效期 5. Cloudflare 部署，免费版就支持 300 万次访问/月 仓库： 作者 @ccbikai 是我在 WordPress\",\n    \"url\": \"https://x.com/LuoSays/status/1796478331522781460\",\n    \"verified\": true,\n    \"date\": \"2024-05-31T09:46:25.000Z\"\n  },\n  {\n    \"id\": \"1893594908147270073\",\n    \"name\": \"katon\",\n    \"username\": \"hellokaton\",\n    \"content\": \"呜呼~ 今天发一个分享URL里面链接有点长，用了别人的竟然过期了，这谁能忍啊。 @miantiao_me 开源的 Sink 短链接系统基于 Cloudflare 部署非常丝滑，顺便看了下代码写的简洁优雅（不是吹啊），自用的话强烈推荐！！！ 今天买了个域名 555 .dog （流泪小狗）花了不到20分钟部署好了😉\",\n    \"url\": \"https://x.com/hellokaton/status/1893594908147270073\",\n    \"verified\": true,\n    \"date\": \"2025-02-23T09:33:02.000Z\"\n  },\n  {\n    \"id\": \"1809763345320624271\",\n    \"name\": \"f_sugar\",\n    \"username\": \"f_sugar\",\n    \"content\": \"ccbikai/Sink: ⚡ A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.\",\n    \"url\": \"https://x.com/f_sugar/status/1809763345320624271\",\n    \"verified\": false,\n    \"date\": \"2024-07-07T01:36:19.000Z\"\n  },\n  {\n    \"id\": \"1905626254931624281\",\n    \"name\": \"DeBill\",\n    \"username\": \"DeBill_me\",\n    \"content\": \"@yetone 面条大佬的Sink，Cloudflare 一键部署\",\n    \"url\": \"https://x.com/DeBill_me/status/1905626254931624281\",\n    \"verified\": true,\n    \"date\": \"2025-03-28T14:21:18.000Z\"\n  },\n  {\n    \"id\": \"1833125667568804284\",\n    \"name\": \"Dragos\",\n    \"username\": \"bitdoze\",\n    \"content\": \"Created a tutorial of how the sink link shortener created by @0xKaiBi can be installed on cloudflare for beginners: Video is showing everything:\",\n    \"url\": \"https://x.com/bitdoze/status/1833125667568804284\",\n    \"verified\": true,\n    \"date\": \"2024-09-09T12:49:51.000Z\"\n  },\n  {\n    \"id\": \"1846465874389356916\",\n    \"name\": \"luolei\",\n    \"username\": \"luoleiorg\",\n    \"content\": \"转码萌新，用 Cursor 花了2小时 , 给效率神器 Raycast 开发了个短链插件。🐶 基于目前市面上最佳，由 @ccbikai 打造的使用 Cloudflare 技术栈的开源短链系统 Sink 。直接通过 Raycast 便捷管理和使用 Sink 短链。 🔗 仓库地址: 🛍️ 商店地址: 0️⃣ 开源\",\n    \"url\": \"https://x.com/luoleiorg/status/1846465874389356916\",\n    \"verified\": true,\n    \"date\": \"2024-10-16T08:19:04.000Z\"\n  },\n  {\n    \"id\": \"1930301401323975179\",\n    \"name\": \"Lakphy\",\n    \"username\": \"lakphy\",\n    \"content\": \"部署了一份 @miantiao_me 面条学长开源的 sink 短链服务，很厉害，我第一次用 workers ，十几分钟就跑通了，现在云服务真是越来越方便了😍\",\n    \"url\": \"https://x.com/lakphy/status/1930301401323975179\",\n    \"verified\": true,\n    \"date\": \"2025-06-04T16:31:31.000Z\"\n  },\n  {\n    \"id\": \"1988243083558035901\",\n    \"name\": \"王小嗨\",\n    \"username\": \"yeahwong\",\n    \"content\": \"自從 @miantiao_me 推出部署在 Cloudflare 上的 短鏈服務後，我就一直很感興趣。只是自己太笨，一直沒操練起來。😂 今天在網上找到了一份教程，順利完成部署，感謝教程作者分享，也特別感謝 @miantiao_me 大佬。🙏 📘 教程：\",\n    \"url\": \"https://x.com/yeahwong/status/1988243083558035901\",\n    \"verified\": false,\n    \"date\": \"2025-11-11T13:51:05.000Z\"\n  }\n]"
  },
  {
    "path": "app/error.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NuxtError } from '#app'\nimport errorImage from './assets/images/404.svg?raw'\n\ndefineProps<{\n  error: NuxtError\n}>()\n</script>\n\n<template>\n  <NuxtLayout name=\"default\">\n    <a\n      class=\"\n        mx-auto flex h-full w-full max-w-[600px] items-center justify-center\n        [&_svg]:w-full\n      \"\n      href=\"/\"\n      v-html=\"errorImage\"\n    />\n  </NuxtLayout>\n</template>\n"
  },
  {
    "path": "app/layouts/default.vue",
    "content": "<script setup lang=\"ts\">\nimport { Menu, Star, X } from 'lucide-vue-next'\nimport { GitHubIcon, TelegramIcon, XIcon } from 'vue3-simple-icons'\n\nconst showMenu = ref(false)\nconst { title, telegram, twitter, github } = useAppConfig()\nconst { stats } = useGithubStats()\n</script>\n\n<template>\n  <div class=\"flex min-h-screen flex-col\">\n    <!-- Header -->\n    <header>\n      <nav\n        :data-state=\"showMenu && 'active'\"\n        class=\"fixed z-20 w-full border-b bg-background/50 backdrop-blur-3xl\"\n      >\n        <div class=\"mx-auto max-w-6xl px-6 transition-all duration-300\">\n          <div\n            class=\"\n              relative flex flex-wrap items-center justify-between gap-6 py-3\n              lg:gap-0 lg:py-4\n            \"\n          >\n            <div\n              class=\"\n                flex w-full items-center justify-between gap-12\n                lg:w-auto\n              \"\n            >\n              <NuxtLink\n                to=\"/\"\n                :title=\"title\"\n                aria-label=\"home\"\n                class=\"flex items-center space-x-2\"\n              >\n                <span\n                  class=\"flex size-8 items-center justify-center rounded-full\"\n                >\n                  <img\n                    src=\"/sink.png\"\n                    :alt=\"`${title} Logo`\"\n                    class=\"size-full rounded-full\"\n                  >\n                </span>\n                <span class=\"text-xl font-black\">{{ title }}</span>\n              </NuxtLink>\n\n              <button\n                aria-label=\"Toggle Menu\"\n                :aria-expanded=\"showMenu\"\n                aria-controls=\"mobile-menu\"\n                class=\"\n                  relative z-20 -m-2.5 -mr-4 block cursor-pointer p-2.5\n                  lg:hidden\n                \"\n                @click=\"showMenu = !showMenu\"\n              >\n                <Menu\n                  class=\"m-auto size-6 duration-200\" :class=\"[\n                    showMenu && 'scale-0 rotate-180 opacity-0',\n                  ]\"\n                />\n                <X\n                  class=\"absolute inset-0 m-auto size-6 duration-200\" :class=\"[\n                    showMenu ? 'scale-100 rotate-0 opacity-100' : `\n                      scale-0 -rotate-180 opacity-0\n                    `,\n                  ]\"\n                />\n              </button>\n            </div>\n\n            <div\n              id=\"mobile-menu\"\n              class=\"\n                mb-6 w-full flex-wrap items-center justify-end space-y-8\n                rounded-3xl border bg-background p-6 shadow-2xl\n                shadow-zinc-300/20\n                md:flex-nowrap\n                lg:m-0 lg:flex lg:w-fit lg:items-center lg:gap-6 lg:space-y-0\n                lg:border-transparent lg:bg-transparent lg:p-0 lg:shadow-none\n                dark:shadow-none dark:lg:bg-transparent\n              \" :class=\"[\n                showMenu ? 'block' : 'hidden',\n              ]\"\n            >\n              <div\n                class=\"\n                  flex w-full flex-col items-center space-y-3\n                  sm:flex-row sm:gap-3 sm:space-y-0\n                  md:w-fit\n                \"\n              >\n                <Button\n                  as-child\n                  variant=\"outline\"\n                  size=\"sm\"\n                >\n                  <a\n                    :href=\"github\"\n                    target=\"_blank\"\n                    :title=\"$t('layouts.footer.social.github')\"\n                    class=\"flex items-center gap-1.5\"\n                  >\n                    <GitHubIcon class=\"size-4\" />\n                    <Star class=\"size-3\" />\n                    <span class=\"tabular-nums\">{{ stats.stars }}</span>\n                  </a>\n                </Button>\n\n                <SwitchLanguage />\n                <SwitchTheme />\n              </div>\n            </div>\n          </div>\n        </div>\n      </nav>\n    </header>\n\n    <!-- Main Content -->\n    <main class=\"flex flex-1 flex-col pt-20\">\n      <slot />\n    </main>\n\n    <!-- Footer -->\n    <footer class=\"border-t bg-background py-8\">\n      <div class=\"mx-auto max-w-6xl px-6\">\n        <div\n          class=\"\n            flex flex-col items-center gap-6 pt-2\n            md:flex-row md:justify-between\n          \"\n        >\n          <div\n            class=\"\n              flex flex-col items-center gap-4\n              md:flex-row md:gap-6\n            \"\n          >\n            <NuxtLink\n              to=\"/\"\n              :title=\"title\"\n              aria-label=\"home\"\n              class=\"block size-fit\"\n            >\n              <div class=\"flex items-center space-x-2\">\n                <span\n                  class=\"flex size-8 items-center justify-center rounded-full\"\n                >\n                  <img\n                    src=\"/sink.png\"\n                    :alt=\"`${title} Logo`\"\n                    class=\"size-full rounded-full\"\n                  >\n                </span>\n                <span class=\"text-xl font-black\">{{ title }}</span>\n              </div>\n            </NuxtLink>\n\n            <small class=\"block text-center text-sm text-muted-foreground\">\n              &copy; {{ new Date().getFullYear() }}\n              <a\n                href=\"https://html.zone\"\n                target=\"_blank\"\n                title=\"HTML.ZONE\"\n                class=\"hover:text-primary\"\n              >\n                {{ $t('layouts.footer.copyright') }}\n              </a>\n            </small>\n          </div>\n\n          <div class=\"flex justify-center gap-6 text-sm\">\n            <a\n              v-if=\"twitter\"\n              :href=\"twitter\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              :title=\"$t('layouts.footer.social.twitter')\"\n              aria-label=\"Twitter\"\n              class=\"\n                block text-muted-foreground\n                hover:text-primary\n              \"\n            >\n              <XIcon class=\"size-6\" />\n            </a>\n            <a\n              v-if=\"telegram\"\n              :href=\"telegram\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              :title=\"$t('layouts.footer.social.telegram')\"\n              aria-label=\"Telegram\"\n              class=\"\n                block text-muted-foreground\n                hover:text-primary\n              \"\n            >\n              <TelegramIcon class=\"size-6\" />\n            </a>\n            <a\n              v-if=\"github\"\n              :href=\"github\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              :title=\"$t('layouts.footer.social.github')\"\n              aria-label=\"GitHub\"\n              class=\"\n                block text-muted-foreground\n                hover:text-primary\n              \"\n            >\n              <GitHubIcon class=\"size-6\" />\n            </a>\n          </div>\n        </div>\n      </div>\n    </footer>\n  </div>\n</template>\n"
  },
  {
    "path": "app/lib/utils.ts",
    "content": "import type { ClassValue } from 'clsx'\nimport { clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"
  },
  {
    "path": "app/pages/index.vue",
    "content": "<template>\n  <div class=\"flex flex-col justify-center\">\n    <HomeHero />\n    <HomeLogos />\n    <HomeFeatures />\n    <HomeStats />\n    <HomeTestimonials />\n    <HomeCta />\n  </div>\n</template>\n"
  },
  {
    "path": "app/plugins/ssr-width.ts",
    "content": "import { provideSSRWidth } from '@vueuse/core'\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  provideSSRWidth(1024, nuxtApp.vueApp)\n})\n"
  },
  {
    "path": "app/types/index.ts",
    "content": "export * from '#layers/dashboard/shared/types'\nexport * from '#shared/types/link'\nexport * from '#shared/types/traffic'\n"
  },
  {
    "path": "app/utils/api.ts",
    "content": "import type { NitroFetchOptions, NitroFetchRequest } from 'nitropack'\nimport { navigateTo } from '#imports'\nimport { defu } from 'defu'\nimport { useAuthToken } from '@/composables/useAuthToken'\n\ntype APIOptions = Omit<NitroFetchOptions<NitroFetchRequest>, 'headers'> & {\n  headers?: Record<string, string>\n}\n\nexport function useAPI<T = unknown>(api: string, options?: APIOptions): Promise<T> {\n  const { getToken, removeToken } = useAuthToken()\n\n  const mergedOptions = defu(options || {}, {\n    headers: {\n      Authorization: `Bearer ${getToken() || ''}`,\n    },\n  }) as NitroFetchOptions<NitroFetchRequest>\n\n  return $fetch<T>(api, mergedOptions).catch((error) => {\n    if (error?.status === 401) {\n      removeToken()\n      navigateTo('/dashboard/login')\n    }\n    return Promise.reject(error)\n  }) as Promise<T>\n}\n"
  },
  {
    "path": "app/utils/file.ts",
    "content": "/**\n * Save data as a JSON file and trigger download\n */\nexport function saveAsJson(data: unknown, filename: string): void {\n  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })\n  const url = URL.createObjectURL(blob)\n  const link = document.createElement('a')\n  link.href = url\n  link.download = filename\n  document.body.appendChild(link)\n  link.click()\n  document.body.removeChild(link)\n  URL.revokeObjectURL(url)\n}\n"
  },
  {
    "path": "app/utils/flag.ts",
    "content": "const EMOJI_FLAG_UNICODE_STARTING_POSITION = 127397\n\nexport function getFlag(countryCode: string = '') {\n  const regex = /^[A-Z]{2}$/.test(countryCode)\n  if (!countryCode || !regex)\n    return void 0\n  return String.fromCodePoint(...countryCode.split('').map(char => EMOJI_FLAG_UNICODE_STARTING_POSITION + char.charCodeAt(0)))\n}\n"
  },
  {
    "path": "app/utils/image.ts",
    "content": "export const IMAGE_MAX_SIZE = 5 * 1024 * 1024\nexport const IMAGE_ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']\n"
  },
  {
    "path": "app/utils/intl.ts",
    "content": "/**\n * Get region/country display name\n * @param code - ISO 3166-1 alpha-2 country code (e.g., 'CN', 'JP', 'US')\n * @param locale - Locale string (e.g., 'zh-CN', 'en')\n */\nexport function getRegionName(code: string, locale: string): string {\n  if (!code || typeof Intl === 'undefined')\n    return code\n  try {\n    const displayNames = new Intl.DisplayNames([locale], { type: 'region' })\n    return displayNames.of(code) ?? code\n  }\n  catch {\n    return code\n  }\n}\n\n/**\n * Get language display name\n * @param code - Language code (e.g., 'zh', 'en', 'ja')\n * @param locale - Locale string\n */\nexport function getLanguageName(code: string, locale: string): string {\n  if (!code || typeof Intl === 'undefined')\n    return code\n  try {\n    const displayNames = new Intl.DisplayNames([locale], { type: 'language' })\n    return displayNames.of(code) ?? code\n  }\n  catch {\n    return code\n  }\n}\n"
  },
  {
    "path": "app/utils/number.ts",
    "content": "export function formatNumber(number: number): string {\n  if (!number || typeof Intl === 'undefined')\n    return String(number)\n\n  return new Intl.NumberFormat('en').format(number)\n}\n"
  },
  {
    "path": "app/utils/time.ts",
    "content": "import type { DateValue } from '@internationalized/date'\nimport { fromAbsolute, toCalendarDate } from '@internationalized/date'\n\nexport function getTimeZone() {\n  if (typeof Intl === 'undefined')\n    return 'Etc/UTC'\n\n  return Intl.DateTimeFormat().resolvedOptions().timeZone\n}\n\nexport function getLocale() {\n  if (typeof Intl === 'undefined')\n    return navigator.language\n\n  return Intl.DateTimeFormat().resolvedOptions().locale\n}\n\nexport function shortDate(unix = 0) {\n  return new Intl.DateTimeFormat(undefined, { dateStyle: 'short' }).format(unix * 1000)\n}\n\nexport function longDate(unix = 0) {\n  return new Intl.DateTimeFormat(undefined, { dateStyle: 'long' }).format(unix * 1000)\n}\n\nexport function shortTime(unix = 0) {\n  return new Intl.DateTimeFormat(undefined, { timeStyle: 'short' }).format(unix * 1000)\n}\n\nexport function longTime(unix = 0) {\n  return new Intl.DateTimeFormat(undefined, { timeStyle: 'long' }).format(unix * 1000)\n}\n\nexport function date2unix(dateValue: DateValue | Date, type?: string) {\n  const date = dateValue instanceof Date ? dateValue : dateValue.toDate(getTimeZone())\n  if (type === 'start')\n    return Math.floor(date.setHours(0, 0, 0) / 1000)\n\n  if (type === 'end')\n    return Math.floor(date.setHours(23, 59, 59) / 1000)\n\n  return Math.floor(date.getTime() / 1000)\n}\n\nexport function unix2date(unix: number) {\n  return toCalendarDate(fromAbsolute(unix * 1000, getTimeZone()))\n}\n\nexport function getWeekdayNames(style: 'long' | 'short' | 'narrow' = 'short') {\n  const formatter = new Intl.DateTimeFormat(undefined, { weekday: style })\n  // 2024-01-01 is Monday\n  return Array.from({ length: 7 }, (_, i) => {\n    const date = new Date(2024, 0, 1 + i)\n    return formatter.format(date)\n  })\n}\n"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://shadcn-vue.com/schema.json\",\n  \"style\": \"new-york\",\n  \"typescript\": true,\n  \"tailwind\": {\n    \"config\": \"\",\n    \"css\": \"app/assets/css/tailwind.css\",\n    \"baseColor\": \"zinc\",\n    \"cssVariables\": true,\n    \"prefix\": \"\"\n  },\n  \"iconLibrary\": \"lucide\",\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\",\n    \"ui\": \"@/components/ui\",\n    \"lib\": \"@/lib\",\n    \"composables\": \"@/composables\"\n  },\n  \"registries\": {}\n}\n"
  },
  {
    "path": "docs/api.md",
    "content": "# Sink API\n\nSink provides a complete RESTful API for managing short links. Full API documentation is available via OpenAPI.\n\n## OpenAPI Documentation\n\n- **OpenAPI JSON**: `/_docs/openapi.json`\n- **Scalar UI**: `/_docs/scalar`\n- **Swagger UI**: `/_docs/swagger`\n\nVisit your Sink instance at `https://your-domain/_docs/scalar` for interactive API documentation.\n\n## Authentication\n\nAll API endpoints require authentication via Bearer token in the `Authorization` header:\n\n```http\nAuthorization: Bearer YOUR_SITE_TOKEN\n```\n\nThe token is the same as `NUXT_SITE_TOKEN` configured in your environment variables.\n\n## API Endpoints\n\n### Links\n\n| Method | Endpoint           | Description                         |\n| ------ | ------------------ | ----------------------------------- |\n| `POST` | `/api/link/create` | Create a new short link             |\n| `PUT`  | `/api/link/edit`   | Update an existing link             |\n| `POST` | `/api/link/delete` | Delete a link                       |\n| `GET`  | `/api/link/list`   | List all links (paginated)          |\n| `GET`  | `/api/link/export` | Export all links as JSON            |\n| `POST` | `/api/link/import` | Import links from JSON              |\n| `GET`  | `/api/link/ai`     | Generate AI-powered slug suggestion |\n\n### Analytics\n\n| Method | Endpoint              | Description                       |\n| ------ | --------------------- | --------------------------------- |\n| `GET`  | `/api/stats/summary`  | Get analytics summary             |\n| `GET`  | `/api/stats/metrics`  | Get detailed metrics by dimension |\n| `GET`  | `/api/stats/realtime` | Get real-time analytics data      |\n\n## Example: Create Short Link\n\n```http\nPOST /api/link/create\nAuthorization: Bearer SinkCool\nContent-Type: application/json\n\n{\n  \"url\": \"https://github.com/miantiao-me/Sink\",\n  \"slug\": \"sink\",\n  \"comment\": \"GitHub repo\",\n  \"expiration\": \"2025-12-31T23:59:59Z\",\n  \"ios\": \"https://apps.apple.com/app/id6745417598\",\n  \"android\": \"https://play.google.com/store/apps/details?id=com.example\",\n  \"ogTitle\": \"Sink - Link Shortener\",\n  \"ogDescription\": \"A simple, speedy, secure link shortener\",\n  \"ogImage\": \"https://example.com/image.png\"\n}\n```\n\n### Response\n\n```json\n{\n  \"link\": {\n    \"id\": \"01jxyz...\",\n    \"url\": \"https://github.com/miantiao-me/Sink\",\n    \"slug\": \"sink\",\n    \"comment\": \"GitHub repo\",\n    \"createdAt\": 1718119809,\n    \"updatedAt\": 1718119809\n  }\n}\n```\n\n## Request Body Fields\n\n| Field               | Type      | Required | Description                                                           |\n| ------------------- | --------- | -------- | --------------------------------------------------------------------- |\n| `url`               | `string`  | ✅       | Target URL (max 2048 chars)                                           |\n| `slug`              | `string`  | ❌       | Custom slug (auto-generated if omitted)                               |\n| `comment`           | `string`  | ❌       | Internal note for the link                                            |\n| `expiration`        | `string`  | ❌       | ISO 8601 expiration date                                              |\n| `ios`               | `string`  | ❌       | iOS/macOS redirect URL                                                |\n| `android`           | `string`  | ❌       | Android redirect URL                                                  |\n| `ogTitle`           | `string`  | ❌       | OpenGraph title                                                       |\n| `ogDescription`     | `string`  | ❌       | OpenGraph description                                                 |\n| `ogImage`           | `string`  | ❌       | OpenGraph image URL                                                   |\n| `cloaking`          | `boolean` | ❌       | Enable link cloaking (mask destination URL with short link)           |\n| `redirectWithQuery` | `boolean` | ❌       | Append query parameters to destination URL (overrides global setting) |\n| `password`          | `string`  | ❌       | Password protection for the link                                      |\n| `unsafe`            | `boolean` | ❌       | Mark link as unsafe (shows warning page before redirect)              |\n\n## CORS\n\nTo enable CORS for API endpoints, set `NUXT_API_CORS=true` during build.\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "# Sink Configuration\n\nSink provides some configuration options, which can be referred to in [.env.example](../.env.example).\n\n> When using Worker deployment, please note that variables with the `NUXT_PUBLIC_` prefix need to be configured in Workers' **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.\n\n## `NUXT_PUBLIC_PREVIEW_MODE`\n\n> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.\n\nSets the site to demo mode, the generated links will expire after 5 minutes, and the links cannot be edited or deleted.\n\n## `NUXT_PUBLIC_SLUG_DEFAULT_LENGTH`\n\n> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.\n\nSets the default length of the generated SLUG.\n\n## `NUXT_PUBLIC_KV_BATCH_LIMIT`\n\n> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.\n\nSets the maximum number of KV operations per request for import/export. Default is 50 (Cloudflare Workers limit per request). Import operations use half of this value since each link requires 2 KV operations (check existence + write).\n\n## `NUXT_REDIRECT_STATUS_CODE`\n\nRedirects default to use HTTP 301 status code, you can set it to `302`/`307`/`308`.\n\n## `NUXT_LINK_CACHE_TTL`\n\nCache links can speed up access, but setting them too long may result in slow changes taking effect. The default value is 60 seconds.\n\n## `NUXT_REDIRECT_WITH_QUERY`\n\nURL parameters are not carried during link redirection by default and it is not recommended to enable this feature. This is the global default; individual links can override this via the **Redirect with Query Parameters** toggle in **Link Settings**.\n\n## `NUXT_HOME_URL`\n\n> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.\n\nThe default Sink homepage is the introduction page, you can replace it with your own website.\n\n## `NUXT_DATASET`\n\nThe Analytics Engine DATASET, it is not recommended to modify unless you need to switch databases and clear historical data.\n\n## `NUXT_AI_MODEL`\n\nYou can modify the large model yourself. The supported names can be viewed at [Workers AI Models](https://developers.cloudflare.com/workers-ai/models/#text-generation).\n\n## `NUXT_AI_PROMPT`\n\nSupports custom prompts, it is recommended to keep the placeholder {slugRegex}.\n\nDefault prompt:\n\n```txt\nYou are a URL shortening assistant, please shorten the URL provided by the user into a SLUG. The SLUG information must come from the URL itself, do not make any assumptions. A SLUG is human-readable and should not exceed three words and can be validated using regular expressions {slugRegex} . Only the best one is returned, the format must be JSON reference {\"slug\": \"example-slug\"}\n```\n\n## `NUXT_CASE_SENSITIVE`\n\nSet URL case sensitivity.\n\n## `NUXT_LIST_QUERY_LIMIT`\n\nSet the maximum query data volume for the Metric list.\n\n## `NUXT_DISABLE_BOT_ACCESS_LOG`\n\nAccess statistics do not count bot traffic.\n\n## `NUXT_API_CORS`\n\nSet the environment variable `NUXT_API_CORS=true` during build to enable CORS support for the API.\n\n## `NUXT_DISABLE_AUTO_BACKUP`\n\nSet to `true` to disable the automatic daily KV backup to R2 storage. Default is `false`.\n\nThis feature requires:\n\n1. R2 bucket binding configured in `wrangler.jsonc`\n2. Create R2 bucket: `wrangler r2 bucket create sink`\n\nBackups are stored in R2 with the path `backups/links-{timestamp}.json` and run daily at 00:00 UTC.\n\n## `NUXT_SAFE_BROWSING_DOH`\n\nSet to a DNS over HTTPS (DoH) endpoint URL to enable automatic unsafe link detection when creating or editing links. When enabled, Sink queries the DoH service to check if the destination domain is flagged as malicious. If the domain resolves to `0.0.0.0`, the link is automatically marked as unsafe and visitors will see a warning page before being redirected.\n\nRecommended values:\n\n- `https://family.cloudflare-dns.com/dns-query` — Cloudflare Family DNS (blocks malware and adult content)\n- Custom [Cloudflare Zero Trust Gateway](https://developers.cloudflare.com/cloudflare-one/policies/gateway/) DoH URL — supports custom block lists, domain risk categories, and more granular control\n\nDefault is empty (disabled). Users can still manually mark links as unsafe in the dashboard regardless of this setting.\n\n## `NUXT_NOT_FOUND_REDIRECT`\n\nOptional custom redirect target when a slug is not found.\nIf this is not set, Sink will fall back to its default 404 page.\n"
  },
  {
    "path": "docs/deployment/pages.md",
    "content": "# Deployment on Cloudflare Pages\n\n1. [Fork](https://github.com/miantiao-me/Sink/fork) the repository to your GitHub account.\n2. Create a project in [Cloudflare Pages](https://developers.cloudflare.com/pages/).\n3. Select the `Sink` repository and choose the `Nuxt.js` preset.\n4. Configure the following environment variables:\n   - `NUXT_SITE_TOKEN`: Must be at least **8** characters long. This token grants access to your dashboard.\n   - `NUXT_CF_ACCOUNT_ID`: Find your [account ID](https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/).\n   - `NUXT_CF_API_TOKEN`: Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with at least `Account.Account Analytics` permission. [See reference.](https://developers.cloudflare.com/analytics/analytics-engine/sql-api/#authentication)\n\n5. Save and deploy the project.\n6. Cancel the deployment, then go to **Settings** -> **Bindings** -> **Add**:\n   - **KV Namespace**: Bind the variable name `KV` to a [KV namespace](https://developers.cloudflare.com/kv/) (create a new one under **Storage & Databases** -> **KV**).\n   - **Workers AI** (_Optional_): Bind the variable name `AI` to the Workers AI Catalog.\n   - **R2 Bucket** (_Optional, for OpenGraph image upload_): Create an R2 bucket under **Storage & Databases** -> **R2**, then bind the variable name `R2` to the bucket.\n   - **Analytics Engine**:\n     - In **Workers & Pages**, go to **Account details** in the right panel, locate `Analytics Engine`, and click `Set up` to enable the free tier.\n     - Return to **Settings** -> **Bindings** -> **Add** and select **Analytics engine**.\n     - Bind the variable name `ANALYTICS` to the `sink` dataset.\n\n7. Add Compatibility flags\n   - Go to **Settings** -> **Runtime** -> **Compatibility flags** and set the following flags `nodejs_compat`.\n8. Redeploy the project.\n9. To update code, refer to the official GitHub documentation [Syncing a fork branch from the web UI](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork#syncing-a-fork-branch-from-the-web-ui 'GitHub: Syncing a fork').\n"
  },
  {
    "path": "docs/deployment/workers.md",
    "content": "# Deployment on Cloudflare Workers\n\n1. [Fork](https://github.com/miantiao-me/Sink/fork) the repository to your GitHub account.\n2. Create a [KV namespace](https://developers.cloudflare.com/kv/) (under **Storage & Databases** -> **KV**), and copy the namespace ID.\n3. Update the `kv_namespaces` ID in `wrangler.jsonc` with your own namespace ID.\n4. (_Optional_) For OpenGraph image upload, create an [R2 bucket](https://developers.cloudflare.com/r2/) named `sink` (or run `wrangler r2 bucket create sink`). If you don't need this feature, comment out the `r2_buckets` section in `wrangler.jsonc`.\n5. Create a project in [Cloudflare Workers](https://developers.cloudflare.com/workers/).\n6. Select the `Sink` repository and use the following build and deploy commands:\n   - **Build command**: `pnpm run build` or `npm run build`\n   - **Deploy command**: `npx wrangler deploy`\n\n7. Save and deploy the project.\n8. After deployment, go to **Settings** -> **Variables and Secrets** -> **Add**, and configure the following environment variables:\n   - `NUXT_SITE_TOKEN`: Must be at least **8** characters long. This token grants access to your dashboard.\n   - `NUXT_CF_ACCOUNT_ID`: Find your [account ID](https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/).\n   - `NUXT_CF_API_TOKEN`: Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with at least `Account.Account Analytics` permission. [See reference.](https://developers.cloudflare.com/analytics/analytics-engine/sql-api/#authentication)\n\n9. Enable Analytics Engine. In **Workers & Pages**, go to **Account details** in the right panel, locate **Analytics Engine**, and click **Set up** to enable the free tier. Name them `sink` and `ANALYTICS`, or else overwrite it with `NUXT_DATASET` and update your `wrangler.jsonc` accordingly\n10. Redeploy the project.\n11. To update your code, refer to the official GitHub documentation: [Syncing a fork branch from the web UI](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork#syncing-a-fork-branch-from-the-web-ui 'GitHub: Syncing a fork').\n"
  },
  {
    "path": "docs/faqs.md",
    "content": "# FAQs\n\n## 1. Why can't I create a link?\n\nPlease check the Cloudflare KV bindings, the KV environment variable name should be all uppercase letters.\n\n<details>\n  <summary><b>Screenshot</b></summary>\n  <img alt=\"KV Bindings setting in Cloudflare\" src=\"/docs/images/faqs-kv.png\"/>\n</details>\n\n## 2. Why can't I log in?\n\nPlease check if `NUXT_SITE_TOKEN` is set to pure numbers, Sink does not support pure number Tokens, we consider this to be unsafe.\n\n## 3. Why can't I see the analytics data?\n\nAnalytics data requires access to Cloudflare’s settings:\n\n1. Verify `NUXT_CF_ACCOUNT_ID` and `NUXT_CF_API_TOKEN` are configured correctly (ensure the Account ID matches the deployment zone ID).\n2. Check that the Worker analytics engine is enabled.\n\n<details>\n  <summary><b>Screenshot</b></summary>\n  <img alt=\"Analytics engine Bindings setting in Cloudflare \" src=\"/docs/images/faqs-Analytics_engine.png\"/>\n</details>\n\n## 4. I don't want the current homepage? Can it be redirected to my blog?\n\nOf course. Please set the environment variable `NUXT_HOME_URL` to your blog or official website address.\n\n## 5. Why can't I see statistics after deploying with NuxtHub?\n\nNuxtHub's ANALYTICS points to its dataset, you need to set the `NUXT_DATASET` environment variable to point to the same dataset.\n\n## 6. Why are links always case-insensitive?\n\nThis is a feature of Sink. By default, we automatically convert all links to lowercase to avoid case-sensitive issues and improve usability. This ensures users don’t encounter errors due to accidental capitalization differences.\n\nHowever, you can disable this feature by setting the `NUXT_CASE_SENSITIVE` environment variable to `true`.\n\n### What happens when `NUXT_CASE_SENSITIVE` is `true`?\n\nNewly generated links will be case-sensitive, treating `MyLink` and `mylink` as distinct. Randomly generated slugs will include both uppercase and lowercase characters, offering a larger pool of unique combinations (but not user-friendly that why we default to non-case-sensitive).\n\n## 7. Why does the Metric list only show the top 500 data entries?\n\nTo improve query performance, we have limited the amount of data. If you need to query more data, you can adjust it through `NUXT_LIST_QUERY_LIMIT`.\n\n## 8. I don't want to count bot or crawler traffic\n\nSet `NUXT_DISABLE_BOT_ACCESS_LOG` to `true`.\n\n## 9. What is Link Cloaking?\n\nLink cloaking masks your destination URL by showing your short link domain in the browser address bar instead of redirecting to the target URL. The destination page loads inside a full-screen iframe.\n\n### How to enable it\n\nToggle **Enable Link Cloaking** in the **Link Settings** section when creating or editing a link.\n\n### Limitations\n\n- **Sites that block iframes**: Websites with `X-Frame-Options: DENY` or `Content-Security-Policy: frame-ancestors 'none'` will not load inside the iframe. Most major sites (Google, GitHub, Twitter, etc.) block iframe embedding.\n- **HTTPS required**: The destination URL must use HTTPS. Mixed content (HTTPS short link → HTTP destination) will be blocked by browsers.\n- **Limited interaction**: Some features like OAuth login flows, `window.top` navigation, and certain payment forms may not work correctly inside the iframe.\n- **Device redirects take priority**: If both cloaking and device redirects (iOS/Android) are configured, device redirects will take precedence on matching devices.\n\n### If the destination site blocks iframes\n\nIf you control the destination site, you can whitelist your short link domain by adding this response header:\n\n```\nContent-Security-Policy: frame-ancestors 'self' your-short-domain.com\n```\n\n## 10. What is Redirect with Query Parameters?\n\nWhen enabled, query parameters from the short link URL are appended to the destination URL. For example, visiting `https://s.ink/my-link?ref=twitter` would redirect to `https://example.com/page?ref=twitter`.\n\n### Per-link vs Global\n\n- **Global setting**: Set `NUXT_REDIRECT_WITH_QUERY=true` to enable for all links by default.\n- **Per-link override**: Toggle **Redirect with Query Parameters** in the **Link Settings** section when creating or editing a link. This overrides the global setting for that specific link.\n\nIf a link has no per-link setting, it falls back to the global configuration.\n\n## 11. How does the Import/Export feature work?\n\nImport and Export are designed to work within Cloudflare Workers' KV operation limits (50 per request by default).\n\n- **Export**: Downloads links in batches, automatically paginating until complete.\n- **Import**: Uploads links in batches (half of `NUXT_PUBLIC_KV_BATCH_LIMIT`, default 25) since each link requires 2 KV operations (check existence + write).\n- **Expired links**: Imported as-is to support migration scenarios.\n- **Duplicate slugs**: Skipped during import (existing links are preserved).\n- **Validation**: All links are validated against the schema before import starts.\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import antfu from '@antfu/eslint-config'\nimport eslintPluginBetterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport withNuxt from './.nuxt/eslint.config.mjs'\n\nexport default withNuxt(antfu(\n  {\n    formatters: true,\n    pnpm: false,\n    ignores: [\n      'app/components/ui/**',\n      'app/data/**',\n      'public/*.json',\n    ],\n    rules: {\n      '@typescript-eslint/ban-ts-comment': 'off',\n      'no-console': 'off',\n      'node/prefer-global/process': 'off',\n      'vue/no-v-html': 'off',\n    },\n  },\n  {\n    plugins: {\n      'better-tailwindcss': eslintPluginBetterTailwindcss,\n    },\n    settings: {\n      'better-tailwindcss': {\n        entryPoint: './app/assets/css/tailwind.css',\n      },\n    },\n    rules: {\n      ...eslintPluginBetterTailwindcss.configs['recommended-warn'].rules,\n    },\n  },\n))\n"
  },
  {
    "path": "i18n/i18n.config.ts",
    "content": "import { currentLocales } from './i18n'\n\nexport default defineI18nConfig(() => {\n  return {\n    legacy: false,\n    availableLocales: currentLocales.map(l => l.code),\n    fallbackLocale: 'en-US',\n    fallbackWarn: true,\n    missingWarn: true,\n  }\n})\n"
  },
  {
    "path": "i18n/i18n.ts",
    "content": "import type { LocaleObject } from '@nuxtjs/i18n'\n\nconst locales: LocaleObject[] = [\n  {\n    code: 'en-US',\n    file: 'en-US.json',\n    name: 'English',\n    emoji: '🇺🇸',\n  },\n  {\n    code: 'zh-CN',\n    file: 'zh-CN.json',\n    name: '简体中文',\n    emoji: '🇨🇳',\n  },\n  {\n    code: 'zh-TW',\n    file: 'zh-TW.json',\n    name: '繁體中文',\n    emoji: '🇹🇼',\n  },\n  {\n    code: 'fr-FR',\n    file: 'fr-FR.json',\n    name: 'Français',\n    emoji: '🇫🇷',\n  },\n  {\n    code: 'it-IT',\n    file: 'it-IT.json',\n    name: 'Italiano',\n    emoji: '🇮🇹',\n  },\n  {\n    code: 'vi-VN',\n    file: 'vi-VN.json',\n    name: 'Tiếng Việt',\n    emoji: '🇻🇳',\n  },\n  {\n    code: 'de-DE',\n    file: 'de-DE.json',\n    name: 'Deutsch',\n    emoji: '🇩🇪',\n  },\n  {\n    code: 'pt-PT',\n    file: 'pt-PT.json',\n    name: 'Português (PT)',\n    emoji: '🇵🇹',\n  },\n  {\n    code: 'pt-BR',\n    file: 'pt-BR.json',\n    name: 'Português (BR)',\n    emoji: '🇧🇷',\n  },\n]\n\nfunction buildLocales() {\n  const useLocales = Object.values(locales).reduce((acc, data) => {\n    acc.push(data)\n\n    return acc\n  }, <LocaleObject[]>[])\n\n  return useLocales.sort((a, b) => a.code.localeCompare(b.code))\n}\n\nexport const currentLocales = buildLocales()\n"
  },
  {
    "path": "i18n/locales/de-DE.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Mehrsprachigkeit\",\n    \"abbreviations\": \"De\",\n    \"current_language\": \"Deutsch\",\n    \"try_again\": \"Erneut versuchen\",\n    \"cancel\": \"Abbrechen\",\n    \"continue\": \"Weiter\",\n    \"close\": \"Schließen\",\n    \"save\": \"Speichern\",\n    \"edit\": \"Bearbeiten\",\n    \"delete\": \"Löschen\",\n    \"delete_success\": \"Erfolgreich gelöscht!\",\n    \"search\": \"Suchen\"\n  },\n  \"theme\": {\n    \"toggle\": \"Theme wechseln\",\n    \"light\": \"Hell\",\n    \"dark\": \"Dunkel\",\n    \"system\": \"System\"\n  },\n  \"login\": {\n    \"title\": \"Anmelden\",\n    \"description\": \"Gib deinen Site-Token ein, um dich anzumelden\",\n    \"tips\": \"Tipps\",\n    \"preview_token\": \"Der Site-Token für den Vorschaumodus ist\",\n    \"submit\": \"Anmelden\",\n    \"failed\": \"Anmeldung fehlgeschlagen, bitte versuche es erneut\",\n    \"token_label\": \"Site-Token\",\n    \"token_required\": \"Token ist erforderlich\"\n  },\n  \"logout\": {\n    \"title\": \"Abmelden?\",\n    \"confirm\": \"Möchtest du dich wirklich abmelden?\",\n    \"action\": \"Abmelden\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Produkte von HTML.ZONE\",\n      \"social\": {\n        \"email\": \"E-Mail\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Sprache auswählen\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Links\",\n    \"analysis\": \"Analyse\",\n    \"realtime\": \"Echtzeit\",\n    \"migrate\": \"Import/Export\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Link-Verkürzer\",\n    \"platform\": \"Plattform\",\n    \"settings\": \"Einstellungen\",\n    \"import\": \"Importieren\",\n    \"export\": \"Exportieren\",\n    \"coffee\": \"Kauf mir einen Kaffee\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stars\",\n    \"update\": \"Neue Version {version} verfügbar (aktuell: {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"GitHub-Repository\"\n    },\n    \"features\": {\n      \"title\": \"Funktionen\",\n      \"subtitle\": \"Einfache und effektive Funktionalität.\",\n      \"url_shortening\": {\n        \"title\": \"URL-Verkürzung\",\n        \"description\": \"Komprimiere deine URLs auf ihre minimale Länge\"\n      },\n      \"analytics\": {\n        \"title\": \"Analysen\",\n        \"description\": \"Überwache Link-Analysen und sammle aufschlussreiche Statistiken\"\n      },\n      \"serverless\": {\n        \"title\": \"Serverless\",\n        \"description\": \"Deployment ohne herkömmliche Server\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Anpassbarer Slug\",\n        \"description\": \"Unterstützung für personalisierte Slugs und Beachtung von Groß-/Kleinschreibung\"\n      },\n      \"ai_slug\": {\n        \"title\": \"KI-Slug\",\n        \"description\": \"Nutze KI, um Slugs zu generieren\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Link-Ablaufdatum\",\n        \"description\": \"Lege Ablaufdaten für deine Links fest\"\n      },\n      \"device_routing\": {\n        \"title\": \"Geräte-Routing\",\n        \"description\": \"iOS/Android-Nutzer zu App-Store-Links weiterleiten\"\n      },\n      \"og_preview\": {\n        \"title\": \"Social Preview\",\n        \"description\": \"Benutzerdefinierte OpenGraph-Vorschauen für soziale Medien\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Echtzeit-Analysen\",\n        \"description\": \"Live-3D-Globus und Echtzeit-Ereignisprotokolle\"\n      },\n      \"qr_code\": {\n        \"title\": \"QR-Code\",\n        \"description\": \"Generiere QR-Codes für deine Kurzlinks\"\n      },\n      \"import_export\": {\n        \"title\": \"Import/Export\",\n        \"description\": \"Massenmigration über JSON-Dateien\"\n      },\n      \"multi_language\": {\n        \"title\": \"Mehrsprachig\",\n        \"description\": \"Vollständige i18n-Unterstützung für das Dashboard\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Sofortiges Deployment\",\n      \"description\": \"Mit nur wenigen einfachen Klicks kannst du ohne Kosten mit der Bereitstellung beginnen\",\n      \"button\": \"Deployment starten\"\n    },\n    \"logos\": {\n      \"title\": \"Entwickelt mit großartigen Technologien\"\n    },\n    \"twitter\": {\n      \"follow\": \"Folge mir auf X (Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Sink in Zahlen\",\n      \"subtitle\": \"Von Entwicklern weltweit vertraut\",\n      \"stars\": \"Stars auf GitHub\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"Von Nutzern geliebt\",\n      \"subtitle\": \"Erfahre, was Entwickler über Sink sagen\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Dashboard\",\n    \"visits\": \"Besuche\",\n    \"views\": \"Aufrufe\",\n    \"visitors\": \"Besucher\",\n    \"referers\": \"Referrer\",\n    \"stats\": \"'s Statistiken\",\n    \"trend\": \"Trend\",\n    \"weekly_trend\": \"Wochentrend\",\n    \"custom_date\": \"Benutzerdefiniertes Datum\",\n    \"date\": \"Datum\",\n    \"date_range\": \"Datumsbereich\",\n    \"locations\": \"Standorte\",\n    \"details\": \"Details\",\n    \"name\": \"Name\",\n    \"count\": \"Anzahl\",\n    \"loading\": \"Laden\",\n    \"no_data\": \"Keine Daten\",\n    \"none\": \"(Keine)\",\n    \"filter_placeholder\": \"Links filtern...\",\n    \"date_picker\": {\n      \"today\": \"Heute\",\n      \"last_24h\": \"Letzte 24 Stunden\",\n      \"this_week\": \"Diese Woche\",\n      \"last_7d\": \"Letzte 7 Tage\",\n      \"this_month\": \"Dieser Monat\",\n      \"last_30d\": \"Letzte 30 Tage\",\n      \"last_90d\": \"Letzte 90 Tage\",\n      \"custom\": \"Benutzerdefiniert\",\n      \"custom_title\": \"Benutzerdefiniertes Datum\",\n      \"single_date\": \"Datum\",\n      \"date_range\": \"Datumsbereich\"\n    },\n    \"metrics\": {\n      \"country\": \"Land\",\n      \"region\": \"Region\",\n      \"city\": \"Stadt\",\n      \"referer\": \"Referrer\",\n      \"slug\": \"Slug\",\n      \"language\": \"Sprache\",\n      \"timezone\": \"Zeitzone\",\n      \"device\": \"Gerät\",\n      \"deviceType\": \"Gerätetyp\",\n      \"os\": \"Betriebssystem\",\n      \"browser\": \"Browser\",\n      \"browserType\": \"Browsertyp\"\n    },\n    \"time_picker\": {\n      \"today\": \"Heute\",\n      \"last_5m\": \"Letzte 5 Minuten\",\n      \"last_10m\": \"Letzte 10 Minuten\",\n      \"last_30m\": \"Letzte 30 Minuten\",\n      \"last_1h\": \"Letzte Stunde\",\n      \"last_6h\": \"Letzte 6 Stunden\",\n      \"last_12h\": \"Letzte 12 Stunden\",\n      \"last_24h\": \"Letzte 24 Stunden\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Link erstellen\",\n    \"edit\": \"Link bearbeiten\",\n    \"delete_confirm_title\": \"Bist du dir absolut sicher?\",\n    \"delete_confirm_desc\": \"Diese Aktion kann nicht rückgängig gemacht werden. Dadurch wird dein Link wirklich von den Servern gelöscht.\",\n    \"delete_success\": \"Erfolgreich gelöscht!\",\n    \"delete_failed\": \"Löschen fehlgeschlagen!\",\n    \"update_success\": \"Link erfolgreich aktualisiert\",\n    \"create_success\": \"Link erfolgreich erstellt\",\n    \"preview_mode_tip\": \"Der Link im Vorschaumodus ist bis zu 5 Minuten gültig.\",\n    \"search_placeholder\": \"Links suchen...\",\n    \"no_results\": \"Keine Links gefunden.\",\n    \"group_title\": \"Links\",\n    \"copy_success\": \"Erfolgreich kopiert!\",\n    \"created_at\": \"Erstellt am\",\n    \"updated_at\": \"Aktualisiert am\",\n    \"expires_at\": \"Läuft ab am\",\n    \"no_more\": \"Keine weiteren Links\",\n    \"load_failed\": \"Laden der Links fehlgeschlagen,\",\n    \"download_qr_code\": \"QR-Code herunterladen\",\n    \"change_qr_color\": \"QR-Code-Farbe ändern\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Kommentar\",\n      \"expiration\": \"Ablaufdatum\",\n      \"pick_date\": \"Datum auswählen\",\n      \"device_redirect\": \"Geräteweiterleitung\",\n      \"google_play\": \"Google Play URL\",\n      \"app_store\": \"App Store URL\",\n      \"link_settings\": \"Link-Einstellungen\",\n      \"redirect_with_query_label\": \"Mit Query-Parametern weiterleiten\",\n      \"redirect_with_query_description\": \"Query-Parameter des Kurzlinks an die Ziel-URL anhängen. Überschreibt die globale Einstellung.\",\n      \"cloaking_label\": \"Link-Cloaking aktivieren\",\n      \"cloaking_description\": \"Zeigt Ihren Kurzlink in der Adressleiste des Browsers anstelle der Ziel-URL an. Funktioniert möglicherweise nicht bei Websites, die iframe-Einbettung blockieren.\",\n      \"unsafe_label\": \"Als unsicher markieren\",\n      \"unsafe_description\": \"Vor der Weiterleitung eine Warnseite anzeigen. Besucher müssen bestätigen, um fortzufahren.\",\n      \"password_label\": \"Passwortschutz\",\n      \"password_description\": \"Passwort zum Zugriff auf diesen Kurzlink erforderlich.\",\n      \"password_placeholder\": \"Leer lassen zum Deaktivieren\",\n      \"og_settings\": \"OpenGraph-Einstellungen\",\n      \"og_title\": \"Titel\",\n      \"og_title_placeholder\": \"Leer lassen, um den URL-Hostnamen zu verwenden\",\n      \"og_description\": \"Beschreibung\",\n      \"og_description_placeholder\": \"Link-Beschreibung\",\n      \"og_image\": \"Bild\",\n      \"advanced\": \"Erweitert\",\n      \"more_options\": \"Weitere Optionen\",\n      \"image_invalid_type\": \"Bitte lade eine Bilddatei hoch\",\n      \"image_size_limit\": \"Bildgröße darf 5 MB nicht überschreiten\",\n      \"image_upload_success\": \"Bild erfolgreich hochgeladen\",\n      \"image_upload_failed\": \"Bild konnte nicht hochgeladen werden\",\n      \"image_upload_hint\": \"Klicken oder ziehen zum Hochladen\",\n      \"image_ratio_hint\": \"Empfohlen: 1200×630\",\n      \"slug_required\": \"Bitte zuerst einen Slug eingeben\"\n    },\n    \"sort\": {\n      \"newest\": \"Neueste zuerst\",\n      \"oldest\": \"Älteste zuerst\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Hinweis: Nur für geladene Links\"\n    },\n    \"update_failed\": \"Link konnte nicht aktualisiert werden\",\n    \"create_failed\": \"Link konnte nicht erstellt werden\",\n    \"ai_slug_failed\": \"KI-Slug konnte nicht generiert werden\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Manuelles Backup\",\n      \"description\": \"Löse eine manuelle Sicherung aller Links auf dem konfigurierten R2-Bucket aus\",\n      \"button\": \"Sicherung starten\",\n      \"backing_up\": \"Sichere...\",\n      \"success\": \"Sicherung erfolgreich\",\n      \"failed\": \"Sicherung fehlgeschlagen\"\n    },\n    \"export\": {\n      \"title\": \"Links exportieren\",\n      \"description\": \"Laden Sie alle Links als JSON-Datei herunter\",\n      \"button\": \"Alle exportieren\",\n      \"total_links\": \"Gesamtlinks\",\n      \"success\": \"Export erfolgreich\",\n      \"failed\": \"Export fehlgeschlagen\"\n    },\n    \"import\": {\n      \"title\": \"Links importieren\",\n      \"description\": \"Laden Sie eine JSON-Datei hoch, um Links zu importieren\",\n      \"dropzone\": \"JSON-Datei hierher ziehen oder klicken zum Hochladen\",\n      \"links_found\": \"Links gefunden\",\n      \"button\": \"Import starten\",\n      \"importing\": \"Importiere...\",\n      \"import_more\": \"Weitere importieren\",\n      \"download_success\": \"Erfolgreiche herunterladen\",\n      \"download_skipped\": \"Übersprungene herunterladen\",\n      \"download_failed\": \"Fehlgeschlagene herunterladen\",\n      \"result\": {\n        \"title\": \"Importergebnis\",\n        \"success\": \"Erfolgreich importiert\",\n        \"skipped\": \"Übersprungen (bereits vorhanden)\",\n        \"failed\": \"Fehlgeschlagen\",\n        \"success_message\": \"{count} Links erfolgreich importiert\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"Ungültige JSON-Datei\",\n        \"invalid_format\": \"Ungültiges Dateiformat. Bitte verwenden Sie eine von Sink exportierte Datei.\",\n        \"no_links\": \"Keine Links in der Datei gefunden\",\n        \"parse_error\": \"Datei konnte nicht analysiert werden\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/en-US.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Multi Language\",\n    \"abbreviations\": \"En\",\n    \"current_language\": \"English\",\n    \"try_again\": \"Try again\",\n    \"cancel\": \"Cancel\",\n    \"continue\": \"Continue\",\n    \"close\": \"Close\",\n    \"save\": \"Save\",\n    \"edit\": \"Edit\",\n    \"delete\": \"Delete\",\n    \"delete_success\": \"Delete successful!\",\n    \"search\": \"Search\"\n  },\n  \"theme\": {\n    \"toggle\": \"Toggle theme\",\n    \"light\": \"Light\",\n    \"dark\": \"Dark\",\n    \"system\": \"System\"\n  },\n  \"login\": {\n    \"title\": \"Login\",\n    \"description\": \"Enter your site token to login\",\n    \"tips\": \"Tips\",\n    \"preview_token\": \"The site token for preview mode is\",\n    \"submit\": \"Login\",\n    \"failed\": \"Login failed, please try again\",\n    \"token_label\": \"Site Token\",\n    \"token_required\": \"Token is required\"\n  },\n  \"logout\": {\n    \"title\": \"LogOut?\",\n    \"confirm\": \"Are you sure you want to log out?\",\n    \"action\": \"LogOut\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Products of HTML.ZONE\",\n      \"social\": {\n        \"email\": \"Email\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Select Language\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Links\",\n    \"analysis\": \"Analysis\",\n    \"realtime\": \"Realtime\",\n    \"migrate\": \"Import/Export\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Link Shortener\",\n    \"platform\": \"Platform\",\n    \"settings\": \"Settings\",\n    \"import\": \"Import\",\n    \"export\": \"Export\",\n    \"coffee\": \"Buy me a coffee\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stars\",\n    \"update\": \"New version {version} available (current: {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"GitHub Repo\"\n    },\n    \"features\": {\n      \"title\": \"Features\",\n      \"subtitle\": \"Simple and sufficient functionality\",\n      \"url_shortening\": {\n        \"title\": \"URL Shortening\",\n        \"description\": \"Compress your URLs to their minimal length\"\n      },\n      \"analytics\": {\n        \"title\": \"Analytics\",\n        \"description\": \"Monitor link analytics and gather insightful statistics\"\n      },\n      \"serverless\": {\n        \"title\": \"Serverless\",\n        \"description\": \"Deploy without the need for traditional servers\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Customizable Slug\",\n        \"description\": \"Support for personalized slugs and case sensitivity\"\n      },\n      \"ai_slug\": {\n        \"title\": \"AI Slug\",\n        \"description\": \"Leverage AI to generate slugs\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Link Expiration\",\n        \"description\": \"Set expiration dates for your links\"\n      },\n      \"device_routing\": {\n        \"title\": \"Device Routing\",\n        \"description\": \"Redirect iOS/Android users to App Store links\"\n      },\n      \"og_preview\": {\n        \"title\": \"Social Preview\",\n        \"description\": \"Custom OpenGraph previews for social media\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Real-time Analytics\",\n        \"description\": \"Live 3D globe and real-time event logs\"\n      },\n      \"qr_code\": {\n        \"title\": \"QR Code\",\n        \"description\": \"Generate QR codes for your short links\"\n      },\n      \"import_export\": {\n        \"title\": \"Import/Export\",\n        \"description\": \"Bulk migration via JSON files\"\n      },\n      \"multi_language\": {\n        \"title\": \"Multi-language\",\n        \"description\": \"Full i18n support for the dashboard\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Deployment immediately\",\n      \"description\": \"With just a few simple clicks, you can start deploying without any expenses\",\n      \"button\": \"Start Deploy\"\n    },\n    \"logos\": {\n      \"title\": \"Built with awesome technologies\"\n    },\n    \"twitter\": {\n      \"follow\": \"Follow on X(Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Sink in Numbers\",\n      \"subtitle\": \"Trusted by developers around the world\",\n      \"stars\": \"Stars on GitHub\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"Loved by Users\",\n      \"subtitle\": \"See what developers are saying about Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Dashboard\",\n    \"visits\": \"Visits\",\n    \"views\": \"Views\",\n    \"visitors\": \"Visitors\",\n    \"referers\": \"Referers\",\n    \"stats\": \"'s Stats\",\n    \"trend\": \"Trend\",\n    \"weekly_trend\": \"Weekly Trend\",\n    \"custom_date\": \"Custom Date\",\n    \"date\": \"Date\",\n    \"date_range\": \"Date Range\",\n    \"locations\": \"Locations\",\n    \"details\": \"Details\",\n    \"name\": \"Name\",\n    \"count\": \"Count\",\n    \"loading\": \"Loading\",\n    \"no_data\": \"No Data\",\n    \"none\": \"(None)\",\n    \"filter_placeholder\": \"Filter links...\",\n    \"date_picker\": {\n      \"today\": \"Today\",\n      \"last_24h\": \"Last 24 hours\",\n      \"this_week\": \"This week\",\n      \"last_7d\": \"Last 7 days\",\n      \"this_month\": \"This month\",\n      \"last_30d\": \"Last 30 days\",\n      \"last_90d\": \"Last 90 days\",\n      \"custom\": \"Custom\",\n      \"custom_title\": \"Custom Date\",\n      \"single_date\": \"Date\",\n      \"date_range\": \"Date Range\"\n    },\n    \"metrics\": {\n      \"country\": \"Country\",\n      \"region\": \"Region\",\n      \"city\": \"City\",\n      \"referer\": \"Referer\",\n      \"slug\": \"Slug\",\n      \"language\": \"Language\",\n      \"timezone\": \"Timezone\",\n      \"device\": \"Device\",\n      \"deviceType\": \"Device Type\",\n      \"os\": \"OS\",\n      \"browser\": \"Browser\",\n      \"browserType\": \"Browser Type\"\n    },\n    \"time_picker\": {\n      \"today\": \"Today\",\n      \"last_5m\": \"Last 5 minutes\",\n      \"last_10m\": \"Last 10 minutes\",\n      \"last_30m\": \"Last 30 minutes\",\n      \"last_1h\": \"Last 1 hour\",\n      \"last_6h\": \"Last 6 hours\",\n      \"last_12h\": \"Last 12 hours\",\n      \"last_24h\": \"Last 24 hours\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Create Link\",\n    \"edit\": \"Edit Link\",\n    \"delete_confirm_title\": \"Are you absolutely sure?\",\n    \"delete_confirm_desc\": \"This action cannot be undone. This will really delete your link from servers.\",\n    \"delete_success\": \"Delete successful!\",\n    \"delete_failed\": \"Delete failed!\",\n    \"update_success\": \"Link updated successfully\",\n    \"create_success\": \"Link created successfully\",\n    \"preview_mode_tip\": \"The preview mode link is valid for up to 5 minutes.\",\n    \"search_placeholder\": \"Search links...\",\n    \"no_results\": \"No links found.\",\n    \"group_title\": \"Links\",\n    \"copy_success\": \"Copy successful!\",\n    \"created_at\": \"Created At\",\n    \"updated_at\": \"Updated At\",\n    \"expires_at\": \"Expires At\",\n    \"no_more\": \"No more links\",\n    \"load_failed\": \"Loading links failed,\",\n    \"download_qr_code\": \"Download QR Code\",\n    \"change_qr_color\": \"Change QR code color\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Comment\",\n      \"expiration\": \"Expiration\",\n      \"pick_date\": \"Pick a date\",\n      \"device_redirect\": \"Device Redirect\",\n      \"google_play\": \"Google Play URL\",\n      \"app_store\": \"App Store URL\",\n      \"link_settings\": \"Link Settings\",\n      \"redirect_with_query_label\": \"Redirect with Query Parameters\",\n      \"redirect_with_query_description\": \"Append query parameters from the short link to the destination URL. Overrides global setting.\",\n      \"cloaking_label\": \"Enable Link Cloaking\",\n      \"cloaking_description\": \"Show your short link in the browser address bar instead of the destination URL. May not work for sites that block iframe embedding.\",\n      \"unsafe_label\": \"Mark as Unsafe\",\n      \"unsafe_description\": \"Show a warning page before redirecting. Visitors must confirm to continue.\",\n      \"password_label\": \"Password Protection\",\n      \"password_description\": \"Require a password to access this short link.\",\n      \"password_placeholder\": \"Leave empty to disable\",\n      \"og_settings\": \"OpenGraph Settings\",\n      \"og_title\": \"Title\",\n      \"og_title_placeholder\": \"Leave empty to use URL hostname\",\n      \"og_description\": \"Description\",\n      \"og_description_placeholder\": \"Link description\",\n      \"og_image\": \"Image\",\n      \"advanced\": \"Advanced\",\n      \"more_options\": \"More Options\",\n      \"image_invalid_type\": \"Please upload an image file\",\n      \"image_size_limit\": \"Image size cannot exceed 5MB\",\n      \"image_upload_success\": \"Image uploaded successfully\",\n      \"image_upload_failed\": \"Failed to upload image\",\n      \"image_upload_hint\": \"Click or drag to upload image\",\n      \"image_ratio_hint\": \"Recommended: 1200×630\",\n      \"slug_required\": \"Please enter a slug first\"\n    },\n    \"sort\": {\n      \"newest\": \"Newest First\",\n      \"oldest\": \"Oldest First\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Experience: Only for loaded links\"\n    },\n    \"update_failed\": \"Failed to update link\",\n    \"create_failed\": \"Failed to create link\",\n    \"ai_slug_failed\": \"Failed to generate AI slug\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Manual Backup\",\n      \"description\": \"Trigger a manual backup of all links to the configured R2 bucket\",\n      \"button\": \"Start Backup\",\n      \"backing_up\": \"Backing up...\",\n      \"success\": \"Backup successful\",\n      \"failed\": \"Backup failed\"\n    },\n    \"export\": {\n      \"title\": \"Export Links\",\n      \"description\": \"Download all your links as a JSON file\",\n      \"button\": \"Export All\",\n      \"total_links\": \"Total links\",\n      \"success\": \"Export successful\",\n      \"failed\": \"Export failed\"\n    },\n    \"import\": {\n      \"title\": \"Import Links\",\n      \"description\": \"Upload a JSON file to import links\",\n      \"dropzone\": \"Drop JSON file here or click to upload\",\n      \"links_found\": \"links found\",\n      \"button\": \"Start Import\",\n      \"importing\": \"Importing...\",\n      \"import_more\": \"Import More\",\n      \"download_success\": \"Download Success Items\",\n      \"download_skipped\": \"Download Skipped Items\",\n      \"download_failed\": \"Download Failed Items\",\n      \"result\": {\n        \"title\": \"Import Result\",\n        \"success\": \"Successfully imported\",\n        \"skipped\": \"Skipped (already exists)\",\n        \"failed\": \"Failed\",\n        \"success_message\": \"{count} links imported successfully\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"Invalid JSON file\",\n        \"invalid_format\": \"Invalid file format. Please use a file exported from Sink.\",\n        \"no_links\": \"No links found in file\",\n        \"parse_error\": \"Failed to parse file\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/fr-FR.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Multi Langue\",\n    \"abbreviations\": \"Fr\",\n    \"current_language\": \"Français\",\n    \"try_again\": \"Réessayer\",\n    \"cancel\": \"Annuler\",\n    \"continue\": \"Continuer\",\n    \"close\": \"Fermer\",\n    \"save\": \"Enregistrer\",\n    \"edit\": \"Modifier\",\n    \"delete\": \"Supprimer\",\n    \"delete_success\": \"Suppression réussie !\",\n    \"search\": \"Rechercher\"\n  },\n  \"theme\": {\n    \"toggle\": \"Changer de thème\",\n    \"light\": \"Clair\",\n    \"dark\": \"Sombre\",\n    \"system\": \"Système\"\n  },\n  \"login\": {\n    \"title\": \"Connexion\",\n    \"description\": \"Entrez votre jeton pour vous connecter\",\n    \"tips\": \"Astuces\",\n    \"preview_token\": \"Le jeton du site pour le mode aperçu est\",\n    \"submit\": \"Connexion\",\n    \"failed\": \"Échec de la connexion, veuillez réessayer\",\n    \"token_label\": \"Jeton du site\",\n    \"token_required\": \"Le jeton est requis\"\n  },\n  \"logout\": {\n    \"title\": \"Déconnexion ?\",\n    \"confirm\": \"Êtes-vous sûr de vouloir vous déconnecter ?\",\n    \"action\": \"Déconnexion\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Produits de HTML.ZONE\",\n      \"social\": {\n        \"email\": \"Email\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Choisir la langue\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Liens\",\n    \"analysis\": \"Statistiques\",\n    \"realtime\": \"Realtime\",\n    \"migrate\": \"Import/Export\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Raccourcisseur de liens\",\n    \"platform\": \"Plateforme\",\n    \"settings\": \"Paramètres\",\n    \"import\": \"Importer\",\n    \"export\": \"Exporter\",\n    \"coffee\": \"Offrez-moi un café\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stars\",\n    \"update\": \"Nouvelle version {version} disponible (actuelle : {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"Dépôt GitHub\"\n    },\n    \"features\": {\n      \"title\": \"Fonctionnalités\",\n      \"subtitle\": \"Fonctionnalité simple et adéquate\",\n      \"url_shortening\": {\n        \"title\": \"Raccourcissement d'URL\",\n        \"description\": \"Raccourcissez la taille de vos liens\"\n      },\n      \"analytics\": {\n        \"title\": \"Statistique\",\n        \"description\": \"Suivre l'analyse des liens et recueillir des statistiques pertinentes\"\n      },\n      \"serverless\": {\n        \"title\": \"Serverless\",\n        \"description\": \"Déployez sans avoir besoin de serveurs traditionnels\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Slug personnalisable\",\n        \"description\": \"Prise en charge des slugs personnalisés et de la sensibilité à la casse\"\n      },\n      \"ai_slug\": {\n        \"title\": \"Slug IA\",\n        \"description\": \"Utilisez l'IA pour créer des slugs\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Expiration des liens\",\n        \"description\": \"Définissez des dates d'expiration pour vos liens\"\n      },\n      \"device_routing\": {\n        \"title\": \"Routage par appareil\",\n        \"description\": \"Redirigez les utilisateurs iOS/Android vers les stores d'apps\"\n      },\n      \"og_preview\": {\n        \"title\": \"Aperçu social\",\n        \"description\": \"Aperçus OpenGraph personnalisés pour les réseaux sociaux\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Analyses en temps réel\",\n        \"description\": \"Globe 3D en direct et journaux d'événements\"\n      },\n      \"qr_code\": {\n        \"title\": \"Code QR\",\n        \"description\": \"Générez des codes QR pour vos liens courts\"\n      },\n      \"import_export\": {\n        \"title\": \"Import/Export\",\n        \"description\": \"Migration en masse via fichiers JSON\"\n      },\n      \"multi_language\": {\n        \"title\": \"Multilingue\",\n        \"description\": \"Support i18n complet pour le tableau de bord\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Déploiement immédiat\",\n      \"description\": \"En quelques clics, vous pouvez commencer à déployer sans aucun frais\",\n      \"button\": \"Lancer le déploiement\"\n    },\n    \"logos\": {\n      \"title\": \"Construit avec de superbes technologies\"\n    },\n    \"twitter\": {\n      \"follow\": \"Suivez-moi sur X(Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Sink en chiffres\",\n      \"subtitle\": \"Approuvé par les développeurs du monde entier\",\n      \"stars\": \"Stars sur GitHub\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"Aimé par les utilisateurs\",\n      \"subtitle\": \"Découvrez ce que disent les développeurs à propos de Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Tableau de bord\",\n    \"visits\": \"Visites\",\n    \"views\": \"Vues\",\n    \"visitors\": \"Visiteurs\",\n    \"referers\": \"Referrers\",\n    \"stats\": \"Statistiques\",\n    \"trend\": \"Tendance\",\n    \"weekly_trend\": \"Tendance hebdomadaire\",\n    \"custom_date\": \"Date personnalisée\",\n    \"date\": \"Date\",\n    \"date_range\": \"Intervalle de temps\",\n    \"locations\": \"Localisations\",\n    \"details\": \"Détails\",\n    \"name\": \"Nom\",\n    \"count\": \"Nombre\",\n    \"loading\": \"Chargement\",\n    \"no_data\": \"Aucune donnée\",\n    \"none\": \"(Aucun)\",\n    \"filter_placeholder\": \"Filtrer les liens...\",\n    \"date_picker\": {\n      \"today\": \"Aujourd'hui\",\n      \"last_24h\": \"Dernières 24 heures\",\n      \"this_week\": \"Cette semaine\",\n      \"last_7d\": \"7 derniers jours\",\n      \"this_month\": \"Ce mois-ci\",\n      \"last_30d\": \"30 derniers jours\",\n      \"last_90d\": \"90 derniers jours\",\n      \"custom\": \"Personnalisé\",\n      \"custom_title\": \"Date personnalisée\",\n      \"single_date\": \"Date\",\n      \"date_range\": \"Plage de dates\"\n    },\n    \"metrics\": {\n      \"country\": \"Pays\",\n      \"region\": \"Région\",\n      \"city\": \"Ville\",\n      \"referer\": \"Referrer\",\n      \"slug\": \"Slug\",\n      \"language\": \"Langue\",\n      \"timezone\": \"Fuseau horaire\",\n      \"device\": \"Appareil\",\n      \"deviceType\": \"Type d'appareil\",\n      \"os\": \"OS\",\n      \"browser\": \"Navigateur\",\n      \"browserType\": \"Type de navigateur\"\n    },\n    \"time_picker\": {\n      \"today\": \"Aujourd'hui\",\n      \"last_5m\": \"5 minutes\",\n      \"last_10m\": \"10 minutes\",\n      \"last_30m\": \"30 minutes\",\n      \"last_1h\": \"1 heure\",\n      \"last_6h\": \"6 heures\",\n      \"last_12h\": \"12 heures\",\n      \"last_24h\": \"24 heures\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Créer un lien\",\n    \"edit\": \"Modifier le lien\",\n    \"delete_confirm_title\": \"Êtes-vous absolument sûr ?\",\n    \"delete_confirm_desc\": \"Cette action est irréversible. Cela supprimera votre lien des serveurs.\",\n    \"delete_success\": \"Suppression réussie !\",\n    \"delete_failed\": \"Échec de la suppression !\",\n    \"update_success\": \"Lien mis à jour avec succès\",\n    \"create_success\": \"Lien créé avec succès\",\n    \"preview_mode_tip\": \"Le lien en mode aperçu est valide jusqu'à 5 minutes.\",\n    \"search_placeholder\": \"Rechercher des liens...\",\n    \"no_results\": \"Aucun lien trouvé.\",\n    \"group_title\": \"Liens\",\n    \"copy_success\": \"Copie réussie !\",\n    \"created_at\": \"Créé le\",\n    \"updated_at\": \"Mis à jour le\",\n    \"expires_at\": \"Expire le\",\n    \"no_more\": \"Plus de liens\",\n    \"load_failed\": \"Échec du chargement des liens,\",\n    \"download_qr_code\": \"Télécharger le code QR\",\n    \"change_qr_color\": \"Changer la couleur du code QR\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Commentaire\",\n      \"expiration\": \"Expiration\",\n      \"pick_date\": \"Choisir une date\",\n      \"device_redirect\": \"Redirection par appareil\",\n      \"google_play\": \"URL Google Play\",\n      \"app_store\": \"URL App Store\",\n      \"link_settings\": \"Paramètres du lien\",\n      \"redirect_with_query_label\": \"Rediriger avec les paramètres de requête\",\n      \"redirect_with_query_description\": \"Ajouter les paramètres de requête du lien court à l'URL de destination. Remplace le paramètre global.\",\n      \"cloaking_label\": \"Activer le masquage de lien\",\n      \"cloaking_description\": \"Affiche votre lien court dans la barre d'adresse du navigateur au lieu de l'URL de destination. Peut ne pas fonctionner pour les sites qui bloquent l'intégration iframe.\",\n      \"unsafe_label\": \"Marquer comme dangereux\",\n      \"unsafe_description\": \"Afficher une page d'avertissement avant la redirection. Les visiteurs doivent confirmer pour continuer.\",\n      \"password_label\": \"Protection par mot de passe\",\n      \"password_description\": \"Exiger un mot de passe pour accéder à ce lien court.\",\n      \"password_placeholder\": \"Laisser vide pour désactiver\",\n      \"og_settings\": \"Paramètres OpenGraph\",\n      \"og_title\": \"Titre\",\n      \"og_title_placeholder\": \"Laisser vide pour utiliser le nom de domaine\",\n      \"og_description\": \"Description\",\n      \"og_description_placeholder\": \"Description du lien\",\n      \"og_image\": \"Image\",\n      \"advanced\": \"Avancé\",\n      \"more_options\": \"Plus d'options\",\n      \"image_invalid_type\": \"Veuillez télécharger un fichier image\",\n      \"image_size_limit\": \"La taille de l'image ne peut pas dépasser 5 Mo\",\n      \"image_upload_success\": \"Image téléchargée avec succès\",\n      \"image_upload_failed\": \"Échec du téléchargement de l'image\",\n      \"image_upload_hint\": \"Cliquez ou glissez pour télécharger une image\",\n      \"image_ratio_hint\": \"Recommandé : 1200×630\",\n      \"slug_required\": \"Veuillez d'abord entrer un slug\"\n    },\n    \"sort\": {\n      \"newest\": \"Les plus récents\",\n      \"oldest\": \"Les plus anciens\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Essai : uniquement pour les liens chargés\"\n    },\n    \"update_failed\": \"Échec de la mise à jour du lien\",\n    \"create_failed\": \"Échec de la création du lien\",\n    \"ai_slug_failed\": \"Échec de la génération du slug IA\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Sauvegarde manuelle\",\n      \"description\": \"Déclenchez une sauvegarde manuelle de tous les liens vers le bucket R2 configuré\",\n      \"button\": \"Démarrer la sauvegarde\",\n      \"backing_up\": \"Sauvegarde en cours...\",\n      \"success\": \"Sauvegarde réussie\",\n      \"failed\": \"Échec de la sauvegarde\"\n    },\n    \"export\": {\n      \"title\": \"Exporter les liens\",\n      \"description\": \"Téléchargez tous vos liens au format JSON\",\n      \"button\": \"Tout exporter\",\n      \"total_links\": \"Total des liens\",\n      \"success\": \"Export réussi\",\n      \"failed\": \"Échec de l'export\"\n    },\n    \"import\": {\n      \"title\": \"Importer des liens\",\n      \"description\": \"Téléchargez un fichier JSON pour importer des liens\",\n      \"dropzone\": \"Déposez le fichier JSON ici ou cliquez pour télécharger\",\n      \"links_found\": \"liens trouvés\",\n      \"button\": \"Lancer l'import\",\n      \"importing\": \"Importation...\",\n      \"import_more\": \"Importer plus\",\n      \"download_success\": \"Télécharger les éléments réussis\",\n      \"download_skipped\": \"Télécharger les éléments ignorés\",\n      \"download_failed\": \"Télécharger les éléments échoués\",\n      \"result\": {\n        \"title\": \"Résultat de l'import\",\n        \"success\": \"Importé avec succès\",\n        \"skipped\": \"Ignoré (existe déjà)\",\n        \"failed\": \"Échoué\",\n        \"success_message\": \"{count} liens importés avec succès\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"Fichier JSON invalide\",\n        \"invalid_format\": \"Format de fichier invalide. Veuillez utiliser un fichier exporté depuis Sink.\",\n        \"no_links\": \"Aucun lien trouvé dans le fichier\",\n        \"parse_error\": \"Échec de l'analyse du fichier\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/it-IT.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Multilingua\",\n    \"abbreviations\": \"It\",\n    \"current_language\": \"Italiano\",\n    \"try_again\": \"Riprova\",\n    \"cancel\": \"Annulla\",\n    \"continue\": \"Continua\",\n    \"close\": \"Chiudi\",\n    \"save\": \"Salva\",\n    \"edit\": \"Modifica\",\n    \"delete\": \"Elimina\",\n    \"delete_success\": \"Eliminazione riuscita!\",\n    \"search\": \"Cerca\"\n  },\n  \"theme\": {\n    \"toggle\": \"Cambia tema\",\n    \"light\": \"Chiaro\",\n    \"dark\": \"Scuro\",\n    \"system\": \"Sistema\"\n  },\n  \"login\": {\n    \"title\": \"Accedi\",\n    \"description\": \"Inserisci il token del sito per accedere\",\n    \"tips\": \"Suggerimenti\",\n    \"preview_token\": \"Il token del sito per la modalità anteprima è\",\n    \"submit\": \"Accedi\",\n    \"failed\": \"Accesso fallito, riprova\",\n    \"token_label\": \"Token del Sito\",\n    \"token_required\": \"Il token è obbligatorio\"\n  },\n  \"logout\": {\n    \"title\": \"Disconnettersi?\",\n    \"confirm\": \"Sei sicuro di volerti disconnettere?\",\n    \"action\": \"Disconnetti\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Prodotto da HTML.ZONE\",\n      \"social\": {\n        \"email\": \"Email\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Seleziona Lingua\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Link\",\n    \"analysis\": \"Analisi\",\n    \"realtime\": \"In Tempo Reale\",\n    \"migrate\": \"Importa/Esporta\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Accorcia i tuoi Link\",\n    \"platform\": \"Piattaforma\",\n    \"settings\": \"Impostazioni\",\n    \"import\": \"Importa\",\n    \"export\": \"Esporta\",\n    \"coffee\": \"Offrimi un caffè\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stelle\",\n    \"update\": \"Nuova versione {version} disponibile (attuale: {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"Repository GitHub\"\n    },\n    \"features\": {\n      \"title\": \"Funzionalità\",\n      \"subtitle\": \"Funzionalità semplici e sufficienti\",\n      \"url_shortening\": {\n        \"title\": \"Accorciamento URL\",\n        \"description\": \"Comprimi i tuoi URL alla lunghezza minima\"\n      },\n      \"analytics\": {\n        \"title\": \"Analisi\",\n        \"description\": \"Monitora le analisi dei link e raccogli statistiche utili\"\n      },\n      \"serverless\": {\n        \"title\": \"Serverless\",\n        \"description\": \"Distribuisci senza bisogno di server tradizionali\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Slug Personalizzabile\",\n        \"description\": \"Supporto per slug personalizzati e distinzione maiuscole/minuscole\"\n      },\n      \"ai_slug\": {\n        \"title\": \"Slug IA\",\n        \"description\": \"Sfrutta l'IA per generare slug\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Scadenza Link\",\n        \"description\": \"Imposta date di scadenza per i tuoi link\"\n      },\n      \"device_routing\": {\n        \"title\": \"Reindirizzamento Dispositivo\",\n        \"description\": \"Reindirizza gli utenti iOS/Android ai link degli store\"\n      },\n      \"og_preview\": {\n        \"title\": \"Anteprima Social\",\n        \"description\": \"Anteprime OpenGraph personalizzate per i social media\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Analisi in Tempo Reale\",\n        \"description\": \"Globo 3D dal vivo e log eventi in tempo reale\"\n      },\n      \"qr_code\": {\n        \"title\": \"Codice QR\",\n        \"description\": \"Genera codici QR per i tuoi link brevi\"\n      },\n      \"import_export\": {\n        \"title\": \"Importa/Esporta\",\n        \"description\": \"Migrazione massiva tramite file JSON\"\n      },\n      \"multi_language\": {\n        \"title\": \"Multilingua\",\n        \"description\": \"Supporto completo i18n per la dashboard\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Distribuisci subito\",\n      \"description\": \"Con pochi semplici clic, puoi iniziare a distribuire senza alcun costo\",\n      \"button\": \"Inizia il Deploy\"\n    },\n    \"logos\": {\n      \"title\": \"Costruito con tecnologie eccezionali\"\n    },\n    \"twitter\": {\n      \"follow\": \"Segui su X(Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Sink in Numeri\",\n      \"subtitle\": \"Affidabile per sviluppatori in tutto il mondo\",\n      \"stars\": \"Stelle su GitHub\",\n      \"forks\": \"Fork\"\n    },\n    \"testimonials\": {\n      \"title\": \"Amato dagli Utenti\",\n      \"subtitle\": \"Scopri cosa dicono gli sviluppatori su Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Dashboard\",\n    \"visits\": \"Visite\",\n    \"views\": \"Visualizzazioni\",\n    \"visitors\": \"Visitatori\",\n    \"referers\": \"Referenti\",\n    \"stats\": \" - Statistiche\",\n    \"trend\": \"Tendenza\",\n    \"weekly_trend\": \"Tendenza Settimanale\",\n    \"custom_date\": \"Data Personalizzata\",\n    \"date\": \"Data\",\n    \"date_range\": \"Intervallo Date\",\n    \"locations\": \"Posizioni\",\n    \"details\": \"Dettagli\",\n    \"name\": \"Nome\",\n    \"count\": \"Conteggio\",\n    \"loading\": \"Caricamento\",\n    \"no_data\": \"Nessun Dato\",\n    \"none\": \"(Nessuno)\",\n    \"filter_placeholder\": \"Filtra link...\",\n    \"date_picker\": {\n      \"today\": \"Oggi\",\n      \"last_24h\": \"Ultime 24 ore\",\n      \"this_week\": \"Questa settimana\",\n      \"last_7d\": \"Ultimi 7 giorni\",\n      \"this_month\": \"Questo mese\",\n      \"last_30d\": \"Ultimi 30 giorni\",\n      \"last_90d\": \"Ultimi 90 giorni\",\n      \"custom\": \"Personalizzato\",\n      \"custom_title\": \"Data Personalizzata\",\n      \"single_date\": \"Data\",\n      \"date_range\": \"Intervallo Date\"\n    },\n    \"metrics\": {\n      \"country\": \"Paese\",\n      \"region\": \"Regione\",\n      \"city\": \"Città\",\n      \"referer\": \"Referente\",\n      \"slug\": \"Slug\",\n      \"language\": \"Lingua\",\n      \"timezone\": \"Fuso Orario\",\n      \"device\": \"Dispositivo\",\n      \"deviceType\": \"Tipo Dispositivo\",\n      \"os\": \"SO\",\n      \"browser\": \"Browser\",\n      \"browserType\": \"Tipo Browser\"\n    },\n    \"time_picker\": {\n      \"today\": \"Oggi\",\n      \"last_5m\": \"Ultimi 5 minuti\",\n      \"last_10m\": \"Ultimi 10 minuti\",\n      \"last_30m\": \"Ultimi 30 minuti\",\n      \"last_1h\": \"Ultima ora\",\n      \"last_6h\": \"Ultime 6 ore\",\n      \"last_12h\": \"Ultime 12 ore\",\n      \"last_24h\": \"Ultime 24 ore\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Crea Link\",\n    \"edit\": \"Modifica Link\",\n    \"delete_confirm_title\": \"Sei assolutamente sicuro?\",\n    \"delete_confirm_desc\": \"Questa azione non può essere annullata. Il link verrà eliminato definitivamente dai server.\",\n    \"delete_success\": \"Eliminazione riuscita!\",\n    \"delete_failed\": \"Eliminazione fallita!\",\n    \"update_success\": \"Link aggiornato con successo\",\n    \"create_success\": \"Link creato con successo\",\n    \"preview_mode_tip\": \"Il link in modalità anteprima è valido per un massimo di 5 minuti.\",\n    \"search_placeholder\": \"Cerca link...\",\n    \"no_results\": \"Nessun link trovato.\",\n    \"group_title\": \"Link\",\n    \"copy_success\": \"Copiato con successo!\",\n    \"created_at\": \"Creato il\",\n    \"updated_at\": \"Aggiornato il\",\n    \"expires_at\": \"Scade il\",\n    \"no_more\": \"Non ci sono altri link\",\n    \"load_failed\": \"Caricamento link fallito,\",\n    \"download_qr_code\": \"Scarica Codice QR\",\n    \"change_qr_color\": \"Cambia colore del codice QR\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Commento\",\n      \"expiration\": \"Scadenza\",\n      \"pick_date\": \"Scegli una data\",\n      \"device_redirect\": \"Reindirizzamento Dispositivo\",\n      \"google_play\": \"URL Google Play\",\n      \"app_store\": \"URL App Store\",\n      \"link_settings\": \"Impostazioni Link\",\n      \"redirect_with_query_label\": \"Reindirizza con Parametri Query\",\n      \"redirect_with_query_description\": \"Aggiungi i parametri query dal link breve all'URL di destinazione. Sovrascrive l'impostazione globale.\",\n      \"cloaking_label\": \"Abilita Mascheramento Link\",\n      \"cloaking_description\": \"Mostra il tuo link breve nella barra degli indirizzi del browser invece dell'URL di destinazione. Potrebbe non funzionare per siti che bloccano l'incorporamento iframe.\",\n      \"unsafe_label\": \"Segna come non sicuro\",\n      \"unsafe_description\": \"Mostra una pagina di avviso prima del reindirizzamento. I visitatori devono confermare per continuare.\",\n      \"password_label\": \"Protezione con Password\",\n      \"password_description\": \"Richiedi una password per accedere a questo link breve.\",\n      \"password_placeholder\": \"Lascia vuoto per disabilitare\",\n      \"og_settings\": \"Impostazioni OpenGraph\",\n      \"og_title\": \"Titolo\",\n      \"og_title_placeholder\": \"Lascia vuoto per usare l'hostname dell'URL\",\n      \"og_description\": \"Descrizione\",\n      \"og_description_placeholder\": \"Descrizione del link\",\n      \"og_image\": \"Immagine\",\n      \"advanced\": \"Avanzate\",\n      \"more_options\": \"Altre Opzioni\",\n      \"image_invalid_type\": \"Carica un file immagine valido\",\n      \"image_size_limit\": \"La dimensione dell'immagine non può superare 5MB\",\n      \"image_upload_success\": \"Immagine caricata con successo\",\n      \"image_upload_failed\": \"Caricamento immagine fallito\",\n      \"image_upload_hint\": \"Clicca o trascina per caricare l'immagine\",\n      \"image_ratio_hint\": \"Consigliato: 1200×630\",\n      \"slug_required\": \"Inserisci prima uno slug\"\n    },\n    \"sort\": {\n      \"newest\": \"Più Recenti\",\n      \"oldest\": \"Più Vecchi\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Esperienza: Solo per i link caricati\"\n    },\n    \"update_failed\": \"Aggiornamento link fallito\",\n    \"create_failed\": \"Creazione link fallita\",\n    \"ai_slug_failed\": \"Generazione slug IA fallita\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Backup manuale\",\n      \"description\": \"Avvia un backup manuale di tutti i link nel bucket R2 configurato\",\n      \"button\": \"Avvia backup\",\n      \"backing_up\": \"Backup in corso...\",\n      \"success\": \"Backup riuscito\",\n      \"failed\": \"Backup fallito\"\n    },\n    \"export\": {\n      \"title\": \"Esporta Link\",\n      \"description\": \"Scarica tutti i tuoi link come file JSON\",\n      \"button\": \"Esporta Tutto\",\n      \"total_links\": \"Link totali\",\n      \"success\": \"Esportazione riuscita\",\n      \"failed\": \"Esportazione fallita\"\n    },\n    \"import\": {\n      \"title\": \"Importa Link\",\n      \"description\": \"Carica un file JSON per importare i link\",\n      \"dropzone\": \"Trascina qui il file JSON o clicca per caricare\",\n      \"links_found\": \"link trovati\",\n      \"button\": \"Avvia Importazione\",\n      \"importing\": \"Importazione in corso...\",\n      \"import_more\": \"Importa Altro\",\n      \"download_success\": \"Scarica Elementi Riusciti\",\n      \"download_skipped\": \"Scarica Elementi Saltati\",\n      \"download_failed\": \"Scarica Elementi Falliti\",\n      \"result\": {\n        \"title\": \"Risultato Importazione\",\n        \"success\": \"Importati con successo\",\n        \"skipped\": \"Saltati (già esistenti)\",\n        \"failed\": \"Falliti\",\n        \"success_message\": \"{count} link importati con successo\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"File JSON non valido\",\n        \"invalid_format\": \"Formato file non valido. Usa un file esportato da Sink.\",\n        \"no_links\": \"Nessun link trovato nel file\",\n        \"parse_error\": \"Analisi del file fallita\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/pt-BR.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Multi idiomas\",\n    \"abbreviations\": \"Br\",\n    \"current_language\": \"Português (BR)\",\n    \"try_again\": \"Tente novamente\",\n    \"cancel\": \"Cancelar\",\n    \"continue\": \"Continuar\",\n    \"close\": \"Fechar\",\n    \"save\": \"Salvar\",\n    \"edit\": \"Editar\",\n    \"delete\": \"Excluir\",\n    \"delete_success\": \"Excluído com sucesso!\",\n    \"search\": \"Buscar\"\n  },\n  \"theme\": {\n    \"toggle\": \"Alternar tema\",\n    \"light\": \"Claro\",\n    \"dark\": \"Escuro\",\n    \"system\": \"Sistema\"\n  },\n  \"login\": {\n    \"title\": \"Login\",\n    \"description\": \"Insira seu token do site para entrar\",\n    \"tips\": \"Dicas\",\n    \"preview_token\": \"O token do site para modo de visualização é\",\n    \"submit\": \"Entrar\",\n    \"failed\": \"Falha no login, por favor tente novamente\",\n    \"token_label\": \"Token do site\",\n    \"token_required\": \"Token é obrigatório\"\n  },\n  \"logout\": {\n    \"title\": \"Sair?\",\n    \"confirm\": \"Tem certeza que deseja sair?\",\n    \"action\": \"Sair\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Produtos da HTML.ZONE\",\n      \"social\": {\n        \"email\": \"E-mail\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Selecionar idioma\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Links\",\n    \"analysis\": \"Análise\",\n    \"realtime\": \"Tempo Real\",\n    \"migrate\": \"Importar/Exportar\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Encurtador de links\",\n    \"platform\": \"Plataforma\",\n    \"settings\": \"Configurações\",\n    \"import\": \"Importar\",\n    \"export\": \"Exportar\",\n    \"coffee\": \"Pague-me um café\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Estrelas\",\n    \"update\": \"Nova versão {version} disponível (atual: {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"Repositório GitHub\"\n    },\n    \"features\": {\n      \"title\": \"Funcionalidades\",\n      \"subtitle\": \"Funcionalidade simples e suficiente\",\n      \"url_shortening\": {\n        \"title\": \"Encurtamento de URL\",\n        \"description\": \"Comprima suas URLs ao tamanho mínimo\"\n      },\n      \"analytics\": {\n        \"title\": \"Analytics\",\n        \"description\": \"Monitore a análise de links e obtenha estatísticas detalhadas\"\n      },\n      \"serverless\": {\n        \"title\": \"Serverless\",\n        \"description\": \"Implante sem a necessidade de servidores tradicionais\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Slug Personalizável\",\n        \"description\": \"Suporte para slugs personalizados e sensibilidade a maiúsculas/minúsculas\"\n      },\n      \"ai_slug\": {\n        \"title\": \"Slug via IA\",\n        \"description\": \"Use IA para gerar slugs automaticamente\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Expiração de Link\",\n        \"description\": \"Defina datas de expiração para seus links\"\n      },\n      \"device_routing\": {\n        \"title\": \"Roteamento por aparelho\",\n        \"description\": \"Redirecione usuários iOS/Android para links da App Store\"\n      },\n      \"og_preview\": {\n        \"title\": \"Prévia Social\",\n        \"description\": \"Prévias OpenGraph personalizadas para redes sociais\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Analytics em Tempo Real\",\n        \"description\": \"Globo 3D ao vivo e logs de eventos em tempo real\"\n      },\n      \"qr_code\": {\n        \"title\": \"QR Code\",\n        \"description\": \"Gere códigos QR para seus links curtos\"\n      },\n      \"import_export\": {\n        \"title\": \"Importar/Exportar\",\n        \"description\": \"Migração em massa via arquivos JSON\"\n      },\n      \"multi_language\": {\n        \"title\": \"Multi-idioma\",\n        \"description\": \"Suporte completo de i18n para o painel\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Implantação imediata\",\n      \"description\": \"Com apenas alguns cliques simples, você pode começar a implantar sem custos\",\n      \"button\": \"Iniciar implantação\"\n    },\n    \"logos\": {\n      \"title\": \"Construído com tecnologias incríveis\"\n    },\n    \"twitter\": {\n      \"follow\": \"Siga no X (Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Mergulhe nos números\",\n      \"subtitle\": \"Confiado por desenvolvedores ao redor do mundo\",\n      \"stars\": \"Estrelas no GitHub\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"Amado pelos usuários\",\n      \"subtitle\": \"Veja o que os desenvolvedores estão dizendo sobre o Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Dashboard\",\n    \"visits\": \"Visitas\",\n    \"views\": \"Visualizações\",\n    \"visitors\": \"Visitantes\",\n    \"referers\": \"Origens\",\n    \"stats\": \" - Estatísticas\",\n    \"trend\": \"Tendência\",\n    \"weekly_trend\": \"Tendência semanal\",\n    \"custom_date\": \"Data personalizada\",\n    \"date\": \"Data\",\n    \"date_range\": \"Intervalo de datas\",\n    \"locations\": \"Localizações\",\n    \"details\": \"Detalhes\",\n    \"name\": \"Nome\",\n    \"count\": \"Contagem\",\n    \"loading\": \"Carregando\",\n    \"no_data\": \"Sem dados\",\n    \"none\": \"(Nenhum)\",\n    \"filter_placeholder\": \"Filtrar links...\",\n    \"date_picker\": {\n      \"today\": \"Hoje\",\n      \"last_24h\": \"Últimas 24 horas\",\n      \"this_week\": \"Esta semana\",\n      \"last_7d\": \"Últimos 7 dias\",\n      \"this_month\": \"Este mês\",\n      \"last_30d\": \"Últimos 30 dias\",\n      \"last_90d\": \"Últimos 90 dias\",\n      \"custom\": \"Personalizado\",\n      \"custom_title\": \"Data personalizada\",\n      \"single_date\": \"Data\",\n      \"date_range\": \"Intervalo\"\n    },\n    \"metrics\": {\n      \"country\": \"País\",\n      \"region\": \"Região\",\n      \"city\": \"Cidade\",\n      \"referer\": \"Referência\",\n      \"slug\": \"Slug\",\n      \"language\": \"Idioma\",\n      \"timezone\": \"Fuso horário\",\n      \"device\": \"Aparelho\",\n      \"deviceType\": \"Tipo de aparelho\",\n      \"os\": \"SO\",\n      \"browser\": \"Navegador\",\n      \"browserType\": \"Tipo de navegador\"\n    },\n    \"time_picker\": {\n      \"today\": \"Hoje\",\n      \"last_5m\": \"Últimos 5 minutos\",\n      \"last_10m\": \"Últimos 10 minutos\",\n      \"last_30m\": \"Últimos 30 minutos\",\n      \"last_1h\": \"Última 1 hora\",\n      \"last_6h\": \"Últimas 6 horas\",\n      \"last_12h\": \"Últimas 12 horas\",\n      \"last_24h\": \"Últimas 24 horas\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Criar Link\",\n    \"edit\": \"Editar Link\",\n    \"delete_confirm_title\": \"Você tem certeza absoluta?\",\n    \"delete_confirm_desc\": \"Esta ação não pode ser desfeita. Isso excluirá permanentemente seu link dos servidores.\",\n    \"delete_success\": \"Excluído com sucesso!\",\n    \"delete_failed\": \"Falha ao excluir!\",\n    \"update_success\": \"Link atualizado com sucesso\",\n    \"create_success\": \"Link gerado com sucesso\",\n    \"preview_mode_tip\": \"O link do modo de visualização é válido por até 5 minutos.\",\n    \"search_placeholder\": \"Buscar links...\",\n    \"no_results\": \"Nenhum link encontrado.\",\n    \"group_title\": \"Links\",\n    \"copy_success\": \"Copiado com sucesso!\",\n    \"created_at\": \"Criado em\",\n    \"updated_at\": \"Atualizado em\",\n    \"expires_at\": \"Expira em\",\n    \"no_more\": \"Não há mais links\",\n    \"load_failed\": \"Falha ao carregar links,\",\n    \"download_qr_code\": \"Baixar QR Code\",\n    \"change_qr_color\": \"Alterar cor do QR code\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Comentário\",\n      \"expiration\": \"Expiração\",\n      \"pick_date\": \"Escolha uma data\",\n      \"device_redirect\": \"Redirecionamento de aparelho\",\n      \"google_play\": \"URL do Google Play\",\n      \"app_store\": \"URL da App Store\",\n      \"link_settings\": \"Configurações do link\",\n      \"redirect_with_query_label\": \"Redirecionar com parâmetros de consulta\",\n      \"redirect_with_query_description\": \"Anexar parâmetros de consulta do link curto à URL de destino. Substitui a configuração global.\",\n      \"cloaking_label\": \"Habilitar mascaramento de link\",\n      \"cloaking_description\": \"Mostrar seu link curto na barra de endereço do navegador em vez da URL de destino. Pode não funcionar em sites que bloqueiam incorporação de iframe.\",\n      \"unsafe_label\": \"Marcar como inseguro\",\n      \"unsafe_description\": \"Mostrar uma página de aviso antes do redirecionamento. Os visitantes devem confirmar para continuar.\",\n      \"password_label\": \"Proteção por senha\",\n      \"password_description\": \"Exigir uma senha para acessar este link curto.\",\n      \"password_placeholder\": \"Deixe vazio para desativar\",\n      \"og_settings\": \"Configurações OpenGraph\",\n      \"og_title\": \"Título\",\n      \"og_title_placeholder\": \"Deixe vazio para usar o nome do host da URL\",\n      \"og_description\": \"Descrição\",\n      \"og_description_placeholder\": \"Descrição do link\",\n      \"og_image\": \"Imagem\",\n      \"advanced\": \"Avançado\",\n      \"more_options\": \"Mais opções\",\n      \"image_invalid_type\": \"Por favor, envie um arquivo de imagem\",\n      \"image_size_limit\": \"O tamanho da imagem não pode exceder 5MB\",\n      \"image_upload_success\": \"Imagem enviada com sucesso\",\n      \"image_upload_failed\": \"Falha ao enviar imagem\",\n      \"image_upload_hint\": \"Clique ou arraste para enviar a imagem\",\n      \"image_ratio_hint\": \"Recomendado: 1200×630\",\n      \"slug_required\": \"Por favor, insira um slug primeiro\"\n    },\n    \"sort\": {\n      \"newest\": \"Mais recentes primeiro\",\n      \"oldest\": \"Mais antigos primeiro\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Experiência: Apenas para links carregados\"\n    },\n    \"update_failed\": \"Falha ao atualizar link\",\n    \"create_failed\": \"Falha ao criar link\",\n    \"ai_slug_failed\": \"Falha ao gerar slug via IA\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Backup manual\",\n      \"description\": \"Acione um backup manual de todos os links para o bucket R2 configurado\",\n      \"button\": \"Iniciar backup\",\n      \"backing_up\": \"Fazendo backup...\",\n      \"success\": \"Backup realizado com sucesso\",\n      \"failed\": \"Falha no backup\"\n    },\n    \"export\": {\n      \"title\": \"Exportar links\",\n      \"description\": \"Baixe todos os seus links como um arquivo JSON\",\n      \"button\": \"Exportar tudo\",\n      \"total_links\": \"Total de links\",\n      \"success\": \"Exportação bem-sucedida\",\n      \"failed\": \"Falha na exportação\"\n    },\n    \"import\": {\n      \"title\": \"Importar links\",\n      \"description\": \"Envie um arquivo JSON para importar links\",\n      \"dropzone\": \"Arraste o arquivo JSON aqui ou clique para enviar\",\n      \"links_found\": \"links encontrados\",\n      \"button\": \"Iniciar importação\",\n      \"importing\": \"Importando...\",\n      \"import_more\": \"Importar mais\",\n      \"download_success\": \"Baixar itens com sucesso\",\n      \"download_skipped\": \"Baixar itens ignorados\",\n      \"download_failed\": \"Baixar itens com falha\",\n      \"result\": {\n        \"title\": \"Resultado da importação\",\n        \"success\": \"Importado com sucesso\",\n        \"skipped\": \"Ignorado (já existe)\",\n        \"failed\": \"Falhou\",\n        \"success_message\": \"{count} links importados com sucesso\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"Arquivo JSON inválido\",\n        \"invalid_format\": \"Formato de arquivo inválido. Por favor, use um arquivo exportado do Sink.\",\n        \"no_links\": \"Nenhum link encontrado no arquivo\",\n        \"parse_error\": \"Falha ao analisar o arquivo\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/pt-PT.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Multi idioma\",\n    \"abbreviations\": \"Pt\",\n    \"current_language\": \"Português (PT)\",\n    \"try_again\": \"Tenta novamente\",\n    \"cancel\": \"Cancelar\",\n    \"continue\": \"Continuar\",\n    \"close\": \"Fechar\",\n    \"save\": \"Guardar\",\n    \"edit\": \"Editar\",\n    \"delete\": \"Eliminar\",\n    \"delete_success\": \"Eliminado com sucesso!\",\n    \"search\": \"Pesquisar\"\n  },\n  \"theme\": {\n    \"toggle\": \"Alternar tema\",\n    \"light\": \"Claro\",\n    \"dark\": \"Escuro\",\n    \"system\": \"Sistema\"\n  },\n  \"login\": {\n    \"title\": \"Iniciar sessão\",\n    \"description\": \"Insere o teu token do site para entrar\",\n    \"tips\": \"Dicas\",\n    \"preview_token\": \"O token do site para o modo de pré-visualização é\",\n    \"submit\": \"Entrar\",\n    \"failed\": \"O início de sessão falhou, por favor tenta novamente\",\n    \"token_label\": \"Token do site\",\n    \"token_required\": \"O token é obrigatório\"\n  },\n  \"logout\": {\n    \"title\": \"Terminar sessão?\",\n    \"confirm\": \"Tens a certeza que queres terminar sessão?\",\n    \"action\": \"Sair\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Produtos da HTML.ZONE\",\n      \"social\": {\n        \"email\": \"Email\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Escolher idioma\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Links\",\n    \"analysis\": \"Análise\",\n    \"realtime\": \"Tempo Real\",\n    \"migrate\": \"Importar/Exportar\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Encurtador de links\",\n    \"platform\": \"Plataforma\",\n    \"settings\": \"Definições\",\n    \"import\": \"Importar\",\n    \"export\": \"Exportar\",\n    \"coffee\": \"Paga-me um café\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Estrelas\",\n    \"update\": \"Nova versão {version} disponível (atual: {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"Repositório GitHub\"\n    },\n    \"features\": {\n      \"title\": \"Funcionalidades\",\n      \"subtitle\": \"Funcionalidade simples e suficiente\",\n      \"url_shortening\": {\n        \"title\": \"Encurtamento de URL\",\n        \"description\": \"Comprime os teus URLs para o tamanho mínimo\"\n      },\n      \"analytics\": {\n        \"title\": \"Analítica\",\n        \"description\": \"Monitoriza a análise de links e obtém estatísticas detalhadas\"\n      },\n      \"serverless\": {\n        \"title\": \"Serverless\",\n        \"description\": \"Implementa sem a necessidade de servidores tradicionais\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Slug Personalizável\",\n        \"description\": \"Suporte para slugs personalizados e sensibilidade a maiúsculas/minúsculas\"\n      },\n      \"ai_slug\": {\n        \"title\": \"Slug via IA\",\n        \"description\": \"Utiliza a IA para gerar slugs automaticamente\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Expiração de Link\",\n        \"description\": \"Define datas de expiração para os teus links\"\n      },\n      \"device_routing\": {\n        \"title\": \"Encaminhamento por Dispositivo\",\n        \"description\": \"Redireciona utilizadores iOS/Android para links da App Store\"\n      },\n      \"og_preview\": {\n        \"title\": \"Pré-visualização Social\",\n        \"description\": \"Pré-visualizações OpenGraph personalizadas para redes sociais\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Analítica em Tempo Real\",\n        \"description\": \"Globo 3D ao vivo e registos de eventos em tempo real\"\n      },\n      \"qr_code\": {\n        \"title\": \"Código QR\",\n        \"description\": \"Gere códigos QR para os teus links curtos\"\n      },\n      \"import_export\": {\n        \"title\": \"Importar/Exportar\",\n        \"description\": \"Migração em massa via ficheiros JSON\"\n      },\n      \"multi_language\": {\n        \"title\": \"Multi-idioma\",\n        \"description\": \"Suporte completo de i18n para o painel de controlo\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Implementação imediata\",\n      \"description\": \"Com apenas alguns cliques simples, podes começar a implementar sem custos\",\n      \"button\": \"Iniciar implementação\"\n    },\n    \"logos\": {\n      \"title\": \"Construído com tecnologias incríveis\"\n    },\n    \"twitter\": {\n      \"follow\": \"Segue no X (Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Mergulha nos números\",\n      \"subtitle\": \"Confiado por programadores no mundo inteiro\",\n      \"stars\": \"Estrelas no GitHub\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"Adorado pelos clientes\",\n      \"subtitle\": \"Vê o que os programadores dizem sobre o Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Dashboard\",\n    \"visits\": \"Visitas\",\n    \"views\": \"Visualizações\",\n    \"visitors\": \"Visitantes\",\n    \"referers\": \"Referências\",\n    \"stats\": \" - Estatísticas\",\n    \"trend\": \"Tendência\",\n    \"weekly_trend\": \"Tendência semanal\",\n    \"custom_date\": \"Data personalizada\",\n    \"date\": \"Data\",\n    \"date_range\": \"Intervalo de datas\",\n    \"locations\": \"Localizações\",\n    \"details\": \"Detalhes\",\n    \"name\": \"Nome\",\n    \"count\": \"Contagem\",\n    \"loading\": \"A carregar\",\n    \"no_data\": \"Sem dados\",\n    \"none\": \"(Nenhum)\",\n    \"filter_placeholder\": \"Filtrar links...\",\n    \"date_picker\": {\n      \"today\": \"Hoje\",\n      \"last_24h\": \"Últimas 24 horas\",\n      \"this_week\": \"Esta semana\",\n      \"last_7d\": \"Últimos 7 dias\",\n      \"this_month\": \"Este mês\",\n      \"last_30d\": \"Últimos 30 dias\",\n      \"last_90d\": \"Últimos 90 dias\",\n      \"custom\": \"Personalizado\",\n      \"custom_title\": \"Data personalizada\",\n      \"single_date\": \"Data\",\n      \"date_range\": \"Intervalo\"\n    },\n    \"metrics\": {\n      \"country\": \"País\",\n      \"region\": \"Região\",\n      \"city\": \"Cidade\",\n      \"referer\": \"Referência\",\n      \"slug\": \"Slug\",\n      \"language\": \"Idioma\",\n      \"timezone\": \"Fuso horário\",\n      \"device\": \"Dispositivo\",\n      \"deviceType\": \"Tipo de dispositivo\",\n      \"os\": \"SO\",\n      \"browser\": \"Browser\",\n      \"browserType\": \"Tipo de browser\"\n    },\n    \"time_picker\": {\n      \"today\": \"Hoje\",\n      \"last_5m\": \"Últimos 5 minutos\",\n      \"last_10m\": \"Últimos 10 minutos\",\n      \"last_30m\": \"Últimos 30 minutos\",\n      \"last_1h\": \"Última 1 hora\",\n      \"last_6h\": \"Últimas 6 horas\",\n      \"last_12h\": \"Últimas 12 horas\",\n      \"last_24h\": \"Últimas 24 horas\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Criar link\",\n    \"edit\": \"Editar link\",\n    \"delete_confirm_title\": \"Tens a certeza absoluta?\",\n    \"delete_confirm_desc\": \"Esta ação é irreversível. Isto irá eliminar permanentemente o teu link dos servidores.\",\n    \"delete_success\": \"Eliminado com sucesso!\",\n    \"delete_failed\": \"Falha ao eliminar!\",\n    \"update_success\": \"Link atualizado com sucesso\",\n    \"create_success\": \"Link criado com sucesso\",\n    \"preview_mode_tip\": \"O link do modo de pré-visualização é válido por até 5 minutos.\",\n    \"search_placeholder\": \"Pesquisar links...\",\n    \"no_results\": \"Nenhum link encontrado.\",\n    \"group_title\": \"Links\",\n    \"copy_success\": \"Copiado com sucesso!\",\n    \"created_at\": \"Criado em\",\n    \"updated_at\": \"Atualizado em\",\n    \"expires_at\": \"Expira em\",\n    \"no_more\": \"Não há mais links\",\n    \"load_failed\": \"Falha ao carregar links,\",\n    \"download_qr_code\": \"Descarregar código QR\",\n    \"change_qr_color\": \"Alterar cor do código QR\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Comentário\",\n      \"expiration\": \"Expiração\",\n      \"pick_date\": \"Escolhe uma data\",\n      \"device_redirect\": \"Redirecionamento de dispositivo\",\n      \"google_play\": \"URL do Google Play\",\n      \"app_store\": \"URL da App Store\",\n      \"link_settings\": \"Definições do link\",\n      \"redirect_with_query_label\": \"Redirecionar com parâmetros de consulta\",\n      \"redirect_with_query_description\": \"Anexar parâmetros de consulta do link curto ao URL de destino. Substitui a definição global.\",\n      \"cloaking_label\": \"Ativar ocultação de link\",\n      \"cloaking_description\": \"Mostrar o teu link curto na barra de endereço do navegador em vez do URL de destino. Isto pode não funcionar em sites que bloqueiam a incorporação de iframes.\",\n      \"unsafe_label\": \"Marcar como inseguro\",\n      \"unsafe_description\": \"Mostrar uma página de aviso antes do redirecionamento. Os visitantes devem confirmar para continuar.\",\n      \"password_label\": \"Proteção por palavra-passe\",\n      \"password_description\": \"Exigir uma palavra-passe para aceder a este link curto.\",\n      \"password_placeholder\": \"Deixa vazio para desativar\",\n      \"og_settings\": \"Definições OpenGraph\",\n      \"og_title\": \"Título\",\n      \"og_title_placeholder\": \"Deixa vazio para usar o nome do host do URL\",\n      \"og_description\": \"Descrição\",\n      \"og_description_placeholder\": \"Descrição do link\",\n      \"og_image\": \"Imagem\",\n      \"advanced\": \"Avançado\",\n      \"more_options\": \"Mais opções\",\n      \"image_invalid_type\": \"Por favor, carrega um ficheiro de imagem\",\n      \"image_size_limit\": \"O tamanho da imagem não pode exceder 5MB\",\n      \"image_upload_success\": \"Imagem carregada com sucesso\",\n      \"image_upload_failed\": \"Falha ao carregar imagem\",\n      \"image_upload_hint\": \"Clica ou arrasta para carregar a imagem\",\n      \"image_ratio_hint\": \"Recomendado: 1200×630\",\n      \"slug_required\": \"Por favor, insere um slug primeiro\"\n    },\n    \"sort\": {\n      \"newest\": \"Mais recentes primeiro\",\n      \"oldest\": \"Mais antigos primeiro\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Experiência: Apenas para links carregados\"\n    },\n    \"update_failed\": \"Falha ao atualizar link\",\n    \"create_failed\": \"Falha ao criar link\",\n    \"ai_slug_failed\": \"Falha ao gerar slug via IA\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Backup manual\",\n      \"description\": \"Aciona um backup manual de todos os links para o bucket R2 configurado\",\n      \"button\": \"Iniciar backup\",\n      \"backing_up\": \"A fazer backup...\",\n      \"success\": \"Backup concluído com sucesso\",\n      \"failed\": \"Falha no backup\"\n    },\n    \"export\": {\n      \"title\": \"Exportar links\",\n      \"description\": \"Descarrega todos os teus links como um ficheiro JSON\",\n      \"button\": \"Exportar tudo\",\n      \"total_links\": \"Total de links\",\n      \"success\": \"Exportação bem-sucedida\",\n      \"failed\": \"Falha na exportação\"\n    },\n    \"import\": {\n      \"title\": \"Importar links\",\n      \"description\": \"Carrega um ficheiro JSON para importar links\",\n      \"dropzone\": \"Arrasta o ficheiro JSON aqui ou clica para carregar\",\n      \"links_found\": \"links encontrados\",\n      \"button\": \"Iniciar importação\",\n      \"importing\": \"A importar...\",\n      \"import_more\": \"Importar mais\",\n      \"download_success\": \"Descarregar itens com sucesso\",\n      \"download_skipped\": \"Descarregar itens ignorados\",\n      \"download_failed\": \"Descarregar itens com falha\",\n      \"result\": {\n        \"title\": \"Resultado da importação\",\n        \"success\": \"Importado com sucesso\",\n        \"skipped\": \"Ignorado (já existe)\",\n        \"failed\": \"Falhou\",\n        \"success_message\": \"{count} links importados com sucesso\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"Ficheiro JSON inválido\",\n        \"invalid_format\": \"Formato de ficheiro inválido. Por favor, utiliza um ficheiro exportado do Sink.\",\n        \"no_links\": \"Nenhum link encontrado no ficheiro\",\n        \"parse_error\": \"Falha ao analisar o ficheiro\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/vi-VN.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"Đa ngôn ngữ\",\n    \"abbreviations\": \"Vi\",\n    \"current_language\": \"Tiếng Việt\",\n    \"try_again\": \"Thử lại\",\n    \"cancel\": \"Hủy\",\n    \"continue\": \"Tiếp tục\",\n    \"close\": \"Đóng\",\n    \"save\": \"Lưu\",\n    \"edit\": \"Chỉnh sửa\",\n    \"delete\": \"Xóa\",\n    \"delete_success\": \"Xóa thành công!\",\n    \"search\": \"Tìm kiếm\"\n  },\n  \"theme\": {\n    \"toggle\": \"Chuyển đổi giao diện\",\n    \"light\": \"Sáng\",\n    \"dark\": \"Tối\",\n    \"system\": \"Hệ thống\"\n  },\n  \"login\": {\n    \"title\": \"Đăng nhập\",\n    \"description\": \"Nhập token của trang để đăng nhập\",\n    \"tips\": \"Mẹo\",\n    \"preview_token\": \"Token của chế độ xem trước là\",\n    \"submit\": \"Đăng nhập\",\n    \"failed\": \"Đăng nhập thất bại, vui lòng thử lại\",\n    \"token_label\": \"Token trang\",\n    \"token_required\": \"Vui lòng nhập token\"\n  },\n  \"logout\": {\n    \"title\": \"Đăng xuất?\",\n    \"confirm\": \"Bạn có chắc chắn muốn đăng xuất không?\",\n    \"action\": \"Đăng xuất\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"Sản phẩm của HTML.ZONE\",\n      \"social\": {\n        \"email\": \"Email\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"Blog\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"Chọn ngôn ngữ\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"Liên kết\",\n    \"analysis\": \"Phân tích\",\n    \"realtime\": \"Thời gian thực\",\n    \"migrate\": \"Nhập/Xuất\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"Rút gọn liên kết\",\n    \"platform\": \"Nền tảng\",\n    \"settings\": \"Cài đặt\",\n    \"import\": \"Nhập\",\n    \"export\": \"Xuất\",\n    \"coffee\": \"Mua cho tôi một ly cà phê\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stars\",\n    \"update\": \"Phiên bản mới {version} có sẵn (hiện tại: {current})\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"Kho GitHub\"\n    },\n    \"features\": {\n      \"title\": \"Tính năng\",\n      \"subtitle\": \"Đơn giản và đủ dùng\",\n      \"url_shortening\": {\n        \"title\": \"Rút gọn URL\",\n        \"description\": \"Rút gọn liên kết đến độ dài tối thiểu\"\n      },\n      \"analytics\": {\n        \"title\": \"Phân tích\",\n        \"description\": \"Theo dõi dữ liệu và thu thập thống kê hữu ích\"\n      },\n      \"serverless\": {\n        \"title\": \"Không máy chủ\",\n        \"description\": \"Triển khai mà không cần máy chủ truyền thống\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"Slug tùy chỉnh\",\n        \"description\": \"Hỗ trợ slug cá nhân hóa và phân biệt chữ hoa/thường\"\n      },\n      \"ai_slug\": {\n        \"title\": \"Slug AI\",\n        \"description\": \"Tạo slug bằng trí tuệ nhân tạo\"\n      },\n      \"link_expiration\": {\n        \"title\": \"Hết hạn liên kết\",\n        \"description\": \"Đặt ngày hết hạn cho liên kết\"\n      },\n      \"device_routing\": {\n        \"title\": \"Điều hướng thiết bị\",\n        \"description\": \"Chuyển hướng người dùng iOS/Android đến App Store\"\n      },\n      \"og_preview\": {\n        \"title\": \"Xem trước mạng xã hội\",\n        \"description\": \"Tùy chỉnh OpenGraph cho mạng xã hội\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"Phân tích thời gian thực\",\n        \"description\": \"Địa cầu 3D trực tiếp và nhật ký sự kiện\"\n      },\n      \"qr_code\": {\n        \"title\": \"Mã QR\",\n        \"description\": \"Tạo mã QR cho liên kết rút gọn\"\n      },\n      \"import_export\": {\n        \"title\": \"Nhập/Xuất\",\n        \"description\": \"Di chuyển hàng loạt qua tệp JSON\"\n      },\n      \"multi_language\": {\n        \"title\": \"Đa ngôn ngữ\",\n        \"description\": \"Hỗ trợ i18n đầy đủ cho bảng điều khiển\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"Triển khai ngay\",\n      \"description\": \"Chỉ với vài cú nhấp chuột, bạn có thể triển khai miễn phí\",\n      \"button\": \"Bắt đầu triển khai\"\n    },\n    \"logos\": {\n      \"title\": \"Xây dựng bằng những công nghệ tuyệt vời\"\n    },\n    \"twitter\": {\n      \"follow\": \"Theo dõi tôi trên X (Twitter)\"\n    },\n    \"stats\": {\n      \"title\": \"Số liệu Sink\",\n      \"subtitle\": \"Được tin dùng bởi các nhà phát triển trên toàn thế giới\",\n      \"stars\": \"Stars trên GitHub\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"Người dùng yêu thích\",\n      \"subtitle\": \"Xem các nhà phát triển nói gì về Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"Bảng điều khiển\",\n    \"visits\": \"Lượt truy cập\",\n    \"views\": \"Lượt xem\",\n    \"visitors\": \"Khách truy cập\",\n    \"referers\": \"Nguồn giới thiệu\",\n    \"stats\": \"Thống kê của\",\n    \"trend\": \"Xu hướng\",\n    \"weekly_trend\": \"Xu hướng tuần\",\n    \"custom_date\": \"Ngày tùy chỉnh\",\n    \"date\": \"Ngày\",\n    \"date_range\": \"Khoảng ngày\",\n    \"locations\": \"Vị trí\",\n    \"details\": \"Chi tiết\",\n    \"name\": \"Tên\",\n    \"count\": \"Số lượng\",\n    \"loading\": \"Đang tải\",\n    \"no_data\": \"Không có dữ liệu\",\n    \"none\": \"(Không có)\",\n    \"filter_placeholder\": \"Lọc liên kết...\",\n    \"date_picker\": {\n      \"today\": \"Hôm nay\",\n      \"last_24h\": \"24 giờ qua\",\n      \"this_week\": \"Tuần này\",\n      \"last_7d\": \"7 ngày qua\",\n      \"this_month\": \"Tháng này\",\n      \"last_30d\": \"30 ngày qua\",\n      \"last_90d\": \"90 ngày qua\",\n      \"custom\": \"Tùy chỉnh\",\n      \"custom_title\": \"Chọn ngày\",\n      \"single_date\": \"Ngày\",\n      \"date_range\": \"Khoảng ngày\"\n    },\n    \"metrics\": {\n      \"country\": \"Quốc gia\",\n      \"region\": \"Vùng\",\n      \"city\": \"Thành phố\",\n      \"referer\": \"Nguồn giới thiệu\",\n      \"slug\": \"Slug\",\n      \"language\": \"Ngôn ngữ\",\n      \"timezone\": \"Múi giờ\",\n      \"device\": \"Thiết bị\",\n      \"deviceType\": \"Loại thiết bị\",\n      \"os\": \"Hệ điều hành\",\n      \"browser\": \"Trình duyệt\",\n      \"browserType\": \"Loại trình duyệt\"\n    },\n    \"time_picker\": {\n      \"today\": \"Hôm nay\",\n      \"last_5m\": \"5 phút qua\",\n      \"last_10m\": \"10 phút qua\",\n      \"last_30m\": \"30 phút qua\",\n      \"last_1h\": \"1 giờ qua\",\n      \"last_6h\": \"6 giờ qua\",\n      \"last_12h\": \"12 giờ qua\",\n      \"last_24h\": \"24 giờ qua\"\n    }\n  },\n  \"links\": {\n    \"create\": \"Tạo liên kết\",\n    \"edit\": \"Chỉnh sửa liên kết\",\n    \"delete_confirm_title\": \"Bạn có chắc chắn không?\",\n    \"delete_confirm_desc\": \"Hành động này không thể hoàn tác. Điều này sẽ xóa hoàn toàn liên kết khỏi máy chủ.\",\n    \"delete_success\": \"Xóa thành công!\",\n    \"delete_failed\": \"Xóa thất bại!\",\n    \"update_success\": \"Cập nhật liên kết thành công\",\n    \"create_success\": \"Tạo liên kết thành công\",\n    \"preview_mode_tip\": \"Liên kết ở chế độ xem trước có hiệu lực trong 5 phút.\",\n    \"search_placeholder\": \"Tìm liên kết...\",\n    \"no_results\": \"Không tìm thấy liên kết nào.\",\n    \"group_title\": \"Liên kết\",\n    \"copy_success\": \"Sao chép thành công!\",\n    \"created_at\": \"Ngày tạo\",\n    \"updated_at\": \"Ngày cập nhật\",\n    \"expires_at\": \"Ngày hết hạn\",\n    \"no_more\": \"Không còn liên kết nào\",\n    \"load_failed\": \"Tải liên kết thất bại,\",\n    \"download_qr_code\": \"Tải mã QR\",\n    \"change_qr_color\": \"Thay đổi màu mã QR\",\n    \"form\": {\n      \"url\": \"URL\",\n      \"slug\": \"Slug\",\n      \"comment\": \"Ghi chú\",\n      \"expiration\": \"Hết hạn\",\n      \"pick_date\": \"Chọn ngày\",\n      \"device_redirect\": \"Chuyển hướng theo thiết bị\",\n      \"google_play\": \"URL Google Play\",\n      \"app_store\": \"URL App Store\",\n      \"link_settings\": \"Cài đặt liên kết\",\n      \"redirect_with_query_label\": \"Chuyển hướng kèm tham số truy vấn\",\n      \"redirect_with_query_description\": \"Thêm tham số truy vấn từ liên kết rút gọn vào URL đích. Ghi đè cài đặt toàn cục.\",\n      \"cloaking_label\": \"Bật che giấu liên kết\",\n      \"cloaking_description\": \"Hiển thị liên kết rút gọn trên thanh địa chỉ trình duyệt thay vì URL đích. Có thể không hoạt động với các trang web chặn nhúng iframe.\",\n      \"unsafe_label\": \"Đánh dấu là không an toàn\",\n      \"unsafe_description\": \"Hiển thị trang cảnh báo trước khi chuyển hướng. Khách truy cập phải xác nhận để tiếp tục.\",\n      \"password_label\": \"Bảo vệ bằng mật khẩu\",\n      \"password_description\": \"Yêu cầu mật khẩu để truy cập liên kết rút gọn này.\",\n      \"password_placeholder\": \"Để trống để tắt\",\n      \"og_settings\": \"Cài đặt OpenGraph\",\n      \"og_title\": \"Tiêu đề\",\n      \"og_title_placeholder\": \"Để trống sẽ sử dụng tên miền URL\",\n      \"og_description\": \"Mô tả\",\n      \"og_description_placeholder\": \"Mô tả liên kết\",\n      \"og_image\": \"Hình ảnh\",\n      \"advanced\": \"Nâng cao\",\n      \"more_options\": \"Thêm tùy chọn\",\n      \"image_invalid_type\": \"Vui lòng tải lên tệp hình ảnh\",\n      \"image_size_limit\": \"Kích thước hình ảnh không được vượt quá 5MB\",\n      \"image_upload_success\": \"Tải lên hình ảnh thành công\",\n      \"image_upload_failed\": \"Tải lên hình ảnh thất bại\",\n      \"image_upload_hint\": \"Nhấp hoặc kéo thả để tải lên hình ảnh\",\n      \"image_ratio_hint\": \"Khuyến nghị: 1200×630\",\n      \"slug_required\": \"Vui lòng nhập slug trước\"\n    },\n    \"sort\": {\n      \"newest\": \"Mới nhất trước\",\n      \"oldest\": \"Cũ nhất trước\",\n      \"az\": \"Slug A-Z\",\n      \"za\": \"Slug Z-A\",\n      \"tip\": \"Trải nghiệm: Chỉ áp dụng với các liên kết đã tải\"\n    },\n    \"update_failed\": \"Cập nhật liên kết thất bại\",\n    \"create_failed\": \"Tạo liên kết thất bại\",\n    \"ai_slug_failed\": \"Tạo slug AI thất bại\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"Sao lưu thủ công\",\n      \"description\": \"Kích hoạt sao lưu thủ công tất cả liên kết sang bộ chứa R2 đã được cấu hình\",\n      \"button\": \"Bắt đầu sao lưu\",\n      \"backing_up\": \"Đang sao lưu...\",\n      \"success\": \"Sao lưu thành công\",\n      \"failed\": \"Sao lưu thất bại\"\n    },\n    \"export\": {\n      \"title\": \"Xuất liên kết\",\n      \"description\": \"Tải xuống tất cả liên kết dưới dạng tệp JSON\",\n      \"button\": \"Xuất tất cả\",\n      \"total_links\": \"Tổng liên kết\",\n      \"success\": \"Xuất thành công\",\n      \"failed\": \"Xuất thất bại\"\n    },\n    \"import\": {\n      \"title\": \"Nhập liên kết\",\n      \"description\": \"Tải lên tệp JSON để nhập liên kết\",\n      \"dropzone\": \"Kéo thả tệp JSON vào đây hoặc nhấp để tải lên\",\n      \"links_found\": \"liên kết được tìm thấy\",\n      \"button\": \"Bắt đầu nhập\",\n      \"importing\": \"Đang nhập...\",\n      \"import_more\": \"Nhập thêm\",\n      \"download_success\": \"Tải về các mục thành công\",\n      \"download_skipped\": \"Tải về các mục đã bỏ qua\",\n      \"download_failed\": \"Tải về các mục thất bại\",\n      \"result\": {\n        \"title\": \"Kết quả nhập\",\n        \"success\": \"Nhập thành công\",\n        \"skipped\": \"Đã bỏ qua (đã tồn tại)\",\n        \"failed\": \"Thất bại\",\n        \"success_message\": \"Đã nhập thành công {count} liên kết\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"Tệp JSON không hợp lệ\",\n        \"invalid_format\": \"Định dạng tệp không hợp lệ. Vui lòng sử dụng tệp được xuất từ Sink.\",\n        \"no_links\": \"Không tìm thấy liên kết trong tệp\",\n        \"parse_error\": \"Không thể phân tích tệp\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/zh-CN.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"多语言\",\n    \"abbreviations\": \"简\",\n    \"current_language\": \"简体中文\",\n    \"try_again\": \"重试\",\n    \"cancel\": \"取消\",\n    \"continue\": \"继续\",\n    \"close\": \"关闭\",\n    \"save\": \"保存\",\n    \"edit\": \"编辑\",\n    \"delete\": \"删除\",\n    \"delete_success\": \"删除成功！\",\n    \"search\": \"搜索\"\n  },\n  \"theme\": {\n    \"toggle\": \"切换主题\",\n    \"light\": \"浅色\",\n    \"dark\": \"深色\",\n    \"system\": \"跟随系统\"\n  },\n  \"login\": {\n    \"title\": \"登录\",\n    \"description\": \"请输入站点令牌以登录\",\n    \"tips\": \"提示\",\n    \"preview_token\": \"预览模式的站点令牌是\",\n    \"submit\": \"登录\",\n    \"failed\": \"登录失败，请重试\",\n    \"token_label\": \"站点令牌\",\n    \"token_required\": \"请输入令牌\"\n  },\n  \"logout\": {\n    \"title\": \"退出登录？\",\n    \"confirm\": \"确定要退出登录吗？\",\n    \"action\": \"退出登录\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"产品来自 HTML.ZONE\",\n      \"social\": {\n        \"email\": \"邮箱\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"博客\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"选择语言\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"链接\",\n    \"analysis\": \"分析\",\n    \"realtime\": \"实时\",\n    \"migrate\": \"导入/导出\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"短链接服务\",\n    \"platform\": \"平台\",\n    \"settings\": \"设置\",\n    \"import\": \"导入\",\n    \"export\": \"导出\",\n    \"coffee\": \"请我喝杯咖啡\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stars\",\n    \"update\": \"新版本 {version} 可用（当前: {current}）\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"GitHub 仓库\"\n    },\n    \"features\": {\n      \"title\": \"功能特点\",\n      \"subtitle\": \"简单而实用的功能\",\n      \"url_shortening\": {\n        \"title\": \"URL 缩短\",\n        \"description\": \"将您的 URL 压缩到最小长度\"\n      },\n      \"analytics\": {\n        \"title\": \"数据分析\",\n        \"description\": \"监控链接分析并收集有价值的统计数据\"\n      },\n      \"serverless\": {\n        \"title\": \"无服务器\",\n        \"description\": \"无需传统服务器即可部署\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"自定义短链\",\n        \"description\": \"支持个性化短链和大小写敏感\"\n      },\n      \"ai_slug\": {\n        \"title\": \"AI 短链\",\n        \"description\": \"利用 AI 生成短链\"\n      },\n      \"link_expiration\": {\n        \"title\": \"链接过期\",\n        \"description\": \"为您的链接设置过期日期\"\n      },\n      \"device_routing\": {\n        \"title\": \"设备路由\",\n        \"description\": \"iOS/Android 用户自动跳转应用商店\"\n      },\n      \"og_preview\": {\n        \"title\": \"社交预览\",\n        \"description\": \"自定义社交媒体 OpenGraph 预览\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"实时分析\",\n        \"description\": \"3D 地球可视化和实时事件日志\"\n      },\n      \"qr_code\": {\n        \"title\": \"二维码\",\n        \"description\": \"为短链生成二维码\"\n      },\n      \"import_export\": {\n        \"title\": \"导入/导出\",\n        \"description\": \"通过 JSON 文件批量迁移\"\n      },\n      \"multi_language\": {\n        \"title\": \"多语言\",\n        \"description\": \"仪表盘完整的国际化支持\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"立即部署\",\n      \"description\": \"只需几个简单的点击，您就可以免费开始部署\",\n      \"button\": \"开始部署\"\n    },\n    \"logos\": {\n      \"title\": \"使用优秀的技术构建\"\n    },\n    \"twitter\": {\n      \"follow\": \"在 X(Twitter) 上关注我\"\n    },\n    \"stats\": {\n      \"title\": \"Sink 数据\",\n      \"subtitle\": \"受到全球开发者的信赖\",\n      \"stars\": \"GitHub Stars\",\n      \"forks\": \"Forks\"\n    },\n    \"testimonials\": {\n      \"title\": \"用户好评\",\n      \"subtitle\": \"看看开发者们怎么说\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"仪表盘\",\n    \"visits\": \"访问量\",\n    \"views\": \"浏览量\",\n    \"visitors\": \"访客数\",\n    \"referers\": \"引荐来源\",\n    \"stats\": \"的统计\",\n    \"trend\": \"趋势\",\n    \"weekly_trend\": \"周度趋势\",\n    \"custom_date\": \"自定义日期\",\n    \"date\": \"日期\",\n    \"date_range\": \"日期范围\",\n    \"locations\": \"地理位置\",\n    \"details\": \"详情\",\n    \"name\": \"名称\",\n    \"count\": \"数量\",\n    \"loading\": \"加载中\",\n    \"no_data\": \"暂无数据\",\n    \"none\": \"（无）\",\n    \"filter_placeholder\": \"过滤链接...\",\n    \"date_picker\": {\n      \"today\": \"今天\",\n      \"last_24h\": \"最近24小时\",\n      \"this_week\": \"本周\",\n      \"last_7d\": \"最近7天\",\n      \"this_month\": \"本月\",\n      \"last_30d\": \"最近30天\",\n      \"last_90d\": \"最近90天\",\n      \"custom\": \"自定义\",\n      \"custom_title\": \"自定义日期\",\n      \"single_date\": \"单个日期\",\n      \"date_range\": \"日期范围\"\n    },\n    \"metrics\": {\n      \"country\": \"国家\",\n      \"region\": \"地区\",\n      \"city\": \"城市\",\n      \"referer\": \"来源\",\n      \"slug\": \"短链\",\n      \"language\": \"语言\",\n      \"timezone\": \"时区\",\n      \"device\": \"设备\",\n      \"deviceType\": \"设备类型\",\n      \"os\": \"操作系统\",\n      \"browser\": \"浏览器\",\n      \"browserType\": \"浏览器类型\"\n    },\n    \"time_picker\": {\n      \"today\": \"今天\",\n      \"last_5m\": \"最近5分钟\",\n      \"last_10m\": \"最近10分钟\",\n      \"last_30m\": \"最近30分钟\",\n      \"last_1h\": \"最近1小时\",\n      \"last_6h\": \"最近6小时\",\n      \"last_12h\": \"最近12小时\",\n      \"last_24h\": \"最近24小时\"\n    }\n  },\n  \"links\": {\n    \"create\": \"创建链接\",\n    \"edit\": \"编辑链接\",\n    \"delete_confirm_title\": \"确定要删除吗？\",\n    \"delete_confirm_desc\": \"此操作无法撤销。这将从服务器中永久删除您的链接。\",\n    \"delete_success\": \"删除成功！\",\n    \"delete_failed\": \"删除失败！\",\n    \"update_success\": \"链接更新成功\",\n    \"create_success\": \"链接创建成功\",\n    \"preview_mode_tip\": \"预览模式下的链接最多有效5分钟。\",\n    \"search_placeholder\": \"搜索链接...\",\n    \"no_results\": \"未找到链接。\",\n    \"group_title\": \"链接列表\",\n    \"copy_success\": \"复制成功！\",\n    \"created_at\": \"创建时间\",\n    \"updated_at\": \"更新时间\",\n    \"expires_at\": \"过期时间\",\n    \"no_more\": \"没有更多链接\",\n    \"load_failed\": \"加载链接失败,\",\n    \"download_qr_code\": \"下载二维码\",\n    \"change_qr_color\": \"更改二维码颜色\",\n    \"form\": {\n      \"url\": \"链接地址\",\n      \"slug\": \"短链\",\n      \"comment\": \"备注\",\n      \"expiration\": \"过期时间\",\n      \"pick_date\": \"选择日期\",\n      \"device_redirect\": \"设备跳转\",\n      \"google_play\": \"Google Play 链接\",\n      \"app_store\": \"App Store 链接\",\n      \"link_settings\": \"链接设置\",\n      \"redirect_with_query_label\": \"携带查询参数跳转\",\n      \"redirect_with_query_description\": \"将短链接的查询参数追加到目标 URL。优先级高于全局设置。\",\n      \"cloaking_label\": \"启用链接伪装\",\n      \"cloaking_description\": \"在浏览器地址栏中显示短链接而非目标 URL。部分禁止 iframe 嵌入的网站可能无法生效。\",\n      \"unsafe_label\": \"标记为不安全\",\n      \"unsafe_description\": \"跳转前显示警告页面，访客需确认后才能继续。\",\n      \"password_label\": \"密码保护\",\n      \"password_description\": \"访问此短链接时需要输入密码。\",\n      \"password_placeholder\": \"留空则不启用\",\n      \"og_settings\": \"OpenGraph 设置\",\n      \"og_title\": \"标题\",\n      \"og_title_placeholder\": \"留空则使用 URL 域名\",\n      \"og_description\": \"描述\",\n      \"og_description_placeholder\": \"链接描述\",\n      \"og_image\": \"图片\",\n      \"advanced\": \"高级设置\",\n      \"more_options\": \"更多选项\",\n      \"image_invalid_type\": \"请上传图片文件\",\n      \"image_size_limit\": \"图片大小不能超过 5MB\",\n      \"image_upload_success\": \"图片上传成功\",\n      \"image_upload_failed\": \"图片上传失败\",\n      \"image_upload_hint\": \"点击或拖拽上传图片\",\n      \"image_ratio_hint\": \"推荐尺寸：1200×630\",\n      \"slug_required\": \"请先输入短链\"\n    },\n    \"sort\": {\n      \"newest\": \"最新\",\n      \"oldest\": \"最旧\",\n      \"az\": \"短链(A-Z)\",\n      \"za\": \"短链(Z-A)\",\n      \"tip\": \"体验功能: 仅对已经加载的链接有效\"\n    },\n    \"update_failed\": \"更新链接失败\",\n    \"create_failed\": \"创建链接失败\",\n    \"ai_slug_failed\": \"AI 生成短链失败\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"手动备份\",\n      \"description\": \"手动触发所有链接到已配置 R2 存储桶的备份\",\n      \"button\": \"开始备份\",\n      \"backing_up\": \"备份中...\",\n      \"success\": \"备份成功\",\n      \"failed\": \"备份失败\"\n    },\n    \"export\": {\n      \"title\": \"导出链接\",\n      \"description\": \"将所有链接下载为 JSON 文件\",\n      \"button\": \"导出全部\",\n      \"total_links\": \"链接总数\",\n      \"success\": \"导出成功\",\n      \"failed\": \"导出失败\"\n    },\n    \"import\": {\n      \"title\": \"导入链接\",\n      \"description\": \"上传 JSON 文件以导入链接\",\n      \"dropzone\": \"拖拽 JSON 文件到此处或点击上传\",\n      \"links_found\": \"条链接\",\n      \"button\": \"开始导入\",\n      \"importing\": \"导入中...\",\n      \"import_more\": \"继续导入\",\n      \"download_success\": \"下载成功项\",\n      \"download_skipped\": \"下载跳过项\",\n      \"download_failed\": \"下载失败项\",\n      \"result\": {\n        \"title\": \"导入结果\",\n        \"success\": \"成功导入\",\n        \"skipped\": \"已跳过（已存在）\",\n        \"failed\": \"失败\",\n        \"success_message\": \"成功导入 {count} 条链接\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"无效的 JSON 文件\",\n        \"invalid_format\": \"文件格式无效，请使用从 Sink 导出的文件\",\n        \"no_links\": \"文件中未找到链接\",\n        \"parse_error\": \"文件解析失败\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "i18n/locales/zh-TW.json",
    "content": "{\n  \"common\": {\n    \"i18n\": \"多語言\",\n    \"abbreviations\": \"繁\",\n    \"current_language\": \"繁體中文\",\n    \"try_again\": \"重試\",\n    \"cancel\": \"取消\",\n    \"continue\": \"繼續\",\n    \"close\": \"關閉\",\n    \"save\": \"保存\",\n    \"edit\": \"編輯\",\n    \"delete\": \"刪除\",\n    \"delete_success\": \"刪除成功！\",\n    \"search\": \"搜尋\"\n  },\n  \"theme\": {\n    \"toggle\": \"切換主题\",\n    \"light\": \"淺色\",\n    \"dark\": \"深色\",\n    \"system\": \"跟隨系統\"\n  },\n  \"login\": {\n    \"title\": \"登入\",\n    \"description\": \"請輸入站點密碼以登入\",\n    \"tips\": \"提示\",\n    \"preview_token\": \"預覽模式的站點密碼是\",\n    \"submit\": \"登入\",\n    \"failed\": \"登入失敗，請重試\",\n    \"token_label\": \"站點密碼\",\n    \"token_required\": \"請輸入密碼\"\n  },\n  \"logout\": {\n    \"title\": \"登出？\",\n    \"confirm\": \"確定要登出嗎？\",\n    \"action\": \"登出\"\n  },\n  \"layouts\": {\n    \"footer\": {\n      \"copyright\": \"產品來自 HTML.ZONE\",\n      \"social\": {\n        \"email\": \"信箱\",\n        \"telegram\": \"Telegram\",\n        \"blog\": \"部落格\",\n        \"twitter\": \"Twitter\",\n        \"mastodon\": \"Mastodon\",\n        \"github\": \"GitHub\"\n      }\n    },\n    \"header\": {\n      \"select_language\": \"選擇語言\"\n    }\n  },\n  \"nav\": {\n    \"links\": \"連結\",\n    \"analysis\": \"分析\",\n    \"realtime\": \"即時\",\n    \"migrate\": \"匯入/匯出\"\n  },\n  \"sidebar\": {\n    \"subtitle\": \"縮網址服務\",\n    \"platform\": \"平台\",\n    \"settings\": \"設定\",\n    \"import\": \"匯入\",\n    \"export\": \"匯出\",\n    \"coffee\": \"請我喝杯咖啡\",\n    \"github\": \"GitHub\",\n    \"stars\": \"Stars\",\n    \"update\": \"新版本 {version} 可用（當前: {current}）\"\n  },\n  \"home\": {\n    \"hero\": {\n      \"github_repo\": \"GitHub 倉庫\"\n    },\n    \"features\": {\n      \"title\": \"功能特色\",\n      \"subtitle\": \"簡單而實用的功能\",\n      \"url_shortening\": {\n        \"title\": \"URL 縮短\",\n        \"description\": \"將您的 URL 壓縮到最小長度\"\n      },\n      \"analytics\": {\n        \"title\": \"數據分析\",\n        \"description\": \"監控連結分析病收集有價值的統計數據\"\n      },\n      \"serverless\": {\n        \"title\": \"無伺服器\",\n        \"description\": \"無需傳統伺服器即可部署\"\n      },\n      \"customizable_slug\": {\n        \"title\": \"自定義短連結\",\n        \"description\": \"支持自訂連結和大小寫敏感\"\n      },\n      \"ai_slug\": {\n        \"title\": \"AI 短連結\",\n        \"description\": \"利用 AI 生成短連結\"\n      },\n      \"link_expiration\": {\n        \"title\": \"短連結過期\",\n        \"description\": \"為您的短連結設定過期日期\"\n      },\n      \"device_routing\": {\n        \"title\": \"裝置路由\",\n        \"description\": \"iOS/Android 用戶自動跳轉應用商店\"\n      },\n      \"og_preview\": {\n        \"title\": \"社交預覽\",\n        \"description\": \"自訂社交媒體 OpenGraph 預覽\"\n      },\n      \"realtime_analytics\": {\n        \"title\": \"即時分析\",\n        \"description\": \"3D 地球視覺化和即時事件日誌\"\n      },\n      \"qr_code\": {\n        \"title\": \"QR Code\",\n        \"description\": \"為短連結生成 QR Code\"\n      },\n      \"import_export\": {\n        \"title\": \"匯入/匯出\",\n        \"description\": \"透過 JSON 檔案批次遷移\"\n      },\n      \"multi_language\": {\n        \"title\": \"多語言\",\n        \"description\": \"儀表板完整的國際化支援\"\n      }\n    },\n    \"cta\": {\n      \"title\": \"立即部署\",\n      \"description\": \"只需幾個簡單的點擊，您就可以免費開始部署\",\n      \"button\": \"開始部署\"\n    },\n    \"logos\": {\n      \"title\": \"使用優秀的技術構建\"\n    },\n    \"twitter\": {\n      \"follow\": \"在 X(Twitter) 上追蹤我\"\n    },\n    \"stats\": {\n      \"title\": \"有關 Sink 的數據\",\n      \"subtitle\": \"受到全球開發者信賴\",\n      \"stars\": \"個 GitHub 星星\",\n      \"forks\": \"次分叉\"\n    },\n    \"testimonials\": {\n      \"title\": \"深受用戶喜愛\",\n      \"subtitle\": \"看看開發者們都怎麼描述 Sink\"\n    }\n  },\n  \"dashboard\": {\n    \"title\": \"儀表板\",\n    \"visits\": \"訪問次數\",\n    \"views\": \"瀏覽次數\",\n    \"visitors\": \"訪客人數\",\n    \"referers\": \"引薦來源\",\n    \"stats\": \"的統計\",\n    \"trend\": \"趨勢\",\n    \"weekly_trend\": \"週度趨勢\",\n    \"custom_date\": \"自定義日期\",\n    \"date\": \"日期\",\n    \"date_range\": \"日期範圍\",\n    \"locations\": \"地理位置\",\n    \"details\": \"詳細資料\",\n    \"name\": \"名稱\",\n    \"count\": \"數量\",\n    \"loading\": \"載入中\",\n    \"no_data\": \"暫無數據\",\n    \"none\": \"（無）\",\n    \"filter_placeholder\": \"篩選連結...\",\n    \"date_picker\": {\n      \"today\": \"今天\",\n      \"last_24h\": \"最近24小時\",\n      \"this_week\": \"本週\",\n      \"last_7d\": \"最近7天\",\n      \"this_month\": \"本月\",\n      \"last_30d\": \"最近30天\",\n      \"last_90d\": \"最近90天\",\n      \"custom\": \"自定義\",\n      \"custom_title\": \"自定義日期\",\n      \"single_date\": \"單個日期\",\n      \"date_range\": \"日期範圍\"\n    },\n    \"metrics\": {\n      \"country\": \"國家\",\n      \"region\": \"地區\",\n      \"city\": \"城市\",\n      \"referer\": \"來源\",\n      \"slug\": \"短連結\",\n      \"language\": \"語言\",\n      \"timezone\": \"時區\",\n      \"device\": \"裝置\",\n      \"deviceType\": \"裝置類型\",\n      \"os\": \"作業系統\",\n      \"browser\": \"瀏覽器\",\n      \"browserType\": \"瀏覽器類型\"\n    },\n    \"time_picker\": {\n      \"today\": \"今天\",\n      \"last_5m\": \"最近5分鐘\",\n      \"last_10m\": \"最近10分鐘\",\n      \"last_30m\": \"最近30分鐘\",\n      \"last_1h\": \"最近1小時\",\n      \"last_6h\": \"最近6小時\",\n      \"last_12h\": \"最近12小時\",\n      \"last_24h\": \"最近24小時\"\n    }\n  },\n  \"links\": {\n    \"create\": \"創建連結\",\n    \"edit\": \"編輯連結\",\n    \"delete_confirm_title\": \"確定要刪除嗎？\",\n    \"delete_confirm_desc\": \"此操作無法撤銷。這將從伺服器中永久刪除您的連結。\",\n    \"delete_success\": \"刪除成功！\",\n    \"delete_failed\": \"刪除失敗！\",\n    \"update_success\": \"連結更新成功\",\n    \"create_success\": \"連結創建成功\",\n    \"preview_mode_tip\": \"預覽模式下的連結最多有效5分鐘\",\n    \"search_placeholder\": \"搜尋連結...\",\n    \"no_results\": \"找不到連結。\",\n    \"group_title\": \"連結列表\",\n    \"copy_success\": \"複製成功！\",\n    \"created_at\": \"創建時間\",\n    \"updated_at\": \"更新時間\",\n    \"expires_at\": \"過期時間\",\n    \"no_more\": \"沒有更多連結\",\n    \"load_failed\": \"載入連結失敗,\",\n    \"download_qr_code\": \"下載 QR code\",\n    \"change_qr_color\": \"更改 QR code 顏色\",\n    \"form\": {\n      \"url\": \"連結地址\",\n      \"slug\": \"短連結\",\n      \"comment\": \"備註\",\n      \"expiration\": \"過期時間\",\n      \"pick_date\": \"選擇日期\",\n      \"device_redirect\": \"設備跳轉\",\n      \"google_play\": \"Google Play 連結\",\n      \"app_store\": \"App Store 連結\",\n      \"link_settings\": \"連結設置\",\n      \"redirect_with_query_label\": \"攜帶查詢參數跳轉\",\n      \"redirect_with_query_description\": \"將短連結的查詢參數附加到目標 URL。優先級高於全域設定。\",\n      \"cloaking_label\": \"啟用連結偽裝\",\n      \"cloaking_description\": \"在瀏覽器網址列中顯示短連結而非目標 URL。部分禁止 iframe 嵌入的網站可能無法生效。\",\n      \"unsafe_label\": \"標記為不安全\",\n      \"unsafe_description\": \"跳轉前顯示警告頁面，訪客需確認後才能繼續。\",\n      \"password_label\": \"密碼保護\",\n      \"password_description\": \"存取此短連結時需要輸入密碼。\",\n      \"password_placeholder\": \"留空則不啟用\",\n      \"og_settings\": \"OpenGraph 設置\",\n      \"og_title\": \"標題\",\n      \"og_title_placeholder\": \"留空則使用 URL 域名\",\n      \"og_description\": \"描述\",\n      \"og_description_placeholder\": \"連結描述\",\n      \"og_image\": \"圖片\",\n      \"advanced\": \"進階設置\",\n      \"more_options\": \"更多選項\",\n      \"image_invalid_type\": \"請上傳圖片檔案\",\n      \"image_size_limit\": \"圖片大小不能超過 5MB\",\n      \"image_upload_success\": \"圖片上傳成功\",\n      \"image_upload_failed\": \"圖片上傳失敗\",\n      \"image_upload_hint\": \"點擊或拖曳上傳圖片\",\n      \"image_ratio_hint\": \"建議尺寸：1200×630\",\n      \"slug_required\": \"請先輸入短連結\"\n    },\n    \"sort\": {\n      \"newest\": \"最新\",\n      \"oldest\": \"最舊\",\n      \"az\": \"短連結(A-Z)\",\n      \"za\": \"短連結(Z-A)\",\n      \"tip\": \"體驗功能: 僅對已經加載的連結有效\"\n    },\n    \"update_failed\": \"更新連結失敗\",\n    \"create_failed\": \"創建連結失敗\",\n    \"ai_slug_failed\": \"AI 生成短連結失敗\"\n  },\n  \"migrate\": {\n    \"backup\": {\n      \"title\": \"手動備份\",\n      \"description\": \"手動觸發所有連結到已配置 R2 儲存桶的備份\",\n      \"button\": \"開始備份\",\n      \"backing_up\": \"備份中...\",\n      \"success\": \"備份成功\",\n      \"failed\": \"備份失敗\"\n    },\n    \"export\": {\n      \"title\": \"匯出連結\",\n      \"description\": \"將所有連結下載為 JSON 檔案\",\n      \"button\": \"匯出全部\",\n      \"total_links\": \"連結總數\",\n      \"success\": \"匯出成功\",\n      \"failed\": \"匯出失敗\"\n    },\n    \"import\": {\n      \"title\": \"匯入連結\",\n      \"description\": \"上傳 JSON 檔案以匯入連結\",\n      \"dropzone\": \"拖曳 JSON 檔案至此或點擊上傳\",\n      \"links_found\": \"個連結\",\n      \"button\": \"開始匯入\",\n      \"importing\": \"匯入中...\",\n      \"import_more\": \"繼續匯入\",\n      \"download_success\": \"下載成功項\",\n      \"download_skipped\": \"下載跳過項\",\n      \"download_failed\": \"下載失敗項\",\n      \"result\": {\n        \"title\": \"匯入結果\",\n        \"success\": \"成功匯入\",\n        \"skipped\": \"已跳過（已存在）\",\n        \"failed\": \"失敗\",\n        \"success_message\": \"成功匯入 {count} 個連結\"\n      },\n      \"errors\": {\n        \"invalid_json\": \"無效的 JSON 檔案\",\n        \"invalid_format\": \"檔案格式無效，請使用從 Sink 匯出的檔案\",\n        \"no_links\": \"檔案中找不到連結\",\n        \"parse_error\": \"檔案解析失敗\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/DatePicker.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DateRange, DateValue } from 'reka-ui'\nimport { getLocalTimeZone } from '@internationalized/date'\n\nconst analysisStore = useDashboardAnalysisStore()\n\nconst openCustomDateRange = ref(false)\nconst customDate = ref<DateValue | undefined>()\nconst customDateRange = ref<DateRange | undefined>()\n\nconst tz = getLocalTimeZone()\n\nfunction updateCustomDate(customDateValue: DateValue) {\n  analysisStore.datePreset = null\n  analysisStore.updateDateRange([date2unix(customDateValue, 'start'), date2unix(customDateValue, 'end')])\n  openCustomDateRange.value = false\n  customDate.value = undefined\n}\n\nfunction updateCustomDateRange(customDateRangeValue: DateRange) {\n  if (customDateRangeValue.start && customDateRangeValue.end) {\n    analysisStore.datePreset = null\n    analysisStore.updateDateRange([date2unix(customDateRangeValue.start, 'start'), date2unix(customDateRangeValue.end, 'end')])\n    openCustomDateRange.value = false\n    customDateRange.value = undefined\n  }\n}\n\nfunction isDateDisabled(dateValue: DateValue) {\n  return dateValue.toDate(tz) > new Date()\n}\n\nfunction onPresetChange(value: string | number | bigint | Record<string, any> | null) {\n  if (typeof value !== 'string')\n    return\n\n  if (value === 'custom') {\n    openCustomDateRange.value = true\n    analysisStore.datePreset = null\n    return\n  }\n\n  analysisStore.selectPreset(value)\n}\n</script>\n\n<template>\n  <Select :model-value=\"analysisStore.datePreset\" @update:model-value=\"onPresetChange\">\n    <SelectTrigger>\n      <SelectValue v-if=\"analysisStore.datePreset\" />\n      <div v-else>\n        {{ shortDate(analysisStore.dateRange.startAt) }} - {{ shortDate(analysisStore.dateRange.endAt) }}\n      </div>\n    </SelectTrigger>\n    <SelectContent>\n      <SelectItem value=\"today\">\n        {{ $t('dashboard.date_picker.today') }}\n      </SelectItem>\n      <SelectItem value=\"last-24h\">\n        {{ $t('dashboard.date_picker.last_24h') }}\n      </SelectItem>\n      <SelectSeparator />\n      <SelectItem value=\"this-week\">\n        {{ $t('dashboard.date_picker.this_week') }}\n      </SelectItem>\n      <SelectItem value=\"last-7d\">\n        {{ $t('dashboard.date_picker.last_7d') }}\n      </SelectItem>\n      <SelectSeparator />\n      <SelectItem value=\"this-month\">\n        {{ $t('dashboard.date_picker.this_month') }}\n      </SelectItem>\n      <SelectItem value=\"last-30d\">\n        {{ $t('dashboard.date_picker.last_30d') }}\n      </SelectItem>\n      <SelectSeparator />\n      <SelectItem value=\"last-90d\">\n        {{ $t('dashboard.date_picker.last_90d') }}\n      </SelectItem>\n      <SelectSeparator />\n      <SelectItem value=\"custom\">\n        {{ $t('dashboard.date_picker.custom') }}\n      </SelectItem>\n    </SelectContent>\n  </Select>\n\n  <ResponsiveModal\n    v-model:open=\"openCustomDateRange\"\n    :title=\"$t('dashboard.date_picker.custom_title')\"\n    content-class=\"w-auto md:max-w-(--breakpoint-md)\"\n  >\n    <Tabs\n      default-value=\"range\"\n    >\n      <div class=\"flex justify-center\">\n        <TabsList>\n          <TabsTrigger value=\"date\">\n            {{ $t('dashboard.date_picker.single_date') }}\n          </TabsTrigger>\n          <TabsTrigger value=\"range\">\n            {{ $t('dashboard.date_picker.date_range') }}\n          </TabsTrigger>\n        </TabsList>\n      </div>\n      <TabsContent\n        value=\"date\"\n        class=\"h-80 overflow-y-auto\"\n      >\n        <Calendar\n          :model-value=\"customDate\"\n          weekday-format=\"short\"\n          :is-date-disabled=\"isDateDisabled\"\n          @update:model-value=\"(date) => date && updateCustomDate(date)\"\n        />\n      </TabsContent>\n      <TabsContent\n        value=\"range\"\n        class=\"h-80 overflow-y-auto\"\n      >\n        <RangeCalendar\n          :model-value=\"customDateRange\"\n          initial-focus\n          weekday-format=\"short\"\n          :number-of-months=\"2\"\n          :is-date-disabled=\"isDateDisabled\"\n          @update:model-value=\"updateCustomDateRange\"\n        />\n      </TabsContent>\n    </Tabs>\n  </ResponsiveModal>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/Filters.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\nimport { createReusableTemplate, useMediaQuery, watchDebounced } from '@vueuse/core'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { VList } from 'virtua/vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  filters?: Record<string, string>\n}>()\n\nconst emit = defineEmits<{\n  change: [key: string, value: string]\n}>()\n\nconst [TriggerTemplate, TriggerComponent] = createReusableTemplate()\nconst [FilterTemplate, FilterComponent] = createReusableTemplate()\n\nconst isDesktop = useMediaQuery('(min-width: 640px)')\n\nconst links = ref<Link[]>([])\nconst isOpen = ref(false)\nconst selectedLinks = ref<string[]>(props.filters?.slug?.split(',').filter(Boolean) ?? [])\n\n// Sync selectedLinks when props.filters changes (e.g., store restore/clear)\nwatch(() => props.filters?.slug, (newSlug) => {\n  const newValue = newSlug?.split(',').filter(Boolean) ?? []\n  if (JSON.stringify(newValue) !== JSON.stringify(selectedLinks.value)) {\n    selectedLinks.value = newValue\n  }\n})\n\nasync function getLinks() {\n  try {\n    links.value = await useAPI<Link[]>('/api/link/search')\n  }\n  catch (error) {\n    console.error(error)\n  }\n}\n\nonMounted(() => {\n  getLinks()\n})\n\nwatchDebounced(selectedLinks, (value) => {\n  emit('change', 'slug', value.join(','))\n}, { debounce: 500, maxWait: 1000 })\n</script>\n\n<template>\n  <TriggerTemplate>\n    <Button\n      variant=\"outline\"\n      role=\"combobox\"\n      :aria-expanded=\"isOpen\"\n      class=\"\n        flex w-full justify-between px-3\n        sm:w-48\n      \"\n    >\n      <div\n        class=\"flex-1 truncate text-left\" :class=\"selectedLinks.length ? `\n          text-foreground\n        ` : `text-muted-foreground`\"\n      >\n        {{ selectedLinks.length ? selectedLinks.join(', ') : $t('dashboard.filter_placeholder') }}\n      </div>\n      <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n    </Button>\n  </TriggerTemplate>\n  <FilterTemplate>\n    <Command v-model=\"selectedLinks\" multiple>\n      <CommandInput :placeholder=\"selectedLinks.length ? selectedLinks.join(', ') : $t('dashboard.filter_placeholder')\" />\n      <CommandEmpty>No link found.</CommandEmpty>\n      <CommandList :class=\"{ 'max-h-none': !isDesktop }\">\n        <CommandGroup>\n          <VList\n            v-slot=\"{ item: link }\"\n            :data=\"links\"\n            :style=\"{ height: isDesktop ? '292px' : '420px' }\"\n          >\n            <CommandItem\n              :value=\"link.slug\"\n              class=\"py-2\"\n            >\n              <Check\n                :class=\"cn(\n                  'h-4 w-4',\n                  selectedLinks.includes(link.slug) ? 'opacity-100' : `\n                    opacity-0\n                  `,\n                )\"\n              />\n              {{ link.slug }}\n            </CommandItem>\n          </VList>\n        </CommandGroup>\n      </CommandList>\n    </Command>\n  </FilterTemplate>\n  <Popover v-if=\"isDesktop\" v-model:open=\"isOpen\">\n    <PopoverTrigger as-child>\n      <TriggerComponent />\n    </PopoverTrigger>\n    <PopoverContent\n      class=\"\n        w-full p-0\n        sm:w-48\n      \"\n    >\n      <FilterComponent />\n    </PopoverContent>\n  </Popover>\n\n  <Drawer v-else v-model:open=\"isOpen\">\n    <DrawerTrigger as-child>\n      <TriggerComponent />\n    </DrawerTrigger>\n    <DrawerContent class=\"h-[500px]\">\n      <DrawerHeader class=\"sr-only\">\n        <DrawerTitle>{{ $t('dashboard.filter_placeholder') }}</DrawerTitle>\n      </DrawerHeader>\n      <FilterComponent />\n    </DrawerContent>\n  </Drawer>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/Logout.vue",
    "content": "<script setup lang=\"ts\">\nimport { LogOut } from 'lucide-vue-next'\n\nconst { removeToken } = useAuthToken()\n\nfunction logOut() {\n  removeToken()\n  navigateTo('/dashboard/login')\n}\n</script>\n\n<template>\n  <AlertDialog>\n    <AlertDialogTrigger as-child>\n      <LogOut\n        class=\"h-4 w-4 cursor-pointer\"\n      />\n    </AlertDialogTrigger>\n    <AlertDialogContent\n      class=\"\n        max-h-[95svh] max-w-[95svw] grid-rows-[auto_minmax(0,1fr)_auto]\n        md:max-w-lg\n      \"\n    >\n      <AlertDialogHeader>\n        <AlertDialogTitle>{{ $t('logout.title') }}</AlertDialogTitle>\n        <AlertDialogDescription>\n          {{ $t('logout.confirm') }}\n        </AlertDialogDescription>\n      </AlertDialogHeader>\n      <AlertDialogFooter>\n        <AlertDialogCancel>{{ $t('common.cancel') }}</AlertDialogCancel>\n        <AlertDialogAction @click=\"logOut\">\n          {{ $t('logout.action') }}\n        </AlertDialogAction>\n      </AlertDialogFooter>\n    </AlertDialogContent>\n  </AlertDialog>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/PageBreadcrumb.vue",
    "content": "<script setup lang=\"ts\">\ndefineProps<{\n  title: string\n}>()\n</script>\n\n<template>\n  <Breadcrumb>\n    <BreadcrumbList>\n      <BreadcrumbItem>\n        <span class=\"text-muted-foreground\">\n          {{ $t('dashboard.title') }}\n        </span>\n      </BreadcrumbItem>\n      <BreadcrumbSeparator />\n      <BreadcrumbItem>\n        <BreadcrumbPage>{{ title }}</BreadcrumbPage>\n      </BreadcrumbItem>\n    </BreadcrumbList>\n  </Breadcrumb>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/TimePicker.vue",
    "content": "<script setup lang=\"ts\">\nconst realtimeStore = useDashboardRealtimeStore()\n\nfunction onPresetChange(value: string | number | bigint | Record<string, any> | null) {\n  if (typeof value !== 'string')\n    return\n\n  realtimeStore.selectPreset(value)\n}\n</script>\n\n<template>\n  <Select :model-value=\"realtimeStore.timeName\" @update:model-value=\"onPresetChange\">\n    <SelectTrigger>\n      <SelectValue />\n    </SelectTrigger>\n    <SelectContent>\n      <SelectItem value=\"last-5m\">\n        {{ $t('dashboard.time_picker.last_5m') }}\n      </SelectItem>\n      <SelectItem value=\"last-10m\">\n        {{ $t('dashboard.time_picker.last_10m') }}\n      </SelectItem>\n      <SelectItem value=\"last-30m\">\n        {{ $t('dashboard.time_picker.last_30m') }}\n      </SelectItem>\n      <SelectItem value=\"last-1h\">\n        {{ $t('dashboard.time_picker.last_1h') }}\n      </SelectItem>\n      <SelectItem value=\"last-6h\">\n        {{ $t('dashboard.time_picker.last_6h') }}\n      </SelectItem>\n      <SelectItem value=\"last-12h\">\n        {{ $t('dashboard.time_picker.last_12h') }}\n      </SelectItem>\n      <SelectItem value=\"last-24h\">\n        {{ $t('dashboard.time_picker.last_24h') }}\n      </SelectItem>\n      <SelectSeparator />\n      <SelectItem value=\"today\">\n        {{ $t('dashboard.time_picker.today') }}\n      </SelectItem>\n    </SelectContent>\n  </Select>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/Counters.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CounterData } from '@/types'\nimport NumberFlow from '@number-flow/vue'\nimport { watchThrottled } from '@vueuse/core'\nimport { Flame, MousePointerClick, Users } from 'lucide-vue-next'\n\nconst defaultData: CounterData = Object.freeze({\n  visits: 0,\n  visitors: 0,\n  referers: 0,\n})\n\nconst counters = ref<CounterData>(defaultData)\n\nconst id = inject(LINK_ID_KEY, computed(() => undefined))\nconst analysisStore = useDashboardAnalysisStore()\n\nasync function getLinkCounters() {\n  counters.value = defaultData\n  const result = await useAPI<{ data: CounterData[] }>('/api/stats/counters', {\n    query: {\n      id: id.value,\n      startAt: analysisStore.dateRange.startAt,\n      endAt: analysisStore.dateRange.endAt,\n      ...analysisStore.filters,\n    },\n  })\n  counters.value = result.data?.[0] ?? defaultData\n}\n\nwatchThrottled([() => analysisStore.dateRange, () => analysisStore.filters], getLinkCounters, {\n  deep: true,\n  throttle: 500,\n  leading: true,\n  trailing: true,\n})\n\nonMounted(async () => {\n  getLinkCounters()\n})\n</script>\n\n<template>\n  <div\n    class=\"\n      grid gap-4\n      sm:grid-cols-3 sm:gap-3\n      lg:gap-4\n    \"\n  >\n    <Card class=\"gap-0\">\n      <CardHeader\n        class=\"flex flex-row items-center justify-between space-y-0 pb-2\"\n      >\n        <CardTitle class=\"text-sm font-medium\">\n          {{ $t('dashboard.visits') }}\n        </CardTitle>\n        <MousePointerClick\n          aria-hidden=\"true\" class=\"h-4 w-4 text-muted-foreground\"\n        />\n      </CardHeader>\n      <CardContent>\n        <NumberFlow class=\"text-2xl font-bold tabular-nums\" :class=\"{ 'opacity-60 blur-md': !counters.visits }\" :value=\"counters.visits\" />\n      </CardContent>\n    </Card>\n    <Card class=\"gap-0\">\n      <CardHeader\n        class=\"flex flex-row items-center justify-between space-y-0 pb-2\"\n      >\n        <CardTitle class=\"text-sm font-medium\">\n          {{ $t('dashboard.visitors') }}\n        </CardTitle>\n        <Users aria-hidden=\"true\" class=\"h-4 w-4 text-muted-foreground\" />\n      </CardHeader>\n      <CardContent>\n        <NumberFlow class=\"text-2xl font-bold tabular-nums\" :class=\"{ 'opacity-60 blur-md': !counters.visitors }\" :value=\"counters.visitors\" />\n      </CardContent>\n    </Card>\n    <Card class=\"gap-0\">\n      <CardHeader\n        class=\"flex flex-row items-center justify-between space-y-0 pb-2\"\n      >\n        <CardTitle class=\"text-sm font-medium\">\n          {{ $t('dashboard.referers') }}\n        </CardTitle>\n        <Flame aria-hidden=\"true\" class=\"h-4 w-4 text-muted-foreground\" />\n      </CardHeader>\n      <CardContent>\n        <NumberFlow class=\"text-2xl font-bold tabular-nums\" :class=\"{ 'opacity-60 blur-md': !counters.referers }\" :value=\"counters.referers\" />\n      </CardContent>\n    </Card>\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/Heatmap.vue",
    "content": "<script setup lang=\"ts\">\nimport type { HeatmapDataPoint } from '@/types'\nimport { watchThrottled } from '@vueuse/core'\n\nconst props = withDefaults(defineProps<{\n  metric?: 'visits' | 'visitors'\n}>(), {\n  metric: 'visits',\n})\n\nconst heatmapData = shallowRef<HeatmapDataPoint[]>([])\nconst isLoaded = ref(false)\nconst id = inject(LINK_ID_KEY, computed(() => undefined))\nconst analysisStore = useDashboardAnalysisStore()\n\nconst effectiveTimeRange = computed(() => ({\n  startAt: analysisStore.dateRange.startAt,\n  endAt: analysisStore.dateRange.endAt,\n}))\n\nconst effectiveFilters = computed(() => analysisStore.filters)\n\nconst weekdays = getWeekdayNames('short')\n// weekday indices: Monday=1, Tuesday=2, ..., Sunday=7 (ISO 8601)\nconst weekdayIndices = [1, 2, 3, 4, 5, 6, 7]\n\nconst hours = Array.from({ length: 24 }, (_, i) => i)\n\nconst gridData = computed(() => {\n  const grid: Record<string, number> = {}\n  let maxValue = 0\n\n  for (const item of heatmapData.value) {\n    const key = `${item.weekday}-${item.hour}`\n    const value = +item[props.metric]\n    grid[key] = value\n    if (value > maxValue)\n      maxValue = value\n  }\n\n  return { grid, maxValue }\n})\n\nfunction getCellValue(weekday: number, hour: number): number {\n  const key = `${weekday}-${hour}`\n  return gridData.value.grid[key] || 0\n}\n\nfunction getCellColor(weekday: number, hour: number): string {\n  const value = getCellValue(weekday, hour)\n  const { maxValue } = gridData.value\n\n  // Calculate alpha based on value intensity\n  let alpha = 0.05 // empty cell\n  if (value > 0 && maxValue > 0) {\n    alpha = Math.max(0.1, value / maxValue)\n  }\n\n  const baseColor = props.metric === 'visits' ? 'var(--chart-1)' : 'var(--chart-2)'\n  const emptyColor = 'var(--foreground)'\n\n  // Use color-mix to apply alpha to the color\n  const color = value === 0 ? emptyColor : baseColor\n  return `color-mix(in srgb, ${color} ${Math.round(alpha * 100)}%, transparent)`\n}\n\nasync function getHeatmapData() {\n  isLoaded.value = false\n  const { startAt, endAt } = effectiveTimeRange.value\n  const result = await useAPI<{ data: HeatmapDataPoint[] }>('/api/stats/heatmap', {\n    query: {\n      id: id.value,\n      clientTimezone: getTimeZone(),\n      startAt,\n      endAt,\n      ...effectiveFilters.value,\n    },\n  })\n  heatmapData.value = (result.data || []).map(item => ({\n    ...item,\n    visitors: +item.visitors,\n    visits: +item.visits,\n    weekday: +item.weekday,\n    hour: +item.hour,\n  }))\n  await nextTick()\n  isLoaded.value = true\n}\n\nwatchThrottled(\n  [effectiveTimeRange, effectiveFilters],\n  getHeatmapData,\n  {\n    deep: true,\n    throttle: 500,\n    leading: true,\n    trailing: true,\n  },\n)\n\nonMounted(() => {\n  getHeatmapData()\n})\n</script>\n\n<template>\n  <Card\n    class=\"\n      p-4\n      md:p-10\n    \"\n  >\n    <!-- Heatmap container with same aspect ratio as Views -->\n    <div\n      class=\"\n        aspect-[4/1] w-full overflow-x-auto transition-opacity duration-500\n        ease-out\n      \"\n      :class=\"isLoaded ? 'opacity-100' : 'opacity-0'\"\n    >\n      <div class=\"flex h-full min-w-[600px] flex-col\">\n        <!-- Hours header -->\n        <div\n          class=\"\n            mb-2 ml-12 grid flex-none grid-cols-24 gap-2 text-[10px]\n            text-muted-foreground\n          \"\n        >\n          <div v-for=\"hour in hours\" :key=\"hour\" class=\"text-center\">\n            {{ hour }}\n          </div>\n        </div>\n\n        <!-- Heatmap grid -->\n        <div class=\"flex flex-1 flex-col gap-3\">\n          <div\n            v-for=\"(weekdayIdx, arrayIdx) in weekdayIndices\"\n            :key=\"weekdayIdx\"\n            class=\"flex flex-1 items-center gap-3\"\n          >\n            <div\n              class=\"w-9 shrink-0 text-right text-[10px] text-muted-foreground\"\n            >\n              {{ weekdays[arrayIdx] }}\n            </div>\n            <div class=\"grid h-full flex-1 grid-cols-24 gap-2\">\n              <Tooltip v-for=\"hour in hours\" :key=\"hour\">\n                <TooltipTrigger as-child>\n                  <div\n                    class=\"\n                      h-full cursor-pointer rounded-sm\n                      transition-[background-color,box-shadow] duration-300\n                      hover:ring-1 hover:ring-foreground/10\n                    \"\n                    role=\"gridcell\"\n                    :aria-label=\"`${weekdays[arrayIdx]} ${hour}:00 - ${metric === 'visits' ? $t('dashboard.visits') : $t('dashboard.visitors')}: ${formatNumber(getCellValue(weekdayIdx, hour))}`\"\n                    :style=\"{\n                      backgroundColor: getCellColor(weekdayIdx, hour),\n                    }\"\n                  />\n                </TooltipTrigger>\n                <TooltipContent>\n                  <p class=\"font-medium\">\n                    {{ weekdays[arrayIdx] }} {{ hour }}:00\n                  </p>\n                  <p class=\"text-muted-foreground\">\n                    {{ metric === 'visits' ? $t('dashboard.visits') : $t('dashboard.visitors') }}:\n                    {{ formatNumber(getCellValue(weekdayIdx, hour)) }}\n                  </p>\n                </TooltipContent>\n              </Tooltip>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </Card>\n</template>\n\n<style scoped>\n.grid-cols-24 {\n  grid-template-columns: repeat(24, minmax(0, 1fr));\n}\n</style>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/Index.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\n\nwithDefaults(defineProps<{\n  link?: Link | null\n}>(), {\n  link: null,\n})\n\nconst analysisStore = useDashboardAnalysisStore()\n\nconst viewMode = ref<'trend' | 'heatmap'>('trend')\nconst heatmapMetric = ref<'visits' | 'visitors'>('visits')\n\nonBeforeMount(() => {\n  analysisStore.init()\n})\n</script>\n\n<template>\n  <h3 v-if=\"link\" class=\"text-xl leading-10 font-bold\">\n    {{ link.slug }} {{ $t('dashboard.stats') }}\n  </h3>\n  <DashboardAnalysisCounters />\n  <Tabs v-model=\"viewMode\" default-value=\"trend\">\n    <div class=\"mb-4 flex items-center justify-between\">\n      <TabsList>\n        <TabsTrigger value=\"trend\">\n          {{ $t('dashboard.trend') }}\n        </TabsTrigger>\n        <TabsTrigger value=\"heatmap\">\n          {{ $t('dashboard.weekly_trend') }}\n        </TabsTrigger>\n      </TabsList>\n\n      <Select v-if=\"viewMode === 'heatmap'\" v-model=\"heatmapMetric\">\n        <SelectTrigger class=\"h-8 w-[120px]\">\n          <SelectValue />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value=\"visits\">\n            {{ $t('dashboard.visits') }}\n          </SelectItem>\n          <SelectItem value=\"visitors\">\n            {{ $t('dashboard.visitors') }}\n          </SelectItem>\n        </SelectContent>\n      </Select>\n    </div>\n    <TabsContent value=\"trend\" class=\"mt-0\">\n      <DashboardAnalysisViews v-if=\"viewMode === 'trend'\" />\n    </TabsContent>\n    <TabsContent value=\"heatmap\" class=\"mt-0\">\n      <DashboardAnalysisHeatmap v-if=\"viewMode === 'heatmap'\" :metric=\"heatmapMetric\" />\n    </TabsContent>\n  </Tabs>\n  <DashboardAnalysisMetrics />\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/Views.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ChartConfig } from '@/components/ui/chart'\nimport type { ViewDataPoint } from '@/types'\nimport { VisArea, VisAxis, VisGroupedBar, VisLine, VisXYContainer } from '@unovis/vue'\nimport { watchThrottled } from '@vueuse/core'\nimport {\n  ChartTooltipContent,\n  componentToString,\n} from '@/components/ui/chart'\n\nconst props = withDefaults(defineProps<{\n  mode?: 'full' | 'simple'\n  chartType?: 'area' | 'bar'\n  startAt?: number\n  endAt?: number\n  filters?: Record<string, string>\n}>(), {\n  mode: 'full',\n  chartType: 'area',\n})\n\nconst { t } = useI18n()\n\nconst views = shallowRef<ViewDataPoint[]>([])\n\nconst isAreaMode = computed(() => props.chartType === 'area' && views.value.length > 1)\n\nconst chartConfig = computed<ChartConfig>(() => {\n  const config: ChartConfig = {\n    visits: {\n      label: t('dashboard.visits'),\n      color: 'var(--chart-1)',\n    },\n  }\n  if (props.mode === 'full') {\n    config.visitors = {\n      label: t('dashboard.visitors'),\n      color: 'var(--chart-2)',\n    }\n  }\n  return config\n})\n\nconst categories = computed(() =>\n  props.mode === 'full' ? ['visits', 'visitors'] : ['visits'],\n)\n\nconst id = inject(LINK_ID_KEY, computed(() => undefined))\nconst analysisStore = useDashboardAnalysisStore()\n\nconst effectiveTimeRange = computed(() => ({\n  startAt: props.startAt ?? analysisStore.dateRange.startAt,\n  endAt: props.endAt ?? analysisStore.dateRange.endAt,\n}))\n\nconst effectiveFilters = computed(() =>\n  props.filters ?? analysisStore.filters,\n)\n\nconst OneHour = 60 * 60\nconst OneDay = 24 * 60 * 60\n\nfunction getUnit(startAt: number, endAt: number): 'minute' | 'hour' | 'day' {\n  if (startAt && endAt && endAt - startAt <= OneHour)\n    return 'minute'\n\n  if (startAt && endAt && endAt - startAt <= OneDay)\n    return 'hour'\n\n  return 'day'\n}\n\nfunction parseTimeString(time: string): number {\n  if (time.includes(' ')) {\n    const [date, timePart] = time.split(' ')\n    const normalizedTime = (timePart ?? '').includes(':')\n      ? timePart\n      : `${(timePart ?? '').padStart(2, '0')}:00`\n    return new Date(`${date}T${normalizedTime}:00`).getTime()\n  }\n  return new Date(time).getTime()\n}\n\nasync function getLinkViews() {\n  views.value = []\n  const { startAt, endAt } = effectiveTimeRange.value\n  const result = await useAPI<{ data: ViewDataPoint[] }>('/api/stats/views', {\n    query: {\n      id: id.value,\n      unit: getUnit(startAt, endAt),\n      clientTimezone: getTimeZone(),\n      startAt,\n      endAt,\n      ...effectiveFilters.value,\n    },\n  })\n  views.value = (result.data || []).map(item => ({\n    ...item,\n    visitors: +item.visitors,\n    visits: +item.visits,\n  }))\n}\n\nwatchThrottled(\n  [effectiveTimeRange, effectiveFilters],\n  getLinkViews,\n  {\n    deep: true,\n    throttle: 500,\n    leading: true,\n    trailing: true,\n  },\n)\n\nonMounted(async () => {\n  getLinkViews()\n})\n\ntype Data = ViewDataPoint\n</script>\n\n<template>\n  <Card\n    class=\"\n      p-4\n      md:p-10\n    \"\n  >\n    <ChartContainer :config=\"chartConfig\" class=\"aspect-[4/1] w-full\">\n      <VisXYContainer :data=\"views\" :margin=\"{ left: 0, right: 0 }\">\n        <template v-if=\"isAreaMode\">\n          <template v-for=\"cat in categories\" :key=\"cat\">\n            <VisArea\n              :x=\"(d: Data) => parseTimeString(d.time)\"\n              :y=\"(d: Data) => d[cat as keyof Data] as number\"\n              :color=\"chartConfig[cat]?.color ?? 'var(--chart-1)'\"\n              :opacity=\"0.4\"\n            />\n            <VisLine\n              :x=\"(d: Data) => parseTimeString(d.time)\"\n              :y=\"(d: Data) => d[cat as keyof Data] as number\"\n              :color=\"chartConfig[cat]?.color ?? 'var(--chart-1)'\"\n              :line-width=\"2\"\n            />\n          </template>\n        </template>\n\n        <template v-else>\n          <VisGroupedBar\n            :x=\"(d: Data) => parseTimeString(d.time)\"\n            :y=\"categories.map(cat => (d: Data) => d[cat as keyof Data] as number)\"\n            :color=\"categories.map(cat => chartConfig[cat]?.color ?? 'var(--chart-1)')\"\n            :rounded-corners=\"4\"\n            :group-width=\"getUnit(startAt ?? 0, endAt ?? 0) === 'minute' ? 8 : undefined\"\n          />\n        </template>\n\n        <VisAxis\n          v-if=\"mode === 'full' && views.length\"\n          type=\"y\"\n          :tick-format=\"formatNumber\"\n          :tick-line=\"false\"\n          :domain-line=\"false\"\n          :grid-line=\"true\"\n          :num-ticks=\"3\"\n        />\n\n        <!-- Tooltip -->\n        <ChartTooltip />\n        <ChartCrosshair\n          :template=\"componentToString(chartConfig, ChartTooltipContent, { labelKey: 'time' })\"\n          :color=\"categories.map(cat => chartConfig[cat]?.color ?? 'var(--chart-1)')\"\n        />\n      </VisXYContainer>\n    </ChartContainer>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/Group.vue",
    "content": "<script setup lang=\"ts\">\ndefineProps<{\n  tabs: readonly string[]\n  rawTabs: readonly string[]\n}>()\n</script>\n\n<template>\n  <Tabs\n    :default-value=\"rawTabs[0]\"\n    class=\"flex flex-col\"\n  >\n    <TabsList class=\"w-fit\">\n      <TabsTrigger\n        v-for=\"(tab, index) in tabs\"\n        :key=\"tab\"\n        :value=\"rawTabs[index]!\"\n      >\n        {{ tab }}\n      </TabsTrigger>\n    </TabsList>\n    <TabsContent\n      v-for=\"(tab, index) in tabs\"\n      :key=\"tab\"\n      :value=\"rawTabs[index]!\"\n      class=\"flex-1\"\n    >\n      <DashboardAnalysisMetricsMetric\n        :type=\"rawTabs[index]!\"\n        :name=\"tab\"\n        class=\"h-full\"\n      />\n    </TabsContent>\n  </Tabs>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/Index.vue",
    "content": "<script setup lang=\"ts\">\nconst { t } = useI18n()\n\nconst tabs = {\n  location: ['country', 'region', 'city'],\n  referer: ['referer', 'slug'],\n  time: ['language', 'timezone'],\n  device: ['device', 'deviceType'],\n  browser: ['os', 'browser', 'browserType'],\n} as const\n\nconst translatedTabs = computed(() => ({\n  location: tabs.location.map(tab => t(`dashboard.metrics.${tab}`)),\n  referer: tabs.referer.map(tab => t(`dashboard.metrics.${tab}`)),\n  time: tabs.time.map(tab => t(`dashboard.metrics.${tab}`)),\n  device: tabs.device.map(tab => t(`dashboard.metrics.${tab}`)),\n  browser: tabs.browser.map(tab => t(`dashboard.metrics.${tab}`)),\n}))\n</script>\n\n<template>\n  <main\n    class=\"\n      grid gap-8\n      lg:grid-cols-12\n    \"\n  >\n    <LazyDashboardAnalysisMetricsLocations\n      class=\"\n        col-span-1\n        lg:col-span-8\n      \"\n    />\n    <DashboardAnalysisMetricsGroup\n      class=\"lg:col-span-4\"\n      :tabs=\"translatedTabs.location\"\n      :raw-tabs=\"tabs.location\"\n    />\n    <DashboardAnalysisMetricsGroup\n      class=\"lg:col-span-6\"\n      :tabs=\"translatedTabs.referer\"\n      :raw-tabs=\"tabs.referer\"\n    />\n    <DashboardAnalysisMetricsGroup\n      class=\"lg:col-span-6\"\n      :tabs=\"translatedTabs.time\"\n      :raw-tabs=\"tabs.time\"\n    />\n    <DashboardAnalysisMetricsGroup\n      class=\"lg:col-span-6\"\n      :tabs=\"translatedTabs.device\"\n      :raw-tabs=\"tabs.device\"\n    />\n    <DashboardAnalysisMetricsGroup\n      class=\"lg:col-span-6\"\n      :tabs=\"translatedTabs.browser\"\n      :raw-tabs=\"tabs.browser\"\n    />\n  </main>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/List.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MetricItem } from '@/types'\nimport { VList } from 'virtua/vue'\n\ndefineProps<{\n  metrics: MetricItem[]\n  type: string\n}>()\n</script>\n\n<template>\n  <div class=\"w-full text-sm\">\n    <div\n      class=\"\n        flex justify-between border-b leading-[48px] transition-colors\n        hover:bg-muted/50\n      \"\n    >\n      <div\n        class=\"\n          h-12 px-4 text-left align-middle font-medium text-muted-foreground\n        \"\n      >\n        {{ $t('dashboard.name') }}\n      </div>\n      <div\n        class=\"\n          h-12 px-4 text-right align-middle font-medium text-muted-foreground\n        \"\n      >\n        {{ $t('dashboard.count') }}\n      </div>\n    </div>\n    <VList\n      v-slot=\"{ item: metric }\"\n      :data=\"metrics\"\n      :style=\"{ height: '342px' }\"\n    >\n      <div\n        class=\"\n          border-b px-4 py-2 transition-colors\n          hover:bg-muted/50\n        \"\n      >\n        <div class=\"flex justify-between\">\n          <div\n            class=\"flex-1 truncate leading-5\"\n          >\n            <DashboardAnalysisMetricsName\n              :name=\"metric.name\"\n              :type=\"type\"\n            />\n          </div>\n          <div\n            class=\"text-right\"\n          >\n            {{ formatNumber(metric.count) }}\n            <span class=\"text-xs text-gray-500\">({{ metric.percent }}%)</span>\n          </div>\n        </div>\n        <div\n          class=\"flex-1\"\n        >\n          <TooltipProvider>\n            <Tooltip>\n              <TooltipTrigger class=\"w-full\">\n                <Progress\n                  v-model=\"metric.percent\"\n                  class=\"h-2\"\n                />\n              </TooltipTrigger>\n              <TooltipContent>\n                <p>{{ metric.percent }}%</p>\n              </TooltipContent>\n            </Tooltip>\n          </TooltipProvider>\n        </div>\n      </div>\n    </VList>\n  </div>\n</template>\n\n<style scoped>\n:deep([data-slot='progress']) {\n  background-color: var(--muted);\n}\n\n:deep([data-slot='progress-indicator']) {\n  background-color: var(--chart-1);\n}\n</style>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/Locations.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ChartConfig } from '@/components/ui/chart'\nimport type { AreaData } from '@/types'\nimport { VisSingleContainer, VisTooltip, VisTopoJSONMap, VisTopoJSONMapSelectors } from '@unovis/vue'\nimport { useMounted, watchThrottled } from '@vueuse/core'\nimport { render } from 'vue'\nimport { ChartTooltipContent } from '@/components/ui/chart'\n\nconst isMounted = useMounted()\nconst id = inject(LINK_ID_KEY, computed(() => undefined))\nconst analysisStore = useDashboardAnalysisStore()\nconst { t, locale } = useI18n()\n\nconst worldMapTopoJSON = ref<Record<string, unknown>>({})\nconst areaData = ref<AreaData[]>([])\n\nconst chartConfig = computed<ChartConfig>(() => ({\n  count: {\n    label: t('dashboard.visits'),\n    color: 'var(--chart-1)',\n  },\n}))\n\nasync function getWorldMapJSON() {\n  const data = await $fetch('/world.json')\n  worldMapTopoJSON.value = data as Record<string, unknown>\n}\n\nasync function getMapData() {\n  areaData.value = []\n  const result = await useAPI<{ data: Array<{ name: string, count: number }> }>('/api/stats/metrics', {\n    query: {\n      type: 'country',\n      id: id.value,\n      startAt: analysisStore.dateRange.startAt,\n      endAt: analysisStore.dateRange.endAt,\n      ...analysisStore.filters,\n    },\n  })\n  if (Array.isArray(result.data)) {\n    areaData.value = result.data.map(country => ({\n      ...country,\n      id: country.name,\n    }))\n  }\n}\n\nwatchThrottled([() => analysisStore.dateRange, () => analysisStore.filters], getMapData, {\n  deep: true,\n  throttle: 500,\n  leading: true,\n  trailing: true,\n})\n\nonMounted(() => {\n  getWorldMapJSON()\n  getMapData()\n})\n\nlet tooltipCache = new WeakMap<object, string>()\n\nwatch(locale, () => {\n  tooltipCache = new WeakMap()\n})\n\nfunction tooltipTemplate(d: any): string {\n  // VisTopoJSONMap 传入的数据结构可能是嵌套的\n  const data = d?.data ?? d\n  if (!data?.name)\n    return ''\n\n  // 检查缓存\n  if (tooltipCache.has(data))\n    return tooltipCache.get(data) as string\n\n  data.displayName = getRegionName(data.name, locale.value)\n\n  const div = document.createElement('div')\n  const vnode = h(ChartTooltipContent, {\n    payload: { count: data.count },\n    config: chartConfig.value,\n    x: data.displayName,\n  })\n  render(vnode, div)\n  const html = div.innerHTML\n  tooltipCache.set(data, html)\n  return html\n}\n</script>\n\n<template>\n  <Card\n    class=\"\n      flex flex-col\n      md:h-[500px]\n    \"\n  >\n    <CardHeader>\n      <CardTitle>{{ $t('dashboard.locations') }}</CardTitle>\n    </CardHeader>\n    <CardContent class=\"relative flex-1\">\n      <VisSingleContainer\n        v-if=\"worldMapTopoJSON.type\"\n        :data=\"{ areas: areaData }\"\n        :style=\"{ height: isMounted ? '100%' : 'auto', width: '100%' }\"\n        class=\"absolute inset-0\"\n      >\n        <VisTopoJSONMap\n          :topojson=\"worldMapTopoJSON\"\n          map-feature-name=\"countries\"\n        />\n        <VisTooltip\n          :horizontal-shift=\"20\"\n          :vertical-shift=\"20\"\n          :triggers=\"{\n            [VisTopoJSONMapSelectors.feature]: tooltipTemplate,\n          }\"\n          :attributes=\"{\n            style: '--vis-tooltip-padding: 0; --vis-tooltip-background-color: transparent; --vis-tooltip-border-color: transparent; --vis-tooltip-shadow-color: transparent;',\n          }\"\n        />\n      </VisSingleContainer>\n    </CardContent>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/Metric.vue",
    "content": "<script setup lang=\"ts\">\nimport type { MetricItem } from '@/types'\nimport { watchThrottled } from '@vueuse/core'\nimport { Maximize } from 'lucide-vue-next'\n\nconst props = defineProps<{\n  type: string\n  name: string\n}>()\n\nconst id = inject(LINK_ID_KEY, computed(() => undefined))\nconst analysisStore = useDashboardAnalysisStore()\n\nconst total = ref(0)\nconst metrics = ref<MetricItem[]>([])\nconst top10 = ref<MetricItem[]>([])\n\ninterface RawMetricData {\n  name: string\n  count: number\n}\n\nasync function getLinkMetrics() {\n  total.value = 0\n  metrics.value = []\n  top10.value = []\n  const result = await useAPI<{ data: RawMetricData[] }>('/api/stats/metrics', {\n    query: {\n      type: props.type,\n      id: id.value,\n      startAt: analysisStore.dateRange.startAt,\n      endAt: analysisStore.dateRange.endAt,\n      ...analysisStore.filters,\n    },\n  })\n  if (Array.isArray(result.data)) {\n    total.value = result.data.reduce((acc, cur) => acc + Number(cur.count), 0)\n    metrics.value = result.data.map(item => ({\n      ...item,\n      percent: Math.floor(item.count / total.value * 100) || (item.count ? 1 : 0),\n    }))\n    top10.value = metrics.value.slice(0, 10)\n  }\n}\n\nwatchThrottled([() => analysisStore.dateRange, () => analysisStore.filters], getLinkMetrics, {\n  deep: true,\n  throttle: 500,\n  leading: true,\n  trailing: true,\n})\n\nonMounted(() => {\n  getLinkMetrics()\n})\n</script>\n\n<template>\n  <Card class=\"flex flex-col gap-0 p-0\">\n    <template v-if=\"metrics.length\">\n      <CardContent class=\"p-0\">\n        <DashboardAnalysisMetricsList\n          class=\"flex-1\"\n          :metrics=\"top10\"\n          :type=\"type\"\n        />\n      </CardContent>\n      <CardFooter class=\"py-2\">\n        <ResponsiveModal :title=\"name\" content-class=\"md:max-w-(--breakpoint-md)\">\n          <template #trigger>\n            <Button variant=\"link\" class=\"w-full\">\n              <Maximize class=\"mr-2 h-4 w-4\" />\n              {{ $t('dashboard.details') }}\n            </Button>\n          </template>\n\n          <DashboardAnalysisMetricsList\n            class=\"overflow-y-auto\"\n            :metrics=\"metrics\"\n            :type=\"type\"\n          />\n        </ResponsiveModal>\n      </CardFooter>\n    </template>\n    <template v-else>\n      <div class=\"flex h-12 items-center justify-between px-4\">\n        <Skeleton\n          class=\"h-4 w-32 rounded-full\"\n        />\n        <Skeleton\n          class=\"h-4 w-20 rounded-full\"\n        />\n      </div>\n      <div\n        v-for=\"i in 5\"\n        :key=\"i\"\n        class=\"px-4 py-4\"\n      >\n        <Skeleton\n          class=\"h-4 w-full rounded-full\"\n        />\n      </div>\n    </template>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/name/Icon.vue",
    "content": "<script setup lang=\"ts\">\nimport {\n  Globe,\n  Laptop,\n  MonitorCheck,\n  Smartphone,\n  Tablet,\n  Terminal,\n} from 'lucide-vue-next'\n\nimport {\n  AcerIcon,\n  AndroidIcon,\n  AppleIcon,\n  AppleTvIcon,\n  ArchLinuxIcon,\n  ArcIcon,\n  AsusIcon,\n  BaiduIcon,\n  BlackberryIcon,\n  BraveIcon,\n  CentOsIcon,\n  DebianIcon,\n  DellIcon,\n  DiscordIcon,\n  DuckDuckGoIcon,\n  EricssonIcon,\n  FacebookIcon,\n  FedoraIcon,\n  FirefoxBrowserIcon,\n  FitbitIcon,\n  FreeBsdIcon,\n  FujitsuIcon,\n  GarminIcon,\n  GnuIcon,\n  GoogleCastIcon,\n  GoogleChromeIcon,\n  GoogleIcon,\n  GoogleTvIcon,\n  HonorIcon,\n  HpIcon,\n  HtcIcon,\n  HuaweiIcon,\n  IOsIcon,\n  LenovoIcon,\n  LgIcon,\n  LinuxIcon,\n  LinuxMintIcon,\n  LogitechIcon,\n  MacOsIcon,\n  MeizuIcon,\n  MetaIcon,\n  MotorolaIcon,\n  NecIcon,\n  NokiaIcon,\n  NotionIcon,\n  NvidiaIcon,\n  OculusIcon,\n  OnePlusIcon,\n  OpenAiIcon,\n  OperaGxIcon,\n  OperaIcon,\n  OppoIcon,\n  PanasonicIcon,\n  PinterestIcon,\n  PlayStationIcon,\n  QqIcon,\n  RazerIcon,\n  RedHatIcon,\n  RokuIcon,\n  SafariIcon,\n  SamsungIcon,\n  SharpIcon,\n  SlackIcon,\n  SonyIcon,\n  SteamIcon,\n  TelegramIcon,\n  TeslaIcon,\n  TikTokIcon,\n  TorBrowserIcon,\n  ToshibaIcon,\n  UbuntuIcon,\n  ValveIcon,\n  VivaldiIcon,\n  VivoIcon,\n  WearOsIcon,\n  WeChatIcon,\n  XiaomiIcon,\n  XIcon,\n  YandexCloudIcon,\n} from 'vue3-simple-icons'\n\nwithDefaults(defineProps<{\n  name: string\n  type?: string\n}>(), {\n  type: 'browser',\n})\n\nconst iconMaps: Record<string, Component> = {\n  'acer': AcerIcon,\n  'android': AndroidIcon,\n  'android browser': AndroidIcon,\n  'apple tv': AppleTvIcon,\n  'applebot': AppleIcon,\n  'arc': ArcIcon,\n  'arch': ArchLinuxIcon,\n  'arch linux': ArchLinuxIcon,\n  'asus': AsusIcon,\n  'baidu': BaiduIcon,\n  'baidu browser': BaiduIcon,\n  'baiduspider': BaiduIcon,\n  'blackberry': BlackberryIcon,\n  'brave': BraveIcon,\n  'browser': Globe,\n  'centos': CentOsIcon,\n  'chrome': GoogleChromeIcon,\n  'chrome headless': GoogleChromeIcon,\n  'chrome webview': GoogleChromeIcon,\n  'chromecast': GoogleCastIcon,\n  'chromium': GoogleChromeIcon,\n  'claudebot': OpenAiIcon,\n  'console': SteamIcon,\n  'curl': Terminal,\n  'debian': DebianIcon,\n  'dell': DellIcon,\n  'desktop': MonitorCheck,\n  'discord': DiscordIcon,\n  'discordbot': DiscordIcon,\n  'duckduckbot': DuckDuckGoIcon,\n  'duckduckgo': DuckDuckGoIcon,\n  'ericsson': EricssonIcon,\n  'facebook': FacebookIcon,\n  'facebookexternalhit': FacebookIcon,\n  'fedora': FedoraIcon,\n  'firefox': FirefoxBrowserIcon,\n  'firefox focus': FirefoxBrowserIcon,\n  'firefox reality': FirefoxBrowserIcon,\n  'fitbit': FitbitIcon,\n  'freebsd': FreeBsdIcon,\n  'fujitsu': FujitsuIcon,\n  'garmin': GarminIcon,\n  'gnu': GnuIcon,\n  'google': GoogleIcon,\n  'google tv': GoogleTvIcon,\n  'googlebot': GoogleIcon,\n  'googlebot-image': GoogleIcon,\n  'gptbot': OpenAiIcon,\n  'harmonyos': HuaweiIcon,\n  'honor': HonorIcon,\n  'hp': HpIcon,\n  'htc': HtcIcon,\n  'huawei': HuaweiIcon,\n  'huawei browser': HuaweiIcon,\n  'ios': IOsIcon,\n  'ipad': AppleIcon,\n  'iphone': AppleIcon,\n  'ipod': AppleIcon,\n  'laptop': Laptop,\n  'lenovo': LenovoIcon,\n  'lg': LgIcon,\n  'linux': LinuxIcon,\n  'linux mint': LinuxMintIcon,\n  'logitech': LogitechIcon,\n  'macintosh': AppleIcon,\n  'macos': MacOsIcon,\n  'meizu': MeizuIcon,\n  'meta': MetaIcon,\n  'mint': LinuxMintIcon,\n  'miui browser': XiaomiIcon,\n  'mobile': Smartphone,\n  'mobile chrome': GoogleChromeIcon,\n  'mobile firefox': FirefoxBrowserIcon,\n  'mobile safari': SafariIcon,\n  'motorola': MotorolaIcon,\n  'nec': NecIcon,\n  'nokia': NokiaIcon,\n  'notion': NotionIcon,\n  'nvidia': NvidiaIcon,\n  'oculus': OculusIcon,\n  'oculus browser': OculusIcon,\n  'oneplus': OnePlusIcon,\n  'opera': OperaIcon,\n  'opera coast': OperaIcon,\n  'opera gx': OperaGxIcon,\n  'opera mini': OperaIcon,\n  'opera mobi': OperaIcon,\n  'opera touch': OperaIcon,\n  'oppo': OppoIcon,\n  'os': MonitorCheck,\n  'panasonic': PanasonicIcon,\n  'pico': OculusIcon,\n  'pico browser': OculusIcon,\n  'pinterestbot': PinterestIcon,\n  'playstation': PlayStationIcon,\n  'qqbrowser': QqIcon,\n  'razer': RazerIcon,\n  'realme': OppoIcon,\n  'red hat': RedHatIcon,\n  'redhat': RedHatIcon,\n  'roku': RokuIcon,\n  'safari': SafariIcon,\n  'samsung': SamsungIcon,\n  'samsung internet': SamsungIcon,\n  'sharp': SharpIcon,\n  'slackbot': SlackIcon,\n  'smarttv': GoogleTvIcon,\n  'sony': SonyIcon,\n  'tablet': Tablet,\n  'telegrambot': TelegramIcon,\n  'tesla': TeslaIcon,\n  'tiktok': TikTokIcon,\n  'tiktok lite': TikTokIcon,\n  'tor browser': TorBrowserIcon,\n  'toshiba': ToshibaIcon,\n  'twitterbot': XIcon,\n  'ubuntu': UbuntuIcon,\n  'valve': ValveIcon,\n  'vivaldi': VivaldiIcon,\n  'vivo': VivoIcon,\n  'vivo browser': VivoIcon,\n  'wearable': WearOsIcon,\n  'wechat': WeChatIcon,\n  'xiaomi': XiaomiIcon,\n  'xr': MetaIcon,\n  'yandex': YandexCloudIcon,\n  'yandexbot': YandexCloudIcon,\n}\n</script>\n\n<template>\n  <div class=\"w-full truncate\">\n    <component\n      :is=\"iconMaps[name.toLowerCase()] || iconMaps[type]\"\n      class=\"float-left h-5 w-5 py-0.5\"\n    />\n    <span>{{ name }}</span>\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/name/Index.vue",
    "content": "<script setup lang=\"ts\">\ndefineProps<{\n  name: string\n  type: string\n}>()\n\nconst locale = useI18n().locale\n\nfunction formatName(name: string, type: string): string {\n  if (!name)\n    return name\n\n  if (type === 'country')\n    return `${getFlag(name)} ${getRegionName(name, locale.value)}`\n\n  if (type === 'language')\n    return getLanguageName(name, locale.value)\n\n  return name\n}\n</script>\n\n<template>\n  <TooltipProvider>\n    <Tooltip>\n      <TooltipTrigger class=\"w-full text-left\">\n        <DashboardAnalysisMetricsNameReferer\n          v-if=\"name && type === 'referer'\"\n          :name=\"name\"\n        />\n        <DashboardAnalysisMetricsNameSlug\n          v-else-if=\"name && type === 'slug'\"\n          :name=\"name\"\n        />\n        <DashboardAnalysisMetricsNameIcon\n          v-else-if=\"name && ['os', 'browser', 'browserType', 'device', 'deviceType'].includes(type)\"\n          :name=\"name\"\n          :type=\"type\"\n        />\n        <div\n          v-else\n          class=\"w-full truncate\"\n        >\n          {{ formatName(name, type) || $t('dashboard.none') }}\n        </div>\n      </TooltipTrigger>\n      <TooltipContent v-if=\"name\">\n        <p>\n          {{ formatName(name, type) }}\n        </p>\n      </TooltipContent>\n    </Tooltip>\n  </TooltipProvider>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/name/Referer.vue",
    "content": "<script setup lang=\"ts\">\ndefineProps<{\n  name?: string\n}>()\n</script>\n\n<template>\n  <a\n    :href=\"`http://${name}`\"\n    target=\"_blank\"\n    rel=\"noopener noreferrer\"\n    class=\"block w-full truncate\"\n  >\n    <Avatar\n      class=\"float-left h-5 w-5 p-0.5\"\n    >\n      <AvatarImage\n        :src=\"`https://unavatar.webp.se/${name}?fallback=false`\"\n        :alt=\"name\"\n        loading=\"lazy\"\n      />\n      <AvatarFallback>\n        <img\n          src=\"/icon.png\"\n          :alt=\"name\"\n          loading=\"lazy\"\n        >\n      </AvatarFallback>\n    </Avatar>\n    <span>{{ name }}</span>\n  </a>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/analysis/metrics/name/Slug.vue",
    "content": "<script setup lang=\"ts\">\ndefineProps<{\n  name?: string\n}>()\n</script>\n\n<template>\n  <NuxtLink\n    :to=\"`/dashboard/link?slug=${name}`\"\n    class=\"inline-flex w-full items-center justify-start\"\n  >\n    <span class=\"w-full truncate\">{{ name }}</span>\n  </NuxtLink>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/Delete.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\nimport { toast } from 'vue-sonner'\n\nconst props = defineProps<{\n  link: Link\n}>()\n\nconst { t } = useI18n()\nconst linksStore = useDashboardLinksStore()\n\nasync function deleteLink() {\n  try {\n    await useAPI('/api/link/delete', {\n      method: 'POST',\n      body: {\n        slug: props.link.slug,\n      },\n    })\n    linksStore.notifyLinkUpdate(props.link, 'delete')\n    toast(t('links.delete_success'))\n  }\n  catch (error) {\n    console.error(error)\n    toast.error(t('links.delete_failed'))\n  }\n}\n</script>\n\n<template>\n  <AlertDialog>\n    <AlertDialogTrigger as-child>\n      <slot />\n    </AlertDialogTrigger>\n    <AlertDialogContent\n      class=\"\n        max-h-[95svh] max-w-[95svw] grid-rows-[auto_minmax(0,1fr)_auto]\n        md:max-w-lg\n      \"\n    >\n      <AlertDialogHeader>\n        <AlertDialogTitle>{{ $t('links.delete_confirm_title') }}</AlertDialogTitle>\n        <AlertDialogDescription>\n          {{ $t('links.delete_confirm_desc') }}\n        </AlertDialogDescription>\n      </AlertDialogHeader>\n      <AlertDialogFooter>\n        <AlertDialogCancel>{{ $t('common.cancel') }}</AlertDialogCancel>\n        <AlertDialogAction @click=\"deleteLink\">\n          {{ $t('common.continue') }}\n        </AlertDialogAction>\n      </AlertDialogFooter>\n    </AlertDialogContent>\n  </AlertDialog>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/Index.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CounterData, Link, LinkListResponse, LinkUpdateType } from '@/types'\nimport { useInfiniteScroll } from '@vueuse/core'\nimport { Loader } from 'lucide-vue-next'\n\nconst linksStore = useDashboardLinksStore()\n\nconst links = ref<Link[]>([])\nconst listComplete = ref(false)\nconst listError = ref(false)\nconst limit = 24\nlet cursor = ''\n\nconst countersMap = ref<Record<string, CounterData>>({})\nprovide('linksCountersMap', countersMap)\n\nconst pendingIds = new Set<string>()\nconst defaultCounters: CounterData = Object.freeze({ visits: 0, visitors: 0, referers: 0 })\n\nasync function fetchCounters(ids: string[]) {\n  if (!ids.length)\n    return\n  ids.forEach(id => pendingIds.add(id))\n  try {\n    const result = await useAPI<{ data: (CounterData & { id: string })[] }>('/api/stats/counters', {\n      query: { id: ids.join(',') },\n    })\n    for (const item of result.data ?? []) {\n      countersMap.value[item.id] = {\n        visits: item.visits,\n        visitors: item.visitors,\n        referers: item.referers,\n      }\n    }\n  }\n  catch (error) {\n    console.error('Failed to fetch counters:', error)\n  }\n  finally {\n    for (const id of ids) {\n      if (!countersMap.value[id])\n        countersMap.value[id] = { ...defaultCounters }\n      pendingIds.delete(id)\n    }\n  }\n}\n\nconst scrollContainer = ref<HTMLElement | Window | null>(null)\n\nonMounted(() => {\n  scrollContainer.value = document.querySelector('.overflow-y-auto') as HTMLElement | null\n})\n\nconst displayedLinks = computed(() => {\n  const sorted = [...links.value]\n  switch (linksStore.sortBy) {\n    case 'newest':\n      return sorted.sort((a, b) => b.createdAt - a.createdAt)\n    case 'oldest':\n      return sorted.sort((a, b) => a.createdAt - b.createdAt)\n    case 'az':\n      return sorted.sort((a, b) => a.slug.localeCompare(b.slug))\n    case 'za':\n      return sorted.sort((a, b) => b.slug.localeCompare(a.slug))\n    default:\n      return sorted\n  }\n})\n\nasync function getLinks() {\n  try {\n    const data = await useAPI<LinkListResponse>('/api/link/list', {\n      query: {\n        limit,\n        cursor,\n      },\n    })\n    const newLinks = data.links.filter(Boolean)\n    links.value = links.value.concat(newLinks)\n    cursor = data.cursor\n    listComplete.value = data.list_complete\n    listError.value = false\n\n    const ids = newLinks.map(l => l.id).filter(id => !countersMap.value[id] && !pendingIds.has(id))\n    fetchCounters(ids)\n  }\n  catch (error) {\n    console.error(error)\n    listError.value = true\n  }\n}\n\nconst { isLoading } = useInfiniteScroll(\n  scrollContainer as unknown as Ref<HTMLElement | null>,\n  getLinks,\n  {\n    distance: 150,\n    interval: 1000,\n    canLoadMore: () => {\n      return !listError.value && !listComplete.value\n    },\n  },\n)\n\nfunction updateLinkList(link: Link, type: LinkUpdateType) {\n  if (type === 'edit') {\n    const index = links.value.findIndex(l => l.id === link.id)\n    links.value[index] = link\n  }\n  else if (type === 'delete') {\n    const index = links.value.findIndex(l => l.id === link.id)\n    links.value.splice(index, 1)\n  }\n  else {\n    links.value.unshift(link)\n    linksStore.sortBy = 'newest'\n  }\n}\n\nlinksStore.onLinkUpdate(({ link, type }) => {\n  updateLinkList(link, type)\n})\n</script>\n\n<template>\n  <section\n    class=\"\n      grid grid-cols-1 gap-4\n      md:grid-cols-2\n      lg:grid-cols-3\n    \"\n  >\n    <DashboardLinksLink\n      v-for=\"link in displayedLinks\"\n      :key=\"link.id\"\n      :link=\"link\"\n    />\n  </section>\n  <div\n    v-if=\"isLoading\"\n    class=\"flex items-center justify-center\"\n  >\n    <Loader class=\"animate-spin\" />\n  </div>\n  <div\n    v-if=\"!isLoading && listComplete\"\n    class=\"flex items-center justify-center text-sm\"\n  >\n    {{ $t('links.no_more') }}\n  </div>\n  <div\n    v-if=\"listError\"\n    class=\"flex items-center justify-center text-sm\"\n  >\n    {{ $t('links.load_failed') }}\n    <Button variant=\"link\" @click=\"getLinks\">\n      {{ $t('common.try_again') }}\n    </Button>\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/Link.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CounterData, Link } from '@/types'\nimport { useClipboard } from '@vueuse/core'\nimport { CalendarPlus2, Copy, CopyCheck, Eraser, Flame, Hourglass, Link as LinkIcon, MousePointerClick, QrCode, ShieldAlert, SquareChevronDown, SquarePen, Users } from 'lucide-vue-next'\nimport { parseURL } from 'ufo'\nimport { toast } from 'vue-sonner'\n\nconst props = defineProps<{\n  link: Link\n}>()\n\nconst { t } = useI18n()\nconst editPopoverOpen = ref(false)\n\nconst countersMap = inject<Ref<Record<string, CounterData>> | undefined>('linksCountersMap', undefined)\nconst counters = computed(() => countersMap?.value?.[props.link.id])\n\nconst requestUrl = useRequestURL()\nconst host = requestUrl.host\nconst origin = requestUrl.origin\n\nfunction getLinkHost(url: string): string | undefined {\n  const { host } = parseURL(url)\n  return host\n}\n\nconst shortLink = computed(() => `${origin}/${props.link.slug}`)\nconst linkIcon = computed(() => `https://unavatar.webp.se/${getLinkHost(props.link.url)}?fallback=https://sink.cool/icon.png`)\n\nconst { copy, copied } = useClipboard({ source: shortLink.value, copiedDuring: 400 })\n\nfunction copyLink() {\n  copy(shortLink.value)\n  toast(t('links.copy_success'))\n}\n</script>\n\n<template>\n  <Card class=\"h-full\">\n    <CardContent class=\"flex-1\">\n      <NuxtLink\n        class=\"flex h-full flex-col space-y-3\"\n        :to=\"`/dashboard/link?slug=${link.slug}`\"\n      >\n        <div class=\"flex items-center justify-center space-x-3\">\n          <Avatar>\n            <AvatarImage\n              :src=\"linkIcon\"\n              :alt=\"link.slug\"\n              loading=\"lazy\"\n            />\n            <AvatarFallback>\n              <img\n                src=\"/icon.png\"\n                :alt=\"link.slug\"\n                loading=\"lazy\"\n              >\n            </AvatarFallback>\n          </Avatar>\n\n          <div class=\"flex-1 overflow-hidden\">\n            <div class=\"flex items-center\">\n              <div class=\"truncate leading-5 font-bold\">\n                {{ host }}/{{ link.slug }}\n              </div>\n              <Badge\n                v-if=\"link.unsafe\" variant=\"destructive\" class=\"ml-1 shrink-0\"\n              >\n                <ShieldAlert class=\"h-3 w-3\" />\n              </Badge>\n\n              <Button\n                v-if=\"copied\"\n                variant=\"ghost\"\n                size=\"icon\"\n                class=\"ml-1 h-auto w-auto p-0\"\n                aria-label=\"Link copied\"\n                @click.prevent\n              >\n                <CopyCheck class=\"h-4 w-4 shrink-0\" />\n              </Button>\n              <Button\n                v-else\n                variant=\"ghost\"\n                size=\"icon\"\n                class=\"ml-1 h-auto w-auto p-0\"\n                aria-label=\"Copy link\"\n                @click.prevent=\"copyLink\"\n              >\n                <Copy class=\"h-4 w-4 shrink-0\" />\n              </Button>\n            </div>\n\n            <TooltipProvider>\n              <Tooltip>\n                <TooltipTrigger as-child>\n                  <p class=\"truncate text-sm\">\n                    {{ link.comment || link.title || link.description }}\n                  </p>\n                </TooltipTrigger>\n                <TooltipContent class=\"max-w-[90svw] break-all\">\n                  <p>{{ link.comment || link.title || link.description }}</p>\n                </TooltipContent>\n              </Tooltip>\n            </TooltipProvider>\n          </div>\n\n          <a\n            :href=\"link.url\"\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            aria-label=\"Open original link\"\n            @click.stop\n          >\n            <LinkIcon class=\"h-5 w-5\" />\n          </a>\n\n          <Popover>\n            <PopoverTrigger aria-label=\"Show QR code\">\n              <QrCode\n                class=\"h-5 w-5\"\n                @click.prevent\n              />\n            </PopoverTrigger>\n            <PopoverContent>\n              <DashboardLinksQRCode\n                :data=\"shortLink\"\n                :image=\"linkIcon\"\n              />\n            </PopoverContent>\n          </Popover>\n\n          <Popover v-model:open=\"editPopoverOpen\">\n            <PopoverTrigger aria-label=\"More actions\">\n              <SquareChevronDown\n                class=\"h-5 w-5\"\n                @click.prevent\n              />\n            </PopoverTrigger>\n            <PopoverContent\n              class=\"w-auto p-0\"\n              :hide-when-detached=\"false\"\n            >\n              <DashboardLinksEditor\n                :link=\"link\"\n              >\n                <div\n                  class=\"\n                    flex cursor-pointer items-center rounded-sm px-2 py-1.5\n                    text-sm outline-hidden select-none\n                    hover:bg-accent hover:text-accent-foreground\n                  \"\n                >\n                  <SquarePen\n                    aria-hidden=\"true\"\n                    class=\"mr-2 h-5 w-5\"\n                  />\n                  {{ $t('common.edit') }}\n                </div>\n              </DashboardLinksEditor>\n\n              <Separator />\n\n              <DashboardLinksDelete\n                :link=\"link\"\n              >\n                <div\n                  class=\"\n                    flex cursor-pointer items-center rounded-sm px-2 py-1.5\n                    text-sm outline-hidden select-none\n                    hover:bg-accent hover:text-accent-foreground\n                  \"\n                >\n                  <Eraser\n                    aria-hidden=\"true\"\n                    class=\"mr-2 h-5 w-5\"\n                  /> {{ $t('common.delete') }}\n                </div>\n              </DashboardLinksDelete>\n            </PopoverContent>\n          </Popover>\n        </div>\n        <div class=\"mt-auto flex flex-col space-y-3\">\n          <div class=\"flex h-5 w-full space-x-2 text-sm\">\n            <TooltipProvider>\n              <Tooltip>\n                <TooltipTrigger as-child>\n                  <span\n                    class=\"inline-flex items-center leading-5 whitespace-nowrap\"\n                  ><CalendarPlus2 aria-hidden=\"true\" class=\"mr-1 h-4 w-4\" /> {{ shortDate(link.createdAt) }}</span>\n                </TooltipTrigger>\n                <TooltipContent>\n                  <p>{{ $t('links.created_at') }}: {{ longDate(link.createdAt) }}</p>\n                  <p>{{ $t('links.updated_at') }}: {{ longDate(link.updatedAt) }}</p>\n                </TooltipContent>\n              </Tooltip>\n            </TooltipProvider>\n            <template v-if=\"link.expiration\">\n              <Separator orientation=\"vertical\" />\n              <TooltipProvider>\n                <Tooltip>\n                  <TooltipTrigger as-child>\n                    <span\n                      class=\"\n                        inline-flex items-center leading-5 whitespace-nowrap\n                      \"\n                    ><Hourglass aria-hidden=\"true\" class=\"mr-1 h-4 w-4\" /> {{ shortDate(link.expiration) }}</span>\n                  </TooltipTrigger>\n                  <TooltipContent>\n                    <p>{{ $t('links.expires_at') }}: {{ longDate(link.expiration) }}</p>\n                  </TooltipContent>\n                </Tooltip>\n              </TooltipProvider>\n            </template>\n            <Separator orientation=\"vertical\" />\n            <span class=\"truncate\">{{ link.url }}</span>\n          </div>\n          <div\n            v-if=\"countersMap\" class=\"flex h-5 w-full space-x-2 text-sm\"\n          >\n            <template v-if=\"counters\">\n              <Badge variant=\"secondary\">\n                <MousePointerClick aria-hidden=\"true\" class=\"h-3.5 w-3.5\" />\n                {{ counters.visits }}\n              </Badge>\n              <Badge variant=\"secondary\">\n                <Users aria-hidden=\"true\" class=\"h-3.5 w-3.5\" />\n                {{ counters.visitors }}\n              </Badge>\n              <Badge variant=\"secondary\">\n                <Flame aria-hidden=\"true\" class=\"h-3.5 w-3.5\" />\n                {{ counters.referers }}\n              </Badge>\n            </template>\n            <template v-else>\n              <Skeleton class=\"h-5 w-full rounded-full bg-secondary\" />\n            </template>\n          </div>\n        </div>\n      </NuxtLink>\n    </CardContent>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/QRCode.vue",
    "content": "<script setup lang=\"ts\">\nimport { Download } from 'lucide-vue-next'\nimport QRCodeStyling from 'qr-code-styling'\n\nconst props = withDefaults(defineProps<{\n  data: string\n  image?: string\n}>(), {\n  image: '',\n})\nconst color = ref('#000000')\nconst options = {\n  width: 256,\n  height: 256,\n  data: props.data,\n  type: 'svg' as const,\n  margin: 10,\n  qrOptions: { typeNumber: 0 as const, mode: 'Byte' as const, errorCorrectionLevel: 'Q' as const },\n  imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 2 },\n  dotsOptions: { type: 'dots' as const, color: '#000000' },\n  backgroundOptions: { color: '#ffffff' },\n  image: props.image,\n  dotsOptionsHelper: {\n    colorType: { single: true, gradient: false },\n    gradient: {\n      linear: true,\n      radial: false,\n      color1: '#6a1a4c',\n      color2: '#6a1a4c',\n      rotation: '0',\n    },\n  },\n  cornersSquareOptions: { type: 'extra-rounded' as const, color: '#000000' },\n  cornersSquareOptionsHelper: {\n    colorType: { single: true, gradient: false },\n    gradient: {\n      linear: true,\n      radial: false,\n      color1: '#000000',\n      color2: '#000000',\n      rotation: '0',\n    },\n  },\n  cornersDotOptions: { type: 'dot' as const, color: '#000000' },\n  cornersDotOptionsHelper: {\n    colorType: { single: true, gradient: false },\n    gradient: {\n      linear: true,\n      radial: false,\n      color1: '#000000',\n      color2: '#000000',\n      rotation: '0',\n    },\n  },\n  backgroundOptionsHelper: {\n    colorType: { single: true, gradient: false },\n    gradient: {\n      linear: true,\n      radial: false,\n      color1: '#ffffff',\n      color2: '#ffffff',\n      rotation: '0',\n    },\n  },\n}\n\nconst qrCode = new QRCodeStyling(options)\nconst qrCodeEl = useTemplateRef<HTMLElement>('qrCodeEl')\n\nfunction updateColor(newColor: string) {\n  qrCode.update({\n    dotsOptions: { type: 'dots' as const, color: newColor },\n    cornersSquareOptions: { type: 'extra-rounded' as const, color: newColor },\n    cornersDotOptions: { type: 'dot' as const, color: newColor },\n  })\n}\n\nwatch(color, (newColor) => {\n  updateColor(newColor)\n})\n\nfunction downloadQRCode() {\n  const slug = props.data.split('/').pop()\n  qrCode.download({\n    extension: 'png',\n    name: `qr_${slug}`,\n  })\n}\n\nonMounted(() => {\n  if (qrCodeEl.value) {\n    qrCode.append(qrCodeEl.value as unknown as HTMLElement)\n  }\n})\n</script>\n\n<template>\n  <div class=\"flex flex-col items-center gap-4\">\n    <div\n      ref=\"qrCodeEl\"\n      :data-text=\"data\"\n      class=\"rounded-lg bg-white p-1\"\n    />\n    <div class=\"flex items-center gap-4\">\n      <div class=\"relative flex items-center\">\n        <div\n          class=\"\n            h-8 w-8 cursor-pointer overflow-hidden rounded-full border\n            border-gray-300\n            dark:border-gray-600\n          \"\n          :style=\"{ backgroundColor: color }\"\n          :title=\"$t('links.change_qr_color')\"\n        >\n          <input\n            v-model=\"color\"\n            type=\"color\"\n            class=\"absolute inset-0 h-full w-full cursor-pointer opacity-0\"\n            :title=\"$t('links.change_qr_color')\"\n          >\n        </div>\n      </div>\n      <Button\n        variant=\"outline\"\n        size=\"sm\"\n        @click=\"downloadQRCode\"\n      >\n        <Download class=\"mr-2 h-4 w-4\" />\n        {{ $t('links.download_qr_code') }}\n      </Button>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/Search.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\nimport { createReusableTemplate, useMagicKeys, useMediaQuery } from '@vueuse/core'\nimport { useFuse } from '@vueuse/integrations/useFuse'\n\ndefineOptions({\n  inheritAttrs: false,\n})\nconst [TriggerTemplate, TriggerComponent] = createReusableTemplate()\nconst [SearchTemplate, SearchComponent] = createReusableTemplate()\n\nconst isDesktop = useMediaQuery('(min-width: 640px)')\n\nconst router = useRouter()\n\nconst isOpen = ref(false)\nconst searchTerm = ref('')\nconst links = ref<Link[]>([])\n\nconst { results: filteredLinks } = useFuse(searchTerm, links, {\n  fuseOptions: {\n    keys: ['slug', 'url', 'comment'],\n  },\n  resultLimit: 20,\n})\n\nconst { Meta_K, Ctrl_K } = useMagicKeys({\n  passive: false,\n  onEventFired(e) {\n    if (e.key === 'k' && (e.metaKey || e.ctrlKey))\n      e.preventDefault()\n  },\n})\n\nfunction sanitizeSlotAttrs(attrs?: Record<string, unknown>) {\n  if (!attrs)\n    return {}\n\n  return Object.fromEntries(\n    Object.entries(attrs).filter(([key]) => !key.startsWith('$')),\n  ) as Record<string, unknown>\n}\n\nwatch([Meta_K, Ctrl_K], (v) => {\n  if (v[0] || v[1])\n    isOpen.value = true\n})\n\nfunction selectLink(link: Link | undefined) {\n  if (!link)\n    return\n  isOpen.value = false\n  router.push({\n    path: '/dashboard/link',\n    query: { slug: link.slug },\n  })\n}\n\nasync function getLinks() {\n  try {\n    links.value = await useAPI<Link[]>('/api/link/search')\n  }\n  catch (error) {\n    console.error(error)\n  }\n}\n\nonMounted(() => {\n  getLinks()\n})\n</script>\n\n<template>\n  <TriggerTemplate v-slot=\"attrs\">\n    <Button\n      v-bind=\"sanitizeSlotAttrs(attrs)\"\n      variant=\"outline\"\n      size=\"sm\"\n      class=\"\n        relative h-9 w-full justify-start bg-background text-muted-foreground\n        sm:w-32\n        md:w-48\n      \"\n    >\n      <span\n        class=\"\n          hidden\n          md:inline-flex\n        \"\n      >{{ $t('links.search_placeholder') }}</span>\n      <span\n        class=\"\n          inline-flex\n          md:hidden\n        \"\n      >{{ $t('common.search') }}</span>\n      <kbd\n        class=\"\n          pointer-events-none absolute top-2 right-[0.3rem] hidden h-5\n          items-center gap-1 rounded border bg-muted px-1.5 font-mono\n          text-[10px] font-medium opacity-100 select-none\n          sm:flex\n        \"\n      >\n        <span class=\"text-xs\">⌘</span>K\n      </kbd>\n    </Button>\n  </TriggerTemplate>\n  <SearchTemplate>\n    <Command class=\"h-auto\">\n      <CommandInput v-model=\"searchTerm\" :placeholder=\"$t('links.search_placeholder')\" autocomplete=\"off\" />\n    </Command>\n    <!-- disable command search -->\n    <Command class=\"flex-1\">\n      <CommandList\n        class=\"\n          max-h-none\n          sm:max-h-[300px]\n        \"\n      >\n        <CommandEmpty>\n          {{ $t('links.no_results') }}\n        </CommandEmpty>\n        <CommandGroup v-if=\"filteredLinks.length\" :heading=\"$t('links.group_title')\">\n          <CommandItem\n            v-for=\"link in filteredLinks\" :key=\"link.item?.id\" class=\"\n              cursor-pointer\n            \" :value=\"link.item\" @select=\"selectLink(link.item)\"\n          >\n            <div class=\"flex w-full gap-1\">\n              <div class=\"inline-flex flex-1 items-center gap-1 overflow-hidden\">\n                <div class=\"text-sm font-medium\">\n                  {{ link.item?.slug }}\n                </div>\n                <div class=\"flex-1 truncate text-xs text-muted-foreground\">\n                  ({{ link.item?.url }})\n                </div>\n              </div>\n              <Badge v-if=\"link.item?.comment\" variant=\"secondary\">\n                <div class=\"max-w-24 truncate\">\n                  {{ link.item?.comment }}\n                </div>\n              </Badge>\n            </div>\n          </CommandItem>\n        </CommandGroup>\n      </CommandList>\n    </Command>\n  </SearchTemplate>\n  <Dialog v-if=\"isDesktop\" v-model:open=\"isOpen\">\n    <DialogTrigger as-child>\n      <TriggerComponent />\n    </DialogTrigger>\n    <DialogContent class=\"gap-0 overflow-hidden p-0 shadow-lg\" :show-close-button=\"false\">\n      <DialogHeader class=\"sr-only\">\n        <DialogTitle>{{ $t('links.search_placeholder') }}</DialogTitle>\n      </DialogHeader>\n      <SearchComponent />\n    </DialogContent>\n  </Dialog>\n  <Drawer v-else v-model:open=\"isOpen\">\n    <DrawerTrigger as-child>\n      <TriggerComponent />\n    </DrawerTrigger>\n    <DrawerContent class=\"h-[500px] gap-0\">\n      <DrawerHeader class=\"sr-only\">\n        <DrawerTitle>{{ $t('links.search_placeholder') }}</DrawerTitle>\n      </DrawerHeader>\n      <SearchComponent />\n    </DrawerContent>\n  </Drawer>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/Sort.vue",
    "content": "<script setup lang=\"ts\">\nimport { ArrowUpDown } from 'lucide-vue-next'\n\nconst linksStore = useDashboardLinksStore()\n</script>\n\n<template>\n  <DropdownMenu>\n    <DropdownMenuTrigger as-child>\n      <Button variant=\"outline\">\n        <TooltipProvider>\n          <Tooltip :delay-duration=\"100\">\n            <TooltipTrigger class=\"flex items-center\">\n              <ArrowUpDown\n                class=\"\n                  h-4 w-4\n                  sm:mr-2\n                \"\n              />\n              <span\n                class=\"\n                  hidden\n                  sm:inline\n                \"\n              >\n                {{ $t(`links.sort.${linksStore.sortBy}`) }}\n              </span>\n            </TooltipTrigger>\n            <TooltipContent>\n              <p>{{ $t('links.sort.tip') }}</p>\n            </TooltipContent>\n          </Tooltip>\n        </TooltipProvider>\n      </Button>\n    </DropdownMenuTrigger>\n    <DropdownMenuContent>\n      <DropdownMenuItem @click=\"linksStore.sortBy = 'newest'\">\n        {{ $t('links.sort.newest') }}\n      </DropdownMenuItem>\n      <DropdownMenuItem @click=\"linksStore.sortBy = 'oldest'\">\n        {{ $t('links.sort.oldest') }}\n      </DropdownMenuItem>\n      <DropdownMenuItem @click=\"linksStore.sortBy = 'az'\">\n        {{ $t('links.sort.az') }}\n      </DropdownMenuItem>\n      <DropdownMenuItem @click=\"linksStore.sortBy = 'za'\">\n        {{ $t('links.sort.za') }}\n      </DropdownMenuItem>\n    </DropdownMenuContent>\n  </DropdownMenu>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/editor/Advanced.vue",
    "content": "<script setup lang=\"ts\">\nimport type { DateValue } from '@internationalized/date'\nimport type { Component } from 'vue'\nimport type { AnyFieldApi, LinkFormData } from '@/types'\nimport { today } from '@internationalized/date'\nimport { CalendarIcon } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  form: {\n    Field: Component\n    getFieldValue: (name: keyof LinkFormData) => LinkFormData[keyof LinkFormData]\n  }\n  validateOptionalUrl: (ctx: { value: string }) => string | undefined\n  isInvalid: (field: AnyFieldApi) => boolean\n  getAriaInvalid: (field: AnyFieldApi) => string | undefined\n  formatErrors: (errors: unknown[]) => string[]\n  currentSlug: string\n}>()\n\nconst datePickerOpen = ref(false)\n\n// Compute default open items based on existing values\nconst defaultOpenItems = computed(() => {\n  const items: string[] = []\n  if (props.form.getFieldValue('expiration')) {\n    items.push('expiration')\n  }\n  if (props.form.getFieldValue('title') || props.form.getFieldValue('description') || props.form.getFieldValue('image')) {\n    items.push('og')\n  }\n  if (props.form.getFieldValue('google') || props.form.getFieldValue('apple')) {\n    items.push('device')\n  }\n  if (props.form.getFieldValue('cloaking') || props.form.getFieldValue('redirectWithQuery') || props.form.getFieldValue('password') || props.form.getFieldValue('unsafe')) {\n    items.push('link_settings')\n  }\n  return items\n})\n</script>\n\n<template>\n  <Accordion type=\"multiple\" :default-value=\"defaultOpenItems\" class=\"w-full\">\n    <AccordionItem value=\"expiration\">\n      <AccordionTrigger>{{ $t('links.form.expiration') }}</AccordionTrigger>\n      <AccordionContent class=\"px-1\">\n        <props.form.Field v-slot=\"{ field }\" name=\"expiration\">\n          <Field :data-invalid=\"isInvalid(field)\">\n            <Popover v-model:open=\"datePickerOpen\">\n              <PopoverTrigger as-child>\n                <Button\n                  :id=\"field.name\"\n                  variant=\"outline\"\n                  :class=\"cn(\n                    'w-full justify-start text-left font-normal',\n                    !field.state.value && 'text-muted-foreground',\n                  )\"\n                >\n                  <CalendarIcon class=\"mr-2 h-4 w-4\" />\n                  {{\n                    field.state.value\n                      ? field.state.value.toDate(getTimeZone()).toLocaleDateString()\n                      : $t('links.form.pick_date')\n                  }}\n                </Button>\n              </PopoverTrigger>\n              <PopoverContent class=\"w-auto p-0\" align=\"start\">\n                <Calendar\n                  :model-value=\"field.state.value\"\n                  :default-placeholder=\"today(getTimeZone())\"\n                  layout=\"month-and-year\"\n                  initial-focus\n                  @update:model-value=\"(v: DateValue | undefined) => {\n                    field.handleChange(v)\n                    datePickerOpen = false\n                  }\"\n                />\n              </PopoverContent>\n            </Popover>\n            <FieldError\n              v-if=\"isInvalid(field)\"\n              :errors=\"formatErrors(field.state.meta.errors)\"\n            />\n          </Field>\n        </props.form.Field>\n      </AccordionContent>\n    </AccordionItem>\n\n    <AccordionItem value=\"og\">\n      <AccordionTrigger>{{ $t('links.form.og_settings') }}</AccordionTrigger>\n      <AccordionContent class=\"px-1\">\n        <FieldGroup>\n          <props.form.Field v-slot=\"{ field }\" name=\"title\">\n            <Field>\n              <FieldLabel :for=\"field.name\">\n                {{ $t('links.form.og_title') }}\n              </FieldLabel>\n              <Input\n                :id=\"field.name\"\n                :name=\"field.name\"\n                :model-value=\"field.state.value\"\n                :placeholder=\"$t('links.form.og_title_placeholder')\"\n                @blur=\"field.handleBlur\"\n                @input=\"field.handleChange(($event.target as HTMLInputElement).value)\"\n              />\n            </Field>\n          </props.form.Field>\n\n          <props.form.Field v-slot=\"{ field }\" name=\"description\">\n            <Field>\n              <FieldLabel :for=\"field.name\">\n                {{ $t('links.form.og_description') }}\n              </FieldLabel>\n              <Textarea\n                :id=\"field.name\"\n                :name=\"field.name\"\n                :model-value=\"field.state.value\"\n                :placeholder=\"$t('links.form.og_description_placeholder')\"\n                @blur=\"field.handleBlur\"\n                @input=\"field.handleChange(($event.target as HTMLTextAreaElement).value)\"\n              />\n            </Field>\n          </props.form.Field>\n\n          <props.form.Field v-slot=\"{ field }\" name=\"image\">\n            <Field>\n              <FieldLabel :for=\"field.name\">\n                {{ $t('links.form.og_image') }}\n              </FieldLabel>\n              <DashboardLinksEditorImageUploader\n                :model-value=\"field.state.value\"\n                :slug=\"currentSlug\"\n                @update:model-value=\"field.handleChange($event || '')\"\n              />\n            </Field>\n          </props.form.Field>\n        </FieldGroup>\n      </AccordionContent>\n    </AccordionItem>\n\n    <AccordionItem value=\"link_settings\">\n      <AccordionTrigger>{{ $t('links.form.link_settings') }}</AccordionTrigger>\n      <AccordionContent class=\"px-1\">\n        <FieldGroup>\n          <props.form.Field v-slot=\"{ field }\" name=\"redirectWithQuery\">\n            <Field>\n              <div class=\"flex items-center justify-between\">\n                <div class=\"space-y-0.5\">\n                  <FieldLabel :for=\"field.name\">\n                    {{ $t('links.form.redirect_with_query_label') }}\n                  </FieldLabel>\n                  <p class=\"text-xs text-muted-foreground\">\n                    {{ $t('links.form.redirect_with_query_description') }}\n                  </p>\n                </div>\n                <Switch\n                  :id=\"field.name\"\n                  :model-value=\"field.state.value\"\n                  @update:model-value=\"field.handleChange\"\n                />\n              </div>\n            </Field>\n          </props.form.Field>\n\n          <props.form.Field v-slot=\"{ field }\" name=\"cloaking\">\n            <Field>\n              <div class=\"flex items-center justify-between\">\n                <div class=\"space-y-0.5\">\n                  <FieldLabel :for=\"field.name\">\n                    {{ $t('links.form.cloaking_label') }}\n                  </FieldLabel>\n                  <p class=\"text-xs text-muted-foreground\">\n                    {{ $t('links.form.cloaking_description') }}\n                  </p>\n                </div>\n                <Switch\n                  :id=\"field.name\"\n                  :model-value=\"field.state.value\"\n                  @update:model-value=\"field.handleChange\"\n                />\n              </div>\n            </Field>\n          </props.form.Field>\n\n          <props.form.Field v-slot=\"{ field }\" name=\"unsafe\">\n            <Field>\n              <div class=\"flex items-center justify-between\">\n                <div class=\"space-y-0.5\">\n                  <FieldLabel :for=\"field.name\">\n                    {{ $t('links.form.unsafe_label') }}\n                  </FieldLabel>\n                  <p class=\"text-xs text-muted-foreground\">\n                    {{ $t('links.form.unsafe_description') }}\n                  </p>\n                </div>\n                <Switch\n                  :id=\"field.name\"\n                  :model-value=\"field.state.value\"\n                  @update:model-value=\"field.handleChange\"\n                />\n              </div>\n            </Field>\n          </props.form.Field>\n\n          <props.form.Field v-slot=\"{ field }\" name=\"password\">\n            <Field>\n              <FieldLabel :for=\"field.name\">\n                {{ $t('links.form.password_label') }}\n              </FieldLabel>\n              <p class=\"text-xs text-muted-foreground\">\n                {{ $t('links.form.password_description') }}\n              </p>\n              <Input\n                :id=\"field.name\"\n                :name=\"field.name\"\n                :model-value=\"field.state.value\"\n                :placeholder=\"$t('links.form.password_placeholder')\"\n                autocomplete=\"off\"\n                class=\"mt-1.5\"\n                @blur=\"field.handleBlur\"\n                @input=\"field.handleChange(($event.target as HTMLInputElement).value)\"\n              />\n            </Field>\n          </props.form.Field>\n        </FieldGroup>\n      </AccordionContent>\n    </AccordionItem>\n\n    <AccordionItem value=\"device\">\n      <AccordionTrigger>{{ $t('links.form.device_redirect') }}</AccordionTrigger>\n      <AccordionContent class=\"px-1\">\n        <FieldGroup>\n          <props.form.Field\n            v-slot=\"{ field }\"\n            name=\"google\"\n            :validators=\"{ onBlur: validateOptionalUrl }\"\n          >\n            <Field :data-invalid=\"isInvalid(field)\">\n              <FieldLabel :for=\"field.name\">\n                {{ $t('links.form.google_play') }}\n              </FieldLabel>\n              <Input\n                :id=\"field.name\"\n                :name=\"field.name\"\n                :model-value=\"field.state.value\"\n                :aria-invalid=\"getAriaInvalid(field)\"\n                placeholder=\"https://play.google.com/store/apps/…\"\n                autocomplete=\"off\"\n                @blur=\"field.handleBlur\"\n                @input=\"field.handleChange(($event.target as HTMLInputElement).value)\"\n              />\n              <FieldError\n                v-if=\"isInvalid(field)\"\n                :errors=\"formatErrors(field.state.meta.errors)\"\n              />\n            </Field>\n          </props.form.Field>\n\n          <props.form.Field\n            v-slot=\"{ field }\"\n            name=\"apple\"\n            :validators=\"{ onBlur: validateOptionalUrl }\"\n          >\n            <Field :data-invalid=\"isInvalid(field)\">\n              <FieldLabel :for=\"field.name\">\n                {{ $t('links.form.app_store') }}\n              </FieldLabel>\n              <Input\n                :id=\"field.name\"\n                :name=\"field.name\"\n                :model-value=\"field.state.value\"\n                :aria-invalid=\"getAriaInvalid(field)\"\n                placeholder=\"https://apps.apple.com/app/…\"\n                autocomplete=\"off\"\n                @blur=\"field.handleBlur\"\n                @input=\"field.handleChange(($event.target as HTMLInputElement).value)\"\n              />\n              <FieldError\n                v-if=\"isInvalid(field)\"\n                :errors=\"formatErrors(field.state.meta.errors)\"\n              />\n            </Field>\n          </props.form.Field>\n        </FieldGroup>\n      </AccordionContent>\n    </AccordionItem>\n  </Accordion>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/editor/Form.vue",
    "content": "<script setup lang=\"ts\">\nimport type { AnyFieldApi, Link, LinkFormData } from '@/types'\nimport { LinkSchema, nanoid } from '#shared/schemas/link'\nimport { useForm } from '@tanstack/vue-form'\nimport { Shuffle, Sparkles } from 'lucide-vue-next'\nimport { toast } from 'vue-sonner'\nimport { z } from 'zod'\n\nconst props = defineProps<{\n  link: Partial<Link>\n  isEdit: boolean\n}>()\n\nconst emit = defineEmits<{\n  success: [link: Link]\n}>()\n\nconst { t } = useI18n()\n\nconst urlValidator = LinkSchema.shape.url\nconst slugValidator = LinkSchema.shape.slug\nconst commentValidator = z.string().max(500).optional()\nconst optionalUrlValidator = z.string().trim().url().max(2048).optional().or(z.literal(''))\n\nconst generateSlug = nanoid()\n\nconst form = useForm({\n  defaultValues: {\n    url: props.link.url ?? '',\n    slug: props.link.slug ?? '',\n    comment: props.link.comment ?? '',\n    expiration: props.link.expiration\n      ? unix2date(props.link.expiration)\n      : undefined,\n    google: props.link.google ?? '',\n    apple: props.link.apple ?? '',\n    title: props.link.title ?? '',\n    description: props.link.description ?? '',\n    image: props.link.image ?? '',\n    cloaking: props.link.cloaking ?? false,\n    redirectWithQuery: props.link.redirectWithQuery ?? false,\n    password: props.link.password ?? '',\n    unsafe: props.link.unsafe ?? false,\n  } satisfies LinkFormData,\n  onSubmit: async ({ value }) => {\n    try {\n      const linkData = {\n        url: value.url,\n        slug: value.slug,\n        comment: value.comment || undefined,\n        expiration: value.expiration\n          ? date2unix(value.expiration, 'end')\n          : undefined,\n        google: value.google || undefined,\n        apple: value.apple || undefined,\n        title: value.title || undefined,\n        description: value.description || undefined,\n        image: value.image || undefined,\n        cloaking: value.cloaking,\n        redirectWithQuery: value.redirectWithQuery,\n        password: value.password || undefined,\n        unsafe: value.unsafe || undefined,\n      }\n      const { link: newLink } = await useAPI<{ link: Link }>(\n        props.isEdit ? '/api/link/edit' : '/api/link/create',\n        {\n          method: props.isEdit ? 'PUT' : 'POST',\n          body: linkData,\n        },\n      )\n      emit('success', newLink)\n      toast(props.isEdit ? t('links.update_success') : t('links.create_success'))\n    }\n    catch (error) {\n      console.error(error)\n      toast.error(props.isEdit ? t('links.update_failed') : t('links.create_failed'), {\n        description: error instanceof Error ? error.message : String(error),\n      })\n    }\n  },\n})\n\nfunction makeValidator<T>(schema: z.ZodSchema<T>) {\n  return ({ value }: { value: T }) => {\n    const result = schema.safeParse(value)\n    return result.success ? undefined : result.error.errors[0]?.message\n  }\n}\n\nconst validateUrl = makeValidator(urlValidator)\nconst validateSlug = makeValidator(slugValidator)\nconst validateComment = makeValidator(commentValidator)\nconst validateOptionalUrl = makeValidator(optionalUrlValidator)\n\nfunction isInvalid(field: AnyFieldApi) {\n  return field.state.meta.isTouched && !field.state.meta.isValid\n}\n\nfunction getAriaInvalid(field: AnyFieldApi) {\n  return isInvalid(field) ? 'true' : undefined\n}\n\nfunction formatErrors(errors: unknown[]): string[] {\n  return errors\n    .map((e) => {\n      if (typeof e === 'string')\n        return e\n      if (e && typeof e === 'object' && 'message' in e && typeof e.message === 'string')\n        return e.message\n      return null\n    })\n    .filter((m): m is string => m !== null)\n}\n\nfunction randomSlug() {\n  form.setFieldValue('slug', generateSlug())\n}\n\nconst aiSlugPending = ref(false)\nasync function aiSlug() {\n  const url = form.getFieldValue('url')\n  if (!url)\n    return\n\n  aiSlugPending.value = true\n  try {\n    const result = await useAPI<{ slug: string }>('/api/link/ai', {\n      query: { url },\n    })\n    form.setFieldValue('slug', result.slug)\n  }\n  catch (error) {\n    console.error(error)\n    toast.error(t('links.ai_slug_failed'), {\n      description: error instanceof Error ? error.message : String(error),\n    })\n  }\n  finally {\n    aiSlugPending.value = false\n  }\n}\n\nconst currentSlug = form.useStore(state => state.values.slug || '')\n\nconst { previewMode } = useRuntimeConfig().public\n\ndefineExpose({ randomSlug })\n</script>\n\n<template>\n  <form\n    id=\"link-editor-form\"\n    class=\"w-full space-y-4 px-1\"\n    @submit.prevent=\"form.handleSubmit\"\n  >\n    <p\n      v-if=\"previewMode\"\n      class=\"text-sm text-muted-foreground\"\n    >\n      {{ $t('links.preview_mode_tip') }}\n    </p>\n\n    <FieldGroup>\n      <form.Field\n        v-slot=\"{ field }\"\n        name=\"url\"\n        :validators=\"{ onBlur: validateUrl }\"\n      >\n        <Field :data-invalid=\"isInvalid(field)\">\n          <FieldLabel :for=\"field.name\">\n            {{ $t('links.form.url') }}\n          </FieldLabel>\n          <Input\n            :id=\"field.name\"\n            :name=\"field.name\"\n            :model-value=\"field.state.value\"\n            :aria-invalid=\"getAriaInvalid(field)\"\n            placeholder=\"https://example.com\"\n            autocomplete=\"url\"\n            @blur=\"field.handleBlur\"\n            @input=\"field.handleChange(($event.target as HTMLInputElement).value)\"\n          />\n          <FieldError\n            v-if=\"isInvalid(field)\"\n            :errors=\"formatErrors(field.state.meta.errors)\"\n          />\n        </Field>\n      </form.Field>\n\n      <form.Field\n        v-slot=\"{ field }\"\n        name=\"slug\"\n        :validators=\"{ onBlur: validateSlug }\"\n      >\n        <Field :data-invalid=\"isInvalid(field)\">\n          <div class=\"flex items-center justify-between\">\n            <FieldLabel :for=\"field.name\">\n              {{ $t('links.form.slug') }}\n            </FieldLabel>\n            <div v-if=\"!isEdit\" class=\"flex space-x-3\">\n              <Button\n                variant=\"ghost\"\n                size=\"icon\"\n                class=\"h-auto w-auto p-0\"\n                aria-label=\"Generate random slug\"\n                @click=\"randomSlug\"\n              >\n                <Shuffle class=\"h-4 w-4\" />\n              </Button>\n              <Button\n                variant=\"ghost\"\n                size=\"icon\"\n                class=\"h-auto w-auto p-0\"\n                aria-label=\"Generate AI slug\"\n                :disabled=\"aiSlugPending\"\n                @click=\"aiSlug\"\n              >\n                <Sparkles\n                  class=\"h-4 w-4\"\n                  :class=\"{ 'animate-bounce': aiSlugPending }\"\n                />\n              </Button>\n            </div>\n          </div>\n          <Input\n            :id=\"field.name\"\n            :name=\"field.name\"\n            :model-value=\"field.state.value\"\n            :disabled=\"isEdit\"\n            :aria-invalid=\"getAriaInvalid(field)\"\n            placeholder=\"my-short-link\"\n            autocomplete=\"off\"\n            @blur=\"field.handleBlur\"\n            @input=\"field.handleChange(($event.target as HTMLInputElement).value)\"\n          />\n          <FieldError\n            v-if=\"isInvalid(field)\"\n            :errors=\"formatErrors(field.state.meta.errors)\"\n          />\n        </Field>\n      </form.Field>\n\n      <form.Field\n        v-slot=\"{ field }\"\n        name=\"comment\"\n        :validators=\"{ onBlur: validateComment }\"\n      >\n        <Field :data-invalid=\"isInvalid(field)\">\n          <FieldLabel :for=\"field.name\">\n            {{ $t('links.form.comment') }}\n          </FieldLabel>\n          <Textarea\n            :id=\"field.name\"\n            :name=\"field.name\"\n            :model-value=\"field.state.value\"\n            :aria-invalid=\"getAriaInvalid(field)\"\n            @blur=\"field.handleBlur\"\n            @input=\"field.handleChange(($event.target as HTMLTextAreaElement).value)\"\n          />\n          <FieldError\n            v-if=\"isInvalid(field)\"\n            :errors=\"formatErrors(field.state.meta.errors)\"\n          />\n        </Field>\n      </form.Field>\n    </FieldGroup>\n\n    <DashboardLinksEditorAdvanced\n      :form=\"form\"\n      :validate-optional-url=\"validateOptionalUrl\"\n      :is-invalid=\"isInvalid\"\n      :get-aria-invalid=\"getAriaInvalid\"\n      :format-errors=\"formatErrors\"\n      :current-slug=\"currentSlug\"\n    />\n  </form>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/editor/ImageUploader.vue",
    "content": "<script setup lang=\"ts\">\nimport { ImagePlus, Loader2, X } from 'lucide-vue-next'\nimport { toast } from 'vue-sonner'\nimport { IMAGE_ALLOWED_TYPES, IMAGE_MAX_SIZE } from '@/utils/image'\n\nconst props = defineProps<{\n  slug: string\n}>()\n\nconst imageUrl = defineModel<string>()\n\nconst { t } = useI18n()\nconst canUpload = computed(() => !!props.slug?.trim())\nconst uploading = ref(false)\nconst dragOver = ref(false)\nconst fileInput = useTemplateRef<HTMLInputElement>('fileInput')\n\nasync function handleFile(file: File) {\n  if (!canUpload.value) {\n    toast.error(t('links.form.slug_required'))\n    return\n  }\n\n  if (!IMAGE_ALLOWED_TYPES.includes(file.type)) {\n    toast.error(t('links.form.image_invalid_type'))\n    return\n  }\n\n  if (file.size > IMAGE_MAX_SIZE) {\n    toast.error(t('links.form.image_size_limit'))\n    return\n  }\n\n  uploading.value = true\n  try {\n    const formData = new FormData()\n    formData.append('file', file)\n    formData.append('slug', props.slug)\n\n    const result = await useAPI<{ url: string }>('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n\n    imageUrl.value = result.url\n    toast.success(t('links.form.image_upload_success'))\n  }\n  catch (error) {\n    console.error(error)\n    toast.error(t('links.form.image_upload_failed'))\n  }\n  finally {\n    uploading.value = false\n  }\n}\n\nfunction onFileChange(event: Event) {\n  const target = event.target as HTMLInputElement\n  const file = target.files?.[0]\n  if (file) {\n    handleFile(file)\n  }\n  target.value = ''\n}\n\nfunction onDrop(event: DragEvent) {\n  dragOver.value = false\n  const file = event.dataTransfer?.files[0]\n  if (file) {\n    handleFile(file)\n  }\n}\n\nfunction onDragOver() {\n  dragOver.value = true\n}\n\nfunction onDragLeave() {\n  dragOver.value = false\n}\n\nfunction clearImage() {\n  imageUrl.value = undefined\n}\n\nfunction openFilePicker() {\n  fileInput.value?.click()\n}\n</script>\n\n<template>\n  <div class=\"space-y-2\">\n    <div\n      v-if=\"!imageUrl\"\n      class=\"\n        relative flex aspect-[1200/630] cursor-pointer items-center\n        justify-center rounded-md border-2 border-dashed transition-colors\n      \"\n      :class=\"[\n        !canUpload ? 'cursor-not-allowed opacity-50' : 'cursor-pointer',\n        dragOver ? 'border-primary bg-primary/5' : `\n          border-muted-foreground/25\n          hover:border-primary/50\n        `,\n      ]\"\n      @click=\"canUpload && openFilePicker()\"\n      @drop.prevent=\"onDrop\"\n      @dragover.prevent=\"onDragOver\"\n      @dragleave=\"onDragLeave\"\n    >\n      <div class=\"flex flex-col items-center gap-1 text-muted-foreground\">\n        <Loader2 v-if=\"uploading\" class=\"h-8 w-8 animate-spin\" />\n        <ImagePlus v-else class=\"h-8 w-8\" />\n        <span class=\"text-sm\">{{ canUpload ? $t('links.form.image_upload_hint') : $t('links.form.slug_required') }}</span>\n        <span v-if=\"canUpload\" class=\"text-xs opacity-60\">{{ $t('links.form.image_ratio_hint') }}</span>\n      </div>\n      <input\n        ref=\"fileInput\"\n        type=\"file\"\n        accept=\"image/jpeg,image/png,image/webp,image/gif\"\n        class=\"hidden\"\n        @change=\"onFileChange\"\n      >\n    </div>\n\n    <div v-else class=\"relative aspect-[1200/630]\">\n      <img\n        :src=\"imageUrl\"\n        alt=\"Preview\"\n        class=\"aspect-[1200/630] w-full rounded-md object-cover\"\n      >\n      <Button\n        type=\"button\"\n        variant=\"destructive\"\n        size=\"icon\"\n        class=\"absolute top-2 right-2 h-6 w-6\"\n        @click=\"clearImage\"\n      >\n        <X class=\"h-4 w-4\" />\n      </Button>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/links/editor/index.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\n\nconst props = withDefaults(defineProps<{\n  link?: Partial<Link>\n}>(), {\n  link: () => ({}),\n})\n\nconst { t } = useI18n()\nconst linksStore = useDashboardLinksStore()\nconst dialogOpen = ref(false)\nconst isEdit = !!props.link.id\n\nconst formRef = ref<{ randomSlug: () => void } | null>(null)\n\nwatch(dialogOpen, (open) => {\n  if (open && !isEdit) {\n    nextTick(() => {\n      formRef.value?.randomSlug()\n    })\n  }\n})\n\nfunction handleSuccess(link: Link) {\n  dialogOpen.value = false\n  linksStore.notifyLinkUpdate(link, isEdit ? 'edit' : 'create')\n}\n\nfunction handleClose() {\n  dialogOpen.value = false\n}\n</script>\n\n<template>\n  <ResponsiveModal\n    v-model:open=\"dialogOpen\"\n    :title=\"isEdit ? t('links.edit') : t('links.create')\"\n  >\n    <template #trigger>\n      <slot>\n        <Button class=\"md:ml-2\" variant=\"outline\">\n          {{ $t('links.create') }}\n        </Button>\n      </slot>\n    </template>\n\n    <DashboardLinksEditorForm\n      ref=\"formRef\"\n      :link=\"link\"\n      :is-edit=\"isEdit\"\n      @success=\"handleSuccess\"\n    />\n\n    <template #footer>\n      <Button\n        type=\"button\"\n        variant=\"secondary\"\n        @click=\"handleClose\"\n      >\n        {{ $t('common.close') }}\n      </Button>\n      <Button type=\"submit\" form=\"link-editor-form\">\n        {{ $t('common.save') }}\n      </Button>\n    </template>\n  </ResponsiveModal>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/migrate/Backup.vue",
    "content": "<script setup lang=\"ts\">\nimport { CloudUpload, Loader } from 'lucide-vue-next'\nimport { toast } from 'vue-sonner'\n\nconst { t } = useI18n()\nconst isBackingUp = ref(false)\n\nasync function handleBackup() {\n  isBackingUp.value = true\n\n  try {\n    await useAPI('/api/backup', {\n      method: 'POST',\n    })\n\n    toast.success(t('migrate.backup.success'))\n  }\n  catch (error) {\n    toast.error(t('migrate.backup.failed'), {\n      description: error instanceof Error ? error.message : String(error),\n    })\n  }\n  finally {\n    isBackingUp.value = false\n  }\n}\n</script>\n\n<template>\n  <Card class=\"h-fit\">\n    <CardHeader>\n      <CardTitle>{{ $t('migrate.backup.title') }}</CardTitle>\n      <CardDescription>{{ $t('migrate.backup.description') }}</CardDescription>\n    </CardHeader>\n    <CardContent>\n      <Button :disabled=\"isBackingUp\" @click=\"handleBackup\">\n        <Loader v-if=\"isBackingUp\" class=\"mr-2 h-4 w-4 animate-spin\" />\n        <CloudUpload v-else class=\"mr-2 h-4 w-4\" />\n        <template v-if=\"isBackingUp\">\n          {{ $t('migrate.backup.backing_up') }}\n        </template>\n        <template v-else>\n          {{ $t('migrate.backup.button') }}\n        </template>\n      </Button>\n    </CardContent>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/migrate/Export.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\nimport { Download, Loader } from 'lucide-vue-next'\nimport { toast } from 'vue-sonner'\n\ninterface ExportResponse {\n  version: string\n  exportedAt: string\n  count: number\n  links: Link[]\n  cursor?: string\n  list_complete: boolean\n}\n\nconst { t } = useI18n()\nconst isExporting = ref(false)\nconst exportedCount = ref(0)\n\nconst sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))\n\nasync function handleExport() {\n  isExporting.value = true\n  exportedCount.value = 0\n\n  try {\n    const allLinks: Link[] = []\n    let cursor: string | undefined\n    let listComplete = false\n\n    while (!listComplete) {\n      const params = cursor ? `?cursor=${encodeURIComponent(cursor)}` : ''\n      const data = await useAPI<ExportResponse>(`/api/link/export${params}`)\n\n      allLinks.push(...data.links)\n      exportedCount.value = allLinks.length\n      listComplete = data.list_complete\n      cursor = data.cursor\n\n      if (!listComplete) {\n        await sleep(1000)\n      }\n    }\n\n    const exportData = {\n      version: '1.0',\n      exportedAt: new Date().toISOString(),\n      count: allLinks.length,\n      links: allLinks,\n    }\n\n    saveAsJson(exportData, `sink-links-${Date.now()}.json`)\n\n    toast.success(t('migrate.export.success'))\n  }\n  catch (error) {\n    toast.error(t('migrate.export.failed'), {\n      description: error instanceof Error ? error.message : String(error),\n    })\n  }\n  finally {\n    isExporting.value = false\n    exportedCount.value = 0\n  }\n}\n</script>\n\n<template>\n  <Card class=\"h-fit\">\n    <CardHeader>\n      <CardTitle>{{ $t('migrate.export.title') }}</CardTitle>\n      <CardDescription>{{ $t('migrate.export.description') }}</CardDescription>\n    </CardHeader>\n    <CardContent>\n      <Button :disabled=\"isExporting\" @click=\"handleExport\">\n        <Loader v-if=\"isExporting\" class=\"mr-2 h-4 w-4 animate-spin\" />\n        <Download v-else class=\"mr-2 h-4 w-4\" />\n        <template v-if=\"isExporting && exportedCount > 0\">\n          {{ exportedCount }} {{ $t('migrate.export.total_links') }}…\n        </template>\n        <template v-else>\n          {{ $t('migrate.export.button') }}\n        </template>\n      </Button>\n    </CardContent>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/migrate/Import.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ImportData } from '#shared/schemas/import'\nimport { ImportDataSchema } from '#shared/schemas/import'\nimport { AlertCircle, CheckCircle, Download, SkipForward, Upload, XCircle } from 'lucide-vue-next'\nimport { toast } from 'vue-sonner'\n\ninterface ImportResultItem {\n  index: number\n  slug: string\n  url: string\n}\n\ninterface ImportResult {\n  success: number\n  skipped: number\n  failed: number\n  successItems: ImportResultItem[]\n  skippedItems: ImportResultItem[]\n  failedItems: (ImportResultItem & { reason: string })[]\n}\n\nconst { t } = useI18n()\nconst runtimeConfig = useRuntimeConfig()\nconst BATCH_SIZE = Math.floor(+runtimeConfig.public.kvBatchLimit / 2)\n\nconst fileInput = ref<HTMLInputElement | null>(null)\nconst selectedFile = ref<File | null>(null)\nconst parsedData = ref<ImportData | null>(null)\nconst parseError = ref<string | null>(null)\nconst validationErrors = ref<string[]>([])\n\nconst isImporting = ref(false)\nconst importProgress = ref(0)\nconst importResult = ref<ImportResult | null>(null)\n\nconst sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))\n\nfunction handleFileSelect(e: Event) {\n  const input = e.target as HTMLInputElement\n  if (input.files && input.files.length > 0) {\n    handleFile(input.files[0]!)\n  }\n}\n\nasync function handleFile(file: File) {\n  selectedFile.value = file\n  parsedData.value = null\n  parseError.value = null\n  validationErrors.value = []\n  importResult.value = null\n\n  if (!file.name.endsWith('.json')) {\n    parseError.value = t('migrate.import.errors.invalid_json')\n    return\n  }\n\n  try {\n    const text = await file.text()\n    const data = JSON.parse(text)\n\n    const result = ImportDataSchema.safeParse(data)\n\n    if (!result.success) {\n      const errors = result.error.errors.slice(0, 10).map((err) => {\n        const path = err.path.join('.')\n        return `${path}: ${err.message}`\n      })\n      if (result.error.errors.length > 10) {\n        errors.push(`... and ${result.error.errors.length - 10} more errors`)\n      }\n      validationErrors.value = errors\n      parseError.value = t('migrate.import.errors.invalid_format')\n      return\n    }\n\n    if (result.data.links.length === 0) {\n      parseError.value = t('migrate.import.errors.no_links')\n      return\n    }\n\n    parsedData.value = result.data\n  }\n  catch {\n    parseError.value = t('migrate.import.errors.parse_error')\n  }\n}\n\nasync function handleImport() {\n  if (!parsedData.value)\n    return\n\n  isImporting.value = true\n  importProgress.value = 0\n  importResult.value = null\n\n  const allLinks = parsedData.value.links\n  const totalBatches = Math.ceil(allLinks.length / BATCH_SIZE)\n  const result: ImportResult = {\n    success: 0,\n    skipped: 0,\n    failed: 0,\n    successItems: [],\n    skippedItems: [],\n    failedItems: [],\n  }\n\n  for (let i = 0; i < totalBatches; i++) {\n    const batchStart = i * BATCH_SIZE\n    const batchEnd = Math.min((i + 1) * BATCH_SIZE, allLinks.length)\n    const batch = allLinks.slice(batchStart, batchEnd)\n\n    try {\n      const batchData: ImportData = {\n        version: parsedData.value.version,\n        links: batch,\n      }\n      const batchResult = await useAPI<ImportResult>('/api/link/import', {\n        method: 'POST',\n        body: batchData,\n      })\n\n      result.success += batchResult.success\n      result.skipped += batchResult.skipped\n      result.failed += batchResult.failed\n      result.successItems.push(...batchResult.successItems.map(item => ({\n        ...item,\n        index: item.index + batchStart,\n      })))\n      result.skippedItems.push(...batchResult.skippedItems.map(item => ({\n        ...item,\n        index: item.index + batchStart,\n      })))\n      result.failedItems.push(...batchResult.failedItems.map(item => ({\n        ...item,\n        index: item.index + batchStart,\n      })))\n    }\n    catch (error) {\n      batch.forEach((link, idx) => {\n        result.failed++\n        result.failedItems.push({\n          index: batchStart + idx,\n          slug: link.slug,\n          url: link.url,\n          reason: error instanceof Error ? error.message : 'Batch import failed',\n        })\n      })\n    }\n\n    importProgress.value = Math.round(((i + 1) / totalBatches) * 100)\n\n    if (i < totalBatches - 1) {\n      await sleep(1000)\n    }\n  }\n\n  importResult.value = result\n  isImporting.value = false\n\n  if (result.success > 0) {\n    toast.success(t('migrate.import.result.success_message', { count: result.success }))\n  }\n}\n\nfunction downloadItems(\n  items: ImportResultItem[] | undefined,\n  filename: string,\n  transform?: (item: ImportResultItem, originalLink: unknown) => unknown,\n) {\n  if (!items || items.length === 0)\n    return\n\n  const links = items.map((item) => {\n    const originalLink = parsedData.value?.links[item.index]\n    return transform ? transform(item, originalLink) : { ...originalLink }\n  })\n\n  saveAsJson({\n    version: '1.0',\n    exportedAt: new Date().toISOString(),\n    count: links.length,\n    links,\n  }, `sink-import-${filename}-${Date.now()}.json`)\n}\n\nfunction downloadSuccessItems() {\n  downloadItems(importResult.value?.successItems, 'success')\n}\n\nfunction downloadSkippedItems() {\n  downloadItems(importResult.value?.skippedItems, 'skipped')\n}\n\nfunction downloadFailedItems() {\n  downloadItems(\n    importResult.value?.failedItems,\n    'failed',\n    (item, originalLink) => ({\n      ...(originalLink as object),\n      _importError: (item as ImportResultItem & { reason: string }).reason,\n    }),\n  )\n}\n\nfunction reset() {\n  selectedFile.value = null\n  parsedData.value = null\n  parseError.value = null\n  validationErrors.value = []\n  importResult.value = null\n  importProgress.value = 0\n  if (fileInput.value) {\n    fileInput.value.value = ''\n  }\n}\n</script>\n\n<template>\n  <Card>\n    <CardHeader>\n      <CardTitle>{{ $t('migrate.import.title') }}</CardTitle>\n      <CardDescription>{{ $t('migrate.import.description') }}</CardDescription>\n    </CardHeader>\n    <CardContent class=\"space-y-4\">\n      <div v-if=\"!parsedData && !parseError && !importResult\">\n        <Input\n          ref=\"fileInput\"\n          type=\"file\"\n          accept=\".json\"\n          class=\"cursor-pointer\"\n          @change=\"handleFileSelect\"\n        />\n      </div>\n\n      <div\n        v-if=\"parseError\" class=\"\n          rounded-lg border border-destructive/50 bg-destructive/10 p-4\n        \"\n      >\n        <div class=\"flex items-center gap-2 text-destructive\">\n          <AlertCircle class=\"h-5 w-5\" />\n          <span class=\"font-medium\">{{ parseError }}</span>\n        </div>\n        <div v-if=\"validationErrors.length > 0\" class=\"mt-2 space-y-1\">\n          <p\n            v-for=\"(error, index) in validationErrors\"\n            :key=\"index\"\n            class=\"font-mono text-sm text-destructive/80\"\n          >\n            {{ error }}\n          </p>\n        </div>\n        <Button variant=\"outline\" size=\"sm\" class=\"mt-3\" @click=\"reset\">\n          {{ $t('common.try_again') }}\n        </Button>\n      </div>\n\n      <div v-if=\"parsedData && !importResult\" class=\"space-y-4\">\n        <div class=\"rounded-lg border bg-muted/30 p-4\">\n          <div class=\"flex items-center justify-between\">\n            <div>\n              <p class=\"font-medium\">\n                {{ selectedFile?.name }}\n              </p>\n              <p class=\"text-sm text-muted-foreground\">\n                {{ parsedData.links.length }} {{ $t('migrate.import.links_found') }}\n              </p>\n            </div>\n            <Button variant=\"ghost\" size=\"sm\" @click=\"reset\">\n              <XCircle class=\"h-4 w-4\" />\n            </Button>\n          </div>\n        </div>\n\n        <div v-if=\"isImporting\" class=\"space-y-2\">\n          <div class=\"flex items-center justify-between text-sm\">\n            <span>{{ $t('migrate.import.importing') }}</span>\n            <span>{{ importProgress }}%</span>\n          </div>\n          <Progress :model-value=\"importProgress\" />\n        </div>\n\n        <Button\n          v-else\n          class=\"w-full\"\n          @click=\"handleImport\"\n        >\n          <Upload class=\"mr-2 h-4 w-4\" />\n          {{ $t('migrate.import.button') }}\n        </Button>\n      </div>\n\n      <div v-if=\"importResult\" class=\"space-y-4\">\n        <div class=\"rounded-lg border bg-muted/30 p-4\">\n          <h4 class=\"mb-3 font-medium\">\n            {{ $t('migrate.import.result.title') }}\n          </h4>\n          <div class=\"space-y-2\">\n            <div class=\"flex items-center gap-2 text-sm\">\n              <CheckCircle class=\"h-4 w-4 text-green-500\" />\n              <span>{{ $t('migrate.import.result.success') }}: {{ importResult.success }}</span>\n            </div>\n            <div class=\"flex items-center gap-2 text-sm\">\n              <SkipForward class=\"h-4 w-4 text-yellow-500\" />\n              <span>{{ $t('migrate.import.result.skipped') }}: {{ importResult.skipped }}</span>\n            </div>\n            <div class=\"flex items-center gap-2 text-sm\">\n              <XCircle class=\"h-4 w-4 text-red-500\" />\n              <span>{{ $t('migrate.import.result.failed') }}: {{ importResult.failed }}</span>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"flex flex-wrap gap-2\">\n          <Button variant=\"outline\" @click=\"reset\">\n            {{ $t('migrate.import.import_more') }}\n          </Button>\n          <Button\n            v-if=\"importResult.success > 0\"\n            variant=\"default\"\n            @click=\"downloadSuccessItems\"\n          >\n            <Download class=\"mr-2 h-4 w-4\" />\n            {{ $t('migrate.import.download_success') }}\n          </Button>\n          <Button\n            v-if=\"importResult.skipped > 0\"\n            variant=\"secondary\"\n            @click=\"downloadSkippedItems\"\n          >\n            <Download class=\"mr-2 h-4 w-4\" />\n            {{ $t('migrate.import.download_skipped') }}\n          </Button>\n          <Button\n            v-if=\"importResult.failed > 0\"\n            variant=\"destructive\"\n            @click=\"downloadFailedItems\"\n          >\n            <Download class=\"mr-2 h-4 w-4\" />\n            {{ $t('migrate.import.download_failed') }}\n          </Button>\n        </div>\n      </div>\n    </CardContent>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/migrate/Index.vue",
    "content": "<script setup lang=\"ts\">\n</script>\n\n<template>\n  <div\n    class=\"\n      grid gap-6\n      lg:grid-cols-2\n    \"\n  >\n    <DashboardMigrateExport />\n    <DashboardMigrateImport />\n    <DashboardMigrateBackup />\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/realtime/Chart.vue",
    "content": "<script setup lang=\"ts\">\nimport type { CounterData } from '@/types'\nimport NumberFlow from '@number-flow/vue'\nimport { watchDeep } from '@vueuse/core'\nimport { MousePointerClick } from 'lucide-vue-next'\n\nprovide(LINK_ID_KEY, computed(() => undefined))\n\nconst realtimeStore = useDashboardRealtimeStore()\nconst stats = ref<CounterData>({ visits: 0, visitors: 0, referers: 0 })\n\nasync function getRealtimeStats() {\n  if (realtimeStore.timeRange.startAt === 0) {\n    return\n  }\n  const result = await useAPI<{ data: CounterData[] }>('/api/stats/counters', {\n    query: {\n      startAt: realtimeStore.timeRange.startAt,\n      endAt: realtimeStore.timeRange.endAt,\n      ...realtimeStore.filters,\n    },\n  })\n\n  stats.value = result.data?.[0] || { visits: 0, visitors: 0, referers: 0 }\n}\n\nwatchDeep([() => realtimeStore.timeRange, () => realtimeStore.filters], getRealtimeStats)\n\nonMounted(() => {\n  getRealtimeStats()\n})\n</script>\n\n<template>\n  <Card\n    class=\"\n      flex h-72 flex-col gap-0 p-4\n      md:m-2 md:w-80\n    \"\n  >\n    <div class=\"h-24\">\n      <CardHeader\n        v-if=\"stats.visits\" class=\"\n          flex flex-row items-center justify-between space-y-0 px-0 pt-2 pb-2\n        \"\n      >\n        <CardTitle class=\"flex items-center gap-2 text-sm font-medium\">\n          <span\n            class=\"\n              inline-flex size-1.5 animate-ping rounded-full bg-green-400\n              opacity-75\n            \"\n          />\n          {{ $t('dashboard.visits') }}\n        </CardTitle>\n        <MousePointerClick class=\"h-4 w-4 text-muted-foreground\" />\n      </CardHeader>\n      <CardContent class=\"px-0 pb-4\">\n        <NumberFlow class=\"text-2xl font-bold\" :class=\"{ 'opacity-60 blur-md': !stats.visits }\" :value=\"stats.visits\" />\n      </CardContent>\n    </div>\n    <DashboardAnalysisViews\n      class=\"h-40 w-full border-none p-0! shadow-none\"\n      mode=\"simple\"\n      chart-type=\"bar\"\n      :start-at=\"realtimeStore.timeRange.startAt\"\n      :end-at=\"realtimeStore.timeRange.endAt\"\n      :filters=\"realtimeStore.filters\"\n    />\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/realtime/Index.vue",
    "content": "<script setup lang=\"ts\">\nconst realtimeStore = useDashboardRealtimeStore()\n\nconst showGlobe = ref(false)\n\nconst rIC = window.requestIdleCallback || ((cb: IdleRequestCallback) => setTimeout(cb, 50))\n\nonBeforeMount(() => {\n  realtimeStore.init()\n})\n\nonMounted(() => {\n  rIC(() => {\n    showGlobe.value = true\n  })\n})\n</script>\n\n<template>\n  <div\n    class=\"\n      relative flex w-full flex-col gap-4\n      md:block md:h-full\n    \"\n  >\n    <DashboardRealtimeChart\n      class=\"\n        z-10\n        md:absolute md:top-0 md:left-0\n      \"\n    />\n    <div\n      class=\"\n        aspect-square\n        md:absolute md:inset-0 md:aspect-auto\n      \"\n    >\n      <LazyDashboardRealtimeGlobe\n        v-if=\"showGlobe\"\n        class=\"h-full w-full\"\n      />\n      <div\n        v-else\n        class=\"flex h-full w-full items-center justify-center\"\n      >\n        <div class=\"size-3/4 animate-pulse rounded-full bg-muted/20\" />\n      </div>\n    </div>\n    <DashboardRealtimeLogs\n      class=\"\n        z-10 h-[400px]\n        md:absolute md:top-0 md:right-0 md:h-full\n      \"\n    />\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/realtime/Logs.vue",
    "content": "<script setup lang=\"ts\">\nimport type { LogEvent, TrafficEventParams } from '@/types'\n\nconst trafficEventBus = useTrafficEventBus()\n\nconst realtimeStore = useDashboardRealtimeStore()\nconst logs = shallowRef<LogEvent[]>([])\nconst logskey = ref(0)\n\nasync function getEvents() {\n  if (realtimeStore.timeRange.startAt === 0) {\n    return\n  }\n  const data = await useAPI<LogEvent[]>('/api/logs/events', {\n    query: {\n      startAt: realtimeStore.timeRange.startAt,\n      endAt: realtimeStore.timeRange.endAt,\n      ...realtimeStore.filters,\n    },\n  })\n  logs.value = data?.reverse() ?? []\n  logskey.value = Date.now()\n}\n\nwatch([() => realtimeStore.timeRange, () => realtimeStore.filters], getEvents, {\n  deep: true,\n})\n\nonMounted(async () => {\n  getEvents()\n})\n\nfunction onUpdateItems(item: unknown, props: { delay?: number }) {\n  if (item && typeof item === 'object' && 'props' in item) {\n    trafficEventBus.emit(item as TrafficEventParams, props)\n  }\n}\n</script>\n\n<template>\n  <SparkUiAnimatedList v-if=\"logs.length\" :key=\"logskey\" class=\"md:w-72\" @update:items=\"onUpdateItems\">\n    <template #default>\n      <SparkUiNotification\n        v-for=\"item in logs\"\n        :key=\"item.id\"\n        :name=\"item.slug\"\n        :description=\"[item.os, item.browser].filter(Boolean).join(' ')\"\n        :icon=\"getFlag(item.country || '')\"\n        :time=\"item.timestamp\"\n        :item=\"item\"\n        class=\"w-full\"\n      />\n    </template>\n  </SparkUiAnimatedList>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/realtime/globe/Globe.vue",
    "content": "<script setup lang=\"ts\">\nimport { useGlobeColors, useGlobeData, useWebGLGlobe } from '#layers/dashboard/app/composables/globe'\nimport { useDebounceFn, useElementSize } from '@vueuse/core'\n\nconst trafficEventBus = useTrafficEventBus()\n\nconst containerRef = useTemplateRef('containerRef')\nconst canvasRef = useTemplateRef('canvasRef')\nconst { width, height } = useElementSize(containerRef)\n\nconst globeData = useGlobeData()\nconst { arcColor, colors, countryColorTiers, heatmapColorTiers } = useGlobeColors()\n\nconst globe = useWebGLGlobe({\n  canvasRef,\n  width,\n  height,\n  countries: globeData.countries,\n  locations: globeData.locations,\n  countryStats: globeData.countryStats,\n  maxCountryVisits: globeData.maxCountryVisits,\n  highest: globeData.highest,\n  colors,\n  countryColorTiers,\n  heatmapColorTiers,\n})\n\nconst trafficEvent = useTrafficEvent({\n  colos: globeData.colos,\n  arcColor,\n  globe: {\n    isReady: () => globe.isReady.value,\n    drawArc: globe.drawArc,\n    drawRipple: globe.drawRipple,\n  },\n})\n\n// Rebuild texture when data/colors change\nwatch([globeData.countryStats, countryColorTiers, heatmapColorTiers, colors, globeData.countries, globeData.locations], () => {\n  globe.updateCountryTexture()\n})\n\nconst handleResize = useDebounceFn(() => {\n  globe.updateCanvasSize()\n}, 100)\nwatch([width, height], handleResize)\n\nonMounted(async () => {\n  await globeData.init()\n\n  const glInitialized = await globe.init()\n  if (!glInitialized) {\n    console.error('Failed to initialize WebGL')\n    return\n  }\n\n  globe.updateCanvasSize()\n  await globe.updateCountryTexture()\n\n  const { latitude, longitude } = globeData.currentLocation.value\n  const parsedLat = Number(latitude)\n  const parsedLng = Number(longitude)\n  const targetLat = Number.isFinite(parsedLat) ? parsedLat : 0\n  const targetLng = Number.isFinite(parsedLng) ? parsedLng : 0\n  globe.setPointOfView(targetLat, targetLng, true)\n\n  globe.startRenderLoop()\n  trafficEventBus.on(trafficEvent.handleTrafficEvent)\n})\n\nonBeforeUnmount(() => {\n  globe.stopRenderLoop()\n  globe.destroy()\n  trafficEventBus.off(trafficEvent.handleTrafficEvent)\n  trafficEvent.cleanup()\n})\n</script>\n\n<template>\n  <div\n    ref=\"containerRef\"\n    class=\"relative h-full w-full\"\n    @mousedown=\"globe.stopAutoRotate\"\n    @touchstart=\"globe.stopAutoRotate\"\n  >\n    <canvas ref=\"canvasRef\" class=\"absolute inset-0\" />\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/sidebar/AppSidebar.vue",
    "content": "<script setup lang=\"ts\">\ninterface NavItem {\n  title: string\n  url: string\n  icon: Component\n  isActive: boolean\n}\n\nconst { title } = useAppConfig()\nconst { isActive } = useDashboardRoute()\n\nconst platformItems = computed<NavItem[]>(() => [\n  {\n    title: 'nav.links',\n    url: '/dashboard/links',\n    icon: DASHBOARD_ROUTES.links.icon,\n    isActive: isActive('links'),\n  },\n  {\n    title: 'nav.analysis',\n    url: '/dashboard/analysis',\n    icon: DASHBOARD_ROUTES.analysis.icon,\n    isActive: isActive('analysis'),\n  },\n  {\n    title: 'nav.realtime',\n    url: '/dashboard/realtime',\n    icon: DASHBOARD_ROUTES.realtime.icon,\n    isActive: isActive('realtime'),\n  },\n])\n\nconst settingsItems = computed<NavItem[]>(() => [\n  {\n    title: 'nav.migrate',\n    url: '/dashboard/migrate',\n    icon: DASHBOARD_ROUTES.migrate.icon,\n    isActive: isActive('migrate'),\n  },\n])\n</script>\n\n<template>\n  <Sidebar collapsible=\"icon\" variant=\"inset\">\n    <SidebarHeader>\n      <SidebarMenu>\n        <SidebarMenuItem>\n          <SidebarMenuButton size=\"lg\" as-child>\n            <NuxtLink to=\"/\" :title=\"title\">\n              <div\n                class=\"\n                  flex aspect-square size-8 items-center justify-center\n                  rounded-full\n                \"\n              >\n                <img\n                  src=\"/sink.png\"\n                  :alt=\"title\"\n                  class=\"size-8 rounded-full\"\n                >\n              </div>\n              <div class=\"grid flex-1 text-left text-sm leading-tight\">\n                <span class=\"truncate font-medium\">{{ title }}</span>\n                <span class=\"truncate text-xs\">{{ $t('sidebar.subtitle') }}</span>\n              </div>\n            </NuxtLink>\n          </SidebarMenuButton>\n        </SidebarMenuItem>\n      </SidebarMenu>\n    </SidebarHeader>\n    <SidebarContent>\n      <DashboardSidebarNavMain :platform-items=\"platformItems\" :settings-items=\"settingsItems\" />\n      <DashboardSidebarNavSecondary class=\"mt-auto\" />\n    </SidebarContent>\n    <SidebarFooter>\n      <DashboardSidebarNavUser />\n    </SidebarFooter>\n  </Sidebar>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/sidebar/NavMain.vue",
    "content": "<script setup lang=\"ts\">\nimport { useSidebar } from '@/components/ui/sidebar'\n\ndefineProps<{\n  platformItems: {\n    title: string\n    url: string\n    icon: Component\n    isActive?: boolean\n  }[]\n  settingsItems: {\n    title: string\n    url: string\n    icon: Component\n    isActive?: boolean\n  }[]\n}>()\n\nconst { t } = useI18n()\nconst { isMobile, setOpenMobile } = useSidebar()\nconst route = useRoute()\n\nwatch(() => route.path, () => {\n  if (isMobile.value) {\n    setOpenMobile(false)\n  }\n})\n</script>\n\n<template>\n  <SidebarGroup>\n    <SidebarGroupLabel>{{ $t('sidebar.platform') }}</SidebarGroupLabel>\n    <SidebarMenu>\n      <SidebarMenuItem v-for=\"item in platformItems\" :key=\"item.title\">\n        <SidebarMenuButton\n          as-child\n          :tooltip=\"t(item.title)\"\n          :data-active=\"item.isActive\"\n        >\n          <NuxtLink :to=\"item.url\">\n            <component :is=\"item.icon\" />\n            <span>{{ t(item.title) }}</span>\n          </NuxtLink>\n        </SidebarMenuButton>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  </SidebarGroup>\n\n  <SidebarGroup>\n    <SidebarGroupLabel>{{ $t('sidebar.settings') }}</SidebarGroupLabel>\n    <SidebarMenu>\n      <SidebarMenuItem v-for=\"item in settingsItems\" :key=\"item.title\">\n        <SidebarMenuButton\n          as-child\n          :tooltip=\"t(item.title)\"\n          :data-active=\"item.isActive\"\n        >\n          <NuxtLink :to=\"item.url\">\n            <component :is=\"item.icon\" />\n            <span>{{ t(item.title) }}</span>\n          </NuxtLink>\n        </SidebarMenuButton>\n      </SidebarMenuItem>\n    </SidebarMenu>\n  </SidebarGroup>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/sidebar/NavSecondary.vue",
    "content": "<script setup lang=\"ts\">\nimport { ArrowUpCircle, Coffee, Languages, Laptop, Moon, Sun } from 'lucide-vue-next'\nimport { useSidebar } from '@/components/ui/sidebar'\n\nconst { coffee } = useAppConfig()\nconst colorMode = useColorMode()\nconst { setLocale, locales } = useI18n()\nconst { state } = useSidebar()\nconst { hasUpdate, currentVersion, latestVersion } = useVersionCheck()\n</script>\n\n<template>\n  <SidebarGroup>\n    <SidebarGroupContent>\n      <SidebarMenu>\n        <SidebarMenuItem>\n          <div\n            class=\"flex w-full p-1.5 pr-0\" :class=\"[\n              state === 'collapsed'\n                ? 'flex-col items-center gap-2'\n                : 'items-center justify-between',\n            ]\"\n          >\n            <div class=\"flex items-center\">\n              <TooltipProvider>\n                <Tooltip :delay-duration=\"100\">\n                  <TooltipTrigger as-child>\n                    <a\n                      :href=\"coffee\"\n                      target=\"_blank\"\n                      :title=\"$t('sidebar.coffee')\"\n                      class=\"\n                        flex h-8 items-center justify-center rounded-md px-2\n                        hover:bg-sidebar-accent\n                        hover:text-sidebar-accent-foreground\n                      \"\n                    >\n                      <Coffee class=\"size-4\" />\n                    </a>\n                  </TooltipTrigger>\n                  <TooltipContent :side=\"state === 'collapsed' ? 'right' : 'top'\">\n                    <p>{{ $t('sidebar.coffee') }}</p>\n                  </TooltipContent>\n                </Tooltip>\n              </TooltipProvider>\n\n              <TooltipProvider v-if=\"hasUpdate\">\n                <Tooltip :delay-duration=\"100\">\n                  <TooltipTrigger as-child>\n                    <a\n                      href=\"https://github.com/ccbikai/Sink/releases\"\n                      target=\"_blank\"\n                      class=\"\n                        relative flex h-8 items-center justify-center rounded-md\n                        px-2\n                        hover:bg-sidebar-accent\n                        hover:text-sidebar-accent-foreground\n                      \"\n                    >\n                      <ArrowUpCircle class=\"size-4\" />\n                      <span\n                        class=\"\n                          absolute top-1 right-1 size-2 animate-pulse\n                          rounded-full bg-green-500\n                        \"\n                      />\n                    </a>\n                  </TooltipTrigger>\n                  <TooltipContent :side=\"state === 'collapsed' ? 'right' : 'top'\">\n                    <p>{{ $t('sidebar.update', { current: currentVersion, version: latestVersion }) }}</p>\n                  </TooltipContent>\n                </Tooltip>\n              </TooltipProvider>\n            </div>\n\n            <div\n              class=\"flex gap-1\" :class=\"[\n                state === 'collapsed' ? 'flex-col items-center' : 'items-center',\n              ]\"\n            >\n              <DropdownMenu>\n                <DropdownMenuTrigger as-child>\n                  <button\n                    class=\"\n                      flex size-8 items-center justify-center rounded-md\n                      hover:bg-sidebar-accent\n                      hover:text-sidebar-accent-foreground\n                    \"\n                  >\n                    <Languages class=\"size-4\" />\n                  </button>\n                </DropdownMenuTrigger>\n                <DropdownMenuContent\n                  :align=\"state === 'collapsed' ? 'start' : 'end'\"\n                  :side=\"state === 'collapsed' ? 'right' : 'top'\"\n                  class=\"min-w-min\"\n                >\n                  <DropdownMenuItem\n                    v-for=\"locale in locales\"\n                    :key=\"locale.code\"\n                    class=\"cursor-pointer\"\n                    @click=\"setLocale(locale.code)\"\n                  >\n                    <span class=\"mr-1\">{{ locale.emoji }}</span>\n                    {{ locale.name }}\n                  </DropdownMenuItem>\n                </DropdownMenuContent>\n              </DropdownMenu>\n\n              <DropdownMenu>\n                <DropdownMenuTrigger as-child>\n                  <button\n                    class=\"\n                      flex size-8 items-center justify-center rounded-md\n                      hover:bg-sidebar-accent\n                      hover:text-sidebar-accent-foreground\n                    \"\n                  >\n                    <Sun\n                      class=\"\n                        size-4\n                        dark:hidden\n                      \"\n                    />\n                    <Moon\n                      class=\"\n                        hidden size-4\n                        dark:block\n                      \"\n                    />\n                  </button>\n                </DropdownMenuTrigger>\n                <DropdownMenuContent\n                  :align=\"state === 'collapsed' ? 'start' : 'end'\"\n                  :side=\"state === 'collapsed' ? 'right' : 'top'\"\n                  class=\"min-w-min\"\n                >\n                  <DropdownMenuItem\n                    class=\"cursor-pointer\"\n                    @click=\"colorMode.preference = 'light'\"\n                  >\n                    <Sun class=\"mr-1 h-4 w-4\" />\n                    {{ $t('theme.light') }}\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    class=\"cursor-pointer\"\n                    @click=\"colorMode.preference = 'dark'\"\n                  >\n                    <Moon class=\"mr-1 h-4 w-4\" />\n                    {{ $t('theme.dark') }}\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    class=\"cursor-pointer\"\n                    @click=\"colorMode.preference = 'system'\"\n                  >\n                    <Laptop class=\"mr-1 h-4 w-4\" />\n                    {{ $t('theme.system') }}\n                  </DropdownMenuItem>\n                </DropdownMenuContent>\n              </DropdownMenu>\n            </div>\n          </div>\n        </SidebarMenuItem>\n      </SidebarMenu>\n    </SidebarGroupContent>\n  </SidebarGroup>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/dashboard/sidebar/NavUser.vue",
    "content": "<script setup lang=\"ts\">\nimport { ChevronsUpDown, LogOut } from 'lucide-vue-next'\nimport { useSidebar } from '@/components/ui/sidebar'\n\ninterface User {\n  name: string\n  email: string\n  avatar: string\n}\n\nconst { isMobile } = useSidebar()\n\nconst hostname = computed<string>(() => {\n  if (import.meta.client) {\n    return window.location.hostname\n  }\n  return 'localhost'\n})\n\nconst user = computed<User>(() => ({\n  name: 'Root',\n  email: `root@${hostname.value}`,\n  avatar: '/sink.png',\n}))\n\nfunction logOut() {\n  localStorage.removeItem('SinkSiteToken')\n  navigateTo('/dashboard/login')\n}\n</script>\n\n<template>\n  <SidebarMenu>\n    <SidebarMenuItem>\n      <DropdownMenu>\n        <DropdownMenuTrigger as-child>\n          <SidebarMenuButton\n            size=\"lg\"\n            class=\"\n              data-[state=open]:bg-sidebar-accent\n              data-[state=open]:text-sidebar-accent-foreground\n            \"\n          >\n            <Avatar class=\"h-8 w-8 rounded-full\">\n              <AvatarImage :src=\"user.avatar\" :alt=\"user.name\" />\n              <AvatarFallback class=\"rounded-full\">\n                R\n              </AvatarFallback>\n            </Avatar>\n            <div class=\"grid flex-1 text-left text-sm leading-tight\">\n              <span class=\"truncate font-medium\">{{ user.name }}</span>\n              <span class=\"truncate text-xs\">{{ user.email }}</span>\n            </div>\n            <ChevronsUpDown class=\"ml-auto size-4\" />\n          </SidebarMenuButton>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent\n          class=\"w-[--reka-dropdown-menu-trigger-width] min-w-56 rounded-lg\"\n          :side=\"isMobile ? 'bottom' : 'right'\"\n          align=\"end\"\n          :side-offset=\"4\"\n        >\n          <DropdownMenuLabel class=\"p-0 font-normal\">\n            <div class=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n              <Avatar class=\"h-8 w-8 rounded-full\">\n                <AvatarImage :src=\"user.avatar\" :alt=\"user.name\" />\n                <AvatarFallback class=\"rounded-full\">\n                  R\n                </AvatarFallback>\n              </Avatar>\n              <div class=\"grid flex-1 text-left text-sm leading-tight\">\n                <span class=\"truncate font-semibold\">{{ user.name }}</span>\n                <span class=\"truncate text-xs\">{{ user.email }}</span>\n              </div>\n            </div>\n          </DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          <AlertDialog>\n            <AlertDialogTrigger as-child>\n              <DropdownMenuItem\n                class=\"cursor-pointer\"\n                @select.prevent\n              >\n                <LogOut class=\"mr-2 h-4 w-4\" />\n                {{ $t('logout.action') }}\n              </DropdownMenuItem>\n            </AlertDialogTrigger>\n            <AlertDialogContent\n              class=\"\n                max-h-[95svh] max-w-[95svw] grid-rows-[auto_minmax(0,1fr)_auto]\n                md:max-w-lg\n              \"\n            >\n              <AlertDialogHeader>\n                <AlertDialogTitle>{{ $t('logout.title') }}</AlertDialogTitle>\n                <AlertDialogDescription>\n                  {{ $t('logout.confirm') }}\n                </AlertDialogDescription>\n              </AlertDialogHeader>\n              <AlertDialogFooter>\n                <AlertDialogCancel>{{ $t('common.cancel') }}</AlertDialogCancel>\n                <AlertDialogAction @click=\"logOut\">\n                  {{ $t('logout.action') }}\n                </AlertDialogAction>\n              </AlertDialogFooter>\n            </AlertDialogContent>\n          </AlertDialog>\n        </DropdownMenuContent>\n      </DropdownMenu>\n    </SidebarMenuItem>\n  </SidebarMenu>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/login/index.vue",
    "content": "<script setup lang=\"ts\">\nimport { AlertCircle } from 'lucide-vue-next'\nimport { toast } from 'vue-sonner'\nimport { z } from 'zod'\n\nconst { t } = useI18n()\nconst { previewMode } = useRuntimeConfig().public\nconst { setToken, removeToken } = useAuthToken()\n\nconst token = ref('')\nconst error = ref('')\n\nconst LoginSchema = z.object({\n  token: z.string().min(1),\n})\n\nasync function handleSubmit() {\n  error.value = ''\n  const result = LoginSchema.safeParse({ token: token.value })\n\n  if (!result.success) {\n    error.value = t('login.token_required')\n    return\n  }\n\n  try {\n    setToken(token.value)\n    await useAPI('/api/verify')\n    navigateTo('/dashboard')\n  }\n  catch (e) {\n    removeToken()\n    console.error(e)\n    toast.error(t('login.failed'), {\n      description: e instanceof Error ? e.message : String(e),\n    })\n  }\n}\n</script>\n\n<template>\n  <Card class=\"w-full max-w-sm\">\n    <CardHeader>\n      <CardTitle class=\"text-2xl\">\n        {{ $t('login.title') }}\n      </CardTitle>\n      <CardDescription>\n        {{ $t('login.description') }}\n      </CardDescription>\n    </CardHeader>\n    <CardContent class=\"grid gap-4\">\n      <form class=\"space-y-6\" @submit.prevent=\"handleSubmit\">\n        <!-- Hidden username field for password managers -->\n        <Input\n          type=\"text\"\n          name=\"username\"\n          autocomplete=\"username\"\n          value=\"root\"\n          readonly\n          class=\"sr-only\"\n          tabindex=\"-1\"\n          aria-hidden=\"true\"\n        />\n        <FieldGroup>\n          <Field :data-invalid=\"!!error\">\n            <FieldLabel for=\"token\">\n              {{ $t('login.token_label') }}\n            </FieldLabel>\n            <Input\n              id=\"token\"\n              v-model=\"token\"\n              type=\"password\"\n              name=\"password\"\n              autocomplete=\"current-password\"\n              placeholder=\"********\"\n              :aria-invalid=\"!!error\"\n            />\n            <FieldError v-if=\"error\" :errors=\"[error]\" />\n          </Field>\n        </FieldGroup>\n\n        <Alert v-if=\"previewMode\">\n          <AlertCircle class=\"h-4 w-4\" />\n          <AlertTitle>{{ $t('login.tips') }}</AlertTitle>\n          <AlertDescription>\n            {{ $t('login.preview_token') }}\n            <code class=\"font-mono text-green-500\">SinkCool</code>\n          </AlertDescription>\n        </Alert>\n\n        <Button class=\"w-full\" type=\"submit\">\n          {{ $t('login.submit') }}\n        </Button>\n      </form>\n    </CardContent>\n  </Card>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/components/spark-ui/AnimatedList.vue",
    "content": "<script lang=\"ts\" setup>\nimport { cn } from '@/lib/utils'\n\ninterface SlotItem {\n  props?: { key?: string | number } | null\n  [key: string]: unknown\n}\n\nconst props = withDefaults(defineProps<{\n  class?: string\n  delay?: number\n}>(), {\n  delay: 1000,\n})\n\nconst emit = defineEmits(['update:items'])\n\nconst slots = useSlots()\nconst index = ref(0)\nconst slotsArray = ref<SlotItem[]>([])\nconst maxShowItems = 100\n\nconst itemsToShow = computed(() => {\n  const start = index.value - maxShowItems < 0 ? 0 : index.value - maxShowItems\n  return slotsArray.value.slice(start, index.value)\n})\n\nwatch([itemsToShow], () => {\n  emit('update:items', itemsToShow.value.at(-1), props)\n})\n\nasync function loadComponents() {\n  const children = slots.default?.()?.[0]?.children\n  slotsArray.value = Array.isArray(children) ? children as unknown as SlotItem[] : []\n\n  while (index.value < slotsArray.value.length) {\n    index.value++\n    await delay(props.delay)\n  }\n}\n\nasync function delay(ms: number) {\n  return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nfunction getInitial(idx: number) {\n  return idx === index.value - 1\n    ? {\n        scale: 0,\n        opacity: 0,\n      }\n    : undefined\n}\nfunction getEnter(idx: number) {\n  return idx === index.value - 1\n    ? {\n        scale: 1,\n        opacity: 1,\n        y: 0,\n        transition: {\n          type: 'spring',\n          stiffness: 250,\n          damping: 40,\n        },\n      }\n    : undefined\n}\n\nfunction getLeave() {\n  return {\n    scale: 0,\n    opacity: 0,\n    y: 0,\n    transition: {\n      type: 'spring',\n      stiffness: 350,\n      damping: 40,\n    },\n  }\n}\n\nonMounted(() => loadComponents())\n</script>\n\n<template>\n  <div :class=\"cn('overflow-auto', $props.class)\">\n    <transition-group\n      name=\"list\" tag=\"div\" class=\"flex flex-col-reverse items-center p-2\" move-class=\"move\"\n    >\n      <div\n        v-for=\"(item, idx) in itemsToShow\"\n        :key=\"item.props?.key\"\n        v-motion\n        :initial=\"getInitial(+idx)\" :enter=\"getEnter(+idx)\" :leave=\"getLeave()\"\n        :class=\"cn('mx-auto w-full')\"\n      >\n        <component :is=\"item\" />\n      </div>\n    </transition-group>\n  </div>\n</template>\n\n<style scoped>\n.move {\n  transition: transform 0.4s ease-out;\n}\n</style>\n"
  },
  {
    "path": "layers/dashboard/app/components/spark-ui/Notification.vue",
    "content": "<script setup lang='ts'>\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  name: string\n  class?: string\n  description: string\n  icon?: string\n  color?: string\n  time: number\n}>()\n</script>\n\n<template>\n  <figure\n    :class=\"cn(\n      'relative mx-auto my-1 min-h-fit w-full cursor-pointer rounded-2xl border',\n      `\n        transform-gpu transition-transform duration-200 ease-in-out\n        hover:scale-[103%]\n      `,\n      'bg-white',\n      `\n        dark:bg-transparent dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]\n        dark:backdrop-blur-md dark:[border:1px_solid_rgba(255,255,255,.1)]\n      `,\n      props.class,\n    )\"\n  >\n    <div class=\"flex flex-row items-center gap-2 px-2 py-2\">\n      <div\n        class=\"flex size-10 items-center justify-center rounded-2xl\"\n        :style=\"{ backgroundColor: props.color || 'transparent' }\"\n      >\n        <span class=\"text-lg\">{{ props.icon }}</span>\n      </div>\n      <div class=\"flex flex-col overflow-hidden\">\n        <div\n          class=\"flex flex-row items-center text-lg font-medium whitespace-pre\"\n        >\n          <span\n            class=\"\n              text-sm text-foreground\n              sm:text-lg\n            \"\n          >{{ props.name }}</span>\n          <span class=\"mx-1\">·</span>\n          <span class=\"text-xs text-gray-500\">{{ shortTime(props.time) }}</span>\n        </div>\n        <p class=\"text-sm font-normal\">\n          {{ props.description }}\n        </p>\n      </div>\n    </div>\n  </figure>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/composables/analysis.ts",
    "content": "import { defineStore } from '#imports'\nimport { getLocalTimeZone, now, startOfMonth, startOfWeek } from '@internationalized/date'\nimport { useUrlSearchParams } from '@vueuse/core'\nimport { safeDestr } from 'destr'\nimport { ref, watch } from 'vue'\nimport { date2unix, getLocale } from '@/utils/time'\n\nfunction computeDateRange(name: string): [number, number] {\n  const tz = getLocalTimeZone()\n  const currentTime = now(tz)\n\n  const presets: Record<string, () => [number, number]> = {\n    'today': () => [date2unix(currentTime, 'start'), date2unix(currentTime)],\n    'last-24h': () => [date2unix(currentTime.subtract({ hours: 24 })), date2unix(currentTime)],\n    'this-week': () => [date2unix(startOfWeek(currentTime, getLocale()), 'start'), date2unix(currentTime)],\n    'last-7d': () => [date2unix(currentTime.subtract({ days: 7 })), date2unix(currentTime)],\n    'this-month': () => [date2unix(startOfMonth(currentTime), 'start'), date2unix(currentTime)],\n    'last-30d': () => [date2unix(currentTime.subtract({ days: 30 })), date2unix(currentTime)],\n    'last-90d': () => [date2unix(currentTime.subtract({ days: 90 })), date2unix(currentTime)],\n  }\n\n  const getRange = presets[name]\n  return getRange ? getRange() : presets['last-7d']!()\n}\n\nexport const useDashboardAnalysisStore = defineStore('dashboard-analysis', () => {\n  const searchParams = useUrlSearchParams('history')\n  let initialized = false\n\n  const dateRange = ref({ startAt: 0, endAt: 0 })\n  const datePreset = ref<string | null>('last-7d')\n  const filters = ref<Record<string, string>>({})\n\n  function updateDateRange(range: [number, number]) {\n    dateRange.value.startAt = range[0]\n    dateRange.value.endAt = range[1]\n  }\n\n  function selectPreset(name: string) {\n    datePreset.value = name\n    updateDateRange(computeDateRange(name))\n  }\n\n  function updateFilter(type: string, value: string) {\n    filters.value[type] = value\n  }\n\n  function clearFilters() {\n    filters.value = {}\n  }\n\n  // URL > Store > Default, then enable URL sync\n  function init() {\n    if (initialized)\n      return\n\n    // Restore from URL\n    // Custom time range takes priority over preset\n    if (searchParams.time) {\n      const time = safeDestr<{ startAt: number, endAt: number }>(searchParams.time)\n      if (Number.isFinite(time?.startAt) && Number.isFinite(time?.endAt)) {\n        dateRange.value.startAt = time.startAt\n        dateRange.value.endAt = time.endAt\n        datePreset.value = null\n      }\n    }\n    else if (searchParams.preset) {\n      datePreset.value = searchParams.preset as string\n    }\n    if (searchParams.filters) {\n      const restored = safeDestr<Record<string, string>>(searchParams.filters)\n      if (restored) {\n        Object.assign(filters.value, restored)\n      }\n    }\n\n    // Apply default date range from preset if not restored\n    if (dateRange.value.startAt === 0 && datePreset.value) {\n      const [start, end] = computeDateRange(datePreset.value)\n      dateRange.value.startAt = start\n      dateRange.value.endAt = end\n    }\n\n    initialized = true\n  }\n\n  // Store → URL sync (only after init)\n  watch(dateRange, () => {\n    if (!initialized)\n      return\n    searchParams.time = JSON.stringify(dateRange.value)\n  }, { deep: true })\n\n  watch(datePreset, (val) => {\n    if (!initialized)\n      return\n    searchParams.preset = val || ''\n  })\n\n  watch(filters, (val) => {\n    if (!initialized)\n      return\n    searchParams.filters = Object.keys(val).length ? JSON.stringify(val) : ''\n  }, { deep: true })\n\n  return {\n    dateRange,\n    datePreset,\n    filters,\n    updateDateRange,\n    selectPreset,\n    updateFilter,\n    clearFilters,\n    init,\n  }\n})\n"
  },
  {
    "path": "layers/dashboard/app/composables/dashboard.ts",
    "content": "import type { Component } from 'vue'\nimport { useRoute } from '#imports'\nimport { Activity, ChartArea, FolderSync, Link } from 'lucide-vue-next'\nimport { computed } from 'vue'\n\nexport interface DashboardRouteConfig {\n  readonly paths: readonly string[]\n  readonly titleKey: string\n  readonly icon: Component\n}\n\nexport const DASHBOARD_ROUTES = {\n  links: {\n    paths: ['/dashboard/links'],\n    titleKey: 'nav.links',\n    icon: Link,\n  },\n  link: {\n    paths: ['/dashboard/link'],\n    titleKey: 'nav.links',\n    icon: Link,\n  },\n  analysis: {\n    paths: ['/dashboard/analysis'],\n    titleKey: 'nav.analysis',\n    icon: ChartArea,\n  },\n  realtime: {\n    paths: ['/dashboard/realtime'],\n    titleKey: 'nav.realtime',\n    icon: Activity,\n  },\n  migrate: {\n    paths: ['/dashboard/migrate'],\n    titleKey: 'nav.migrate',\n    icon: FolderSync,\n  },\n} as const satisfies Record<string, DashboardRouteConfig>\n\nexport type DashboardRouteName = keyof typeof DASHBOARD_ROUTES\n\nexport function useDashboardRoute() {\n  const route = useRoute()\n\n  const currentPage = computed<DashboardRouteName | ''>(() => {\n    for (const [page, config] of Object.entries(DASHBOARD_ROUTES)) {\n      if ((config.paths as readonly string[]).includes(route.path))\n        return page as DashboardRouteName\n    }\n    return ''\n  })\n\n  const pageTitle = computed(() => {\n    const page = currentPage.value\n    return page ? DASHBOARD_ROUTES[page].titleKey : 'dashboard.title'\n  })\n\n  const isActive = (page: DashboardRouteName) => {\n    return currentPage.value === page\n  }\n\n  return { currentPage, pageTitle, isActive }\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/color.ts",
    "content": "// LRU cache for parsed colors (avoids expensive canvas operations on repeated calls)\nconst COLOR_CACHE_MAX = 64\nconst colorCache = new Map<string, [number, number, number]>()\n\nfunction cacheColor(key: string, value: [number, number, number]): [number, number, number] {\n  // Simple LRU: delete oldest if at capacity\n  if (colorCache.size >= COLOR_CACHE_MAX) {\n    const firstKey = colorCache.keys().next().value\n    if (firstKey !== undefined) {\n      colorCache.delete(firstKey)\n    }\n  }\n  colorCache.set(key, value)\n  return value\n}\n\nexport function parseColor(color: string): [number, number, number] {\n  // Check cache first\n  const cached = colorCache.get(color)\n  if (cached) {\n    // Move to end for LRU behavior\n    colorCache.delete(color)\n    colorCache.set(color, cached)\n    return cached\n  }\n\n  if (color.startsWith('oklch')) {\n    const canvas = document.createElement('canvas')\n    canvas.width = 1\n    canvas.height = 1\n    const ctx = canvas.getContext('2d')!\n    ctx.fillStyle = color\n    ctx.fillRect(0, 0, 1, 1)\n    const data = ctx.getImageData(0, 0, 1, 1).data\n    return cacheColor(color, [data[0]! / 255, data[1]! / 255, data[2]! / 255])\n  }\n\n  if (color.startsWith('#')) {\n    const hex = color.slice(1)\n    return cacheColor(color, [\n      Number.parseInt(hex.slice(0, 2), 16) / 255,\n      Number.parseInt(hex.slice(2, 4), 16) / 255,\n      Number.parseInt(hex.slice(4, 6), 16) / 255,\n    ])\n  }\n\n  const match = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n  if (match) {\n    return cacheColor(color, [\n      Number.parseInt(match[1]!) / 255,\n      Number.parseInt(match[2]!) / 255,\n      Number.parseInt(match[3]!) / 255,\n    ])\n  }\n\n  return cacheColor(color, [0.5, 0.5, 0.5])\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/colors.ts",
    "content": "import type { CountryColorTiers, GlobeColors, HeatmapColorTiers } from './types'\nimport { useColorMode } from '#imports'\nimport { computed } from 'vue'\n\nexport type { CountryColorTiers, GlobeColors, HeatmapColorTiers }\n\nexport function useGlobeColors() {\n  const colorMode = useColorMode()\n  const isDark = computed(() => colorMode.value === 'dark')\n\n  const arcColor = computed(() =>\n    isDark.value ? 'oklch(0.85 0.15 70)' : 'oklch(0.8 0.12 65)',\n  )\n\n  const colors = computed<GlobeColors>(() => ({\n    globeFill: isDark.value ? 'oklch(0.3 0.005 286)' : 'oklch(0.95 0.002 286)',\n  }))\n\n  const countryColorTiers = computed<CountryColorTiers>(() => ({\n    noData: isDark.value ? 'oklch(0.4 0.005 286)' : 'oklch(0.88 0.003 286)',\n    tier1: isDark.value ? 'oklch(0.723 0.219 149.579 / 35%)' : 'oklch(0.723 0.219 149.579 / 40%)',\n    tier2: isDark.value ? 'oklch(0.723 0.219 149.579 / 55%)' : 'oklch(0.723 0.219 149.579 / 60%)',\n    tier3: isDark.value ? 'oklch(0.723 0.219 149.579 / 75%)' : 'oklch(0.723 0.219 149.579 / 80%)',\n  }))\n\n  const heatmapColorTiers = computed<HeatmapColorTiers>(() => ({\n    tier1: isDark.value ? 'oklch(0.82 0.1 75)' : 'oklch(0.82 0.12 72)',\n    tier2: isDark.value ? 'oklch(0.75 0.14 68)' : 'oklch(0.75 0.16 65)',\n    tier3: isDark.value ? 'oklch(0.65 0.18 58)' : 'oklch(0.65 0.2 55)',\n    tier4: isDark.value ? 'oklch(0.55 0.22 48)' : 'oklch(0.55 0.24 45)',\n    tier5: isDark.value ? 'oklch(0.45 0.24 40)' : 'oklch(0.48 0.26 38)',\n  }))\n\n  return {\n    isDark,\n    arcColor,\n    colors,\n    countryColorTiers,\n    heatmapColorTiers,\n  }\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/data.ts",
    "content": "import type { ComputedRef, Ref, ShallowRef } from 'vue'\nimport type { AreaData, ColoData, CurrentLocation, GeoJSONData, LocationData } from '@/types'\nimport { watchDeep } from '@vueuse/core'\nimport { computed, ref, shallowRef } from 'vue'\nimport { useAPI } from '@/utils/api'\nimport { useDashboardRealtimeStore } from '../realtime'\n\ninterface RawLocationData {\n  latitude: number\n  longitude: number\n  count: string | number\n}\n\nexport interface GlobeDataState {\n  countries: ShallowRef<GeoJSONData>\n  locations: ShallowRef<LocationData[]>\n  colos: ShallowRef<Record<string, ColoData>>\n  currentLocation: Ref<CurrentLocation>\n  countryStats: ShallowRef<Map<string, number>>\n  highest: ComputedRef<number>\n  maxCountryVisits: ComputedRef<number>\n}\n\nexport function useGlobeData() {\n  const realtimeStore = useDashboardRealtimeStore()\n\n  const countries = shallowRef<GeoJSONData>({ features: [] })\n  const locations = shallowRef<LocationData[]>([])\n  const colos = shallowRef<Record<string, ColoData>>({})\n  const currentLocation = ref<CurrentLocation>({})\n  const countryStats = shallowRef<Map<string, number>>(new Map())\n\n  // Request version for race condition control\n  let requestVersion = 0\n\n  const getBaseQuery = () => ({\n    startAt: realtimeStore.timeRange.startAt,\n    endAt: realtimeStore.timeRange.endAt,\n    ...realtimeStore.filters,\n  })\n\n  const nextRequestVersion = () => ++requestVersion\n  const isStale = (version: number) => version !== requestVersion\n\n  const highest = computed(() => Math.max(...locations.value.map(l => l.count), 1))\n  const maxCountryVisits = computed(() => Math.max(...countryStats.value.values(), 1))\n\n  async function getGlobeJSON() {\n    const data = await $fetch('/countries.geojson')\n    countries.value = data as GeoJSONData\n  }\n\n  async function getColosJSON() {\n    const data = await $fetch('/colos.json')\n    colos.value = data as Record<string, ColoData>\n  }\n\n  async function getCurrentLocation() {\n    const data = await useAPI<CurrentLocation>('/api/location')\n    currentLocation.value = data\n  }\n\n  async function getCountryStats(version: number) {\n    const result = await useAPI<{ data: AreaData[] }>('/api/stats/metrics', {\n      query: {\n        type: 'country',\n        ...getBaseQuery(),\n      },\n    })\n\n    // Discard stale response\n    if (isStale(version))\n      return\n\n    const statsMap = new Map<string, number>()\n    if (Array.isArray(result.data)) {\n      result.data.forEach((item) => {\n        statsMap.set(item.name, item.count)\n      })\n    }\n    countryStats.value = statsMap\n  }\n\n  async function getLiveLocations(version: number) {\n    const result = await useAPI<{ data: RawLocationData[] }>('/api/logs/locations', {\n      query: {\n        ...getBaseQuery(),\n      },\n    })\n\n    // Discard stale response\n    if (isStale(version))\n      return\n\n    locations.value = result.data?.map(e => ({\n      lat: e.latitude,\n      lng: e.longitude,\n      count: +e.count,\n    })) || []\n  }\n\n  async function init() {\n    const version = nextRequestVersion()\n    await Promise.all([\n      getLiveLocations(version),\n      getCurrentLocation(),\n      getGlobeJSON(),\n      getColosJSON(),\n      getCountryStats(version),\n    ])\n  }\n\n  async function refresh() {\n    const version = nextRequestVersion()\n    await Promise.all([\n      getLiveLocations(version),\n      getCountryStats(version),\n    ])\n  }\n\n  // Watch store changes\n  watchDeep([() => realtimeStore.timeRange, () => realtimeStore.filters], refresh)\n\n  return {\n    countries,\n    locations,\n    colos,\n    currentLocation,\n    countryStats,\n    highest,\n    maxCountryVisits,\n    init,\n    refresh,\n  }\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/geometry.ts",
    "content": "import { geoDistance, geoInterpolate } from 'd3-geo'\n\nexport { createOctahedronSphere } from './sphere'\n\n// Great-circle arc geometry via d3-geo interpolation (ribbon for visible width)\nexport function createArcGeometry(\n  startLat: number,\n  startLng: number,\n  endLat: number,\n  endLng: number,\n  segments: number = 50,\n  progress: number = 1,\n  ribbonHalfWidth: number = 0.002,\n): { positions: Float32Array, alphas: Float32Array, dashParams: Float32Array } {\n  const interpolate = geoInterpolate([startLng, startLat], [endLng, endLat])\n  const angle = geoDistance([startLng, startLat], [endLng, endLat])\n\n  // First pass: compute center line points\n  const numSegments = Math.floor(segments * progress)\n  const center: { x: number, y: number, z: number, t: number }[] = []\n\n  for (let i = 0; i <= numSegments; i++) {\n    const t = i / segments\n    const [lng, lat] = interpolate(t)\n    const point = latLngToXYZ(lat!, lng!, 1.0)\n\n    // Lift arc above sphere surface\n    const lift = Math.sin(t * Math.PI) * 0.08 * (angle / Math.PI)\n    const len = Math.sqrt(point[0] ** 2 + point[1] ** 2 + point[2] ** 2)\n    const scale = (1.008 + lift) / len\n    center.push({ x: point[0] * scale, y: point[1] * scale, z: point[2] * scale, t })\n  }\n\n  if (center.length < 2) {\n    return {\n      positions: new Float32Array(0),\n      alphas: new Float32Array(0),\n      dashParams: new Float32Array(0),\n    }\n  }\n\n  // Second pass: expand center line into ribbon (triangle strip)\n  const positions: number[] = []\n  const alphas: number[] = []\n  const dashParams: number[] = []\n\n  for (let i = 0; i < center.length; i++) {\n    const p = center[i]!\n\n    // Tangent direction along the arc\n    let tx: number, ty: number, tz: number\n    if (i === 0) {\n      const n = center[1]!\n      tx = n.x - p.x\n      ty = n.y - p.y\n      tz = n.z - p.z\n    }\n    else if (i === center.length - 1) {\n      const prev = center[i - 1]!\n      tx = p.x - prev.x\n      ty = p.y - prev.y\n      tz = p.z - prev.z\n    }\n    else {\n      const prev = center[i - 1]!\n      const n = center[i + 1]!\n      tx = n.x - prev.x\n      ty = n.y - prev.y\n      tz = n.z - prev.z\n    }\n\n    // Side = cross(tangent, radial), normalized, gives perpendicular ribbon direction\n    let sx = ty * p.z - tz * p.y\n    let sy = tz * p.x - tx * p.z\n    let sz = tx * p.y - ty * p.x\n    const sLen = Math.sqrt(sx * sx + sy * sy + sz * sz)\n    if (sLen > 0) {\n      sx /= sLen\n      sy /= sLen\n      sz /= sLen\n    }\n\n    const alpha = Math.sin(p.t * Math.PI)\n\n    // Two vertices per arc point (left / right of center)\n    positions.push(\n      p.x + sx * ribbonHalfWidth,\n      p.y + sy * ribbonHalfWidth,\n      p.z + sz * ribbonHalfWidth,\n      p.x - sx * ribbonHalfWidth,\n      p.y - sy * ribbonHalfWidth,\n      p.z - sz * ribbonHalfWidth,\n    )\n    alphas.push(alpha, alpha)\n    dashParams.push(p.t, p.t)\n  }\n\n  return {\n    positions: new Float32Array(positions),\n    alphas: new Float32Array(alphas),\n    dashParams: new Float32Array(dashParams),\n  }\n}\n\nexport function latLngToXYZ(lat: number, lng: number, radius: number): [number, number, number] {\n  const latRad = (lat * Math.PI) / 180\n  const lngRad = (lng * Math.PI) / 180\n  return [\n    radius * Math.cos(latRad) * Math.sin(lngRad),\n    radius * Math.sin(latRad),\n    radius * Math.cos(latRad) * Math.cos(lngRad),\n  ]\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/index.ts",
    "content": "// Utilities (internal, but exported for advanced usage)\nexport { parseColor } from './color'\n// Colors\nexport { useGlobeColors } from './colors'\n\nexport type { CountryColorTiers, GlobeColors, HeatmapColorTiers } from './colors'\n// Data\nexport { useGlobeData } from './data'\n\nexport type { GlobeDataState } from './data'\nexport { createArcGeometry, createOctahedronSphere, latLngToXYZ } from './geometry'\n\nexport { setupGlobeInteraction } from './interaction'\nexport type { InertiaState, InteractionContext } from './interaction'\nexport {\n  arcFragmentShader,\n  arcVertexShader,\n  earthFragmentShader,\n  earthVertexShader,\n  rippleFragmentShader,\n  rippleVertexShader,\n} from './shaders'\nexport { createCountryTexture } from './texture'\nexport type { ArcData, RippleData, WebGLGlobeContext } from './types'\n// WebGL Globe\nexport { useWebGLGlobe } from './useWebGLGlobe'\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/interaction.ts",
    "content": "import type { Ref } from 'vue'\n\nexport interface InertiaState {\n  velocityX: number\n  velocityY: number\n  isActive: boolean\n}\n\nexport interface InteractionContext {\n  longitude: Ref<number>\n  latitude: Ref<number>\n  zoom: Ref<number>\n  isDragging: Ref<boolean>\n  stopAutoRotate: () => void\n}\n\nexport function setupGlobeInteraction(\n  canvas: HTMLCanvasElement,\n  ctx: InteractionContext,\n  inertia: InertiaState,\n): () => void {\n  let lastMouseX = 0\n  let lastMouseY = 0\n  let lastTouchX = 0\n  let lastTouchY = 0\n  let lastDragTime = 0\n\n  function stopInertia() {\n    inertia.isActive = false\n    inertia.velocityX = 0\n    inertia.velocityY = 0\n  }\n\n  function startInertia() {\n    if (Math.abs(inertia.velocityX) > 0.01 || Math.abs(inertia.velocityY) > 0.01) {\n      inertia.isActive = true\n    }\n  }\n\n  function applyDrag(deltaX: number, deltaY: number, sensitivity: number) {\n    const now = performance.now()\n    const dt = now - lastDragTime\n    if (dt > 0 && dt < 100) {\n      inertia.velocityX = deltaX / dt\n      inertia.velocityY = deltaY / dt\n    }\n    lastDragTime = now\n\n    const zoomFactor = 1 - ctx.zoom.value * 0.8\n    ctx.longitude.value = ((ctx.longitude.value - deltaX * sensitivity * zoomFactor) % 360 + 540) % 360 - 180\n    ctx.latitude.value = Math.max(-85, Math.min(85, ctx.latitude.value + deltaY * sensitivity * zoomFactor))\n  }\n\n  const onMouseDown = (e: MouseEvent) => {\n    ctx.isDragging.value = true\n    lastMouseX = e.screenX\n    lastMouseY = e.screenY\n    lastDragTime = performance.now()\n    ctx.stopAutoRotate()\n    stopInertia()\n  }\n\n  const onMouseMove = (e: MouseEvent) => {\n    if (!ctx.isDragging.value)\n      return\n    const deltaX = e.screenX - lastMouseX\n    const deltaY = e.screenY - lastMouseY\n    lastMouseX = e.screenX\n    lastMouseY = e.screenY\n    applyDrag(deltaX, deltaY, 0.3)\n  }\n\n  const onMouseUp = () => {\n    if (!ctx.isDragging.value)\n      return\n    ctx.isDragging.value = false\n    startInertia()\n  }\n\n  const onWheel = (e: WheelEvent) => {\n    e.preventDefault()\n    let amount = -e.deltaY * 0.001\n    if ((e as any).mozInputSource === 1 && e.deltaMode === 1) {\n      amount *= 50\n    }\n    ctx.zoom.value = Math.max(0, Math.min(1, ctx.zoom.value + amount))\n    ctx.stopAutoRotate()\n  }\n\n  const onTouchStart = (e: TouchEvent) => {\n    if (e.touches.length === 1) {\n      ctx.isDragging.value = true\n      lastTouchX = e.touches[0]!.clientX\n      lastTouchY = e.touches[0]!.clientY\n      lastDragTime = performance.now()\n      ctx.stopAutoRotate()\n      stopInertia()\n    }\n  }\n\n  const onTouchMove = (e: TouchEvent) => {\n    if (!ctx.isDragging.value || e.touches.length !== 1)\n      return\n    const deltaX = e.touches[0]!.clientX - lastTouchX\n    const deltaY = e.touches[0]!.clientY - lastTouchY\n    lastTouchX = e.touches[0]!.clientX\n    lastTouchY = e.touches[0]!.clientY\n    applyDrag(deltaX, deltaY, 0.5)\n  }\n\n  const onTouchEnd = () => {\n    if (!ctx.isDragging.value)\n      return\n    ctx.isDragging.value = false\n    startInertia()\n  }\n\n  canvas.addEventListener('mousedown', onMouseDown)\n  document.addEventListener('mousemove', onMouseMove)\n  document.addEventListener('mouseup', onMouseUp)\n  canvas.addEventListener('wheel', onWheel, { passive: false })\n  canvas.addEventListener('touchstart', onTouchStart)\n  canvas.addEventListener('touchmove', onTouchMove)\n  canvas.addEventListener('touchend', onTouchEnd)\n\n  return () => {\n    stopInertia()\n    canvas.removeEventListener('mousedown', onMouseDown)\n    document.removeEventListener('mousemove', onMouseMove)\n    document.removeEventListener('mouseup', onMouseUp)\n    canvas.removeEventListener('wheel', onWheel)\n    canvas.removeEventListener('touchstart', onTouchStart)\n    canvas.removeEventListener('touchmove', onTouchMove)\n    canvas.removeEventListener('touchend', onTouchEnd)\n  }\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/shaders.ts",
    "content": "export const earthVertexShader = /* glsl */ `\n  attribute vec3 position;\n  attribute vec2 texcoord;\n\n  uniform mat4 model;\n  uniform mat4 view;\n  uniform mat4 projection;\n\n  varying vec2 vUv;\n\n  void main() {\n    vUv = texcoord;\n    gl_Position = projection * view * model * vec4(position, 1.0);\n  }\n`\n\nexport const earthFragmentShader = /* glsl */ `\n  precision mediump float;\n\n  uniform sampler2D u_countryTexture;\n\n  varying vec2 vUv;\n\n  void main() {\n    vec2 uv = vUv;\n    uv.x = fract(uv.x);\n    gl_FragColor = vec4(texture2D(u_countryTexture, uv).rgb, 1.0);\n  }\n`\n\nexport const arcVertexShader = /* glsl */ `\n  attribute vec3 position;\n  attribute float alpha;\n  attribute float dashParam;\n\n  uniform mat4 model;\n  uniform mat4 view;\n  uniform mat4 projection;\n\n  varying float vAlpha;\n  varying float vDashParam;\n\n  void main() {\n    gl_Position = projection * view * model * vec4(position, 1.0);\n    vAlpha = alpha;\n    vDashParam = dashParam;\n  }\n`\n\nexport const arcFragmentShader = /* glsl */ `\n  precision mediump float;\n\n  uniform vec3 u_color;\n  uniform float u_fade;\n  uniform float u_dashCount;\n  uniform float u_dashRatio;\n\n  varying float vAlpha;\n  varying float vDashParam;\n\n  void main() {\n    // Dashed line: discard fragments in gaps\n    float dashPhase = fract(vDashParam * u_dashCount);\n    if (dashPhase > u_dashRatio) discard;\n\n    float a = min(vAlpha * u_fade * 2.5, 1.0);\n    gl_FragColor = vec4(u_color * a, a);\n  }\n`\n\nexport const rippleVertexShader = /* glsl */ `\n  attribute vec3 position;\n\n  uniform mat4 model;\n  uniform mat4 view;\n  uniform mat4 projection;\n  uniform float u_pointSize;\n\n  varying vec3 vWorldPos;\n\n  void main() {\n    vec4 worldPos = model * vec4(position, 1.0);\n    vWorldPos = worldPos.xyz;\n    gl_Position = projection * view * worldPos;\n    gl_PointSize = u_pointSize;\n  }\n`\n\nexport const rippleFragmentShader = /* glsl */ `\n  precision mediump float;\n\n  uniform vec3 u_color;\n  uniform vec3 u_eye;\n  uniform float u_alpha;\n  uniform float u_ringWidth;\n\n  varying vec3 vWorldPos;\n\n  void main() {\n    vec3 toEye = normalize(u_eye - vWorldPos);\n    vec3 normal = normalize(vWorldPos);\n    if (dot(toEye, normal) < 0.0) discard;\n\n    float dist = length(gl_PointCoord - vec2(0.5));\n    if (dist > 0.5) discard;\n\n    // Ring shape\n    float inner = 0.5 - u_ringWidth;\n    float ring = smoothstep(inner - 0.05, inner, dist) * (1.0 - smoothstep(0.45, 0.5, dist));\n    float a = ring * u_alpha;\n    if (a < 0.01) discard;\n\n    gl_FragColor = vec4(u_color * a, a);\n  }\n`\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/sphere.ts",
    "content": "// Pure sphere tessellation — no external dependencies.\n// Used by both runtime (geometry.ts re-export) and build script (scripts/build-sphere.js).\n\nexport function createOctahedronSphere(divisions: number) {\n  const initialPoints: number[] = [\n    0,\n    1,\n    0,\n    0,\n    0,\n    -1,\n    -1,\n    0,\n    0,\n    0,\n    1,\n    0,\n    -1,\n    0,\n    0,\n    0,\n    0,\n    1,\n    0,\n    1,\n    0,\n    0,\n    0,\n    1,\n    1,\n    0,\n    0,\n    0,\n    1,\n    0,\n    1,\n    0,\n    0,\n    0,\n    0,\n    -1,\n    0,\n    -1,\n    0,\n    -1,\n    0,\n    0,\n    0,\n    0,\n    -1,\n    0,\n    -1,\n    0,\n    0,\n    0,\n    1,\n    -1,\n    0,\n    0,\n    0,\n    -1,\n    0,\n    1,\n    0,\n    0,\n    0,\n    0,\n    1,\n    0,\n    -1,\n    0,\n    0,\n    0,\n    -1,\n    1,\n    0,\n    0,\n  ]\n\n  let points = new Float32Array(initialPoints)\n\n  for (let i = 0; i < divisions; i++) {\n    const newPoints = new Float32Array(points.length * 4)\n    let offset = 0\n\n    for (let j = 0; j < points.length; j += 9) {\n      const a: [number, number, number] = [points[j]!, points[j + 1]!, points[j + 2]!]\n      const b: [number, number, number] = [points[j + 3]!, points[j + 4]!, points[j + 5]!]\n      const c: [number, number, number] = [points[j + 6]!, points[j + 7]!, points[j + 8]!]\n\n      const ab = normalizeVec3([(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2])\n      const bc = normalizeVec3([(b[0] + c[0]) / 2, (b[1] + c[1]) / 2, (b[2] + c[2]) / 2])\n      const ca = normalizeVec3([(c[0] + a[0]) / 2, (c[1] + a[1]) / 2, (c[2] + a[2]) / 2])\n\n      newPoints.set([...a, ...ab, ...ca], offset)\n      offset += 9\n      newPoints.set([...ab, ...bc, ...ca], offset)\n      offset += 9\n      newPoints.set([...ab, ...b, ...bc], offset)\n      offset += 9\n      newPoints.set([...ca, ...bc, ...c], offset)\n      offset += 9\n    }\n\n    points = newPoints\n  }\n\n  // Build indexed mesh (triangle-at-a-time for proper seam/pole handling)\n  const vertexMap = new Map<string, number>()\n  const positions: number[] = []\n  const texcoords: number[] = []\n  const indices: number[] = []\n\n  for (let tri = 0; tri < points.length; tri += 9) {\n    const triVerts: [number, number, number][] = []\n    const triUVs: [number, number][] = []\n\n    for (let v = 0; v < 3; v++) {\n      const p: [number, number, number] = [\n        points[tri + v * 3]!,\n        points[tri + v * 3 + 1]!,\n        points[tri + v * 3 + 2]!,\n      ]\n      triVerts.push(p)\n      triUVs.push(uvFromPoint(p))\n    }\n\n    // Fix poles: at poles atan2 is undefined, use average u from other verts\n    for (let v = 0; v < 3; v++) {\n      if (Math.abs(triVerts[v]![1]) > 0.999) {\n        const otherUs = triUVs.filter((_, i) => i !== v).map(uv => uv[0])\n        triUVs[v]![0] = (otherUs[0]! + otherUs[1]!) / 2\n      }\n    }\n\n    // Fix seam: if triangle spans the dateline (u range > 0.5), wrap low values\n    const us = triUVs.map(uv => uv[0])\n    const maxU = Math.max(...us)\n    const minU = Math.min(...us)\n    if (maxU - minU > 0.5) {\n      for (const uv of triUVs) {\n        if (uv[0] < 0.25)\n          uv[0] += 1.0\n      }\n    }\n\n    for (let v = 0; v < 3; v++) {\n      const p = triVerts[v]!\n      const uv = triUVs[v]!\n      const key = `${p[0].toFixed(6)},${p[1].toFixed(6)},${p[2].toFixed(6)},${uv[0].toFixed(6)},${uv[1].toFixed(6)}`\n\n      let index = vertexMap.get(key)\n      if (index === undefined) {\n        index = positions.length / 3\n        vertexMap.set(key, index)\n        positions.push(...p)\n        texcoords.push(...uv)\n      }\n      indices.push(index)\n    }\n  }\n\n  return {\n    position: new Float32Array(positions),\n    texcoord: new Float32Array(texcoords),\n    indices: new Uint16Array(indices),\n  }\n}\n\nfunction normalizeVec3(v: number[]): [number, number, number] {\n  const len = Math.sqrt(v[0]! * v[0]! + v[1]! * v[1]! + v[2]! * v[2]!)\n  return [v[0]! / len, v[1]! / len, v[2]! / len]\n}\n\nfunction uvFromPoint(p: number[]): [number, number] {\n  return [\n    (Math.atan2(p[0]!, p[2]!) / (2 * Math.PI)) + 0.5,\n    1.0 - ((Math.asin(p[1]!) / Math.PI) + 0.5),\n  ]\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/texture.ts",
    "content": "import type { GeoProjection } from 'd3-geo'\nimport type { CountryColorTiers, HeatmapColorTiers } from './types'\nimport type { GeoJSONData, LocationData } from '@/types'\nimport { scaleThreshold } from 'd3-scale'\n\n// Hex grid constants (flat-top hexagons)\n// Base hexSize=9 is calibrated for 4096px texture width\nconst BASE_HEX_SIZE = 9\nconst BASE_TEXTURE_WIDTH = 4096\nconst HEX_MARGIN = 0.2\n\nconst HEX_ANGLES = Array.from({ length: 6 }, (_, i) => {\n  const angle = (Math.PI / 3) * i\n  return [Math.cos(angle), Math.sin(angle)] as const\n})\n\ntype Canvas2D = CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n\nfunction createCanvas(width: number, height: number): HTMLCanvasElement | OffscreenCanvas {\n  if (typeof OffscreenCanvas !== 'undefined')\n    return new OffscreenCanvas(width, height)\n  return Object.assign(document.createElement('canvas'), { width, height })\n}\n\nexport async function createCountryTexture(\n  gl: WebGLRenderingContext,\n  countries: GeoJSONData,\n  countryStats: Map<string, number>,\n  maxVisits: number,\n  tiers: CountryColorTiers,\n  globeFill: string,\n  locations: LocationData[],\n  highest: number,\n  heatmapTiers: HeatmapColorTiers,\n  containerSize: number,\n): Promise<WebGLTexture | null> {\n  const { geoPath, geoEquirectangular } = await import('d3-geo')\n\n  const maxSize = gl.getParameter(gl.MAX_TEXTURE_SIZE) as number\n  const width = Math.min(containerSize * 2, maxSize)\n  const height = width / 2\n  const hexSize = BASE_HEX_SIZE * (width / BASE_TEXTURE_WIDTH)\n\n  const canvas = createCanvas(width, height)\n  const ctx = canvas.getContext('2d')! as Canvas2D\n\n  ctx.fillStyle = globeFill\n  ctx.fillRect(0, 0, width, height)\n\n  const projection = geoEquirectangular()\n    .scale(width / (2 * Math.PI))\n    .translate([width / 2, height / 2])\n\n  const pathGen = geoPath(projection)\n\n  // Country lookup texture: each country drawn with unique color encoding its index\n  const lookupCanvas = createCanvas(width, height)\n  const lookupCtx = lookupCanvas.getContext('2d')! as Canvas2D\n\n  lookupCtx.fillStyle = 'rgb(0, 0, 0)'\n  lookupCtx.fillRect(0, 0, width, height)\n\n  const features = countries.features\n  for (let i = 0; i < features.length; i++) {\n    const id = i + 1\n    lookupCtx.fillStyle = `rgb(${id & 0xFF}, ${(id >> 8) & 0xFF}, 0)`\n    lookupCtx.beginPath()\n    pathGen.context(lookupCtx as any)(features[i] as any)\n    lookupCtx.fill()\n  }\n\n  const lookupData = lookupCtx.getImageData(0, 0, width, height).data\n\n  // Country color map from visit tiers\n  const tierScale = scaleThreshold<number, string>()\n    .domain([1, maxVisits * 0.33, maxVisits * 0.66])\n    .range([tiers.noData, tiers.tier1, tiers.tier2, tiers.tier3])\n\n  const countryColorMap = new Map<number, string>()\n  for (let i = 0; i < features.length; i++) {\n    const props = (features[i] as any)?.properties\n    const countryCode = props?.ISO_A2 || props?.iso_a2 || ''\n    const count = countryStats.get(countryCode) || 0\n    countryColorMap.set(i, tierScale(count))\n  }\n\n  // Generate hex grid grouped by color for batch rendering\n  drawHexGrid(ctx, width, height, hexSize, lookupData, countryColorMap, tiers.noData)\n\n  // Heatmap overlay from location density\n  if (locations.length > 0 && highest > 0) {\n    drawHeatmapHexes(ctx, width, height, hexSize, projection, locations, highest, heatmapTiers)\n  }\n\n  return createGLTexture(gl, canvas)\n}\n\nfunction drawHexGrid(\n  ctx: Canvas2D,\n  width: number,\n  height: number,\n  hexSize: number,\n  lookupData: Uint8ClampedArray,\n  countryColorMap: Map<number, string>,\n  noDataColor: string,\n) {\n  const drawRadius = hexSize * (1 - HEX_MARGIN)\n  const colSpacing = 1.5 * hexSize\n  const rowSpacing = Math.sqrt(3) * hexSize\n  const halfRowSpacing = rowSpacing / 2\n  const numCols = Math.ceil(width / colSpacing) + 1\n  const numRows = Math.ceil(height / rowSpacing) + 1\n\n  const hexesByColor = new Map<string, number[]>()\n\n  for (let col = 0; col < numCols; col++) {\n    const isOddCol = col & 1\n    for (let row = 0; row < numRows; row++) {\n      const cx = col * colSpacing\n      const cy = row * rowSpacing + (isOddCol ? halfRowSpacing : 0)\n\n      if (cx >= width || cy >= height)\n        continue\n\n      const px = Math.min(Math.floor(cx), width - 1)\n      const py = Math.min(Math.floor(cy), height - 1)\n      const idx = (py * width + px) * 4\n      const countryIdx = (lookupData[idx]! | (lookupData[idx + 1]! << 8)) - 1\n\n      if (countryIdx < 0)\n        continue\n\n      const color = countryColorMap.get(countryIdx) || noDataColor\n      let coords = hexesByColor.get(color)\n      if (!coords) {\n        coords = []\n        hexesByColor.set(color, coords)\n      }\n      coords.push(cx, cy)\n    }\n  }\n\n  for (const [color, coords] of hexesByColor) {\n    drawHexBatch(ctx, coords, drawRadius, color)\n  }\n}\n\nfunction drawHeatmapHexes(\n  ctx: Canvas2D,\n  width: number,\n  height: number,\n  hexSize: number,\n  projection: GeoProjection,\n  locations: LocationData[],\n  highest: number,\n  heatmapTiers: HeatmapColorTiers,\n) {\n  const drawRadius = hexSize * (1 - HEX_MARGIN)\n  const colSpacing = 1.5 * hexSize\n  const rowSpacing = Math.sqrt(3) * hexSize\n  const halfRowSpacing = rowSpacing / 2\n\n  const projectedLocs: { px: number, py: number, count: number }[] = []\n  for (const loc of locations) {\n    const projected = projection([loc.lng, loc.lat])\n    if (!projected)\n      continue\n    projectedLocs.push({ px: projected[0], py: projected[1], count: loc.count })\n  }\n\n  const influenceRadius = colSpacing * 1.5\n  const influenceRadiusSq = influenceRadius * influenceRadius\n\n  const heatTierScale = scaleThreshold<number, string>()\n    .domain([highest * 0.05, highest * 0.15, highest * 0.35, highest * 0.65])\n    .range([heatmapTiers.tier1, heatmapTiers.tier2, heatmapTiers.tier3, heatmapTiers.tier4, heatmapTiers.tier5])\n\n  const heatHexesByColor = new Map<string, number[]>()\n  const numCols = Math.ceil(width / colSpacing) + 1\n  const numRows = Math.ceil(height / rowSpacing) + 1\n\n  for (let col = 0; col < numCols; col++) {\n    const isOddCol = col & 1\n    for (let row = 0; row < numRows; row++) {\n      const cx = col * colSpacing\n      const cy = row * rowSpacing + (isOddCol ? halfRowSpacing : 0)\n\n      if (cx >= width || cy >= height)\n        continue\n\n      let density = 0\n      for (const loc of projectedLocs) {\n        const dx = cx - loc.px\n        const dy = cy - loc.py\n        const distSq = dx * dx + dy * dy\n        if (distSq < influenceRadiusSq) {\n          density += loc.count * (1 - Math.sqrt(distSq) / influenceRadius)\n        }\n      }\n\n      if (density <= 0)\n        continue\n\n      const color = heatTierScale(density)\n      let coords = heatHexesByColor.get(color)\n      if (!coords) {\n        coords = []\n        heatHexesByColor.set(color, coords)\n      }\n      coords.push(cx, cy)\n    }\n  }\n\n  for (const [color, coords] of heatHexesByColor) {\n    drawHexBatch(ctx, coords, drawRadius, color)\n  }\n}\n\n// Batch-draw flat-top hexagons at given coordinates\nfunction drawHexBatch(ctx: Canvas2D, coords: number[], radius: number, color: string) {\n  ctx.fillStyle = color\n  ctx.beginPath()\n  for (let i = 0; i < coords.length; i += 2) {\n    const cx = coords[i]!\n    const cy = coords[i + 1]!\n    const [cos0, sin0] = HEX_ANGLES[0]!\n    ctx.moveTo(cx + radius * cos0, cy + radius * sin0)\n    for (let v = 1; v < 6; v++) {\n      const [cos, sin] = HEX_ANGLES[v]!\n      ctx.lineTo(cx + radius * cos, cy + radius * sin)\n    }\n    ctx.closePath()\n  }\n  ctx.fill()\n}\n\nfunction createGLTexture(gl: WebGLRenderingContext, canvas: HTMLCanvasElement | OffscreenCanvas): WebGLTexture | null {\n  const texture = gl.createTexture()\n  gl.bindTexture(gl.TEXTURE_2D, texture)\n  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas as TexImageSource)\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)\n  return texture\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/types.ts",
    "content": "import type { ComputedRef, Ref, ShallowRef } from 'vue'\nimport type { GeoJSONData, LocationData } from '@/types'\n\nexport interface GlobeColors {\n  globeFill: string\n}\n\nexport interface CountryColorTiers {\n  noData: string\n  tier1: string\n  tier2: string\n  tier3: string\n}\n\nexport interface HeatmapColorTiers {\n  tier1: string\n  tier2: string\n  tier3: string\n  tier4: string\n  tier5: string\n}\n\nexport interface WebGLGlobeContext {\n  canvasRef: Ref<HTMLCanvasElement | null>\n  width: Ref<number>\n  height: Ref<number>\n  countries: ShallowRef<GeoJSONData>\n  locations: ShallowRef<LocationData[]>\n  countryStats: ShallowRef<Map<string, number>>\n  maxCountryVisits: ComputedRef<number>\n  highest: ComputedRef<number>\n  colors: ComputedRef<GlobeColors>\n  countryColorTiers: ComputedRef<CountryColorTiers>\n  heatmapColorTiers: ComputedRef<HeatmapColorTiers>\n}\n\nexport interface ArcData {\n  startLat: number\n  startLng: number\n  endLat: number\n  endLng: number\n  color?: string\n}\n\nexport interface RippleData {\n  lat: number\n  lng: number\n  maxRadius?: number\n  duration?: number\n  color?: string\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/globe/useWebGLGlobe.ts",
    "content": "import type { InertiaState } from './interaction'\nimport type { ArcData, RippleData, WebGLGlobeContext } from './types'\nimport * as twgl from 'twgl.js'\nimport { ref, shallowRef } from 'vue'\nimport { parseColor } from './color'\nimport { createArcGeometry, latLngToXYZ } from './geometry'\nimport { setupGlobeInteraction } from './interaction'\nimport {\n  arcFragmentShader,\n  arcVertexShader,\n  earthFragmentShader,\n  earthVertexShader,\n  rippleFragmentShader,\n  rippleVertexShader,\n} from './shaders'\nimport { createCountryTexture } from './texture'\n\nexport type { ArcData, RippleData, WebGLGlobeContext }\n\nconst m4 = twgl.m4\n\nfunction deleteBufferInfo(gl: WebGLRenderingContext, bufferInfo: twgl.BufferInfo) {\n  if (bufferInfo.attribs) {\n    for (const attrib of Object.values(bufferInfo.attribs)) {\n      if (attrib.buffer)\n        gl.deleteBuffer(attrib.buffer)\n    }\n  }\n  if (bufferInfo.indices)\n    gl.deleteBuffer(bufferInfo.indices)\n}\n\nconst INTRO_SPIN_DURATION = 1000\n\n// Cached MAX_VERTEX_ATTRIBS per WebGL context (WeakMap avoids memory leaks)\nconst maxAttribsCache = new WeakMap<WebGLRenderingContext, number>()\n\nfunction disableAllAttribs(gl: WebGLRenderingContext) {\n  let maxAttribs = maxAttribsCache.get(gl)\n  if (maxAttribs === undefined) {\n    maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS) as number\n    maxAttribsCache.set(gl, maxAttribs)\n  }\n  for (let i = 0; i < maxAttribs; i++) {\n    gl.disableVertexAttribArray(i)\n  }\n}\n\n// Monotonic version counter for texture cache invalidation\nlet textureVersion = 0\n\ninterface ArcAnimation {\n  data: ArcData\n  startTime: number\n  duration: number\n  bufferInfo: twgl.BufferInfo\n  vertexCount: number\n  color: [number, number, number]\n}\n\ninterface RippleAnimation {\n  data: RippleData\n  startTime: number\n  color: [number, number, number]\n}\n\nexport function useWebGLGlobe(ctx: WebGLGlobeContext) {\n  // Reactive state\n  const longitude = ref(0)\n  const latitude = ref(0)\n  const zoom = ref(0)\n  const isAutoRotating = ref(true)\n  const isDragging = ref(false)\n  const isReady = ref(false)\n\n  // WebGL state\n  let gl: WebGLRenderingContext | null = null\n  let earthProgram: twgl.ProgramInfo | null = null\n  let arcProgram: twgl.ProgramInfo | null = null\n  let rippleProgram: twgl.ProgramInfo | null = null\n  let earthBufferInfo: twgl.BufferInfo | null = null\n  let countryTexture: WebGLTexture | null = null\n  let animationFrameId: number | null = null\n  let cleanupInteraction: (() => void) | null = null\n\n  // Inertia (shared between interaction and render loop)\n  const inertia: InertiaState = { velocityX: 0, velocityY: 0, isActive: false }\n\n  // Intro spin animation\n  let introSpinActive = false\n  let introSpinStartTime = 0\n  let introTargetLat = 0\n  let introTargetLng = 0\n  let introStartLng = 0\n  let introStartLat = 0\n\n  // Active animations\n  const activeArcs = shallowRef<ArcAnimation[]>([])\n  const activeRipples = shallowRef<RippleAnimation[]>([])\n\n  // Reusable ripple buffer\n  let rippleBufferInfo: twgl.BufferInfo | null = null\n  const ripplePositionData = new Float32Array(3)\n\n  // Sphere geometry (loaded async from prebuilt binary)\n  let sphereGeometry: { position: Float32Array, texcoord: Float32Array, indices: Uint16Array } | null = null\n\n  // Texture cache version\n  let lastTextureVersion = -1\n\n  // Render timing\n  let lastFrameTime = 0\n\n  // ============================================================================\n  // Camera\n  // ============================================================================\n\n  function getCameraValues() {\n    const w = ctx.width.value\n    const h = ctx.height.value\n    const aspect = w / h\n\n    const fov = (30 * Math.PI / 180) / Math.min(aspect, 1.0)\n    const projection = m4.perspective(fov, aspect, 0.01, 10)\n\n    const baseDistance = 1 / Math.tan(0.8 * fov / 2)\n    const distance = baseDistance * (1 - zoom.value * 0.7)\n    let camera = m4.identity()\n    camera = m4.rotateY(camera, (longitude.value + 180) * Math.PI / 180)\n    camera = m4.rotateX(camera, latitude.value * Math.PI / 180)\n\n    const eye = m4.transformPoint(camera, [0, 0, -distance]) as number[]\n    const up = m4.transformPoint(camera, [0, 1, 0]) as number[]\n    const view = m4.inverse(m4.lookAt(eye, [0, 0, 0], up))\n\n    return { view, projection, eye }\n  }\n\n  // ============================================================================\n  // Initialization\n  // ============================================================================\n\n  async function init() {\n    const canvas = ctx.canvasRef.value\n    if (!canvas)\n      return false\n\n    // Load prebuilt sphere geometry\n    if (!sphereGeometry) {\n      const buf = await $fetch<ArrayBuffer>('/sphere.bin', { responseType: 'arrayBuffer' })\n      const header = new Uint32Array(buf, 0, 3)\n      let offset = 12\n      const position = new Float32Array(buf, offset, header[0]! / 4)\n      offset += header[0]!\n      const texcoord = new Float32Array(buf, offset, header[1]! / 4)\n      offset += header[1]!\n      const indices = new Uint16Array(buf, offset, header[2]! / 2)\n      sphereGeometry = { position, texcoord, indices }\n    }\n\n    gl = canvas.getContext('webgl', { alpha: true, antialias: true })\n    if (!gl) {\n      console.error('WebGL not supported')\n      return false\n    }\n\n    earthProgram = twgl.createProgramInfo(gl, [earthVertexShader, earthFragmentShader])\n    arcProgram = twgl.createProgramInfo(gl, [arcVertexShader, arcFragmentShader])\n    rippleProgram = twgl.createProgramInfo(gl, [rippleVertexShader, rippleFragmentShader])\n\n    earthBufferInfo = twgl.createBufferInfoFromArrays(gl, {\n      position: { numComponents: 3, data: sphereGeometry.position },\n      texcoord: { numComponents: 2, data: sphereGeometry.texcoord },\n      indices: { numComponents: 3, data: sphereGeometry.indices },\n    })\n\n    gl.enable(gl.DEPTH_TEST)\n    gl.enable(gl.CULL_FACE)\n    gl.clearColor(0, 0, 0, 0)\n\n    const textures = twgl.createTextures(gl, {\n      placeholder: { min: gl.LINEAR, mag: gl.LINEAR, width: 1, height: 1, src: [0, 0, 0, 0] },\n    })\n    countryTexture = textures.placeholder ?? null\n\n    cleanupInteraction = setupGlobeInteraction(canvas, {\n      longitude,\n      latitude,\n      zoom,\n      isDragging,\n      stopAutoRotate,\n    }, inertia)\n\n    isReady.value = true\n    return true\n  }\n\n  function updateCanvasSize() {\n    const canvas = ctx.canvasRef.value\n    if (!canvas || !gl)\n      return\n\n    const w = ctx.width.value\n    const h = ctx.height.value\n    const dpr = window.devicePixelRatio || 1\n\n    canvas.width = w * dpr\n    canvas.height = h * dpr\n    canvas.style.width = `${w}px`\n    canvas.style.height = `${h}px`\n\n    gl.viewport(0, 0, canvas.width, canvas.height)\n  }\n\n  async function updateCountryTexture() {\n    if (!gl || !isReady.value)\n      return\n\n    const containerSize = Math.max(ctx.width.value, ctx.height.value)\n    if (containerSize < 1) {\n      // Retry once when container size becomes available\n      const unwatch = watch([ctx.width, ctx.height], () => {\n        if (Math.max(ctx.width.value, ctx.height.value) >= 1) {\n          unwatch()\n          updateCountryTexture()\n        }\n      })\n      return\n    }\n\n    const currentVersion = ++textureVersion\n    lastTextureVersion = currentVersion\n\n    const countries = ctx.countries.value\n    const stats = ctx.countryStats.value\n    const max = ctx.maxCountryVisits.value\n    const tiers = ctx.countryColorTiers.value\n    const colors = ctx.colors.value\n    const locs = ctx.locations.value\n    const high = ctx.highest.value\n    const heatTiers = ctx.heatmapColorTiers.value\n\n    // Keep old texture reference for delayed replacement\n    const oldTexture = countryTexture\n\n    const newTexture = await createCountryTexture(\n      gl,\n      countries,\n      stats,\n      max,\n      tiers,\n      colors.globeFill,\n      locs,\n      high,\n      heatTiers,\n      containerSize,\n    )\n\n    // Guard: gl may have been destroyed during async texture creation\n    if (!gl || !isReady.value) {\n      if (newTexture) {\n        // Context destroyed, but we still have a reference - can't delete without gl\n        // Just discard the reference; browser will GC the texture\n      }\n      return\n    }\n\n    // Discard stale result if a newer update was triggered\n    if (lastTextureVersion !== currentVersion) {\n      if (newTexture)\n        gl.deleteTexture(newTexture)\n      return\n    }\n\n    // Replace texture: assign new first, then delete old (avoids blank frame)\n    countryTexture = newTexture\n    if (oldTexture && oldTexture !== newTexture) {\n      gl.deleteTexture(oldTexture)\n    }\n  }\n\n  // ============================================================================\n  // Auto Rotation\n  // ============================================================================\n\n  function startAutoRotate() {\n    isAutoRotating.value = true\n  }\n\n  function stopAutoRotate() {\n    isAutoRotating.value = false\n  }\n\n  // ============================================================================\n  // Arc and Ripple\n  // ============================================================================\n\n  function drawArc(arcData: ArcData, duration: number = 2000) {\n    if (!gl)\n      return\n\n    const color = arcData.color ? parseColor(arcData.color) : [1.0, 0.6, 0.2] as [number, number, number]\n\n    const geom = createArcGeometry(\n      arcData.startLat,\n      arcData.startLng,\n      arcData.endLat,\n      arcData.endLng,\n      50,\n      1,\n    )\n\n    const bufferInfo = twgl.createBufferInfoFromArrays(gl, {\n      position: { numComponents: 3, data: geom.positions },\n      alpha: { numComponents: 1, data: geom.alphas },\n      dashParam: { numComponents: 1, data: geom.dashParams },\n    })\n\n    activeArcs.value = [...activeArcs.value, {\n      data: arcData,\n      startTime: performance.now(),\n      duration,\n      bufferInfo,\n      vertexCount: geom.positions.length / 3,\n      color,\n    }]\n  }\n\n  function drawRipple(rippleData: RippleData) {\n    const color = rippleData.color ? parseColor(rippleData.color) : [1.0, 0.6, 0.2] as [number, number, number]\n\n    activeRipples.value = [...activeRipples.value, {\n      data: rippleData,\n      startTime: performance.now(),\n      color,\n    }]\n  }\n\n  // ============================================================================\n  // Render Loop\n  // ============================================================================\n\n  function render() {\n    if (!gl || !earthProgram || !earthBufferInfo || !countryTexture)\n      return\n\n    const w = ctx.width.value\n    const h = ctx.height.value\n    if (w < 2 || h < 2)\n      return\n\n    const now = performance.now()\n    const dt = lastFrameTime > 0 ? Math.min((now - lastFrameTime) / 1000, 0.1) : 0.016\n    lastFrameTime = now\n\n    // Intro spin animation\n    if (introSpinActive) {\n      const elapsed = now - introSpinStartTime\n      const t = Math.min(elapsed / INTRO_SPIN_DURATION, 1)\n      const eased = 1 - (1 - t) ** 3\n      longitude.value = introStartLng + (introTargetLng - introStartLng) * eased\n      latitude.value = introStartLat + (introTargetLat - introStartLat) * eased\n\n      if (t >= 1) {\n        introSpinActive = false\n        longitude.value = introTargetLng\n        latitude.value = introTargetLat\n        isAutoRotating.value = true\n      }\n    }\n\n    // Auto-rotate\n    if (isAutoRotating.value && !introSpinActive) {\n      longitude.value = ((longitude.value - 9 * dt) % 360 + 540) % 360 - 180\n    }\n\n    // Inertia decay\n    if (inertia.isActive) {\n      const decay = Math.exp(-3.0 * dt)\n      inertia.velocityX *= decay\n      inertia.velocityY *= decay\n\n      const zoomFactor = 1 - zoom.value * 0.8\n      const dtMs = dt * 1000\n      longitude.value = ((longitude.value - inertia.velocityX * dtMs * 0.3 * zoomFactor) % 360 + 540) % 360 - 180\n      latitude.value = Math.max(-85, Math.min(85, latitude.value + inertia.velocityY * dtMs * 0.3 * zoomFactor))\n\n      if (Math.abs(inertia.velocityX) < 0.001 && Math.abs(inertia.velocityY) < 0.001) {\n        inertia.isActive = false\n      }\n    }\n\n    const { view, projection, eye } = getCameraValues()\n    const model = m4.identity()\n\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)\n\n    // Draw earth\n    disableAllAttribs(gl)\n    gl.useProgram(earthProgram.program)\n    twgl.setBuffersAndAttributes(gl, earthProgram, earthBufferInfo)\n    twgl.setUniforms(earthProgram, { model, view, projection, u_countryTexture: countryTexture })\n    twgl.drawBufferInfo(gl, earthBufferInfo)\n\n    // Draw arcs (ribbon triangle strips)\n    if (arcProgram && activeArcs.value.length > 0) {\n      gl.enable(gl.BLEND)\n      gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)\n      gl.depthMask(false)\n      gl.disable(gl.CULL_FACE)\n\n      const newArcs: ArcAnimation[] = []\n      for (const arc of activeArcs.value) {\n        const elapsed = now - arc.startTime\n        const progress = Math.min(elapsed / arc.duration, 1)\n\n        if (progress < 1 || elapsed < arc.duration + 500) {\n          const fadeProgress = progress >= 1 ? 1 - (elapsed - arc.duration) / 500 : 1\n          // Each arc point produces 2 vertices; ensure even count for triangle strip\n          let visibleVerts = Math.floor(arc.vertexCount * Math.min(progress, 1))\n          visibleVerts = visibleVerts & ~1\n\n          if (visibleVerts >= 4) {\n            disableAllAttribs(gl!)\n            gl!.useProgram(arcProgram!.program)\n            twgl.setBuffersAndAttributes(gl!, arcProgram!, arc.bufferInfo)\n            twgl.setUniforms(arcProgram!, { model, view, projection, u_color: arc.color, u_fade: fadeProgress, u_dashCount: 8.0, u_dashRatio: 0.8 })\n            gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, visibleVerts)\n          }\n          newArcs.push(arc)\n        }\n        else {\n          // Release GPU buffers for expired arcs\n          deleteBufferInfo(gl!, arc.bufferInfo)\n        }\n      }\n\n      activeArcs.value = newArcs\n      gl.enable(gl.CULL_FACE)\n      gl.depthMask(true)\n      gl.disable(gl.BLEND)\n    }\n\n    // Draw ripples (depth test off — shader backface check handles occlusion)\n    if (rippleProgram && activeRipples.value.length > 0) {\n      gl.enable(gl.BLEND)\n      gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)\n      gl.disable(gl.DEPTH_TEST)\n\n      if (!rippleBufferInfo) {\n        rippleBufferInfo = twgl.createBufferInfoFromArrays(gl, {\n          position: { numComponents: 3, data: ripplePositionData },\n        })\n      }\n\n      const newRipples: RippleAnimation[] = []\n      for (const ripple of activeRipples.value) {\n        const elapsed = now - ripple.startTime\n        const duration = ripple.data.duration ?? 1500\n        const progress = elapsed / duration\n\n        if (progress < 1) {\n          const maxRadius = ripple.data.maxRadius ?? 6\n          const projectedGlobeRadius = h * 0.4\n          const maxSize = maxRadius * projectedGlobeRadius * Math.PI / 180 * 4\n          const currentSize = Math.max(12, maxSize * progress)\n          const alpha = 1.0 - progress\n\n          const pos = latLngToXYZ(ripple.data.lat, ripple.data.lng, 1.005)\n          ripplePositionData[0] = pos[0]\n          ripplePositionData[1] = pos[1]\n          ripplePositionData[2] = pos[2]\n\n          const posAttrib = rippleBufferInfo!.attribs?.position\n          if (posAttrib) {\n            gl!.bindBuffer(gl!.ARRAY_BUFFER, posAttrib.buffer)\n            gl!.bufferData(gl!.ARRAY_BUFFER, ripplePositionData, gl!.DYNAMIC_DRAW)\n          }\n\n          disableAllAttribs(gl!)\n          gl!.useProgram(rippleProgram!.program)\n          twgl.setBuffersAndAttributes(gl!, rippleProgram!, rippleBufferInfo!)\n          twgl.setUniforms(rippleProgram!, {\n            model,\n            view,\n            projection,\n            u_pointSize: currentSize,\n            u_color: ripple.color,\n            u_eye: eye,\n            u_alpha: Math.min(alpha, 1.0),\n            u_ringWidth: 0.25 + 0.15 * (1 - progress),\n          })\n\n          gl!.drawArrays(gl!.POINTS, 0, 1)\n          newRipples.push(ripple)\n        }\n      }\n\n      activeRipples.value = newRipples\n      gl.enable(gl.DEPTH_TEST)\n      gl.disable(gl.BLEND)\n    }\n  }\n\n  function startRenderLoop() {\n    if (animationFrameId !== null)\n      return\n\n    function loop() {\n      render()\n      animationFrameId = requestAnimationFrame(loop)\n    }\n\n    animationFrameId = requestAnimationFrame(loop)\n  }\n\n  function stopRenderLoop() {\n    if (animationFrameId !== null) {\n      cancelAnimationFrame(animationFrameId)\n      animationFrameId = null\n    }\n  }\n\n  // ============================================================================\n  // Public API\n  // ============================================================================\n\n  function setPointOfView(lat: number, lng: number, animate: boolean = false) {\n    const targetLat = Math.max(-85, Math.min(85, lat))\n    const targetLng = lng\n\n    if (animate) {\n      introSpinActive = true\n      introSpinStartTime = performance.now()\n      introStartLng = targetLng + 360\n      introStartLat = targetLat\n      introTargetLng = targetLng\n      introTargetLat = targetLat\n      longitude.value = introStartLng\n      latitude.value = introStartLat\n      isAutoRotating.value = false\n    }\n    else {\n      latitude.value = targetLat\n      longitude.value = targetLng\n    }\n  }\n\n  function destroy() {\n    stopRenderLoop()\n    stopAutoRotate()\n    isReady.value = false\n\n    if (cleanupInteraction) {\n      cleanupInteraction()\n      cleanupInteraction = null\n    }\n\n    if (gl) {\n      // Release arc buffers\n      for (const arc of activeArcs.value) {\n        deleteBufferInfo(gl, arc.bufferInfo)\n      }\n      activeArcs.value = []\n      activeRipples.value = []\n\n      // Release ripple buffer\n      if (rippleBufferInfo) {\n        deleteBufferInfo(gl, rippleBufferInfo)\n        rippleBufferInfo = null\n      }\n\n      // Release earth geometry buffer\n      if (earthBufferInfo) {\n        deleteBufferInfo(gl, earthBufferInfo)\n        earthBufferInfo = null\n      }\n\n      // Release textures\n      if (countryTexture) {\n        gl.deleteTexture(countryTexture)\n        countryTexture = null\n      }\n\n      // Release shader programs\n      if (earthProgram) {\n        gl.deleteProgram(earthProgram.program)\n        earthProgram = null\n      }\n      if (arcProgram) {\n        gl.deleteProgram(arcProgram.program)\n        arcProgram = null\n      }\n      if (rippleProgram) {\n        gl.deleteProgram(rippleProgram.program)\n        rippleProgram = null\n      }\n\n      gl = null\n    }\n  }\n\n  return {\n    longitude,\n    latitude,\n    zoom,\n    isAutoRotating,\n    isDragging,\n    isReady,\n\n    init,\n    updateCanvasSize,\n    updateCountryTexture,\n    setPointOfView,\n    startAutoRotate,\n    stopAutoRotate,\n    drawArc,\n    drawRipple,\n    startRenderLoop,\n    stopRenderLoop,\n    destroy,\n\n    hasActiveAnimations: () => activeArcs.value.length > 0 || activeRipples.value.length > 0,\n  }\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/links.ts",
    "content": "import type { Link, LinkUpdateType } from '@/types'\nimport { defineStore } from '#imports'\nimport { createEventHook, tryOnScopeDispose } from '@vueuse/core'\nimport { ref } from 'vue'\n\nexport interface LinkUpdateEvent {\n  link: Link\n  type: LinkUpdateType\n}\n\nexport const useDashboardLinksStore = defineStore('dashboard-links', () => {\n  const sortBy = ref<'newest' | 'oldest' | 'az' | 'za'>('az')\n\n  const showLinkEditor = ref(false)\n  const editingLink = ref<Record<string, unknown> | null>(null)\n\n  const linkUpdateHook = createEventHook<LinkUpdateEvent>()\n\n  function openLinkEditor(link?: Record<string, unknown>) {\n    editingLink.value = link || null\n    showLinkEditor.value = true\n  }\n\n  function closeLinkEditor() {\n    showLinkEditor.value = false\n    editingLink.value = null\n  }\n\n  function notifyLinkUpdate(link: Link, type: LinkUpdateType) {\n    linkUpdateHook.trigger({ link, type })\n  }\n\n  function onLinkUpdate(callback: (event: LinkUpdateEvent) => void) {\n    const { off } = linkUpdateHook.on(callback)\n    tryOnScopeDispose(off)\n    return off\n  }\n\n  return {\n    sortBy,\n    showLinkEditor,\n    editingLink,\n    openLinkEditor,\n    closeLinkEditor,\n    notifyLinkUpdate,\n    onLinkUpdate,\n  }\n})\n"
  },
  {
    "path": "layers/dashboard/app/composables/realtime.ts",
    "content": "import { defineStore } from '#imports'\nimport { getLocalTimeZone, now } from '@internationalized/date'\nimport { useUrlSearchParams } from '@vueuse/core'\nimport { safeDestr } from 'destr'\nimport { ref, watch } from 'vue'\nimport { date2unix } from '@/utils/time'\n\nconst TIME_PRESETS: Record<string, { minutes?: number, hours?: number } | 'today'> = {\n  'today': 'today',\n  'last-5m': { minutes: 5 },\n  'last-10m': { minutes: 10 },\n  'last-30m': { minutes: 30 },\n  'last-1h': { hours: 1 },\n  'last-6h': { hours: 6 },\n  'last-12h': { hours: 12 },\n  'last-24h': { hours: 24 },\n}\n\nfunction computeTimeRange(name: string): [number, number] {\n  const tz = getLocalTimeZone()\n  const currentTime = now(tz)\n  const preset = TIME_PRESETS[name] ?? { hours: 1 }\n\n  if (preset === 'today') {\n    return [date2unix(currentTime, 'start'), date2unix(currentTime)]\n  }\n\n  return [date2unix(currentTime.subtract(preset)), date2unix(currentTime)]\n}\n\nexport const useDashboardRealtimeStore = defineStore('dashboard-realtime', () => {\n  const searchParams = useUrlSearchParams('history')\n  let initialized = false\n\n  const timeRange = ref({ startAt: 0, endAt: 0 })\n  const timeName = ref('last-1h')\n  const filters = ref<Record<string, string>>({})\n\n  function selectPreset(name: string) {\n    timeName.value = name\n    const [start, end] = computeTimeRange(name)\n    timeRange.value.startAt = start\n    timeRange.value.endAt = end\n  }\n\n  function updateFilter(type: string, value: string) {\n    filters.value[type] = value\n  }\n\n  function clearFilters() {\n    filters.value = {}\n  }\n\n  // URL > Store > Default, then enable URL sync\n  function init() {\n    if (initialized)\n      return\n\n    // Restore from URL\n    if (searchParams.time && typeof searchParams.time === 'string') {\n      timeName.value = searchParams.time\n    }\n    if (searchParams.filters && typeof searchParams.filters === 'string') {\n      const restored = safeDestr<Record<string, string>>(searchParams.filters)\n      if (restored) {\n        Object.assign(filters.value, restored)\n      }\n    }\n\n    // Apply default time range from preset if not restored\n    if (timeRange.value.startAt === 0) {\n      const [start, end] = computeTimeRange(timeName.value)\n      timeRange.value.startAt = start\n      timeRange.value.endAt = end\n    }\n\n    initialized = true\n  }\n\n  // Store → URL sync (only after init)\n  watch(timeName, (val) => {\n    if (!initialized)\n      return\n    searchParams.time = val\n  })\n\n  watch(filters, (val) => {\n    if (!initialized)\n      return\n    searchParams.filters = Object.keys(val).length ? JSON.stringify(val) : ''\n  }, { deep: true })\n\n  return {\n    timeRange,\n    timeName,\n    filters,\n    selectPreset,\n    updateFilter,\n    clearFilters,\n    init,\n  }\n})\n"
  },
  {
    "path": "layers/dashboard/app/composables/useTrafficEvent.ts",
    "content": "import type { ComputedRef, ShallowRef } from 'vue'\nimport type { ArcData, RippleData } from './globe/types'\nimport type { ColoData, TrafficEventParams } from '@/types'\n\nexport interface TrafficEventContext {\n  colos: ShallowRef<Record<string, ColoData>>\n  arcColor: ComputedRef<string>\n  globe: {\n    isReady: () => boolean\n    drawArc: (arcData: ArcData, duration?: number) => void\n    drawRipple: (rippleData: RippleData) => void\n  }\n}\n\nexport function useTrafficEvent(ctx: TrafficEventContext) {\n  const pendingTimeouts = new Set<ReturnType<typeof setTimeout>>()\n\n  function handleTrafficEvent({ props }: TrafficEventParams, { delay = 1200 }: { delay?: number } = {}) {\n    if (!ctx.globe.isReady())\n      return\n\n    const { item } = props\n    const { latitude, longitude, COLO, city } = item\n    if (latitude == null || longitude == null || !COLO) {\n      console.warn('Missing location data for traffic event', item)\n      return\n    }\n\n    const colo = ctx.colos.value[COLO]\n    const endLat = colo?.lat\n    const endLng = colo?.lon\n\n    if (endLat === undefined || endLng === undefined) {\n      console.warn(`Missing COLO coordinates for ${COLO}`)\n      return\n    }\n\n    // Near trajectories: show only destination ripple, skip arc\n    const isNear = Math.abs(endLat - latitude) < 5 && Math.abs(endLng - longitude) < 5\n    const color = ctx.arcColor.value\n\n    if (isNear) {\n      if (import.meta.dev)\n        console.info(`from ${city} to ${COLO} is near, ripple only`)\n      ctx.globe.drawRipple({\n        lat: endLat,\n        lng: endLng,\n        maxRadius: 6,\n        duration: delay * 1.2,\n        color,\n      })\n      return\n    }\n\n    if (import.meta.dev)\n      console.info(`from ${city}(${latitude}, ${longitude}) to ${COLO}(${endLat}, ${endLng})`)\n\n    // Draw arc\n    ctx.globe.drawArc({\n      startLat: latitude,\n      startLng: longitude,\n      endLat,\n      endLng,\n      color,\n    }, delay)\n\n    // Draw start ripple\n    ctx.globe.drawRipple({\n      lat: latitude,\n      lng: longitude,\n      maxRadius: 3,\n      duration: delay * 0.8,\n      color,\n    })\n\n    // Draw end ripple after delay\n    const timeoutId = setTimeout(() => {\n      pendingTimeouts.delete(timeoutId)\n      ctx.globe.drawRipple({\n        lat: endLat,\n        lng: endLng,\n        maxRadius: 6,\n        duration: delay * 1.2,\n        color,\n      })\n    }, delay)\n    pendingTimeouts.add(timeoutId)\n  }\n\n  function cleanup() {\n    pendingTimeouts.forEach(id => clearTimeout(id))\n    pendingTimeouts.clear()\n  }\n\n  return {\n    handleTrafficEvent,\n    cleanup,\n  }\n}\n"
  },
  {
    "path": "layers/dashboard/app/composables/useVersionCheck.ts",
    "content": "import { useFetch } from '#imports'\nimport { version as currentVersion } from '@@/package.json'\nimport { computed } from 'vue'\n\nexport function useVersionCheck() {\n  const { data } = useFetch<{ version: string }>('https://cdn.jsdelivr.net/gh/miantiao-me/Sink@master/package.json')\n\n  const latestVersion = computed(() => data.value?.version)\n  const hasUpdate = computed(() => latestVersion.value ? compareVersion(latestVersion.value, currentVersion) > 0 : false)\n\n  return { hasUpdate, currentVersion, latestVersion }\n}\n\nfunction compareVersion(a: string, b: string): number {\n  const pa = a.split('.').map(Number)\n  const pb = b.split('.').map(Number)\n  for (let i = 0; i < 3; i++) {\n    if ((pa[i] || 0) > (pb[i] || 0))\n      return 1\n    if ((pa[i] || 0) < (pb[i] || 0))\n      return -1\n  }\n  return 0\n}\n"
  },
  {
    "path": "layers/dashboard/app/layouts/dashboard.vue",
    "content": "<script setup lang=\"ts\">\nimport { useScroll } from '@vueuse/core'\n\nconst { pageTitle } = useDashboardRoute()\nconst route = useRoute()\n\nconst scrollContainer = ref<HTMLElement | null>(null)\nconst { y } = useScroll(scrollContainer)\n\nwatch(() => route.fullPath, () => {\n  y.value = 0\n})\n\nuseSeoMeta({\n  robots: 'noindex, nofollow',\n})\n</script>\n\n<template>\n  <SidebarProvider>\n    <DashboardSidebarAppSidebar />\n    <SidebarInset\n      class=\"\n        max-h-svh overflow-hidden\n        md:max-h-[calc(100svh-1rem)]\n      \"\n    >\n      <div class=\"flex h-full flex-col\">\n        <header\n          class=\"\n            z-20 flex shrink-0 flex-col gap-2 border-b bg-background p-4\n            sm:h-16 sm:flex-row sm:items-center sm:gap-2 sm:py-0\n          \"\n        >\n          <div class=\"flex items-center gap-2\">\n            <SidebarTrigger class=\"-ml-1\" />\n            <Separator\n              orientation=\"vertical\"\n              class=\"\n                mr-2\n                data-[orientation=vertical]:h-4\n              \"\n            />\n            <DashboardPageBreadcrumb :title=\"$t(pageTitle ?? 'dashboard.title')\" />\n          </div>\n\n          <div\n            id=\"dashboard-header-actions\"\n            class=\"flex flex-1 flex-wrap items-center justify-end gap-2\"\n          />\n        </header>\n\n        <div ref=\"scrollContainer\" class=\"flex-1 overflow-y-auto p-4\">\n          <slot />\n        </div>\n      </div>\n    </SidebarInset>\n  </SidebarProvider>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/middleware/auth.global.ts",
    "content": "export default defineNuxtRouteMiddleware(async (to) => {\n  if (import.meta.server)\n    return\n\n  const { getToken } = useAuthToken()\n\n  if (to.path.startsWith('/dashboard') && to.path !== '/dashboard/login') {\n    if (!getToken())\n      return navigateTo('/dashboard/login')\n  }\n\n  if (to.path === '/dashboard/login') {\n    try {\n      await useAPI('/api/verify')\n      return navigateTo('/dashboard')\n    }\n    catch (e) {\n      console.warn(e)\n    }\n  }\n})\n"
  },
  {
    "path": "layers/dashboard/app/pages/dashboard/analysis.vue",
    "content": "<script setup lang=\"ts\">\ndefinePageMeta({\n  layout: 'dashboard',\n})\n\nprovide(LINK_ID_KEY, computed(() => undefined))\n\nconst analysisStore = useDashboardAnalysisStore()\n\nfunction handleFilterChange(type: string, value: string) {\n  analysisStore.updateFilter(type, value)\n}\n</script>\n\n<template>\n  <main class=\"space-y-6\">\n    <Teleport to=\"#dashboard-header-actions\" defer>\n      <div\n        class=\"\n          flex-1\n          sm:hidden\n        \"\n      />\n      <DashboardDatePicker />\n      <DashboardFilters :filters=\"analysisStore.filters\" @change=\"handleFilterChange\" />\n    </Teleport>\n\n    <DashboardAnalysis />\n  </main>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/pages/dashboard/link.vue",
    "content": "<script setup lang=\"ts\">\nimport type { Link } from '@/types'\n\ndefinePageMeta({\n  layout: 'dashboard',\n})\n\nconst slug = useRoute().query.slug\nconst linksStore = useDashboardLinksStore()\n\nconst link = ref<Link | null>(null)\nconst id = computed(() => link.value?.id)\n\nprovide(LINK_ID_KEY, id)\n\nasync function getLink() {\n  const data = await useAPI<Link>('/api/link/query', {\n    query: { slug },\n  })\n  link.value = data\n}\n\nonMounted(() => {\n  getLink()\n})\n\nlinksStore.onLinkUpdate(({ link: updatedLink, type }) => {\n  if (updatedLink.id !== link.value?.id)\n    return\n\n  if (type === 'delete') {\n    navigateTo('/dashboard/links', { replace: true })\n  }\n  else if (type === 'edit') {\n    link.value = updatedLink\n  }\n})\n</script>\n\n<template>\n  <main class=\"space-y-6\">\n    <Teleport to=\"#dashboard-header-actions\" defer>\n      <div\n        class=\"\n          flex-1\n          sm:hidden\n        \"\n      />\n      <DashboardDatePicker />\n    </Teleport>\n\n    <DashboardLinksLink\n      v-if=\"link?.id\"\n      :link=\"link\"\n    />\n    <DashboardAnalysis\n      v-if=\"link?.id\"\n      :link=\"link\"\n    />\n  </main>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/pages/dashboard/links.vue",
    "content": "<script setup lang=\"ts\">\ndefinePageMeta({\n  layout: 'dashboard',\n})\n</script>\n\n<template>\n  <main class=\"space-y-6\">\n    <Teleport to=\"#dashboard-header-actions\" defer>\n      <DashboardLinksEditor />\n      <div\n        class=\"\n          flex-1\n          sm:hidden\n        \"\n      />\n      <DashboardLinksSort />\n      <DashboardLinksSearch\n        class=\"max-sm:w-full\"\n      />\n    </Teleport>\n\n    <DashboardLinks />\n  </main>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/pages/dashboard/login.vue",
    "content": "<script setup lang=\"ts\">\ndefinePageMeta({\n  layout: 'default',\n})\n</script>\n\n<template>\n  <div class=\"flex flex-1 items-center justify-center\">\n    <Login />\n  </div>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/pages/dashboard/migrate.vue",
    "content": "<script setup lang=\"ts\">\ndefinePageMeta({\n  layout: 'dashboard',\n})\n</script>\n\n<template>\n  <main class=\"space-y-6\">\n    <DashboardMigrate />\n  </main>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/pages/dashboard/realtime.vue",
    "content": "<script setup lang=\"ts\">\ndefinePageMeta({\n  layout: 'dashboard',\n})\n\nconst realtimeStore = useDashboardRealtimeStore()\n\nfunction handleFilterChange(type: string, value: string) {\n  realtimeStore.updateFilter(type, value)\n}\n</script>\n\n<template>\n  <main\n    class=\"\n      w-full\n      md:h-full md:overflow-hidden\n    \"\n  >\n    <Teleport to=\"#dashboard-header-actions\" defer>\n      <div\n        class=\"\n          flex-1\n          sm:hidden\n        \"\n      />\n      <DashboardTimePicker />\n      <DashboardFilters :filters=\"realtimeStore.filters\" @change=\"handleFilterChange\" />\n    </Teleport>\n\n    <DashboardRealtime />\n  </main>\n</template>\n"
  },
  {
    "path": "layers/dashboard/app/utils/injection-keys.ts",
    "content": "import type { ComputedRef, InjectionKey } from 'vue'\n\nexport const LINK_ID_KEY: InjectionKey<ComputedRef<string | undefined>> = Symbol('linkId')\n\n// eslint-disable-next-line symbol-description\nexport const FORM_ITEM_INJECTION_KEY = Symbol() as InjectionKey<string>\n"
  },
  {
    "path": "layers/dashboard/nuxt.config.ts",
    "content": "// Dashboard Layer - Client-side only rendering\nexport default defineNuxtConfig({\n  ssr: false,\n\n  routeRules: {\n    '/dashboard/**': {\n      prerender: true,\n    },\n    '/dashboard': {\n      redirect: '/dashboard/links',\n    },\n  },\n})\n"
  },
  {
    "path": "layers/dashboard/shared/types/events.ts",
    "content": "export interface LogEvent {\n  id: string\n  slug: string\n  os?: string\n  browser?: string\n  country?: string\n  city?: string\n  latitude?: number\n  longitude?: number\n  COLO?: string\n  timestamp: number\n}\n\nexport interface ArcData {\n  startLat: number\n  startLng: number\n  endLat: number\n  endLng: number\n  color: string\n  arcAltitude: number\n}\n\nexport interface ColoData {\n  lat: number\n  lon: number\n}\n"
  },
  {
    "path": "layers/dashboard/shared/types/index.ts",
    "content": "export * from './events'\nexport * from './metrics'\n"
  },
  {
    "path": "layers/dashboard/shared/types/metrics.ts",
    "content": "export interface MetricItem {\n  name: string\n  count: number\n  color?: string\n  percent?: number\n}\n\nexport interface ViewDataPoint {\n  time: string\n  visitors: number\n  visits: number\n}\n\nexport interface CounterData {\n  visits: number\n  visitors: number\n  referers: number\n}\n\nexport interface LocationData {\n  lat: number\n  lng: number\n  count: number\n}\n\nexport interface DateRange {\n  startAt: number\n  endAt: number\n}\n\nexport interface GeoJSONData {\n  features: unknown[]\n  type?: string\n}\n\nexport interface CurrentLocation {\n  latitude?: number\n  longitude?: number\n}\n\nexport interface AreaData {\n  id: string\n  name: string\n  count: number\n}\n\nexport interface HeatmapDataPoint {\n  weekday: number\n  hour: number\n  visits: number\n  visitors: number\n}\n"
  },
  {
    "path": "nuxt.config.ts",
    "content": "import process from 'node:process'\nimport tailwindcss from '@tailwindcss/vite'\nimport { currentLocales } from './i18n/i18n'\n\n// https://nuxt.com/docs/api/configuration/nuxt-config\nexport default defineNuxtConfig({\n  extends: ['./layers/dashboard'],\n  modules: [\n    '@nuxtjs/color-mode',\n    '@nuxtjs/i18n',\n    '@nuxt/eslint',\n    '@pinia/nuxt',\n    '@vueuse/motion/nuxt',\n    'shadcn-nuxt',\n  ],\n  devtools: { enabled: true },\n  css: ['@/assets/css/tailwind.css'],\n  colorMode: {\n    classSuffix: '',\n  },\n  runtimeConfig: {\n    siteToken: process.env.NUXT_SITE_TOKEN || crypto.randomUUID(),\n    redirectStatusCode: '301',\n    linkCacheTtl: 60,\n    redirectWithQuery: false,\n    homeURL: '',\n    cfAccountId: '',\n    cfApiToken: '',\n    dataset: 'sink',\n    aiModel: '@cf/qwen/qwen3-30b-a3b-fp8',\n    aiPrompt: `You are a URL shortening assistant, please shorten the URL provided by the user into a SLUG. The SLUG information should be derived from the URL and page content (if provided). Do not make any assumptions beyond the given information. A SLUG is human-readable and should not exceed three words and can be validated using regular expressions {slugRegex} . Only the best one is returned, the format must be JSON reference {\"slug\": \"example-slug\"}`,\n    caseSensitive: false,\n    listQueryLimit: 500,\n    disableBotAccessLog: false,\n    disableAutoBackup: false,\n    notFoundRedirect: '',\n    safeBrowsingDoh: '', // Set to DoH URL to enable auto-detection, e.g. https://family.cloudflare-dns.com/dns-query\n    public: {\n      previewMode: '',\n      slugDefaultLength: '6',\n      kvBatchLimit: '50',\n    },\n  },\n  routeRules: {\n    '/': {\n      prerender: true,\n    },\n    '/api/**': {\n      cors: process.env.NUXT_API_CORS === 'true',\n    },\n    '/sphere.bin': {\n      headers: { 'Cache-Control': 'public, max-age=2592000, immutable' },\n    },\n    '/*.json': {\n      headers: { 'Cache-Control': 'public, max-age=2592000, immutable' },\n    },\n    '/*.geojson': {\n      headers: { 'Cache-Control': 'public, max-age=2592000, immutable' },\n    },\n  },\n  experimental: {\n    enforceModuleCompatibility: true,\n  },\n  typescript: {\n    tsConfig: {\n      compilerOptions: {\n        types: ['vite/client'],\n      },\n    },\n  },\n  compatibilityDate: 'latest',\n  nitro: {\n    preset: !import.meta.env.CI ? 'cloudflare-module' : undefined,\n    experimental: {\n      openAPI: true,\n    },\n    timing: true,\n    openAPI: {\n      production: 'runtime',\n      meta: {\n        title: 'Sink API',\n        description: 'A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.',\n      },\n      route: '/_docs/openapi.json',\n      ui: {\n        scalar: {\n          route: '/_docs/scalar',\n        },\n        swagger: {\n          route: '/_docs/swagger',\n        },\n      },\n    },\n  },\n  vite: {\n    plugins: [\n      tailwindcss(),\n    ],\n    worker: {\n      format: 'es',\n    },\n  },\n  eslint: {\n    config: {\n      standalone: false,\n    },\n  },\n  i18n: {\n    locales: currentLocales,\n    compilation: {\n      strictMessage: false,\n      escapeHtml: true,\n    },\n    strategy: 'no_prefix',\n    detectBrowserLanguage: {\n      useCookie: true,\n      cookieKey: 'sink_i18n_redirected',\n      redirectOn: 'root',\n    },\n    baseUrl: '/',\n    defaultLocale: 'en-US',\n  },\n  shadcn: {\n    /**\n     * Prefix for all the imported component\n     */\n    prefix: '',\n    /**\n     * Directory that the component lives in.\n     * @default \"./components/ui\"\n     */\n    componentDir: './app/components/ui',\n  },\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sink\",\n  \"type\": \"module\",\n  \"version\": \"0.2.7\",\n  \"private\": true,\n  \"packageManager\": \"pnpm@10.28.2\",\n  \"engines\": {\n    \"node\": \">=22\"\n  },\n  \"scripts\": {\n    \"dev\": \"nuxt dev --port 7465\",\n    \"analyze\": \"nuxt analyze\",\n    \"test\": \"vitest\",\n    \"build\": \"NODE_OPTIONS=--max-old-space-size=8192 nuxt build\",\n    \"build:map\": \"node scripts/build-map.js\",\n    \"build:sphere\": \"node scripts/build-sphere.js\",\n    \"build:colo\": \"node scripts/build-colo.js\",\n    \"build:testimonials\": \"node scripts/build-testimonials.js\",\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"lint-staged\": \"lint-staged\",\n    \"types:check\": \"nuxt typecheck\",\n    \"wrangler\": \"wrangler\",\n    \"gen:types\": \"wrangler types\",\n    \"deploy\": \"npm run deploy:pages\",\n    \"deploy:pages\": \"wrangler pages deploy dist\",\n    \"deploy:worker\": \"wrangler deploy .output/server/index.mjs --assets .output/public\",\n    \"preview\": \"wrangler dev .output/server/index.mjs --assets .output/public \",\n    \"postinstall\": \"npm run build:map && nuxt prepare\",\n    \"prepare\": \"simple-git-hooks\"\n  },\n  \"devDependencies\": {\n    \"@anatine/zod-mock\": \"^3.14.0\",\n    \"@antfu/eslint-config\": \"^7.2.0\",\n    \"@cloudflare/vitest-pool-workers\": \"^0.12.9\",\n    \"@internationalized/date\": \"^3.11.0\",\n    \"@intlify/message-compiler\": \"^11.2.8\",\n    \"@number-flow/vue\": \"^0.4.9\",\n    \"@nuxt/eslint\": \"^1.13.0\",\n    \"@nuxt/eslint-config\": \"^1.13.0\",\n    \"@nuxtjs/color-mode\": \"^4.0.0\",\n    \"@nuxtjs/i18n\": \"^10.2.1\",\n    \"@pinia/nuxt\": \"^0.11.3\",\n    \"@tailwindcss/vite\": \"^4.1.18\",\n    \"@tanstack/vue-form\": \"^1.28.0\",\n    \"@tanstack/vue-table\": \"^8.21.3\",\n    \"@types/d3-geo\": \"^3.1.0\",\n    \"@types/d3-scale\": \"^4.0.9\",\n    \"@types/node\": \"^25.2.0\",\n    \"@unovis/ts\": \"^1.6.4\",\n    \"@unovis/vue\": \"^1.6.4\",\n    \"@vueuse/core\": \"^14.2.0\",\n    \"@vueuse/integrations\": \"^14.2.0\",\n    \"@vueuse/motion\": \"^3.0.3\",\n    \"baseline-browser-mapping\": \"^2.9.19\",\n    \"class-variance-authority\": \"^0.7.1\",\n    \"clsx\": \"^2.1.1\",\n    \"d3-geo\": \"^3.1.1\",\n    \"d3-scale\": \"^4.0.2\",\n    \"destr\": \"^2.0.5\",\n    \"embla-carousel-vue\": \"^8.6.0\",\n    \"es-toolkit\": \"^1.44.0\",\n    \"eslint\": \"^9.39.2\",\n    \"eslint-plugin-better-tailwindcss\": \"^3.8.0\",\n    \"eslint-plugin-format\": \"^1.3.1\",\n    \"fuse.js\": \"^7.1.0\",\n    \"intl-parse-accept-language\": \"^1.0.0\",\n    \"lint-staged\": \"^16.2.7\",\n    \"lucide-vue-next\": \"^0.563.0\",\n    \"mysql-bricks\": \"^2.0.0\",\n    \"nanoid\": \"^5.1.6\",\n    \"nuxt\": \"^4.3.0\",\n    \"p-limit\": \"^7.3.0\",\n    \"pluralize\": \"^8.0.0\",\n    \"qr-code-styling\": \"^1.9.2\",\n    \"reka-ui\": \"^2.8.0\",\n    \"shadcn-nuxt\": \"^2.4.3\",\n    \"simple-git-hooks\": \"^2.13.1\",\n    \"sql-bricks\": \"^3.0.1\",\n    \"tailwind-merge\": \"^3.4.0\",\n    \"tailwindcss\": \"^4.1.18\",\n    \"tw-animate-css\": \"^1.4.0\",\n    \"twgl.js\": \"^7.0.0\",\n    \"typescript\": \"^5.9.3\",\n    \"ua-parser-js\": \"^2.0.9\",\n    \"ufo\": \"^1.6.3\",\n    \"vaul-vue\": \"^0.4.1\",\n    \"virtua\": \"^0.48.5\",\n    \"vite\": \"^7.3.1\",\n    \"vitest\": \"^3.2.4\",\n    \"vue-input-otp\": \"^0.3.2\",\n    \"vue-sonner\": \"^2.0.9\",\n    \"vue-tsc\": \"^3.2.4\",\n    \"vue3-simple-icons\": \"^15.6.0\",\n    \"wrangler\": \"^4.62.0\",\n    \"zod\": \"^3.25.76\"\n  },\n  \"simple-git-hooks\": {\n    \"pre-commit\": \"npm run lint-staged\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,ts,tsx,vue}\": [\n      \"eslint --fix\"\n    ]\n  }\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "onlyBuiltDependencies:\n  - '@tailwindcss/oxide'\n  - esbuild\n  - maplibre-gl\n  - sharp\n  - simple-git-hooks\n  - unrs-resolver\n  - vue-demi\n  - workerd\n"
  },
  {
    "path": "public/colos.json",
    "content": "{\"ZRH\":{\"lat\":47.464699,\"lon\":8.54917},\"ZDM\":{\"lat\":32.2719,\"lon\":35.0194},\"ZAG\":{\"lat\":45.742901,\"lon\":16.0688},\"YYZ\":{\"lat\":43.6772,\"lon\":-79.6306},\"YYC\":{\"lat\":51.113899,\"lon\":-114.019997},\"YXE\":{\"lat\":52.170799,\"lon\":-106.699997},\"YWG\":{\"lat\":49.91,\"lon\":-97.239899},\"YVR\":{\"lat\":49.193901,\"lon\":-123.183998},\"YUL\":{\"lat\":45.4706,\"lon\":-73.740799},\"YOW\":{\"lat\":45.322498,\"lon\":-75.669197},\"YHZ\":{\"lat\":44.880798,\"lon\":-63.508598},\"XNH\":{\"lat\":30.935801,\"lon\":46.090099},\"XAP\":{\"lat\":-27.134199,\"lon\":-52.656601},\"WRO\":{\"lat\":51.102699,\"lon\":16.885799},\"WDH\":{\"lat\":-22.4799,\"lon\":17.4709},\"WAW\":{\"lat\":52.165699,\"lon\":20.9671},\"VTE\":{\"lat\":17.9883,\"lon\":102.563004},\"VNO\":{\"lat\":54.634102,\"lon\":25.285801},\"VIX\":{\"lat\":-20.258057,\"lon\":-40.286388},\"VIE\":{\"lat\":48.110298,\"lon\":16.5697},\"VCP\":{\"lat\":-23.007401,\"lon\":-47.134499},\"URT\":{\"lat\":9.1326,\"lon\":99.135597},\"ULN\":{\"lat\":47.843102,\"lon\":106.766998},\"UIO\":{\"lat\":-0.129167,\"lon\":-78.3575},\"UDI\":{\"lat\":-18.883612,\"lon\":-48.225277},\"TXL\":{\"lat\":52.5597,\"lon\":13.2877},\"TUN\":{\"lat\":36.851002,\"lon\":10.2272},\"TPE\":{\"lat\":25.0777,\"lon\":121.233002},\"TPA\":{\"lat\":27.9755,\"lon\":-82.533203},\"TNR\":{\"lat\":-18.7969,\"lon\":47.478802},\"TLV\":{\"lat\":32.011398,\"lon\":34.8867},\"TLL\":{\"lat\":59.4133,\"lon\":24.8328},\"TLH\":{\"lat\":30.3965,\"lon\":-84.350304},\"TIA\":{\"lat\":41.4147,\"lon\":19.7206},\"TGU\":{\"lat\":14.0609,\"lon\":-87.217201},\"TBS\":{\"lat\":41.669201,\"lon\":44.9547},\"SYD\":{\"lat\":-33.946098,\"lon\":151.177002},\"SUV\":{\"lat\":-18.043301,\"lon\":178.559006},\"STR\":{\"lat\":48.689899,\"lon\":9.22196},\"STL\":{\"lat\":38.748699,\"lon\":-90.370003},\"STI\":{\"lat\":19.406099,\"lon\":-70.604698},\"SSA\":{\"lat\":-12.068355,\"lon\":-45.711454},\"SOF\":{\"lat\":42.696693,\"lon\":23.411436},\"SOD\":{\"lat\":-23.478001,\"lon\":-47.490002},\"SMF\":{\"lat\":38.6954,\"lon\":-121.591003},\"SLC\":{\"lat\":40.788399,\"lon\":-111.977997},\"SKP\":{\"lat\":41.961601,\"lon\":21.621401},\"SKG\":{\"lat\":40.519699,\"lon\":22.9709},\"SJU\":{\"lat\":18.4394,\"lon\":-66.001801},\"SJP\":{\"lat\":-20.816601,\"lon\":-49.406502},\"SJO\":{\"lat\":9.99386,\"lon\":-84.208801},\"SJK\":{\"lat\":-23.2292,\"lon\":-45.8615},\"SJC\":{\"lat\":37.362598,\"lon\":-121.929001},\"SIN\":{\"lat\":1.35019,\"lon\":103.994003},\"SGN\":{\"lat\":10.8188,\"lon\":106.652},\"SFO\":{\"lat\":37.618999,\"lon\":-122.375},\"SEA\":{\"lat\":47.449001,\"lon\":-122.308998},\"SDQ\":{\"lat\":18.429701,\"lon\":-69.6689},\"SCL\":{\"lat\":-33.393002,\"lon\":-70.785797},\"SAT\":{\"lat\":29.533701,\"lon\":-98.469803},\"SAP\":{\"lat\":15.4526,\"lon\":-87.923599},\"SAN\":{\"lat\":32.733601,\"lon\":-117.190002},\"RUN\":{\"lat\":-20.8871,\"lon\":55.5103},\"RUH\":{\"lat\":24.9576,\"lon\":46.698799},\"RIX\":{\"lat\":56.923599,\"lon\":23.9711},\"RIC\":{\"lat\":37.505199,\"lon\":-77.319702},\"REC\":{\"lat\":-8.12649,\"lon\":-34.923599},\"RDU\":{\"lat\":35.877602,\"lon\":-78.787498},\"RAO\":{\"lat\":-21.136389,\"lon\":-47.776669},\"QWJ\":{\"lat\":-22.738,\"lon\":-47.334},\"QRO\":{\"lat\":20.6173,\"lon\":-100.185997},\"PTY\":{\"lat\":9.07136,\"lon\":-79.383499},\"PRG\":{\"lat\":50.1008,\"lon\":14.26},\"PPT\":{\"lat\":-17.553699,\"lon\":-149.606995},\"POS\":{\"lat\":10.5954,\"lon\":-61.3372},\"POA\":{\"lat\":-29.9944,\"lon\":-51.171398},\"PNH\":{\"lat\":11.5466,\"lon\":104.844002},\"PMW\":{\"lat\":-10.2915,\"lon\":-48.356998},\"PMO\":{\"lat\":38.175999,\"lon\":13.091},\"PIT\":{\"lat\":40.491501,\"lon\":-80.232903},\"PHX\":{\"lat\":33.434299,\"lon\":-112.012001},\"PHL\":{\"lat\":39.871899,\"lon\":-75.241096},\"PER\":{\"lat\":-31.9403,\"lon\":115.967003},\"PDX\":{\"lat\":45.588699,\"lon\":-122.598},\"PBM\":{\"lat\":5.45283,\"lon\":-55.187801},\"PBH\":{\"lat\":27.4032,\"lon\":89.424599},\"PAT\":{\"lat\":25.591299,\"lon\":85.087997},\"OUA\":{\"lat\":12.3532,\"lon\":-1.51242},\"OTP\":{\"lat\":44.572201,\"lon\":26.1022},\"OSL\":{\"lat\":60.193901,\"lon\":11.1004},\"ORN\":{\"lat\":35.623901,\"lon\":-0.621183},\"ORF\":{\"lat\":36.8946,\"lon\":-76.201202},\"ORD\":{\"lat\":41.9786,\"lon\":-87.9048},\"OMA\":{\"lat\":41.3032,\"lon\":-95.894096},\"OKC\":{\"lat\":35.393101,\"lon\":-97.6007},\"OKA\":{\"lat\":26.195801,\"lon\":127.646004},\"NVT\":{\"lat\":-26.879999,\"lon\":-48.651402},\"NRT\":{\"lat\":35.764702,\"lon\":140.386002},\"NQZ\":{\"lat\":51.022202,\"lon\":71.466904},\"NQN\":{\"lat\":-38.949001,\"lon\":-68.155701},\"NOU\":{\"lat\":-22.014601,\"lon\":166.212997},\"NJF\":{\"lat\":31.989722,\"lon\":44.404167},\"NBO\":{\"lat\":-1.31924,\"lon\":36.927799},\"NAG\":{\"lat\":21.092199,\"lon\":79.047203},\"MXP\":{\"lat\":45.6306,\"lon\":8.72811},\"MUC\":{\"lat\":48.353802,\"lon\":11.7861},\"MSQ\":{\"lat\":53.8825,\"lon\":28.030701},\"MSP\":{\"lat\":44.882,\"lon\":-93.221802},\"MRU\":{\"lat\":-20.430201,\"lon\":57.683601},\"MRS\":{\"lat\":43.439272,\"lon\":5.221424},\"MPM\":{\"lat\":-25.920799,\"lon\":32.572601},\"MNL\":{\"lat\":14.5086,\"lon\":121.019997},\"MLG\":{\"lat\":-7.92656,\"lon\":112.714996},\"MLE\":{\"lat\":4.19183,\"lon\":73.529099},\"MLA\":{\"lat\":35.857498,\"lon\":14.4775},\"MIA\":{\"lat\":25.7932,\"lon\":-80.290604},\"MFM\":{\"lat\":22.149599,\"lon\":113.592003},\"MEX\":{\"lat\":19.4363,\"lon\":-99.072098},\"MEM\":{\"lat\":35.0424,\"lon\":-89.9767},\"MEL\":{\"lat\":-37.673302,\"lon\":144.843002},\"MDE\":{\"lat\":6.16454,\"lon\":-75.4231},\"MCT\":{\"lat\":23.5933,\"lon\":58.284401},\"MCI\":{\"lat\":39.2976,\"lon\":-94.713898},\"MBA\":{\"lat\":-4.03483,\"lon\":39.5942},\"MAO\":{\"lat\":-3.03861,\"lon\":-60.049702},\"MAN\":{\"lat\":53.353699,\"lon\":-2.27495},\"MAD\":{\"lat\":40.4936,\"lon\":-3.56676},\"MAA\":{\"lat\":12.990005,\"lon\":80.169296},\"LYS\":{\"lat\":45.726398,\"lon\":5.09083},\"LUX\":{\"lat\":49.626598,\"lon\":6.21152},\"LUN\":{\"lat\":-15.3308,\"lon\":28.4526},\"LPB\":{\"lat\":-16.5133,\"lon\":-68.192299},\"LOS\":{\"lat\":6.57737,\"lon\":3.32116},\"LLW\":{\"lat\":-13.7894,\"lon\":33.780998},\"LLK\":{\"lat\":38.746399,\"lon\":48.818001},\"LIS\":{\"lat\":38.7813,\"lon\":-9.13592},\"LIM\":{\"lat\":-12.0219,\"lon\":-77.114304},\"LHR\":{\"lat\":51.4706,\"lon\":-0.461941},\"LHE\":{\"lat\":31.521601,\"lon\":74.403603},\"LED\":{\"lat\":59.800301,\"lon\":30.262501},\"LCA\":{\"lat\":34.875099,\"lon\":33.624901},\"LAX\":{\"lat\":33.942501,\"lon\":-118.407997},\"LAS\":{\"lat\":36.080101,\"lon\":-115.152},\"LAD\":{\"lat\":-8.85837,\"lon\":13.2312},\"KWI\":{\"lat\":29.226601,\"lon\":47.968899},\"KUL\":{\"lat\":2.74558,\"lon\":101.709999},\"KTM\":{\"lat\":27.6966,\"lon\":85.3591},\"KNU\":{\"lat\":26.399462,\"lon\":80.42695},\"KJA\":{\"lat\":56.172901,\"lon\":92.493301},\"KIX\":{\"lat\":34.427299,\"lon\":135.244003},\"KIN\":{\"lat\":17.935699,\"lon\":-76.787498},\"KHI\":{\"lat\":24.9065,\"lon\":67.160797},\"KHH\":{\"lat\":22.577101,\"lon\":120.349998},\"KGL\":{\"lat\":-1.96863,\"lon\":30.1395},\"KEF\":{\"lat\":63.985001,\"lon\":-22.6056},\"KCH\":{\"lat\":1.4847,\"lon\":110.347},\"KBP\":{\"lat\":50.345001,\"lon\":30.894699},\"JOI\":{\"lat\":-26.224501,\"lon\":-48.797401},\"JOG\":{\"lat\":-7.78818,\"lon\":110.431999},\"JNB\":{\"lat\":-26.133333,\"lon\":28.25},\"JIB\":{\"lat\":11.5473,\"lon\":43.1595},\"JHB\":{\"lat\":1.64131,\"lon\":103.669998},\"JED\":{\"lat\":21.6796,\"lon\":39.156502},\"JDO\":{\"lat\":-7.21896,\"lon\":-39.2701},\"JAX\":{\"lat\":30.494101,\"lon\":-81.687897},\"IXC\":{\"lat\":30.6735,\"lon\":76.788498},\"ISU\":{\"lat\":35.561749,\"lon\":45.316738},\"IST\":{\"lat\":41.262222,\"lon\":28.727778},\"ISB\":{\"lat\":33.616699,\"lon\":73.099197},\"IND\":{\"lat\":39.7173,\"lon\":-86.294403},\"ICN\":{\"lat\":37.469101,\"lon\":126.450996},\"IAH\":{\"lat\":29.9844,\"lon\":-95.3414},\"IAD\":{\"lat\":38.9445,\"lon\":-77.455803},\"HYD\":{\"lat\":17.231318,\"lon\":78.429855},\"HRE\":{\"lat\":-17.931801,\"lon\":31.0928},\"HNL\":{\"lat\":21.318701,\"lon\":-157.921997},\"HKG\":{\"lat\":22.308901,\"lon\":113.915001},\"HFA\":{\"lat\":32.809399,\"lon\":35.043098},\"HEL\":{\"lat\":60.3172,\"lon\":24.963301},\"HBA\":{\"lat\":-42.836102,\"lon\":147.509995},\"HAN\":{\"lat\":21.221201,\"lon\":105.806999},\"HAM\":{\"lat\":53.630402,\"lon\":9.98823},\"GYN\":{\"lat\":-16.632,\"lon\":-49.220699},\"GYE\":{\"lat\":-2.15742,\"lon\":-79.883598},\"GYD\":{\"lat\":40.467499,\"lon\":50.0467},\"GVA\":{\"lat\":46.238098,\"lon\":6.10895},\"GUM\":{\"lat\":13.4834,\"lon\":144.796005},\"GUA\":{\"lat\":14.5833,\"lon\":-90.527496},\"GRU\":{\"lat\":-23.435556,\"lon\":-46.473057},\"GOT\":{\"lat\":57.6628,\"lon\":12.2798},\"GND\":{\"lat\":12.0042,\"lon\":-61.786201},\"GIG\":{\"lat\":-22.809999,\"lon\":-43.250557},\"GEO\":{\"lat\":6.49855,\"lon\":-58.254101},\"GDL\":{\"lat\":20.521799,\"lon\":-103.310997},\"GBE\":{\"lat\":-24.555201,\"lon\":25.9182},\"FUK\":{\"lat\":33.585899,\"lon\":130.451004},\"FSD\":{\"lat\":43.582001,\"lon\":-96.741898},\"FRU\":{\"lat\":43.061272,\"lon\":74.477508},\"FRA\":{\"lat\":50.026402,\"lon\":8.54313},\"FOR\":{\"lat\":-3.77628,\"lon\":-38.5326},\"FLN\":{\"lat\":-27.670279,\"lon\":-48.552502},\"FIH\":{\"lat\":-4.38575,\"lon\":15.4446},\"FCO\":{\"lat\":41.804501,\"lon\":12.2508},\"EZE\":{\"lat\":-34.8222,\"lon\":-58.5358},\"EWR\":{\"lat\":40.692501,\"lon\":-74.168701},\"EVN\":{\"lat\":40.147301,\"lon\":44.395901},\"EBL\":{\"lat\":36.237598,\"lon\":43.9632},\"EBB\":{\"lat\":0.042386,\"lon\":32.443501},\"DXB\":{\"lat\":25.2528,\"lon\":55.364399},\"DUS\":{\"lat\":51.289501,\"lon\":6.76678},\"DUR\":{\"lat\":-29.614444,\"lon\":31.119722},\"DUB\":{\"lat\":53.421299,\"lon\":-6.27007},\"DTW\":{\"lat\":42.212399,\"lon\":-83.353401},\"DPS\":{\"lat\":-8.74817,\"lon\":115.167},\"DOH\":{\"lat\":25.260595,\"lon\":51.613767},\"DMM\":{\"lat\":26.471201,\"lon\":49.797901},\"DME\":{\"lat\":55.408798,\"lon\":37.9063},\"DKR\":{\"lat\":14.7397,\"lon\":-17.4902},\"DFW\":{\"lat\":32.896801,\"lon\":-97.038002},\"DEN\":{\"lat\":39.861698,\"lon\":-104.672997},\"DEL\":{\"lat\":28.5665,\"lon\":77.103104},\"DAR\":{\"lat\":-6.87811,\"lon\":39.202599},\"DAD\":{\"lat\":16.0439,\"lon\":108.198997},\"DAC\":{\"lat\":23.843347,\"lon\":90.397783},\"CZL\":{\"lat\":36.276001,\"lon\":6.62039},\"CWB\":{\"lat\":-25.5285,\"lon\":-49.1758},\"CRK\":{\"lat\":15.186,\"lon\":120.559998},\"CPT\":{\"lat\":-33.964802,\"lon\":18.6017},\"CPH\":{\"lat\":55.617901,\"lon\":12.656},\"COR\":{\"lat\":-31.323601,\"lon\":-64.208},\"COK\":{\"lat\":10.152,\"lon\":76.401901},\"CNX\":{\"lat\":18.7668,\"lon\":98.962601},\"CNN\":{\"lat\":11.92,\"lon\":75.55},\"CNF\":{\"lat\":-19.624443,\"lon\":-43.971943},\"CMH\":{\"lat\":39.998001,\"lon\":-82.891899},\"CMB\":{\"lat\":7.18076,\"lon\":79.884102},\"CLT\":{\"lat\":35.214001,\"lon\":-80.9431},\"CLO\":{\"lat\":3.54322,\"lon\":-76.3816},\"CLE\":{\"lat\":41.411701,\"lon\":-81.8498},\"CJB\":{\"lat\":11.03,\"lon\":77.043404},\"CHC\":{\"lat\":-43.489399,\"lon\":172.531998},\"CGY\":{\"lat\":8.41562,\"lon\":124.611},\"CGP\":{\"lat\":22.249599,\"lon\":91.813301},\"CGK\":{\"lat\":-6.12557,\"lon\":106.655998},\"CGB\":{\"lat\":-15.6529,\"lon\":-56.116699},\"CFC\":{\"lat\":-26.7762,\"lon\":-51.0125},\"CEB\":{\"lat\":10.3075,\"lon\":123.978996},\"CDG\":{\"lat\":49.012798,\"lon\":2.55},\"CCU\":{\"lat\":22.654699,\"lon\":88.446701},\"CCP\":{\"lat\":-36.772701,\"lon\":-73.063103},\"CBR\":{\"lat\":-35.3069,\"lon\":149.195007},\"CAW\":{\"lat\":-21.698299,\"lon\":-41.301701},\"CAI\":{\"lat\":30.121901,\"lon\":31.4056},\"BWN\":{\"lat\":4.9442,\"lon\":114.928001},\"BUF\":{\"lat\":42.940498,\"lon\":-78.732201},\"BUD\":{\"lat\":47.436901,\"lon\":19.2556},\"BTS\":{\"lat\":48.1702,\"lon\":17.2127},\"BSR\":{\"lat\":30.549101,\"lon\":47.662102},\"BSB\":{\"lat\":-15.869167,\"lon\":-47.920834},\"BRU\":{\"lat\":50.901402,\"lon\":4.48444},\"BOS\":{\"lat\":42.3643,\"lon\":-71.005203},\"BOM\":{\"lat\":19.088699,\"lon\":72.867897},\"BOG\":{\"lat\":4.70159,\"lon\":-74.1469},\"BOD\":{\"lat\":44.8283,\"lon\":-0.715556},\"BNU\":{\"lat\":-26.830601,\"lon\":-49.090302},\"BNE\":{\"lat\":-27.384199,\"lon\":153.117004},\"BNA\":{\"lat\":36.1245,\"lon\":-86.6782},\"BLR\":{\"lat\":13.1979,\"lon\":77.706299},\"BKK\":{\"lat\":13.6811,\"lon\":100.747002},\"BGW\":{\"lat\":33.262501,\"lon\":44.2346},\"BGR\":{\"lat\":44.8074,\"lon\":-68.828102},\"BGI\":{\"lat\":13.0746,\"lon\":-59.4925},\"BEY\":{\"lat\":33.8209,\"lon\":35.4884},\"BEL\":{\"lat\":-1.37925,\"lon\":-48.476299},\"BEG\":{\"lat\":44.818401,\"lon\":20.309099},\"BCN\":{\"lat\":41.2971,\"lon\":2.07846},\"BAQ\":{\"lat\":10.8896,\"lon\":-74.7808},\"BAH\":{\"lat\":26.2708,\"lon\":50.633598},\"AUS\":{\"lat\":30.1945,\"lon\":-97.669899},\"ATL\":{\"lat\":33.6367,\"lon\":-84.428101},\"ATH\":{\"lat\":37.936401,\"lon\":23.9445},\"ASU\":{\"lat\":-25.24,\"lon\":-57.52},\"ASK\":{\"lat\":6.90317,\"lon\":-5.36558},\"ARU\":{\"lat\":-21.1413,\"lon\":-50.424702},\"ARN\":{\"lat\":59.651901,\"lon\":17.9186},\"ARI\":{\"lat\":-18.348499,\"lon\":-70.338699},\"ANC\":{\"lat\":61.1744,\"lon\":-149.996002},\"AMS\":{\"lat\":52.308601,\"lon\":4.76389},\"AMM\":{\"lat\":31.722601,\"lon\":35.993198},\"AMD\":{\"lat\":23.0772,\"lon\":72.634697},\"ALG\":{\"lat\":36.691002,\"lon\":3.21541},\"ALA\":{\"lat\":43.3521,\"lon\":77.040497},\"AKX\":{\"lat\":50.2458,\"lon\":57.206699},\"AKL\":{\"lat\":-37.008099,\"lon\":174.792007},\"AGR\":{\"lat\":27.1558,\"lon\":77.960899},\"ADL\":{\"lat\":-34.945,\"lon\":138.531006},\"ADD\":{\"lat\":8.97789,\"lon\":38.799301},\"ADB\":{\"lat\":38.2924,\"lon\":27.157},\"ACC\":{\"lat\":5.60519,\"lon\":-0.166786},\"ABQ\":{\"lat\":35.040199,\"lon\":-106.609001},\"ABJ\":{\"lat\":5.26139,\"lon\":-3.92629},\"AAE\":{\"lat\":36.822201,\"lon\":7.80917}}"
  },
  {
    "path": "public/countries.geojson",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Afghanistan\",\n        \"SOV_A3\": \"AFG\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Afghanistan\",\n        \"ADM0_A3\": \"AFG\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Afghanistan\",\n        \"GU_A3\": \"AFG\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Afghanistan\",\n        \"SU_A3\": \"AFG\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Afghanistan\",\n        \"NAME_LONG\": \"Afghanistan\",\n        \"BRK_A3\": \"AFG\",\n        \"BRK_NAME\": \"Afghanistan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Afg.\",\n        \"POSTAL\": \"AF\",\n        \"FORMAL_EN\": \"Islamic State of Afghanistan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Afghanistan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Afghanistan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 8,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 34124811,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 64080,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1979,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AF\",\n        \"ISO_A2\": \"AF\",\n        \"ISO_A3\": \"AFG\",\n        \"ISO_A3_EH\": \"AFG\",\n        \"ISO_N3\": \"004\",\n        \"UN_A3\": \"004\",\n        \"WB_A2\": \"AF\",\n        \"WB_A3\": \"AFG\",\n        \"WOE_ID\": 23424739,\n        \"WOE_ID_EH\": 23424739,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"AFG\",\n        \"ADM0_A3_US\": \"AFG\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [60.52843, 29.318572, 75.158028, 38.486282],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [61.210817, 35.650072],\n            [62.230651, 35.270664],\n            [62.984662, 35.404041],\n            [63.193538, 35.857166],\n            [63.982896, 36.007957],\n            [64.546479, 36.312073],\n            [64.746105, 37.111818],\n            [65.588948, 37.305217],\n            [65.745631, 37.661164],\n            [66.217385, 37.39379],\n            [66.518607, 37.362784],\n            [67.075782, 37.356144],\n            [67.83, 37.144994],\n            [68.135562, 37.023115],\n            [68.859446, 37.344336],\n            [69.196273, 37.151144],\n            [69.518785, 37.608997],\n            [70.116578, 37.588223],\n            [70.270574, 37.735165],\n            [70.376304, 38.138396],\n            [70.806821, 38.486282],\n            [71.348131, 38.258905],\n            [71.239404, 37.953265],\n            [71.541918, 37.905774],\n            [71.448693, 37.065645],\n            [71.844638, 36.738171],\n            [72.193041, 36.948288],\n            [72.63689, 37.047558],\n            [73.260056, 37.495257],\n            [73.948696, 37.421566],\n            [74.980002, 37.41999],\n            [75.158028, 37.133031],\n            [74.575893, 37.020841],\n            [74.067552, 36.836176],\n            [72.920025, 36.720007],\n            [71.846292, 36.509942],\n            [71.262348, 36.074388],\n            [71.498768, 35.650563],\n            [71.613076, 35.153203],\n            [71.115019, 34.733126],\n            [71.156773, 34.348911],\n            [70.881803, 33.988856],\n            [69.930543, 34.02012],\n            [70.323594, 33.358533],\n            [69.687147, 33.105499],\n            [69.262522, 32.501944],\n            [69.317764, 31.901412],\n            [68.926677, 31.620189],\n            [68.556932, 31.71331],\n            [67.792689, 31.58293],\n            [67.683394, 31.303154],\n            [66.938891, 31.304911],\n            [66.381458, 30.738899],\n            [66.346473, 29.887943],\n            [65.046862, 29.472181],\n            [64.350419, 29.560031],\n            [64.148002, 29.340819],\n            [63.550261, 29.468331],\n            [62.549857, 29.318572],\n            [60.874248, 29.829239],\n            [61.781222, 30.73585],\n            [61.699314, 31.379506],\n            [60.941945, 31.548075],\n            [60.863655, 32.18292],\n            [60.536078, 32.981269],\n            [60.9637, 33.528832],\n            [60.52843, 33.676446],\n            [60.803193, 34.404102],\n            [61.210817, 35.650072]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Angola\",\n        \"SOV_A3\": \"AGO\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Angola\",\n        \"ADM0_A3\": \"AGO\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Angola\",\n        \"GU_A3\": \"AGO\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Angola\",\n        \"SU_A3\": \"AGO\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Angola\",\n        \"NAME_LONG\": \"Angola\",\n        \"BRK_A3\": \"AGO\",\n        \"BRK_NAME\": \"Angola\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ang.\",\n        \"POSTAL\": \"AO\",\n        \"FORMAL_EN\": \"People's Republic of Angola\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Angola\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Angola\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 29310273,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 189000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1970,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AO\",\n        \"ISO_A2\": \"AO\",\n        \"ISO_A3\": \"AGO\",\n        \"ISO_A3_EH\": \"AGO\",\n        \"ISO_N3\": \"024\",\n        \"UN_A3\": \"024\",\n        \"WB_A2\": \"AO\",\n        \"WB_A3\": \"AGO\",\n        \"WOE_ID\": 23424745,\n        \"WOE_ID_EH\": 23424745,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"AGO\",\n        \"ADM0_A3_US\": \"AGO\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [11.640096, -17.930636, 24.079905, -4.438023],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [23.904154, -11.722282],\n              [24.079905, -12.191297],\n              [23.930922, -12.565848],\n              [24.016137, -12.911046],\n              [21.933886, -12.898437],\n              [21.887843, -16.08031],\n              [22.562478, -16.898451],\n              [23.215048, -17.523116],\n              [21.377176, -17.930636],\n              [18.956187, -17.789095],\n              [18.263309, -17.309951],\n              [14.209707, -17.353101],\n              [14.058501, -17.423381],\n              [13.462362, -16.971212],\n              [12.814081, -16.941343],\n              [12.215461, -17.111668],\n              [11.734199, -17.301889],\n              [11.640096, -16.673142],\n              [11.778537, -15.793816],\n              [12.123581, -14.878316],\n              [12.175619, -14.449144],\n              [12.500095, -13.5477],\n              [12.738479, -13.137906],\n              [13.312914, -12.48363],\n              [13.633721, -12.038645],\n              [13.738728, -11.297863],\n              [13.686379, -10.731076],\n              [13.387328, -10.373578],\n              [13.120988, -9.766897],\n              [12.87537, -9.166934],\n              [12.929061, -8.959091],\n              [13.236433, -8.562629],\n              [12.93304, -7.596539],\n              [12.728298, -6.927122],\n              [12.227347, -6.294448],\n              [12.322432, -6.100092],\n              [12.735171, -5.965682],\n              [13.024869, -5.984389],\n              [13.375597, -5.864241],\n              [16.326528, -5.87747],\n              [16.57318, -6.622645],\n              [16.860191, -7.222298],\n              [17.089996, -7.545689],\n              [17.47297, -8.068551],\n              [18.134222, -7.987678],\n              [18.464176, -7.847014],\n              [19.016752, -7.988246],\n              [19.166613, -7.738184],\n              [19.417502, -7.155429],\n              [20.037723, -7.116361],\n              [20.091622, -6.94309],\n              [20.601823, -6.939318],\n              [20.514748, -7.299606],\n              [21.728111, -7.290872],\n              [21.746456, -7.920085],\n              [21.949131, -8.305901],\n              [21.801801, -8.908707],\n              [21.875182, -9.523708],\n              [22.208753, -9.894796],\n              [22.155268, -11.084801],\n              [22.402798, -10.993075],\n              [22.837345, -11.017622],\n              [23.456791, -10.867863],\n              [23.912215, -10.926826],\n              [24.017894, -11.237298],\n              [23.904154, -11.722282]\n            ]\n          ],\n          [\n            [\n              [12.182337, -5.789931],\n              [11.914963, -5.037987],\n              [12.318608, -4.60623],\n              [12.62076, -4.438023],\n              [12.995517, -4.781103],\n              [12.631612, -4.991271],\n              [12.468004, -5.248362],\n              [12.436688, -5.684304],\n              [12.182337, -5.789931]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Albania\",\n        \"SOV_A3\": \"ALB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Albania\",\n        \"ADM0_A3\": \"ALB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Albania\",\n        \"GU_A3\": \"ALB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Albania\",\n        \"SU_A3\": \"ALB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Albania\",\n        \"NAME_LONG\": \"Albania\",\n        \"BRK_A3\": \"ALB\",\n        \"BRK_NAME\": \"Albania\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Alb.\",\n        \"POSTAL\": \"AL\",\n        \"FORMAL_EN\": \"Republic of Albania\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Albania\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Albania\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 3047987,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 33900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AL\",\n        \"ISO_A2\": \"AL\",\n        \"ISO_A3\": \"ALB\",\n        \"ISO_A3_EH\": \"ALB\",\n        \"ISO_N3\": \"008\",\n        \"UN_A3\": \"008\",\n        \"WB_A2\": \"AL\",\n        \"WB_A3\": \"ALB\",\n        \"WOE_ID\": 23424742,\n        \"WOE_ID_EH\": 23424742,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ALB\",\n        \"ADM0_A3_US\": \"ALB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [19.304486, 39.624998, 21.02004, 42.688247],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [21.02004, 40.842727],\n            [20.99999, 40.580004],\n            [20.674997, 40.435],\n            [20.615, 40.110007],\n            [20.150016, 39.624998],\n            [19.98, 39.694993],\n            [19.960002, 39.915006],\n            [19.406082, 40.250773],\n            [19.319059, 40.72723],\n            [19.40355, 41.409566],\n            [19.540027, 41.719986],\n            [19.371769, 41.877548],\n            [19.371768, 41.877551],\n            [19.304486, 42.195745],\n            [19.738051, 42.688247],\n            [19.801613, 42.500093],\n            [20.0707, 42.58863],\n            [20.283755, 42.32026],\n            [20.52295, 42.21787],\n            [20.590247, 41.855409],\n            [20.590247, 41.855404],\n            [20.463175, 41.515089],\n            [20.605182, 41.086226],\n            [21.02004, 40.842727]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"United Arab Emirates\",\n        \"SOV_A3\": \"ARE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"United Arab Emirates\",\n        \"ADM0_A3\": \"ARE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"United Arab Emirates\",\n        \"GU_A3\": \"ARE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"United Arab Emirates\",\n        \"SU_A3\": \"ARE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"United Arab Emirates\",\n        \"NAME_LONG\": \"United Arab Emirates\",\n        \"BRK_A3\": \"ARE\",\n        \"BRK_NAME\": \"United Arab Emirates\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"U.A.E.\",\n        \"POSTAL\": \"AE\",\n        \"FORMAL_EN\": \"United Arab Emirates\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"United Arab Emirates\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"United Arab Emirates\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 6072475,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 667200,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AE\",\n        \"ISO_A2\": \"AE\",\n        \"ISO_A3\": \"ARE\",\n        \"ISO_A3_EH\": \"ARE\",\n        \"ISO_N3\": \"784\",\n        \"UN_A3\": \"784\",\n        \"WB_A2\": \"AE\",\n        \"WB_A3\": \"ARE\",\n        \"WOE_ID\": 23424738,\n        \"WOE_ID_EH\": 23424738,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ARE\",\n        \"ADM0_A3_US\": \"ARE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 20,\n        \"LONG_LEN\": 20,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [51.579519, 22.496948, 56.396847, 26.055464],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [51.579519, 24.245497],\n            [51.757441, 24.294073],\n            [51.794389, 24.019826],\n            [52.577081, 24.177439],\n            [53.404007, 24.151317],\n            [54.008001, 24.121758],\n            [54.693024, 24.797892],\n            [55.439025, 25.439145],\n            [56.070821, 26.055464],\n            [56.261042, 25.714606],\n            [56.396847, 24.924732],\n            [55.886233, 24.920831],\n            [55.804119, 24.269604],\n            [55.981214, 24.130543],\n            [55.528632, 23.933604],\n            [55.525841, 23.524869],\n            [55.234489, 23.110993],\n            [55.208341, 22.70833],\n            [55.006803, 22.496948],\n            [52.000733, 23.001154],\n            [51.617708, 24.014219],\n            [51.579519, 24.245497]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Argentina\",\n        \"SOV_A3\": \"ARG\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Argentina\",\n        \"ADM0_A3\": \"ARG\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Argentina\",\n        \"GU_A3\": \"ARG\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Argentina\",\n        \"SU_A3\": \"ARG\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Argentina\",\n        \"NAME_LONG\": \"Argentina\",\n        \"BRK_A3\": \"ARG\",\n        \"BRK_NAME\": \"Argentina\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Arg.\",\n        \"POSTAL\": \"AR\",\n        \"FORMAL_EN\": \"Argentine Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Argentina\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Argentina\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 44293293,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 879400,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AR\",\n        \"ISO_A2\": \"AR\",\n        \"ISO_A3\": \"ARG\",\n        \"ISO_A3_EH\": \"ARG\",\n        \"ISO_N3\": \"032\",\n        \"UN_A3\": \"032\",\n        \"WB_A2\": \"AR\",\n        \"WB_A3\": \"ARG\",\n        \"WOE_ID\": 23424747,\n        \"WOE_ID_EH\": 23424747,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ARG\",\n        \"ADM0_A3_US\": \"ARG\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-73.415436, -55.25, -53.628349, -21.83231],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-66.95992, -54.89681],\n              [-67.56244, -54.87001],\n              [-68.63335, -54.8695],\n              [-68.63401, -52.63637],\n              [-68.25, -53.1],\n              [-67.75, -53.85],\n              [-66.45, -54.45],\n              [-65.05, -54.7],\n              [-65.5, -55.2],\n              [-66.45, -55.25],\n              [-66.95992, -54.89681]\n            ]\n          ],\n          [\n            [\n              [-68.571545, -52.299444],\n              [-69.498362, -52.142761],\n              [-71.914804, -52.009022],\n              [-72.329404, -51.425956],\n              [-72.309974, -50.67701],\n              [-72.975747, -50.74145],\n              [-73.328051, -50.378785],\n              [-73.415436, -49.318436],\n              [-72.648247, -48.878618],\n              [-72.331161, -48.244238],\n              [-72.447355, -47.738533],\n              [-71.917258, -46.884838],\n              [-71.552009, -45.560733],\n              [-71.659316, -44.973689],\n              [-71.222779, -44.784243],\n              [-71.329801, -44.407522],\n              [-71.793623, -44.207172],\n              [-71.464056, -43.787611],\n              [-71.915424, -43.408565],\n              [-72.148898, -42.254888],\n              [-71.746804, -42.051386],\n              [-71.915734, -40.832339],\n              [-71.680761, -39.808164],\n              [-71.413517, -38.916022],\n              [-70.814664, -38.552995],\n              [-71.118625, -37.576827],\n              [-71.121881, -36.658124],\n              [-70.364769, -36.005089],\n              [-70.388049, -35.169688],\n              [-69.817309, -34.193571],\n              [-69.814777, -33.273886],\n              [-70.074399, -33.09121],\n              [-70.535069, -31.36501],\n              [-69.919008, -30.336339],\n              [-70.01355, -29.367923],\n              [-69.65613, -28.459141],\n              [-69.001235, -27.521214],\n              [-68.295542, -26.89934],\n              [-68.5948, -26.506909],\n              [-68.386001, -26.185016],\n              [-68.417653, -24.518555],\n              [-67.328443, -24.025303],\n              [-66.985234, -22.986349],\n              [-67.106674, -22.735925],\n              [-66.273339, -21.83231],\n              [-64.964892, -22.075862],\n              [-64.377021, -22.798091],\n              [-63.986838, -21.993644],\n              [-62.846468, -22.034985],\n              [-62.685057, -22.249029],\n              [-60.846565, -23.880713],\n              [-60.028966, -24.032796],\n              [-58.807128, -24.771459],\n              [-57.777217, -25.16234],\n              [-57.63366, -25.603657],\n              [-58.618174, -27.123719],\n              [-57.60976, -27.395899],\n              [-56.486702, -27.548499],\n              [-55.695846, -27.387837],\n              [-54.788795, -26.621786],\n              [-54.625291, -25.739255],\n              [-54.13005, -25.547639],\n              [-53.628349, -26.124865],\n              [-53.648735, -26.923473],\n              [-54.490725, -27.474757],\n              [-55.162286, -27.881915],\n              [-56.2909, -28.852761],\n              [-57.625133, -30.216295],\n              [-57.874937, -31.016556],\n              [-58.14244, -32.044504],\n              [-58.132648, -33.040567],\n              [-58.349611, -33.263189],\n              [-58.427074, -33.909454],\n              [-58.495442, -34.43149],\n              [-57.22583, -35.288027],\n              [-57.362359, -35.97739],\n              [-56.737487, -36.413126],\n              [-56.788285, -36.901572],\n              [-57.749157, -38.183871],\n              [-59.231857, -38.72022],\n              [-61.237445, -38.928425],\n              [-62.335957, -38.827707],\n              [-62.125763, -39.424105],\n              [-62.330531, -40.172586],\n              [-62.145994, -40.676897],\n              [-62.745803, -41.028761],\n              [-63.770495, -41.166789],\n              [-64.73209, -40.802677],\n              [-65.118035, -41.064315],\n              [-64.978561, -42.058001],\n              [-64.303408, -42.359016],\n              [-63.755948, -42.043687],\n              [-63.458059, -42.563138],\n              [-64.378804, -42.873558],\n              [-65.181804, -43.495381],\n              [-65.328823, -44.501366],\n              [-65.565269, -45.036786],\n              [-66.509966, -45.039628],\n              [-67.293794, -45.551896],\n              [-67.580546, -46.301773],\n              [-66.597066, -47.033925],\n              [-65.641027, -47.236135],\n              [-65.985088, -48.133289],\n              [-67.166179, -48.697337],\n              [-67.816088, -49.869669],\n              [-68.728745, -50.264218],\n              [-69.138539, -50.73251],\n              [-68.815561, -51.771104],\n              [-68.149995, -52.349983],\n              [-68.571545, -52.299444]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Armenia\",\n        \"SOV_A3\": \"ARM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Armenia\",\n        \"ADM0_A3\": \"ARM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Armenia\",\n        \"GU_A3\": \"ARM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Armenia\",\n        \"SU_A3\": \"ARM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Armenia\",\n        \"NAME_LONG\": \"Armenia\",\n        \"BRK_A3\": \"ARM\",\n        \"BRK_NAME\": \"Armenia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Arm.\",\n        \"POSTAL\": \"ARM\",\n        \"FORMAL_EN\": \"Republic of Armenia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Armenia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Armenia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 3045191,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 26300,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AM\",\n        \"ISO_A2\": \"AM\",\n        \"ISO_A3\": \"ARM\",\n        \"ISO_A3_EH\": \"ARM\",\n        \"ISO_N3\": \"051\",\n        \"UN_A3\": \"051\",\n        \"WB_A2\": \"AM\",\n        \"WB_A3\": \"ARM\",\n        \"WOE_ID\": 23424743,\n        \"WOE_ID_EH\": 23424743,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ARM\",\n        \"ADM0_A3_US\": \"ARM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [43.582746, 38.741201, 46.50572, 41.248129],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [43.582746, 41.092143],\n            [44.97248, 41.248129],\n            [45.179496, 40.985354],\n            [45.560351, 40.81229],\n            [45.359175, 40.561504],\n            [45.891907, 40.218476],\n            [45.610012, 39.899994],\n            [46.034534, 39.628021],\n            [46.483499, 39.464155],\n            [46.50572, 38.770605],\n            [46.143623, 38.741201],\n            [45.735379, 39.319719],\n            [45.739978, 39.473999],\n            [45.298145, 39.471751],\n            [45.001987, 39.740004],\n            [44.79399, 39.713003],\n            [44.400009, 40.005],\n            [43.656436, 40.253564],\n            [43.752658, 40.740201],\n            [43.582746, 41.092143]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Antarctica\",\n        \"SOV_A3\": \"ATA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Indeterminate\",\n        \"ADMIN\": \"Antarctica\",\n        \"ADM0_A3\": \"ATA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Antarctica\",\n        \"GU_A3\": \"ATA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Antarctica\",\n        \"SU_A3\": \"ATA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Antarctica\",\n        \"NAME_LONG\": \"Antarctica\",\n        \"BRK_A3\": \"ATA\",\n        \"BRK_NAME\": \"Antarctica\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ant.\",\n        \"POSTAL\": \"AQ\",\n        \"FORMAL_EN\": null,\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": null,\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": \"Multiple claims held in abeyance\",\n        \"NAME_SORT\": \"Antarctica\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": -99,\n        \"POP_EST\": 4050,\n        \"POP_RANK\": 4,\n        \"GDP_MD_EST\": 810,\n        \"POP_YEAR\": 2013,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2013,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AY\",\n        \"ISO_A2\": \"AQ\",\n        \"ISO_A3\": \"ATA\",\n        \"ISO_A3_EH\": \"ATA\",\n        \"ISO_N3\": \"010\",\n        \"UN_A3\": \"-099\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": 28289409,\n        \"WOE_ID_EH\": 28289409,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ATA\",\n        \"ADM0_A3_US\": \"ATA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Antarctica\",\n        \"REGION_UN\": \"Antarctica\",\n        \"SUBREGION\": \"Antarctica\",\n        \"REGION_WB\": \"Antarctica\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-180, -90, 180, -63.27066],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-59.572095, -80.040179],\n              [-59.865849, -80.549657],\n              [-60.159656, -81.000327],\n              [-62.255393, -80.863178],\n              [-64.488125, -80.921934],\n              [-65.741666, -80.588827],\n              [-65.741666, -80.549657],\n              [-66.290031, -80.255773],\n              [-64.037688, -80.294944],\n              [-61.883246, -80.39287],\n              [-61.138976, -79.981371],\n              [-60.610119, -79.628679],\n              [-59.572095, -80.040179]\n            ]\n          ],\n          [\n            [\n              [-159.208184, -79.497059],\n              [-161.127601, -79.634209],\n              [-162.439847, -79.281465],\n              [-163.027408, -78.928774],\n              [-163.066604, -78.869966],\n              [-163.712896, -78.595667],\n              [-163.712896, -78.595667],\n              [-163.105801, -78.223338],\n              [-161.245113, -78.380176],\n              [-160.246208, -78.693645],\n              [-159.482405, -79.046338],\n              [-159.208184, -79.497059]\n            ]\n          ],\n          [\n            [\n              [-45.154758, -78.04707],\n              [-43.920828, -78.478103],\n              [-43.48995, -79.08556],\n              [-43.372438, -79.516645],\n              [-43.333267, -80.026123],\n              [-44.880537, -80.339644],\n              [-46.506174, -80.594357],\n              [-48.386421, -80.829485],\n              [-50.482107, -81.025442],\n              [-52.851988, -80.966685],\n              [-54.164259, -80.633528],\n              [-53.987991, -80.222028],\n              [-51.853134, -79.94773],\n              [-50.991326, -79.614623],\n              [-50.364595, -79.183487],\n              [-49.914131, -78.811209],\n              [-49.306959, -78.458569],\n              [-48.660616, -78.047018],\n              [-48.660616, -78.047019],\n              [-48.151396, -78.04707],\n              [-46.662857, -77.831476],\n              [-45.154758, -78.04707]\n            ]\n          ],\n          [\n            [\n              [-121.211511, -73.50099],\n              [-119.918851, -73.657725],\n              [-118.724143, -73.481353],\n              [-119.292119, -73.834097],\n              [-120.232217, -74.08881],\n              [-121.62283, -74.010468],\n              [-122.621735, -73.657778],\n              [-122.621735, -73.657777],\n              [-122.406245, -73.324619],\n              [-121.211511, -73.50099]\n            ]\n          ],\n          [\n            [\n              [-125.559566, -73.481353],\n              [-124.031882, -73.873268],\n              [-124.619469, -73.834097],\n              [-125.912181, -73.736118],\n              [-127.28313, -73.461769],\n              [-127.28313, -73.461768],\n              [-126.558472, -73.246226],\n              [-125.559566, -73.481353]\n            ]\n          ],\n          [\n            [\n              [-98.98155, -71.933334],\n              [-97.884743, -72.070535],\n              [-96.787937, -71.952971],\n              [-96.20035, -72.521205],\n              [-96.983765, -72.442864],\n              [-98.198083, -72.482035],\n              [-99.432013, -72.442864],\n              [-100.783455, -72.50162],\n              [-101.801868, -72.305663],\n              [-102.330725, -71.894164],\n              [-102.330725, -71.894164],\n              [-101.703967, -71.717792],\n              [-100.430919, -71.854993],\n              [-98.98155, -71.933334]\n            ]\n          ],\n          [\n            [\n              [-68.451346, -70.955823],\n              [-68.333834, -71.406493],\n              [-68.510128, -71.798407],\n              [-68.784297, -72.170736],\n              [-69.959471, -72.307885],\n              [-71.075889, -72.503842],\n              [-72.388134, -72.484257],\n              [-71.8985, -72.092343],\n              [-73.073622, -72.229492],\n              [-74.19004, -72.366693],\n              [-74.953895, -72.072757],\n              [-75.012625, -71.661258],\n              [-73.915819, -71.269345],\n              [-73.915819, -71.269344],\n              [-73.230331, -71.15178],\n              [-72.074717, -71.190951],\n              [-71.780962, -70.681473],\n              [-71.72218, -70.309196],\n              [-71.741791, -69.505782],\n              [-71.173815, -69.035475],\n              [-70.253252, -68.87874],\n              [-69.724447, -69.251017],\n              [-69.489422, -69.623346],\n              [-69.058518, -70.074016],\n              [-68.725541, -70.505153],\n              [-68.451346, -70.955823]\n            ]\n          ],\n          [\n            [\n              [-58.614143, -64.152467],\n              [-59.045073, -64.36801],\n              [-59.789342, -64.211223],\n              [-60.611928, -64.309202],\n              [-61.297416, -64.54433],\n              [-62.0221, -64.799094],\n              [-62.51176, -65.09303],\n              [-62.648858, -65.484942],\n              [-62.590128, -65.857219],\n              [-62.120079, -66.190326],\n              [-62.805567, -66.425505],\n              [-63.74569, -66.503847],\n              [-64.294106, -66.837004],\n              [-64.881693, -67.150474],\n              [-65.508425, -67.58161],\n              [-65.665082, -67.953887],\n              [-65.312545, -68.365335],\n              [-64.783715, -68.678908],\n              [-63.961103, -68.913984],\n              [-63.1973, -69.227556],\n              [-62.785955, -69.619419],\n              [-62.570516, -69.991747],\n              [-62.276736, -70.383661],\n              [-61.806661, -70.716768],\n              [-61.512906, -71.089045],\n              [-61.375809, -72.010074],\n              [-61.081977, -72.382351],\n              [-61.003661, -72.774265],\n              [-60.690269, -73.166179],\n              [-60.827367, -73.695242],\n              [-61.375809, -74.106742],\n              [-61.96337, -74.439848],\n              [-63.295201, -74.576997],\n              [-63.74569, -74.92974],\n              [-64.352836, -75.262847],\n              [-65.860987, -75.635124],\n              [-67.192818, -75.79191],\n              [-68.446282, -76.007452],\n              [-69.797724, -76.222995],\n              [-70.600724, -76.634494],\n              [-72.206776, -76.673665],\n              [-73.969536, -76.634494],\n              [-75.555977, -76.712887],\n              [-77.24037, -76.712887],\n              [-76.926979, -77.104802],\n              [-75.399294, -77.28107],\n              [-74.282876, -77.55542],\n              [-73.656119, -77.908112],\n              [-74.772536, -78.221633],\n              [-76.4961, -78.123654],\n              [-77.925858, -78.378419],\n              [-77.984666, -78.789918],\n              [-78.023785, -79.181833],\n              [-76.848637, -79.514939],\n              [-76.633224, -79.887216],\n              [-75.360097, -80.259545],\n              [-73.244852, -80.416331],\n              [-71.442946, -80.69063],\n              [-70.013163, -81.004151],\n              [-68.191646, -81.317672],\n              [-65.704279, -81.474458],\n              [-63.25603, -81.748757],\n              [-61.552026, -82.042692],\n              [-59.691416, -82.37585],\n              [-58.712121, -82.846106],\n              [-58.222487, -83.218434],\n              [-57.008117, -82.865691],\n              [-55.362894, -82.571755],\n              [-53.619771, -82.258235],\n              [-51.543644, -82.003521],\n              [-49.76135, -81.729171],\n              [-47.273931, -81.709586],\n              [-44.825708, -81.846735],\n              [-42.808363, -82.081915],\n              [-42.16202, -81.65083],\n              [-40.771433, -81.356894],\n              [-38.244818, -81.337309],\n              [-36.26667, -81.121715],\n              [-34.386397, -80.906172],\n              [-32.310296, -80.769023],\n              [-30.097098, -80.592651],\n              [-28.549802, -80.337938],\n              [-29.254901, -79.985195],\n              [-29.685805, -79.632503],\n              [-29.685805, -79.260226],\n              [-31.624808, -79.299397],\n              [-33.681324, -79.456132],\n              [-35.639912, -79.456132],\n              [-35.914107, -79.083855],\n              [-35.77701, -78.339248],\n              [-35.326546, -78.123654],\n              [-33.896763, -77.888526],\n              [-32.212369, -77.65345],\n              [-30.998051, -77.359515],\n              [-29.783732, -77.065579],\n              [-28.882779, -76.673665],\n              [-27.511752, -76.497345],\n              [-26.160336, -76.360144],\n              [-25.474822, -76.281803],\n              [-23.927552, -76.24258],\n              [-22.458598, -76.105431],\n              [-21.224694, -75.909474],\n              [-20.010375, -75.674346],\n              [-18.913543, -75.439218],\n              [-17.522982, -75.125698],\n              [-16.641589, -74.79254],\n              [-15.701491, -74.498604],\n              [-15.40771, -74.106742],\n              [-16.46532, -73.871614],\n              [-16.112784, -73.460114],\n              [-15.446855, -73.146542],\n              [-14.408805, -72.950585],\n              [-13.311973, -72.715457],\n              [-12.293508, -72.401936],\n              [-11.510067, -72.010074],\n              [-11.020433, -71.539767],\n              [-10.295774, -71.265416],\n              [-9.101015, -71.324224],\n              [-8.611381, -71.65733],\n              [-7.416622, -71.696501],\n              [-7.377451, -71.324224],\n              [-6.868232, -70.93231],\n              [-5.790985, -71.030289],\n              [-5.536375, -71.402617],\n              [-4.341667, -71.461373],\n              [-3.048981, -71.285053],\n              [-1.795492, -71.167438],\n              [-0.659489, -71.226246],\n              [-0.228637, -71.637745],\n              [0.868195, -71.304639],\n              [1.886686, -71.128267],\n              [3.022638, -70.991118],\n              [4.139055, -70.853917],\n              [5.157546, -70.618789],\n              [6.273912, -70.462055],\n              [7.13572, -70.246512],\n              [7.742866, -69.893769],\n              [8.48711, -70.148534],\n              [9.525135, -70.011333],\n              [10.249845, -70.48164],\n              [10.817821, -70.834332],\n              [11.953824, -70.638375],\n              [12.404287, -70.246512],\n              [13.422778, -69.972162],\n              [14.734998, -70.030918],\n              [15.126757, -70.403247],\n              [15.949342, -70.030918],\n              [17.026589, -69.913354],\n              [18.201711, -69.874183],\n              [19.259373, -69.893769],\n              [20.375739, -70.011333],\n              [21.452985, -70.07014],\n              [21.923034, -70.403247],\n              [22.569403, -70.697182],\n              [23.666184, -70.520811],\n              [24.841357, -70.48164],\n              [25.977309, -70.48164],\n              [27.093726, -70.462055],\n              [28.09258, -70.324854],\n              [29.150242, -70.20729],\n              [30.031583, -69.93294],\n              [30.971733, -69.75662],\n              [31.990172, -69.658641],\n              [32.754053, -69.384291],\n              [33.302443, -68.835642],\n              [33.870419, -68.502588],\n              [34.908495, -68.659271],\n              [35.300202, -69.012014],\n              [36.16201, -69.247142],\n              [37.200035, -69.168748],\n              [37.905108, -69.52144],\n              [38.649404, -69.776205],\n              [39.667894, -69.541077],\n              [40.020431, -69.109941],\n              [40.921358, -68.933621],\n              [41.959434, -68.600514],\n              [42.938702, -68.463313],\n              [44.113876, -68.267408],\n              [44.897291, -68.051866],\n              [45.719928, -67.816738],\n              [46.503343, -67.601196],\n              [47.44344, -67.718759],\n              [48.344419, -67.366068],\n              [48.990736, -67.091718],\n              [49.930885, -67.111303],\n              [50.753471, -66.876175],\n              [50.949325, -66.523484],\n              [51.791547, -66.249133],\n              [52.614133, -66.053176],\n              [53.613038, -65.89639],\n              [54.53355, -65.818049],\n              [55.414943, -65.876805],\n              [56.355041, -65.974783],\n              [57.158093, -66.249133],\n              [57.255968, -66.680218],\n              [58.137361, -67.013324],\n              [58.744508, -67.287675],\n              [59.939318, -67.405239],\n              [60.605221, -67.679589],\n              [61.427806, -67.953887],\n              [62.387489, -68.012695],\n              [63.19049, -67.816738],\n              [64.052349, -67.405239],\n              [64.992447, -67.620729],\n              [65.971715, -67.738345],\n              [66.911864, -67.855909],\n              [67.891133, -67.934302],\n              [68.890038, -67.934302],\n              [69.712624, -68.972791],\n              [69.673453, -69.227556],\n              [69.555941, -69.678226],\n              [68.596258, -69.93294],\n              [67.81274, -70.305268],\n              [67.949889, -70.697182],\n              [69.066307, -70.677545],\n              [68.929157, -71.069459],\n              [68.419989, -71.441788],\n              [67.949889, -71.853287],\n              [68.71377, -72.166808],\n              [69.869307, -72.264787],\n              [71.024895, -72.088415],\n              [71.573285, -71.696501],\n              [71.906288, -71.324224],\n              [72.454627, -71.010703],\n              [73.08141, -70.716768],\n              [73.33602, -70.364024],\n              [73.864877, -69.874183],\n              [74.491557, -69.776205],\n              [75.62756, -69.737034],\n              [76.626465, -69.619419],\n              [77.644904, -69.462684],\n              [78.134539, -69.07077],\n              [78.428371, -68.698441],\n              [79.113859, -68.326216],\n              [80.093127, -68.071503],\n              [80.93535, -67.875546],\n              [81.483792, -67.542388],\n              [82.051767, -67.366068],\n              [82.776426, -67.209282],\n              [83.775331, -67.30726],\n              [84.676206, -67.209282],\n              [85.655527, -67.091718],\n              [86.752359, -67.150474],\n              [87.477017, -66.876175],\n              [87.986289, -66.209911],\n              [88.358411, -66.484261],\n              [88.828408, -66.954568],\n              [89.67063, -67.150474],\n              [90.630365, -67.228867],\n              [91.5901, -67.111303],\n              [92.608539, -67.189696],\n              [93.548637, -67.209282],\n              [94.17542, -67.111303],\n              [95.017591, -67.170111],\n              [95.781472, -67.385653],\n              [96.682399, -67.248504],\n              [97.759646, -67.248504],\n              [98.68021, -67.111303],\n              [99.718182, -67.248504],\n              [100.384188, -66.915346],\n              [100.893356, -66.58224],\n              [101.578896, -66.30789],\n              [102.832411, -65.563284],\n              [103.478676, -65.700485],\n              [104.242557, -65.974783],\n              [104.90846, -66.327527],\n              [106.181561, -66.934931],\n              [107.160881, -66.954568],\n              [108.081393, -66.954568],\n              [109.15864, -66.837004],\n              [110.235835, -66.699804],\n              [111.058472, -66.425505],\n              [111.74396, -66.13157],\n              [112.860378, -66.092347],\n              [113.604673, -65.876805],\n              [114.388088, -66.072762],\n              [114.897308, -66.386283],\n              [115.602381, -66.699804],\n              [116.699161, -66.660633],\n              [117.384701, -66.915346],\n              [118.57946, -67.170111],\n              [119.832924, -67.268089],\n              [120.871, -67.189696],\n              [121.654415, -66.876175],\n              [122.320369, -66.562654],\n              [123.221296, -66.484261],\n              [124.122274, -66.621462],\n              [125.160247, -66.719389],\n              [126.100396, -66.562654],\n              [127.001427, -66.562654],\n              [127.882768, -66.660633],\n              [128.80328, -66.758611],\n              [129.704259, -66.58224],\n              [130.781454, -66.425505],\n              [131.799945, -66.386283],\n              [132.935896, -66.386283],\n              [133.85646, -66.288304],\n              [134.757387, -66.209963],\n              [135.031582, -65.72007],\n              [135.070753, -65.308571],\n              [135.697485, -65.582869],\n              [135.873805, -66.033591],\n              [136.206705, -66.44509],\n              [136.618049, -66.778197],\n              [137.460271, -66.954568],\n              [138.596223, -66.895761],\n              [139.908442, -66.876175],\n              [140.809421, -66.817367],\n              [142.121692, -66.817367],\n              [143.061842, -66.797782],\n              [144.374061, -66.837004],\n              [145.490427, -66.915346],\n              [146.195552, -67.228867],\n              [145.999699, -67.601196],\n              [146.646067, -67.895131],\n              [147.723263, -68.130259],\n              [148.839629, -68.385024],\n              [150.132314, -68.561292],\n              [151.483705, -68.71813],\n              [152.502247, -68.874813],\n              [153.638199, -68.894502],\n              [154.284567, -68.561292],\n              [155.165857, -68.835642],\n              [155.92979, -69.149215],\n              [156.811132, -69.384291],\n              [158.025528, -69.482269],\n              [159.181013, -69.599833],\n              [159.670699, -69.991747],\n              [160.80665, -70.226875],\n              [161.570479, -70.579618],\n              [162.686897, -70.736353],\n              [163.842434, -70.716768],\n              [164.919681, -70.775524],\n              [166.11444, -70.755938],\n              [167.309095, -70.834332],\n              [168.425616, -70.971481],\n              [169.463589, -71.20666],\n              [170.501665, -71.402617],\n              [171.20679, -71.696501],\n              [171.089227, -72.088415],\n              [170.560422, -72.441159],\n              [170.109958, -72.891829],\n              [169.75737, -73.24452],\n              [169.287321, -73.65602],\n              [167.975101, -73.812806],\n              [167.387489, -74.165498],\n              [166.094803, -74.38104],\n              [165.644391, -74.772954],\n              [164.958851, -75.145283],\n              [164.234193, -75.458804],\n              [163.822797, -75.870303],\n              [163.568239, -76.24258],\n              [163.47026, -76.693302],\n              [163.489897, -77.065579],\n              [164.057873, -77.457442],\n              [164.273363, -77.82977],\n              [164.743464, -78.182514],\n              [166.604126, -78.319611],\n              [166.995781, -78.750748],\n              [165.193876, -78.907483],\n              [163.666217, -79.123025],\n              [161.766385, -79.162248],\n              [160.924162, -79.730482],\n              [160.747894, -80.200737],\n              [160.316964, -80.573066],\n              [159.788211, -80.945395],\n              [161.120016, -81.278501],\n              [161.629287, -81.690001],\n              [162.490992, -82.062278],\n              [163.705336, -82.395435],\n              [165.095949, -82.708956],\n              [166.604126, -83.022477],\n              [168.895665, -83.335998],\n              [169.404782, -83.825891],\n              [172.283934, -84.041433],\n              [172.477049, -84.117914],\n              [173.224083, -84.41371],\n              [175.985672, -84.158997],\n              [178.277212, -84.472518],\n              [180, -84.71338],\n              [180, -90],\n              [-180, -90],\n              [-180, -84.71338],\n              [-179.942499, -84.721443],\n              [-179.058677, -84.139412],\n              [-177.256772, -84.452933],\n              [-177.140807, -84.417941],\n              [-176.084673, -84.099259],\n              [-175.947235, -84.110449],\n              [-175.829882, -84.117914],\n              [-174.382503, -84.534323],\n              [-173.116559, -84.117914],\n              [-172.889106, -84.061019],\n              [-169.951223, -83.884647],\n              [-168.999989, -84.117914],\n              [-168.530199, -84.23739],\n              [-167.022099, -84.570497],\n              [-164.182144, -84.82521],\n              [-161.929775, -85.138731],\n              [-158.07138, -85.37391],\n              [-155.192253, -85.09956],\n              [-150.942099, -85.295517],\n              [-148.533073, -85.609038],\n              [-145.888918, -85.315102],\n              [-143.107718, -85.040752],\n              [-142.892279, -84.570497],\n              [-146.829068, -84.531274],\n              [-150.060732, -84.296146],\n              [-150.902928, -83.904232],\n              [-153.586201, -83.68869],\n              [-153.409907, -83.23802],\n              [-153.037759, -82.82652],\n              [-152.665637, -82.454192],\n              [-152.861517, -82.042692],\n              [-154.526299, -81.768394],\n              [-155.29018, -81.41565],\n              [-156.83745, -81.102129],\n              [-154.408787, -81.160937],\n              [-152.097662, -81.004151],\n              [-150.648293, -81.337309],\n              [-148.865998, -81.043373],\n              [-147.22075, -80.671045],\n              [-146.417749, -80.337938],\n              [-146.770286, -79.926439],\n              [-148.062947, -79.652089],\n              [-149.531901, -79.358205],\n              [-151.588416, -79.299397],\n              [-153.390322, -79.162248],\n              [-155.329376, -79.064269],\n              [-155.975668, -78.69194],\n              [-157.268302, -78.378419],\n              [-158.051768, -78.025676],\n              [-158.365134, -76.889207],\n              [-157.875474, -76.987238],\n              [-156.974573, -77.300759],\n              [-155.329376, -77.202728],\n              [-153.742832, -77.065579],\n              [-152.920247, -77.496664],\n              [-151.33378, -77.398737],\n              [-150.00195, -77.183143],\n              [-148.748486, -76.908845],\n              [-147.612483, -76.575738],\n              [-146.104409, -76.47776],\n              [-146.143528, -76.105431],\n              [-146.496091, -75.733154],\n              [-146.20231, -75.380411],\n              [-144.909624, -75.204039],\n              [-144.322037, -75.537197],\n              [-142.794353, -75.34124],\n              [-141.638764, -75.086475],\n              [-140.209007, -75.06689],\n              [-138.85759, -74.968911],\n              [-137.5062, -74.733783],\n              [-136.428901, -74.518241],\n              [-135.214583, -74.302699],\n              [-134.431194, -74.361455],\n              [-133.745654, -74.439848],\n              [-132.257168, -74.302699],\n              [-130.925311, -74.479019],\n              [-129.554284, -74.459433],\n              [-128.242038, -74.322284],\n              [-126.890622, -74.420263],\n              [-125.402082, -74.518241],\n              [-124.011496, -74.479019],\n              [-122.562152, -74.498604],\n              [-121.073613, -74.518241],\n              [-119.70256, -74.479019],\n              [-118.684145, -74.185083],\n              [-117.469801, -74.028348],\n              [-116.216312, -74.243891],\n              [-115.021552, -74.067519],\n              [-113.944331, -73.714828],\n              [-113.297988, -74.028348],\n              [-112.945452, -74.38104],\n              [-112.299083, -74.714198],\n              [-111.261059, -74.420263],\n              [-110.066325, -74.79254],\n              [-108.714909, -74.910103],\n              [-107.559346, -75.184454],\n              [-106.149148, -75.125698],\n              [-104.876074, -74.949326],\n              [-103.367949, -74.988497],\n              [-102.016507, -75.125698],\n              [-100.645531, -75.302018],\n              [-100.1167, -74.870933],\n              [-100.763043, -74.537826],\n              [-101.252703, -74.185083],\n              [-102.545337, -74.106742],\n              [-103.113313, -73.734413],\n              [-103.328752, -73.362084],\n              [-103.681289, -72.61753],\n              [-102.917485, -72.754679],\n              [-101.60524, -72.813436],\n              [-100.312528, -72.754679],\n              [-99.13738, -72.911414],\n              [-98.118889, -73.20535],\n              [-97.688037, -73.558041],\n              [-96.336595, -73.616849],\n              [-95.043961, -73.4797],\n              [-93.672907, -73.283743],\n              [-92.439003, -73.166179],\n              [-91.420564, -73.401307],\n              [-90.088733, -73.322914],\n              [-89.226951, -72.558722],\n              [-88.423951, -73.009393],\n              [-87.268337, -73.185764],\n              [-86.014822, -73.087786],\n              [-85.192236, -73.4797],\n              [-83.879991, -73.518871],\n              [-82.665646, -73.636434],\n              [-81.470913, -73.851977],\n              [-80.687447, -73.4797],\n              [-80.295791, -73.126956],\n              [-79.296886, -73.518871],\n              [-77.925858, -73.420892],\n              [-76.907367, -73.636434],\n              [-76.221879, -73.969541],\n              [-74.890049, -73.871614],\n              [-73.852024, -73.65602],\n              [-72.833533, -73.401307],\n              [-71.619215, -73.264157],\n              [-70.209042, -73.146542],\n              [-68.935916, -73.009393],\n              [-67.956622, -72.79385],\n              [-67.369061, -72.480329],\n              [-67.134036, -72.049244],\n              [-67.251548, -71.637745],\n              [-67.56494, -71.245831],\n              [-67.917477, -70.853917],\n              [-68.230843, -70.462055],\n              [-68.485452, -70.109311],\n              [-68.544209, -69.717397],\n              [-68.446282, -69.325535],\n              [-67.976233, -68.953206],\n              [-67.5845, -68.541707],\n              [-67.427843, -68.149844],\n              [-67.62367, -67.718759],\n              [-67.741183, -67.326845],\n              [-67.251548, -66.876175],\n              [-66.703184, -66.58224],\n              [-66.056815, -66.209963],\n              [-65.371327, -65.89639],\n              [-64.568276, -65.602506],\n              [-64.176542, -65.171423],\n              [-63.628152, -64.897073],\n              [-63.001394, -64.642308],\n              [-62.041686, -64.583552],\n              [-61.414928, -64.270031],\n              [-60.709855, -64.074074],\n              [-59.887269, -63.95651],\n              [-59.162585, -63.701745],\n              [-58.594557, -63.388224],\n              [-57.811143, -63.27066],\n              [-57.223582, -63.525425],\n              [-57.59573, -63.858532],\n              [-58.614143, -64.152467]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 3,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"France\",\n        \"SOV_A3\": \"FR1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Dependency\",\n        \"ADMIN\": \"French Southern and Antarctic Lands\",\n        \"ADM0_A3\": \"ATF\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"French Southern and Antarctic Lands\",\n        \"GU_A3\": \"ATF\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"French Southern and Antarctic Lands\",\n        \"SU_A3\": \"ATF\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Fr. S. Antarctic Lands\",\n        \"NAME_LONG\": \"French Southern and Antarctic Lands\",\n        \"BRK_A3\": \"ATF\",\n        \"BRK_NAME\": \"Fr. S. and Antarctic Lands\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Fr. S.A.L.\",\n        \"POSTAL\": \"TF\",\n        \"FORMAL_EN\": \"Territory of the French Southern and Antarctic Lands\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": null,\n        \"NOTE_ADM0\": \"Fr.\",\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"French Southern and Antarctic Lands\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 7,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 9,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 140,\n        \"POP_RANK\": 1,\n        \"GDP_MD_EST\": 16,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"FS\",\n        \"ISO_A2\": \"TF\",\n        \"ISO_A3\": \"ATF\",\n        \"ISO_A3_EH\": \"ATF\",\n        \"ISO_N3\": \"260\",\n        \"UN_A3\": \"-099\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": 28289406,\n        \"WOE_ID_EH\": 28289406,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ATF\",\n        \"ADM0_A3_US\": \"ATF\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Seven seas (open ocean)\",\n        \"REGION_UN\": \"Seven seas (open ocean)\",\n        \"SUBREGION\": \"Seven seas (open ocean)\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 22,\n        \"LONG_LEN\": 35,\n        \"ABBREV_LEN\": 10,\n        \"TINY\": 2,\n        \"HOMEPART\": -99,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [68.72, -49.775, 70.56, -48.625],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [68.935, -48.625],\n            [69.58, -48.94],\n            [70.525, -49.065],\n            [70.56, -49.255],\n            [70.28, -49.71],\n            [68.745, -49.775],\n            [68.72, -49.2425],\n            [68.8675, -48.83],\n            [68.935, -48.625]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Australia\",\n        \"SOV_A3\": \"AU1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"Australia\",\n        \"ADM0_A3\": \"AUS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Australia\",\n        \"GU_A3\": \"AUS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Australia\",\n        \"SU_A3\": \"AUS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Australia\",\n        \"NAME_LONG\": \"Australia\",\n        \"BRK_A3\": \"AUS\",\n        \"BRK_NAME\": \"Australia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Auz.\",\n        \"POSTAL\": \"AU\",\n        \"FORMAL_EN\": \"Commonwealth of Australia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Australia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Australia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 23232413,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 1189000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AS\",\n        \"ISO_A2\": \"AU\",\n        \"ISO_A3\": \"AUS\",\n        \"ISO_A3_EH\": \"AUS\",\n        \"ISO_N3\": \"036\",\n        \"UN_A3\": \"036\",\n        \"WB_A2\": \"AU\",\n        \"WB_A3\": \"AUS\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424748,\n        \"WOE_NOTE\": \"Includes Ashmore and Cartier Islands (23424749) and Coral Sea Islands (23424790).\",\n        \"ADM0_A3_IS\": \"AUS\",\n        \"ADM0_A3_US\": \"AUS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Australia and New Zealand\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 5.7\n      },\n      \"bbox\": [113.338953, -43.634597, 153.569469, -10.668186],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [145.397978, -40.792549],\n              [146.364121, -41.137695],\n              [146.908584, -41.000546],\n              [147.689259, -40.808258],\n              [148.289068, -40.875438],\n              [148.359865, -42.062445],\n              [148.017301, -42.407024],\n              [147.914052, -43.211522],\n              [147.564564, -42.937689],\n              [146.870343, -43.634597],\n              [146.663327, -43.580854],\n              [146.048378, -43.549745],\n              [145.43193, -42.693776],\n              [145.29509, -42.03361],\n              [144.718071, -41.162552],\n              [144.743755, -40.703975],\n              [145.397978, -40.792549]\n            ]\n          ],\n          [\n            [\n              [143.561811, -13.763656],\n              [143.922099, -14.548311],\n              [144.563714, -14.171176],\n              [144.894908, -14.594458],\n              [145.374724, -14.984976],\n              [145.271991, -15.428205],\n              [145.48526, -16.285672],\n              [145.637033, -16.784918],\n              [145.888904, -16.906926],\n              [146.160309, -17.761655],\n              [146.063674, -18.280073],\n              [146.387478, -18.958274],\n              [147.471082, -19.480723],\n              [148.177602, -19.955939],\n              [148.848414, -20.39121],\n              [148.717465, -20.633469],\n              [149.28942, -21.260511],\n              [149.678337, -22.342512],\n              [150.077382, -22.122784],\n              [150.482939, -22.556142],\n              [150.727265, -22.402405],\n              [150.899554, -23.462237],\n              [151.609175, -24.076256],\n              [152.07354, -24.457887],\n              [152.855197, -25.267501],\n              [153.136162, -26.071173],\n              [153.161949, -26.641319],\n              [153.092909, -27.2603],\n              [153.569469, -28.110067],\n              [153.512108, -28.995077],\n              [153.339095, -29.458202],\n              [153.069241, -30.35024],\n              [153.089602, -30.923642],\n              [152.891578, -31.640446],\n              [152.450002, -32.550003],\n              [151.709117, -33.041342],\n              [151.343972, -33.816023],\n              [151.010555, -34.31036],\n              [150.714139, -35.17346],\n              [150.32822, -35.671879],\n              [150.075212, -36.420206],\n              [149.946124, -37.109052],\n              [149.997284, -37.425261],\n              [149.423882, -37.772681],\n              [148.304622, -37.809061],\n              [147.381733, -38.219217],\n              [146.922123, -38.606532],\n              [146.317922, -39.035757],\n              [145.489652, -38.593768],\n              [144.876976, -38.417448],\n              [145.032212, -37.896188],\n              [144.485682, -38.085324],\n              [143.609974, -38.809465],\n              [142.745427, -38.538268],\n              [142.17833, -38.380034],\n              [141.606582, -38.308514],\n              [140.638579, -38.019333],\n              [139.992158, -37.402936],\n              [139.806588, -36.643603],\n              [139.574148, -36.138362],\n              [139.082808, -35.732754],\n              [138.120748, -35.612296],\n              [138.449462, -35.127261],\n              [138.207564, -34.384723],\n              [137.71917, -35.076825],\n              [136.829406, -35.260535],\n              [137.352371, -34.707339],\n              [137.503886, -34.130268],\n              [137.890116, -33.640479],\n              [137.810328, -32.900007],\n              [136.996837, -33.752771],\n              [136.372069, -34.094766],\n              [135.989043, -34.890118],\n              [135.208213, -34.47867],\n              [135.239218, -33.947953],\n              [134.613417, -33.222778],\n              [134.085904, -32.848072],\n              [134.273903, -32.617234],\n              [132.990777, -32.011224],\n              [132.288081, -31.982647],\n              [131.326331, -31.495803],\n              [129.535794, -31.590423],\n              [128.240938, -31.948489],\n              [127.102867, -32.282267],\n              [126.148714, -32.215966],\n              [125.088623, -32.728751],\n              [124.221648, -32.959487],\n              [124.028947, -33.483847],\n              [123.659667, -33.890179],\n              [122.811036, -33.914467],\n              [122.183064, -34.003402],\n              [121.299191, -33.821036],\n              [120.580268, -33.930177],\n              [119.893695, -33.976065],\n              [119.298899, -34.509366],\n              [119.007341, -34.464149],\n              [118.505718, -34.746819],\n              [118.024972, -35.064733],\n              [117.295507, -35.025459],\n              [116.625109, -35.025097],\n              [115.564347, -34.386428],\n              [115.026809, -34.196517],\n              [115.048616, -33.623425],\n              [115.545123, -33.487258],\n              [115.714674, -33.259572],\n              [115.679379, -32.900369],\n              [115.801645, -32.205062],\n              [115.689611, -31.612437],\n              [115.160909, -30.601594],\n              [114.997043, -30.030725],\n              [115.040038, -29.461095],\n              [114.641974, -28.810231],\n              [114.616498, -28.516399],\n              [114.173579, -28.118077],\n              [114.048884, -27.334765],\n              [113.477498, -26.543134],\n              [113.338953, -26.116545],\n              [113.778358, -26.549025],\n              [113.440962, -25.621278],\n              [113.936901, -25.911235],\n              [114.232852, -26.298446],\n              [114.216161, -25.786281],\n              [113.721255, -24.998939],\n              [113.625344, -24.683971],\n              [113.393523, -24.384764],\n              [113.502044, -23.80635],\n              [113.706993, -23.560215],\n              [113.843418, -23.059987],\n              [113.736552, -22.475475],\n              [114.149756, -21.755881],\n              [114.225307, -22.517488],\n              [114.647762, -21.82952],\n              [115.460167, -21.495173],\n              [115.947373, -21.068688],\n              [116.711615, -20.701682],\n              [117.166316, -20.623599],\n              [117.441545, -20.746899],\n              [118.229559, -20.374208],\n              [118.836085, -20.263311],\n              [118.987807, -20.044203],\n              [119.252494, -19.952942],\n              [119.805225, -19.976506],\n              [120.85622, -19.683708],\n              [121.399856, -19.239756],\n              [121.655138, -18.705318],\n              [122.241665, -18.197649],\n              [122.286624, -17.798603],\n              [122.312772, -17.254967],\n              [123.012574, -16.4052],\n              [123.433789, -17.268558],\n              [123.859345, -17.069035],\n              [123.503242, -16.596506],\n              [123.817073, -16.111316],\n              [124.258287, -16.327944],\n              [124.379726, -15.56706],\n              [124.926153, -15.0751],\n              [125.167275, -14.680396],\n              [125.670087, -14.51007],\n              [125.685796, -14.230656],\n              [126.125149, -14.347341],\n              [126.142823, -14.095987],\n              [126.582589, -13.952791],\n              [127.065867, -13.817968],\n              [127.804633, -14.276906],\n              [128.35969, -14.86917],\n              [128.985543, -14.875991],\n              [129.621473, -14.969784],\n              [129.4096, -14.42067],\n              [129.888641, -13.618703],\n              [130.339466, -13.357376],\n              [130.183506, -13.10752],\n              [130.617795, -12.536392],\n              [131.223495, -12.183649],\n              [131.735091, -12.302453],\n              [132.575298, -12.114041],\n              [132.557212, -11.603012],\n              [131.824698, -11.273782],\n              [132.357224, -11.128519],\n              [133.019561, -11.376411],\n              [133.550846, -11.786515],\n              [134.393068, -12.042365],\n              [134.678632, -11.941183],\n              [135.298491, -12.248606],\n              [135.882693, -11.962267],\n              [136.258381, -12.049342],\n              [136.492475, -11.857209],\n              [136.95162, -12.351959],\n              [136.685125, -12.887223],\n              [136.305407, -13.29123],\n              [135.961758, -13.324509],\n              [136.077617, -13.724278],\n              [135.783836, -14.223989],\n              [135.428664, -14.715432],\n              [135.500184, -14.997741],\n              [136.295175, -15.550265],\n              [137.06536, -15.870762],\n              [137.580471, -16.215082],\n              [138.303217, -16.807604],\n              [138.585164, -16.806622],\n              [139.108543, -17.062679],\n              [139.260575, -17.371601],\n              [140.215245, -17.710805],\n              [140.875463, -17.369069],\n              [141.07111, -16.832047],\n              [141.274095, -16.38887],\n              [141.398222, -15.840532],\n              [141.702183, -15.044921],\n              [141.56338, -14.561333],\n              [141.63552, -14.270395],\n              [141.519869, -13.698078],\n              [141.65092, -12.944688],\n              [141.842691, -12.741548],\n              [141.68699, -12.407614],\n              [141.928629, -11.877466],\n              [142.118488, -11.328042],\n              [142.143706, -11.042737],\n              [142.51526, -10.668186],\n              [142.79731, -11.157355],\n              [142.866763, -11.784707],\n              [143.115947, -11.90563],\n              [143.158632, -12.325656],\n              [143.522124, -12.834358],\n              [143.597158, -13.400422],\n              [143.561811, -13.763656]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Austria\",\n        \"SOV_A3\": \"AUT\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Austria\",\n        \"ADM0_A3\": \"AUT\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Austria\",\n        \"GU_A3\": \"AUT\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Austria\",\n        \"SU_A3\": \"AUT\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Austria\",\n        \"NAME_LONG\": \"Austria\",\n        \"BRK_A3\": \"AUT\",\n        \"BRK_NAME\": \"Austria\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Aust.\",\n        \"POSTAL\": \"A\",\n        \"FORMAL_EN\": \"Republic of Austria\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Austria\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Austria\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 8754413,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 416600,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AU\",\n        \"ISO_A2\": \"AT\",\n        \"ISO_A3\": \"AUT\",\n        \"ISO_A3_EH\": \"AUT\",\n        \"ISO_N3\": \"040\",\n        \"UN_A3\": \"040\",\n        \"WB_A2\": \"AT\",\n        \"WB_A3\": \"AUT\",\n        \"WOE_ID\": 23424750,\n        \"WOE_ID_EH\": 23424750,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"AUT\",\n        \"ADM0_A3_US\": \"AUT\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [9.47997, 46.431817, 16.979667, 49.039074],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [16.979667, 48.123497],\n            [16.903754, 47.714866],\n            [16.340584, 47.712902],\n            [16.534268, 47.496171],\n            [16.202298, 46.852386],\n            [16.011664, 46.683611],\n            [15.137092, 46.658703],\n            [14.632472, 46.431817],\n            [13.806475, 46.509306],\n            [12.376485, 46.767559],\n            [12.153088, 47.115393],\n            [11.164828, 46.941579],\n            [11.048556, 46.751359],\n            [10.442701, 46.893546],\n            [9.932448, 46.920728],\n            [9.47997, 47.10281],\n            [9.632932, 47.347601],\n            [9.594226, 47.525058],\n            [9.896068, 47.580197],\n            [10.402084, 47.302488],\n            [10.544504, 47.566399],\n            [11.426414, 47.523766],\n            [12.141357, 47.703083],\n            [12.62076, 47.672388],\n            [12.932627, 47.467646],\n            [13.025851, 47.637584],\n            [12.884103, 48.289146],\n            [13.243357, 48.416115],\n            [13.595946, 48.877172],\n            [14.338898, 48.555305],\n            [14.901447, 48.964402],\n            [15.253416, 49.039074],\n            [16.029647, 48.733899],\n            [16.499283, 48.785808],\n            [16.960288, 48.596982],\n            [16.879983, 48.470013],\n            [16.979667, 48.123497]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Azerbaijan\",\n        \"SOV_A3\": \"AZE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Azerbaijan\",\n        \"ADM0_A3\": \"AZE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Azerbaijan\",\n        \"GU_A3\": \"AZE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Azerbaijan\",\n        \"SU_A3\": \"AZE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Azerbaijan\",\n        \"NAME_LONG\": \"Azerbaijan\",\n        \"BRK_A3\": \"AZE\",\n        \"BRK_NAME\": \"Azerbaijan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Aze.\",\n        \"POSTAL\": \"AZ\",\n        \"FORMAL_EN\": \"Republic of Azerbaijan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Azerbaijan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Azerbaijan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 9961396,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 167900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AJ\",\n        \"ISO_A2\": \"AZ\",\n        \"ISO_A3\": \"AZE\",\n        \"ISO_A3_EH\": \"AZE\",\n        \"ISO_N3\": \"031\",\n        \"UN_A3\": \"031\",\n        \"WB_A2\": \"AZ\",\n        \"WB_A3\": \"AZE\",\n        \"WOE_ID\": 23424741,\n        \"WOE_ID_EH\": 23424741,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"AZE\",\n        \"ADM0_A3_US\": \"AZE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [44.79399, 38.270378, 50.392821, 41.860675],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [46.50572, 38.770605],\n              [46.483499, 39.464155],\n              [46.034534, 39.628021],\n              [45.610012, 39.899994],\n              [45.891907, 40.218476],\n              [45.359175, 40.561504],\n              [45.560351, 40.81229],\n              [45.179496, 40.985354],\n              [44.97248, 41.248129],\n              [45.217426, 41.411452],\n              [45.962601, 41.123873],\n              [46.501637, 41.064445],\n              [46.637908, 41.181673],\n              [46.145432, 41.722802],\n              [46.404951, 41.860675],\n              [46.686071, 41.827137],\n              [47.373315, 41.219732],\n              [47.815666, 41.151416],\n              [47.987283, 41.405819],\n              [48.584353, 41.808869],\n              [49.110264, 41.282287],\n              [49.618915, 40.572924],\n              [50.08483, 40.526157],\n              [50.392821, 40.256561],\n              [49.569202, 40.176101],\n              [49.395259, 39.399482],\n              [49.223228, 39.049219],\n              [48.856532, 38.815486],\n              [48.883249, 38.320245],\n              [48.634375, 38.270378],\n              [48.010744, 38.794015],\n              [48.355529, 39.288765],\n              [48.060095, 39.582235],\n              [47.685079, 39.508364],\n              [46.50572, 38.770605]\n            ]\n          ],\n          [\n            [\n              [44.79399, 39.713003],\n              [45.001987, 39.740004],\n              [45.298145, 39.471751],\n              [45.739978, 39.473999],\n              [45.735379, 39.319719],\n              [46.143623, 38.741201],\n              [45.457722, 38.874139],\n              [44.952688, 39.335765],\n              [44.79399, 39.713003]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Burundi\",\n        \"SOV_A3\": \"BDI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Burundi\",\n        \"ADM0_A3\": \"BDI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Burundi\",\n        \"GU_A3\": \"BDI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Burundi\",\n        \"SU_A3\": \"BDI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Burundi\",\n        \"NAME_LONG\": \"Burundi\",\n        \"BRK_A3\": \"BDI\",\n        \"BRK_NAME\": \"Burundi\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bur.\",\n        \"POSTAL\": \"BI\",\n        \"FORMAL_EN\": \"Republic of Burundi\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Burundi\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Burundi\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 11466756,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 7892,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BY\",\n        \"ISO_A2\": \"BI\",\n        \"ISO_A3\": \"BDI\",\n        \"ISO_A3_EH\": \"BDI\",\n        \"ISO_N3\": \"108\",\n        \"UN_A3\": \"108\",\n        \"WB_A2\": \"BI\",\n        \"WB_A3\": \"BDI\",\n        \"WOE_ID\": 23424774,\n        \"WOE_ID_EH\": 23424774,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BDI\",\n        \"ADM0_A3_US\": \"BDI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [29.024926, -4.499983, 30.75224, -2.348487],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [29.339998, -4.499983],\n            [29.276384, -3.293907],\n            [29.024926, -2.839258],\n            [29.632176, -2.917858],\n            [29.938359, -2.348487],\n            [30.469674, -2.413855],\n            [30.52766, -2.80762],\n            [30.74301, -3.03431],\n            [30.75224, -3.35931],\n            [30.50554, -3.56858],\n            [30.11632, -4.09012],\n            [29.753512, -4.452389],\n            [29.339998, -4.499983]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Belgium\",\n        \"SOV_A3\": \"BEL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Belgium\",\n        \"ADM0_A3\": \"BEL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Belgium\",\n        \"GU_A3\": \"BEL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Belgium\",\n        \"SU_A3\": \"BEL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Belgium\",\n        \"NAME_LONG\": \"Belgium\",\n        \"BRK_A3\": \"BEL\",\n        \"BRK_NAME\": \"Belgium\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Belg.\",\n        \"POSTAL\": \"B\",\n        \"FORMAL_EN\": \"Kingdom of Belgium\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Belgium\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Belgium\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 11491346,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 508600,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BE\",\n        \"ISO_A2\": \"BE\",\n        \"ISO_A3\": \"BEL\",\n        \"ISO_A3_EH\": \"BEL\",\n        \"ISO_N3\": \"056\",\n        \"UN_A3\": \"056\",\n        \"WB_A2\": \"BE\",\n        \"WB_A3\": \"BEL\",\n        \"WOE_ID\": 23424757,\n        \"WOE_ID_EH\": 23424757,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BEL\",\n        \"ADM0_A3_US\": \"BEL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [2.513573, 49.529484, 6.156658, 51.475024],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [4.047071, 51.267259],\n            [4.973991, 51.475024],\n            [5.606976, 51.037298],\n            [6.156658, 50.803721],\n            [6.043073, 50.128052],\n            [5.782417, 50.090328],\n            [5.674052, 49.529484],\n            [4.799222, 49.985373],\n            [4.286023, 49.907497],\n            [3.588184, 50.378992],\n            [3.123252, 50.780363],\n            [2.658422, 50.796848],\n            [2.513573, 51.148506],\n            [3.314971, 51.345781],\n            [3.315011, 51.345777],\n            [3.314971, 51.345755],\n            [4.047071, 51.267259]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Benin\",\n        \"SOV_A3\": \"BEN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Benin\",\n        \"ADM0_A3\": \"BEN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Benin\",\n        \"GU_A3\": \"BEN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Benin\",\n        \"SU_A3\": \"BEN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Benin\",\n        \"NAME_LONG\": \"Benin\",\n        \"BRK_A3\": \"BEN\",\n        \"BRK_NAME\": \"Benin\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Benin\",\n        \"POSTAL\": \"BJ\",\n        \"FORMAL_EN\": \"Republic of Benin\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Benin\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Benin\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 11038805,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 24310,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BN\",\n        \"ISO_A2\": \"BJ\",\n        \"ISO_A3\": \"BEN\",\n        \"ISO_A3_EH\": \"BEN\",\n        \"ISO_N3\": \"204\",\n        \"UN_A3\": \"204\",\n        \"WB_A2\": \"BJ\",\n        \"WB_A3\": \"BEN\",\n        \"WOE_ID\": 23424764,\n        \"WOE_ID_EH\": 23424764,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BEN\",\n        \"ADM0_A3_US\": \"BEN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [0.772336, 6.142158, 3.797112, 12.235636],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [2.691702, 6.258817],\n            [1.865241, 6.142158],\n            [1.618951, 6.832038],\n            [1.664478, 9.12859],\n            [1.463043, 9.334624],\n            [1.425061, 9.825395],\n            [1.077795, 10.175607],\n            [0.772336, 10.470808],\n            [0.899563, 10.997339],\n            [1.24347, 11.110511],\n            [1.447178, 11.547719],\n            [1.935986, 11.64115],\n            [2.154474, 11.94015],\n            [2.490164, 12.233052],\n            [2.848643, 12.235636],\n            [3.61118, 11.660167],\n            [3.572216, 11.327939],\n            [3.797112, 10.734746],\n            [3.60007, 10.332186],\n            [3.705438, 10.06321],\n            [3.220352, 9.444153],\n            [2.912308, 9.137608],\n            [2.723793, 8.506845],\n            [2.749063, 7.870734],\n            [2.691702, 6.258817]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Burkina Faso\",\n        \"SOV_A3\": \"BFA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Burkina Faso\",\n        \"ADM0_A3\": \"BFA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Burkina Faso\",\n        \"GU_A3\": \"BFA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Burkina Faso\",\n        \"SU_A3\": \"BFA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Burkina Faso\",\n        \"NAME_LONG\": \"Burkina Faso\",\n        \"BRK_A3\": \"BFA\",\n        \"BRK_NAME\": \"Burkina Faso\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"B.F.\",\n        \"POSTAL\": \"BF\",\n        \"FORMAL_EN\": \"Burkina Faso\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Burkina Faso\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Burkina Faso\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 20107509,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 32990,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"UV\",\n        \"ISO_A2\": \"BF\",\n        \"ISO_A3\": \"BFA\",\n        \"ISO_A3_EH\": \"BFA\",\n        \"ISO_N3\": \"854\",\n        \"UN_A3\": \"854\",\n        \"WB_A2\": \"BF\",\n        \"WB_A3\": \"BFA\",\n        \"WOE_ID\": 23424978,\n        \"WOE_ID_EH\": 23424978,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BFA\",\n        \"ADM0_A3_US\": \"BFA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 12,\n        \"LONG_LEN\": 12,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-5.470565, 9.610835, 2.177108, 15.116158],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [2.154474, 11.94015],\n            [1.935986, 11.64115],\n            [1.447178, 11.547719],\n            [1.24347, 11.110511],\n            [0.899563, 10.997339],\n            [0.023803, 11.018682],\n            [-0.438702, 11.098341],\n            [-0.761576, 10.93693],\n            [-1.203358, 11.009819],\n            [-2.940409, 10.96269],\n            [-2.963896, 10.395335],\n            [-2.827496, 9.642461],\n            [-3.511899, 9.900326],\n            [-3.980449, 9.862344],\n            [-4.330247, 9.610835],\n            [-4.779884, 9.821985],\n            [-4.954653, 10.152714],\n            [-5.404342, 10.370737],\n            [-5.470565, 10.95127],\n            [-5.197843, 11.375146],\n            [-5.220942, 11.713859],\n            [-4.427166, 12.542646],\n            [-4.280405, 13.228444],\n            [-4.006391, 13.472485],\n            [-3.522803, 13.337662],\n            [-3.103707, 13.541267],\n            [-2.967694, 13.79815],\n            [-2.191825, 14.246418],\n            [-2.001035, 14.559008],\n            [-1.066363, 14.973815],\n            [-0.515854, 15.116158],\n            [-0.266257, 14.924309],\n            [0.374892, 14.928908],\n            [0.295646, 14.444235],\n            [0.429928, 13.988733],\n            [0.993046, 13.33575],\n            [1.024103, 12.851826],\n            [2.177108, 12.625018],\n            [2.154474, 11.94015]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Bangladesh\",\n        \"SOV_A3\": \"BGD\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Bangladesh\",\n        \"ADM0_A3\": \"BGD\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Bangladesh\",\n        \"GU_A3\": \"BGD\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Bangladesh\",\n        \"SU_A3\": \"BGD\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Bangladesh\",\n        \"NAME_LONG\": \"Bangladesh\",\n        \"BRK_A3\": \"BGD\",\n        \"BRK_NAME\": \"Bangladesh\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bang.\",\n        \"POSTAL\": \"BD\",\n        \"FORMAL_EN\": \"People's Republic of Bangladesh\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Bangladesh\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Bangladesh\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 157826578,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 628400,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BG\",\n        \"ISO_A2\": \"BD\",\n        \"ISO_A3\": \"BGD\",\n        \"ISO_A3_EH\": \"BGD\",\n        \"ISO_N3\": \"050\",\n        \"UN_A3\": \"050\",\n        \"WB_A2\": \"BD\",\n        \"WB_A3\": \"BGD\",\n        \"WOE_ID\": 23424759,\n        \"WOE_ID_EH\": 23424759,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BGD\",\n        \"ADM0_A3_US\": \"BGD\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [88.084422, 20.670883, 92.672721, 26.446526],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [92.672721, 22.041239],\n            [92.652257, 21.324048],\n            [92.303234, 21.475485],\n            [92.368554, 20.670883],\n            [92.082886, 21.192195],\n            [92.025215, 21.70157],\n            [91.834891, 22.182936],\n            [91.417087, 22.765019],\n            [90.496006, 22.805017],\n            [90.586957, 22.392794],\n            [90.272971, 21.836368],\n            [89.847467, 22.039146],\n            [89.70205, 21.857116],\n            [89.418863, 21.966179],\n            [89.031961, 22.055708],\n            [88.876312, 22.879146],\n            [88.52977, 23.631142],\n            [88.69994, 24.233715],\n            [88.084422, 24.501657],\n            [88.306373, 24.866079],\n            [88.931554, 25.238692],\n            [88.209789, 25.768066],\n            [88.563049, 26.446526],\n            [89.355094, 26.014407],\n            [89.832481, 25.965082],\n            [89.920693, 25.26975],\n            [90.872211, 25.132601],\n            [91.799596, 25.147432],\n            [92.376202, 24.976693],\n            [91.915093, 24.130414],\n            [91.46773, 24.072639],\n            [91.158963, 23.503527],\n            [91.706475, 22.985264],\n            [91.869928, 23.624346],\n            [92.146035, 23.627499],\n            [92.672721, 22.041239]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Bulgaria\",\n        \"SOV_A3\": \"BGR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Bulgaria\",\n        \"ADM0_A3\": \"BGR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Bulgaria\",\n        \"GU_A3\": \"BGR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Bulgaria\",\n        \"SU_A3\": \"BGR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Bulgaria\",\n        \"NAME_LONG\": \"Bulgaria\",\n        \"BRK_A3\": \"BGR\",\n        \"BRK_NAME\": \"Bulgaria\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bulg.\",\n        \"POSTAL\": \"BG\",\n        \"FORMAL_EN\": \"Republic of Bulgaria\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Bulgaria\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Bulgaria\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 7101510,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 143100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BU\",\n        \"ISO_A2\": \"BG\",\n        \"ISO_A3\": \"BGR\",\n        \"ISO_A3_EH\": \"BGR\",\n        \"ISO_N3\": \"100\",\n        \"UN_A3\": \"100\",\n        \"WB_A2\": \"BG\",\n        \"WB_A3\": \"BGR\",\n        \"WOE_ID\": 23424771,\n        \"WOE_ID_EH\": 23424771,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BGR\",\n        \"ADM0_A3_US\": \"BGR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [22.380526, 41.234486, 28.558081, 44.234923],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [22.65715, 44.234923],\n            [22.944832, 43.823785],\n            [23.332302, 43.897011],\n            [24.100679, 43.741051],\n            [25.569272, 43.688445],\n            [26.065159, 43.943494],\n            [27.2424, 44.175986],\n            [27.970107, 43.812468],\n            [28.558081, 43.707462],\n            [28.039095, 43.293172],\n            [27.673898, 42.577892],\n            [27.99672, 42.007359],\n            [27.135739, 42.141485],\n            [26.117042, 41.826905],\n            [26.106138, 41.328899],\n            [25.197201, 41.234486],\n            [24.492645, 41.583896],\n            [23.692074, 41.309081],\n            [22.952377, 41.337994],\n            [22.881374, 41.999297],\n            [22.380526, 42.32026],\n            [22.545012, 42.461362],\n            [22.436595, 42.580321],\n            [22.604801, 42.898519],\n            [22.986019, 43.211161],\n            [22.500157, 43.642814],\n            [22.410446, 44.008063],\n            [22.65715, 44.234923]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"The Bahamas\",\n        \"SOV_A3\": \"BHS\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"The Bahamas\",\n        \"ADM0_A3\": \"BHS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"The Bahamas\",\n        \"GU_A3\": \"BHS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"The Bahamas\",\n        \"SU_A3\": \"BHS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Bahamas\",\n        \"NAME_LONG\": \"Bahamas\",\n        \"BRK_A3\": \"BHS\",\n        \"BRK_NAME\": \"Bahamas\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bhs.\",\n        \"POSTAL\": \"BS\",\n        \"FORMAL_EN\": \"Commonwealth of the Bahamas\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Bahamas, The\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Bahamas, The\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 329988,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 9066,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BF\",\n        \"ISO_A2\": \"BS\",\n        \"ISO_A3\": \"BHS\",\n        \"ISO_A3_EH\": \"BHS\",\n        \"ISO_N3\": \"044\",\n        \"UN_A3\": \"044\",\n        \"WB_A2\": \"BS\",\n        \"WB_A3\": \"BHS\",\n        \"WOE_ID\": 23424758,\n        \"WOE_ID_EH\": 23424758,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BHS\",\n        \"ADM0_A3_US\": \"BHS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-78.98, 23.71, -77, 27.04],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-77.53466, 23.75975],\n              [-77.78, 23.71],\n              [-78.03405, 24.28615],\n              [-78.40848, 24.57564],\n              [-78.19087, 25.2103],\n              [-77.89, 25.17],\n              [-77.54, 24.34],\n              [-77.53466, 23.75975]\n            ]\n          ],\n          [\n            [\n              [-77.82, 26.58],\n              [-78.91, 26.42],\n              [-78.98, 26.79],\n              [-78.51, 26.87],\n              [-77.85, 26.84],\n              [-77.82, 26.58]\n            ]\n          ],\n          [\n            [\n              [-77, 26.59],\n              [-77.17255, 25.87918],\n              [-77.35641, 26.00735],\n              [-77.34, 26.53],\n              [-77.78802, 26.92516],\n              [-77.79, 27.04],\n              [-77, 26.59]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Bosnia and Herzegovina\",\n        \"SOV_A3\": \"BIH\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Bosnia and Herzegovina\",\n        \"ADM0_A3\": \"BIH\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Bosnia and Herzegovina\",\n        \"GU_A3\": \"BIH\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Bosnia and Herzegovina\",\n        \"SU_A3\": \"BIH\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Bosnia and Herz.\",\n        \"NAME_LONG\": \"Bosnia and Herzegovina\",\n        \"BRK_A3\": \"BIH\",\n        \"BRK_NAME\": \"Bosnia and Herz.\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"B.H.\",\n        \"POSTAL\": \"BiH\",\n        \"FORMAL_EN\": \"Bosnia and Herzegovina\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Bosnia and Herzegovina\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Bosnia and Herzegovina\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 3856181,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 42530,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1991,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BK\",\n        \"ISO_A2\": \"BA\",\n        \"ISO_A3\": \"BIH\",\n        \"ISO_A3_EH\": \"BIH\",\n        \"ISO_N3\": \"070\",\n        \"UN_A3\": \"070\",\n        \"WB_A2\": \"BA\",\n        \"WB_A3\": \"BIH\",\n        \"WOE_ID\": 23424761,\n        \"WOE_ID_EH\": 23424761,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BIH\",\n        \"ADM0_A3_US\": \"BIH\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 16,\n        \"LONG_LEN\": 22,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9.5\n      },\n      \"bbox\": [15.750026, 42.65, 19.59976, 45.233777],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [19.36803, 44.863],\n            [19.11761, 44.42307],\n            [19.59976, 44.03847],\n            [19.454, 43.5681],\n            [19.21852, 43.52384],\n            [19.03165, 43.43253],\n            [18.70648, 43.20011],\n            [18.56, 42.65],\n            [17.674922, 43.028563],\n            [17.297373, 43.446341],\n            [16.916156, 43.667722],\n            [16.456443, 44.04124],\n            [16.23966, 44.351143],\n            [15.750026, 44.818712],\n            [15.959367, 45.233777],\n            [16.318157, 45.004127],\n            [16.534939, 45.211608],\n            [17.002146, 45.233777],\n            [17.861783, 45.06774],\n            [18.553214, 45.08159],\n            [19.005485, 44.860234],\n            [19.00548, 44.86023],\n            [19.36803, 44.863]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Belarus\",\n        \"SOV_A3\": \"BLR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Belarus\",\n        \"ADM0_A3\": \"BLR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Belarus\",\n        \"GU_A3\": \"BLR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Belarus\",\n        \"SU_A3\": \"BLR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Belarus\",\n        \"NAME_LONG\": \"Belarus\",\n        \"BRK_A3\": \"BLR\",\n        \"BRK_NAME\": \"Belarus\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bela.\",\n        \"POSTAL\": \"BY\",\n        \"FORMAL_EN\": \"Republic of Belarus\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Belarus\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Belarus\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 9549747,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 165400,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BO\",\n        \"ISO_A2\": \"BY\",\n        \"ISO_A3\": \"BLR\",\n        \"ISO_A3_EH\": \"BLR\",\n        \"ISO_N3\": \"112\",\n        \"UN_A3\": \"112\",\n        \"WB_A2\": \"BY\",\n        \"WB_A3\": \"BLR\",\n        \"WOE_ID\": 23424765,\n        \"WOE_ID_EH\": 23424765,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BLR\",\n        \"ADM0_A3_US\": \"BLR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [23.199494, 51.319503, 32.693643, 56.16913],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [23.484128, 53.912498],\n            [24.450684, 53.905702],\n            [25.536354, 54.282423],\n            [25.768433, 54.846963],\n            [26.588279, 55.167176],\n            [26.494331, 55.615107],\n            [27.10246, 55.783314],\n            [28.176709, 56.16913],\n            [29.229513, 55.918344],\n            [29.371572, 55.670091],\n            [29.896294, 55.789463],\n            [30.873909, 55.550976],\n            [30.971836, 55.081548],\n            [30.757534, 54.811771],\n            [31.384472, 54.157056],\n            [31.791424, 53.974639],\n            [31.731273, 53.794029],\n            [32.405599, 53.618045],\n            [32.693643, 53.351421],\n            [32.304519, 53.132726],\n            [31.49764, 53.16743],\n            [31.305201, 53.073996],\n            [31.540018, 52.742052],\n            [31.78597, 52.10168],\n            [31.785992, 52.101678],\n            [30.927549, 52.042353],\n            [30.619454, 51.822806],\n            [30.555117, 51.319503],\n            [30.157364, 51.416138],\n            [29.254938, 51.368234],\n            [28.992835, 51.602044],\n            [28.617613, 51.427714],\n            [28.241615, 51.572227],\n            [27.454066, 51.592303],\n            [26.337959, 51.832289],\n            [25.327788, 51.910656],\n            [24.553106, 51.888461],\n            [24.005078, 51.617444],\n            [23.527071, 51.578454],\n            [23.508002, 52.023647],\n            [23.199494, 52.486977],\n            [23.799199, 52.691099],\n            [23.804935, 53.089731],\n            [23.527536, 53.470122],\n            [23.484128, 53.912498]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Belize\",\n        \"SOV_A3\": \"BLZ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Belize\",\n        \"ADM0_A3\": \"BLZ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Belize\",\n        \"GU_A3\": \"BLZ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Belize\",\n        \"SU_A3\": \"BLZ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Belize\",\n        \"NAME_LONG\": \"Belize\",\n        \"BRK_A3\": \"BLZ\",\n        \"BRK_NAME\": \"Belize\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Belize\",\n        \"POSTAL\": \"BZ\",\n        \"FORMAL_EN\": \"Belize\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Belize\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Belize\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 360346,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 3088,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BH\",\n        \"ISO_A2\": \"BZ\",\n        \"ISO_A3\": \"BLZ\",\n        \"ISO_A3_EH\": \"BLZ\",\n        \"ISO_N3\": \"084\",\n        \"UN_A3\": \"084\",\n        \"WB_A2\": \"BZ\",\n        \"WB_A3\": \"BLZ\",\n        \"WOE_ID\": 23424760,\n        \"WOE_ID_EH\": 23424760,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BLZ\",\n        \"ADM0_A3_US\": \"BLZ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [-89.229122, 15.886938, -88.106813, 18.499982],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-89.14308, 17.808319],\n            [-89.150909, 17.955468],\n            [-89.029857, 18.001511],\n            [-88.848344, 17.883198],\n            [-88.490123, 18.486831],\n            [-88.300031, 18.499982],\n            [-88.296336, 18.353273],\n            [-88.106813, 18.348674],\n            [-88.123479, 18.076675],\n            [-88.285355, 17.644143],\n            [-88.197867, 17.489475],\n            [-88.302641, 17.131694],\n            [-88.239518, 17.036066],\n            [-88.355428, 16.530774],\n            [-88.551825, 16.265467],\n            [-88.732434, 16.233635],\n            [-88.930613, 15.887273],\n            [-89.229122, 15.886938],\n            [-89.150806, 17.015577],\n            [-89.14308, 17.808319]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Bolivia\",\n        \"SOV_A3\": \"BOL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Bolivia\",\n        \"ADM0_A3\": \"BOL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Bolivia\",\n        \"GU_A3\": \"BOL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Bolivia\",\n        \"SU_A3\": \"BOL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Bolivia\",\n        \"NAME_LONG\": \"Bolivia\",\n        \"BRK_A3\": \"BOL\",\n        \"BRK_NAME\": \"Bolivia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bolivia\",\n        \"POSTAL\": \"BO\",\n        \"FORMAL_EN\": \"Plurinational State of Bolivia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Bolivia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Bolivia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 11138234,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 78350,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BL\",\n        \"ISO_A2\": \"BO\",\n        \"ISO_A3\": \"BOL\",\n        \"ISO_A3_EH\": \"BOL\",\n        \"ISO_N3\": \"068\",\n        \"UN_A3\": \"068\",\n        \"WB_A2\": \"BO\",\n        \"WB_A3\": \"BOL\",\n        \"WOE_ID\": 23424762,\n        \"WOE_ID_EH\": 23424762,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BOL\",\n        \"ADM0_A3_US\": \"BOL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 7,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7.5\n      },\n      \"bbox\": [-69.590424, -22.872919, -57.498371, -9.761988],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-62.685057, -22.249029],\n            [-62.846468, -22.034985],\n            [-63.986838, -21.993644],\n            [-64.377021, -22.798091],\n            [-64.964892, -22.075862],\n            [-66.273339, -21.83231],\n            [-67.106674, -22.735925],\n            [-67.82818, -22.872919],\n            [-68.219913, -21.494347],\n            [-68.757167, -20.372658],\n            [-68.442225, -19.405068],\n            [-68.966818, -18.981683],\n            [-69.100247, -18.260125],\n            [-69.590424, -17.580012],\n            [-68.959635, -16.500698],\n            [-69.389764, -15.660129],\n            [-69.160347, -15.323974],\n            [-69.339535, -14.953195],\n            [-68.948887, -14.453639],\n            [-68.929224, -13.602684],\n            [-68.88008, -12.899729],\n            [-68.66508, -12.5613],\n            [-69.529678, -10.951734],\n            [-68.786158, -11.03638],\n            [-68.271254, -11.014521],\n            [-68.048192, -10.712059],\n            [-67.173801, -10.306812],\n            [-66.646908, -9.931331],\n            [-65.338435, -9.761988],\n            [-65.444837, -10.511451],\n            [-65.321899, -10.895872],\n            [-65.402281, -11.56627],\n            [-64.316353, -12.461978],\n            [-63.196499, -12.627033],\n            [-62.80306, -13.000653],\n            [-62.127081, -13.198781],\n            [-61.713204, -13.489202],\n            [-61.084121, -13.479384],\n            [-60.503304, -13.775955],\n            [-60.459198, -14.354007],\n            [-60.264326, -14.645979],\n            [-60.251149, -15.077219],\n            [-60.542966, -15.09391],\n            [-60.15839, -16.258284],\n            [-58.24122, -16.299573],\n            [-58.388058, -16.877109],\n            [-58.280804, -17.27171],\n            [-57.734558, -17.552468],\n            [-57.498371, -18.174188],\n            [-57.676009, -18.96184],\n            [-57.949997, -19.400004],\n            [-57.853802, -19.969995],\n            [-58.166392, -20.176701],\n            [-58.183471, -19.868399],\n            [-59.115042, -19.356906],\n            [-60.043565, -19.342747],\n            [-61.786326, -19.633737],\n            [-62.265961, -20.513735],\n            [-62.291179, -21.051635],\n            [-62.685057, -22.249029]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Brazil\",\n        \"SOV_A3\": \"BRA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Brazil\",\n        \"ADM0_A3\": \"BRA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Brazil\",\n        \"GU_A3\": \"BRA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Brazil\",\n        \"SU_A3\": \"BRA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Brazil\",\n        \"NAME_LONG\": \"Brazil\",\n        \"BRK_A3\": \"BRA\",\n        \"BRK_NAME\": \"Brazil\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Brazil\",\n        \"POSTAL\": \"BR\",\n        \"FORMAL_EN\": \"Federative Republic of Brazil\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Brazil\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Brazil\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 207353391,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 3081000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"3. Emerging region: BRIC\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BR\",\n        \"ISO_A2\": \"BR\",\n        \"ISO_A3\": \"BRA\",\n        \"ISO_A3_EH\": \"BRA\",\n        \"ISO_N3\": \"076\",\n        \"UN_A3\": \"076\",\n        \"WB_A2\": \"BR\",\n        \"WB_A3\": \"BRA\",\n        \"WOE_ID\": 23424768,\n        \"WOE_ID_EH\": 23424768,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BRA\",\n        \"ADM0_A3_US\": \"BRA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 5.7\n      },\n      \"bbox\": [-73.987235, -33.768378, -34.729993, 5.244486],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-57.625133, -30.216295],\n            [-56.2909, -28.852761],\n            [-55.162286, -27.881915],\n            [-54.490725, -27.474757],\n            [-53.648735, -26.923473],\n            [-53.628349, -26.124865],\n            [-54.13005, -25.547639],\n            [-54.625291, -25.739255],\n            [-54.428946, -25.162185],\n            [-54.293476, -24.5708],\n            [-54.29296, -24.021014],\n            [-54.652834, -23.839578],\n            [-55.027902, -24.001274],\n            [-55.400747, -23.956935],\n            [-55.517639, -23.571998],\n            [-55.610683, -22.655619],\n            [-55.797958, -22.35693],\n            [-56.473317, -22.0863],\n            [-56.88151, -22.282154],\n            [-57.937156, -22.090176],\n            [-57.870674, -20.732688],\n            [-58.166392, -20.176701],\n            [-57.853802, -19.969995],\n            [-57.949997, -19.400004],\n            [-57.676009, -18.96184],\n            [-57.498371, -18.174188],\n            [-57.734558, -17.552468],\n            [-58.280804, -17.27171],\n            [-58.388058, -16.877109],\n            [-58.24122, -16.299573],\n            [-60.15839, -16.258284],\n            [-60.542966, -15.09391],\n            [-60.251149, -15.077219],\n            [-60.264326, -14.645979],\n            [-60.459198, -14.354007],\n            [-60.503304, -13.775955],\n            [-61.084121, -13.479384],\n            [-61.713204, -13.489202],\n            [-62.127081, -13.198781],\n            [-62.80306, -13.000653],\n            [-63.196499, -12.627033],\n            [-64.316353, -12.461978],\n            [-65.402281, -11.56627],\n            [-65.321899, -10.895872],\n            [-65.444837, -10.511451],\n            [-65.338435, -9.761988],\n            [-66.646908, -9.931331],\n            [-67.173801, -10.306812],\n            [-68.048192, -10.712059],\n            [-68.271254, -11.014521],\n            [-68.786158, -11.03638],\n            [-69.529678, -10.951734],\n            [-70.093752, -11.123972],\n            [-70.548686, -11.009147],\n            [-70.481894, -9.490118],\n            [-71.302412, -10.079436],\n            [-72.184891, -10.053598],\n            [-72.563033, -9.520194],\n            [-73.226713, -9.462213],\n            [-73.015383, -9.032833],\n            [-73.571059, -8.424447],\n            [-73.987235, -7.52383],\n            [-73.723401, -7.340999],\n            [-73.724487, -6.918595],\n            [-73.120027, -6.629931],\n            [-73.219711, -6.089189],\n            [-72.964507, -5.741251],\n            [-72.891928, -5.274561],\n            [-71.748406, -4.593983],\n            [-70.928843, -4.401591],\n            [-70.794769, -4.251265],\n            [-69.893635, -4.298187],\n            [-69.444102, -1.556287],\n            [-69.420486, -1.122619],\n            [-69.577065, -0.549992],\n            [-70.020656, -0.185156],\n            [-70.015566, 0.541414],\n            [-69.452396, 0.706159],\n            [-69.252434, 0.602651],\n            [-69.218638, 0.985677],\n            [-69.804597, 1.089081],\n            [-69.816973, 1.714805],\n            [-67.868565, 1.692455],\n            [-67.53781, 2.037163],\n            [-67.259998, 1.719999],\n            [-67.065048, 1.130112],\n            [-66.876326, 1.253361],\n            [-66.325765, 0.724452],\n            [-65.548267, 0.789254],\n            [-65.354713, 1.095282],\n            [-64.611012, 1.328731],\n            [-64.199306, 1.492855],\n            [-64.083085, 1.916369],\n            [-63.368788, 2.2009],\n            [-63.422867, 2.411068],\n            [-64.269999, 2.497006],\n            [-64.408828, 3.126786],\n            [-64.368494, 3.79721],\n            [-64.816064, 4.056445],\n            [-64.628659, 4.148481],\n            [-63.888343, 4.02053],\n            [-63.093198, 3.770571],\n            [-62.804533, 4.006965],\n            [-62.08543, 4.162124],\n            [-60.966893, 4.536468],\n            [-60.601179, 4.918098],\n            [-60.733574, 5.200277],\n            [-60.213683, 5.244486],\n            [-59.980959, 5.014061],\n            [-60.111002, 4.574967],\n            [-59.767406, 4.423503],\n            [-59.53804, 3.958803],\n            [-59.815413, 3.606499],\n            [-59.974525, 2.755233],\n            [-59.718546, 2.24963],\n            [-59.646044, 1.786894],\n            [-59.030862, 1.317698],\n            [-58.540013, 1.268088],\n            [-58.429477, 1.463942],\n            [-58.11345, 1.507195],\n            [-57.660971, 1.682585],\n            [-57.335823, 1.948538],\n            [-56.782704, 1.863711],\n            [-56.539386, 1.899523],\n            [-55.995698, 1.817667],\n            [-55.9056, 2.021996],\n            [-56.073342, 2.220795],\n            [-55.973322, 2.510364],\n            [-55.569755, 2.421506],\n            [-55.097587, 2.523748],\n            [-54.524754, 2.311849],\n            [-54.088063, 2.105557],\n            [-53.778521, 2.376703],\n            [-53.554839, 2.334897],\n            [-53.418465, 2.053389],\n            [-52.939657, 2.124858],\n            [-52.556425, 2.504705],\n            [-52.249338, 3.241094],\n            [-51.657797, 4.156232],\n            [-51.317146, 4.203491],\n            [-51.069771, 3.650398],\n            [-50.508875, 1.901564],\n            [-49.974076, 1.736483],\n            [-49.947101, 1.04619],\n            [-50.699251, 0.222984],\n            [-50.388211, -0.078445],\n            [-48.620567, -0.235489],\n            [-48.584497, -1.237805],\n            [-47.824956, -0.581618],\n            [-46.566584, -0.941028],\n            [-44.905703, -1.55174],\n            [-44.417619, -2.13775],\n            [-44.581589, -2.691308],\n            [-43.418791, -2.38311],\n            [-41.472657, -2.912018],\n            [-39.978665, -2.873054],\n            [-38.500383, -3.700652],\n            [-37.223252, -4.820946],\n            [-36.452937, -5.109404],\n            [-35.597796, -5.149504],\n            [-35.235389, -5.464937],\n            [-34.89603, -6.738193],\n            [-34.729993, -7.343221],\n            [-35.128212, -8.996401],\n            [-35.636967, -9.649282],\n            [-37.046519, -11.040721],\n            [-37.683612, -12.171195],\n            [-38.423877, -13.038119],\n            [-38.673887, -13.057652],\n            [-38.953276, -13.79337],\n            [-38.882298, -15.667054],\n            [-39.161092, -17.208407],\n            [-39.267339, -17.867746],\n            [-39.583521, -18.262296],\n            [-39.760823, -19.599113],\n            [-40.774741, -20.904512],\n            [-40.944756, -21.937317],\n            [-41.754164, -22.370676],\n            [-41.988284, -22.97007],\n            [-43.074704, -22.967693],\n            [-44.647812, -23.351959],\n            [-45.352136, -23.796842],\n            [-46.472093, -24.088969],\n            [-47.648972, -24.885199],\n            [-48.495458, -25.877025],\n            [-48.641005, -26.623698],\n            [-48.474736, -27.175912],\n            [-48.66152, -28.186135],\n            [-48.888457, -28.674115],\n            [-49.587329, -29.224469],\n            [-50.696874, -30.984465],\n            [-51.576226, -31.777698],\n            [-52.256081, -32.24537],\n            [-52.7121, -33.196578],\n            [-53.373662, -33.768378],\n            [-53.650544, -33.202004],\n            [-53.209589, -32.727666],\n            [-53.787952, -32.047243],\n            [-54.572452, -31.494511],\n            [-55.60151, -30.853879],\n            [-55.973245, -30.883076],\n            [-56.976026, -30.109686],\n            [-57.625133, -30.216295]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Brunei\",\n        \"SOV_A3\": \"BRN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Brunei\",\n        \"ADM0_A3\": \"BRN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Brunei\",\n        \"GU_A3\": \"BRN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Brunei\",\n        \"SU_A3\": \"BRN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Brunei\",\n        \"NAME_LONG\": \"Brunei Darussalam\",\n        \"BRK_A3\": \"BRN\",\n        \"BRK_NAME\": \"Brunei\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Brunei\",\n        \"POSTAL\": \"BN\",\n        \"FORMAL_EN\": \"Negara Brunei Darussalam\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Brunei\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Brunei\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 443593,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 33730,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BX\",\n        \"ISO_A2\": \"BN\",\n        \"ISO_A3\": \"BRN\",\n        \"ISO_A3_EH\": \"BRN\",\n        \"ISO_N3\": \"096\",\n        \"UN_A3\": \"096\",\n        \"WB_A2\": \"BN\",\n        \"WB_A3\": \"BRN\",\n        \"WOE_ID\": 23424773,\n        \"WOE_ID_EH\": 23424773,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BRN\",\n        \"ADM0_A3_US\": \"BRN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 17,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": 2,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [114.204017, 4.007637, 115.45071, 5.44773],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [114.204017, 4.525874],\n            [114.599961, 4.900011],\n            [115.45071, 5.44773],\n            [115.4057, 4.955228],\n            [115.347461, 4.316636],\n            [114.869557, 4.348314],\n            [114.659596, 4.007637],\n            [114.204017, 4.525874]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Bhutan\",\n        \"SOV_A3\": \"BTN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Bhutan\",\n        \"ADM0_A3\": \"BTN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Bhutan\",\n        \"GU_A3\": \"BTN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Bhutan\",\n        \"SU_A3\": \"BTN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Bhutan\",\n        \"NAME_LONG\": \"Bhutan\",\n        \"BRK_A3\": \"BTN\",\n        \"BRK_NAME\": \"Bhutan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bhutan\",\n        \"POSTAL\": \"BT\",\n        \"FORMAL_EN\": \"Kingdom of Bhutan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Bhutan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Bhutan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 758288,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 6432,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2005,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BT\",\n        \"ISO_A2\": \"BT\",\n        \"ISO_A3\": \"BTN\",\n        \"ISO_A3_EH\": \"BTN\",\n        \"ISO_N3\": \"064\",\n        \"UN_A3\": \"064\",\n        \"WB_A2\": \"BT\",\n        \"WB_A3\": \"BTN\",\n        \"WOE_ID\": 23424770,\n        \"WOE_ID_EH\": 23424770,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BTN\",\n        \"ADM0_A3_US\": \"BTN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [88.814248, 26.719403, 92.103712, 28.296439],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [91.696657, 27.771742],\n            [92.103712, 27.452614],\n            [92.033484, 26.83831],\n            [91.217513, 26.808648],\n            [90.373275, 26.875724],\n            [89.744528, 26.719403],\n            [88.835643, 27.098966],\n            [88.814248, 27.299316],\n            [89.47581, 28.042759],\n            [90.015829, 28.296439],\n            [90.730514, 28.064954],\n            [91.258854, 28.040614],\n            [91.696657, 27.771742]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Botswana\",\n        \"SOV_A3\": \"BWA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Botswana\",\n        \"ADM0_A3\": \"BWA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Botswana\",\n        \"GU_A3\": \"BWA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Botswana\",\n        \"SU_A3\": \"BWA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Botswana\",\n        \"NAME_LONG\": \"Botswana\",\n        \"BRK_A3\": \"BWA\",\n        \"BRK_NAME\": \"Botswana\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Bwa.\",\n        \"POSTAL\": \"BW\",\n        \"FORMAL_EN\": \"Republic of Botswana\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Botswana\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Botswana\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 2214858,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 35900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BC\",\n        \"ISO_A2\": \"BW\",\n        \"ISO_A3\": \"BWA\",\n        \"ISO_A3_EH\": \"BWA\",\n        \"ISO_N3\": \"072\",\n        \"UN_A3\": \"072\",\n        \"WB_A2\": \"BW\",\n        \"WB_A3\": \"BWA\",\n        \"WOE_ID\": 23424755,\n        \"WOE_ID_EH\": 23424755,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"BWA\",\n        \"ADM0_A3_US\": \"BWA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Southern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [19.895458, -26.828543, 29.432188, -17.661816],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [29.432188, -22.091313],\n            [28.017236, -22.827754],\n            [27.11941, -23.574323],\n            [26.786407, -24.240691],\n            [26.485753, -24.616327],\n            [25.941652, -24.696373],\n            [25.765849, -25.174845],\n            [25.664666, -25.486816],\n            [25.025171, -25.71967],\n            [24.211267, -25.670216],\n            [23.73357, -25.390129],\n            [23.312097, -25.26869],\n            [22.824271, -25.500459],\n            [22.579532, -25.979448],\n            [22.105969, -26.280256],\n            [21.605896, -26.726534],\n            [20.889609, -26.828543],\n            [20.66647, -26.477453],\n            [20.758609, -25.868136],\n            [20.165726, -24.917962],\n            [19.895768, -24.76779],\n            [19.895458, -21.849157],\n            [20.881134, -21.814327],\n            [20.910641, -18.252219],\n            [21.65504, -18.219146],\n            [23.196858, -17.869038],\n            [23.579006, -18.281261],\n            [24.217365, -17.889347],\n            [24.520705, -17.887125],\n            [25.084443, -17.661816],\n            [25.264226, -17.73654],\n            [25.649163, -18.536026],\n            [25.850391, -18.714413],\n            [26.164791, -19.293086],\n            [27.296505, -20.39152],\n            [27.724747, -20.499059],\n            [27.727228, -20.851802],\n            [28.02137, -21.485975],\n            [28.794656, -21.639454],\n            [29.432188, -22.091313]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Central African Republic\",\n        \"SOV_A3\": \"CAF\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Central African Republic\",\n        \"ADM0_A3\": \"CAF\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Central African Republic\",\n        \"GU_A3\": \"CAF\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Central African Republic\",\n        \"SU_A3\": \"CAF\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Central African Rep.\",\n        \"NAME_LONG\": \"Central African Republic\",\n        \"BRK_A3\": \"CAF\",\n        \"BRK_NAME\": \"Central African Rep.\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"C.A.R.\",\n        \"POSTAL\": \"CF\",\n        \"FORMAL_EN\": \"Central African Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Central African Republic\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Central African Republic\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 5625118,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 3206,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2003,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CT\",\n        \"ISO_A2\": \"CF\",\n        \"ISO_A3\": \"CAF\",\n        \"ISO_A3_EH\": \"CAF\",\n        \"ISO_N3\": \"140\",\n        \"UN_A3\": \"140\",\n        \"WB_A2\": \"CF\",\n        \"WB_A3\": \"CAF\",\n        \"WOE_ID\": 23424792,\n        \"WOE_ID_EH\": 23424792,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CAF\",\n        \"ADM0_A3_US\": \"CAF\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 20,\n        \"LONG_LEN\": 24,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [14.459407, 2.26764, 27.374226, 11.142395],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [15.27946, 7.421925],\n            [16.106232, 7.497088],\n            [16.290562, 7.754307],\n            [16.456185, 7.734774],\n            [16.705988, 7.508328],\n            [17.96493, 7.890914],\n            [18.389555, 8.281304],\n            [18.911022, 8.630895],\n            [18.81201, 8.982915],\n            [19.094008, 9.074847],\n            [20.059685, 9.012706],\n            [21.000868, 9.475985],\n            [21.723822, 10.567056],\n            [22.231129, 10.971889],\n            [22.864165, 11.142395],\n            [22.977544, 10.714463],\n            [23.554304, 10.089255],\n            [23.55725, 9.681218],\n            [23.394779, 9.265068],\n            [23.459013, 8.954286],\n            [23.805813, 8.666319],\n            [24.567369, 8.229188],\n            [25.114932, 7.825104],\n            [25.124131, 7.500085],\n            [25.796648, 6.979316],\n            [26.213418, 6.546603],\n            [26.465909, 5.946717],\n            [27.213409, 5.550953],\n            [27.374226, 5.233944],\n            [27.044065, 5.127853],\n            [26.402761, 5.150875],\n            [25.650455, 5.256088],\n            [25.278798, 5.170408],\n            [25.128833, 4.927245],\n            [24.805029, 4.897247],\n            [24.410531, 5.108784],\n            [23.297214, 4.609693],\n            [22.84148, 4.710126],\n            [22.704124, 4.633051],\n            [22.405124, 4.02916],\n            [21.659123, 4.224342],\n            [20.927591, 4.322786],\n            [20.290679, 4.691678],\n            [19.467784, 5.031528],\n            [18.932312, 4.709506],\n            [18.542982, 4.201785],\n            [18.453065, 3.504386],\n            [17.8099, 3.560196],\n            [17.133042, 3.728197],\n            [16.537058, 3.198255],\n            [16.012852, 2.26764],\n            [15.907381, 2.557389],\n            [15.862732, 3.013537],\n            [15.405396, 3.335301],\n            [15.03622, 3.851367],\n            [14.950953, 4.210389],\n            [14.478372, 4.732605],\n            [14.558936, 5.030598],\n            [14.459407, 5.451761],\n            [14.53656, 6.226959],\n            [14.776545, 6.408498],\n            [15.27946, 7.421925]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Canada\",\n        \"SOV_A3\": \"CAN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Canada\",\n        \"ADM0_A3\": \"CAN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Canada\",\n        \"GU_A3\": \"CAN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Canada\",\n        \"SU_A3\": \"CAN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Canada\",\n        \"NAME_LONG\": \"Canada\",\n        \"BRK_A3\": \"CAN\",\n        \"BRK_NAME\": \"Canada\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Can.\",\n        \"POSTAL\": \"CA\",\n        \"FORMAL_EN\": \"Canada\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Canada\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Canada\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 35623680,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 1674000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CA\",\n        \"ISO_A2\": \"CA\",\n        \"ISO_A3\": \"CAN\",\n        \"ISO_A3_EH\": \"CAN\",\n        \"ISO_N3\": \"124\",\n        \"UN_A3\": \"124\",\n        \"WB_A2\": \"CA\",\n        \"WB_A3\": \"CAN\",\n        \"WOE_ID\": 23424775,\n        \"WOE_ID_EH\": 23424775,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CAN\",\n        \"ADM0_A3_US\": \"CAN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Northern America\",\n        \"REGION_WB\": \"North America\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 5.7\n      },\n      \"bbox\": [-140.99778, 41.675105, -52.648099, 83.23324],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-63.6645, 46.55001],\n              [-62.9393, 46.41587],\n              [-62.01208, 46.44314],\n              [-62.50391, 46.03339],\n              [-62.87433, 45.96818],\n              [-64.1428, 46.39265],\n              [-64.39261, 46.72747],\n              [-64.01486, 47.03601],\n              [-63.6645, 46.55001]\n            ]\n          ],\n          [\n            [\n              [-61.806305, 49.10506],\n              [-62.29318, 49.08717],\n              [-63.58926, 49.40069],\n              [-64.51912, 49.87304],\n              [-64.17322, 49.95718],\n              [-62.85829, 49.70641],\n              [-61.835585, 49.28855],\n              [-61.806305, 49.10506]\n            ]\n          ],\n          [\n            [\n              [-123.510002, 48.510011],\n              [-124.012891, 48.370846],\n              [-125.655013, 48.825005],\n              [-125.954994, 49.179996],\n              [-126.850004, 49.53],\n              [-127.029993, 49.814996],\n              [-128.059336, 49.994959],\n              [-128.444584, 50.539138],\n              [-128.358414, 50.770648],\n              [-127.308581, 50.552574],\n              [-126.695001, 50.400903],\n              [-125.755007, 50.295018],\n              [-125.415002, 49.950001],\n              [-124.920768, 49.475275],\n              [-123.922509, 49.062484],\n              [-123.510002, 48.510011]\n            ]\n          ],\n          [\n            [\n              [-56.134036, 50.68701],\n              [-56.795882, 49.812309],\n              [-56.143105, 50.150117],\n              [-55.471492, 49.935815],\n              [-55.822401, 49.587129],\n              [-54.935143, 49.313011],\n              [-54.473775, 49.556691],\n              [-53.476549, 49.249139],\n              [-53.786014, 48.516781],\n              [-53.086134, 48.687804],\n              [-52.958648, 48.157164],\n              [-52.648099, 47.535548],\n              [-53.069158, 46.655499],\n              [-53.521456, 46.618292],\n              [-54.178936, 46.807066],\n              [-53.961869, 47.625207],\n              [-54.240482, 47.752279],\n              [-55.400773, 46.884994],\n              [-55.997481, 46.91972],\n              [-55.291219, 47.389562],\n              [-56.250799, 47.632545],\n              [-57.325229, 47.572807],\n              [-59.266015, 47.603348],\n              [-59.419494, 47.899454],\n              [-58.796586, 48.251525],\n              [-59.231625, 48.523188],\n              [-58.391805, 49.125581],\n              [-57.35869, 50.718274],\n              [-56.73865, 51.287438],\n              [-55.870977, 51.632094],\n              [-55.406974, 51.588273],\n              [-55.600218, 51.317075],\n              [-56.134036, 50.68701]\n            ]\n          ],\n          [\n            [\n              [-133.180004, 54.169975],\n              [-132.710008, 54.040009],\n              [-131.74999, 54.120004],\n              [-132.04948, 52.984621],\n              [-131.179043, 52.180433],\n              [-131.57783, 52.182371],\n              [-132.180428, 52.639707],\n              [-132.549992, 53.100015],\n              [-133.054611, 53.411469],\n              [-133.239664, 53.85108],\n              [-133.180004, 54.169975]\n            ]\n          ],\n          [\n            [\n              [-79.26582, 62.158675],\n              [-79.65752, 61.63308],\n              [-80.09956, 61.7181],\n              [-80.36215, 62.01649],\n              [-80.315395, 62.085565],\n              [-79.92939, 62.3856],\n              [-79.52002, 62.36371],\n              [-79.26582, 62.158675]\n            ]\n          ],\n          [\n            [\n              [-81.89825, 62.7108],\n              [-83.06857, 62.15922],\n              [-83.77462, 62.18231],\n              [-83.99367, 62.4528],\n              [-83.25048, 62.91409],\n              [-81.87699, 62.90458],\n              [-81.89825, 62.7108]\n            ]\n          ],\n          [\n            [\n              [-85.161308, 65.657285],\n              [-84.975764, 65.217518],\n              [-84.464012, 65.371772],\n              [-83.882626, 65.109618],\n              [-82.787577, 64.766693],\n              [-81.642014, 64.455136],\n              [-81.55344, 63.979609],\n              [-80.817361, 64.057486],\n              [-80.103451, 63.725981],\n              [-80.99102, 63.411246],\n              [-82.547178, 63.651722],\n              [-83.108798, 64.101876],\n              [-84.100417, 63.569712],\n              [-85.523405, 63.052379],\n              [-85.866769, 63.637253],\n              [-87.221983, 63.541238],\n              [-86.35276, 64.035833],\n              [-86.224886, 64.822917],\n              [-85.883848, 65.738778],\n              [-85.161308, 65.657285]\n            ]\n          ],\n          [\n            [\n              [-75.86588, 67.14886],\n              [-76.98687, 67.09873],\n              [-77.2364, 67.58809],\n              [-76.81166, 68.14856],\n              [-75.89521, 68.28721],\n              [-75.1145, 68.01036],\n              [-75.10333, 67.58202],\n              [-75.21597, 67.44425],\n              [-75.86588, 67.14886]\n            ]\n          ],\n          [\n            [\n              [-95.647681, 69.10769],\n              [-96.269521, 68.75704],\n              [-97.617401, 69.06003],\n              [-98.431801, 68.9507],\n              [-99.797401, 69.40003],\n              [-98.917401, 69.71003],\n              [-98.218261, 70.14354],\n              [-97.157401, 69.86003],\n              [-96.557401, 69.68003],\n              [-96.257401, 69.49003],\n              [-95.647681, 69.10769]\n            ]\n          ],\n          [\n            [\n              [-68.23444, 47.35486],\n              [-68.905, 47.185],\n              [-69.237216, 47.447781],\n              [-69.99997, 46.69307],\n              [-70.305, 45.915],\n              [-70.66, 45.46],\n              [-71.08482, 45.30524],\n              [-71.405, 45.255],\n              [-71.50506, 45.0082],\n              [-73.34783, 45.00738],\n              [-74.867, 45.00048],\n              [-75.31821, 44.81645],\n              [-76.375, 44.09631],\n              [-76.5, 44.018459],\n              [-76.820034, 43.628784],\n              [-77.737885, 43.629056],\n              [-78.72028, 43.625089],\n              [-79.171674, 43.466339],\n              [-79.01, 43.27],\n              [-78.92, 42.965],\n              [-78.939362, 42.863611],\n              [-80.247448, 42.3662],\n              [-81.277747, 42.209026],\n              [-82.439278, 41.675105],\n              [-82.690089, 41.675105],\n              [-83.02981, 41.832796],\n              [-83.142, 41.975681],\n              [-83.12, 42.08],\n              [-82.9, 42.43],\n              [-82.43, 42.98],\n              [-82.137642, 43.571088],\n              [-82.337763, 44.44],\n              [-82.550925, 45.347517],\n              [-83.592851, 45.816894],\n              [-83.469551, 45.994686],\n              [-83.616131, 46.116927],\n              [-83.890765, 46.116927],\n              [-84.091851, 46.275419],\n              [-84.14212, 46.512226],\n              [-84.3367, 46.40877],\n              [-84.6049, 46.4396],\n              [-84.543749, 46.538684],\n              [-84.779238, 46.637102],\n              [-84.87608, 46.900083],\n              [-85.652363, 47.220219],\n              [-86.461991, 47.553338],\n              [-87.439793, 47.94],\n              [-88.378114, 48.302918],\n              [-89.272917, 48.019808],\n              [-89.6, 48.01],\n              [-90.83, 48.27],\n              [-91.64, 48.14],\n              [-92.61, 48.45],\n              [-93.63087, 48.60926],\n              [-94.32914, 48.67074],\n              [-94.64, 48.84],\n              [-94.81758, 49.38905],\n              [-95.15609, 49.38425],\n              [-95.15907, 49],\n              [-97.22872, 49.0007],\n              [-100.65, 49],\n              [-104.04826, 48.99986],\n              [-107.05, 49],\n              [-110.05, 49],\n              [-113, 49],\n              [-116.04818, 49],\n              [-117.03121, 49],\n              [-120, 49],\n              [-122.84, 49],\n              [-122.97421, 49.002538],\n              [-124.91024, 49.98456],\n              [-125.62461, 50.41656],\n              [-127.43561, 50.83061],\n              [-127.99276, 51.71583],\n              [-127.85032, 52.32961],\n              [-129.12979, 52.75538],\n              [-129.30523, 53.56159],\n              [-130.51497, 54.28757],\n              [-130.536109, 54.802754],\n              [-130.53611, 54.80278],\n              [-129.98, 55.285],\n              [-130.00778, 55.91583],\n              [-131.70781, 56.55212],\n              [-132.73042, 57.69289],\n              [-133.35556, 58.41028],\n              [-134.27111, 58.86111],\n              [-134.945, 59.27056],\n              [-135.47583, 59.78778],\n              [-136.47972, 59.46389],\n              [-137.4525, 58.905],\n              [-138.34089, 59.56211],\n              [-139.039, 60],\n              [-140.013, 60.27682],\n              [-140.99778, 60.30639],\n              [-140.9925, 66.00003],\n              [-140.986, 69.712],\n              [-140.985988, 69.711998],\n              [-139.12052, 69.47102],\n              [-137.54636, 68.99002],\n              [-136.50358, 68.89804],\n              [-135.62576, 69.31512],\n              [-134.41464, 69.62743],\n              [-132.92925, 69.50534],\n              [-131.43136, 69.94451],\n              [-129.79471, 70.19369],\n              [-129.10773, 69.77927],\n              [-128.36156, 70.01286],\n              [-128.13817, 70.48384],\n              [-127.44712, 70.37721],\n              [-125.75632, 69.48058],\n              [-124.42483, 70.1584],\n              [-124.28968, 69.39969],\n              [-123.06108, 69.56372],\n              [-122.6835, 69.85553],\n              [-121.47226, 69.79778],\n              [-119.94288, 69.37786],\n              [-117.60268, 69.01128],\n              [-116.22643, 68.84151],\n              [-115.2469, 68.90591],\n              [-113.89794, 68.3989],\n              [-115.30489, 67.90261],\n              [-113.49727, 67.68815],\n              [-110.798, 67.80612],\n              [-109.94619, 67.98104],\n              [-108.8802, 67.38144],\n              [-107.79239, 67.88736],\n              [-108.81299, 68.31164],\n              [-108.16721, 68.65392],\n              [-106.95, 68.7],\n              [-106.15, 68.8],\n              [-105.34282, 68.56122],\n              [-104.33791, 68.018],\n              [-103.22115, 68.09775],\n              [-101.45433, 67.64689],\n              [-99.90195, 67.80566],\n              [-98.4432, 67.78165],\n              [-98.5586, 68.40394],\n              [-97.66948, 68.57864],\n              [-96.11991, 68.23939],\n              [-96.12588, 67.29338],\n              [-95.48943, 68.0907],\n              [-94.685, 68.06383],\n              [-94.23282, 69.06903],\n              [-95.30408, 69.68571],\n              [-96.47131, 70.08976],\n              [-96.39115, 71.19482],\n              [-95.2088, 71.92053],\n              [-93.88997, 71.76015],\n              [-92.87818, 71.31869],\n              [-91.51964, 70.19129],\n              [-92.40692, 69.69997],\n              [-90.5471, 69.49766],\n              [-90.55151, 68.47499],\n              [-89.21515, 69.25873],\n              [-88.01966, 68.61508],\n              [-88.31749, 67.87338],\n              [-87.35017, 67.19872],\n              [-86.30607, 67.92146],\n              [-85.57664, 68.78456],\n              [-85.52197, 69.88211],\n              [-84.10081, 69.80539],\n              [-82.62258, 69.65826],\n              [-81.28043, 69.16202],\n              [-81.2202, 68.66567],\n              [-81.96436, 68.13253],\n              [-81.25928, 67.59716],\n              [-81.38653, 67.11078],\n              [-83.34456, 66.41154],\n              [-84.73542, 66.2573],\n              [-85.76943, 66.55833],\n              [-86.0676, 66.05625],\n              [-87.03143, 65.21297],\n              [-87.32324, 64.77563],\n              [-88.48296, 64.09897],\n              [-89.91444, 64.03273],\n              [-90.70398, 63.61017],\n              [-90.77004, 62.96021],\n              [-91.93342, 62.83508],\n              [-93.15698, 62.02469],\n              [-94.24153, 60.89865],\n              [-94.62931, 60.11021],\n              [-94.6846, 58.94882],\n              [-93.21502, 58.78212],\n              [-92.76462, 57.84571],\n              [-92.29703, 57.08709],\n              [-90.89769, 57.28468],\n              [-89.03953, 56.85172],\n              [-88.03978, 56.47162],\n              [-87.32421, 55.99914],\n              [-86.07121, 55.72383],\n              [-85.01181, 55.3026],\n              [-83.36055, 55.24489],\n              [-82.27285, 55.14832],\n              [-82.4362, 54.28227],\n              [-82.12502, 53.27703],\n              [-81.40075, 52.15788],\n              [-79.91289, 51.20842],\n              [-79.14301, 51.53393],\n              [-78.60191, 52.56208],\n              [-79.12421, 54.14145],\n              [-79.82958, 54.66772],\n              [-78.22874, 55.13645],\n              [-77.0956, 55.83741],\n              [-76.54137, 56.53423],\n              [-76.62319, 57.20263],\n              [-77.30226, 58.05209],\n              [-78.51688, 58.80458],\n              [-77.33676, 59.85261],\n              [-77.77272, 60.75788],\n              [-78.10687, 62.31964],\n              [-77.41067, 62.55053],\n              [-75.69621, 62.2784],\n              [-74.6682, 62.18111],\n              [-73.83988, 62.4438],\n              [-72.90853, 62.10507],\n              [-71.67708, 61.52535],\n              [-71.37369, 61.13717],\n              [-69.59042, 61.06141],\n              [-69.62033, 60.22125],\n              [-69.2879, 58.95736],\n              [-68.37455, 58.80106],\n              [-67.64976, 58.21206],\n              [-66.20178, 58.76731],\n              [-65.24517, 59.87071],\n              [-64.58352, 60.33558],\n              [-63.80475, 59.4426],\n              [-62.50236, 58.16708],\n              [-61.39655, 56.96745],\n              [-61.79866, 56.33945],\n              [-60.46853, 55.77548],\n              [-59.56962, 55.20407],\n              [-57.97508, 54.94549],\n              [-57.3332, 54.6265],\n              [-56.93689, 53.78032],\n              [-56.15811, 53.64749],\n              [-55.75632, 53.27036],\n              [-55.68338, 52.14664],\n              [-56.40916, 51.7707],\n              [-57.12691, 51.41972],\n              [-58.77482, 51.0643],\n              [-60.03309, 50.24277],\n              [-61.72366, 50.08046],\n              [-63.86251, 50.29099],\n              [-65.36331, 50.2982],\n              [-66.39905, 50.22897],\n              [-67.23631, 49.51156],\n              [-68.51114, 49.06836],\n              [-69.95362, 47.74488],\n              [-71.10458, 46.82171],\n              [-70.25522, 46.98606],\n              [-68.65, 48.3],\n              [-66.55243, 49.1331],\n              [-65.05626, 49.23278],\n              [-64.17099, 48.74248],\n              [-65.11545, 48.07085],\n              [-64.79854, 46.99297],\n              [-64.47219, 46.23849],\n              [-63.17329, 45.73902],\n              [-61.52072, 45.88377],\n              [-60.51815, 47.00793],\n              [-60.4486, 46.28264],\n              [-59.80287, 45.9204],\n              [-61.03988, 45.26525],\n              [-63.25471, 44.67014],\n              [-64.24656, 44.26553],\n              [-65.36406, 43.54523],\n              [-66.1234, 43.61867],\n              [-66.16173, 44.46512],\n              [-64.42549, 45.29204],\n              [-66.02605, 45.25931],\n              [-67.13741, 45.13753],\n              [-67.79134, 45.70281],\n              [-67.79046, 47.06636],\n              [-68.23444, 47.35486]\n            ]\n          ],\n          [\n            [\n              [-114.16717, 73.12145],\n              [-114.66634, 72.65277],\n              [-112.44102, 72.9554],\n              [-111.05039, 72.4504],\n              [-109.92035, 72.96113],\n              [-109.00654, 72.63335],\n              [-108.18835, 71.65089],\n              [-107.68599, 72.06548],\n              [-108.39639, 73.08953],\n              [-107.51645, 73.23598],\n              [-106.52259, 73.07601],\n              [-105.40246, 72.67259],\n              [-104.77484, 71.6984],\n              [-104.46476, 70.99297],\n              [-102.78537, 70.49776],\n              [-100.98078, 70.02432],\n              [-101.08929, 69.58447],\n              [-102.73116, 69.50402],\n              [-102.09329, 69.11962],\n              [-102.43024, 68.75282],\n              [-104.24, 68.91],\n              [-105.96, 69.18],\n              [-107.12254, 69.11922],\n              [-109, 68.78],\n              [-111.534149, 68.630059],\n              [-113.3132, 68.53554],\n              [-113.85496, 69.00744],\n              [-115.22, 69.28],\n              [-116.10794, 69.16821],\n              [-117.34, 69.96],\n              [-116.67473, 70.06655],\n              [-115.13112, 70.2373],\n              [-113.72141, 70.19237],\n              [-112.4161, 70.36638],\n              [-114.35, 70.6],\n              [-116.48684, 70.52045],\n              [-117.9048, 70.54056],\n              [-118.43238, 70.9092],\n              [-116.11311, 71.30918],\n              [-117.65568, 71.2952],\n              [-119.40199, 71.55859],\n              [-118.56267, 72.30785],\n              [-117.86642, 72.70594],\n              [-115.18909, 73.31459],\n              [-114.16717, 73.12145]\n            ]\n          ],\n          [\n            [\n              [-104.5, 73.42],\n              [-105.38, 72.76],\n              [-106.94, 73.46],\n              [-106.6, 73.6],\n              [-105.26, 73.64],\n              [-104.5, 73.42]\n            ]\n          ],\n          [\n            [\n              [-76.34, 73.102685],\n              [-76.251404, 72.826385],\n              [-77.314438, 72.855545],\n              [-78.39167, 72.876656],\n              [-79.486252, 72.742203],\n              [-79.775833, 72.802902],\n              [-80.876099, 73.333183],\n              [-80.833885, 73.693184],\n              [-80.353058, 73.75972],\n              [-78.064438, 73.651932],\n              [-76.34, 73.102685]\n            ]\n          ],\n          [\n            [\n              [-86.562179, 73.157447],\n              [-85.774371, 72.534126],\n              [-84.850112, 73.340278],\n              [-82.31559, 73.750951],\n              [-80.600088, 72.716544],\n              [-80.748942, 72.061907],\n              [-78.770639, 72.352173],\n              [-77.824624, 72.749617],\n              [-75.605845, 72.243678],\n              [-74.228616, 71.767144],\n              [-74.099141, 71.33084],\n              [-72.242226, 71.556925],\n              [-71.200015, 70.920013],\n              [-68.786054, 70.525024],\n              [-67.91497, 70.121948],\n              [-66.969033, 69.186087],\n              [-68.805123, 68.720198],\n              [-66.449866, 68.067163],\n              [-64.862314, 67.847539],\n              [-63.424934, 66.928473],\n              [-61.851981, 66.862121],\n              [-62.163177, 66.160251],\n              [-63.918444, 64.998669],\n              [-65.14886, 65.426033],\n              [-66.721219, 66.388041],\n              [-68.015016, 66.262726],\n              [-68.141287, 65.689789],\n              [-67.089646, 65.108455],\n              [-65.73208, 64.648406],\n              [-65.320168, 64.382737],\n              [-64.669406, 63.392927],\n              [-65.013804, 62.674185],\n              [-66.275045, 62.945099],\n              [-68.783186, 63.74567],\n              [-67.369681, 62.883966],\n              [-66.328297, 62.280075],\n              [-66.165568, 61.930897],\n              [-68.877367, 62.330149],\n              [-71.023437, 62.910708],\n              [-72.235379, 63.397836],\n              [-71.886278, 63.679989],\n              [-73.378306, 64.193963],\n              [-74.834419, 64.679076],\n              [-74.818503, 64.389093],\n              [-77.70998, 64.229542],\n              [-78.555949, 64.572906],\n              [-77.897281, 65.309192],\n              [-76.018274, 65.326969],\n              [-73.959795, 65.454765],\n              [-74.293883, 65.811771],\n              [-73.944912, 66.310578],\n              [-72.651167, 67.284576],\n              [-72.92606, 67.726926],\n              [-73.311618, 68.069437],\n              [-74.843307, 68.554627],\n              [-76.869101, 68.894736],\n              [-76.228649, 69.147769],\n              [-77.28737, 69.76954],\n              [-78.168634, 69.826488],\n              [-78.957242, 70.16688],\n              [-79.492455, 69.871808],\n              [-81.305471, 69.743185],\n              [-84.944706, 69.966634],\n              [-87.060003, 70.260001],\n              [-88.681713, 70.410741],\n              [-89.51342, 70.762038],\n              [-88.467721, 71.218186],\n              [-89.888151, 71.222552],\n              [-90.20516, 72.235074],\n              [-89.436577, 73.129464],\n              [-88.408242, 73.537889],\n              [-85.826151, 73.803816],\n              [-86.562179, 73.157447]\n            ]\n          ],\n          [\n            [\n              [-100.35642, 73.84389],\n              [-99.16387, 73.63339],\n              [-97.38, 73.76],\n              [-97.12, 73.47],\n              [-98.05359, 72.99052],\n              [-96.54, 72.56],\n              [-96.72, 71.66],\n              [-98.35966, 71.27285],\n              [-99.32286, 71.35639],\n              [-100.01482, 71.73827],\n              [-102.5, 72.51],\n              [-102.48, 72.83],\n              [-100.43836, 72.70588],\n              [-101.54, 73.36],\n              [-100.35642, 73.84389]\n            ]\n          ],\n          [\n            [\n              [-93.196296, 72.771992],\n              [-94.269047, 72.024596],\n              [-95.409856, 72.061881],\n              [-96.033745, 72.940277],\n              [-96.018268, 73.43743],\n              [-95.495793, 73.862417],\n              [-94.503658, 74.134907],\n              [-92.420012, 74.100025],\n              [-90.509793, 73.856732],\n              [-92.003965, 72.966244],\n              [-93.196296, 72.771992]\n            ]\n          ],\n          [\n            [\n              [-120.46, 71.383602],\n              [-123.09219, 70.90164],\n              [-123.62, 71.34],\n              [-125.928949, 71.868688],\n              [-125.5, 72.292261],\n              [-124.80729, 73.02256],\n              [-123.94, 73.68],\n              [-124.91775, 74.29275],\n              [-121.53788, 74.44893],\n              [-120.10978, 74.24135],\n              [-117.55564, 74.18577],\n              [-116.58442, 73.89607],\n              [-115.51081, 73.47519],\n              [-116.76794, 73.22292],\n              [-119.22, 72.52],\n              [-120.46, 71.82],\n              [-120.46, 71.383602]\n            ]\n          ],\n          [\n            [\n              [-93.612756, 74.979997],\n              [-94.156909, 74.592347],\n              [-95.608681, 74.666864],\n              [-96.820932, 74.927623],\n              [-96.288587, 75.377828],\n              [-94.85082, 75.647218],\n              [-93.977747, 75.29649],\n              [-93.612756, 74.979997]\n            ]\n          ],\n          [\n            [\n              [-98.5, 76.72],\n              [-97.735585, 76.25656],\n              [-97.704415, 75.74344],\n              [-98.16, 75],\n              [-99.80874, 74.89744],\n              [-100.88366, 75.05736],\n              [-100.86292, 75.64075],\n              [-102.50209, 75.5638],\n              [-102.56552, 76.3366],\n              [-101.48973, 76.30537],\n              [-99.98349, 76.64634],\n              [-98.57699, 76.58859],\n              [-98.5, 76.72]\n            ]\n          ],\n          [\n            [\n              [-108.21141, 76.20168],\n              [-107.81943, 75.84552],\n              [-106.92893, 76.01282],\n              [-105.881, 75.9694],\n              [-105.70498, 75.47951],\n              [-106.31347, 75.00527],\n              [-109.7, 74.85],\n              [-112.22307, 74.41696],\n              [-113.74381, 74.39427],\n              [-113.87135, 74.72029],\n              [-111.79421, 75.1625],\n              [-116.31221, 75.04343],\n              [-117.7104, 75.2222],\n              [-116.34602, 76.19903],\n              [-115.40487, 76.47887],\n              [-112.59056, 76.14134],\n              [-110.81422, 75.54919],\n              [-109.0671, 75.47321],\n              [-110.49726, 76.42982],\n              [-109.5811, 76.79417],\n              [-108.54859, 76.67832],\n              [-108.21141, 76.20168]\n            ]\n          ],\n          [\n            [\n              [-94.684086, 77.097878],\n              [-93.573921, 76.776296],\n              [-91.605023, 76.778518],\n              [-90.741846, 76.449597],\n              [-90.969661, 76.074013],\n              [-89.822238, 75.847774],\n              [-89.187083, 75.610166],\n              [-87.838276, 75.566189],\n              [-86.379192, 75.482421],\n              [-84.789625, 75.699204],\n              [-82.753445, 75.784315],\n              [-81.128531, 75.713983],\n              [-80.057511, 75.336849],\n              [-79.833933, 74.923127],\n              [-80.457771, 74.657304],\n              [-81.948843, 74.442459],\n              [-83.228894, 74.564028],\n              [-86.097452, 74.410032],\n              [-88.15035, 74.392307],\n              [-89.764722, 74.515555],\n              [-92.422441, 74.837758],\n              [-92.768285, 75.38682],\n              [-92.889906, 75.882655],\n              [-93.893824, 76.319244],\n              [-95.962457, 76.441381],\n              [-97.121379, 76.751078],\n              [-96.745123, 77.161389],\n              [-94.684086, 77.097878]\n            ]\n          ],\n          [\n            [\n              [-116.198587, 77.645287],\n              [-116.335813, 76.876962],\n              [-117.106051, 76.530032],\n              [-118.040412, 76.481172],\n              [-119.899318, 76.053213],\n              [-121.499995, 75.900019],\n              [-122.854924, 76.116543],\n              [-122.854925, 76.116543],\n              [-121.157535, 76.864508],\n              [-119.103939, 77.51222],\n              [-117.570131, 77.498319],\n              [-116.198587, 77.645287]\n            ]\n          ],\n          [\n            [\n              [-93.840003, 77.519997],\n              [-94.295608, 77.491343],\n              [-96.169654, 77.555111],\n              [-96.436304, 77.834629],\n              [-94.422577, 77.820005],\n              [-93.720656, 77.634331],\n              [-93.840003, 77.519997]\n            ]\n          ],\n          [\n            [\n              [-110.186938, 77.697015],\n              [-112.051191, 77.409229],\n              [-113.534279, 77.732207],\n              [-112.724587, 78.05105],\n              [-111.264443, 78.152956],\n              [-109.854452, 77.996325],\n              [-110.186938, 77.697015]\n            ]\n          ],\n          [\n            [\n              [-109.663146, 78.601973],\n              [-110.881314, 78.40692],\n              [-112.542091, 78.407902],\n              [-112.525891, 78.550555],\n              [-111.50001, 78.849994],\n              [-110.963661, 78.804441],\n              [-109.663146, 78.601973]\n            ]\n          ],\n          [\n            [\n              [-95.830295, 78.056941],\n              [-97.309843, 77.850597],\n              [-98.124289, 78.082857],\n              [-98.552868, 78.458105],\n              [-98.631984, 78.87193],\n              [-97.337231, 78.831984],\n              [-96.754399, 78.765813],\n              [-95.559278, 78.418315],\n              [-95.830295, 78.056941]\n            ]\n          ],\n          [\n            [\n              [-100.060192, 78.324754],\n              [-99.670939, 77.907545],\n              [-101.30394, 78.018985],\n              [-102.949809, 78.343229],\n              [-105.176133, 78.380332],\n              [-104.210429, 78.67742],\n              [-105.41958, 78.918336],\n              [-105.492289, 79.301594],\n              [-103.529282, 79.165349],\n              [-100.825158, 78.800462],\n              [-100.060192, 78.324754]\n            ]\n          ],\n          [\n            [\n              [-87.02, 79.66],\n              [-85.81435, 79.3369],\n              [-87.18756, 79.0393],\n              [-89.03535, 78.28723],\n              [-90.80436, 78.21533],\n              [-92.87669, 78.34333],\n              [-93.95116, 78.75099],\n              [-93.93574, 79.11373],\n              [-93.14524, 79.3801],\n              [-94.974, 79.37248],\n              [-96.07614, 79.70502],\n              [-96.70972, 80.15777],\n              [-96.01644, 80.60233],\n              [-95.32345, 80.90729],\n              [-94.29843, 80.97727],\n              [-94.73542, 81.20646],\n              [-92.40984, 81.25739],\n              [-91.13289, 80.72345],\n              [-89.45, 80.509322],\n              [-87.81, 80.32],\n              [-87.02, 79.66]\n            ]\n          ],\n          [\n            [\n              [-68.5, 83.106322],\n              [-65.82735, 83.02801],\n              [-63.68, 82.9],\n              [-61.85, 82.6286],\n              [-61.89388, 82.36165],\n              [-64.334, 81.92775],\n              [-66.75342, 81.72527],\n              [-67.65755, 81.50141],\n              [-65.48031, 81.50657],\n              [-67.84, 80.9],\n              [-69.4697, 80.61683],\n              [-71.18, 79.8],\n              [-73.2428, 79.63415],\n              [-73.88, 79.430162],\n              [-76.90773, 79.32309],\n              [-75.52924, 79.19766],\n              [-76.22046, 79.01907],\n              [-75.39345, 78.52581],\n              [-76.34354, 78.18296],\n              [-77.88851, 77.89991],\n              [-78.36269, 77.50859],\n              [-79.75951, 77.20968],\n              [-79.61965, 76.98336],\n              [-77.91089, 77.022045],\n              [-77.88911, 76.777955],\n              [-80.56125, 76.17812],\n              [-83.17439, 76.45403],\n              [-86.11184, 76.29901],\n              [-87.6, 76.42],\n              [-89.49068, 76.47239],\n              [-89.6161, 76.95213],\n              [-87.76739, 77.17833],\n              [-88.26, 77.9],\n              [-87.65, 77.970222],\n              [-84.97634, 77.53873],\n              [-86.34, 78.18],\n              [-87.96192, 78.37181],\n              [-87.15198, 78.75867],\n              [-85.37868, 78.9969],\n              [-85.09495, 79.34543],\n              [-86.50734, 79.73624],\n              [-86.93179, 80.25145],\n              [-84.19844, 80.20836],\n              [-83.408696, 80.1],\n              [-81.84823, 80.46442],\n              [-84.1, 80.58],\n              [-87.59895, 80.51627],\n              [-89.36663, 80.85569],\n              [-90.2, 81.26],\n              [-91.36786, 81.5531],\n              [-91.58702, 81.89429],\n              [-90.1, 82.085],\n              [-88.93227, 82.11751],\n              [-86.97024, 82.27961],\n              [-85.5, 82.652273],\n              [-84.260005, 82.6],\n              [-83.18, 82.32],\n              [-82.42, 82.86],\n              [-81.1, 83.02],\n              [-79.30664, 83.13056],\n              [-76.25, 83.172059],\n              [-75.71878, 83.06404],\n              [-72.83153, 83.23324],\n              [-70.665765, 83.169781],\n              [-68.5, 83.106322]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Switzerland\",\n        \"SOV_A3\": \"CHE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Switzerland\",\n        \"ADM0_A3\": \"CHE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Switzerland\",\n        \"GU_A3\": \"CHE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Switzerland\",\n        \"SU_A3\": \"CHE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Switzerland\",\n        \"NAME_LONG\": \"Switzerland\",\n        \"BRK_A3\": \"CHE\",\n        \"BRK_NAME\": \"Switzerland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Switz.\",\n        \"POSTAL\": \"CH\",\n        \"FORMAL_EN\": \"Swiss Confederation\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Switzerland\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Switzerland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 8236303,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 496300,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SZ\",\n        \"ISO_A2\": \"CH\",\n        \"ISO_A3\": \"CHE\",\n        \"ISO_A3_EH\": \"CHE\",\n        \"ISO_N3\": \"756\",\n        \"UN_A3\": \"756\",\n        \"WB_A2\": \"CH\",\n        \"WB_A3\": \"CHE\",\n        \"WOE_ID\": 23424957,\n        \"WOE_ID_EH\": 23424957,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CHE\",\n        \"ADM0_A3_US\": \"CHE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [6.022609, 45.776948, 10.442701, 47.830828],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [9.594226, 47.525058],\n            [9.632932, 47.347601],\n            [9.47997, 47.10281],\n            [9.932448, 46.920728],\n            [10.442701, 46.893546],\n            [10.363378, 46.483571],\n            [9.922837, 46.314899],\n            [9.182882, 46.440215],\n            [8.966306, 46.036932],\n            [8.489952, 46.005151],\n            [8.31663, 46.163642],\n            [7.755992, 45.82449],\n            [7.273851, 45.776948],\n            [6.843593, 45.991147],\n            [6.5001, 46.429673],\n            [6.022609, 46.27299],\n            [6.037389, 46.725779],\n            [6.768714, 47.287708],\n            [6.736571, 47.541801],\n            [7.192202, 47.449766],\n            [7.466759, 47.620582],\n            [8.317301, 47.61358],\n            [8.522612, 47.830828],\n            [9.594226, 47.525058]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Chile\",\n        \"SOV_A3\": \"CHL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Chile\",\n        \"ADM0_A3\": \"CHL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Chile\",\n        \"GU_A3\": \"CHL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Chile\",\n        \"SU_A3\": \"CHL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Chile\",\n        \"NAME_LONG\": \"Chile\",\n        \"BRK_A3\": \"CHL\",\n        \"BRK_NAME\": \"Chile\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Chile\",\n        \"POSTAL\": \"CL\",\n        \"FORMAL_EN\": \"Republic of Chile\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Chile\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Chile\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 17789267,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 436100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CI\",\n        \"ISO_A2\": \"CL\",\n        \"ISO_A3\": \"CHL\",\n        \"ISO_A3_EH\": \"CHL\",\n        \"ISO_N3\": \"152\",\n        \"UN_A3\": \"152\",\n        \"WB_A2\": \"CL\",\n        \"WB_A3\": \"CHL\",\n        \"WOE_ID\": 23424782,\n        \"WOE_ID_EH\": 23424782,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CHL\",\n        \"ADM0_A3_US\": \"CHL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [-75.644395, -55.61183, -66.95992, -17.580012],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-68.63401, -52.63637],\n              [-68.63335, -54.8695],\n              [-67.56244, -54.87001],\n              [-66.95992, -54.89681],\n              [-67.29103, -55.30124],\n              [-68.14863, -55.61183],\n              [-68.639991, -55.580018],\n              [-69.2321, -55.49906],\n              [-69.95809, -55.19843],\n              [-71.00568, -55.05383],\n              [-72.2639, -54.49514],\n              [-73.2852, -53.95752],\n              [-74.66253, -52.83749],\n              [-73.8381, -53.04743],\n              [-72.43418, -53.7154],\n              [-71.10773, -54.07433],\n              [-70.59178, -53.61583],\n              [-70.26748, -52.93123],\n              [-69.34565, -52.5183],\n              [-68.63401, -52.63637]\n            ]\n          ],\n          [\n            [\n              [-67.106674, -22.735925],\n              [-66.985234, -22.986349],\n              [-67.328443, -24.025303],\n              [-68.417653, -24.518555],\n              [-68.386001, -26.185016],\n              [-68.5948, -26.506909],\n              [-68.295542, -26.89934],\n              [-69.001235, -27.521214],\n              [-69.65613, -28.459141],\n              [-70.01355, -29.367923],\n              [-69.919008, -30.336339],\n              [-70.535069, -31.36501],\n              [-70.074399, -33.09121],\n              [-69.814777, -33.273886],\n              [-69.817309, -34.193571],\n              [-70.388049, -35.169688],\n              [-70.364769, -36.005089],\n              [-71.121881, -36.658124],\n              [-71.118625, -37.576827],\n              [-70.814664, -38.552995],\n              [-71.413517, -38.916022],\n              [-71.680761, -39.808164],\n              [-71.915734, -40.832339],\n              [-71.746804, -42.051386],\n              [-72.148898, -42.254888],\n              [-71.915424, -43.408565],\n              [-71.464056, -43.787611],\n              [-71.793623, -44.207172],\n              [-71.329801, -44.407522],\n              [-71.222779, -44.784243],\n              [-71.659316, -44.973689],\n              [-71.552009, -45.560733],\n              [-71.917258, -46.884838],\n              [-72.447355, -47.738533],\n              [-72.331161, -48.244238],\n              [-72.648247, -48.878618],\n              [-73.415436, -49.318436],\n              [-73.328051, -50.378785],\n              [-72.975747, -50.74145],\n              [-72.309974, -50.67701],\n              [-72.329404, -51.425956],\n              [-71.914804, -52.009022],\n              [-69.498362, -52.142761],\n              [-68.571545, -52.299444],\n              [-69.461284, -52.291951],\n              [-69.94278, -52.537931],\n              [-70.845102, -52.899201],\n              [-71.006332, -53.833252],\n              [-71.429795, -53.856455],\n              [-72.557943, -53.53141],\n              [-73.702757, -52.835069],\n              [-73.702757, -52.83507],\n              [-74.946763, -52.262754],\n              [-75.260026, -51.629355],\n              [-74.976632, -51.043396],\n              [-75.479754, -50.378372],\n              [-75.608015, -48.673773],\n              [-75.18277, -47.711919],\n              [-74.126581, -46.939253],\n              [-75.644395, -46.647643],\n              [-74.692154, -45.763976],\n              [-74.351709, -44.103044],\n              [-73.240356, -44.454961],\n              [-72.717804, -42.383356],\n              [-73.3889, -42.117532],\n              [-73.701336, -43.365776],\n              [-74.331943, -43.224958],\n              [-74.017957, -41.794813],\n              [-73.677099, -39.942213],\n              [-73.217593, -39.258689],\n              [-73.505559, -38.282883],\n              [-73.588061, -37.156285],\n              [-73.166717, -37.12378],\n              [-72.553137, -35.50884],\n              [-71.861732, -33.909093],\n              [-71.43845, -32.418899],\n              [-71.668721, -30.920645],\n              [-71.370083, -30.095682],\n              [-71.489894, -28.861442],\n              [-70.905124, -27.64038],\n              [-70.724954, -25.705924],\n              [-70.403966, -23.628997],\n              [-70.091246, -21.393319],\n              [-70.16442, -19.756468],\n              [-70.372572, -18.347975],\n              [-69.858444, -18.092694],\n              [-69.590424, -17.580012],\n              [-69.100247, -18.260125],\n              [-68.966818, -18.981683],\n              [-68.442225, -19.405068],\n              [-68.757167, -20.372658],\n              [-68.219913, -21.494347],\n              [-67.82818, -22.872919],\n              [-67.106674, -22.735925]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"China\",\n        \"SOV_A3\": \"CH1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"China\",\n        \"ADM0_A3\": \"CHN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"China\",\n        \"GU_A3\": \"CHN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"China\",\n        \"SU_A3\": \"CHN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"China\",\n        \"NAME_LONG\": \"China\",\n        \"BRK_A3\": \"CHN\",\n        \"BRK_NAME\": \"China\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"China\",\n        \"POSTAL\": \"CN\",\n        \"FORMAL_EN\": \"People's Republic of China\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"China\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"China\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 1379302771,\n        \"POP_RANK\": 18,\n        \"GDP_MD_EST\": 21140000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"3. Emerging region: BRIC\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CH\",\n        \"ISO_A2\": \"CN\",\n        \"ISO_A3\": \"CHN\",\n        \"ISO_A3_EH\": \"CHN\",\n        \"ISO_N3\": \"156\",\n        \"UN_A3\": \"156\",\n        \"WB_A2\": \"CN\",\n        \"WB_A3\": \"CHN\",\n        \"WOE_ID\": 23424781,\n        \"WOE_ID_EH\": 23424781,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CHN\",\n        \"ADM0_A3_US\": \"CHN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 5.7\n      },\n      \"bbox\": [73.675379, 18.197701, 135.026311, 53.4588],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [75.158028, 37.133031],\n              [74.980002, 37.41999],\n              [74.829986, 37.990007],\n              [74.864816, 38.378846],\n              [74.257514, 38.606507],\n              [73.928852, 38.505815],\n              [73.675379, 39.431237],\n              [73.960013, 39.660008],\n              [73.822244, 39.893973],\n              [74.776862, 40.366425],\n              [75.467828, 40.562072],\n              [76.526368, 40.427946],\n              [76.904484, 41.066486],\n              [78.187197, 41.185316],\n              [78.543661, 41.582243],\n              [80.11943, 42.123941],\n              [80.25999, 42.349999],\n              [80.18015, 42.920068],\n              [80.866206, 43.180362],\n              [79.966106, 44.917517],\n              [81.947071, 45.317027],\n              [82.458926, 45.53965],\n              [83.180484, 47.330031],\n              [85.16429, 47.000956],\n              [85.720484, 47.452969],\n              [85.768233, 48.455751],\n              [86.598776, 48.549182],\n              [87.35997, 49.214981],\n              [87.751264, 49.297198],\n              [88.013832, 48.599463],\n              [88.854298, 48.069082],\n              [90.280826, 47.693549],\n              [90.970809, 46.888146],\n              [90.585768, 45.719716],\n              [90.94554, 45.286073],\n              [92.133891, 45.115076],\n              [93.480734, 44.975472],\n              [94.688929, 44.352332],\n              [95.306875, 44.241331],\n              [95.762455, 43.319449],\n              [96.349396, 42.725635],\n              [97.451757, 42.74889],\n              [99.515817, 42.524691],\n              [100.845866, 42.663804],\n              [101.83304, 42.514873],\n              [103.312278, 41.907468],\n              [104.522282, 41.908347],\n              [104.964994, 41.59741],\n              [106.129316, 42.134328],\n              [107.744773, 42.481516],\n              [109.243596, 42.519446],\n              [110.412103, 42.871234],\n              [111.129682, 43.406834],\n              [111.829588, 43.743118],\n              [111.667737, 44.073176],\n              [111.348377, 44.457442],\n              [111.873306, 45.102079],\n              [112.436062, 45.011646],\n              [113.463907, 44.808893],\n              [114.460332, 45.339817],\n              [115.985096, 45.727235],\n              [116.717868, 46.388202],\n              [117.421701, 46.672733],\n              [118.874326, 46.805412],\n              [119.66327, 46.69268],\n              [119.772824, 47.048059],\n              [118.866574, 47.74706],\n              [118.064143, 48.06673],\n              [117.295507, 47.697709],\n              [116.308953, 47.85341],\n              [115.742837, 47.726545],\n              [115.485282, 48.135383],\n              [116.191802, 49.134598],\n              [116.678801, 49.888531],\n              [117.879244, 49.510983],\n              [119.288461, 50.142883],\n              [119.27939, 50.58292],\n              [120.18208, 51.64355],\n              [120.7382, 51.96411],\n              [120.725789, 52.516226],\n              [120.177089, 52.753886],\n              [121.003085, 53.251401],\n              [122.245748, 53.431726],\n              [123.57147, 53.4588],\n              [125.068211, 53.161045],\n              [125.946349, 52.792799],\n              [126.564399, 51.784255],\n              [126.939157, 51.353894],\n              [127.287456, 50.739797],\n              [127.6574, 49.76027],\n              [129.397818, 49.4406],\n              [130.582293, 48.729687],\n              [130.98726, 47.79013],\n              [132.50669, 47.78896],\n              [133.373596, 48.183442],\n              [135.026311, 48.47823],\n              [134.50081, 47.57845],\n              [134.11235, 47.21248],\n              [133.769644, 46.116927],\n              [133.09712, 45.14409],\n              [131.883454, 45.321162],\n              [131.02519, 44.96796],\n              [131.288555, 44.11152],\n              [131.144688, 42.92999],\n              [130.633866, 42.903015],\n              [130.64, 42.395024],\n              [129.994267, 42.985387],\n              [129.596669, 42.424982],\n              [128.052215, 41.994285],\n              [128.208433, 41.466772],\n              [127.343783, 41.503152],\n              [126.869083, 41.816569],\n              [126.182045, 41.107336],\n              [125.079942, 40.569824],\n              [124.265625, 39.928493],\n              [122.86757, 39.637788],\n              [122.131388, 39.170452],\n              [121.054554, 38.897471],\n              [121.585995, 39.360854],\n              [121.376757, 39.750261],\n              [122.168595, 40.422443],\n              [121.640359, 40.94639],\n              [120.768629, 40.593388],\n              [119.639602, 39.898056],\n              [119.023464, 39.252333],\n              [118.042749, 39.204274],\n              [117.532702, 38.737636],\n              [118.059699, 38.061476],\n              [118.87815, 37.897325],\n              [118.911636, 37.448464],\n              [119.702802, 37.156389],\n              [120.823457, 37.870428],\n              [121.711259, 37.481123],\n              [122.357937, 37.454484],\n              [122.519995, 36.930614],\n              [121.104164, 36.651329],\n              [120.637009, 36.11144],\n              [119.664562, 35.609791],\n              [119.151208, 34.909859],\n              [120.227525, 34.360332],\n              [120.620369, 33.376723],\n              [121.229014, 32.460319],\n              [121.908146, 31.692174],\n              [121.891919, 30.949352],\n              [121.264257, 30.676267],\n              [121.503519, 30.142915],\n              [122.092114, 29.83252],\n              [121.938428, 29.018022],\n              [121.684439, 28.225513],\n              [121.125661, 28.135673],\n              [120.395473, 27.053207],\n              [119.585497, 25.740781],\n              [118.656871, 24.547391],\n              [117.281606, 23.624501],\n              [115.890735, 22.782873],\n              [114.763827, 22.668074],\n              [114.152547, 22.22376],\n              [113.80678, 22.54834],\n              [113.241078, 22.051367],\n              [111.843592, 21.550494],\n              [110.785466, 21.397144],\n              [110.444039, 20.341033],\n              [109.889861, 20.282457],\n              [109.627655, 21.008227],\n              [109.864488, 21.395051],\n              [108.522813, 21.715212],\n              [108.05018, 21.55238],\n              [107.04342, 21.811899],\n              [106.567273, 22.218205],\n              [106.725403, 22.794268],\n              [105.811247, 22.976892],\n              [105.329209, 23.352063],\n              [104.476858, 22.81915],\n              [103.504515, 22.703757],\n              [102.706992, 22.708795],\n              [102.170436, 22.464753],\n              [101.652018, 22.318199],\n              [101.80312, 21.174367],\n              [101.270026, 21.201652],\n              [101.180005, 21.436573],\n              [101.150033, 21.849984],\n              [100.416538, 21.558839],\n              [99.983489, 21.742937],\n              [99.240899, 22.118314],\n              [99.531992, 22.949039],\n              [98.898749, 23.142722],\n              [98.660262, 24.063286],\n              [97.60472, 23.897405],\n              [97.724609, 25.083637],\n              [98.671838, 25.918703],\n              [98.712094, 26.743536],\n              [98.68269, 27.508812],\n              [98.246231, 27.747221],\n              [97.911988, 28.335945],\n              [97.327114, 28.261583],\n              [96.248833, 28.411031],\n              [96.586591, 28.83098],\n              [96.117679, 29.452802],\n              [95.404802, 29.031717],\n              [94.56599, 29.277438],\n              [93.413348, 28.640629],\n              [92.503119, 27.896876],\n              [91.696657, 27.771742],\n              [91.258854, 28.040614],\n              [90.730514, 28.064954],\n              [90.015829, 28.296439],\n              [89.47581, 28.042759],\n              [88.814248, 27.299316],\n              [88.730326, 28.086865],\n              [88.120441, 27.876542],\n              [86.954517, 27.974262],\n              [85.82332, 28.203576],\n              [85.011638, 28.642774],\n              [84.23458, 28.839894],\n              [83.898993, 29.320226],\n              [83.337115, 29.463732],\n              [82.327513, 30.115268],\n              [81.525804, 30.422717],\n              [81.111256, 30.183481],\n              [79.721367, 30.882715],\n              [78.738894, 31.515906],\n              [78.458446, 32.618164],\n              [79.176129, 32.48378],\n              [79.208892, 32.994395],\n              [78.811086, 33.506198],\n              [78.912269, 34.321936],\n              [77.837451, 35.49401],\n              [76.192848, 35.898403],\n              [75.896897, 36.666806],\n              [75.158028, 37.133031]\n            ]\n          ],\n          [\n            [\n              [110.339188, 18.678395],\n              [109.47521, 18.197701],\n              [108.655208, 18.507682],\n              [108.626217, 19.367888],\n              [109.119056, 19.821039],\n              [110.211599, 20.101254],\n              [110.786551, 20.077534],\n              [111.010051, 19.69593],\n              [110.570647, 19.255879],\n              [110.339188, 18.678395]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Ivory Coast\",\n        \"SOV_A3\": \"CIV\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Ivory Coast\",\n        \"ADM0_A3\": \"CIV\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Ivory Coast\",\n        \"GU_A3\": \"CIV\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Ivory Coast\",\n        \"SU_A3\": \"CIV\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Côte d'Ivoire\",\n        \"NAME_LONG\": \"Côte d'Ivoire\",\n        \"BRK_A3\": \"CIV\",\n        \"BRK_NAME\": \"Côte d'Ivoire\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"I.C.\",\n        \"POSTAL\": \"CI\",\n        \"FORMAL_EN\": \"Republic of Ivory Coast\",\n        \"FORMAL_FR\": \"Republic of Cote D'Ivoire\",\n        \"NAME_CIAWF\": \"Cote D'ivoire\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Côte d'Ivoire\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 24184810,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 87120,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1998,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"IV\",\n        \"ISO_A2\": \"CI\",\n        \"ISO_A3\": \"CIV\",\n        \"ISO_A3_EH\": \"CIV\",\n        \"ISO_N3\": \"384\",\n        \"UN_A3\": \"384\",\n        \"WB_A2\": \"CI\",\n        \"WB_A3\": \"CIV\",\n        \"WOE_ID\": 23424854,\n        \"WOE_ID_EH\": 23424854,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CIV\",\n        \"ADM0_A3_US\": \"CIV\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 13,\n        \"LONG_LEN\": 13,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-8.60288, 4.338288, -2.56219, 10.524061],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-5.404342, 10.370737],\n            [-4.954653, 10.152714],\n            [-4.779884, 9.821985],\n            [-4.330247, 9.610835],\n            [-3.980449, 9.862344],\n            [-3.511899, 9.900326],\n            [-2.827496, 9.642461],\n            [-2.56219, 8.219628],\n            [-2.983585, 7.379705],\n            [-3.24437, 6.250472],\n            [-2.810701, 5.389051],\n            [-2.856125, 4.994476],\n            [-3.311084, 4.984296],\n            [-4.00882, 5.179813],\n            [-4.649917, 5.168264],\n            [-5.834496, 4.993701],\n            [-6.528769, 4.705088],\n            [-7.518941, 4.338288],\n            [-7.712159, 4.364566],\n            [-7.635368, 5.188159],\n            [-7.539715, 5.313345],\n            [-7.570153, 5.707352],\n            [-7.993693, 6.12619],\n            [-8.311348, 6.193033],\n            [-8.60288, 6.467564],\n            [-8.385452, 6.911801],\n            [-8.485446, 7.395208],\n            [-8.439298, 7.686043],\n            [-8.280703, 7.68718],\n            [-8.221792, 8.123329],\n            [-8.299049, 8.316444],\n            [-8.203499, 8.455453],\n            [-7.8321, 8.575704],\n            [-8.079114, 9.376224],\n            [-8.309616, 9.789532],\n            [-8.229337, 10.12902],\n            [-8.029944, 10.206535],\n            [-7.89959, 10.297382],\n            [-7.622759, 10.147236],\n            [-6.850507, 10.138994],\n            [-6.666461, 10.430811],\n            [-6.493965, 10.411303],\n            [-6.205223, 10.524061],\n            [-6.050452, 10.096361],\n            [-5.816926, 10.222555],\n            [-5.404342, 10.370737]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Cameroon\",\n        \"SOV_A3\": \"CMR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Cameroon\",\n        \"ADM0_A3\": \"CMR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Cameroon\",\n        \"GU_A3\": \"CMR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Cameroon\",\n        \"SU_A3\": \"CMR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Cameroon\",\n        \"NAME_LONG\": \"Cameroon\",\n        \"BRK_A3\": \"CMR\",\n        \"BRK_NAME\": \"Cameroon\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Cam.\",\n        \"POSTAL\": \"CM\",\n        \"FORMAL_EN\": \"Republic of Cameroon\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Cameroon\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Cameroon\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 24994885,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 77240,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2005,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CM\",\n        \"ISO_A2\": \"CM\",\n        \"ISO_A3\": \"CMR\",\n        \"ISO_A3_EH\": \"CMR\",\n        \"ISO_N3\": \"120\",\n        \"UN_A3\": \"120\",\n        \"WB_A2\": \"CM\",\n        \"WB_A3\": \"CMR\",\n        \"WOE_ID\": 23424785,\n        \"WOE_ID_EH\": 23424785,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CMR\",\n        \"ADM0_A3_US\": \"CMR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [8.488816, 1.727673, 16.012852, 12.859396],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [15.27946, 7.421925],\n            [14.776545, 6.408498],\n            [14.53656, 6.226959],\n            [14.459407, 5.451761],\n            [14.558936, 5.030598],\n            [14.478372, 4.732605],\n            [14.950953, 4.210389],\n            [15.03622, 3.851367],\n            [15.405396, 3.335301],\n            [15.862732, 3.013537],\n            [15.907381, 2.557389],\n            [16.012852, 2.26764],\n            [15.940919, 1.727673],\n            [15.146342, 1.964015],\n            [14.337813, 2.227875],\n            [13.075822, 2.267097],\n            [12.951334, 2.321616],\n            [12.35938, 2.192812],\n            [11.751665, 2.326758],\n            [11.276449, 2.261051],\n            [9.649158, 2.283866],\n            [9.795196, 3.073404],\n            [9.404367, 3.734527],\n            [8.948116, 3.904129],\n            [8.744924, 4.352215],\n            [8.488816, 4.495617],\n            [8.500288, 4.771983],\n            [8.757533, 5.479666],\n            [9.233163, 6.444491],\n            [9.522706, 6.453482],\n            [10.118277, 7.03877],\n            [10.497375, 7.055358],\n            [11.058788, 6.644427],\n            [11.745774, 6.981383],\n            [11.839309, 7.397042],\n            [12.063946, 7.799808],\n            [12.218872, 8.305824],\n            [12.753672, 8.717763],\n            [12.955468, 9.417772],\n            [13.1676, 9.640626],\n            [13.308676, 10.160362],\n            [13.57295, 10.798566],\n            [14.415379, 11.572369],\n            [14.468192, 11.904752],\n            [14.577178, 12.085361],\n            [14.181336, 12.483657],\n            [14.213531, 12.802035],\n            [14.495787, 12.859396],\n            [14.89336, 12.21905],\n            [14.960152, 11.555574],\n            [14.923565, 10.891325],\n            [15.467873, 9.982337],\n            [14.909354, 9.992129],\n            [14.627201, 9.920919],\n            [14.171466, 10.021378],\n            [13.954218, 9.549495],\n            [14.544467, 8.965861],\n            [14.979996, 8.796104],\n            [15.120866, 8.38215],\n            [15.436092, 7.692812],\n            [15.27946, 7.421925]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Democratic Republic of the Congo\",\n        \"SOV_A3\": \"COD\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Democratic Republic of the Congo\",\n        \"ADM0_A3\": \"COD\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Democratic Republic of the Congo\",\n        \"GU_A3\": \"COD\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Democratic Republic of the Congo\",\n        \"SU_A3\": \"COD\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Dem. Rep. Congo\",\n        \"NAME_LONG\": \"Democratic Republic of the Congo\",\n        \"BRK_A3\": \"COD\",\n        \"BRK_NAME\": \"Democratic Republic of the Congo\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"D.R.C.\",\n        \"POSTAL\": \"DRC\",\n        \"FORMAL_EN\": \"Democratic Republic of the Congo\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Congo, Democratic Republic of the\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Congo, Dem. Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 83301151,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 66010,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1984,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CG\",\n        \"ISO_A2\": \"CD\",\n        \"ISO_A3\": \"COD\",\n        \"ISO_A3_EH\": \"COD\",\n        \"ISO_N3\": \"180\",\n        \"UN_A3\": \"180\",\n        \"WB_A2\": \"ZR\",\n        \"WB_A3\": \"ZAR\",\n        \"WOE_ID\": 23424780,\n        \"WOE_ID_EH\": 23424780,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"COD\",\n        \"ADM0_A3_US\": \"COD\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 15,\n        \"LONG_LEN\": 32,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [12.182337, -13.257227, 31.174149, 5.256088],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [23.912215, -10.926826],\n            [23.456791, -10.867863],\n            [22.837345, -11.017622],\n            [22.402798, -10.993075],\n            [22.155268, -11.084801],\n            [22.208753, -9.894796],\n            [21.875182, -9.523708],\n            [21.801801, -8.908707],\n            [21.949131, -8.305901],\n            [21.746456, -7.920085],\n            [21.728111, -7.290872],\n            [20.514748, -7.299606],\n            [20.601823, -6.939318],\n            [20.091622, -6.94309],\n            [20.037723, -7.116361],\n            [19.417502, -7.155429],\n            [19.166613, -7.738184],\n            [19.016752, -7.988246],\n            [18.464176, -7.847014],\n            [18.134222, -7.987678],\n            [17.47297, -8.068551],\n            [17.089996, -7.545689],\n            [16.860191, -7.222298],\n            [16.57318, -6.622645],\n            [16.326528, -5.87747],\n            [13.375597, -5.864241],\n            [13.024869, -5.984389],\n            [12.735171, -5.965682],\n            [12.322432, -6.100092],\n            [12.182337, -5.789931],\n            [12.436688, -5.684304],\n            [12.468004, -5.248362],\n            [12.631612, -4.991271],\n            [12.995517, -4.781103],\n            [13.25824, -4.882957],\n            [13.600235, -4.500138],\n            [14.144956, -4.510009],\n            [14.209035, -4.793092],\n            [14.582604, -4.970239],\n            [15.170992, -4.343507],\n            [15.75354, -3.855165],\n            [16.00629, -3.535133],\n            [15.972803, -2.712392],\n            [16.407092, -1.740927],\n            [16.865307, -1.225816],\n            [17.523716, -0.74383],\n            [17.638645, -0.424832],\n            [17.663553, -0.058084],\n            [17.82654, 0.288923],\n            [17.774192, 0.855659],\n            [17.898835, 1.741832],\n            [18.094276, 2.365722],\n            [18.393792, 2.900443],\n            [18.453065, 3.504386],\n            [18.542982, 4.201785],\n            [18.932312, 4.709506],\n            [19.467784, 5.031528],\n            [20.290679, 4.691678],\n            [20.927591, 4.322786],\n            [21.659123, 4.224342],\n            [22.405124, 4.02916],\n            [22.704124, 4.633051],\n            [22.84148, 4.710126],\n            [23.297214, 4.609693],\n            [24.410531, 5.108784],\n            [24.805029, 4.897247],\n            [25.128833, 4.927245],\n            [25.278798, 5.170408],\n            [25.650455, 5.256088],\n            [26.402761, 5.150875],\n            [27.044065, 5.127853],\n            [27.374226, 5.233944],\n            [27.979977, 4.408413],\n            [28.428994, 4.287155],\n            [28.696678, 4.455077],\n            [29.159078, 4.389267],\n            [29.715995, 4.600805],\n            [29.9535, 4.173699],\n            [30.833852, 3.509172],\n            [30.83386, 3.509166],\n            [30.773347, 2.339883],\n            [31.174149, 2.204465],\n            [30.85267, 1.849396],\n            [30.468508, 1.583805],\n            [30.086154, 1.062313],\n            [29.875779, 0.59738],\n            [29.819503, -0.20531],\n            [29.587838, -0.587406],\n            [29.579466, -1.341313],\n            [29.291887, -1.620056],\n            [29.254835, -2.21511],\n            [29.117479, -2.292211],\n            [29.024926, -2.839258],\n            [29.276384, -3.293907],\n            [29.339998, -4.499983],\n            [29.519987, -5.419979],\n            [29.419993, -5.939999],\n            [29.620032, -6.520015],\n            [30.199997, -7.079981],\n            [30.740015, -8.340007],\n            [30.74001, -8.340006],\n            [30.346086, -8.238257],\n            [29.002912, -8.407032],\n            [28.734867, -8.526559],\n            [28.449871, -9.164918],\n            [28.673682, -9.605925],\n            [28.49607, -10.789884],\n            [28.372253, -11.793647],\n            [28.642417, -11.971569],\n            [29.341548, -12.360744],\n            [29.616001, -12.178895],\n            [29.699614, -13.257227],\n            [28.934286, -13.248958],\n            [28.523562, -12.698604],\n            [28.155109, -12.272481],\n            [27.388799, -12.132747],\n            [27.16442, -11.608748],\n            [26.553088, -11.92444],\n            [25.75231, -11.784965],\n            [25.418118, -11.330936],\n            [24.78317, -11.238694],\n            [24.314516, -11.262826],\n            [24.257155, -10.951993],\n            [23.912215, -10.926826]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Republic of the Congo\",\n        \"SOV_A3\": \"COG\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Republic of the Congo\",\n        \"ADM0_A3\": \"COG\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Republic of the Congo\",\n        \"GU_A3\": \"COG\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Republic of the Congo\",\n        \"SU_A3\": \"COG\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Congo\",\n        \"NAME_LONG\": \"Republic of the Congo\",\n        \"BRK_A3\": \"COG\",\n        \"BRK_NAME\": \"Republic of the Congo\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Rep. Congo\",\n        \"POSTAL\": \"CG\",\n        \"FORMAL_EN\": \"Republic of the Congo\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Congo, Republic of the\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Congo, Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 4954674,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 30270,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CF\",\n        \"ISO_A2\": \"CG\",\n        \"ISO_A3\": \"COG\",\n        \"ISO_A3_EH\": \"COG\",\n        \"ISO_N3\": \"178\",\n        \"UN_A3\": \"178\",\n        \"WB_A2\": \"CG\",\n        \"WB_A3\": \"COG\",\n        \"WOE_ID\": 23424779,\n        \"WOE_ID_EH\": 23424779,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"COG\",\n        \"ADM0_A3_US\": \"COG\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 21,\n        \"ABBREV_LEN\": 10,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [11.093773, -5.037987, 18.453065, 3.728197],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [12.995517, -4.781103],\n            [12.62076, -4.438023],\n            [12.318608, -4.60623],\n            [11.914963, -5.037987],\n            [11.093773, -3.978827],\n            [11.855122, -3.426871],\n            [11.478039, -2.765619],\n            [11.820964, -2.514161],\n            [12.495703, -2.391688],\n            [12.575284, -1.948511],\n            [13.109619, -2.42874],\n            [13.992407, -2.470805],\n            [14.29921, -1.998276],\n            [14.425456, -1.333407],\n            [14.316418, -0.552627],\n            [13.843321, 0.038758],\n            [14.276266, 1.19693],\n            [14.026669, 1.395677],\n            [13.282631, 1.314184],\n            [13.003114, 1.830896],\n            [13.075822, 2.267097],\n            [14.337813, 2.227875],\n            [15.146342, 1.964015],\n            [15.940919, 1.727673],\n            [16.012852, 2.26764],\n            [16.537058, 3.198255],\n            [17.133042, 3.728197],\n            [17.8099, 3.560196],\n            [18.453065, 3.504386],\n            [18.393792, 2.900443],\n            [18.094276, 2.365722],\n            [17.898835, 1.741832],\n            [17.774192, 0.855659],\n            [17.82654, 0.288923],\n            [17.663553, -0.058084],\n            [17.638645, -0.424832],\n            [17.523716, -0.74383],\n            [16.865307, -1.225816],\n            [16.407092, -1.740927],\n            [15.972803, -2.712392],\n            [16.00629, -3.535133],\n            [15.75354, -3.855165],\n            [15.170992, -4.343507],\n            [14.582604, -4.970239],\n            [14.209035, -4.793092],\n            [14.144956, -4.510009],\n            [13.600235, -4.500138],\n            [13.25824, -4.882957],\n            [12.995517, -4.781103]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Colombia\",\n        \"SOV_A3\": \"COL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Colombia\",\n        \"ADM0_A3\": \"COL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Colombia\",\n        \"GU_A3\": \"COL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Colombia\",\n        \"SU_A3\": \"COL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Colombia\",\n        \"NAME_LONG\": \"Colombia\",\n        \"BRK_A3\": \"COL\",\n        \"BRK_NAME\": \"Colombia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Col.\",\n        \"POSTAL\": \"CO\",\n        \"FORMAL_EN\": \"Republic of Colombia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Colombia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Colombia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 47698524,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 688000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CO\",\n        \"ISO_A2\": \"CO\",\n        \"ISO_A3\": \"COL\",\n        \"ISO_A3_EH\": \"COL\",\n        \"ISO_N3\": \"170\",\n        \"UN_A3\": \"170\",\n        \"WB_A2\": \"CO\",\n        \"WB_A3\": \"COL\",\n        \"WOE_ID\": 23424787,\n        \"WOE_ID_EH\": 23424787,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"COL\",\n        \"ADM0_A3_US\": \"COL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-78.990935, -4.298187, -66.876326, 12.437303],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-66.876326, 1.253361],\n            [-67.065048, 1.130112],\n            [-67.259998, 1.719999],\n            [-67.53781, 2.037163],\n            [-67.868565, 1.692455],\n            [-69.816973, 1.714805],\n            [-69.804597, 1.089081],\n            [-69.218638, 0.985677],\n            [-69.252434, 0.602651],\n            [-69.452396, 0.706159],\n            [-70.015566, 0.541414],\n            [-70.020656, -0.185156],\n            [-69.577065, -0.549992],\n            [-69.420486, -1.122619],\n            [-69.444102, -1.556287],\n            [-69.893635, -4.298187],\n            [-70.394044, -3.766591],\n            [-70.692682, -3.742872],\n            [-70.047709, -2.725156],\n            [-70.813476, -2.256865],\n            [-71.413646, -2.342802],\n            [-71.774761, -2.16979],\n            [-72.325787, -2.434218],\n            [-73.070392, -2.308954],\n            [-73.659504, -1.260491],\n            [-74.122395, -1.002833],\n            [-74.441601, -0.53082],\n            [-75.106625, -0.057205],\n            [-75.373223, -0.152032],\n            [-75.801466, 0.084801],\n            [-76.292314, 0.416047],\n            [-76.57638, 0.256936],\n            [-77.424984, 0.395687],\n            [-77.668613, 0.825893],\n            [-77.855061, 0.809925],\n            [-78.855259, 1.380924],\n            [-78.990935, 1.69137],\n            [-78.617831, 1.766404],\n            [-78.662118, 2.267355],\n            [-78.42761, 2.629556],\n            [-77.931543, 2.696606],\n            [-77.510431, 3.325017],\n            [-77.12769, 3.849636],\n            [-77.496272, 4.087606],\n            [-77.307601, 4.667984],\n            [-77.533221, 5.582812],\n            [-77.318815, 5.845354],\n            [-77.476661, 6.691116],\n            [-77.881571, 7.223771],\n            [-77.753414, 7.70984],\n            [-77.431108, 7.638061],\n            [-77.242566, 7.935278],\n            [-77.474723, 8.524286],\n            [-77.353361, 8.670505],\n            [-76.836674, 8.638749],\n            [-76.086384, 9.336821],\n            [-75.6746, 9.443248],\n            [-75.664704, 9.774003],\n            [-75.480426, 10.61899],\n            [-74.906895, 11.083045],\n            [-74.276753, 11.102036],\n            [-74.197223, 11.310473],\n            [-73.414764, 11.227015],\n            [-72.627835, 11.731972],\n            [-72.238195, 11.95555],\n            [-71.75409, 12.437303],\n            [-71.399822, 12.376041],\n            [-71.137461, 12.112982],\n            [-71.331584, 11.776284],\n            [-71.973922, 11.608672],\n            [-72.227575, 11.108702],\n            [-72.614658, 10.821975],\n            [-72.905286, 10.450344],\n            [-73.027604, 9.73677],\n            [-73.304952, 9.152],\n            [-72.78873, 9.085027],\n            [-72.660495, 8.625288],\n            [-72.439862, 8.405275],\n            [-72.360901, 8.002638],\n            [-72.479679, 7.632506],\n            [-72.444487, 7.423785],\n            [-72.198352, 7.340431],\n            [-71.960176, 6.991615],\n            [-70.674234, 7.087785],\n            [-70.093313, 6.960376],\n            [-69.38948, 6.099861],\n            [-68.985319, 6.206805],\n            [-68.265052, 6.153268],\n            [-67.695087, 6.267318],\n            [-67.34144, 6.095468],\n            [-67.521532, 5.55687],\n            [-67.744697, 5.221129],\n            [-67.823012, 4.503937],\n            [-67.621836, 3.839482],\n            [-67.337564, 3.542342],\n            [-67.303173, 3.318454],\n            [-67.809938, 2.820655],\n            [-67.447092, 2.600281],\n            [-67.181294, 2.250638],\n            [-66.876326, 1.253361]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Costa Rica\",\n        \"SOV_A3\": \"CRI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Costa Rica\",\n        \"ADM0_A3\": \"CRI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Costa Rica\",\n        \"GU_A3\": \"CRI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Costa Rica\",\n        \"SU_A3\": \"CRI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Costa Rica\",\n        \"NAME_LONG\": \"Costa Rica\",\n        \"BRK_A3\": \"CRI\",\n        \"BRK_NAME\": \"Costa Rica\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"C.R.\",\n        \"POSTAL\": \"CR\",\n        \"FORMAL_EN\": \"Republic of Costa Rica\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Costa Rica\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Costa Rica\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 4930258,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 79260,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CS\",\n        \"ISO_A2\": \"CR\",\n        \"ISO_A3\": \"CRI\",\n        \"ISO_A3_EH\": \"CRI\",\n        \"ISO_N3\": \"188\",\n        \"UN_A3\": \"188\",\n        \"WB_A2\": \"CR\",\n        \"WB_A3\": \"CRI\",\n        \"WOE_ID\": 23424791,\n        \"WOE_ID_EH\": 23424791,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CRI\",\n        \"ADM0_A3_US\": \"CRI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-85.941725, 8.225028, -82.546196, 11.217119],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-82.965783, 8.225028],\n            [-83.508437, 8.446927],\n            [-83.711474, 8.656836],\n            [-83.596313, 8.830443],\n            [-83.632642, 9.051386],\n            [-83.909886, 9.290803],\n            [-84.303402, 9.487354],\n            [-84.647644, 9.615537],\n            [-84.713351, 9.908052],\n            [-84.97566, 10.086723],\n            [-84.911375, 9.795992],\n            [-85.110923, 9.55704],\n            [-85.339488, 9.834542],\n            [-85.660787, 9.933347],\n            [-85.797445, 10.134886],\n            [-85.791709, 10.439337],\n            [-85.659314, 10.754331],\n            [-85.941725, 10.895278],\n            [-85.71254, 11.088445],\n            [-85.561852, 11.217119],\n            [-84.903003, 10.952303],\n            [-84.673069, 11.082657],\n            [-84.355931, 10.999226],\n            [-84.190179, 10.79345],\n            [-83.895054, 10.726839],\n            [-83.655612, 10.938764],\n            [-83.40232, 10.395438],\n            [-83.015677, 9.992982],\n            [-82.546196, 9.566135],\n            [-82.932891, 9.476812],\n            [-82.927155, 9.07433],\n            [-82.719183, 8.925709],\n            [-82.868657, 8.807266],\n            [-82.829771, 8.626295],\n            [-82.913176, 8.423517],\n            [-82.965783, 8.225028]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Cuba\",\n        \"SOV_A3\": \"CUB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Cuba\",\n        \"ADM0_A3\": \"CUB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Cuba\",\n        \"GU_A3\": \"CUB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Cuba\",\n        \"SU_A3\": \"CUB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Cuba\",\n        \"NAME_LONG\": \"Cuba\",\n        \"BRK_A3\": \"CUB\",\n        \"BRK_NAME\": \"Cuba\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Cuba\",\n        \"POSTAL\": \"CU\",\n        \"FORMAL_EN\": \"Republic of Cuba\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Cuba\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Cuba\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 11147407,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 132900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CU\",\n        \"ISO_A2\": \"CU\",\n        \"ISO_A3\": \"CUB\",\n        \"ISO_A3_EH\": \"CUB\",\n        \"ISO_N3\": \"192\",\n        \"UN_A3\": \"192\",\n        \"WB_A2\": \"CU\",\n        \"WB_A3\": \"CUB\",\n        \"WOE_ID\": 23424793,\n        \"WOE_ID_EH\": 23424793,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CUB\",\n        \"ADM0_A3_US\": \"CUB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-84.974911, 19.855481, -74.178025, 23.188611],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-82.268151, 23.188611],\n            [-81.404457, 23.117271],\n            [-80.618769, 23.10598],\n            [-79.679524, 22.765303],\n            [-79.281486, 22.399202],\n            [-78.347434, 22.512166],\n            [-77.993296, 22.277194],\n            [-77.146422, 21.657851],\n            [-76.523825, 21.20682],\n            [-76.19462, 21.220565],\n            [-75.598222, 21.016624],\n            [-75.67106, 20.735091],\n            [-74.933896, 20.693905],\n            [-74.178025, 20.284628],\n            [-74.296648, 20.050379],\n            [-74.961595, 19.923435],\n            [-75.63468, 19.873774],\n            [-76.323656, 19.952891],\n            [-77.755481, 19.855481],\n            [-77.085108, 20.413354],\n            [-77.492655, 20.673105],\n            [-78.137292, 20.739949],\n            [-78.482827, 21.028613],\n            [-78.719867, 21.598114],\n            [-79.285, 21.559175],\n            [-80.217475, 21.827324],\n            [-80.517535, 22.037079],\n            [-81.820943, 22.192057],\n            [-82.169992, 22.387109],\n            [-81.795002, 22.636965],\n            [-82.775898, 22.68815],\n            [-83.494459, 22.168518],\n            [-83.9088, 22.154565],\n            [-84.052151, 21.910575],\n            [-84.54703, 21.801228],\n            [-84.974911, 21.896028],\n            [-84.447062, 22.20495],\n            [-84.230357, 22.565755],\n            [-83.77824, 22.788118],\n            [-83.267548, 22.983042],\n            [-82.510436, 23.078747],\n            [-82.268151, 23.188611]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Northern Cyprus\",\n        \"SOV_A3\": \"CYN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Northern Cyprus\",\n        \"ADM0_A3\": \"CYN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Northern Cyprus\",\n        \"GU_A3\": \"CYN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Northern Cyprus\",\n        \"SU_A3\": \"CYN\",\n        \"BRK_DIFF\": 1,\n        \"NAME\": \"N. Cyprus\",\n        \"NAME_LONG\": \"Northern Cyprus\",\n        \"BRK_A3\": \"B20\",\n        \"BRK_NAME\": \"N. Cyprus\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"N. Cy.\",\n        \"POSTAL\": \"CN\",\n        \"FORMAL_EN\": \"Turkish Republic of Northern Cyprus\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": null,\n        \"NOTE_ADM0\": \"Self admin.\",\n        \"NOTE_BRK\": \"Self admin.; Claimed by Cyprus\",\n        \"NAME_SORT\": \"Cyprus, Northern\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 265100,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 3600,\n        \"POP_YEAR\": 2013,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2013,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"-99\",\n        \"ISO_A2\": \"-99\",\n        \"ISO_A3\": \"-99\",\n        \"ISO_A3_EH\": \"-99\",\n        \"ISO_N3\": \"-99\",\n        \"UN_A3\": \"-099\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424995,\n        \"WOE_NOTE\": \"WOE lists as subunit of united Cyprus\",\n        \"ADM0_A3_IS\": \"CYP\",\n        \"ADM0_A3_US\": \"CYP\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 15,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 6,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [32.73178, 35.000345, 34.576474, 35.671596],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [32.73178, 35.140026],\n            [32.802474, 35.145504],\n            [32.946961, 35.386703],\n            [33.667227, 35.373216],\n            [34.576474, 35.671596],\n            [33.900804, 35.245756],\n            [33.973617, 35.058506],\n            [33.86644, 35.093595],\n            [33.675392, 35.017863],\n            [33.525685, 35.038688],\n            [33.475817, 35.000345],\n            [33.455922, 35.101424],\n            [33.383833, 35.162712],\n            [33.190977, 35.173125],\n            [32.919572, 35.087833],\n            [32.73178, 35.140026]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Cyprus\",\n        \"SOV_A3\": \"CYP\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Cyprus\",\n        \"ADM0_A3\": \"CYP\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Cyprus\",\n        \"GU_A3\": \"CYP\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Cyprus\",\n        \"SU_A3\": \"CYP\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Cyprus\",\n        \"NAME_LONG\": \"Cyprus\",\n        \"BRK_A3\": \"CYP\",\n        \"BRK_NAME\": \"Cyprus\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Cyp.\",\n        \"POSTAL\": \"CY\",\n        \"FORMAL_EN\": \"Republic of Cyprus\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Cyprus\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Cyprus\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 1221549,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 29260,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CY\",\n        \"ISO_A2\": \"CY\",\n        \"ISO_A3\": \"CYP\",\n        \"ISO_A3_EH\": \"CYP\",\n        \"ISO_N3\": \"196\",\n        \"UN_A3\": \"196\",\n        \"WB_A2\": \"CY\",\n        \"WB_A3\": \"CYP\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424994,\n        \"WOE_NOTE\": \"WOE lists as subunit of united Cyprus\",\n        \"ADM0_A3_IS\": \"CYP\",\n        \"ADM0_A3_US\": \"CYP\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9.5\n      },\n      \"bbox\": [32.256667, 34.571869, 34.004881, 35.173125],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [32.73178, 35.140026],\n            [32.919572, 35.087833],\n            [33.190977, 35.173125],\n            [33.383833, 35.162712],\n            [33.455922, 35.101424],\n            [33.475817, 35.000345],\n            [33.525685, 35.038688],\n            [33.675392, 35.017863],\n            [33.86644, 35.093595],\n            [33.973617, 35.058506],\n            [34.004881, 34.978098],\n            [32.979827, 34.571869],\n            [32.490296, 34.701655],\n            [32.256667, 35.103232],\n            [32.73178, 35.140026]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Czechia\",\n        \"SOV_A3\": \"CZE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Czechia\",\n        \"ADM0_A3\": \"CZE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Czechia\",\n        \"GU_A3\": \"CZE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Czechia\",\n        \"SU_A3\": \"CZE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Czechia\",\n        \"NAME_LONG\": \"Czech Republic\",\n        \"BRK_A3\": \"CZE\",\n        \"BRK_NAME\": \"Czechia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Cz.\",\n        \"POSTAL\": \"CZ\",\n        \"FORMAL_EN\": \"Czech Republic\",\n        \"FORMAL_FR\": \"la République tchèque\",\n        \"NAME_CIAWF\": \"Czechia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Czechia\",\n        \"NAME_ALT\": \"Česko\",\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 10674723,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 350900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"EZ\",\n        \"ISO_A2\": \"CZ\",\n        \"ISO_A3\": \"CZE\",\n        \"ISO_A3_EH\": \"CZE\",\n        \"ISO_N3\": \"203\",\n        \"UN_A3\": \"203\",\n        \"WB_A2\": \"CZ\",\n        \"WB_A3\": \"CZE\",\n        \"WOE_ID\": 23424810,\n        \"WOE_ID_EH\": 23424810,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"CZE\",\n        \"ADM0_A3_US\": \"CZE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 14,\n        \"ABBREV_LEN\": 3,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [12.240111, 48.555305, 18.853144, 51.117268],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [16.960288, 48.596982],\n            [16.499283, 48.785808],\n            [16.029647, 48.733899],\n            [15.253416, 49.039074],\n            [14.901447, 48.964402],\n            [14.338898, 48.555305],\n            [13.595946, 48.877172],\n            [13.031329, 49.307068],\n            [12.521024, 49.547415],\n            [12.415191, 49.969121],\n            [12.240111, 50.266338],\n            [12.966837, 50.484076],\n            [13.338132, 50.733234],\n            [14.056228, 50.926918],\n            [14.307013, 51.117268],\n            [14.570718, 51.002339],\n            [15.016996, 51.106674],\n            [15.490972, 50.78473],\n            [16.238627, 50.697733],\n            [16.176253, 50.422607],\n            [16.719476, 50.215747],\n            [16.868769, 50.473974],\n            [17.554567, 50.362146],\n            [17.649445, 50.049038],\n            [18.392914, 49.988629],\n            [18.853144, 49.49623],\n            [18.554971, 49.495015],\n            [18.399994, 49.315001],\n            [18.170498, 49.271515],\n            [18.104973, 49.043983],\n            [17.913512, 48.996493],\n            [17.886485, 48.903475],\n            [17.545007, 48.800019],\n            [17.101985, 48.816969],\n            [16.960288, 48.596982]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Germany\",\n        \"SOV_A3\": \"DEU\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Germany\",\n        \"ADM0_A3\": \"DEU\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Germany\",\n        \"GU_A3\": \"DEU\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Germany\",\n        \"SU_A3\": \"DEU\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Germany\",\n        \"NAME_LONG\": \"Germany\",\n        \"BRK_A3\": \"DEU\",\n        \"BRK_NAME\": \"Germany\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ger.\",\n        \"POSTAL\": \"D\",\n        \"FORMAL_EN\": \"Federal Republic of Germany\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Germany\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Germany\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 80594017,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 3979000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GM\",\n        \"ISO_A2\": \"DE\",\n        \"ISO_A3\": \"DEU\",\n        \"ISO_A3_EH\": \"DEU\",\n        \"ISO_N3\": \"276\",\n        \"UN_A3\": \"276\",\n        \"WB_A2\": \"DE\",\n        \"WB_A3\": \"DEU\",\n        \"WOE_ID\": 23424829,\n        \"WOE_ID_EH\": 23424829,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"DEU\",\n        \"ADM0_A3_US\": \"DEU\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [5.988658, 47.302488, 15.016996, 54.983104],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [13.595946, 48.877172],\n            [13.243357, 48.416115],\n            [12.884103, 48.289146],\n            [13.025851, 47.637584],\n            [12.932627, 47.467646],\n            [12.62076, 47.672388],\n            [12.141357, 47.703083],\n            [11.426414, 47.523766],\n            [10.544504, 47.566399],\n            [10.402084, 47.302488],\n            [9.896068, 47.580197],\n            [9.594226, 47.525058],\n            [8.522612, 47.830828],\n            [8.317301, 47.61358],\n            [7.466759, 47.620582],\n            [7.593676, 48.333019],\n            [8.099279, 49.017784],\n            [6.65823, 49.201958],\n            [6.18632, 49.463803],\n            [6.242751, 49.902226],\n            [6.043073, 50.128052],\n            [6.156658, 50.803721],\n            [5.988658, 51.851616],\n            [6.589397, 51.852029],\n            [6.84287, 52.22844],\n            [7.092053, 53.144043],\n            [6.90514, 53.482162],\n            [7.100425, 53.693932],\n            [7.936239, 53.748296],\n            [8.121706, 53.527792],\n            [8.800734, 54.020786],\n            [8.572118, 54.395646],\n            [8.526229, 54.962744],\n            [9.282049, 54.830865],\n            [9.921906, 54.983104],\n            [9.93958, 54.596642],\n            [10.950112, 54.363607],\n            [10.939467, 54.008693],\n            [11.956252, 54.196486],\n            [12.51844, 54.470371],\n            [13.647467, 54.075511],\n            [14.119686, 53.757029],\n            [14.353315, 53.248171],\n            [14.074521, 52.981263],\n            [14.4376, 52.62485],\n            [14.685026, 52.089947],\n            [14.607098, 51.745188],\n            [15.016996, 51.106674],\n            [14.570718, 51.002339],\n            [14.307013, 51.117268],\n            [14.056228, 50.926918],\n            [13.338132, 50.733234],\n            [12.966837, 50.484076],\n            [12.240111, 50.266338],\n            [12.415191, 49.969121],\n            [12.521024, 49.547415],\n            [13.031329, 49.307068],\n            [13.595946, 48.877172]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Djibouti\",\n        \"SOV_A3\": \"DJI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Djibouti\",\n        \"ADM0_A3\": \"DJI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Djibouti\",\n        \"GU_A3\": \"DJI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Djibouti\",\n        \"SU_A3\": \"DJI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Djibouti\",\n        \"NAME_LONG\": \"Djibouti\",\n        \"BRK_A3\": \"DJI\",\n        \"BRK_NAME\": \"Djibouti\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Dji.\",\n        \"POSTAL\": \"DJ\",\n        \"FORMAL_EN\": \"Republic of Djibouti\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Djibouti\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Djibouti\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 865267,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 3345,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"DJ\",\n        \"ISO_A2\": \"DJ\",\n        \"ISO_A3\": \"DJI\",\n        \"ISO_A3_EH\": \"DJI\",\n        \"ISO_N3\": \"262\",\n        \"UN_A3\": \"262\",\n        \"WB_A2\": \"DJ\",\n        \"WB_A3\": \"DJI\",\n        \"WOE_ID\": 23424797,\n        \"WOE_ID_EH\": 23424797,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"DJI\",\n        \"ADM0_A3_US\": \"DJI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [41.66176, 10.926879, 43.317852, 12.699639],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [43.081226, 12.699639],\n            [43.317852, 12.390148],\n            [43.286381, 11.974928],\n            [42.715874, 11.735641],\n            [43.145305, 11.46204],\n            [42.776852, 10.926879],\n            [42.55493, 11.10511],\n            [42.31414, 11.0342],\n            [41.75557, 11.05091],\n            [41.73959, 11.35511],\n            [41.66176, 11.6312],\n            [42, 12.1],\n            [42.35156, 12.54223],\n            [42.779642, 12.455416],\n            [43.081226, 12.699639]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Denmark\",\n        \"SOV_A3\": \"DN1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"Denmark\",\n        \"ADM0_A3\": \"DNK\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Denmark\",\n        \"GU_A3\": \"DNK\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Denmark\",\n        \"SU_A3\": \"DNK\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Denmark\",\n        \"NAME_LONG\": \"Denmark\",\n        \"BRK_A3\": \"DNK\",\n        \"BRK_NAME\": \"Denmark\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Den.\",\n        \"POSTAL\": \"DK\",\n        \"FORMAL_EN\": \"Kingdom of Denmark\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Denmark\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Denmark\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 5605948,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 264800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"DA\",\n        \"ISO_A2\": \"DK\",\n        \"ISO_A3\": \"DNK\",\n        \"ISO_A3_EH\": \"DNK\",\n        \"ISO_N3\": \"208\",\n        \"UN_A3\": \"208\",\n        \"WB_A2\": \"DK\",\n        \"WB_A3\": \"DNK\",\n        \"WOE_ID\": 23424796,\n        \"WOE_ID_EH\": 23424796,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"DNK\",\n        \"ADM0_A3_US\": \"DNK\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [8.089977, 54.800015, 12.690006, 57.730017],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [9.921906, 54.983104],\n              [9.282049, 54.830865],\n              [8.526229, 54.962744],\n              [8.120311, 55.517723],\n              [8.089977, 56.540012],\n              [8.256582, 56.809969],\n              [8.543438, 57.110003],\n              [9.424469, 57.172066],\n              [9.775559, 57.447941],\n              [10.580006, 57.730017],\n              [10.546106, 57.215733],\n              [10.25, 56.890016],\n              [10.369993, 56.609982],\n              [10.912182, 56.458621],\n              [10.667804, 56.081383],\n              [10.369993, 56.190007],\n              [9.649985, 55.469999],\n              [9.921906, 54.983104]\n            ]\n          ],\n          [\n            [\n              [12.690006, 55.609991],\n              [12.089991, 54.800015],\n              [11.043543, 55.364864],\n              [10.903914, 55.779955],\n              [12.370904, 56.111407],\n              [12.690006, 55.609991]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Dominican Republic\",\n        \"SOV_A3\": \"DOM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Dominican Republic\",\n        \"ADM0_A3\": \"DOM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Dominican Republic\",\n        \"GU_A3\": \"DOM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Dominican Republic\",\n        \"SU_A3\": \"DOM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Dominican Rep.\",\n        \"NAME_LONG\": \"Dominican Republic\",\n        \"BRK_A3\": \"DOM\",\n        \"BRK_NAME\": \"Dominican Rep.\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Dom. Rep.\",\n        \"POSTAL\": \"DO\",\n        \"FORMAL_EN\": \"Dominican Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Dominican Republic\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Dominican Republic\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 10734247,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 161900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"DR\",\n        \"ISO_A2\": \"DO\",\n        \"ISO_A3\": \"DOM\",\n        \"ISO_A3_EH\": \"DOM\",\n        \"ISO_N3\": \"214\",\n        \"UN_A3\": \"214\",\n        \"WB_A2\": \"DO\",\n        \"WB_A3\": \"DOM\",\n        \"WOE_ID\": 23424800,\n        \"WOE_ID_EH\": 23424800,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"DOM\",\n        \"ADM0_A3_US\": \"DOM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 14,\n        \"LONG_LEN\": 18,\n        \"ABBREV_LEN\": 9,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9.5\n      },\n      \"bbox\": [-71.945112, 17.598564, -68.317943, 19.884911],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-71.712361, 19.714456],\n            [-71.587304, 19.884911],\n            [-70.806706, 19.880286],\n            [-70.214365, 19.622885],\n            [-69.950815, 19.648],\n            [-69.76925, 19.293267],\n            [-69.222126, 19.313214],\n            [-69.254346, 19.015196],\n            [-68.809412, 18.979074],\n            [-68.317943, 18.612198],\n            [-68.689316, 18.205142],\n            [-69.164946, 18.422648],\n            [-69.623988, 18.380713],\n            [-69.952934, 18.428307],\n            [-70.133233, 18.245915],\n            [-70.517137, 18.184291],\n            [-70.669298, 18.426886],\n            [-70.99995, 18.283329],\n            [-71.40021, 17.598564],\n            [-71.657662, 17.757573],\n            [-71.708305, 18.044997],\n            [-71.687738, 18.31666],\n            [-71.945112, 18.6169],\n            [-71.701303, 18.785417],\n            [-71.624873, 19.169838],\n            [-71.712361, 19.714456]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Algeria\",\n        \"SOV_A3\": \"DZA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Algeria\",\n        \"ADM0_A3\": \"DZA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Algeria\",\n        \"GU_A3\": \"DZA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Algeria\",\n        \"SU_A3\": \"DZA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Algeria\",\n        \"NAME_LONG\": \"Algeria\",\n        \"BRK_A3\": \"DZA\",\n        \"BRK_NAME\": \"Algeria\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Alg.\",\n        \"POSTAL\": \"DZ\",\n        \"FORMAL_EN\": \"People's Democratic Republic of Algeria\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Algeria\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Algeria\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 40969443,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 609400,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"AG\",\n        \"ISO_A2\": \"DZ\",\n        \"ISO_A3\": \"DZA\",\n        \"ISO_A3_EH\": \"DZA\",\n        \"ISO_N3\": \"012\",\n        \"UN_A3\": \"012\",\n        \"WB_A2\": \"DZ\",\n        \"WB_A3\": \"DZA\",\n        \"WOE_ID\": 23424740,\n        \"WOE_ID_EH\": 23424740,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"DZA\",\n        \"ADM0_A3_US\": \"DZA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-8.6844, 19.057364, 11.999506, 37.118381],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [4.267419, 19.155265],\n            [3.158133, 19.057364],\n            [3.146661, 19.693579],\n            [2.683588, 19.85623],\n            [2.060991, 20.142233],\n            [1.823228, 20.610809],\n            [-1.550055, 22.792666],\n            [-4.923337, 24.974574],\n            [-8.6844, 27.395744],\n            [-8.665124, 27.589479],\n            [-8.66559, 27.656426],\n            [-8.674116, 28.841289],\n            [-7.059228, 29.579228],\n            [-6.060632, 29.7317],\n            [-5.242129, 30.000443],\n            [-4.859646, 30.501188],\n            [-3.690441, 30.896952],\n            [-3.647498, 31.637294],\n            [-3.06898, 31.724498],\n            [-2.616605, 32.094346],\n            [-1.307899, 32.262889],\n            [-1.124551, 32.651522],\n            [-1.388049, 32.864015],\n            [-1.733455, 33.919713],\n            [-1.792986, 34.527919],\n            [-2.169914, 35.168396],\n            [-1.208603, 35.714849],\n            [-0.127454, 35.888662],\n            [0.503877, 36.301273],\n            [1.466919, 36.605647],\n            [3.161699, 36.783905],\n            [4.815758, 36.865037],\n            [5.32012, 36.716519],\n            [6.26182, 37.110655],\n            [7.330385, 37.118381],\n            [7.737078, 36.885708],\n            [8.420964, 36.946427],\n            [8.217824, 36.433177],\n            [8.376368, 35.479876],\n            [8.140981, 34.655146],\n            [7.524482, 34.097376],\n            [7.612642, 33.344115],\n            [8.430473, 32.748337],\n            [8.439103, 32.506285],\n            [9.055603, 32.102692],\n            [9.48214, 30.307556],\n            [9.805634, 29.424638],\n            [9.859998, 28.95999],\n            [9.683885, 28.144174],\n            [9.756128, 27.688259],\n            [9.629056, 27.140953],\n            [9.716286, 26.512206],\n            [9.319411, 26.094325],\n            [9.910693, 25.365455],\n            [9.948261, 24.936954],\n            [10.303847, 24.379313],\n            [10.771364, 24.562532],\n            [11.560669, 24.097909],\n            [11.999506, 23.471668],\n            [8.572893, 21.565661],\n            [5.677566, 19.601207],\n            [4.267419, 19.155265]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Ecuador\",\n        \"SOV_A3\": \"ECU\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Ecuador\",\n        \"ADM0_A3\": \"ECU\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Ecuador\",\n        \"GU_A3\": \"ECU\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Ecuador\",\n        \"SU_A3\": \"ECU\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Ecuador\",\n        \"NAME_LONG\": \"Ecuador\",\n        \"BRK_A3\": \"ECU\",\n        \"BRK_NAME\": \"Ecuador\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ecu.\",\n        \"POSTAL\": \"EC\",\n        \"FORMAL_EN\": \"Republic of Ecuador\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Ecuador\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Ecuador\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 16290913,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 182400,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"EC\",\n        \"ISO_A2\": \"EC\",\n        \"ISO_A3\": \"ECU\",\n        \"ISO_A3_EH\": \"ECU\",\n        \"ISO_N3\": \"218\",\n        \"UN_A3\": \"218\",\n        \"WB_A2\": \"EC\",\n        \"WB_A3\": \"ECU\",\n        \"WOE_ID\": 23424801,\n        \"WOE_ID_EH\": 23424801,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ECU\",\n        \"ADM0_A3_US\": \"ECU\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-80.967765, -4.959129, -75.233723, 1.380924],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-78.855259, 1.380924],\n            [-77.855061, 0.809925],\n            [-77.668613, 0.825893],\n            [-77.424984, 0.395687],\n            [-76.57638, 0.256936],\n            [-76.292314, 0.416047],\n            [-75.801466, 0.084801],\n            [-75.373223, -0.152032],\n            [-75.233723, -0.911417],\n            [-75.544996, -1.56161],\n            [-76.635394, -2.608678],\n            [-77.837905, -3.003021],\n            [-78.450684, -3.873097],\n            [-78.639897, -4.547784],\n            [-79.205289, -4.959129],\n            [-79.624979, -4.454198],\n            [-80.028908, -4.346091],\n            [-80.442242, -4.425724],\n            [-80.469295, -4.059287],\n            [-80.184015, -3.821162],\n            [-80.302561, -3.404856],\n            [-79.770293, -2.657512],\n            [-79.986559, -2.220794],\n            [-80.368784, -2.685159],\n            [-80.967765, -2.246943],\n            [-80.764806, -1.965048],\n            [-80.933659, -1.057455],\n            [-80.58337, -0.906663],\n            [-80.399325, -0.283703],\n            [-80.020898, 0.36034],\n            [-80.09061, 0.768429],\n            [-79.542762, 0.982938],\n            [-78.855259, 1.380924]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Egypt\",\n        \"SOV_A3\": \"EGY\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Egypt\",\n        \"ADM0_A3\": \"EGY\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Egypt\",\n        \"GU_A3\": \"EGY\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Egypt\",\n        \"SU_A3\": \"EGY\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Egypt\",\n        \"NAME_LONG\": \"Egypt\",\n        \"BRK_A3\": \"EGY\",\n        \"BRK_NAME\": \"Egypt\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Egypt\",\n        \"POSTAL\": \"EG\",\n        \"FORMAL_EN\": \"Arab Republic of Egypt\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Egypt\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Egypt, Arab Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 97041072,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 1105000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"EG\",\n        \"ISO_A2\": \"EG\",\n        \"ISO_A3\": \"EGY\",\n        \"ISO_A3_EH\": \"EGY\",\n        \"ISO_N3\": \"818\",\n        \"UN_A3\": \"818\",\n        \"WB_A2\": \"EG\",\n        \"WB_A3\": \"EGY\",\n        \"WOE_ID\": 23424802,\n        \"WOE_ID_EH\": 23424802,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"EGY\",\n        \"ADM0_A3_US\": \"EGY\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [24.70007, 22, 36.86623, 31.58568],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [36.86623, 22],\n            [32.9, 22],\n            [29.02, 22],\n            [25, 22],\n            [25, 25.6825],\n            [25, 29.238655],\n            [24.70007, 30.04419],\n            [24.95762, 30.6616],\n            [24.80287, 31.08929],\n            [25.16482, 31.56915],\n            [26.49533, 31.58568],\n            [27.45762, 31.32126],\n            [28.45048, 31.02577],\n            [28.91353, 30.87005],\n            [29.68342, 31.18686],\n            [30.09503, 31.4734],\n            [30.97693, 31.55586],\n            [31.68796, 31.4296],\n            [31.96041, 30.9336],\n            [32.19247, 31.26034],\n            [32.99392, 31.02407],\n            [33.7734, 30.96746],\n            [34.265435, 31.219357],\n            [34.26544, 31.21936],\n            [34.823243, 29.761081],\n            [34.9226, 29.50133],\n            [34.64174, 29.09942],\n            [34.42655, 28.34399],\n            [34.15451, 27.8233],\n            [33.92136, 27.6487],\n            [33.58811, 27.97136],\n            [33.13676, 28.41765],\n            [32.42323, 29.85108],\n            [32.32046, 29.76043],\n            [32.73482, 28.70523],\n            [33.34876, 27.69989],\n            [34.10455, 26.14227],\n            [34.47387, 25.59856],\n            [34.79507, 25.03375],\n            [35.69241, 23.92671],\n            [35.49372, 23.75237],\n            [35.52598, 23.10244],\n            [36.69069, 22.20485],\n            [36.86623, 22]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Eritrea\",\n        \"SOV_A3\": \"ERI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Eritrea\",\n        \"ADM0_A3\": \"ERI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Eritrea\",\n        \"GU_A3\": \"ERI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Eritrea\",\n        \"SU_A3\": \"ERI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Eritrea\",\n        \"NAME_LONG\": \"Eritrea\",\n        \"BRK_A3\": \"ERI\",\n        \"BRK_NAME\": \"Eritrea\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Erit.\",\n        \"POSTAL\": \"ER\",\n        \"FORMAL_EN\": \"State of Eritrea\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Eritrea\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Eritrea\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 5918919,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 9169,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1984,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ER\",\n        \"ISO_A2\": \"ER\",\n        \"ISO_A3\": \"ERI\",\n        \"ISO_A3_EH\": \"ERI\",\n        \"ISO_N3\": \"232\",\n        \"UN_A3\": \"232\",\n        \"WB_A2\": \"ER\",\n        \"WB_A3\": \"ERI\",\n        \"WOE_ID\": 23424806,\n        \"WOE_ID_EH\": 23424806,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ERI\",\n        \"ADM0_A3_US\": \"ERI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [36.32322, 12.455416, 43.081226, 17.998307],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [43.081226, 12.699639],\n            [42.779642, 12.455416],\n            [42.35156, 12.54223],\n            [42.00975, 12.86582],\n            [41.59856, 13.45209],\n            [41.1552, 13.77333],\n            [40.8966, 14.11864],\n            [40.02625, 14.51959],\n            [39.34061, 14.53155],\n            [39.0994, 14.74064],\n            [38.51295, 14.50547],\n            [37.90607, 14.95943],\n            [37.59377, 14.2131],\n            [36.42951, 14.42211],\n            [36.32322, 14.82249],\n            [36.75389, 16.29186],\n            [36.85253, 16.95655],\n            [37.16747, 17.26314],\n            [37.904, 17.42754],\n            [38.41009, 17.998307],\n            [38.990623, 16.840626],\n            [39.26611, 15.922723],\n            [39.814294, 15.435647],\n            [41.179275, 14.49108],\n            [41.734952, 13.921037],\n            [42.276831, 13.343992],\n            [42.589576, 13.000421],\n            [43.081226, 12.699639]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Spain\",\n        \"SOV_A3\": \"ESP\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Spain\",\n        \"ADM0_A3\": \"ESP\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Spain\",\n        \"GU_A3\": \"ESP\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Spain\",\n        \"SU_A3\": \"ESP\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Spain\",\n        \"NAME_LONG\": \"Spain\",\n        \"BRK_A3\": \"ESP\",\n        \"BRK_NAME\": \"Spain\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Sp.\",\n        \"POSTAL\": \"E\",\n        \"FORMAL_EN\": \"Kingdom of Spain\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Spain\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Spain\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 48958159,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 1690000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SP\",\n        \"ISO_A2\": \"ES\",\n        \"ISO_A3\": \"ESP\",\n        \"ISO_A3_EH\": \"ESP\",\n        \"ISO_N3\": \"724\",\n        \"UN_A3\": \"724\",\n        \"WB_A2\": \"ES\",\n        \"WB_A3\": \"ESP\",\n        \"WOE_ID\": 23424950,\n        \"WOE_ID_EH\": 23424950,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ESP\",\n        \"ADM0_A3_US\": \"ESP\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 3,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-9.392884, 35.94685, 3.039484, 43.748338],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-9.034818, 41.880571],\n            [-8.984433, 42.592775],\n            [-9.392884, 43.026625],\n            [-7.97819, 43.748338],\n            [-6.754492, 43.567909],\n            [-5.411886, 43.57424],\n            [-4.347843, 43.403449],\n            [-3.517532, 43.455901],\n            [-1.901351, 43.422802],\n            [-1.502771, 43.034014],\n            [0.338047, 42.579546],\n            [0.701591, 42.795734],\n            [1.826793, 42.343385],\n            [2.985999, 42.473015],\n            [3.039484, 41.89212],\n            [2.091842, 41.226089],\n            [0.810525, 41.014732],\n            [0.721331, 40.678318],\n            [0.106692, 40.123934],\n            [-0.278711, 39.309978],\n            [0.111291, 38.738514],\n            [-0.467124, 38.292366],\n            [-0.683389, 37.642354],\n            [-1.438382, 37.443064],\n            [-2.146453, 36.674144],\n            [-3.415781, 36.6589],\n            [-4.368901, 36.677839],\n            [-4.995219, 36.324708],\n            [-5.37716, 35.94685],\n            [-5.866432, 36.029817],\n            [-6.236694, 36.367677],\n            [-6.520191, 36.942913],\n            [-7.453726, 37.097788],\n            [-7.537105, 37.428904],\n            [-7.166508, 37.803894],\n            [-7.029281, 38.075764],\n            [-7.374092, 38.373059],\n            [-7.098037, 39.030073],\n            [-7.498632, 39.629571],\n            [-7.066592, 39.711892],\n            [-7.026413, 40.184524],\n            [-6.86402, 40.330872],\n            [-6.851127, 41.111083],\n            [-6.389088, 41.381815],\n            [-6.668606, 41.883387],\n            [-7.251309, 41.918346],\n            [-7.422513, 41.792075],\n            [-8.013175, 41.790886],\n            [-8.263857, 42.280469],\n            [-8.671946, 42.134689],\n            [-9.034818, 41.880571]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Estonia\",\n        \"SOV_A3\": \"EST\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Estonia\",\n        \"ADM0_A3\": \"EST\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Estonia\",\n        \"GU_A3\": \"EST\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Estonia\",\n        \"SU_A3\": \"EST\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Estonia\",\n        \"NAME_LONG\": \"Estonia\",\n        \"BRK_A3\": \"EST\",\n        \"BRK_NAME\": \"Estonia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Est.\",\n        \"POSTAL\": \"EST\",\n        \"FORMAL_EN\": \"Republic of Estonia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Estonia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Estonia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 1251581,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 38700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2000,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"EN\",\n        \"ISO_A2\": \"EE\",\n        \"ISO_A3\": \"EST\",\n        \"ISO_A3_EH\": \"EST\",\n        \"ISO_N3\": \"233\",\n        \"UN_A3\": \"233\",\n        \"WB_A2\": \"EE\",\n        \"WB_A3\": \"EST\",\n        \"WOE_ID\": 23424805,\n        \"WOE_ID_EH\": 23424805,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"EST\",\n        \"ADM0_A3_US\": \"EST\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [23.339795, 57.474528, 28.131699, 59.61109],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [24.312863, 57.793424],\n            [24.428928, 58.383413],\n            [24.061198, 58.257375],\n            [23.42656, 58.612753],\n            [23.339795, 59.18724],\n            [24.604214, 59.465854],\n            [25.864189, 59.61109],\n            [26.949136, 59.445803],\n            [27.981114, 59.475388],\n            [27.981127, 59.475373],\n            [27.98112, 59.47537],\n            [28.131699, 59.300825],\n            [27.42015, 58.72457],\n            [27.716686, 57.791899],\n            [27.288185, 57.474528],\n            [26.463532, 57.476389],\n            [25.60281, 57.847529],\n            [25.164594, 57.970157],\n            [24.312863, 57.793424]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Ethiopia\",\n        \"SOV_A3\": \"ETH\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Ethiopia\",\n        \"ADM0_A3\": \"ETH\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Ethiopia\",\n        \"GU_A3\": \"ETH\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Ethiopia\",\n        \"SU_A3\": \"ETH\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Ethiopia\",\n        \"NAME_LONG\": \"Ethiopia\",\n        \"BRK_A3\": \"ETH\",\n        \"BRK_NAME\": \"Ethiopia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Eth.\",\n        \"POSTAL\": \"ET\",\n        \"FORMAL_EN\": \"Federal Democratic Republic of Ethiopia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Ethiopia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Ethiopia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 105350020,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 174700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ET\",\n        \"ISO_A2\": \"ET\",\n        \"ISO_A3\": \"ETH\",\n        \"ISO_A3_EH\": \"ETH\",\n        \"ISO_N3\": \"231\",\n        \"UN_A3\": \"231\",\n        \"WB_A2\": \"ET\",\n        \"WB_A3\": \"ETH\",\n        \"WOE_ID\": 23424808,\n        \"WOE_ID_EH\": 23424808,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ETH\",\n        \"ADM0_A3_US\": \"ETH\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [32.95418, 3.42206, 47.78942, 14.95943],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [42.35156, 12.54223],\n            [42, 12.1],\n            [41.66176, 11.6312],\n            [41.73959, 11.35511],\n            [41.75557, 11.05091],\n            [42.31414, 11.0342],\n            [42.55493, 11.10511],\n            [42.776852, 10.926879],\n            [42.55876, 10.57258],\n            [42.92812, 10.02194],\n            [43.29699, 9.54048],\n            [43.67875, 9.18358],\n            [46.94834, 7.99688],\n            [47.78942, 8.003],\n            [44.9636, 5.00162],\n            [43.66087, 4.95755],\n            [42.76967, 4.25259],\n            [42.12861, 4.23413],\n            [41.855083, 3.918912],\n            [41.1718, 3.91909],\n            [40.76848, 4.25702],\n            [39.85494, 3.83879],\n            [39.559384, 3.42206],\n            [38.89251, 3.50074],\n            [38.67114, 3.61607],\n            [38.43697, 3.58851],\n            [38.120915, 3.598605],\n            [36.855093, 4.447864],\n            [36.159079, 4.447864],\n            [35.817448, 4.776966],\n            [35.817448, 5.338232],\n            [35.298007, 5.506],\n            [34.70702, 6.59422],\n            [34.25032, 6.82607],\n            [34.0751, 7.22595],\n            [33.56829, 7.71334],\n            [32.95418, 7.78497],\n            [33.2948, 8.35458],\n            [33.8255, 8.37916],\n            [33.97498, 8.68456],\n            [33.96162, 9.58358],\n            [34.25745, 10.63009],\n            [34.73115, 10.91017],\n            [34.83163, 11.31896],\n            [35.26049, 12.08286],\n            [35.86363, 12.57828],\n            [36.27022, 13.56333],\n            [36.42951, 14.42211],\n            [37.59377, 14.2131],\n            [37.90607, 14.95943],\n            [38.51295, 14.50547],\n            [39.0994, 14.74064],\n            [39.34061, 14.53155],\n            [40.02625, 14.51959],\n            [40.8966, 14.11864],\n            [41.1552, 13.77333],\n            [41.59856, 13.45209],\n            [42.00975, 12.86582],\n            [42.35156, 12.54223]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Finland\",\n        \"SOV_A3\": \"FI1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"Finland\",\n        \"ADM0_A3\": \"FIN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Finland\",\n        \"GU_A3\": \"FIN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Finland\",\n        \"SU_A3\": \"FIN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Finland\",\n        \"NAME_LONG\": \"Finland\",\n        \"BRK_A3\": \"FIN\",\n        \"BRK_NAME\": \"Finland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Fin.\",\n        \"POSTAL\": \"FIN\",\n        \"FORMAL_EN\": \"Republic of Finland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Finland\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Finland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 5491218,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 224137,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"FI\",\n        \"ISO_A2\": \"FI\",\n        \"ISO_A3\": \"FIN\",\n        \"ISO_A3_EH\": \"FIN\",\n        \"ISO_N3\": \"246\",\n        \"UN_A3\": \"246\",\n        \"WB_A2\": \"FI\",\n        \"WB_A3\": \"FIN\",\n        \"WOE_ID\": 23424812,\n        \"WOE_ID_EH\": 23424812,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"FIN\",\n        \"ADM0_A3_US\": \"FIN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [20.645593, 59.846373, 31.516092, 70.164193],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [23.903379, 66.006927],\n            [23.56588, 66.396051],\n            [23.539473, 67.936009],\n            [21.978535, 68.616846],\n            [20.645593, 69.106247],\n            [21.244936, 69.370443],\n            [22.356238, 68.841741],\n            [23.66205, 68.891247],\n            [24.735679, 68.649557],\n            [25.689213, 69.092114],\n            [26.179622, 69.825299],\n            [27.732292, 70.164193],\n            [29.015573, 69.766491],\n            [28.59193, 69.064777],\n            [28.445944, 68.364613],\n            [29.977426, 67.698297],\n            [29.054589, 66.944286],\n            [30.21765, 65.80598],\n            [29.54443, 64.948672],\n            [30.444685, 64.204453],\n            [30.035872, 63.552814],\n            [31.516092, 62.867687],\n            [31.139991, 62.357693],\n            [30.211107, 61.780028],\n            [28.07, 60.50352],\n            [28.070002, 60.503519],\n            [28.069998, 60.503517],\n            [26.255173, 60.423961],\n            [24.496624, 60.057316],\n            [22.869695, 59.846373],\n            [22.290764, 60.391921],\n            [21.322244, 60.72017],\n            [21.544866, 61.705329],\n            [21.059211, 62.607393],\n            [21.536029, 63.189735],\n            [22.442744, 63.81781],\n            [24.730512, 64.902344],\n            [25.398068, 65.111427],\n            [25.294043, 65.534346],\n            [23.903379, 66.006927]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Fiji\",\n        \"SOV_A3\": \"FJI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Fiji\",\n        \"ADM0_A3\": \"FJI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Fiji\",\n        \"GU_A3\": \"FJI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Fiji\",\n        \"SU_A3\": \"FJI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Fiji\",\n        \"NAME_LONG\": \"Fiji\",\n        \"BRK_A3\": \"FJI\",\n        \"BRK_NAME\": \"Fiji\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Fiji\",\n        \"POSTAL\": \"FJ\",\n        \"FORMAL_EN\": \"Republic of Fiji\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Fiji\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Fiji\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 920938,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 8374,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"FJ\",\n        \"ISO_A2\": \"FJ\",\n        \"ISO_A3\": \"FJI\",\n        \"ISO_A3_EH\": \"FJI\",\n        \"ISO_N3\": \"242\",\n        \"UN_A3\": \"242\",\n        \"WB_A2\": \"FJ\",\n        \"WB_A3\": \"FJI\",\n        \"WOE_ID\": 23424813,\n        \"WOE_ID_EH\": 23424813,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"FJI\",\n        \"ADM0_A3_US\": \"FJI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Melanesia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-180, -18.28799, 180, -16.020882],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [178.3736, -17.33992],\n              [178.71806, -17.62846],\n              [178.55271, -18.15059],\n              [177.93266, -18.28799],\n              [177.38146, -18.16432],\n              [177.28504, -17.72465],\n              [177.67087, -17.38114],\n              [178.12557, -17.50481],\n              [178.3736, -17.33992]\n            ]\n          ],\n          [\n            [\n              [179.364143, -16.801354],\n              [178.725059, -17.012042],\n              [178.596839, -16.63915],\n              [179.096609, -16.433984],\n              [179.413509, -16.379054],\n              [180, -16.067133],\n              [180, -16.555217],\n              [179.364143, -16.801354]\n            ]\n          ],\n          [\n            [\n              [-179.917369, -16.501783],\n              [-180, -16.555217],\n              [-180, -16.067133],\n              [-179.79332, -16.020882],\n              [-179.917369, -16.501783]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"United Kingdom\",\n        \"SOV_A3\": \"GB1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Dependency\",\n        \"ADMIN\": \"Falkland Islands\",\n        \"ADM0_A3\": \"FLK\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Falkland Islands\",\n        \"GU_A3\": \"FLK\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Falkland Islands\",\n        \"SU_A3\": \"FLK\",\n        \"BRK_DIFF\": 1,\n        \"NAME\": \"Falkland Is.\",\n        \"NAME_LONG\": \"Falkland Islands\",\n        \"BRK_A3\": \"B12\",\n        \"BRK_NAME\": \"Falkland Is.\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Flk. Is.\",\n        \"POSTAL\": \"FK\",\n        \"FORMAL_EN\": \"Falkland Islands\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Falkland Islands (Islas Malvinas)\",\n        \"NOTE_ADM0\": \"U.K.\",\n        \"NOTE_BRK\": \"Admin. by U.K.; Claimed by Argentina\",\n        \"NAME_SORT\": \"Falkland Islands\",\n        \"NAME_ALT\": \"Islas Malvinas\",\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 2931,\n        \"POP_RANK\": 4,\n        \"GDP_MD_EST\": 281.8,\n        \"POP_YEAR\": 2014,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2012,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"FK\",\n        \"ISO_A2\": \"FK\",\n        \"ISO_A3\": \"FLK\",\n        \"ISO_A3_EH\": \"FLK\",\n        \"ISO_N3\": \"238\",\n        \"UN_A3\": \"238\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": 23424814,\n        \"WOE_ID_EH\": 23424814,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"FLK\",\n        \"ADM0_A3_US\": \"FLK\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 12,\n        \"LONG_LEN\": 16,\n        \"ABBREV_LEN\": 8,\n        \"TINY\": -99,\n        \"HOMEPART\": -99,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-61.2, -52.3, -57.75, -51.1],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-61.2, -51.85],\n            [-60, -51.25],\n            [-59.15, -51.5],\n            [-58.55, -51.1],\n            [-57.75, -51.55],\n            [-58.05, -51.9],\n            [-59.4, -52.2],\n            [-59.85, -51.85],\n            [-60.7, -52.3],\n            [-61.2, -51.85]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"France\",\n        \"SOV_A3\": \"FR1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"France\",\n        \"ADM0_A3\": \"FRA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"France\",\n        \"GU_A3\": \"FRA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"France\",\n        \"SU_A3\": \"FRA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"France\",\n        \"NAME_LONG\": \"France\",\n        \"BRK_A3\": \"FRA\",\n        \"BRK_NAME\": \"France\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Fr.\",\n        \"POSTAL\": \"F\",\n        \"FORMAL_EN\": \"French Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"France\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"France\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 7,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 9,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 67106161,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 2699000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"FR\",\n        \"ISO_A2\": \"-99\",\n        \"ISO_A3\": \"-99\",\n        \"ISO_A3_EH\": \"-99\",\n        \"ISO_N3\": \"250\",\n        \"UN_A3\": \"250\",\n        \"WB_A2\": \"FR\",\n        \"WB_A3\": \"FRA\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424819,\n        \"WOE_NOTE\": \"Includes only Metropolitan France (including Corsica)\",\n        \"ADM0_A3_IS\": \"FRA\",\n        \"ADM0_A3_US\": \"FRA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 3,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [-54.524754, 2.053389, 9.560016, 51.148506],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [2.513573, 51.148506],\n              [2.658422, 50.796848],\n              [3.123252, 50.780363],\n              [3.588184, 50.378992],\n              [4.286023, 49.907497],\n              [4.799222, 49.985373],\n              [5.674052, 49.529484],\n              [5.897759, 49.442667],\n              [6.18632, 49.463803],\n              [6.65823, 49.201958],\n              [8.099279, 49.017784],\n              [7.593676, 48.333019],\n              [7.466759, 47.620582],\n              [7.192202, 47.449766],\n              [6.736571, 47.541801],\n              [6.768714, 47.287708],\n              [6.037389, 46.725779],\n              [6.022609, 46.27299],\n              [6.5001, 46.429673],\n              [6.843593, 45.991147],\n              [6.802355, 45.70858],\n              [7.096652, 45.333099],\n              [6.749955, 45.028518],\n              [7.007562, 44.254767],\n              [7.549596, 44.127901],\n              [7.435185, 43.693845],\n              [6.529245, 43.128892],\n              [4.556963, 43.399651],\n              [3.100411, 43.075201],\n              [2.985999, 42.473015],\n              [1.826793, 42.343385],\n              [0.701591, 42.795734],\n              [0.338047, 42.579546],\n              [-1.502771, 43.034014],\n              [-1.901351, 43.422802],\n              [-1.384225, 44.02261],\n              [-1.193798, 46.014918],\n              [-2.225724, 47.064363],\n              [-2.963276, 47.570327],\n              [-4.491555, 47.954954],\n              [-4.59235, 48.68416],\n              [-3.295814, 48.901692],\n              [-1.616511, 48.644421],\n              [-1.933494, 49.776342],\n              [-0.989469, 49.347376],\n              [1.338761, 50.127173],\n              [1.639001, 50.946606],\n              [2.513573, 51.148506]\n            ]\n          ],\n          [\n            [\n              [-51.657797, 4.156232],\n              [-52.249338, 3.241094],\n              [-52.556425, 2.504705],\n              [-52.939657, 2.124858],\n              [-53.418465, 2.053389],\n              [-53.554839, 2.334897],\n              [-53.778521, 2.376703],\n              [-54.088063, 2.105557],\n              [-54.524754, 2.311849],\n              [-54.27123, 2.738748],\n              [-54.184284, 3.194172],\n              [-54.011504, 3.62257],\n              [-54.399542, 4.212611],\n              [-54.478633, 4.896756],\n              [-53.958045, 5.756548],\n              [-53.618453, 5.646529],\n              [-52.882141, 5.409851],\n              [-51.823343, 4.565768],\n              [-51.657797, 4.156232]\n            ]\n          ],\n          [\n            [\n              [9.560016, 42.152492],\n              [9.229752, 41.380007],\n              [8.775723, 41.583612],\n              [8.544213, 42.256517],\n              [8.746009, 42.628122],\n              [9.390001, 43.009985],\n              [9.560016, 42.152492]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Gabon\",\n        \"SOV_A3\": \"GAB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Gabon\",\n        \"ADM0_A3\": \"GAB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Gabon\",\n        \"GU_A3\": \"GAB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Gabon\",\n        \"SU_A3\": \"GAB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Gabon\",\n        \"NAME_LONG\": \"Gabon\",\n        \"BRK_A3\": \"GAB\",\n        \"BRK_NAME\": \"Gabon\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Gabon\",\n        \"POSTAL\": \"GA\",\n        \"FORMAL_EN\": \"Gabonese Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Gabon\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Gabon\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 1772255,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 35980,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2003,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GB\",\n        \"ISO_A2\": \"GA\",\n        \"ISO_A3\": \"GAB\",\n        \"ISO_A3_EH\": \"GAB\",\n        \"ISO_N3\": \"266\",\n        \"UN_A3\": \"266\",\n        \"WB_A2\": \"GA\",\n        \"WB_A3\": \"GAB\",\n        \"WOE_ID\": 23424822,\n        \"WOE_ID_EH\": 23424822,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GAB\",\n        \"ADM0_A3_US\": \"GAB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": 3,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [8.797996, -3.978827, 14.425456, 2.326758],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [11.276449, 2.261051],\n            [11.751665, 2.326758],\n            [12.35938, 2.192812],\n            [12.951334, 2.321616],\n            [13.075822, 2.267097],\n            [13.003114, 1.830896],\n            [13.282631, 1.314184],\n            [14.026669, 1.395677],\n            [14.276266, 1.19693],\n            [13.843321, 0.038758],\n            [14.316418, -0.552627],\n            [14.425456, -1.333407],\n            [14.29921, -1.998276],\n            [13.992407, -2.470805],\n            [13.109619, -2.42874],\n            [12.575284, -1.948511],\n            [12.495703, -2.391688],\n            [11.820964, -2.514161],\n            [11.478039, -2.765619],\n            [11.855122, -3.426871],\n            [11.093773, -3.978827],\n            [10.066135, -2.969483],\n            [9.405245, -2.144313],\n            [8.797996, -1.111301],\n            [8.830087, -0.779074],\n            [9.04842, -0.459351],\n            [9.291351, 0.268666],\n            [9.492889, 1.01012],\n            [9.830284, 1.067894],\n            [11.285079, 1.057662],\n            [11.276449, 2.261051]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"United Kingdom\",\n        \"SOV_A3\": \"GB1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"United Kingdom\",\n        \"ADM0_A3\": \"GBR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"United Kingdom\",\n        \"GU_A3\": \"GBR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"United Kingdom\",\n        \"SU_A3\": \"GBR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"United Kingdom\",\n        \"NAME_LONG\": \"United Kingdom\",\n        \"BRK_A3\": \"GBR\",\n        \"BRK_NAME\": \"United Kingdom\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"U.K.\",\n        \"POSTAL\": \"GB\",\n        \"FORMAL_EN\": \"United Kingdom of Great Britain and Northern Ireland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"United Kingdom\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"United Kingdom\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 64769452,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 2788000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"UK\",\n        \"ISO_A2\": \"GB\",\n        \"ISO_A3\": \"GBR\",\n        \"ISO_A3_EH\": \"GBR\",\n        \"ISO_N3\": \"826\",\n        \"UN_A3\": \"826\",\n        \"WB_A2\": \"GB\",\n        \"WB_A3\": \"GBR\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424975,\n        \"WOE_NOTE\": \"Eh ID includes Channel Islands and Isle of Man. UK constituent countries of England (24554868), Wales (12578049), Scotland (12578048), and Northern Ireland (20070563).\",\n        \"ADM0_A3_IS\": \"GBR\",\n        \"ADM0_A3_US\": \"GBR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 14,\n        \"LONG_LEN\": 14,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [-7.572168, 49.96, 1.681531, 58.635],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-6.197885, 53.867565],\n              [-6.95373, 54.073702],\n              [-7.572168, 54.059956],\n              [-7.366031, 54.595841],\n              [-7.572168, 55.131622],\n              [-6.733847, 55.17286],\n              [-5.661949, 54.554603],\n              [-6.197885, 53.867565]\n            ]\n          ],\n          [\n            [\n              [-3.005005, 58.635],\n              [-4.073828, 57.553025],\n              [-3.055002, 57.690019],\n              [-1.959281, 57.6848],\n              [-2.219988, 56.870017],\n              [-3.119003, 55.973793],\n              [-2.085009, 55.909998],\n              [-2.005676, 55.804903],\n              [-1.114991, 54.624986],\n              [-0.430485, 54.464376],\n              [0.184981, 53.325014],\n              [0.469977, 52.929999],\n              [1.681531, 52.73952],\n              [1.559988, 52.099998],\n              [1.050562, 51.806761],\n              [1.449865, 51.289428],\n              [0.550334, 50.765739],\n              [-0.787517, 50.774989],\n              [-2.489998, 50.500019],\n              [-2.956274, 50.69688],\n              [-3.617448, 50.228356],\n              [-4.542508, 50.341837],\n              [-5.245023, 49.96],\n              [-5.776567, 50.159678],\n              [-4.30999, 51.210001],\n              [-3.414851, 51.426009],\n              [-3.422719, 51.426848],\n              [-4.984367, 51.593466],\n              [-5.267296, 51.9914],\n              [-4.222347, 52.301356],\n              [-4.770013, 52.840005],\n              [-4.579999, 53.495004],\n              [-3.093831, 53.404547],\n              [-3.09208, 53.404441],\n              [-2.945009, 53.985],\n              [-3.614701, 54.600937],\n              [-3.630005, 54.615013],\n              [-4.844169, 54.790971],\n              [-5.082527, 55.061601],\n              [-4.719112, 55.508473],\n              [-5.047981, 55.783986],\n              [-5.586398, 55.311146],\n              [-5.644999, 56.275015],\n              [-6.149981, 56.78501],\n              [-5.786825, 57.818848],\n              [-5.009999, 58.630013],\n              [-4.211495, 58.550845],\n              [-3.005005, 58.635]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Georgia\",\n        \"SOV_A3\": \"GEO\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Georgia\",\n        \"ADM0_A3\": \"GEO\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Georgia\",\n        \"GU_A3\": \"GEO\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Georgia\",\n        \"SU_A3\": \"GEO\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Georgia\",\n        \"NAME_LONG\": \"Georgia\",\n        \"BRK_A3\": \"GEO\",\n        \"BRK_NAME\": \"Georgia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Geo.\",\n        \"POSTAL\": \"GE\",\n        \"FORMAL_EN\": \"Georgia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Georgia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Georgia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 4926330,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 37270,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GG\",\n        \"ISO_A2\": \"GE\",\n        \"ISO_A3\": \"GEO\",\n        \"ISO_A3_EH\": \"GEO\",\n        \"ISO_N3\": \"268\",\n        \"UN_A3\": \"268\",\n        \"WB_A2\": \"GE\",\n        \"WB_A3\": \"GEO\",\n        \"WOE_ID\": 23424823,\n        \"WOE_ID_EH\": 23424823,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GEO\",\n        \"ADM0_A3_US\": \"GEO\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [39.955009, 41.064445, 46.637908, 43.553104],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [44.97248, 41.248129],\n            [43.582746, 41.092143],\n            [42.619549, 41.583173],\n            [41.554084, 41.535656],\n            [41.703171, 41.962943],\n            [41.45347, 42.645123],\n            [40.875469, 43.013628],\n            [40.321394, 43.128634],\n            [39.955009, 43.434998],\n            [40.076965, 43.553104],\n            [40.92219, 43.38215],\n            [42.3944, 43.2203],\n            [43.75599, 42.74083],\n            [43.93121, 42.55496],\n            [44.537623, 42.711993],\n            [45.470279, 42.502781],\n            [45.7764, 42.09244],\n            [46.404951, 41.860675],\n            [46.145432, 41.722802],\n            [46.637908, 41.181673],\n            [46.501637, 41.064445],\n            [45.962601, 41.123873],\n            [45.217426, 41.411452],\n            [44.97248, 41.248129]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Ghana\",\n        \"SOV_A3\": \"GHA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Ghana\",\n        \"ADM0_A3\": \"GHA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Ghana\",\n        \"GU_A3\": \"GHA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Ghana\",\n        \"SU_A3\": \"GHA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Ghana\",\n        \"NAME_LONG\": \"Ghana\",\n        \"BRK_A3\": \"GHA\",\n        \"BRK_NAME\": \"Ghana\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ghana\",\n        \"POSTAL\": \"GH\",\n        \"FORMAL_EN\": \"Republic of Ghana\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Ghana\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Ghana\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 27499924,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 120800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GH\",\n        \"ISO_A2\": \"GH\",\n        \"ISO_A3\": \"GHA\",\n        \"ISO_A3_EH\": \"GHA\",\n        \"ISO_N3\": \"288\",\n        \"UN_A3\": \"288\",\n        \"WB_A2\": \"GH\",\n        \"WB_A3\": \"GHA\",\n        \"WOE_ID\": 23424824,\n        \"WOE_ID_EH\": 23424824,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GHA\",\n        \"ADM0_A3_US\": \"GHA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-3.24437, 4.710462, 1.060122, 11.098341],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-2.827496, 9.642461],\n            [-2.963896, 10.395335],\n            [-2.940409, 10.96269],\n            [-1.203358, 11.009819],\n            [-0.761576, 10.93693],\n            [-0.438702, 11.098341],\n            [0.023803, 11.018682],\n            [-0.049785, 10.706918],\n            [0.36758, 10.191213],\n            [0.365901, 9.465004],\n            [0.461192, 8.677223],\n            [0.712029, 8.312465],\n            [0.490957, 7.411744],\n            [0.570384, 6.914359],\n            [0.836931, 6.279979],\n            [1.060122, 5.928837],\n            [-0.507638, 5.343473],\n            [-1.063625, 5.000548],\n            [-1.964707, 4.710462],\n            [-2.856125, 4.994476],\n            [-2.810701, 5.389051],\n            [-3.24437, 6.250472],\n            [-2.983585, 7.379705],\n            [-2.56219, 8.219628],\n            [-2.827496, 9.642461]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Guinea\",\n        \"SOV_A3\": \"GIN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Guinea\",\n        \"ADM0_A3\": \"GIN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Guinea\",\n        \"GU_A3\": \"GIN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Guinea\",\n        \"SU_A3\": \"GIN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Guinea\",\n        \"NAME_LONG\": \"Guinea\",\n        \"BRK_A3\": \"GIN\",\n        \"BRK_NAME\": \"Guinea\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Gin.\",\n        \"POSTAL\": \"GN\",\n        \"FORMAL_EN\": \"Republic of Guinea\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Guinea\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Guinea\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 12413867,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 16080,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1996,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GV\",\n        \"ISO_A2\": \"GN\",\n        \"ISO_A3\": \"GIN\",\n        \"ISO_A3_EH\": \"GIN\",\n        \"ISO_N3\": \"324\",\n        \"UN_A3\": \"324\",\n        \"WB_A2\": \"GN\",\n        \"WB_A3\": \"GIN\",\n        \"WOE_ID\": 23424835,\n        \"WOE_ID_EH\": 23424835,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GIN\",\n        \"ADM0_A3_US\": \"GIN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-15.130311, 7.309037, -7.8321, 12.586183],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-8.029944, 10.206535],\n            [-8.229337, 10.12902],\n            [-8.309616, 9.789532],\n            [-8.079114, 9.376224],\n            [-7.8321, 8.575704],\n            [-8.203499, 8.455453],\n            [-8.299049, 8.316444],\n            [-8.221792, 8.123329],\n            [-8.280703, 7.68718],\n            [-8.439298, 7.686043],\n            [-8.722124, 7.711674],\n            [-8.926065, 7.309037],\n            [-9.208786, 7.313921],\n            [-9.403348, 7.526905],\n            [-9.33728, 7.928534],\n            [-9.755342, 8.541055],\n            [-10.016567, 8.428504],\n            [-10.230094, 8.406206],\n            [-10.505477, 8.348896],\n            [-10.494315, 8.715541],\n            [-10.65477, 8.977178],\n            [-10.622395, 9.26791],\n            [-10.839152, 9.688246],\n            [-11.117481, 10.045873],\n            [-11.917277, 10.046984],\n            [-12.150338, 9.858572],\n            [-12.425929, 9.835834],\n            [-12.596719, 9.620188],\n            [-12.711958, 9.342712],\n            [-13.24655, 8.903049],\n            [-13.685154, 9.494744],\n            [-14.074045, 9.886167],\n            [-14.330076, 10.01572],\n            [-14.579699, 10.214467],\n            [-14.693232, 10.656301],\n            [-14.839554, 10.876572],\n            [-15.130311, 11.040412],\n            [-14.685687, 11.527824],\n            [-14.382192, 11.509272],\n            [-14.121406, 11.677117],\n            [-13.9008, 11.678719],\n            [-13.743161, 11.811269],\n            [-13.828272, 12.142644],\n            [-13.718744, 12.247186],\n            [-13.700476, 12.586183],\n            [-13.217818, 12.575874],\n            [-12.499051, 12.33209],\n            [-12.278599, 12.35444],\n            [-12.203565, 12.465648],\n            [-11.658301, 12.386583],\n            [-11.513943, 12.442988],\n            [-11.456169, 12.076834],\n            [-11.297574, 12.077971],\n            [-11.036556, 12.211245],\n            [-10.87083, 12.177887],\n            [-10.593224, 11.923975],\n            [-10.165214, 11.844084],\n            [-9.890993, 12.060479],\n            [-9.567912, 12.194243],\n            [-9.327616, 12.334286],\n            [-9.127474, 12.30806],\n            [-8.905265, 12.088358],\n            [-8.786099, 11.812561],\n            [-8.376305, 11.393646],\n            [-8.581305, 11.136246],\n            [-8.620321, 10.810891],\n            [-8.407311, 10.909257],\n            [-8.282357, 10.792597],\n            [-8.335377, 10.494812],\n            [-8.029944, 10.206535]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Gambia\",\n        \"SOV_A3\": \"GMB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Gambia\",\n        \"ADM0_A3\": \"GMB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Gambia\",\n        \"GU_A3\": \"GMB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Gambia\",\n        \"SU_A3\": \"GMB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Gambia\",\n        \"NAME_LONG\": \"The Gambia\",\n        \"BRK_A3\": \"GMB\",\n        \"BRK_NAME\": \"Gambia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Gambia\",\n        \"POSTAL\": \"GM\",\n        \"FORMAL_EN\": \"Republic of the Gambia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Gambia, The\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Gambia, The\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 2051363,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 3387,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2003,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GA\",\n        \"ISO_A2\": \"GM\",\n        \"ISO_A3\": \"GMB\",\n        \"ISO_A3_EH\": \"GMB\",\n        \"ISO_N3\": \"270\",\n        \"UN_A3\": \"270\",\n        \"WB_A2\": \"GM\",\n        \"WB_A3\": \"GMB\",\n        \"WOE_ID\": 23424821,\n        \"WOE_ID_EH\": 23424821,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GMB\",\n        \"ADM0_A3_US\": \"GMB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [-16.841525, 13.130284, -13.844963, 13.876492],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-16.841525, 13.151394],\n            [-16.713729, 13.594959],\n            [-15.624596, 13.623587],\n            [-15.39877, 13.860369],\n            [-15.081735, 13.876492],\n            [-14.687031, 13.630357],\n            [-14.376714, 13.62568],\n            [-14.046992, 13.794068],\n            [-13.844963, 13.505042],\n            [-14.277702, 13.280585],\n            [-14.712197, 13.298207],\n            [-15.141163, 13.509512],\n            [-15.511813, 13.27857],\n            [-15.691001, 13.270353],\n            [-15.931296, 13.130284],\n            [-16.841525, 13.151394]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Guinea-Bissau\",\n        \"SOV_A3\": \"GNB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Guinea-Bissau\",\n        \"ADM0_A3\": \"GNB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Guinea-Bissau\",\n        \"GU_A3\": \"GNB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Guinea-Bissau\",\n        \"SU_A3\": \"GNB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Guinea-Bissau\",\n        \"NAME_LONG\": \"Guinea-Bissau\",\n        \"BRK_A3\": \"GNB\",\n        \"BRK_NAME\": \"Guinea-Bissau\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"GnB.\",\n        \"POSTAL\": \"GW\",\n        \"FORMAL_EN\": \"Republic of Guinea-Bissau\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Guinea-Bissau\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Guinea-Bissau\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 1792338,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 2851,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PU\",\n        \"ISO_A2\": \"GW\",\n        \"ISO_A3\": \"GNB\",\n        \"ISO_A3_EH\": \"GNB\",\n        \"ISO_N3\": \"624\",\n        \"UN_A3\": \"624\",\n        \"WB_A2\": \"GW\",\n        \"WB_A3\": \"GNB\",\n        \"WOE_ID\": 23424929,\n        \"WOE_ID_EH\": 23424929,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GNB\",\n        \"ADM0_A3_US\": \"GNB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 13,\n        \"LONG_LEN\": 13,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [-16.677452, 11.040412, -13.700476, 12.62817],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-13.700476, 12.586183],\n            [-13.718744, 12.247186],\n            [-13.828272, 12.142644],\n            [-13.743161, 11.811269],\n            [-13.9008, 11.678719],\n            [-14.121406, 11.677117],\n            [-14.382192, 11.509272],\n            [-14.685687, 11.527824],\n            [-15.130311, 11.040412],\n            [-15.66418, 11.458474],\n            [-16.085214, 11.524594],\n            [-16.314787, 11.806515],\n            [-16.308947, 11.958702],\n            [-16.613838, 12.170911],\n            [-16.677452, 12.384852],\n            [-16.147717, 12.547762],\n            [-15.816574, 12.515567],\n            [-15.548477, 12.62817],\n            [-13.700476, 12.586183]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Equatorial Guinea\",\n        \"SOV_A3\": \"GNQ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Equatorial Guinea\",\n        \"ADM0_A3\": \"GNQ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Equatorial Guinea\",\n        \"GU_A3\": \"GNQ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Equatorial Guinea\",\n        \"SU_A3\": \"GNQ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Eq. Guinea\",\n        \"NAME_LONG\": \"Equatorial Guinea\",\n        \"BRK_A3\": \"GNQ\",\n        \"BRK_NAME\": \"Eq. Guinea\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Eq. G.\",\n        \"POSTAL\": \"GQ\",\n        \"FORMAL_EN\": \"Republic of Equatorial Guinea\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Equatorial Guinea\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Equatorial Guinea\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 778358,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 31770,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"EK\",\n        \"ISO_A2\": \"GQ\",\n        \"ISO_A3\": \"GNQ\",\n        \"ISO_A3_EH\": \"GNQ\",\n        \"ISO_N3\": \"226\",\n        \"UN_A3\": \"226\",\n        \"WB_A2\": \"GQ\",\n        \"WB_A3\": \"GNQ\",\n        \"WOE_ID\": 23424804,\n        \"WOE_ID_EH\": 23424804,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GNQ\",\n        \"ADM0_A3_US\": \"GNQ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 17,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [9.305613, 1.01012, 11.285079, 2.283866],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [9.649158, 2.283866],\n            [11.276449, 2.261051],\n            [11.285079, 1.057662],\n            [9.830284, 1.067894],\n            [9.492889, 1.01012],\n            [9.305613, 1.160911],\n            [9.649158, 2.283866]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Greece\",\n        \"SOV_A3\": \"GRC\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Greece\",\n        \"ADM0_A3\": \"GRC\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Greece\",\n        \"GU_A3\": \"GRC\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Greece\",\n        \"SU_A3\": \"GRC\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Greece\",\n        \"NAME_LONG\": \"Greece\",\n        \"BRK_A3\": \"GRC\",\n        \"BRK_NAME\": \"Greece\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Greece\",\n        \"POSTAL\": \"GR\",\n        \"FORMAL_EN\": \"Hellenic Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Greece\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Greece\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 10768477,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 290500,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GR\",\n        \"ISO_A2\": \"GR\",\n        \"ISO_A3\": \"GRC\",\n        \"ISO_A3_EH\": \"GRC\",\n        \"ISO_N3\": \"300\",\n        \"UN_A3\": \"300\",\n        \"WB_A2\": \"GR\",\n        \"WB_A3\": \"GRC\",\n        \"WOE_ID\": 23424833,\n        \"WOE_ID_EH\": 23424833,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GRC\",\n        \"ADM0_A3_US\": \"GRC\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [20.150016, 34.919988, 26.604196, 41.826905],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [20.150016, 39.624998],\n              [20.615, 40.110007],\n              [20.674997, 40.435],\n              [20.99999, 40.580004],\n              [21.02004, 40.842727],\n              [21.674161, 40.931275],\n              [22.055378, 41.149866],\n              [22.597308, 41.130487],\n              [22.76177, 41.3048],\n              [22.952377, 41.337994],\n              [23.692074, 41.309081],\n              [24.492645, 41.583896],\n              [25.197201, 41.234486],\n              [26.106138, 41.328899],\n              [26.117042, 41.826905],\n              [26.604196, 41.562115],\n              [26.294602, 40.936261],\n              [26.056942, 40.824123],\n              [25.447677, 40.852545],\n              [24.925848, 40.947062],\n              [23.714811, 40.687129],\n              [24.407999, 40.124993],\n              [23.899968, 39.962006],\n              [23.342999, 39.960998],\n              [22.813988, 40.476005],\n              [22.626299, 40.256561],\n              [22.849748, 39.659311],\n              [23.350027, 39.190011],\n              [22.973099, 38.970903],\n              [23.530016, 38.510001],\n              [24.025025, 38.219993],\n              [24.040011, 37.655015],\n              [23.115003, 37.920011],\n              [23.409972, 37.409991],\n              [22.774972, 37.30501],\n              [23.154225, 36.422506],\n              [22.490028, 36.41],\n              [21.670026, 36.844986],\n              [21.295011, 37.644989],\n              [21.120034, 38.310323],\n              [20.730032, 38.769985],\n              [20.217712, 39.340235],\n              [20.150016, 39.624998]\n            ]\n          ],\n          [\n            [\n              [23.69998, 35.705004],\n              [24.246665, 35.368022],\n              [25.025015, 35.424996],\n              [25.769208, 35.354018],\n              [25.745023, 35.179998],\n              [26.290003, 35.29999],\n              [26.164998, 35.004995],\n              [24.724982, 34.919988],\n              [24.735007, 35.084991],\n              [23.514978, 35.279992],\n              [23.69998, 35.705004]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Denmark\",\n        \"SOV_A3\": \"DN1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"Greenland\",\n        \"ADM0_A3\": \"GRL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Greenland\",\n        \"GU_A3\": \"GRL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Greenland\",\n        \"SU_A3\": \"GRL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Greenland\",\n        \"NAME_LONG\": \"Greenland\",\n        \"BRK_A3\": \"GRL\",\n        \"BRK_NAME\": \"Greenland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Grlnd.\",\n        \"POSTAL\": \"GL\",\n        \"FORMAL_EN\": \"Greenland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Greenland\",\n        \"NOTE_ADM0\": \"Den.\",\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Greenland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 57713,\n        \"POP_RANK\": 8,\n        \"GDP_MD_EST\": 2173,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2015,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GL\",\n        \"ISO_A2\": \"GL\",\n        \"ISO_A3\": \"GRL\",\n        \"ISO_A3_EH\": \"GRL\",\n        \"ISO_N3\": \"304\",\n        \"UN_A3\": \"304\",\n        \"WB_A2\": \"GL\",\n        \"WB_A3\": \"GRL\",\n        \"WOE_ID\": 23424828,\n        \"WOE_ID_EH\": 23424828,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GRL\",\n        \"ADM0_A3_US\": \"GRL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Northern America\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": -99,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [-73.297, 60.03676, -12.20855, 83.64513],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-46.76379, 82.62796],\n            [-43.40644, 83.22516],\n            [-39.89753, 83.18018],\n            [-38.62214, 83.54905],\n            [-35.08787, 83.64513],\n            [-27.10046, 83.51966],\n            [-20.84539, 82.72669],\n            [-22.69182, 82.34165],\n            [-26.51753, 82.29765],\n            [-31.9, 82.2],\n            [-31.39646, 82.02154],\n            [-27.85666, 82.13178],\n            [-24.84448, 81.78697],\n            [-22.90328, 82.09317],\n            [-22.07175, 81.73449],\n            [-23.16961, 81.15271],\n            [-20.62363, 81.52462],\n            [-15.76818, 81.91245],\n            [-12.77018, 81.71885],\n            [-12.20855, 81.29154],\n            [-16.28533, 80.58004],\n            [-16.85, 80.35],\n            [-20.04624, 80.17708],\n            [-17.73035, 80.12912],\n            [-18.9, 79.4],\n            [-19.70499, 78.75128],\n            [-19.67353, 77.63859],\n            [-18.47285, 76.98565],\n            [-20.03503, 76.94434],\n            [-21.67944, 76.62795],\n            [-19.83407, 76.09808],\n            [-19.59896, 75.24838],\n            [-20.66818, 75.15585],\n            [-19.37281, 74.29561],\n            [-21.59422, 74.22382],\n            [-20.43454, 73.81713],\n            [-20.76234, 73.46436],\n            [-22.17221, 73.30955],\n            [-23.56593, 73.30663],\n            [-22.31311, 72.62928],\n            [-22.29954, 72.18409],\n            [-24.27834, 72.59788],\n            [-24.79296, 72.3302],\n            [-23.44296, 72.08016],\n            [-22.13281, 71.46898],\n            [-21.75356, 70.66369],\n            [-23.53603, 70.471],\n            [-24.30702, 70.85649],\n            [-25.54341, 71.43094],\n            [-25.20135, 70.75226],\n            [-26.36276, 70.22646],\n            [-23.72742, 70.18401],\n            [-22.34902, 70.12946],\n            [-25.02927, 69.2588],\n            [-27.74737, 68.47046],\n            [-30.67371, 68.12503],\n            [-31.77665, 68.12078],\n            [-32.81105, 67.73547],\n            [-34.20196, 66.67974],\n            [-36.35284, 65.9789],\n            [-37.04378, 65.93768],\n            [-38.37505, 65.69213],\n            [-39.81222, 65.45848],\n            [-40.66899, 64.83997],\n            [-40.68281, 64.13902],\n            [-41.1887, 63.48246],\n            [-42.81938, 62.68233],\n            [-42.41666, 61.90093],\n            [-42.86619, 61.07404],\n            [-43.3784, 60.09772],\n            [-44.7875, 60.03676],\n            [-46.26364, 60.85328],\n            [-48.26294, 60.85843],\n            [-49.23308, 61.40681],\n            [-49.90039, 62.38336],\n            [-51.63325, 63.62691],\n            [-52.14014, 64.27842],\n            [-52.27659, 65.1767],\n            [-53.66166, 66.09957],\n            [-53.30161, 66.8365],\n            [-53.96911, 67.18899],\n            [-52.9804, 68.35759],\n            [-51.47536, 68.72958],\n            [-51.08041, 69.14781],\n            [-50.87122, 69.9291],\n            [-52.013585, 69.574925],\n            [-52.55792, 69.42616],\n            [-53.45629, 69.283625],\n            [-54.68336, 69.61003],\n            [-54.75001, 70.28932],\n            [-54.35884, 70.821315],\n            [-53.431315, 70.835755],\n            [-51.39014, 70.56978],\n            [-53.10937, 71.20485],\n            [-54.00422, 71.54719],\n            [-55, 71.406537],\n            [-55.83468, 71.65444],\n            [-54.71819, 72.58625],\n            [-55.32634, 72.95861],\n            [-56.12003, 73.64977],\n            [-57.32363, 74.71026],\n            [-58.59679, 75.09861],\n            [-58.58516, 75.51727],\n            [-61.26861, 76.10238],\n            [-63.39165, 76.1752],\n            [-66.06427, 76.13486],\n            [-68.50438, 76.06141],\n            [-69.66485, 76.37975],\n            [-71.40257, 77.00857],\n            [-68.77671, 77.32312],\n            [-66.76397, 77.37595],\n            [-71.04293, 77.63595],\n            [-73.297, 78.04419],\n            [-73.15938, 78.43271],\n            [-69.37345, 78.91388],\n            [-65.7107, 79.39436],\n            [-65.3239, 79.75814],\n            [-68.02298, 80.11721],\n            [-67.15129, 80.51582],\n            [-63.68925, 81.21396],\n            [-62.23444, 81.3211],\n            [-62.65116, 81.77042],\n            [-60.28249, 82.03363],\n            [-57.20744, 82.19074],\n            [-54.13442, 82.19962],\n            [-53.04328, 81.88833],\n            [-50.39061, 82.43883],\n            [-48.00386, 82.06481],\n            [-46.59984, 81.985945],\n            [-44.523, 81.6607],\n            [-46.9007, 82.19979],\n            [-46.76379, 82.62796]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Guatemala\",\n        \"SOV_A3\": \"GTM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Guatemala\",\n        \"ADM0_A3\": \"GTM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Guatemala\",\n        \"GU_A3\": \"GTM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Guatemala\",\n        \"SU_A3\": \"GTM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Guatemala\",\n        \"NAME_LONG\": \"Guatemala\",\n        \"BRK_A3\": \"GTM\",\n        \"BRK_NAME\": \"Guatemala\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Guat.\",\n        \"POSTAL\": \"GT\",\n        \"FORMAL_EN\": \"Republic of Guatemala\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Guatemala\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Guatemala\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 15460732,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 131800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GT\",\n        \"ISO_A2\": \"GT\",\n        \"ISO_A3\": \"GTM\",\n        \"ISO_A3_EH\": \"GTM\",\n        \"ISO_N3\": \"320\",\n        \"UN_A3\": \"320\",\n        \"WB_A2\": \"GT\",\n        \"WB_A3\": \"GTM\",\n        \"WOE_ID\": 23424834,\n        \"WOE_ID_EH\": 23424834,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GTM\",\n        \"ADM0_A3_US\": \"GTM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": 4,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-92.229249, 13.735338, -88.225023, 17.819326],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-89.14308, 17.808319],\n            [-89.150806, 17.015577],\n            [-89.229122, 15.886938],\n            [-88.930613, 15.887273],\n            [-88.604586, 15.70638],\n            [-88.518364, 15.855389],\n            [-88.225023, 15.727722],\n            [-88.68068, 15.346247],\n            [-89.154811, 15.066419],\n            [-89.22522, 14.874286],\n            [-89.145535, 14.678019],\n            [-89.353326, 14.424133],\n            [-89.587343, 14.362586],\n            [-89.534219, 14.244816],\n            [-89.721934, 14.134228],\n            [-90.064678, 13.88197],\n            [-90.095555, 13.735338],\n            [-90.608624, 13.909771],\n            [-91.23241, 13.927832],\n            [-91.689747, 14.126218],\n            [-92.22775, 14.538829],\n            [-92.20323, 14.830103],\n            [-92.087216, 15.064585],\n            [-92.229249, 15.251447],\n            [-91.74796, 16.066565],\n            [-90.464473, 16.069562],\n            [-90.438867, 16.41011],\n            [-90.600847, 16.470778],\n            [-90.711822, 16.687483],\n            [-91.08167, 16.918477],\n            [-91.453921, 17.252177],\n            [-91.002269, 17.254658],\n            [-91.00152, 17.817595],\n            [-90.067934, 17.819326],\n            [-89.14308, 17.808319]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Guyana\",\n        \"SOV_A3\": \"GUY\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Guyana\",\n        \"ADM0_A3\": \"GUY\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Guyana\",\n        \"GU_A3\": \"GUY\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Guyana\",\n        \"SU_A3\": \"GUY\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Guyana\",\n        \"NAME_LONG\": \"Guyana\",\n        \"BRK_A3\": \"GUY\",\n        \"BRK_NAME\": \"Guyana\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Guy.\",\n        \"POSTAL\": \"GY\",\n        \"FORMAL_EN\": \"Co-operative Republic of Guyana\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Guyana\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Guyana\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 737718,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 6093,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"GY\",\n        \"ISO_A2\": \"GY\",\n        \"ISO_A3\": \"GUY\",\n        \"ISO_A3_EH\": \"GUY\",\n        \"ISO_N3\": \"328\",\n        \"UN_A3\": \"328\",\n        \"WB_A2\": \"GY\",\n        \"WB_A3\": \"GUY\",\n        \"WOE_ID\": 23424836,\n        \"WOE_ID_EH\": 23424836,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"GUY\",\n        \"ADM0_A3_US\": \"GUY\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-61.410303, 1.268088, -56.539386, 8.367035],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-56.539386, 1.899523],\n            [-56.782704, 1.863711],\n            [-57.335823, 1.948538],\n            [-57.660971, 1.682585],\n            [-58.11345, 1.507195],\n            [-58.429477, 1.463942],\n            [-58.540013, 1.268088],\n            [-59.030862, 1.317698],\n            [-59.646044, 1.786894],\n            [-59.718546, 2.24963],\n            [-59.974525, 2.755233],\n            [-59.815413, 3.606499],\n            [-59.53804, 3.958803],\n            [-59.767406, 4.423503],\n            [-60.111002, 4.574967],\n            [-59.980959, 5.014061],\n            [-60.213683, 5.244486],\n            [-60.733574, 5.200277],\n            [-61.410303, 5.959068],\n            [-61.139415, 6.234297],\n            [-61.159336, 6.696077],\n            [-60.543999, 6.856584],\n            [-60.295668, 7.043911],\n            [-60.637973, 7.415],\n            [-60.550588, 7.779603],\n            [-59.758285, 8.367035],\n            [-59.101684, 7.999202],\n            [-58.482962, 7.347691],\n            [-58.454876, 6.832787],\n            [-58.078103, 6.809094],\n            [-57.542219, 6.321268],\n            [-57.147436, 5.97315],\n            [-57.307246, 5.073567],\n            [-57.914289, 4.812626],\n            [-57.86021, 4.576801],\n            [-58.044694, 4.060864],\n            [-57.601569, 3.334655],\n            [-57.281433, 3.333492],\n            [-57.150098, 2.768927],\n            [-56.539386, 1.899523]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Honduras\",\n        \"SOV_A3\": \"HND\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Honduras\",\n        \"ADM0_A3\": \"HND\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Honduras\",\n        \"GU_A3\": \"HND\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Honduras\",\n        \"SU_A3\": \"HND\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Honduras\",\n        \"NAME_LONG\": \"Honduras\",\n        \"BRK_A3\": \"HND\",\n        \"BRK_NAME\": \"Honduras\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Hond.\",\n        \"POSTAL\": \"HN\",\n        \"FORMAL_EN\": \"Republic of Honduras\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Honduras\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Honduras\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 9038741,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 43190,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"HO\",\n        \"ISO_A2\": \"HN\",\n        \"ISO_A3\": \"HND\",\n        \"ISO_A3_EH\": \"HND\",\n        \"ISO_N3\": \"340\",\n        \"UN_A3\": \"340\",\n        \"WB_A2\": \"HN\",\n        \"WB_A3\": \"HND\",\n        \"WOE_ID\": 23424841,\n        \"WOE_ID_EH\": 23424841,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"HND\",\n        \"ADM0_A3_US\": \"HND\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9.5\n      },\n      \"bbox\": [-89.353326, 12.984686, -83.147219, 16.005406],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-89.353326, 14.424133],\n            [-89.145535, 14.678019],\n            [-89.22522, 14.874286],\n            [-89.154811, 15.066419],\n            [-88.68068, 15.346247],\n            [-88.225023, 15.727722],\n            [-88.121153, 15.688655],\n            [-87.901813, 15.864458],\n            [-87.61568, 15.878799],\n            [-87.522921, 15.797279],\n            [-87.367762, 15.84694],\n            [-86.903191, 15.756713],\n            [-86.440946, 15.782835],\n            [-86.119234, 15.893449],\n            [-86.001954, 16.005406],\n            [-85.683317, 15.953652],\n            [-85.444004, 15.885749],\n            [-85.182444, 15.909158],\n            [-84.983722, 15.995923],\n            [-84.52698, 15.857224],\n            [-84.368256, 15.835158],\n            [-84.063055, 15.648244],\n            [-83.773977, 15.424072],\n            [-83.410381, 15.270903],\n            [-83.147219, 14.995829],\n            [-83.489989, 15.016267],\n            [-83.628585, 14.880074],\n            [-83.975721, 14.749436],\n            [-84.228342, 14.748764],\n            [-84.449336, 14.621614],\n            [-84.649582, 14.666805],\n            [-84.820037, 14.819587],\n            [-84.924501, 14.790493],\n            [-85.052787, 14.551541],\n            [-85.148751, 14.560197],\n            [-85.165365, 14.35437],\n            [-85.514413, 14.079012],\n            [-85.698665, 13.960078],\n            [-85.801295, 13.836055],\n            [-86.096264, 14.038187],\n            [-86.312142, 13.771356],\n            [-86.520708, 13.778487],\n            [-86.755087, 13.754845],\n            [-86.733822, 13.263093],\n            [-86.880557, 13.254204],\n            [-87.005769, 13.025794],\n            [-87.316654, 12.984686],\n            [-87.489409, 13.297535],\n            [-87.793111, 13.38448],\n            [-87.723503, 13.78505],\n            [-87.859515, 13.893312],\n            [-88.065343, 13.964626],\n            [-88.503998, 13.845486],\n            [-88.541231, 13.980155],\n            [-88.843073, 14.140507],\n            [-89.058512, 14.340029],\n            [-89.353326, 14.424133]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Croatia\",\n        \"SOV_A3\": \"HRV\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Croatia\",\n        \"ADM0_A3\": \"HRV\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Croatia\",\n        \"GU_A3\": \"HRV\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Croatia\",\n        \"SU_A3\": \"HRV\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Croatia\",\n        \"NAME_LONG\": \"Croatia\",\n        \"BRK_A3\": \"HRV\",\n        \"BRK_NAME\": \"Croatia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Cro.\",\n        \"POSTAL\": \"HR\",\n        \"FORMAL_EN\": \"Republic of Croatia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Croatia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Croatia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 4292095,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 94240,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"HR\",\n        \"ISO_A2\": \"HR\",\n        \"ISO_A3\": \"HRV\",\n        \"ISO_A3_EH\": \"HRV\",\n        \"ISO_N3\": \"191\",\n        \"UN_A3\": \"191\",\n        \"WB_A2\": \"HR\",\n        \"WB_A3\": \"HRV\",\n        \"WOE_ID\": 23424843,\n        \"WOE_ID_EH\": 23424843,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"HRV\",\n        \"ADM0_A3_US\": \"HRV\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [13.656976, 42.479991, 19.390476, 46.503751],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [19.005485, 44.860234],\n            [18.553214, 45.08159],\n            [17.861783, 45.06774],\n            [17.002146, 45.233777],\n            [16.534939, 45.211608],\n            [16.318157, 45.004127],\n            [15.959367, 45.233777],\n            [15.750026, 44.818712],\n            [16.23966, 44.351143],\n            [16.456443, 44.04124],\n            [16.916156, 43.667722],\n            [17.297373, 43.446341],\n            [17.674922, 43.028563],\n            [18.56, 42.65],\n            [18.450017, 42.479992],\n            [18.450016, 42.479991],\n            [17.50997, 42.849995],\n            [16.930006, 43.209998],\n            [16.015385, 43.507215],\n            [15.174454, 44.243191],\n            [15.37625, 44.317915],\n            [14.920309, 44.738484],\n            [14.901602, 45.07606],\n            [14.258748, 45.233777],\n            [13.952255, 44.802124],\n            [13.656976, 45.136935],\n            [13.679403, 45.484149],\n            [13.71506, 45.500324],\n            [14.411968, 45.466166],\n            [14.595109, 45.634941],\n            [14.935244, 45.471695],\n            [15.327675, 45.452316],\n            [15.323954, 45.731783],\n            [15.67153, 45.834154],\n            [15.768733, 46.238108],\n            [16.564808, 46.503751],\n            [16.882515, 46.380632],\n            [17.630066, 45.951769],\n            [18.456062, 45.759481],\n            [18.829825, 45.908872],\n            [19.072769, 45.521511],\n            [19.390476, 45.236516],\n            [19.005485, 44.860234]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Haiti\",\n        \"SOV_A3\": \"HTI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Haiti\",\n        \"ADM0_A3\": \"HTI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Haiti\",\n        \"GU_A3\": \"HTI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Haiti\",\n        \"SU_A3\": \"HTI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Haiti\",\n        \"NAME_LONG\": \"Haiti\",\n        \"BRK_A3\": \"HTI\",\n        \"BRK_NAME\": \"Haiti\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Haiti\",\n        \"POSTAL\": \"HT\",\n        \"FORMAL_EN\": \"Republic of Haiti\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Haiti\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Haiti\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 10646714,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 19340,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2003,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"HA\",\n        \"ISO_A2\": \"HT\",\n        \"ISO_A3\": \"HTI\",\n        \"ISO_A3_EH\": \"HTI\",\n        \"ISO_N3\": \"332\",\n        \"UN_A3\": \"332\",\n        \"WB_A2\": \"HT\",\n        \"WB_A3\": \"HTI\",\n        \"WOE_ID\": 23424839,\n        \"WOE_ID_EH\": 23424839,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"HTI\",\n        \"ADM0_A3_US\": \"HTI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-74.458034, 18.030993, -71.624873, 19.915684],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-71.712361, 19.714456],\n            [-71.624873, 19.169838],\n            [-71.701303, 18.785417],\n            [-71.945112, 18.6169],\n            [-71.687738, 18.31666],\n            [-71.708305, 18.044997],\n            [-72.372476, 18.214961],\n            [-72.844411, 18.145611],\n            [-73.454555, 18.217906],\n            [-73.922433, 18.030993],\n            [-74.458034, 18.34255],\n            [-74.369925, 18.664908],\n            [-73.449542, 18.526053],\n            [-72.694937, 18.445799],\n            [-72.334882, 18.668422],\n            [-72.79165, 19.101625],\n            [-72.784105, 19.483591],\n            [-73.415022, 19.639551],\n            [-73.189791, 19.915684],\n            [-72.579673, 19.871501],\n            [-71.712361, 19.714456]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Hungary\",\n        \"SOV_A3\": \"HUN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Hungary\",\n        \"ADM0_A3\": \"HUN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Hungary\",\n        \"GU_A3\": \"HUN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Hungary\",\n        \"SU_A3\": \"HUN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Hungary\",\n        \"NAME_LONG\": \"Hungary\",\n        \"BRK_A3\": \"HUN\",\n        \"BRK_NAME\": \"Hungary\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Hun.\",\n        \"POSTAL\": \"HU\",\n        \"FORMAL_EN\": \"Republic of Hungary\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Hungary\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Hungary\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 9850845,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 267600,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"HU\",\n        \"ISO_A2\": \"HU\",\n        \"ISO_A3\": \"HUN\",\n        \"ISO_A3_EH\": \"HUN\",\n        \"ISO_N3\": \"348\",\n        \"UN_A3\": \"348\",\n        \"WB_A2\": \"HU\",\n        \"WB_A3\": \"HUN\",\n        \"WOE_ID\": 23424844,\n        \"WOE_ID_EH\": 23424844,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"HUN\",\n        \"ADM0_A3_US\": \"HUN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [16.202298, 45.759481, 22.710531, 48.623854],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [16.202298, 46.852386],\n            [16.534268, 47.496171],\n            [16.340584, 47.712902],\n            [16.903754, 47.714866],\n            [16.979667, 48.123497],\n            [17.488473, 47.867466],\n            [17.857133, 47.758429],\n            [18.696513, 47.880954],\n            [18.777025, 48.081768],\n            [19.174365, 48.111379],\n            [19.661364, 48.266615],\n            [19.769471, 48.202691],\n            [20.239054, 48.327567],\n            [20.473562, 48.56285],\n            [20.801294, 48.623854],\n            [21.872236, 48.319971],\n            [22.085608, 48.422264],\n            [22.64082, 48.15024],\n            [22.710531, 47.882194],\n            [22.099768, 47.672439],\n            [21.626515, 46.994238],\n            [21.021952, 46.316088],\n            [20.220192, 46.127469],\n            [19.596045, 46.17173],\n            [18.829838, 45.908878],\n            [18.829825, 45.908872],\n            [18.456062, 45.759481],\n            [17.630066, 45.951769],\n            [16.882515, 46.380632],\n            [16.564808, 46.503751],\n            [16.370505, 46.841327],\n            [16.202298, 46.852386]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Indonesia\",\n        \"SOV_A3\": \"IDN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Indonesia\",\n        \"ADM0_A3\": \"IDN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Indonesia\",\n        \"GU_A3\": \"IDN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Indonesia\",\n        \"SU_A3\": \"IDN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Indonesia\",\n        \"NAME_LONG\": \"Indonesia\",\n        \"BRK_A3\": \"IDN\",\n        \"BRK_NAME\": \"Indonesia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Indo.\",\n        \"POSTAL\": \"INDO\",\n        \"FORMAL_EN\": \"Republic of Indonesia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Indonesia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Indonesia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 260580739,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 3028000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"4. Emerging region: MIKT\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ID\",\n        \"ISO_A2\": \"ID\",\n        \"ISO_A3\": \"IDN\",\n        \"ISO_A3_EH\": \"IDN\",\n        \"ISO_N3\": \"360\",\n        \"UN_A3\": \"360\",\n        \"WB_A2\": \"ID\",\n        \"WB_A3\": \"IDN\",\n        \"WOE_ID\": 23424846,\n        \"WOE_ID_EH\": 23424846,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"IDN\",\n        \"ADM0_A3_US\": \"IDN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [95.293026, -10.359987, 141.033852, 5.479821],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [120.715609, -10.239581],\n              [120.295014, -10.25865],\n              [118.967808, -9.557969],\n              [119.90031, -9.36134],\n              [120.425756, -9.665921],\n              [120.775502, -9.969675],\n              [120.715609, -10.239581]\n            ]\n          ],\n          [\n            [\n              [124.968682, -8.89279],\n              [125.07002, -9.089987],\n              [125.08852, -9.393173],\n              [124.43595, -10.140001],\n              [123.579982, -10.359987],\n              [123.459989, -10.239995],\n              [123.550009, -9.900016],\n              [123.980009, -9.290027],\n              [124.968682, -8.89279]\n            ]\n          ],\n          [\n            [\n              [117.900018, -8.095681],\n              [118.260616, -8.362383],\n              [118.87846, -8.280683],\n              [119.126507, -8.705825],\n              [117.970402, -8.906639],\n              [117.277731, -9.040895],\n              [116.740141, -9.032937],\n              [117.083737, -8.457158],\n              [117.632024, -8.449303],\n              [117.900018, -8.095681]\n            ]\n          ],\n          [\n            [\n              [122.903537, -8.094234],\n              [122.756983, -8.649808],\n              [121.254491, -8.933666],\n              [119.924391, -8.810418],\n              [119.920929, -8.444859],\n              [120.715092, -8.236965],\n              [121.341669, -8.53674],\n              [122.007365, -8.46062],\n              [122.903537, -8.094234]\n            ]\n          ],\n          [\n            [\n              [108.623479, -6.777674],\n              [110.539227, -6.877358],\n              [110.759576, -6.465186],\n              [112.614811, -6.946036],\n              [112.978768, -7.594213],\n              [114.478935, -7.776528],\n              [115.705527, -8.370807],\n              [114.564511, -8.751817],\n              [113.464734, -8.348947],\n              [112.559672, -8.376181],\n              [111.522061, -8.302129],\n              [110.58615, -8.122605],\n              [109.427667, -7.740664],\n              [108.693655, -7.6416],\n              [108.277763, -7.766657],\n              [106.454102, -7.3549],\n              [106.280624, -6.9249],\n              [105.365486, -6.851416],\n              [106.051646, -5.895919],\n              [107.265009, -5.954985],\n              [108.072091, -6.345762],\n              [108.486846, -6.421985],\n              [108.623479, -6.777674]\n            ]\n          ],\n          [\n            [\n              [134.724624, -6.214401],\n              [134.210134, -6.895238],\n              [134.112776, -6.142467],\n              [134.290336, -5.783058],\n              [134.499625, -5.445042],\n              [134.727002, -5.737582],\n              [134.724624, -6.214401]\n            ]\n          ],\n          [\n            [\n              [127.249215, -3.459065],\n              [126.874923, -3.790983],\n              [126.183802, -3.607376],\n              [125.989034, -3.177273],\n              [127.000651, -3.129318],\n              [127.249215, -3.459065]\n            ]\n          ],\n          [\n            [\n              [130.471344, -3.093764],\n              [130.834836, -3.858472],\n              [129.990547, -3.446301],\n              [129.155249, -3.362637],\n              [128.590684, -3.428679],\n              [127.898891, -3.393436],\n              [128.135879, -2.84365],\n              [129.370998, -2.802154],\n              [130.471344, -3.093764]\n            ]\n          ],\n          [\n            [\n              [141.00021, -2.600151],\n              [141.017057, -5.859022],\n              [141.033852, -9.117893],\n              [140.143415, -8.297168],\n              [139.127767, -8.096043],\n              [138.881477, -8.380935],\n              [137.614474, -8.411683],\n              [138.039099, -7.597882],\n              [138.668621, -7.320225],\n              [138.407914, -6.232849],\n              [137.92784, -5.393366],\n              [135.98925, -4.546544],\n              [135.164598, -4.462931],\n              [133.66288, -3.538853],\n              [133.367705, -4.024819],\n              [132.983956, -4.112979],\n              [132.756941, -3.746283],\n              [132.753789, -3.311787],\n              [131.989804, -2.820551],\n              [133.066845, -2.460418],\n              [133.780031, -2.479848],\n              [133.696212, -2.214542],\n              [132.232373, -2.212526],\n              [131.836222, -1.617162],\n              [130.94284, -1.432522],\n              [130.519558, -0.93772],\n              [131.867538, -0.695461],\n              [132.380116, -0.369538],\n              [133.985548, -0.78021],\n              [134.143368, -1.151867],\n              [134.422627, -2.769185],\n              [135.457603, -3.367753],\n              [136.293314, -2.307042],\n              [137.440738, -1.703513],\n              [138.329727, -1.702686],\n              [139.184921, -2.051296],\n              [139.926684, -2.409052],\n              [141.00021, -2.600151]\n            ]\n          ],\n          [\n            [\n              [125.240501, 1.419836],\n              [124.437035, 0.427881],\n              [123.685505, 0.235593],\n              [122.723083, 0.431137],\n              [121.056725, 0.381217],\n              [120.183083, 0.237247],\n              [120.04087, -0.519658],\n              [120.935905, -1.408906],\n              [121.475821, -0.955962],\n              [123.340565, -0.615673],\n              [123.258399, -1.076213],\n              [122.822715, -0.930951],\n              [122.38853, -1.516858],\n              [121.508274, -1.904483],\n              [122.454572, -3.186058],\n              [122.271896, -3.5295],\n              [123.170963, -4.683693],\n              [123.162333, -5.340604],\n              [122.628515, -5.634591],\n              [122.236394, -5.282933],\n              [122.719569, -4.464172],\n              [121.738234, -4.851331],\n              [121.489463, -4.574553],\n              [121.619171, -4.188478],\n              [120.898182, -3.602105],\n              [120.972389, -2.627643],\n              [120.305453, -2.931604],\n              [120.390047, -4.097579],\n              [120.430717, -5.528241],\n              [119.796543, -5.6734],\n              [119.366906, -5.379878],\n              [119.653606, -4.459417],\n              [119.498835, -3.494412],\n              [119.078344, -3.487022],\n              [118.767769, -2.801999],\n              [119.180974, -2.147104],\n              [119.323394, -1.353147],\n              [119.825999, 0.154254],\n              [120.035702, 0.566477],\n              [120.885779, 1.309223],\n              [121.666817, 1.013944],\n              [122.927567, 0.875192],\n              [124.077522, 0.917102],\n              [125.065989, 1.643259],\n              [125.240501, 1.419836]\n            ]\n          ],\n          [\n            [\n              [128.688249, 1.132386],\n              [128.635952, 0.258486],\n              [128.12017, 0.356413],\n              [127.968034, -0.252077],\n              [128.379999, -0.780004],\n              [128.100016, -0.899996],\n              [127.696475, -0.266598],\n              [127.39949, 1.011722],\n              [127.600512, 1.810691],\n              [127.932378, 2.174596],\n              [128.004156, 1.628531],\n              [128.594559, 1.540811],\n              [128.688249, 1.132386]\n            ]\n          ],\n          [\n            [\n              [109.66326, 2.006467],\n              [109.830227, 1.338136],\n              [110.514061, 0.773131],\n              [111.159138, 0.976478],\n              [111.797548, 0.904441],\n              [112.380252, 1.410121],\n              [112.859809, 1.49779],\n              [113.80585, 1.217549],\n              [114.621355, 1.430688],\n              [115.134037, 2.821482],\n              [115.519078, 3.169238],\n              [115.865517, 4.306559],\n              [117.015214, 4.306094],\n              [117.882035, 4.137551],\n              [117.313232, 3.234428],\n              [118.04833, 2.28769],\n              [117.875627, 1.827641],\n              [118.996747, 0.902219],\n              [117.811858, 0.784242],\n              [117.478339, 0.102475],\n              [117.521644, -0.803723],\n              [116.560048, -1.487661],\n              [116.533797, -2.483517],\n              [116.148084, -4.012726],\n              [116.000858, -3.657037],\n              [114.864803, -4.106984],\n              [114.468652, -3.495704],\n              [113.755672, -3.43917],\n              [113.256994, -3.118776],\n              [112.068126, -3.478392],\n              [111.703291, -2.994442],\n              [111.04824, -3.049426],\n              [110.223846, -2.934032],\n              [110.070936, -1.592874],\n              [109.571948, -1.314907],\n              [109.091874, -0.459507],\n              [108.952658, 0.415375],\n              [109.069136, 1.341934],\n              [109.66326, 2.006467]\n            ]\n          ],\n          [\n            [\n              [105.817655, -5.852356],\n              [104.710384, -5.873285],\n              [103.868213, -5.037315],\n              [102.584261, -4.220259],\n              [102.156173, -3.614146],\n              [101.399113, -2.799777],\n              [100.902503, -2.050262],\n              [100.141981, -0.650348],\n              [99.26374, 0.183142],\n              [98.970011, 1.042882],\n              [98.601351, 1.823507],\n              [97.699598, 2.453184],\n              [97.176942, 3.308791],\n              [96.424017, 3.86886],\n              [95.380876, 4.970782],\n              [95.293026, 5.479821],\n              [95.936863, 5.439513],\n              [97.484882, 5.246321],\n              [98.369169, 4.26837],\n              [99.142559, 3.59035],\n              [99.693998, 3.174329],\n              [100.641434, 2.099381],\n              [101.658012, 2.083697],\n              [102.498271, 1.3987],\n              [103.07684, 0.561361],\n              [103.838396, 0.104542],\n              [103.437645, -0.711946],\n              [104.010789, -1.059212],\n              [104.369991, -1.084843],\n              [104.53949, -1.782372],\n              [104.887893, -2.340425],\n              [105.622111, -2.428844],\n              [106.108593, -3.061777],\n              [105.857446, -4.305525],\n              [105.817655, -5.852356]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"India\",\n        \"SOV_A3\": \"IND\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"India\",\n        \"ADM0_A3\": \"IND\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"India\",\n        \"GU_A3\": \"IND\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"India\",\n        \"SU_A3\": \"IND\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"India\",\n        \"NAME_LONG\": \"India\",\n        \"BRK_A3\": \"IND\",\n        \"BRK_NAME\": \"India\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"India\",\n        \"POSTAL\": \"IND\",\n        \"FORMAL_EN\": \"Republic of India\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"India\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"India\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 1281935911,\n        \"POP_RANK\": 18,\n        \"GDP_MD_EST\": 8721000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"3. Emerging region: BRIC\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"IN\",\n        \"ISO_A2\": \"IN\",\n        \"ISO_A3\": \"IND\",\n        \"ISO_A3_EH\": \"IND\",\n        \"ISO_N3\": \"356\",\n        \"UN_A3\": \"356\",\n        \"WB_A2\": \"IN\",\n        \"WB_A3\": \"IND\",\n        \"WOE_ID\": 23424848,\n        \"WOE_ID_EH\": 23424848,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"IND\",\n        \"ADM0_A3_US\": \"IND\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [68.176645, 7.965535, 97.402561, 35.49401],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [92.672721, 22.041239],\n            [92.146035, 23.627499],\n            [91.869928, 23.624346],\n            [91.706475, 22.985264],\n            [91.158963, 23.503527],\n            [91.46773, 24.072639],\n            [91.915093, 24.130414],\n            [92.376202, 24.976693],\n            [91.799596, 25.147432],\n            [90.872211, 25.132601],\n            [89.920693, 25.26975],\n            [89.832481, 25.965082],\n            [89.355094, 26.014407],\n            [88.563049, 26.446526],\n            [88.209789, 25.768066],\n            [88.931554, 25.238692],\n            [88.306373, 24.866079],\n            [88.084422, 24.501657],\n            [88.69994, 24.233715],\n            [88.52977, 23.631142],\n            [88.876312, 22.879146],\n            [89.031961, 22.055708],\n            [88.888766, 21.690588],\n            [88.208497, 21.703172],\n            [86.975704, 21.495562],\n            [87.033169, 20.743308],\n            [86.499351, 20.151638],\n            [85.060266, 19.478579],\n            [83.941006, 18.30201],\n            [83.189217, 17.671221],\n            [82.192792, 17.016636],\n            [82.191242, 16.556664],\n            [81.692719, 16.310219],\n            [80.791999, 15.951972],\n            [80.324896, 15.899185],\n            [80.025069, 15.136415],\n            [80.233274, 13.835771],\n            [80.286294, 13.006261],\n            [79.862547, 12.056215],\n            [79.857999, 10.357275],\n            [79.340512, 10.308854],\n            [78.885345, 9.546136],\n            [79.18972, 9.216544],\n            [78.277941, 8.933047],\n            [77.941165, 8.252959],\n            [77.539898, 7.965535],\n            [76.592979, 8.899276],\n            [76.130061, 10.29963],\n            [75.746467, 11.308251],\n            [75.396101, 11.781245],\n            [74.864816, 12.741936],\n            [74.616717, 13.992583],\n            [74.443859, 14.617222],\n            [73.534199, 15.990652],\n            [73.119909, 17.92857],\n            [72.820909, 19.208234],\n            [72.824475, 20.419503],\n            [72.630533, 21.356009],\n            [71.175273, 20.757441],\n            [70.470459, 20.877331],\n            [69.16413, 22.089298],\n            [69.644928, 22.450775],\n            [69.349597, 22.84318],\n            [68.176645, 23.691965],\n            [68.842599, 24.359134],\n            [71.04324, 24.356524],\n            [70.844699, 25.215102],\n            [70.282873, 25.722229],\n            [70.168927, 26.491872],\n            [69.514393, 26.940966],\n            [70.616496, 27.989196],\n            [71.777666, 27.91318],\n            [72.823752, 28.961592],\n            [73.450638, 29.976413],\n            [74.42138, 30.979815],\n            [74.405929, 31.692639],\n            [75.258642, 32.271105],\n            [74.451559, 32.7649],\n            [74.104294, 33.441473],\n            [73.749948, 34.317699],\n            [74.240203, 34.748887],\n            [75.757061, 34.504923],\n            [76.871722, 34.653544],\n            [77.837451, 35.49401],\n            [78.912269, 34.321936],\n            [78.811086, 33.506198],\n            [79.208892, 32.994395],\n            [79.176129, 32.48378],\n            [78.458446, 32.618164],\n            [78.738894, 31.515906],\n            [79.721367, 30.882715],\n            [81.111256, 30.183481],\n            [80.476721, 29.729865],\n            [80.088425, 28.79447],\n            [81.057203, 28.416095],\n            [81.999987, 27.925479],\n            [83.304249, 27.364506],\n            [84.675018, 27.234901],\n            [85.251779, 26.726198],\n            [86.024393, 26.630985],\n            [87.227472, 26.397898],\n            [88.060238, 26.414615],\n            [88.174804, 26.810405],\n            [88.043133, 27.445819],\n            [88.120441, 27.876542],\n            [88.730326, 28.086865],\n            [88.814248, 27.299316],\n            [88.835643, 27.098966],\n            [89.744528, 26.719403],\n            [90.373275, 26.875724],\n            [91.217513, 26.808648],\n            [92.033484, 26.83831],\n            [92.103712, 27.452614],\n            [91.696657, 27.771742],\n            [92.503119, 27.896876],\n            [93.413348, 28.640629],\n            [94.56599, 29.277438],\n            [95.404802, 29.031717],\n            [96.117679, 29.452802],\n            [96.586591, 28.83098],\n            [96.248833, 28.411031],\n            [97.327114, 28.261583],\n            [97.402561, 27.882536],\n            [97.051989, 27.699059],\n            [97.133999, 27.083774],\n            [96.419366, 27.264589],\n            [95.124768, 26.573572],\n            [95.155153, 26.001307],\n            [94.603249, 25.162495],\n            [94.552658, 24.675238],\n            [94.106742, 23.850741],\n            [93.325188, 24.078556],\n            [93.286327, 23.043658],\n            [93.060294, 22.703111],\n            [93.166128, 22.27846],\n            [92.672721, 22.041239]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Ireland\",\n        \"SOV_A3\": \"IRL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Ireland\",\n        \"ADM0_A3\": \"IRL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Ireland\",\n        \"GU_A3\": \"IRL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Ireland\",\n        \"SU_A3\": \"IRL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Ireland\",\n        \"NAME_LONG\": \"Ireland\",\n        \"BRK_A3\": \"IRL\",\n        \"BRK_NAME\": \"Ireland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ire.\",\n        \"POSTAL\": \"IRL\",\n        \"FORMAL_EN\": \"Ireland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Ireland\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Ireland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 5011102,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 322000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"EI\",\n        \"ISO_A2\": \"IE\",\n        \"ISO_A3\": \"IRL\",\n        \"ISO_A3_EH\": \"IRL\",\n        \"ISO_N3\": \"372\",\n        \"UN_A3\": \"372\",\n        \"WB_A2\": \"IE\",\n        \"WB_A3\": \"IRL\",\n        \"WOE_ID\": 23424803,\n        \"WOE_ID_EH\": 23424803,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"IRL\",\n        \"ADM0_A3_US\": \"IRL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-9.977086, 51.669301, -6.032985, 55.131622],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-7.572168, 55.131622],\n            [-7.366031, 54.595841],\n            [-7.572168, 54.059956],\n            [-6.95373, 54.073702],\n            [-6.197885, 53.867565],\n            [-6.032985, 53.153164],\n            [-6.788857, 52.260118],\n            [-8.561617, 51.669301],\n            [-9.977086, 51.820455],\n            [-9.166283, 52.864629],\n            [-9.688525, 53.881363],\n            [-8.327987, 54.664519],\n            [-7.572168, 55.131622]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Iran\",\n        \"SOV_A3\": \"IRN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Iran\",\n        \"ADM0_A3\": \"IRN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Iran\",\n        \"GU_A3\": \"IRN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Iran\",\n        \"SU_A3\": \"IRN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Iran\",\n        \"NAME_LONG\": \"Iran\",\n        \"BRK_A3\": \"IRN\",\n        \"BRK_NAME\": \"Iran\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Iran\",\n        \"POSTAL\": \"IRN\",\n        \"FORMAL_EN\": \"Islamic Republic of Iran\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Iran\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Iran, Islamic Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 82021564,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 1459000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"IR\",\n        \"ISO_A2\": \"IR\",\n        \"ISO_A3\": \"IRN\",\n        \"ISO_A3_EH\": \"IRN\",\n        \"ISO_N3\": \"364\",\n        \"UN_A3\": \"364\",\n        \"WB_A2\": \"IR\",\n        \"WB_A3\": \"IRN\",\n        \"WOE_ID\": 23424851,\n        \"WOE_ID_EH\": 23424851,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"IRN\",\n        \"ADM0_A3_US\": \"IRN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2.5,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [44.109225, 25.078237, 63.316632, 39.713003],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [61.210817, 35.650072],\n            [60.803193, 34.404102],\n            [60.52843, 33.676446],\n            [60.9637, 33.528832],\n            [60.536078, 32.981269],\n            [60.863655, 32.18292],\n            [60.941945, 31.548075],\n            [61.699314, 31.379506],\n            [61.781222, 30.73585],\n            [60.874248, 29.829239],\n            [61.369309, 29.303276],\n            [61.771868, 28.699334],\n            [62.72783, 28.259645],\n            [62.755426, 27.378923],\n            [63.233898, 27.217047],\n            [63.316632, 26.756532],\n            [61.874187, 26.239975],\n            [61.497363, 25.078237],\n            [59.616134, 25.380157],\n            [58.525761, 25.609962],\n            [57.397251, 25.739902],\n            [56.970766, 26.966106],\n            [56.492139, 27.143305],\n            [55.72371, 26.964633],\n            [54.71509, 26.480658],\n            [53.493097, 26.812369],\n            [52.483598, 27.580849],\n            [51.520763, 27.86569],\n            [50.852948, 28.814521],\n            [50.115009, 30.147773],\n            [49.57685, 29.985715],\n            [48.941333, 30.31709],\n            [48.567971, 29.926778],\n            [48.014568, 30.452457],\n            [48.004698, 30.985137],\n            [47.685286, 30.984853],\n            [47.849204, 31.709176],\n            [47.334661, 32.469155],\n            [46.109362, 33.017287],\n            [45.416691, 33.967798],\n            [45.64846, 34.748138],\n            [46.151788, 35.093259],\n            [46.07634, 35.677383],\n            [45.420618, 35.977546],\n            [44.772677, 37.170437],\n            [44.77267, 37.17045],\n            [44.225756, 37.971584],\n            [44.421403, 38.281281],\n            [44.109225, 39.428136],\n            [44.79399, 39.713003],\n            [44.952688, 39.335765],\n            [45.457722, 38.874139],\n            [46.143623, 38.741201],\n            [46.50572, 38.770605],\n            [47.685079, 39.508364],\n            [48.060095, 39.582235],\n            [48.355529, 39.288765],\n            [48.010744, 38.794015],\n            [48.634375, 38.270378],\n            [48.883249, 38.320245],\n            [49.199612, 37.582874],\n            [50.147771, 37.374567],\n            [50.842354, 36.872814],\n            [52.264025, 36.700422],\n            [53.82579, 36.965031],\n            [53.921598, 37.198918],\n            [54.800304, 37.392421],\n            [55.511578, 37.964117],\n            [56.180375, 37.935127],\n            [56.619366, 38.121394],\n            [57.330434, 38.029229],\n            [58.436154, 37.522309],\n            [59.234762, 37.412988],\n            [60.377638, 36.527383],\n            [61.123071, 36.491597],\n            [61.210817, 35.650072]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Iraq\",\n        \"SOV_A3\": \"IRQ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Iraq\",\n        \"ADM0_A3\": \"IRQ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Iraq\",\n        \"GU_A3\": \"IRQ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Iraq\",\n        \"SU_A3\": \"IRQ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Iraq\",\n        \"NAME_LONG\": \"Iraq\",\n        \"BRK_A3\": \"IRQ\",\n        \"BRK_NAME\": \"Iraq\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Iraq\",\n        \"POSTAL\": \"IRQ\",\n        \"FORMAL_EN\": \"Republic of Iraq\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Iraq\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Iraq\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 39192111,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 596700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1997,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"IZ\",\n        \"ISO_A2\": \"IQ\",\n        \"ISO_A3\": \"IRQ\",\n        \"ISO_A3_EH\": \"IRQ\",\n        \"ISO_N3\": \"368\",\n        \"UN_A3\": \"368\",\n        \"WB_A2\": \"IQ\",\n        \"WB_A3\": \"IRQ\",\n        \"WOE_ID\": 23424855,\n        \"WOE_ID_EH\": 23424855,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"IRQ\",\n        \"ADM0_A3_US\": \"IRQ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7.5\n      },\n      \"bbox\": [38.792341, 29.099025, 48.567971, 37.385264],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [44.772677, 37.170437],\n            [45.420618, 35.977546],\n            [46.07634, 35.677383],\n            [46.151788, 35.093259],\n            [45.64846, 34.748138],\n            [45.416691, 33.967798],\n            [46.109362, 33.017287],\n            [47.334661, 32.469155],\n            [47.849204, 31.709176],\n            [47.685286, 30.984853],\n            [48.004698, 30.985137],\n            [48.014568, 30.452457],\n            [48.567971, 29.926778],\n            [47.974519, 29.975819],\n            [47.302622, 30.05907],\n            [46.568713, 29.099025],\n            [44.709499, 29.178891],\n            [41.889981, 31.190009],\n            [40.399994, 31.889992],\n            [39.195468, 32.161009],\n            [38.792341, 33.378686],\n            [41.006159, 34.419372],\n            [41.383965, 35.628317],\n            [41.289707, 36.358815],\n            [41.837064, 36.605854],\n            [42.349591, 37.229873],\n            [42.779126, 37.385264],\n            [43.942259, 37.256228],\n            [44.293452, 37.001514],\n            [44.772677, 37.170437]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Iceland\",\n        \"SOV_A3\": \"ISL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Iceland\",\n        \"ADM0_A3\": \"ISL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Iceland\",\n        \"GU_A3\": \"ISL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Iceland\",\n        \"SU_A3\": \"ISL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Iceland\",\n        \"NAME_LONG\": \"Iceland\",\n        \"BRK_A3\": \"ISL\",\n        \"BRK_NAME\": \"Iceland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Iceland\",\n        \"POSTAL\": \"IS\",\n        \"FORMAL_EN\": \"Republic of Iceland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Iceland\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Iceland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 339747,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 16150,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"IC\",\n        \"ISO_A2\": \"IS\",\n        \"ISO_A3\": \"ISL\",\n        \"ISO_A3_EH\": \"ISL\",\n        \"ISO_N3\": \"352\",\n        \"UN_A3\": \"352\",\n        \"WB_A2\": \"IS\",\n        \"WB_A3\": \"ISL\",\n        \"WOE_ID\": 23424845,\n        \"WOE_ID_EH\": 23424845,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ISL\",\n        \"ADM0_A3_US\": \"ISL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 7,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-24.326184, 63.496383, -13.609732, 66.526792],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-14.508695, 66.455892],\n            [-14.739637, 65.808748],\n            [-13.609732, 65.126671],\n            [-14.909834, 64.364082],\n            [-17.794438, 63.678749],\n            [-18.656246, 63.496383],\n            [-19.972755, 63.643635],\n            [-22.762972, 63.960179],\n            [-21.778484, 64.402116],\n            [-23.955044, 64.89113],\n            [-22.184403, 65.084968],\n            [-22.227423, 65.378594],\n            [-24.326184, 65.611189],\n            [-23.650515, 66.262519],\n            [-22.134922, 66.410469],\n            [-20.576284, 65.732112],\n            [-19.056842, 66.276601],\n            [-17.798624, 65.993853],\n            [-16.167819, 66.526792],\n            [-14.508695, 66.455892]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Israel\",\n        \"SOV_A3\": \"IS1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"Israel\",\n        \"ADM0_A3\": \"ISR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Israel\",\n        \"GU_A3\": \"ISR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Israel\",\n        \"SU_A3\": \"ISR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Israel\",\n        \"NAME_LONG\": \"Israel\",\n        \"BRK_A3\": \"ISR\",\n        \"BRK_NAME\": \"Israel\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Isr.\",\n        \"POSTAL\": \"IS\",\n        \"FORMAL_EN\": \"State of Israel\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Israel\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Israel\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 8299706,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 297000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"-99\",\n        \"ISO_A2\": \"IL\",\n        \"ISO_A3\": \"ISR\",\n        \"ISO_A3_EH\": \"ISR\",\n        \"ISO_N3\": \"376\",\n        \"UN_A3\": \"376\",\n        \"WB_A2\": \"IL\",\n        \"WB_A3\": \"ISR\",\n        \"WOE_ID\": 23424852,\n        \"WOE_ID_EH\": 23424852,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ISR\",\n        \"ADM0_A3_US\": \"ISR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [34.265433, 29.501326, 35.836397, 33.277426],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [34.823243, 29.761081],\n            [34.26544, 31.21936],\n            [34.265435, 31.219357],\n            [34.265433, 31.219361],\n            [34.556372, 31.548824],\n            [34.488107, 31.605539],\n            [34.752587, 32.072926],\n            [34.955417, 32.827376],\n            [35.098457, 33.080539],\n            [35.126053, 33.0909],\n            [35.460709, 33.08904],\n            [35.552797, 33.264275],\n            [35.821101, 33.277426],\n            [35.836397, 32.868123],\n            [35.700798, 32.716014],\n            [35.719918, 32.709192],\n            [35.545665, 32.393992],\n            [35.18393, 32.532511],\n            [34.974641, 31.866582],\n            [35.225892, 31.754341],\n            [34.970507, 31.616778],\n            [34.927408, 31.353435],\n            [35.397561, 31.489086],\n            [35.420918, 31.100066],\n            [34.922603, 29.501326],\n            [34.823243, 29.761081]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Italy\",\n        \"SOV_A3\": \"ITA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Italy\",\n        \"ADM0_A3\": \"ITA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Italy\",\n        \"GU_A3\": \"ITA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Italy\",\n        \"SU_A3\": \"ITA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Italy\",\n        \"NAME_LONG\": \"Italy\",\n        \"BRK_A3\": \"ITA\",\n        \"BRK_NAME\": \"Italy\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Italy\",\n        \"POSTAL\": \"I\",\n        \"FORMAL_EN\": \"Italian Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Italy\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Italy\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 8,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 62137802,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 2221000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2012,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"IT\",\n        \"ISO_A2\": \"IT\",\n        \"ISO_A3\": \"ITA\",\n        \"ISO_A3_EH\": \"ITA\",\n        \"ISO_N3\": \"380\",\n        \"UN_A3\": \"380\",\n        \"WB_A2\": \"IT\",\n        \"WB_A3\": \"ITA\",\n        \"WOE_ID\": 23424853,\n        \"WOE_ID_EH\": 23424853,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ITA\",\n        \"ADM0_A3_US\": \"ITA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [6.749955, 36.619987, 18.480247, 47.115393],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [10.442701, 46.893546],\n              [11.048556, 46.751359],\n              [11.164828, 46.941579],\n              [12.153088, 47.115393],\n              [12.376485, 46.767559],\n              [13.806475, 46.509306],\n              [13.69811, 46.016778],\n              [13.93763, 45.591016],\n              [13.141606, 45.736692],\n              [12.328581, 45.381778],\n              [12.383875, 44.885374],\n              [12.261453, 44.600482],\n              [12.589237, 44.091366],\n              [13.526906, 43.587727],\n              [14.029821, 42.761008],\n              [15.14257, 41.95514],\n              [15.926191, 41.961315],\n              [16.169897, 41.740295],\n              [15.889346, 41.541082],\n              [16.785002, 41.179606],\n              [17.519169, 40.877143],\n              [18.376687, 40.355625],\n              [18.480247, 40.168866],\n              [18.293385, 39.810774],\n              [17.73838, 40.277671],\n              [16.869596, 40.442235],\n              [16.448743, 39.795401],\n              [17.17149, 39.4247],\n              [17.052841, 38.902871],\n              [16.635088, 38.843572],\n              [16.100961, 37.985899],\n              [15.684087, 37.908849],\n              [15.687963, 38.214593],\n              [15.891981, 38.750942],\n              [16.109332, 38.964547],\n              [15.718814, 39.544072],\n              [15.413613, 40.048357],\n              [14.998496, 40.172949],\n              [14.703268, 40.60455],\n              [14.060672, 40.786348],\n              [13.627985, 41.188287],\n              [12.888082, 41.25309],\n              [12.106683, 41.704535],\n              [11.191906, 42.355425],\n              [10.511948, 42.931463],\n              [10.200029, 43.920007],\n              [9.702488, 44.036279],\n              [8.888946, 44.366336],\n              [8.428561, 44.231228],\n              [7.850767, 43.767148],\n              [7.435185, 43.693845],\n              [7.549596, 44.127901],\n              [7.007562, 44.254767],\n              [6.749955, 45.028518],\n              [7.096652, 45.333099],\n              [6.802355, 45.70858],\n              [6.843593, 45.991147],\n              [7.273851, 45.776948],\n              [7.755992, 45.82449],\n              [8.31663, 46.163642],\n              [8.489952, 46.005151],\n              [8.966306, 46.036932],\n              [9.182882, 46.440215],\n              [9.922837, 46.314899],\n              [10.363378, 46.483571],\n              [10.442701, 46.893546]\n            ]\n          ],\n          [\n            [\n              [15.520376, 38.231155],\n              [15.160243, 37.444046],\n              [15.309898, 37.134219],\n              [15.099988, 36.619987],\n              [14.335229, 36.996631],\n              [13.826733, 37.104531],\n              [12.431004, 37.61295],\n              [12.570944, 38.126381],\n              [13.741156, 38.034966],\n              [14.761249, 38.143874],\n              [15.520376, 38.231155]\n            ]\n          ],\n          [\n            [\n              [9.210012, 41.209991],\n              [9.809975, 40.500009],\n              [9.669519, 39.177376],\n              [9.214818, 39.240473],\n              [8.806936, 38.906618],\n              [8.428302, 39.171847],\n              [8.388253, 40.378311],\n              [8.159998, 40.950007],\n              [8.709991, 40.899984],\n              [9.210012, 41.209991]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Jamaica\",\n        \"SOV_A3\": \"JAM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Jamaica\",\n        \"ADM0_A3\": \"JAM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Jamaica\",\n        \"GU_A3\": \"JAM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Jamaica\",\n        \"SU_A3\": \"JAM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Jamaica\",\n        \"NAME_LONG\": \"Jamaica\",\n        \"BRK_A3\": \"JAM\",\n        \"BRK_NAME\": \"Jamaica\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Jam.\",\n        \"POSTAL\": \"J\",\n        \"FORMAL_EN\": \"Jamaica\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Jamaica\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Jamaica\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 2990561,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 25390,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"JM\",\n        \"ISO_A2\": \"JM\",\n        \"ISO_A3\": \"JAM\",\n        \"ISO_A3_EH\": \"JAM\",\n        \"ISO_N3\": \"388\",\n        \"UN_A3\": \"388\",\n        \"WB_A2\": \"JM\",\n        \"WB_A3\": \"JAM\",\n        \"WOE_ID\": 23424858,\n        \"WOE_ID_EH\": 23424858,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"JAM\",\n        \"ADM0_A3_US\": \"JAM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-78.337719, 17.701116, -76.199659, 18.524218],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-77.569601, 18.490525],\n            [-76.896619, 18.400867],\n            [-76.365359, 18.160701],\n            [-76.199659, 17.886867],\n            [-76.902561, 17.868238],\n            [-77.206341, 17.701116],\n            [-77.766023, 17.861597],\n            [-78.337719, 18.225968],\n            [-78.217727, 18.454533],\n            [-77.797365, 18.524218],\n            [-77.569601, 18.490525]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Jordan\",\n        \"SOV_A3\": \"JOR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Jordan\",\n        \"ADM0_A3\": \"JOR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Jordan\",\n        \"GU_A3\": \"JOR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Jordan\",\n        \"SU_A3\": \"JOR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Jordan\",\n        \"NAME_LONG\": \"Jordan\",\n        \"BRK_A3\": \"JOR\",\n        \"BRK_NAME\": \"Jordan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Jord.\",\n        \"POSTAL\": \"J\",\n        \"FORMAL_EN\": \"Hashemite Kingdom of Jordan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Jordan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Jordan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 10248069,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 86190,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"JO\",\n        \"ISO_A2\": \"JO\",\n        \"ISO_A3\": \"JOR\",\n        \"ISO_A3_EH\": \"JOR\",\n        \"ISO_N3\": \"400\",\n        \"UN_A3\": \"400\",\n        \"WB_A2\": \"JO\",\n        \"WB_A3\": \"JOR\",\n        \"WOE_ID\": 23424860,\n        \"WOE_ID_EH\": 23424860,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"JOR\",\n        \"ADM0_A3_US\": \"JOR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [34.922603, 29.197495, 39.195468, 33.378686],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [38.792341, 33.378686],\n            [39.195468, 32.161009],\n            [39.004886, 32.010217],\n            [37.002166, 31.508413],\n            [37.998849, 30.5085],\n            [37.66812, 30.338665],\n            [37.503582, 30.003776],\n            [36.740528, 29.865283],\n            [36.501214, 29.505254],\n            [36.068941, 29.197495],\n            [34.956037, 29.356555],\n            [34.922603, 29.501326],\n            [35.420918, 31.100066],\n            [35.397561, 31.489086],\n            [35.545252, 31.782505],\n            [35.545665, 32.393992],\n            [35.719918, 32.709192],\n            [36.834062, 32.312938],\n            [38.792341, 33.378686]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Japan\",\n        \"SOV_A3\": \"JPN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Japan\",\n        \"ADM0_A3\": \"JPN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Japan\",\n        \"GU_A3\": \"JPN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Japan\",\n        \"SU_A3\": \"JPN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Japan\",\n        \"NAME_LONG\": \"Japan\",\n        \"BRK_A3\": \"JPN\",\n        \"BRK_NAME\": \"Japan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Japan\",\n        \"POSTAL\": \"J\",\n        \"FORMAL_EN\": \"Japan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Japan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Japan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 126451398,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 4932000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"JA\",\n        \"ISO_A2\": \"JP\",\n        \"ISO_A3\": \"JPN\",\n        \"ISO_A3_EH\": \"JPN\",\n        \"ISO_N3\": \"392\",\n        \"UN_A3\": \"392\",\n        \"WB_A2\": \"JP\",\n        \"WB_A3\": \"JPN\",\n        \"WOE_ID\": 23424856,\n        \"WOE_ID_EH\": 23424856,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"JPN\",\n        \"ADM0_A3_US\": \"JPN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [129.408463, 31.029579, 145.543137, 45.551483],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [134.638428, 34.149234],\n              [134.766379, 33.806335],\n              [134.203416, 33.201178],\n              [133.79295, 33.521985],\n              [133.280268, 33.28957],\n              [133.014858, 32.704567],\n              [132.363115, 32.989382],\n              [132.371176, 33.463642],\n              [132.924373, 34.060299],\n              [133.492968, 33.944621],\n              [133.904106, 34.364931],\n              [134.638428, 34.149234]\n            ]\n          ],\n          [\n            [\n              [140.976388, 37.142074],\n              [140.59977, 36.343983],\n              [140.774074, 35.842877],\n              [140.253279, 35.138114],\n              [138.975528, 34.6676],\n              [137.217599, 34.606286],\n              [135.792983, 33.464805],\n              [135.120983, 33.849071],\n              [135.079435, 34.596545],\n              [133.340316, 34.375938],\n              [132.156771, 33.904933],\n              [130.986145, 33.885761],\n              [132.000036, 33.149992],\n              [131.33279, 31.450355],\n              [130.686318, 31.029579],\n              [130.20242, 31.418238],\n              [130.447676, 32.319475],\n              [129.814692, 32.61031],\n              [129.408463, 33.296056],\n              [130.353935, 33.604151],\n              [130.878451, 34.232743],\n              [131.884229, 34.749714],\n              [132.617673, 35.433393],\n              [134.608301, 35.731618],\n              [135.677538, 35.527134],\n              [136.723831, 37.304984],\n              [137.390612, 36.827391],\n              [138.857602, 37.827485],\n              [139.426405, 38.215962],\n              [140.05479, 39.438807],\n              [139.883379, 40.563312],\n              [140.305783, 41.195005],\n              [141.368973, 41.37856],\n              [141.914263, 39.991616],\n              [141.884601, 39.180865],\n              [140.959489, 38.174001],\n              [140.976388, 37.142074]\n            ]\n          ],\n          [\n            [\n              [143.910162, 44.1741],\n              [144.613427, 43.960883],\n              [145.320825, 44.384733],\n              [145.543137, 43.262088],\n              [144.059662, 42.988358],\n              [143.18385, 41.995215],\n              [141.611491, 42.678791],\n              [141.067286, 41.584594],\n              [139.955106, 41.569556],\n              [139.817544, 42.563759],\n              [140.312087, 43.333273],\n              [141.380549, 43.388825],\n              [141.671952, 44.772125],\n              [141.967645, 45.551483],\n              [143.14287, 44.510358],\n              [143.910162, 44.1741]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Kazakhstan\",\n        \"SOV_A3\": \"KAZ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Kazakhstan\",\n        \"ADM0_A3\": \"KAZ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Kazakhstan\",\n        \"GU_A3\": \"KAZ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Kazakhstan\",\n        \"SU_A3\": \"KAZ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Kazakhstan\",\n        \"NAME_LONG\": \"Kazakhstan\",\n        \"BRK_A3\": \"KAZ\",\n        \"BRK_NAME\": \"Kazakhstan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Kaz.\",\n        \"POSTAL\": \"KZ\",\n        \"FORMAL_EN\": \"Republic of Kazakhstan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Kazakhstan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Kazakhstan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 18556698,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 460700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KZ\",\n        \"ISO_A2\": \"KZ\",\n        \"ISO_A3\": \"KAZ\",\n        \"ISO_A3_EH\": \"KAZ\",\n        \"ISO_N3\": \"398\",\n        \"UN_A3\": \"398\",\n        \"WB_A2\": \"KZ\",\n        \"WB_A3\": \"KAZ\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424871,\n        \"WOE_NOTE\": \"Includes Baykonur Cosmodrome as an admin-1\",\n        \"ADM0_A3_IS\": \"KAZ\",\n        \"ADM0_A3_US\": \"KAZ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Central Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [46.466446, 40.662325, 87.35997, 55.38525],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [87.35997, 49.214981],\n            [86.598776, 48.549182],\n            [85.768233, 48.455751],\n            [85.720484, 47.452969],\n            [85.16429, 47.000956],\n            [83.180484, 47.330031],\n            [82.458926, 45.53965],\n            [81.947071, 45.317027],\n            [79.966106, 44.917517],\n            [80.866206, 43.180362],\n            [80.18015, 42.920068],\n            [80.25999, 42.349999],\n            [79.643645, 42.496683],\n            [79.142177, 42.856092],\n            [77.658392, 42.960686],\n            [76.000354, 42.988022],\n            [75.636965, 42.8779],\n            [74.212866, 43.298339],\n            [73.645304, 43.091272],\n            [73.489758, 42.500894],\n            [71.844638, 42.845395],\n            [71.186281, 42.704293],\n            [70.962315, 42.266154],\n            [70.388965, 42.081308],\n            [69.070027, 41.384244],\n            [68.632483, 40.668681],\n            [68.259896, 40.662325],\n            [67.985856, 41.135991],\n            [66.714047, 41.168444],\n            [66.510649, 41.987644],\n            [66.023392, 41.994646],\n            [66.098012, 42.99766],\n            [64.900824, 43.728081],\n            [63.185787, 43.650075],\n            [62.0133, 43.504477],\n            [61.05832, 44.405817],\n            [60.239972, 44.784037],\n            [58.689989, 45.500014],\n            [58.503127, 45.586804],\n            [55.928917, 44.995858],\n            [55.968191, 41.308642],\n            [55.455251, 41.259859],\n            [54.755345, 42.043971],\n            [54.079418, 42.324109],\n            [52.944293, 42.116034],\n            [52.50246, 41.783316],\n            [52.446339, 42.027151],\n            [52.692112, 42.443895],\n            [52.501426, 42.792298],\n            [51.342427, 43.132975],\n            [50.891292, 44.031034],\n            [50.339129, 44.284016],\n            [50.305643, 44.609836],\n            [51.278503, 44.514854],\n            [51.316899, 45.245998],\n            [52.16739, 45.408391],\n            [53.040876, 45.259047],\n            [53.220866, 46.234646],\n            [53.042737, 46.853006],\n            [52.042023, 46.804637],\n            [51.191945, 47.048705],\n            [50.034083, 46.60899],\n            [49.10116, 46.39933],\n            [48.59325, 46.56104],\n            [48.694734, 47.075628],\n            [48.05725, 47.74377],\n            [47.31524, 47.71585],\n            [46.466446, 48.394152],\n            [47.043672, 49.152039],\n            [46.751596, 49.356006],\n            [47.54948, 50.454698],\n            [48.577841, 49.87476],\n            [48.702382, 50.605128],\n            [50.766648, 51.692762],\n            [52.328724, 51.718652],\n            [54.532878, 51.02624],\n            [55.71694, 50.62171],\n            [56.77798, 51.04355],\n            [58.36332, 51.06364],\n            [59.642282, 50.545442],\n            [59.932807, 50.842194],\n            [61.337424, 50.79907],\n            [61.588003, 51.272659],\n            [59.967534, 51.96042],\n            [60.927269, 52.447548],\n            [60.739993, 52.719986],\n            [61.699986, 52.979996],\n            [60.978066, 53.664993],\n            [61.4366, 54.00625],\n            [65.178534, 54.354228],\n            [65.66687, 54.60125],\n            [68.1691, 54.970392],\n            [69.068167, 55.38525],\n            [70.865267, 55.169734],\n            [71.180131, 54.133285],\n            [72.22415, 54.376655],\n            [73.508516, 54.035617],\n            [73.425679, 53.48981],\n            [74.38482, 53.54685],\n            [76.8911, 54.490524],\n            [76.525179, 54.177003],\n            [77.800916, 53.404415],\n            [80.03556, 50.864751],\n            [80.568447, 51.388336],\n            [81.945986, 50.812196],\n            [83.383004, 51.069183],\n            [83.935115, 50.889246],\n            [84.416377, 50.3114],\n            [85.11556, 50.117303],\n            [85.54127, 49.692859],\n            [86.829357, 49.826675],\n            [87.35997, 49.214981]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Kenya\",\n        \"SOV_A3\": \"KEN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Kenya\",\n        \"ADM0_A3\": \"KEN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Kenya\",\n        \"GU_A3\": \"KEN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Kenya\",\n        \"SU_A3\": \"KEN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Kenya\",\n        \"NAME_LONG\": \"Kenya\",\n        \"BRK_A3\": \"KEN\",\n        \"BRK_NAME\": \"Kenya\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ken.\",\n        \"POSTAL\": \"KE\",\n        \"FORMAL_EN\": \"Republic of Kenya\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Kenya\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Kenya\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 47615739,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 152700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KE\",\n        \"ISO_A2\": \"KE\",\n        \"ISO_A3\": \"KEN\",\n        \"ISO_A3_EH\": \"KEN\",\n        \"ISO_N3\": \"404\",\n        \"UN_A3\": \"404\",\n        \"WB_A2\": \"KE\",\n        \"WB_A3\": \"KEN\",\n        \"WOE_ID\": 23424863,\n        \"WOE_ID_EH\": 23424863,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"KEN\",\n        \"ADM0_A3_US\": \"KEN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [33.893569, -4.67677, 41.855083, 5.506],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [35.298007, 5.506],\n            [35.817448, 5.338232],\n            [35.817448, 4.776966],\n            [36.159079, 4.447864],\n            [36.855093, 4.447864],\n            [38.120915, 3.598605],\n            [38.43697, 3.58851],\n            [38.67114, 3.61607],\n            [38.89251, 3.50074],\n            [39.559384, 3.42206],\n            [39.85494, 3.83879],\n            [40.76848, 4.25702],\n            [41.1718, 3.91909],\n            [41.855083, 3.918912],\n            [40.98105, 2.78452],\n            [40.993, -0.85829],\n            [41.58513, -1.68325],\n            [40.88477, -2.08255],\n            [40.63785, -2.49979],\n            [40.26304, -2.57309],\n            [40.12119, -3.27768],\n            [39.80006, -3.68116],\n            [39.60489, -4.34653],\n            [39.20222, -4.67677],\n            [37.7669, -3.67712],\n            [37.69869, -3.09699],\n            [34.07262, -1.05982],\n            [33.903711, -0.95],\n            [33.893569, 0.109814],\n            [34.18, 0.515],\n            [34.6721, 1.17694],\n            [35.03599, 1.90584],\n            [34.59607, 3.05374],\n            [34.47913, 3.5556],\n            [34.005, 4.249885],\n            [34.620196, 4.847123],\n            [35.298007, 5.506]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Kyrgyzstan\",\n        \"SOV_A3\": \"KGZ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Kyrgyzstan\",\n        \"ADM0_A3\": \"KGZ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Kyrgyzstan\",\n        \"GU_A3\": \"KGZ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Kyrgyzstan\",\n        \"SU_A3\": \"KGZ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Kyrgyzstan\",\n        \"NAME_LONG\": \"Kyrgyzstan\",\n        \"BRK_A3\": \"KGZ\",\n        \"BRK_NAME\": \"Kyrgyzstan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Kgz.\",\n        \"POSTAL\": \"KG\",\n        \"FORMAL_EN\": \"Kyrgyz Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Kyrgyzstan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Kyrgyz Republic\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 5789122,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 21010,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KG\",\n        \"ISO_A2\": \"KG\",\n        \"ISO_A3\": \"KGZ\",\n        \"ISO_A3_EH\": \"KGZ\",\n        \"ISO_N3\": \"417\",\n        \"UN_A3\": \"417\",\n        \"WB_A2\": \"KG\",\n        \"WB_A3\": \"KGZ\",\n        \"WOE_ID\": 23424864,\n        \"WOE_ID_EH\": 23424864,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"KGZ\",\n        \"ADM0_A3_US\": \"KGZ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Central Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [69.464887, 39.279463, 80.25999, 43.298339],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [80.25999, 42.349999],\n            [80.11943, 42.123941],\n            [78.543661, 41.582243],\n            [78.187197, 41.185316],\n            [76.904484, 41.066486],\n            [76.526368, 40.427946],\n            [75.467828, 40.562072],\n            [74.776862, 40.366425],\n            [73.822244, 39.893973],\n            [73.960013, 39.660008],\n            [73.675379, 39.431237],\n            [71.784694, 39.279463],\n            [70.549162, 39.604198],\n            [69.464887, 39.526683],\n            [69.55961, 40.103211],\n            [70.648019, 39.935754],\n            [71.014198, 40.244366],\n            [71.774875, 40.145844],\n            [73.055417, 40.866033],\n            [71.870115, 41.3929],\n            [71.157859, 41.143587],\n            [70.420022, 41.519998],\n            [71.259248, 42.167711],\n            [70.962315, 42.266154],\n            [71.186281, 42.704293],\n            [71.844638, 42.845395],\n            [73.489758, 42.500894],\n            [73.645304, 43.091272],\n            [74.212866, 43.298339],\n            [75.636965, 42.8779],\n            [76.000354, 42.988022],\n            [77.658392, 42.960686],\n            [79.142177, 42.856092],\n            [79.643645, 42.496683],\n            [80.25999, 42.349999]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Cambodia\",\n        \"SOV_A3\": \"KHM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Cambodia\",\n        \"ADM0_A3\": \"KHM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Cambodia\",\n        \"GU_A3\": \"KHM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Cambodia\",\n        \"SU_A3\": \"KHM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Cambodia\",\n        \"NAME_LONG\": \"Cambodia\",\n        \"BRK_A3\": \"KHM\",\n        \"BRK_NAME\": \"Cambodia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Camb.\",\n        \"POSTAL\": \"KH\",\n        \"FORMAL_EN\": \"Kingdom of Cambodia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Cambodia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Cambodia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 16204486,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 58940,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CB\",\n        \"ISO_A2\": \"KH\",\n        \"ISO_A3\": \"KHM\",\n        \"ISO_A3_EH\": \"KHM\",\n        \"ISO_N3\": \"116\",\n        \"UN_A3\": \"116\",\n        \"WB_A2\": \"KH\",\n        \"WB_A3\": \"KHM\",\n        \"WOE_ID\": 23424776,\n        \"WOE_ID_EH\": 23424776,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"KHM\",\n        \"ADM0_A3_US\": \"KHM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [102.348099, 10.486544, 107.614548, 14.570584],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [102.584932, 12.186595],\n            [102.348099, 13.394247],\n            [102.988422, 14.225721],\n            [104.281418, 14.416743],\n            [105.218777, 14.273212],\n            [106.043946, 13.881091],\n            [106.496373, 14.570584],\n            [107.382727, 14.202441],\n            [107.614548, 13.535531],\n            [107.491403, 12.337206],\n            [105.810524, 11.567615],\n            [106.24967, 10.961812],\n            [105.199915, 10.88931],\n            [104.334335, 10.486544],\n            [103.49728, 10.632555],\n            [103.09069, 11.153661],\n            [102.584932, 12.186595]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"South Korea\",\n        \"SOV_A3\": \"KOR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"South Korea\",\n        \"ADM0_A3\": \"KOR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"South Korea\",\n        \"GU_A3\": \"KOR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"South Korea\",\n        \"SU_A3\": \"KOR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"South Korea\",\n        \"NAME_LONG\": \"Republic of Korea\",\n        \"BRK_A3\": \"KOR\",\n        \"BRK_NAME\": \"Republic of Korea\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"S.K.\",\n        \"POSTAL\": \"KR\",\n        \"FORMAL_EN\": \"Republic of Korea\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Korea, South\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Korea, Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 51181299,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 1929000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"4. Emerging region: MIKT\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KS\",\n        \"ISO_A2\": \"KR\",\n        \"ISO_A3\": \"KOR\",\n        \"ISO_A3_EH\": \"KOR\",\n        \"ISO_N3\": \"410\",\n        \"UN_A3\": \"410\",\n        \"WB_A2\": \"KR\",\n        \"WB_A3\": \"KOR\",\n        \"WOE_ID\": 23424868,\n        \"WOE_ID_EH\": 23424868,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"KOR\",\n        \"ADM0_A3_US\": \"KOR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 17,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [126.117398, 34.390046, 129.468304, 38.612243],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [128.349716, 38.612243],\n            [129.21292, 37.432392],\n            [129.46045, 36.784189],\n            [129.468304, 35.632141],\n            [129.091377, 35.082484],\n            [128.18585, 34.890377],\n            [127.386519, 34.475674],\n            [126.485748, 34.390046],\n            [126.37392, 34.93456],\n            [126.559231, 35.684541],\n            [126.117398, 36.725485],\n            [126.860143, 36.893924],\n            [126.174759, 37.749686],\n            [126.237339, 37.840378],\n            [126.68372, 37.804773],\n            [127.073309, 38.256115],\n            [127.780035, 38.304536],\n            [128.205746, 38.370397],\n            [128.349716, 38.612243]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Kosovo\",\n        \"SOV_A3\": \"KOS\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Kosovo\",\n        \"ADM0_A3\": \"KOS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Kosovo\",\n        \"GU_A3\": \"KOS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Kosovo\",\n        \"SU_A3\": \"KOS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Kosovo\",\n        \"NAME_LONG\": \"Kosovo\",\n        \"BRK_A3\": \"KOS\",\n        \"BRK_NAME\": \"Kosovo\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Kos.\",\n        \"POSTAL\": \"KO\",\n        \"FORMAL_EN\": \"Republic of Kosovo\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Kosovo\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Kosovo\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 1895250,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 18490,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1981,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KV\",\n        \"ISO_A2\": \"XK\",\n        \"ISO_A3\": \"-99\",\n        \"ISO_A3_EH\": \"-99\",\n        \"ISO_N3\": \"-99\",\n        \"UN_A3\": \"-099\",\n        \"WB_A2\": \"KV\",\n        \"WB_A3\": \"KSV\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 29389201,\n        \"WOE_NOTE\": \"Subunit of Serbia in WOE still; should include 29389201, 29389207, 29389218, 29389209 and 29389214.\",\n        \"ADM0_A3_IS\": \"KOS\",\n        \"ADM0_A3_US\": \"KOS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [20.0707, 41.84711, 21.77505, 43.27205],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [20.590247, 41.855409],\n            [20.52295, 42.21787],\n            [20.283755, 42.32026],\n            [20.0707, 42.58863],\n            [20.25758, 42.81275],\n            [20.49679, 42.88469],\n            [20.63508, 43.21671],\n            [20.81448, 43.27205],\n            [20.95651, 43.13094],\n            [21.143395, 43.068685],\n            [21.27421, 42.90959],\n            [21.43866, 42.86255],\n            [21.63302, 42.67717],\n            [21.77505, 42.6827],\n            [21.66292, 42.43922],\n            [21.54332, 42.32025],\n            [21.576636, 42.245224],\n            [21.3527, 42.2068],\n            [20.76216, 42.05186],\n            [20.71731, 41.84711],\n            [20.590247, 41.855409]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Kuwait\",\n        \"SOV_A3\": \"KWT\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Kuwait\",\n        \"ADM0_A3\": \"KWT\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Kuwait\",\n        \"GU_A3\": \"KWT\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Kuwait\",\n        \"SU_A3\": \"KWT\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Kuwait\",\n        \"NAME_LONG\": \"Kuwait\",\n        \"BRK_A3\": \"KWT\",\n        \"BRK_NAME\": \"Kuwait\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Kwt.\",\n        \"POSTAL\": \"KW\",\n        \"FORMAL_EN\": \"State of Kuwait\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Kuwait\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Kuwait\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 2875422,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 301100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KU\",\n        \"ISO_A2\": \"KW\",\n        \"ISO_A3\": \"KWT\",\n        \"ISO_A3_EH\": \"KWT\",\n        \"ISO_N3\": \"414\",\n        \"UN_A3\": \"414\",\n        \"WB_A2\": \"KW\",\n        \"WB_A3\": \"KWT\",\n        \"WOE_ID\": 23424870,\n        \"WOE_ID_EH\": 23424870,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"KWT\",\n        \"ADM0_A3_US\": \"KWT\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [46.568713, 28.526063, 48.416094, 30.05907],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [46.568713, 29.099025],\n            [47.302622, 30.05907],\n            [47.974519, 29.975819],\n            [48.183189, 29.534477],\n            [48.093943, 29.306299],\n            [48.416094, 28.552004],\n            [47.708851, 28.526063],\n            [47.459822, 29.002519],\n            [46.568713, 29.099025]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Laos\",\n        \"SOV_A3\": \"LAO\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Laos\",\n        \"ADM0_A3\": \"LAO\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Laos\",\n        \"GU_A3\": \"LAO\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Laos\",\n        \"SU_A3\": \"LAO\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Laos\",\n        \"NAME_LONG\": \"Lao PDR\",\n        \"BRK_A3\": \"LAO\",\n        \"BRK_NAME\": \"Laos\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Laos\",\n        \"POSTAL\": \"LA\",\n        \"FORMAL_EN\": \"Lao People's Democratic Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Laos\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Lao PDR\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 7126706,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 40960,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2005,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LA\",\n        \"ISO_A2\": \"LA\",\n        \"ISO_A3\": \"LAO\",\n        \"ISO_A3_EH\": \"LAO\",\n        \"ISO_N3\": \"418\",\n        \"UN_A3\": \"418\",\n        \"WB_A2\": \"LA\",\n        \"WB_A3\": \"LAO\",\n        \"WOE_ID\": 23424872,\n        \"WOE_ID_EH\": 23424872,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LAO\",\n        \"ADM0_A3_US\": \"LAO\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [100.115988, 13.881091, 107.564525, 22.464753],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [101.180005, 21.436573],\n            [101.270026, 21.201652],\n            [101.80312, 21.174367],\n            [101.652018, 22.318199],\n            [102.170436, 22.464753],\n            [102.754896, 21.675137],\n            [103.203861, 20.766562],\n            [104.435, 20.758733],\n            [104.822574, 19.886642],\n            [104.183388, 19.624668],\n            [103.896532, 19.265181],\n            [105.094598, 18.666975],\n            [105.925762, 17.485315],\n            [106.556008, 16.604284],\n            [107.312706, 15.908538],\n            [107.564525, 15.202173],\n            [107.382727, 14.202441],\n            [106.496373, 14.570584],\n            [106.043946, 13.881091],\n            [105.218777, 14.273212],\n            [105.544338, 14.723934],\n            [105.589039, 15.570316],\n            [104.779321, 16.441865],\n            [104.716947, 17.428859],\n            [103.956477, 18.240954],\n            [103.200192, 18.309632],\n            [102.998706, 17.961695],\n            [102.413005, 17.932782],\n            [102.113592, 18.109102],\n            [101.059548, 17.512497],\n            [101.035931, 18.408928],\n            [101.282015, 19.462585],\n            [100.606294, 19.508344],\n            [100.548881, 20.109238],\n            [100.115988, 20.41785],\n            [100.329101, 20.786122],\n            [101.180005, 21.436573]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Lebanon\",\n        \"SOV_A3\": \"LBN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Lebanon\",\n        \"ADM0_A3\": \"LBN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Lebanon\",\n        \"GU_A3\": \"LBN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Lebanon\",\n        \"SU_A3\": \"LBN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Lebanon\",\n        \"NAME_LONG\": \"Lebanon\",\n        \"BRK_A3\": \"LBN\",\n        \"BRK_NAME\": \"Lebanon\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Leb.\",\n        \"POSTAL\": \"LB\",\n        \"FORMAL_EN\": \"Lebanese Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Lebanon\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Lebanon\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 6229794,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 85160,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1970,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LE\",\n        \"ISO_A2\": \"LB\",\n        \"ISO_A3\": \"LBN\",\n        \"ISO_A3_EH\": \"LBN\",\n        \"ISO_N3\": \"422\",\n        \"UN_A3\": \"422\",\n        \"WB_A2\": \"LB\",\n        \"WB_A3\": \"LBN\",\n        \"WOE_ID\": 23424873,\n        \"WOE_ID_EH\": 23424873,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LBN\",\n        \"ADM0_A3_US\": \"LBN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": 4,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [35.126053, 33.08904, 36.61175, 34.644914],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [35.821101, 33.277426],\n            [35.552797, 33.264275],\n            [35.460709, 33.08904],\n            [35.126053, 33.0909],\n            [35.482207, 33.90545],\n            [35.979592, 34.610058],\n            [35.998403, 34.644914],\n            [36.448194, 34.593935],\n            [36.61175, 34.201789],\n            [36.06646, 33.824912],\n            [35.821101, 33.277426]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Liberia\",\n        \"SOV_A3\": \"LBR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Liberia\",\n        \"ADM0_A3\": \"LBR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Liberia\",\n        \"GU_A3\": \"LBR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Liberia\",\n        \"SU_A3\": \"LBR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Liberia\",\n        \"NAME_LONG\": \"Liberia\",\n        \"BRK_A3\": \"LBR\",\n        \"BRK_NAME\": \"Liberia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Liberia\",\n        \"POSTAL\": \"LR\",\n        \"FORMAL_EN\": \"Republic of Liberia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Liberia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Liberia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 4689021,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 3881,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LI\",\n        \"ISO_A2\": \"LR\",\n        \"ISO_A3\": \"LBR\",\n        \"ISO_A3_EH\": \"LBR\",\n        \"ISO_N3\": \"430\",\n        \"UN_A3\": \"430\",\n        \"WB_A2\": \"LR\",\n        \"WB_A3\": \"LBR\",\n        \"WOE_ID\": 23424876,\n        \"WOE_ID_EH\": 23424876,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LBR\",\n        \"ADM0_A3_US\": \"LBR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 7,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-11.438779, 4.355755, -7.539715, 8.541055],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-8.439298, 7.686043],\n            [-8.485446, 7.395208],\n            [-8.385452, 6.911801],\n            [-8.60288, 6.467564],\n            [-8.311348, 6.193033],\n            [-7.993693, 6.12619],\n            [-7.570153, 5.707352],\n            [-7.539715, 5.313345],\n            [-7.635368, 5.188159],\n            [-7.712159, 4.364566],\n            [-7.974107, 4.355755],\n            [-9.004794, 4.832419],\n            [-9.91342, 5.593561],\n            [-10.765384, 6.140711],\n            [-11.438779, 6.785917],\n            [-11.199802, 7.105846],\n            [-11.146704, 7.396706],\n            [-10.695595, 7.939464],\n            [-10.230094, 8.406206],\n            [-10.016567, 8.428504],\n            [-9.755342, 8.541055],\n            [-9.33728, 7.928534],\n            [-9.403348, 7.526905],\n            [-9.208786, 7.313921],\n            [-8.926065, 7.309037],\n            [-8.722124, 7.711674],\n            [-8.439298, 7.686043]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Libya\",\n        \"SOV_A3\": \"LBY\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Libya\",\n        \"ADM0_A3\": \"LBY\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Libya\",\n        \"GU_A3\": \"LBY\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Libya\",\n        \"SU_A3\": \"LBY\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Libya\",\n        \"NAME_LONG\": \"Libya\",\n        \"BRK_A3\": \"LBY\",\n        \"BRK_NAME\": \"Libya\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Libya\",\n        \"POSTAL\": \"LY\",\n        \"FORMAL_EN\": \"Libya\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Libya\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Libya\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 6653210,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 90890,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LY\",\n        \"ISO_A2\": \"LY\",\n        \"ISO_A3\": \"LBY\",\n        \"ISO_A3_EH\": \"LBY\",\n        \"ISO_N3\": \"434\",\n        \"UN_A3\": \"434\",\n        \"WB_A2\": \"LY\",\n        \"WB_A3\": \"LBY\",\n        \"WOE_ID\": 23424882,\n        \"WOE_ID_EH\": 23424882,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LBY\",\n        \"ADM0_A3_US\": \"LBY\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [9.319411, 19.58047, 25.16482, 33.136996],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [11.999506, 23.471668],\n            [11.560669, 24.097909],\n            [10.771364, 24.562532],\n            [10.303847, 24.379313],\n            [9.948261, 24.936954],\n            [9.910693, 25.365455],\n            [9.319411, 26.094325],\n            [9.716286, 26.512206],\n            [9.629056, 27.140953],\n            [9.756128, 27.688259],\n            [9.683885, 28.144174],\n            [9.859998, 28.95999],\n            [9.805634, 29.424638],\n            [9.48214, 30.307556],\n            [9.970017, 30.539325],\n            [10.056575, 30.961831],\n            [9.950225, 31.37607],\n            [10.636901, 31.761421],\n            [10.94479, 32.081815],\n            [11.432253, 32.368903],\n            [11.488787, 33.136996],\n            [12.66331, 32.79278],\n            [13.08326, 32.87882],\n            [13.91868, 32.71196],\n            [15.24563, 32.26508],\n            [15.71394, 31.37626],\n            [16.61162, 31.18218],\n            [18.02109, 30.76357],\n            [19.08641, 30.26639],\n            [19.57404, 30.52582],\n            [20.05335, 30.98576],\n            [19.82033, 31.75179],\n            [20.13397, 32.2382],\n            [20.85452, 32.7068],\n            [21.54298, 32.8432],\n            [22.89576, 32.63858],\n            [23.2368, 32.19149],\n            [23.60913, 32.18726],\n            [23.9275, 32.01667],\n            [24.92114, 31.89936],\n            [25.16482, 31.56915],\n            [24.80287, 31.08929],\n            [24.95762, 30.6616],\n            [24.70007, 30.04419],\n            [25, 29.238655],\n            [25, 25.6825],\n            [25, 22],\n            [25, 20.00304],\n            [23.85, 20],\n            [23.83766, 19.58047],\n            [19.84926, 21.49509],\n            [15.86085, 23.40972],\n            [14.8513, 22.86295],\n            [14.143871, 22.491289],\n            [13.581425, 23.040506],\n            [11.999506, 23.471668]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Sri Lanka\",\n        \"SOV_A3\": \"LKA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Sri Lanka\",\n        \"ADM0_A3\": \"LKA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Sri Lanka\",\n        \"GU_A3\": \"LKA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Sri Lanka\",\n        \"SU_A3\": \"LKA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Sri Lanka\",\n        \"NAME_LONG\": \"Sri Lanka\",\n        \"BRK_A3\": \"LKA\",\n        \"BRK_NAME\": \"Sri Lanka\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Sri L.\",\n        \"POSTAL\": \"LK\",\n        \"FORMAL_EN\": \"Democratic Socialist Republic of Sri Lanka\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Sri Lanka\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Sri Lanka\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 22409381,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 236700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CE\",\n        \"ISO_A2\": \"LK\",\n        \"ISO_A3\": \"LKA\",\n        \"ISO_A3_EH\": \"LKA\",\n        \"ISO_N3\": \"144\",\n        \"UN_A3\": \"144\",\n        \"WB_A2\": \"LK\",\n        \"WB_A3\": \"LKA\",\n        \"WOE_ID\": 23424778,\n        \"WOE_ID_EH\": 23424778,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LKA\",\n        \"ADM0_A3_US\": \"LKA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [79.695167, 5.96837, 81.787959, 9.824078],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [81.787959, 7.523055],\n            [81.637322, 6.481775],\n            [81.21802, 6.197141],\n            [80.348357, 5.96837],\n            [79.872469, 6.763463],\n            [79.695167, 8.200843],\n            [80.147801, 9.824078],\n            [80.838818, 9.268427],\n            [81.304319, 8.564206],\n            [81.787959, 7.523055]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Lesotho\",\n        \"SOV_A3\": \"LSO\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Lesotho\",\n        \"ADM0_A3\": \"LSO\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Lesotho\",\n        \"GU_A3\": \"LSO\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Lesotho\",\n        \"SU_A3\": \"LSO\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Lesotho\",\n        \"NAME_LONG\": \"Lesotho\",\n        \"BRK_A3\": \"LSO\",\n        \"BRK_NAME\": \"Lesotho\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Les.\",\n        \"POSTAL\": \"LS\",\n        \"FORMAL_EN\": \"Kingdom of Lesotho\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Lesotho\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Lesotho\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 1958042,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 6019,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LT\",\n        \"ISO_A2\": \"LS\",\n        \"ISO_A3\": \"LSO\",\n        \"ISO_A3_EH\": \"LSO\",\n        \"ISO_N3\": \"426\",\n        \"UN_A3\": \"426\",\n        \"WB_A2\": \"LS\",\n        \"WB_A3\": \"LSO\",\n        \"WOE_ID\": 23424880,\n        \"WOE_ID_EH\": 23424880,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LSO\",\n        \"ADM0_A3_US\": \"LSO\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Southern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [26.999262, -30.645106, 29.325166, -28.647502],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [28.978263, -28.955597],\n            [29.325166, -29.257387],\n            [29.018415, -29.743766],\n            [28.8484, -30.070051],\n            [28.291069, -30.226217],\n            [28.107205, -30.545732],\n            [27.749397, -30.645106],\n            [26.999262, -29.875954],\n            [27.532511, -29.242711],\n            [28.074338, -28.851469],\n            [28.5417, -28.647502],\n            [28.978263, -28.955597]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Lithuania\",\n        \"SOV_A3\": \"LTU\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Lithuania\",\n        \"ADM0_A3\": \"LTU\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Lithuania\",\n        \"GU_A3\": \"LTU\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Lithuania\",\n        \"SU_A3\": \"LTU\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Lithuania\",\n        \"NAME_LONG\": \"Lithuania\",\n        \"BRK_A3\": \"LTU\",\n        \"BRK_NAME\": \"Lithuania\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Lith.\",\n        \"POSTAL\": \"LT\",\n        \"FORMAL_EN\": \"Republic of Lithuania\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Lithuania\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Lithuania\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 2823859,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 85620,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LH\",\n        \"ISO_A2\": \"LT\",\n        \"ISO_A3\": \"LTU\",\n        \"ISO_A3_EH\": \"LTU\",\n        \"ISO_N3\": \"440\",\n        \"UN_A3\": \"440\",\n        \"WB_A2\": \"LT\",\n        \"WB_A3\": \"LTU\",\n        \"WOE_ID\": 23424875,\n        \"WOE_ID_EH\": 23424875,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LTU\",\n        \"ADM0_A3_US\": \"LTU\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [21.0558, 53.905702, 26.588279, 56.372528],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [26.494331, 55.615107],\n            [26.588279, 55.167176],\n            [25.768433, 54.846963],\n            [25.536354, 54.282423],\n            [24.450684, 53.905702],\n            [23.484128, 53.912498],\n            [23.243987, 54.220567],\n            [22.731099, 54.327537],\n            [22.651052, 54.582741],\n            [22.757764, 54.856574],\n            [22.315724, 55.015299],\n            [21.268449, 55.190482],\n            [21.0558, 56.031076],\n            [22.201157, 56.337802],\n            [23.878264, 56.273671],\n            [24.860684, 56.372528],\n            [25.000934, 56.164531],\n            [25.533047, 56.100297],\n            [26.494331, 55.615107]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Luxembourg\",\n        \"SOV_A3\": \"LUX\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Luxembourg\",\n        \"ADM0_A3\": \"LUX\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Luxembourg\",\n        \"GU_A3\": \"LUX\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Luxembourg\",\n        \"SU_A3\": \"LUX\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Luxembourg\",\n        \"NAME_LONG\": \"Luxembourg\",\n        \"BRK_A3\": \"LUX\",\n        \"BRK_NAME\": \"Luxembourg\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Lux.\",\n        \"POSTAL\": \"L\",\n        \"FORMAL_EN\": \"Grand Duchy of Luxembourg\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Luxembourg\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Luxembourg\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 594130,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 58740,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LU\",\n        \"ISO_A2\": \"LU\",\n        \"ISO_A3\": \"LUX\",\n        \"ISO_A3_EH\": \"LUX\",\n        \"ISO_N3\": \"442\",\n        \"UN_A3\": \"442\",\n        \"WB_A2\": \"LU\",\n        \"WB_A3\": \"LUX\",\n        \"WOE_ID\": 23424881,\n        \"WOE_ID_EH\": 23424881,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LUX\",\n        \"ADM0_A3_US\": \"LUX\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": 5,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5.7,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [5.674052, 49.442667, 6.242751, 50.128052],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [5.674052, 49.529484],\n            [5.782417, 50.090328],\n            [6.043073, 50.128052],\n            [6.242751, 49.902226],\n            [6.18632, 49.463803],\n            [5.897759, 49.442667],\n            [5.674052, 49.529484]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Latvia\",\n        \"SOV_A3\": \"LVA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Latvia\",\n        \"ADM0_A3\": \"LVA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Latvia\",\n        \"GU_A3\": \"LVA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Latvia\",\n        \"SU_A3\": \"LVA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Latvia\",\n        \"NAME_LONG\": \"Latvia\",\n        \"BRK_A3\": \"LVA\",\n        \"BRK_NAME\": \"Latvia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Lat.\",\n        \"POSTAL\": \"LV\",\n        \"FORMAL_EN\": \"Republic of Latvia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Latvia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Latvia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 1944643,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 50650,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LG\",\n        \"ISO_A2\": \"LV\",\n        \"ISO_A3\": \"LVA\",\n        \"ISO_A3_EH\": \"LVA\",\n        \"ISO_N3\": \"428\",\n        \"UN_A3\": \"428\",\n        \"WB_A2\": \"LV\",\n        \"WB_A3\": \"LVA\",\n        \"WOE_ID\": 23424874,\n        \"WOE_ID_EH\": 23424874,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"LVA\",\n        \"ADM0_A3_US\": \"LVA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [21.0558, 55.615107, 28.176709, 57.970157],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [28.176709, 56.16913],\n            [27.10246, 55.783314],\n            [26.494331, 55.615107],\n            [25.533047, 56.100297],\n            [25.000934, 56.164531],\n            [24.860684, 56.372528],\n            [23.878264, 56.273671],\n            [22.201157, 56.337802],\n            [21.0558, 56.031076],\n            [21.090424, 56.783873],\n            [21.581866, 57.411871],\n            [22.524341, 57.753374],\n            [23.318453, 57.006236],\n            [24.12073, 57.025693],\n            [24.312863, 57.793424],\n            [25.164594, 57.970157],\n            [25.60281, 57.847529],\n            [26.463532, 57.476389],\n            [27.288185, 57.474528],\n            [27.770016, 57.244258],\n            [27.855282, 56.759326],\n            [28.176709, 56.16913]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Morocco\",\n        \"SOV_A3\": \"MAR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Morocco\",\n        \"ADM0_A3\": \"MAR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Morocco\",\n        \"GU_A3\": \"MAR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Morocco\",\n        \"SU_A3\": \"MAR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Morocco\",\n        \"NAME_LONG\": \"Morocco\",\n        \"BRK_A3\": \"MAR\",\n        \"BRK_NAME\": \"Morocco\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mor.\",\n        \"POSTAL\": \"MA\",\n        \"FORMAL_EN\": \"Kingdom of Morocco\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Morocco\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Morocco\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 33986655,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 282800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MO\",\n        \"ISO_A2\": \"MA\",\n        \"ISO_A3\": \"MAR\",\n        \"ISO_A3_EH\": \"MAR\",\n        \"ISO_N3\": \"504\",\n        \"UN_A3\": \"504\",\n        \"WB_A2\": \"MA\",\n        \"WB_A3\": \"MAR\",\n        \"WOE_ID\": 23424893,\n        \"WOE_ID_EH\": 23424893,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MAR\",\n        \"ADM0_A3_US\": \"MAR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-17.020428, 21.420734, -1.124551, 35.759988],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-2.169914, 35.168396],\n            [-1.792986, 34.527919],\n            [-1.733455, 33.919713],\n            [-1.388049, 32.864015],\n            [-1.124551, 32.651522],\n            [-1.307899, 32.262889],\n            [-2.616605, 32.094346],\n            [-3.06898, 31.724498],\n            [-3.647498, 31.637294],\n            [-3.690441, 30.896952],\n            [-4.859646, 30.501188],\n            [-5.242129, 30.000443],\n            [-6.060632, 29.7317],\n            [-7.059228, 29.579228],\n            [-8.674116, 28.841289],\n            [-8.66559, 27.656426],\n            [-8.817828, 27.656426],\n            [-8.794884, 27.120696],\n            [-9.413037, 27.088476],\n            [-9.735343, 26.860945],\n            [-10.189424, 26.860945],\n            [-10.551263, 26.990808],\n            [-11.392555, 26.883424],\n            [-11.71822, 26.104092],\n            [-12.030759, 26.030866],\n            [-12.500963, 24.770116],\n            [-13.89111, 23.691009],\n            [-14.221168, 22.310163],\n            [-14.630833, 21.86094],\n            [-14.750955, 21.5006],\n            [-17.002962, 21.420734],\n            [-17.020428, 21.42231],\n            [-16.973248, 21.885745],\n            [-16.589137, 22.158234],\n            [-16.261922, 22.67934],\n            [-16.326414, 23.017768],\n            [-15.982611, 23.723358],\n            [-15.426004, 24.359134],\n            [-15.089332, 24.520261],\n            [-14.824645, 25.103533],\n            [-14.800926, 25.636265],\n            [-14.43994, 26.254418],\n            [-13.773805, 26.618892],\n            [-13.139942, 27.640148],\n            [-13.121613, 27.654148],\n            [-12.618837, 28.038186],\n            [-11.688919, 28.148644],\n            [-10.900957, 28.832142],\n            [-10.399592, 29.098586],\n            [-9.564811, 29.933574],\n            [-9.814718, 31.177736],\n            [-9.434793, 32.038096],\n            [-9.300693, 32.564679],\n            [-8.657476, 33.240245],\n            [-7.654178, 33.697065],\n            [-6.912544, 34.110476],\n            [-6.244342, 35.145865],\n            [-5.929994, 35.759988],\n            [-5.193863, 35.755182],\n            [-4.591006, 35.330712],\n            [-3.640057, 35.399855],\n            [-2.604306, 35.179093],\n            [-2.169914, 35.168396]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Moldova\",\n        \"SOV_A3\": \"MDA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Moldova\",\n        \"ADM0_A3\": \"MDA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Moldova\",\n        \"GU_A3\": \"MDA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Moldova\",\n        \"SU_A3\": \"MDA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Moldova\",\n        \"NAME_LONG\": \"Moldova\",\n        \"BRK_A3\": \"MDA\",\n        \"BRK_NAME\": \"Moldova\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mda.\",\n        \"POSTAL\": \"MD\",\n        \"FORMAL_EN\": \"Republic of Moldova\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Moldova\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Moldova\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 3474121,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 18540,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MD\",\n        \"ISO_A2\": \"MD\",\n        \"ISO_A3\": \"MDA\",\n        \"ISO_A3_EH\": \"MDA\",\n        \"ISO_N3\": \"498\",\n        \"UN_A3\": \"498\",\n        \"WB_A2\": \"MD\",\n        \"WB_A3\": \"MDA\",\n        \"WOE_ID\": 23424885,\n        \"WOE_ID_EH\": 23424885,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MDA\",\n        \"ADM0_A3_US\": \"MDA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [26.619337, 45.488283, 30.024659, 48.467119],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [26.619337, 48.220726],\n            [26.857824, 48.368211],\n            [27.522537, 48.467119],\n            [28.259547, 48.155562],\n            [28.670891, 48.118149],\n            [29.122698, 47.849095],\n            [29.050868, 47.510227],\n            [29.415135, 47.346645],\n            [29.559674, 46.928583],\n            [29.908852, 46.674361],\n            [29.83821, 46.525326],\n            [30.024659, 46.423937],\n            [29.759972, 46.349988],\n            [29.170654, 46.379262],\n            [29.072107, 46.517678],\n            [28.862972, 46.437889],\n            [28.933717, 46.25883],\n            [28.659987, 45.939987],\n            [28.485269, 45.596907],\n            [28.233554, 45.488283],\n            [28.054443, 45.944586],\n            [28.160018, 46.371563],\n            [28.12803, 46.810476],\n            [27.551166, 47.405117],\n            [27.233873, 47.826771],\n            [26.924176, 48.123264],\n            [26.619337, 48.220726]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Madagascar\",\n        \"SOV_A3\": \"MDG\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Madagascar\",\n        \"ADM0_A3\": \"MDG\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Madagascar\",\n        \"GU_A3\": \"MDG\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Madagascar\",\n        \"SU_A3\": \"MDG\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Madagascar\",\n        \"NAME_LONG\": \"Madagascar\",\n        \"BRK_A3\": \"MDG\",\n        \"BRK_NAME\": \"Madagascar\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mad.\",\n        \"POSTAL\": \"MG\",\n        \"FORMAL_EN\": \"Republic of Madagascar\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Madagascar\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Madagascar\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 25054161,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 36860,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1993,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MA\",\n        \"ISO_A2\": \"MG\",\n        \"ISO_A3\": \"MDG\",\n        \"ISO_A3_EH\": \"MDG\",\n        \"ISO_N3\": \"450\",\n        \"UN_A3\": \"450\",\n        \"WB_A2\": \"MG\",\n        \"WB_A3\": \"MDG\",\n        \"WOE_ID\": 23424883,\n        \"WOE_ID_EH\": 23424883,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MDG\",\n        \"ADM0_A3_US\": \"MDG\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [43.254187, -25.601434, 50.476537, -12.040557],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [49.543519, -12.469833],\n            [49.808981, -12.895285],\n            [50.056511, -13.555761],\n            [50.217431, -14.758789],\n            [50.476537, -15.226512],\n            [50.377111, -15.706069],\n            [50.200275, -16.000263],\n            [49.860606, -15.414253],\n            [49.672607, -15.710204],\n            [49.863344, -16.451037],\n            [49.774564, -16.875042],\n            [49.498612, -17.106036],\n            [49.435619, -17.953064],\n            [49.041792, -19.118781],\n            [48.548541, -20.496888],\n            [47.930749, -22.391501],\n            [47.547723, -23.781959],\n            [47.095761, -24.94163],\n            [46.282478, -25.178463],\n            [45.409508, -25.601434],\n            [44.833574, -25.346101],\n            [44.03972, -24.988345],\n            [43.763768, -24.460677],\n            [43.697778, -23.574116],\n            [43.345654, -22.776904],\n            [43.254187, -22.057413],\n            [43.433298, -21.336475],\n            [43.893683, -21.163307],\n            [43.89637, -20.830459],\n            [44.374325, -20.072366],\n            [44.464397, -19.435454],\n            [44.232422, -18.961995],\n            [44.042976, -18.331387],\n            [43.963084, -17.409945],\n            [44.312469, -16.850496],\n            [44.446517, -16.216219],\n            [44.944937, -16.179374],\n            [45.502732, -15.974373],\n            [45.872994, -15.793454],\n            [46.312243, -15.780018],\n            [46.882183, -15.210182],\n            [47.70513, -14.594303],\n            [48.005215, -14.091233],\n            [47.869047, -13.663869],\n            [48.293828, -13.784068],\n            [48.84506, -13.089175],\n            [48.863509, -12.487868],\n            [49.194651, -12.040557],\n            [49.543519, -12.469833]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Mexico\",\n        \"SOV_A3\": \"MEX\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Mexico\",\n        \"ADM0_A3\": \"MEX\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Mexico\",\n        \"GU_A3\": \"MEX\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Mexico\",\n        \"SU_A3\": \"MEX\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Mexico\",\n        \"NAME_LONG\": \"Mexico\",\n        \"BRK_A3\": \"MEX\",\n        \"BRK_NAME\": \"Mexico\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mex.\",\n        \"POSTAL\": \"MX\",\n        \"FORMAL_EN\": \"United Mexican States\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Mexico\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Mexico\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 124574795,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 2307000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"4. Emerging region: MIKT\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MX\",\n        \"ISO_A2\": \"MX\",\n        \"ISO_A3\": \"MEX\",\n        \"ISO_A3_EH\": \"MEX\",\n        \"ISO_N3\": \"484\",\n        \"UN_A3\": \"484\",\n        \"WB_A2\": \"MX\",\n        \"WB_A3\": \"MEX\",\n        \"WOE_ID\": 23424900,\n        \"WOE_ID_EH\": 23424900,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MEX\",\n        \"ADM0_A3_US\": \"MEX\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [-117.12776, 14.538829, -86.811982, 32.72083],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-88.300031, 18.499982],\n            [-88.490123, 18.486831],\n            [-88.848344, 17.883198],\n            [-89.029857, 18.001511],\n            [-89.150909, 17.955468],\n            [-89.14308, 17.808319],\n            [-90.067934, 17.819326],\n            [-91.00152, 17.817595],\n            [-91.002269, 17.254658],\n            [-91.453921, 17.252177],\n            [-91.08167, 16.918477],\n            [-90.711822, 16.687483],\n            [-90.600847, 16.470778],\n            [-90.438867, 16.41011],\n            [-90.464473, 16.069562],\n            [-91.74796, 16.066565],\n            [-92.229249, 15.251447],\n            [-92.087216, 15.064585],\n            [-92.20323, 14.830103],\n            [-92.22775, 14.538829],\n            [-93.359464, 15.61543],\n            [-93.875169, 15.940164],\n            [-94.691656, 16.200975],\n            [-95.250227, 16.128318],\n            [-96.053382, 15.752088],\n            [-96.557434, 15.653515],\n            [-97.263592, 15.917065],\n            [-98.01303, 16.107312],\n            [-98.947676, 16.566043],\n            [-99.697397, 16.706164],\n            [-100.829499, 17.171071],\n            [-101.666089, 17.649026],\n            [-101.918528, 17.91609],\n            [-102.478132, 17.975751],\n            [-103.50099, 18.292295],\n            [-103.917527, 18.748572],\n            [-104.99201, 19.316134],\n            [-105.493038, 19.946767],\n            [-105.731396, 20.434102],\n            [-105.397773, 20.531719],\n            [-105.500661, 20.816895],\n            [-105.270752, 21.076285],\n            [-105.265817, 21.422104],\n            [-105.603161, 21.871146],\n            [-105.693414, 22.26908],\n            [-106.028716, 22.773752],\n            [-106.90998, 23.767774],\n            [-107.915449, 24.548915],\n            [-108.401905, 25.172314],\n            [-109.260199, 25.580609],\n            [-109.444089, 25.824884],\n            [-109.291644, 26.442934],\n            [-109.801458, 26.676176],\n            [-110.391732, 27.162115],\n            [-110.641019, 27.859876],\n            [-111.178919, 27.941241],\n            [-111.759607, 28.467953],\n            [-112.228235, 28.954409],\n            [-112.271824, 29.266844],\n            [-112.809594, 30.021114],\n            [-113.163811, 30.786881],\n            [-113.148669, 31.170966],\n            [-113.871881, 31.567608],\n            [-114.205737, 31.524045],\n            [-114.776451, 31.799532],\n            [-114.9367, 31.393485],\n            [-114.771232, 30.913617],\n            [-114.673899, 30.162681],\n            [-114.330974, 29.750432],\n            [-113.588875, 29.061611],\n            [-113.424053, 28.826174],\n            [-113.271969, 28.754783],\n            [-113.140039, 28.411289],\n            [-112.962298, 28.42519],\n            [-112.761587, 27.780217],\n            [-112.457911, 27.525814],\n            [-112.244952, 27.171727],\n            [-111.616489, 26.662817],\n            [-111.284675, 25.73259],\n            [-110.987819, 25.294606],\n            [-110.710007, 24.826004],\n            [-110.655049, 24.298595],\n            [-110.172856, 24.265548],\n            [-109.771847, 23.811183],\n            [-109.409104, 23.364672],\n            [-109.433392, 23.185588],\n            [-109.854219, 22.818272],\n            [-110.031392, 22.823078],\n            [-110.295071, 23.430973],\n            [-110.949501, 24.000964],\n            [-111.670568, 24.484423],\n            [-112.182036, 24.738413],\n            [-112.148989, 25.470125],\n            [-112.300711, 26.012004],\n            [-112.777297, 26.32196],\n            [-113.464671, 26.768186],\n            [-113.59673, 26.63946],\n            [-113.848937, 26.900064],\n            [-114.465747, 27.14209],\n            [-115.055142, 27.722727],\n            [-114.982253, 27.7982],\n            [-114.570366, 27.741485],\n            [-114.199329, 28.115003],\n            [-114.162018, 28.566112],\n            [-114.931842, 29.279479],\n            [-115.518654, 29.556362],\n            [-115.887365, 30.180794],\n            [-116.25835, 30.836464],\n            [-116.721526, 31.635744],\n            [-117.12776, 32.53534],\n            [-115.99135, 32.61239],\n            [-114.72139, 32.72083],\n            [-114.815, 32.52528],\n            [-113.30498, 32.03914],\n            [-111.02361, 31.33472],\n            [-109.035, 31.34194],\n            [-108.24194, 31.34222],\n            [-108.24, 31.754854],\n            [-106.50759, 31.75452],\n            [-106.1429, 31.39995],\n            [-105.63159, 31.08383],\n            [-105.03737, 30.64402],\n            [-104.70575, 30.12173],\n            [-104.45697, 29.57196],\n            [-103.94, 29.27],\n            [-103.11, 28.97],\n            [-102.48, 29.76],\n            [-101.6624, 29.7793],\n            [-100.9576, 29.38071],\n            [-100.45584, 28.69612],\n            [-100.11, 28.11],\n            [-99.52, 27.54],\n            [-99.3, 26.84],\n            [-99.02, 26.37],\n            [-98.24, 26.06],\n            [-97.53, 25.84],\n            [-97.140008, 25.869997],\n            [-97.528072, 24.992144],\n            [-97.702946, 24.272343],\n            [-97.776042, 22.93258],\n            [-97.872367, 22.444212],\n            [-97.699044, 21.898689],\n            [-97.38896, 21.411019],\n            [-97.189333, 20.635433],\n            [-96.525576, 19.890931],\n            [-96.292127, 19.320371],\n            [-95.900885, 18.828024],\n            [-94.839063, 18.562717],\n            [-94.42573, 18.144371],\n            [-93.548651, 18.423837],\n            [-92.786114, 18.524839],\n            [-92.037348, 18.704569],\n            [-91.407903, 18.876083],\n            [-90.77187, 19.28412],\n            [-90.53359, 19.867418],\n            [-90.451476, 20.707522],\n            [-90.278618, 20.999855],\n            [-89.601321, 21.261726],\n            [-88.543866, 21.493675],\n            [-87.658417, 21.458846],\n            [-87.05189, 21.543543],\n            [-86.811982, 21.331515],\n            [-86.845908, 20.849865],\n            [-87.383291, 20.255405],\n            [-87.621054, 19.646553],\n            [-87.43675, 19.472403],\n            [-87.58656, 19.04013],\n            [-87.837191, 18.259816],\n            [-88.090664, 18.516648],\n            [-88.300031, 18.499982]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Macedonia\",\n        \"SOV_A3\": \"MKD\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Macedonia\",\n        \"ADM0_A3\": \"MKD\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Macedonia\",\n        \"GU_A3\": \"MKD\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Macedonia\",\n        \"SU_A3\": \"MKD\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Macedonia\",\n        \"NAME_LONG\": \"Macedonia\",\n        \"BRK_A3\": \"MKD\",\n        \"BRK_NAME\": \"Macedonia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mkd.\",\n        \"POSTAL\": \"MK\",\n        \"FORMAL_EN\": \"Former Yugoslav Republic of Macedonia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Macedonia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Macedonia, FYR\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 2103721,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 29520,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MK\",\n        \"ISO_A2\": \"MK\",\n        \"ISO_A3\": \"MKD\",\n        \"ISO_A3_EH\": \"MKD\",\n        \"ISO_N3\": \"807\",\n        \"UN_A3\": \"807\",\n        \"WB_A2\": \"MK\",\n        \"WB_A3\": \"MKD\",\n        \"WOE_ID\": 23424890,\n        \"WOE_ID_EH\": 23424890,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MKD\",\n        \"ADM0_A3_US\": \"MKD\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [20.463175, 40.842727, 22.952377, 42.32026],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [21.02004, 40.842727],\n            [20.605182, 41.086226],\n            [20.463175, 41.515089],\n            [20.590247, 41.855404],\n            [20.590247, 41.855409],\n            [20.71731, 41.84711],\n            [20.76216, 42.05186],\n            [21.3527, 42.2068],\n            [21.576636, 42.245224],\n            [21.91708, 42.30364],\n            [22.380526, 42.32026],\n            [22.881374, 41.999297],\n            [22.952377, 41.337994],\n            [22.76177, 41.3048],\n            [22.597308, 41.130487],\n            [22.055378, 41.149866],\n            [21.674161, 40.931275],\n            [21.02004, 40.842727]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Mali\",\n        \"SOV_A3\": \"MLI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Mali\",\n        \"ADM0_A3\": \"MLI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Mali\",\n        \"GU_A3\": \"MLI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Mali\",\n        \"SU_A3\": \"MLI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Mali\",\n        \"NAME_LONG\": \"Mali\",\n        \"BRK_A3\": \"MLI\",\n        \"BRK_NAME\": \"Mali\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mali\",\n        \"POSTAL\": \"ML\",\n        \"FORMAL_EN\": \"Republic of Mali\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Mali\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Mali\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 17885245,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 38090,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ML\",\n        \"ISO_A2\": \"ML\",\n        \"ISO_A3\": \"MLI\",\n        \"ISO_A3_EH\": \"MLI\",\n        \"ISO_N3\": \"466\",\n        \"UN_A3\": \"466\",\n        \"WB_A2\": \"ML\",\n        \"WB_A3\": \"MLI\",\n        \"WOE_ID\": 23424891,\n        \"WOE_ID_EH\": 23424891,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MLI\",\n        \"ADM0_A3_US\": \"MLI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-12.17075, 10.096361, 4.27021, 24.974574],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [0.374892, 14.928908],\n            [-0.266257, 14.924309],\n            [-0.515854, 15.116158],\n            [-1.066363, 14.973815],\n            [-2.001035, 14.559008],\n            [-2.191825, 14.246418],\n            [-2.967694, 13.79815],\n            [-3.103707, 13.541267],\n            [-3.522803, 13.337662],\n            [-4.006391, 13.472485],\n            [-4.280405, 13.228444],\n            [-4.427166, 12.542646],\n            [-5.220942, 11.713859],\n            [-5.197843, 11.375146],\n            [-5.470565, 10.95127],\n            [-5.404342, 10.370737],\n            [-5.816926, 10.222555],\n            [-6.050452, 10.096361],\n            [-6.205223, 10.524061],\n            [-6.493965, 10.411303],\n            [-6.666461, 10.430811],\n            [-6.850507, 10.138994],\n            [-7.622759, 10.147236],\n            [-7.89959, 10.297382],\n            [-8.029944, 10.206535],\n            [-8.335377, 10.494812],\n            [-8.282357, 10.792597],\n            [-8.407311, 10.909257],\n            [-8.620321, 10.810891],\n            [-8.581305, 11.136246],\n            [-8.376305, 11.393646],\n            [-8.786099, 11.812561],\n            [-8.905265, 12.088358],\n            [-9.127474, 12.30806],\n            [-9.327616, 12.334286],\n            [-9.567912, 12.194243],\n            [-9.890993, 12.060479],\n            [-10.165214, 11.844084],\n            [-10.593224, 11.923975],\n            [-10.87083, 12.177887],\n            [-11.036556, 12.211245],\n            [-11.297574, 12.077971],\n            [-11.456169, 12.076834],\n            [-11.513943, 12.442988],\n            [-11.467899, 12.754519],\n            [-11.553398, 13.141214],\n            [-11.927716, 13.422075],\n            [-12.124887, 13.994727],\n            [-12.17075, 14.616834],\n            [-11.834208, 14.799097],\n            [-11.666078, 15.388208],\n            [-11.349095, 15.411256],\n            [-10.650791, 15.132746],\n            [-10.086846, 15.330486],\n            [-9.700255, 15.264107],\n            [-9.550238, 15.486497],\n            [-5.537744, 15.50169],\n            [-5.315277, 16.201854],\n            [-5.488523, 16.325102],\n            [-5.971129, 20.640833],\n            [-6.453787, 24.956591],\n            [-4.923337, 24.974574],\n            [-1.550055, 22.792666],\n            [1.823228, 20.610809],\n            [2.060991, 20.142233],\n            [2.683588, 19.85623],\n            [3.146661, 19.693579],\n            [3.158133, 19.057364],\n            [4.267419, 19.155265],\n            [4.27021, 16.852227],\n            [3.723422, 16.184284],\n            [3.638259, 15.56812],\n            [2.749993, 15.409525],\n            [1.385528, 15.323561],\n            [1.015783, 14.968182],\n            [0.374892, 14.928908]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Myanmar\",\n        \"SOV_A3\": \"MMR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Myanmar\",\n        \"ADM0_A3\": \"MMR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Myanmar\",\n        \"GU_A3\": \"MMR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Myanmar\",\n        \"SU_A3\": \"MMR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Myanmar\",\n        \"NAME_LONG\": \"Myanmar\",\n        \"BRK_A3\": \"MMR\",\n        \"BRK_NAME\": \"Myanmar\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Myan.\",\n        \"POSTAL\": \"MM\",\n        \"FORMAL_EN\": \"Republic of the Union of Myanmar\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Burma\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Myanmar\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 55123814,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 311100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1983,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BM\",\n        \"ISO_A2\": \"MM\",\n        \"ISO_A3\": \"MMR\",\n        \"ISO_A3_EH\": \"MMR\",\n        \"ISO_N3\": \"104\",\n        \"UN_A3\": \"104\",\n        \"WB_A2\": \"MM\",\n        \"WB_A3\": \"MMR\",\n        \"WOE_ID\": 23424763,\n        \"WOE_ID_EH\": 23424763,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MMR\",\n        \"ADM0_A3_US\": \"MMR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [92.303234, 9.93296, 101.180005, 28.335945],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [92.368554, 20.670883],\n            [92.303234, 21.475485],\n            [92.652257, 21.324048],\n            [92.672721, 22.041239],\n            [93.166128, 22.27846],\n            [93.060294, 22.703111],\n            [93.286327, 23.043658],\n            [93.325188, 24.078556],\n            [94.106742, 23.850741],\n            [94.552658, 24.675238],\n            [94.603249, 25.162495],\n            [95.155153, 26.001307],\n            [95.124768, 26.573572],\n            [96.419366, 27.264589],\n            [97.133999, 27.083774],\n            [97.051989, 27.699059],\n            [97.402561, 27.882536],\n            [97.327114, 28.261583],\n            [97.911988, 28.335945],\n            [98.246231, 27.747221],\n            [98.68269, 27.508812],\n            [98.712094, 26.743536],\n            [98.671838, 25.918703],\n            [97.724609, 25.083637],\n            [97.60472, 23.897405],\n            [98.660262, 24.063286],\n            [98.898749, 23.142722],\n            [99.531992, 22.949039],\n            [99.240899, 22.118314],\n            [99.983489, 21.742937],\n            [100.416538, 21.558839],\n            [101.150033, 21.849984],\n            [101.180005, 21.436573],\n            [100.329101, 20.786122],\n            [100.115988, 20.41785],\n            [99.543309, 20.186598],\n            [98.959676, 19.752981],\n            [98.253724, 19.708203],\n            [97.797783, 18.62708],\n            [97.375896, 18.445438],\n            [97.859123, 17.567946],\n            [98.493761, 16.837836],\n            [98.903348, 16.177824],\n            [98.537376, 15.308497],\n            [98.192074, 15.123703],\n            [98.430819, 14.622028],\n            [99.097755, 13.827503],\n            [99.212012, 13.269294],\n            [99.196354, 12.804748],\n            [99.587286, 11.892763],\n            [99.038121, 10.960546],\n            [98.553551, 9.93296],\n            [98.457174, 10.675266],\n            [98.764546, 11.441292],\n            [98.428339, 12.032987],\n            [98.509574, 13.122378],\n            [98.103604, 13.64046],\n            [97.777732, 14.837286],\n            [97.597072, 16.100568],\n            [97.16454, 16.928734],\n            [96.505769, 16.427241],\n            [95.369352, 15.71439],\n            [94.808405, 15.803454],\n            [94.188804, 16.037936],\n            [94.533486, 17.27724],\n            [94.324817, 18.213514],\n            [93.540988, 19.366493],\n            [93.663255, 19.726962],\n            [93.078278, 19.855145],\n            [92.368554, 20.670883]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Montenegro\",\n        \"SOV_A3\": \"MNE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Montenegro\",\n        \"ADM0_A3\": \"MNE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Montenegro\",\n        \"GU_A3\": \"MNE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Montenegro\",\n        \"SU_A3\": \"MNE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Montenegro\",\n        \"NAME_LONG\": \"Montenegro\",\n        \"BRK_A3\": \"MNE\",\n        \"BRK_NAME\": \"Montenegro\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mont.\",\n        \"POSTAL\": \"ME\",\n        \"FORMAL_EN\": \"Montenegro\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Montenegro\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Montenegro\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 642550,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 10610,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MJ\",\n        \"ISO_A2\": \"ME\",\n        \"ISO_A3\": \"MNE\",\n        \"ISO_A3_EH\": \"MNE\",\n        \"ISO_N3\": \"499\",\n        \"UN_A3\": \"499\",\n        \"WB_A2\": \"ME\",\n        \"WB_A3\": \"MNE\",\n        \"WOE_ID\": 20069817,\n        \"WOE_ID_EH\": 20069817,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MNE\",\n        \"ADM0_A3_US\": \"MNE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [18.450017, 41.877551, 20.3398, 43.52384],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [20.0707, 42.58863],\n            [19.801613, 42.500093],\n            [19.738051, 42.688247],\n            [19.304486, 42.195745],\n            [19.371768, 41.877551],\n            [19.16246, 41.95502],\n            [18.88214, 42.28151],\n            [18.450017, 42.479992],\n            [18.56, 42.65],\n            [18.70648, 43.20011],\n            [19.03165, 43.43253],\n            [19.21852, 43.52384],\n            [19.48389, 43.35229],\n            [19.63, 43.21378],\n            [19.95857, 43.10604],\n            [20.3398, 42.89852],\n            [20.25758, 42.81275],\n            [20.0707, 42.58863]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Mongolia\",\n        \"SOV_A3\": \"MNG\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Mongolia\",\n        \"ADM0_A3\": \"MNG\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Mongolia\",\n        \"GU_A3\": \"MNG\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Mongolia\",\n        \"SU_A3\": \"MNG\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Mongolia\",\n        \"NAME_LONG\": \"Mongolia\",\n        \"BRK_A3\": \"MNG\",\n        \"BRK_NAME\": \"Mongolia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mong.\",\n        \"POSTAL\": \"MN\",\n        \"FORMAL_EN\": \"Mongolia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Mongolia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Mongolia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 3068243,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 37000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MG\",\n        \"ISO_A2\": \"MN\",\n        \"ISO_A3\": \"MNG\",\n        \"ISO_A3_EH\": \"MNG\",\n        \"ISO_N3\": \"496\",\n        \"UN_A3\": \"496\",\n        \"WB_A2\": \"MN\",\n        \"WB_A3\": \"MNG\",\n        \"WOE_ID\": 23424887,\n        \"WOE_ID_EH\": 23424887,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MNG\",\n        \"ADM0_A3_US\": \"MNG\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [87.751264, 41.59741, 119.772824, 52.047366],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [116.678801, 49.888531],\n            [116.191802, 49.134598],\n            [115.485282, 48.135383],\n            [115.742837, 47.726545],\n            [116.308953, 47.85341],\n            [117.295507, 47.697709],\n            [118.064143, 48.06673],\n            [118.866574, 47.74706],\n            [119.772824, 47.048059],\n            [119.66327, 46.69268],\n            [118.874326, 46.805412],\n            [117.421701, 46.672733],\n            [116.717868, 46.388202],\n            [115.985096, 45.727235],\n            [114.460332, 45.339817],\n            [113.463907, 44.808893],\n            [112.436062, 45.011646],\n            [111.873306, 45.102079],\n            [111.348377, 44.457442],\n            [111.667737, 44.073176],\n            [111.829588, 43.743118],\n            [111.129682, 43.406834],\n            [110.412103, 42.871234],\n            [109.243596, 42.519446],\n            [107.744773, 42.481516],\n            [106.129316, 42.134328],\n            [104.964994, 41.59741],\n            [104.522282, 41.908347],\n            [103.312278, 41.907468],\n            [101.83304, 42.514873],\n            [100.845866, 42.663804],\n            [99.515817, 42.524691],\n            [97.451757, 42.74889],\n            [96.349396, 42.725635],\n            [95.762455, 43.319449],\n            [95.306875, 44.241331],\n            [94.688929, 44.352332],\n            [93.480734, 44.975472],\n            [92.133891, 45.115076],\n            [90.94554, 45.286073],\n            [90.585768, 45.719716],\n            [90.970809, 46.888146],\n            [90.280826, 47.693549],\n            [88.854298, 48.069082],\n            [88.013832, 48.599463],\n            [87.751264, 49.297198],\n            [88.805567, 49.470521],\n            [90.713667, 50.331812],\n            [92.234712, 50.802171],\n            [93.10421, 50.49529],\n            [94.147566, 50.480537],\n            [94.815949, 50.013433],\n            [95.81402, 49.97746],\n            [97.25976, 49.72605],\n            [98.231762, 50.422401],\n            [97.82574, 51.010995],\n            [98.861491, 52.047366],\n            [99.981732, 51.634006],\n            [100.88948, 51.516856],\n            [102.06521, 51.25991],\n            [102.25589, 50.51056],\n            [103.676545, 50.089966],\n            [104.62158, 50.27532],\n            [105.886591, 50.406019],\n            [106.888804, 50.274296],\n            [107.868176, 49.793705],\n            [108.475167, 49.282548],\n            [109.402449, 49.292961],\n            [110.662011, 49.130128],\n            [111.581231, 49.377968],\n            [112.89774, 49.543565],\n            [114.362456, 50.248303],\n            [114.96211, 50.140247],\n            [115.485695, 49.805177],\n            [116.678801, 49.888531]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Mozambique\",\n        \"SOV_A3\": \"MOZ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Mozambique\",\n        \"ADM0_A3\": \"MOZ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Mozambique\",\n        \"GU_A3\": \"MOZ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Mozambique\",\n        \"SU_A3\": \"MOZ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Mozambique\",\n        \"NAME_LONG\": \"Mozambique\",\n        \"BRK_A3\": \"MOZ\",\n        \"BRK_NAME\": \"Mozambique\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Moz.\",\n        \"POSTAL\": \"MZ\",\n        \"FORMAL_EN\": \"Republic of Mozambique\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Mozambique\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Mozambique\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 26573706,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 35010,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MZ\",\n        \"ISO_A2\": \"MZ\",\n        \"ISO_A3\": \"MOZ\",\n        \"ISO_A3_EH\": \"MOZ\",\n        \"ISO_N3\": \"508\",\n        \"UN_A3\": \"508\",\n        \"WB_A2\": \"MZ\",\n        \"WB_A3\": \"MOZ\",\n        \"WOE_ID\": 23424902,\n        \"WOE_ID_EH\": 23424902,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MOZ\",\n        \"ADM0_A3_US\": \"MOZ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [30.179481, -26.742192, 40.775475, -10.317096],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [34.559989, -11.52002],\n            [35.312398, -11.439146],\n            [36.514082, -11.720938],\n            [36.775151, -11.594537],\n            [37.47129, -11.56876],\n            [37.82764, -11.26879],\n            [38.427557, -11.285202],\n            [39.521, -10.89688],\n            [40.31659, -10.3171],\n            [40.316586, -10.317098],\n            [40.316589, -10.317096],\n            [40.478387, -10.765441],\n            [40.437253, -11.761711],\n            [40.560811, -12.639177],\n            [40.59962, -14.201975],\n            [40.775475, -14.691764],\n            [40.477251, -15.406294],\n            [40.089264, -16.100774],\n            [39.452559, -16.720891],\n            [38.538351, -17.101023],\n            [37.411133, -17.586368],\n            [36.281279, -18.659688],\n            [35.896497, -18.84226],\n            [35.1984, -19.552811],\n            [34.786383, -19.784012],\n            [34.701893, -20.497043],\n            [35.176127, -21.254361],\n            [35.373428, -21.840837],\n            [35.385848, -22.14],\n            [35.562546, -22.09],\n            [35.533935, -23.070788],\n            [35.371774, -23.535359],\n            [35.60747, -23.706563],\n            [35.458746, -24.12261],\n            [35.040735, -24.478351],\n            [34.215824, -24.816314],\n            [33.01321, -25.357573],\n            [32.574632, -25.727318],\n            [32.660363, -26.148584],\n            [32.915955, -26.215867],\n            [32.83012, -26.742192],\n            [32.071665, -26.73382],\n            [31.985779, -26.29178],\n            [31.837778, -25.843332],\n            [31.752408, -25.484284],\n            [31.930589, -24.369417],\n            [31.670398, -23.658969],\n            [31.191409, -22.25151],\n            [32.244988, -21.116489],\n            [32.508693, -20.395292],\n            [32.659743, -20.30429],\n            [32.772708, -19.715592],\n            [32.611994, -19.419383],\n            [32.654886, -18.67209],\n            [32.849861, -17.979057],\n            [32.847639, -16.713398],\n            [32.328239, -16.392074],\n            [31.852041, -16.319417],\n            [31.636498, -16.07199],\n            [31.173064, -15.860944],\n            [30.338955, -15.880839],\n            [30.274256, -15.507787],\n            [30.179481, -14.796099],\n            [33.214025, -13.97186],\n            [33.7897, -14.451831],\n            [34.064825, -14.35995],\n            [34.459633, -14.61301],\n            [34.517666, -15.013709],\n            [34.307291, -15.478641],\n            [34.381292, -16.18356],\n            [35.03381, -16.8013],\n            [35.339063, -16.10744],\n            [35.771905, -15.896859],\n            [35.686845, -14.611046],\n            [35.267956, -13.887834],\n            [34.907151, -13.565425],\n            [34.559989, -13.579998],\n            [34.280006, -12.280025],\n            [34.559989, -11.52002]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Mauritania\",\n        \"SOV_A3\": \"MRT\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Mauritania\",\n        \"ADM0_A3\": \"MRT\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Mauritania\",\n        \"GU_A3\": \"MRT\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Mauritania\",\n        \"SU_A3\": \"MRT\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Mauritania\",\n        \"NAME_LONG\": \"Mauritania\",\n        \"BRK_A3\": \"MRT\",\n        \"BRK_NAME\": \"Mauritania\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mrt.\",\n        \"POSTAL\": \"MR\",\n        \"FORMAL_EN\": \"Islamic Republic of Mauritania\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Mauritania\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Mauritania\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 3758571,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 16710,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2000,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MR\",\n        \"ISO_A2\": \"MR\",\n        \"ISO_A3\": \"MRT\",\n        \"ISO_A3_EH\": \"MRT\",\n        \"ISO_N3\": \"478\",\n        \"UN_A3\": \"478\",\n        \"WB_A2\": \"MR\",\n        \"WB_A3\": \"MRT\",\n        \"WOE_ID\": 23424896,\n        \"WOE_ID_EH\": 23424896,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MRT\",\n        \"ADM0_A3_US\": \"MRT\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-17.063423, 14.616834, -4.923337, 27.395744],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-8.6844, 27.395744],\n            [-4.923337, 24.974574],\n            [-6.453787, 24.956591],\n            [-5.971129, 20.640833],\n            [-5.488523, 16.325102],\n            [-5.315277, 16.201854],\n            [-5.537744, 15.50169],\n            [-9.550238, 15.486497],\n            [-9.700255, 15.264107],\n            [-10.086846, 15.330486],\n            [-10.650791, 15.132746],\n            [-11.349095, 15.411256],\n            [-11.666078, 15.388208],\n            [-11.834208, 14.799097],\n            [-12.17075, 14.616834],\n            [-12.830658, 15.303692],\n            [-13.435738, 16.039383],\n            [-14.099521, 16.304302],\n            [-14.577348, 16.598264],\n            [-15.135737, 16.587282],\n            [-15.623666, 16.369337],\n            [-16.12069, 16.455663],\n            [-16.463098, 16.135036],\n            [-16.549708, 16.673892],\n            [-16.270552, 17.166963],\n            [-16.146347, 18.108482],\n            [-16.256883, 19.096716],\n            [-16.377651, 19.593817],\n            [-16.277838, 20.092521],\n            [-16.536324, 20.567866],\n            [-17.063423, 20.999752],\n            [-16.845194, 21.333323],\n            [-12.929102, 21.327071],\n            [-13.118754, 22.77122],\n            [-12.874222, 23.284832],\n            [-11.937224, 23.374594],\n            [-11.969419, 25.933353],\n            [-8.687294, 25.881056],\n            [-8.6844, 27.395744]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Malawi\",\n        \"SOV_A3\": \"MWI\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Malawi\",\n        \"ADM0_A3\": \"MWI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Malawi\",\n        \"GU_A3\": \"MWI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Malawi\",\n        \"SU_A3\": \"MWI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Malawi\",\n        \"NAME_LONG\": \"Malawi\",\n        \"BRK_A3\": \"MWI\",\n        \"BRK_NAME\": \"Malawi\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Mal.\",\n        \"POSTAL\": \"MW\",\n        \"FORMAL_EN\": \"Republic of Malawi\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Malawi\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Malawi\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 19196246,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 21200,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MI\",\n        \"ISO_A2\": \"MW\",\n        \"ISO_A3\": \"MWI\",\n        \"ISO_A3_EH\": \"MWI\",\n        \"ISO_N3\": \"454\",\n        \"UN_A3\": \"454\",\n        \"WB_A2\": \"MW\",\n        \"WB_A3\": \"MWI\",\n        \"WOE_ID\": 23424889,\n        \"WOE_ID_EH\": 23424889,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MWI\",\n        \"ADM0_A3_US\": \"MWI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [32.688165, -16.8013, 35.771905, -9.230599],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [34.559989, -11.52002],\n            [34.280006, -12.280025],\n            [34.559989, -13.579998],\n            [34.907151, -13.565425],\n            [35.267956, -13.887834],\n            [35.686845, -14.611046],\n            [35.771905, -15.896859],\n            [35.339063, -16.10744],\n            [35.03381, -16.8013],\n            [34.381292, -16.18356],\n            [34.307291, -15.478641],\n            [34.517666, -15.013709],\n            [34.459633, -14.61301],\n            [34.064825, -14.35995],\n            [33.7897, -14.451831],\n            [33.214025, -13.97186],\n            [32.688165, -13.712858],\n            [32.991764, -12.783871],\n            [33.306422, -12.435778],\n            [33.114289, -11.607198],\n            [33.31531, -10.79655],\n            [33.485688, -10.525559],\n            [33.231388, -9.676722],\n            [32.759375, -9.230599],\n            [33.73972, -9.41715],\n            [33.940838, -9.693674],\n            [34.28, -10.16],\n            [34.559989, -11.52002]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Malaysia\",\n        \"SOV_A3\": \"MYS\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Malaysia\",\n        \"ADM0_A3\": \"MYS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Malaysia\",\n        \"GU_A3\": \"MYS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Malaysia\",\n        \"SU_A3\": \"MYS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Malaysia\",\n        \"NAME_LONG\": \"Malaysia\",\n        \"BRK_A3\": \"MYS\",\n        \"BRK_NAME\": \"Malaysia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Malay.\",\n        \"POSTAL\": \"MY\",\n        \"FORMAL_EN\": \"Malaysia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Malaysia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Malaysia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 31381992,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 863000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MY\",\n        \"ISO_A2\": \"MY\",\n        \"ISO_A3\": \"MYS\",\n        \"ISO_A3_EH\": \"MYS\",\n        \"ISO_N3\": \"458\",\n        \"UN_A3\": \"458\",\n        \"WB_A2\": \"MY\",\n        \"WB_A3\": \"MYS\",\n        \"WOE_ID\": 23424901,\n        \"WOE_ID_EH\": 23424901,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MYS\",\n        \"ADM0_A3_US\": \"MYS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [100.085757, 0.773131, 119.181904, 6.928053],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [114.204017, 4.525874],\n              [114.659596, 4.007637],\n              [114.869557, 4.348314],\n              [115.347461, 4.316636],\n              [115.4057, 4.955228],\n              [115.45071, 5.44773],\n              [116.220741, 6.143191],\n              [116.725103, 6.924771],\n              [117.129626, 6.928053],\n              [117.643393, 6.422166],\n              [117.689075, 5.98749],\n              [118.347691, 5.708696],\n              [119.181904, 5.407836],\n              [119.110694, 5.016128],\n              [118.439727, 4.966519],\n              [118.618321, 4.478202],\n              [117.882035, 4.137551],\n              [117.015214, 4.306094],\n              [115.865517, 4.306559],\n              [115.519078, 3.169238],\n              [115.134037, 2.821482],\n              [114.621355, 1.430688],\n              [113.80585, 1.217549],\n              [112.859809, 1.49779],\n              [112.380252, 1.410121],\n              [111.797548, 0.904441],\n              [111.159138, 0.976478],\n              [110.514061, 0.773131],\n              [109.830227, 1.338136],\n              [109.66326, 2.006467],\n              [110.396135, 1.663775],\n              [111.168853, 1.850637],\n              [111.370081, 2.697303],\n              [111.796928, 2.885897],\n              [112.995615, 3.102395],\n              [113.712935, 3.893509],\n              [114.204017, 4.525874]\n            ]\n          ],\n          [\n            [\n              [102.141187, 6.221636],\n              [102.371147, 6.128205],\n              [102.961705, 5.524495],\n              [103.381215, 4.855001],\n              [103.438575, 4.181606],\n              [103.332122, 3.726698],\n              [103.429429, 3.382869],\n              [103.502448, 2.791019],\n              [103.854674, 2.515454],\n              [104.247932, 1.631141],\n              [104.228811, 1.293048],\n              [103.519707, 1.226334],\n              [102.573615, 1.967115],\n              [101.390638, 2.760814],\n              [101.27354, 3.270292],\n              [100.695435, 3.93914],\n              [100.557408, 4.76728],\n              [100.196706, 5.312493],\n              [100.30626, 6.040562],\n              [100.085757, 6.464489],\n              [100.259596, 6.642825],\n              [101.075516, 6.204867],\n              [101.154219, 5.691384],\n              [101.814282, 5.810808],\n              [102.141187, 6.221636]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Namibia\",\n        \"SOV_A3\": \"NAM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Namibia\",\n        \"ADM0_A3\": \"NAM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Namibia\",\n        \"GU_A3\": \"NAM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Namibia\",\n        \"SU_A3\": \"NAM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Namibia\",\n        \"NAME_LONG\": \"Namibia\",\n        \"BRK_A3\": \"NAM\",\n        \"BRK_NAME\": \"Namibia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Nam.\",\n        \"POSTAL\": \"NA\",\n        \"FORMAL_EN\": \"Republic of Namibia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Namibia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Namibia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 2484780,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 25990,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"WA\",\n        \"ISO_A2\": \"NA\",\n        \"ISO_A3\": \"NAM\",\n        \"ISO_A3_EH\": \"NAM\",\n        \"ISO_N3\": \"516\",\n        \"UN_A3\": \"516\",\n        \"WB_A2\": \"NA\",\n        \"WB_A3\": \"NAM\",\n        \"WOE_ID\": 23424987,\n        \"WOE_ID_EH\": 23424987,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NAM\",\n        \"ADM0_A3_US\": \"NAM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Southern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7.5\n      },\n      \"bbox\": [11.734199, -29.045462, 25.084443, -16.941343],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [11.734199, -17.301889],\n            [12.215461, -17.111668],\n            [12.814081, -16.941343],\n            [13.462362, -16.971212],\n            [14.058501, -17.423381],\n            [14.209707, -17.353101],\n            [18.263309, -17.309951],\n            [18.956187, -17.789095],\n            [21.377176, -17.930636],\n            [23.215048, -17.523116],\n            [24.033862, -17.295843],\n            [24.682349, -17.353411],\n            [25.07695, -17.578823],\n            [25.084443, -17.661816],\n            [24.520705, -17.887125],\n            [24.217365, -17.889347],\n            [23.579006, -18.281261],\n            [23.196858, -17.869038],\n            [21.65504, -18.219146],\n            [20.910641, -18.252219],\n            [20.881134, -21.814327],\n            [19.895458, -21.849157],\n            [19.895768, -24.76779],\n            [19.894734, -28.461105],\n            [19.002127, -28.972443],\n            [18.464899, -29.045462],\n            [17.836152, -28.856378],\n            [17.387497, -28.783514],\n            [17.218929, -28.355943],\n            [16.824017, -28.082162],\n            [16.344977, -28.576705],\n            [15.601818, -27.821247],\n            [15.210472, -27.090956],\n            [14.989711, -26.117372],\n            [14.743214, -25.39292],\n            [14.408144, -23.853014],\n            [14.385717, -22.656653],\n            [14.257714, -22.111208],\n            [13.868642, -21.699037],\n            [13.352498, -20.872834],\n            [12.826845, -19.673166],\n            [12.608564, -19.045349],\n            [11.794919, -18.069129],\n            [11.734199, -17.301889]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"France\",\n        \"SOV_A3\": \"FR1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Dependency\",\n        \"ADMIN\": \"New Caledonia\",\n        \"ADM0_A3\": \"NCL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"New Caledonia\",\n        \"GU_A3\": \"NCL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"New Caledonia\",\n        \"SU_A3\": \"NCL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"New Caledonia\",\n        \"NAME_LONG\": \"New Caledonia\",\n        \"BRK_A3\": \"NCL\",\n        \"BRK_NAME\": \"New Caledonia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"New C.\",\n        \"POSTAL\": \"NC\",\n        \"FORMAL_EN\": \"New Caledonia\",\n        \"FORMAL_FR\": \"Nouvelle-Calédonie\",\n        \"NAME_CIAWF\": \"New Caledonia\",\n        \"NOTE_ADM0\": \"Fr.\",\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"New Caledonia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 7,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 9,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 279070,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 10770,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NC\",\n        \"ISO_A2\": \"NC\",\n        \"ISO_A3\": \"NCL\",\n        \"ISO_A3_EH\": \"NCL\",\n        \"ISO_N3\": \"540\",\n        \"UN_A3\": \"540\",\n        \"WB_A2\": \"NC\",\n        \"WB_A3\": \"NCL\",\n        \"WOE_ID\": 23424903,\n        \"WOE_ID_EH\": 23424903,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NCL\",\n        \"ADM0_A3_US\": \"NCL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Melanesia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 13,\n        \"LONG_LEN\": 13,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": -99,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.6,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [164.029606, -22.399976, 167.120011, -20.105646],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [165.77999, -21.080005],\n            [166.599991, -21.700019],\n            [167.120011, -22.159991],\n            [166.740035, -22.399976],\n            [166.189732, -22.129708],\n            [165.474375, -21.679607],\n            [164.829815, -21.14982],\n            [164.167995, -20.444747],\n            [164.029606, -20.105646],\n            [164.459967, -20.120012],\n            [165.020036, -20.459991],\n            [165.460009, -20.800022],\n            [165.77999, -21.080005]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Niger\",\n        \"SOV_A3\": \"NER\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Niger\",\n        \"ADM0_A3\": \"NER\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Niger\",\n        \"GU_A3\": \"NER\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Niger\",\n        \"SU_A3\": \"NER\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Niger\",\n        \"NAME_LONG\": \"Niger\",\n        \"BRK_A3\": \"NER\",\n        \"BRK_NAME\": \"Niger\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Niger\",\n        \"POSTAL\": \"NE\",\n        \"FORMAL_EN\": \"Republic of Niger\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Niger\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Niger\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 19245344,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 20150,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NG\",\n        \"ISO_A2\": \"NE\",\n        \"ISO_A3\": \"NER\",\n        \"ISO_A3_EH\": \"NER\",\n        \"ISO_N3\": \"562\",\n        \"UN_A3\": \"562\",\n        \"WB_A2\": \"NE\",\n        \"WB_A3\": \"NER\",\n        \"WOE_ID\": 23424906,\n        \"WOE_ID_EH\": 23424906,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NER\",\n        \"ADM0_A3_US\": \"NER\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [0.295646, 11.660167, 15.903247, 23.471668],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [3.61118, 11.660167],\n            [2.848643, 12.235636],\n            [2.490164, 12.233052],\n            [2.154474, 11.94015],\n            [2.177108, 12.625018],\n            [1.024103, 12.851826],\n            [0.993046, 13.33575],\n            [0.429928, 13.988733],\n            [0.295646, 14.444235],\n            [0.374892, 14.928908],\n            [1.015783, 14.968182],\n            [1.385528, 15.323561],\n            [2.749993, 15.409525],\n            [3.638259, 15.56812],\n            [3.723422, 16.184284],\n            [4.27021, 16.852227],\n            [4.267419, 19.155265],\n            [5.677566, 19.601207],\n            [8.572893, 21.565661],\n            [11.999506, 23.471668],\n            [13.581425, 23.040506],\n            [14.143871, 22.491289],\n            [14.8513, 22.86295],\n            [15.096888, 21.308519],\n            [15.47106, 21.04845],\n            [15.487148, 20.730415],\n            [15.903247, 20.387619],\n            [15.685741, 19.95718],\n            [15.300441, 17.92795],\n            [15.247731, 16.627306],\n            [13.97217, 15.68437],\n            [13.540394, 14.367134],\n            [13.956699, 13.996691],\n            [13.954477, 13.353449],\n            [14.595781, 13.330427],\n            [14.495787, 12.859396],\n            [14.213531, 12.802035],\n            [14.181336, 12.483657],\n            [13.995353, 12.461565],\n            [13.318702, 13.556356],\n            [13.083987, 13.596147],\n            [12.302071, 13.037189],\n            [11.527803, 13.32898],\n            [10.989593, 13.387323],\n            [10.701032, 13.246918],\n            [10.114814, 13.277252],\n            [9.524928, 12.851102],\n            [9.014933, 12.826659],\n            [7.804671, 13.343527],\n            [7.330747, 13.098038],\n            [6.820442, 13.115091],\n            [6.445426, 13.492768],\n            [5.443058, 13.865924],\n            [4.368344, 13.747482],\n            [4.107946, 13.531216],\n            [3.967283, 12.956109],\n            [3.680634, 12.552903],\n            [3.61118, 11.660167]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Nigeria\",\n        \"SOV_A3\": \"NGA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Nigeria\",\n        \"ADM0_A3\": \"NGA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Nigeria\",\n        \"GU_A3\": \"NGA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Nigeria\",\n        \"SU_A3\": \"NGA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Nigeria\",\n        \"NAME_LONG\": \"Nigeria\",\n        \"BRK_A3\": \"NGA\",\n        \"BRK_NAME\": \"Nigeria\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Nigeria\",\n        \"POSTAL\": \"NG\",\n        \"FORMAL_EN\": \"Federal Republic of Nigeria\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Nigeria\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Nigeria\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 190632261,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 1089000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NI\",\n        \"ISO_A2\": \"NG\",\n        \"ISO_A3\": \"NGA\",\n        \"ISO_A3_EH\": \"NGA\",\n        \"ISO_N3\": \"566\",\n        \"UN_A3\": \"566\",\n        \"WB_A2\": \"NG\",\n        \"WB_A3\": \"NGA\",\n        \"WOE_ID\": 23424908,\n        \"WOE_ID_EH\": 23424908,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NGA\",\n        \"ADM0_A3_US\": \"NGA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 7,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [2.691702, 4.240594, 14.577178, 13.865924],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [2.691702, 6.258817],\n            [2.749063, 7.870734],\n            [2.723793, 8.506845],\n            [2.912308, 9.137608],\n            [3.220352, 9.444153],\n            [3.705438, 10.06321],\n            [3.60007, 10.332186],\n            [3.797112, 10.734746],\n            [3.572216, 11.327939],\n            [3.61118, 11.660167],\n            [3.680634, 12.552903],\n            [3.967283, 12.956109],\n            [4.107946, 13.531216],\n            [4.368344, 13.747482],\n            [5.443058, 13.865924],\n            [6.445426, 13.492768],\n            [6.820442, 13.115091],\n            [7.330747, 13.098038],\n            [7.804671, 13.343527],\n            [9.014933, 12.826659],\n            [9.524928, 12.851102],\n            [10.114814, 13.277252],\n            [10.701032, 13.246918],\n            [10.989593, 13.387323],\n            [11.527803, 13.32898],\n            [12.302071, 13.037189],\n            [13.083987, 13.596147],\n            [13.318702, 13.556356],\n            [13.995353, 12.461565],\n            [14.181336, 12.483657],\n            [14.577178, 12.085361],\n            [14.468192, 11.904752],\n            [14.415379, 11.572369],\n            [13.57295, 10.798566],\n            [13.308676, 10.160362],\n            [13.1676, 9.640626],\n            [12.955468, 9.417772],\n            [12.753672, 8.717763],\n            [12.218872, 8.305824],\n            [12.063946, 7.799808],\n            [11.839309, 7.397042],\n            [11.745774, 6.981383],\n            [11.058788, 6.644427],\n            [10.497375, 7.055358],\n            [10.118277, 7.03877],\n            [9.522706, 6.453482],\n            [9.233163, 6.444491],\n            [8.757533, 5.479666],\n            [8.500288, 4.771983],\n            [7.462108, 4.412108],\n            [7.082596, 4.464689],\n            [6.698072, 4.240594],\n            [5.898173, 4.262453],\n            [5.362805, 4.887971],\n            [5.033574, 5.611802],\n            [4.325607, 6.270651],\n            [3.57418, 6.2583],\n            [2.691702, 6.258817]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Nicaragua\",\n        \"SOV_A3\": \"NIC\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Nicaragua\",\n        \"ADM0_A3\": \"NIC\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Nicaragua\",\n        \"GU_A3\": \"NIC\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Nicaragua\",\n        \"SU_A3\": \"NIC\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Nicaragua\",\n        \"NAME_LONG\": \"Nicaragua\",\n        \"BRK_A3\": \"NIC\",\n        \"BRK_NAME\": \"Nicaragua\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Nic.\",\n        \"POSTAL\": \"NI\",\n        \"FORMAL_EN\": \"Republic of Nicaragua\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Nicaragua\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Nicaragua\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 6025951,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 33550,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2005,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NU\",\n        \"ISO_A2\": \"NI\",\n        \"ISO_A3\": \"NIC\",\n        \"ISO_A3_EH\": \"NIC\",\n        \"ISO_N3\": \"558\",\n        \"UN_A3\": \"558\",\n        \"WB_A2\": \"NI\",\n        \"WB_A3\": \"NIC\",\n        \"WOE_ID\": 23424915,\n        \"WOE_ID_EH\": 23424915,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NIC\",\n        \"ADM0_A3_US\": \"NIC\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-87.668493, 10.726839, -83.147219, 15.016267],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-83.655612, 10.938764],\n            [-83.895054, 10.726839],\n            [-84.190179, 10.79345],\n            [-84.355931, 10.999226],\n            [-84.673069, 11.082657],\n            [-84.903003, 10.952303],\n            [-85.561852, 11.217119],\n            [-85.71254, 11.088445],\n            [-86.058488, 11.403439],\n            [-86.52585, 11.806877],\n            [-86.745992, 12.143962],\n            [-87.167516, 12.458258],\n            [-87.668493, 12.90991],\n            [-87.557467, 13.064552],\n            [-87.392386, 12.914018],\n            [-87.316654, 12.984686],\n            [-87.005769, 13.025794],\n            [-86.880557, 13.254204],\n            [-86.733822, 13.263093],\n            [-86.755087, 13.754845],\n            [-86.520708, 13.778487],\n            [-86.312142, 13.771356],\n            [-86.096264, 14.038187],\n            [-85.801295, 13.836055],\n            [-85.698665, 13.960078],\n            [-85.514413, 14.079012],\n            [-85.165365, 14.35437],\n            [-85.148751, 14.560197],\n            [-85.052787, 14.551541],\n            [-84.924501, 14.790493],\n            [-84.820037, 14.819587],\n            [-84.649582, 14.666805],\n            [-84.449336, 14.621614],\n            [-84.228342, 14.748764],\n            [-83.975721, 14.749436],\n            [-83.628585, 14.880074],\n            [-83.489989, 15.016267],\n            [-83.147219, 14.995829],\n            [-83.233234, 14.899866],\n            [-83.284162, 14.676624],\n            [-83.182126, 14.310703],\n            [-83.4125, 13.970078],\n            [-83.519832, 13.567699],\n            [-83.552207, 13.127054],\n            [-83.498515, 12.869292],\n            [-83.473323, 12.419087],\n            [-83.626104, 12.32085],\n            [-83.719613, 11.893124],\n            [-83.650858, 11.629032],\n            [-83.85547, 11.373311],\n            [-83.808936, 11.103044],\n            [-83.655612, 10.938764]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Netherlands\",\n        \"SOV_A3\": \"NL1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"Netherlands\",\n        \"ADM0_A3\": \"NLD\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Netherlands\",\n        \"GU_A3\": \"NLD\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Netherlands\",\n        \"SU_A3\": \"NLD\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Netherlands\",\n        \"NAME_LONG\": \"Netherlands\",\n        \"BRK_A3\": \"NLD\",\n        \"BRK_NAME\": \"Netherlands\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Neth.\",\n        \"POSTAL\": \"NL\",\n        \"FORMAL_EN\": \"Kingdom of the Netherlands\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Netherlands\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Netherlands\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 17084719,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 870800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NL\",\n        \"ISO_A2\": \"NL\",\n        \"ISO_A3\": \"NLD\",\n        \"ISO_A3_EH\": \"NLD\",\n        \"ISO_N3\": \"528\",\n        \"UN_A3\": \"528\",\n        \"WB_A2\": \"NL\",\n        \"WB_A3\": \"NLD\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424909,\n        \"WOE_NOTE\": \"Doesn't include new former units of Netherlands Antilles (24549811, 24549808, and 24549809)\",\n        \"ADM0_A3_IS\": \"NLD\",\n        \"ADM0_A3_US\": \"NLD\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Western Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [3.314971, 50.803721, 7.092053, 53.510403],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [6.156658, 50.803721],\n            [5.606976, 51.037298],\n            [4.973991, 51.475024],\n            [4.047071, 51.267259],\n            [3.314971, 51.345755],\n            [3.315011, 51.345777],\n            [3.830289, 51.620545],\n            [4.705997, 53.091798],\n            [6.074183, 53.510403],\n            [6.90514, 53.482162],\n            [7.092053, 53.144043],\n            [6.84287, 52.22844],\n            [6.589397, 51.852029],\n            [5.988658, 51.851616],\n            [6.156658, 50.803721]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Norway\",\n        \"SOV_A3\": \"NOR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Norway\",\n        \"ADM0_A3\": \"NOR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Norway\",\n        \"GU_A3\": \"NOR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Norway\",\n        \"SU_A3\": \"NOR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Norway\",\n        \"NAME_LONG\": \"Norway\",\n        \"BRK_A3\": \"NOR\",\n        \"BRK_NAME\": \"Norway\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Nor.\",\n        \"POSTAL\": \"N\",\n        \"FORMAL_EN\": \"Kingdom of Norway\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Norway\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Norway\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 8,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 5320045,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 364700,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"-99\",\n        \"ISO_A2\": \"-99\",\n        \"ISO_A3\": \"-99\",\n        \"ISO_A3_EH\": \"-99\",\n        \"ISO_N3\": \"-99\",\n        \"UN_A3\": \"-99\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424910,\n        \"WOE_NOTE\": \"Does not include Svalbard, Jan Mayen, or Bouvet Islands (28289410).\",\n        \"ADM0_A3_IS\": \"NOR\",\n        \"ADM0_A3_US\": \"NOR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [4.992078, 58.078884, 31.293418, 80.657144],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [28.59193, 69.064777],\n              [29.015573, 69.766491],\n              [27.732292, 70.164193],\n              [26.179622, 69.825299],\n              [25.689213, 69.092114],\n              [24.735679, 68.649557],\n              [23.66205, 68.891247],\n              [22.356238, 68.841741],\n              [21.244936, 69.370443],\n              [20.645593, 69.106247],\n              [20.025269, 69.065139],\n              [19.87856, 68.407194],\n              [17.993868, 68.567391],\n              [17.729182, 68.010552],\n              [16.768879, 68.013937],\n              [16.108712, 67.302456],\n              [15.108411, 66.193867],\n              [13.55569, 64.787028],\n              [13.919905, 64.445421],\n              [13.571916, 64.049114],\n              [12.579935, 64.066219],\n              [11.930569, 63.128318],\n              [11.992064, 61.800362],\n              [12.631147, 61.293572],\n              [12.300366, 60.117933],\n              [11.468272, 59.432393],\n              [11.027369, 58.856149],\n              [10.356557, 59.469807],\n              [8.382, 58.313288],\n              [7.048748, 58.078884],\n              [5.665835, 58.588155],\n              [5.308234, 59.663232],\n              [4.992078, 61.970998],\n              [5.9129, 62.614473],\n              [8.553411, 63.454008],\n              [10.527709, 64.486038],\n              [12.358347, 65.879726],\n              [14.761146, 67.810642],\n              [16.435927, 68.563205],\n              [19.184028, 69.817444],\n              [21.378416, 70.255169],\n              [23.023742, 70.202072],\n              [24.546543, 71.030497],\n              [26.37005, 70.986262],\n              [28.165547, 71.185474],\n              [31.293418, 70.453788],\n              [30.005435, 70.186259],\n              [31.101042, 69.558101],\n              [29.39955, 69.15692],\n              [28.59193, 69.064777]\n            ]\n          ],\n          [\n            [\n              [24.72412, 77.85385],\n              [22.49032, 77.44493],\n              [20.72601, 77.67704],\n              [21.41611, 77.93504],\n              [20.8119, 78.25463],\n              [22.88426, 78.45494],\n              [23.28134, 78.07954],\n              [24.72412, 77.85385]\n            ]\n          ],\n          [\n            [\n              [18.25183, 79.70175],\n              [21.54383, 78.95611],\n              [19.02737, 78.5626],\n              [18.47172, 77.82669],\n              [17.59441, 77.63796],\n              [17.1182, 76.80941],\n              [15.91315, 76.77045],\n              [13.76259, 77.38035],\n              [14.66956, 77.73565],\n              [13.1706, 78.02493],\n              [11.22231, 78.8693],\n              [10.44453, 79.65239],\n              [13.17077, 80.01046],\n              [13.71852, 79.66039],\n              [15.14282, 79.67431],\n              [15.52255, 80.01608],\n              [16.99085, 80.05086],\n              [18.25183, 79.70175]\n            ]\n          ],\n          [\n            [\n              [25.447625, 80.40734],\n              [27.407506, 80.056406],\n              [25.924651, 79.517834],\n              [23.024466, 79.400012],\n              [20.075188, 79.566823],\n              [19.897266, 79.842362],\n              [18.462264, 79.85988],\n              [17.368015, 80.318896],\n              [20.455992, 80.598156],\n              [21.907945, 80.357679],\n              [22.919253, 80.657144],\n              [25.447625, 80.40734]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Nepal\",\n        \"SOV_A3\": \"NPL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Nepal\",\n        \"ADM0_A3\": \"NPL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Nepal\",\n        \"GU_A3\": \"NPL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Nepal\",\n        \"SU_A3\": \"NPL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Nepal\",\n        \"NAME_LONG\": \"Nepal\",\n        \"BRK_A3\": \"NPL\",\n        \"BRK_NAME\": \"Nepal\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Nepal\",\n        \"POSTAL\": \"NP\",\n        \"FORMAL_EN\": \"Nepal\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Nepal\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Nepal\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 29384297,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 71520,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NP\",\n        \"ISO_A2\": \"NP\",\n        \"ISO_A3\": \"NPL\",\n        \"ISO_A3_EH\": \"NPL\",\n        \"ISO_N3\": \"524\",\n        \"UN_A3\": \"524\",\n        \"WB_A2\": \"NP\",\n        \"WB_A3\": \"NPL\",\n        \"WOE_ID\": 23424911,\n        \"WOE_ID_EH\": 23424911,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NPL\",\n        \"ADM0_A3_US\": \"NPL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [80.088425, 26.397898, 88.174804, 30.422717],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [81.111256, 30.183481],\n            [81.525804, 30.422717],\n            [82.327513, 30.115268],\n            [83.337115, 29.463732],\n            [83.898993, 29.320226],\n            [84.23458, 28.839894],\n            [85.011638, 28.642774],\n            [85.82332, 28.203576],\n            [86.954517, 27.974262],\n            [88.120441, 27.876542],\n            [88.043133, 27.445819],\n            [88.174804, 26.810405],\n            [88.060238, 26.414615],\n            [87.227472, 26.397898],\n            [86.024393, 26.630985],\n            [85.251779, 26.726198],\n            [84.675018, 27.234901],\n            [83.304249, 27.364506],\n            [81.999987, 27.925479],\n            [81.057203, 28.416095],\n            [80.088425, 28.79447],\n            [80.476721, 29.729865],\n            [81.111256, 30.183481]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"New Zealand\",\n        \"SOV_A3\": \"NZ1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"New Zealand\",\n        \"ADM0_A3\": \"NZL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"New Zealand\",\n        \"GU_A3\": \"NZL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"New Zealand\",\n        \"SU_A3\": \"NZL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"New Zealand\",\n        \"NAME_LONG\": \"New Zealand\",\n        \"BRK_A3\": \"NZL\",\n        \"BRK_NAME\": \"New Zealand\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"N.Z.\",\n        \"POSTAL\": \"NZ\",\n        \"FORMAL_EN\": \"New Zealand\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"New Zealand\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"New Zealand\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 4510327,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 174800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2006,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NZ\",\n        \"ISO_A2\": \"NZ\",\n        \"ISO_A3\": \"NZL\",\n        \"ISO_A3_EH\": \"NZL\",\n        \"ISO_N3\": \"554\",\n        \"UN_A3\": \"554\",\n        \"WB_A2\": \"NZ\",\n        \"WB_A3\": \"NZL\",\n        \"WOE_ID\": 23424916,\n        \"WOE_ID_EH\": 23424916,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"NZL\",\n        \"ADM0_A3_US\": \"NZL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Australia and New Zealand\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [166.509144, -46.641235, 178.517094, -34.450662],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [173.020375, -40.919052],\n              [173.247234, -41.331999],\n              [173.958405, -40.926701],\n              [174.247587, -41.349155],\n              [174.248517, -41.770008],\n              [173.876447, -42.233184],\n              [173.22274, -42.970038],\n              [172.711246, -43.372288],\n              [173.080113, -43.853344],\n              [172.308584, -43.865694],\n              [171.452925, -44.242519],\n              [171.185138, -44.897104],\n              [170.616697, -45.908929],\n              [169.831422, -46.355775],\n              [169.332331, -46.641235],\n              [168.411354, -46.619945],\n              [167.763745, -46.290197],\n              [166.676886, -46.219917],\n              [166.509144, -45.852705],\n              [167.046424, -45.110941],\n              [168.303763, -44.123973],\n              [168.949409, -43.935819],\n              [169.667815, -43.555326],\n              [170.52492, -43.031688],\n              [171.12509, -42.512754],\n              [171.569714, -41.767424],\n              [171.948709, -41.514417],\n              [172.097227, -40.956104],\n              [172.79858, -40.493962],\n              [173.020375, -40.919052]\n            ]\n          ],\n          [\n            [\n              [174.612009, -36.156397],\n              [175.336616, -37.209098],\n              [175.357596, -36.526194],\n              [175.808887, -36.798942],\n              [175.95849, -37.555382],\n              [176.763195, -37.881253],\n              [177.438813, -37.961248],\n              [178.010354, -37.579825],\n              [178.517094, -37.695373],\n              [178.274731, -38.582813],\n              [177.97046, -39.166343],\n              [177.206993, -39.145776],\n              [176.939981, -39.449736],\n              [177.032946, -39.879943],\n              [176.885824, -40.065978],\n              [176.508017, -40.604808],\n              [176.01244, -41.289624],\n              [175.239567, -41.688308],\n              [175.067898, -41.425895],\n              [174.650973, -41.281821],\n              [175.22763, -40.459236],\n              [174.900157, -39.908933],\n              [173.824047, -39.508854],\n              [173.852262, -39.146602],\n              [174.574802, -38.797683],\n              [174.743474, -38.027808],\n              [174.697017, -37.381129],\n              [174.292028, -36.711092],\n              [174.319004, -36.534824],\n              [173.840997, -36.121981],\n              [173.054171, -35.237125],\n              [172.636005, -34.529107],\n              [173.007042, -34.450662],\n              [173.551298, -35.006183],\n              [174.32939, -35.265496],\n              [174.612009, -36.156397]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Oman\",\n        \"SOV_A3\": \"OMN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Oman\",\n        \"ADM0_A3\": \"OMN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Oman\",\n        \"GU_A3\": \"OMN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Oman\",\n        \"SU_A3\": \"OMN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Oman\",\n        \"NAME_LONG\": \"Oman\",\n        \"BRK_A3\": \"OMN\",\n        \"BRK_NAME\": \"Oman\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Oman\",\n        \"POSTAL\": \"OM\",\n        \"FORMAL_EN\": \"Sultanate of Oman\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Oman\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Oman\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 3424386,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 173100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"MU\",\n        \"ISO_A2\": \"OM\",\n        \"ISO_A3\": \"OMN\",\n        \"ISO_A3_EH\": \"OMN\",\n        \"ISO_N3\": \"512\",\n        \"UN_A3\": \"512\",\n        \"WB_A2\": \"OM\",\n        \"WB_A3\": \"OMN\",\n        \"WOE_ID\": 23424898,\n        \"WOE_ID_EH\": 23424898,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"OMN\",\n        \"ADM0_A3_US\": \"OMN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [52.00001, 16.651051, 59.80806, 26.395934],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [56.261042, 25.714606],\n              [56.070821, 26.055464],\n              [56.362017, 26.395934],\n              [56.485679, 26.309118],\n              [56.391421, 25.895991],\n              [56.261042, 25.714606]\n            ]\n          ],\n          [\n            [\n              [55.208341, 22.70833],\n              [55.234489, 23.110993],\n              [55.525841, 23.524869],\n              [55.528632, 23.933604],\n              [55.981214, 24.130543],\n              [55.804119, 24.269604],\n              [55.886233, 24.920831],\n              [56.396847, 24.924732],\n              [56.84514, 24.241673],\n              [57.403453, 23.878594],\n              [58.136948, 23.747931],\n              [58.729211, 23.565668],\n              [59.180502, 22.992395],\n              [59.450098, 22.660271],\n              [59.80806, 22.533612],\n              [59.806148, 22.310525],\n              [59.442191, 21.714541],\n              [59.282408, 21.433886],\n              [58.861141, 21.114035],\n              [58.487986, 20.428986],\n              [58.034318, 20.481437],\n              [57.826373, 20.243002],\n              [57.665762, 19.736005],\n              [57.7887, 19.06757],\n              [57.694391, 18.94471],\n              [57.234264, 18.947991],\n              [56.609651, 18.574267],\n              [56.512189, 18.087113],\n              [56.283521, 17.876067],\n              [55.661492, 17.884128],\n              [55.269939, 17.632309],\n              [55.2749, 17.228354],\n              [54.791002, 16.950697],\n              [54.239253, 17.044981],\n              [53.570508, 16.707663],\n              [53.108573, 16.651051],\n              [52.782184, 17.349742],\n              [52.00001, 19.000003],\n              [54.999982, 19.999994],\n              [55.666659, 22.000001],\n              [55.208341, 22.70833]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Pakistan\",\n        \"SOV_A3\": \"PAK\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Pakistan\",\n        \"ADM0_A3\": \"PAK\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Pakistan\",\n        \"GU_A3\": \"PAK\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Pakistan\",\n        \"SU_A3\": \"PAK\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Pakistan\",\n        \"NAME_LONG\": \"Pakistan\",\n        \"BRK_A3\": \"PAK\",\n        \"BRK_NAME\": \"Pakistan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Pak.\",\n        \"POSTAL\": \"PK\",\n        \"FORMAL_EN\": \"Islamic Republic of Pakistan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Pakistan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Pakistan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 204924861,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 988200,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1998,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PK\",\n        \"ISO_A2\": \"PK\",\n        \"ISO_A3\": \"PAK\",\n        \"ISO_A3_EH\": \"PAK\",\n        \"ISO_N3\": \"586\",\n        \"UN_A3\": \"586\",\n        \"WB_A2\": \"PK\",\n        \"WB_A3\": \"PAK\",\n        \"WOE_ID\": 23424922,\n        \"WOE_ID_EH\": 23424922,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PAK\",\n        \"ADM0_A3_US\": \"PAK\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Southern Asia\",\n        \"REGION_WB\": \"South Asia\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [60.874248, 23.691965, 77.837451, 37.133031],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [60.874248, 29.829239],\n            [62.549857, 29.318572],\n            [63.550261, 29.468331],\n            [64.148002, 29.340819],\n            [64.350419, 29.560031],\n            [65.046862, 29.472181],\n            [66.346473, 29.887943],\n            [66.381458, 30.738899],\n            [66.938891, 31.304911],\n            [67.683394, 31.303154],\n            [67.792689, 31.58293],\n            [68.556932, 31.71331],\n            [68.926677, 31.620189],\n            [69.317764, 31.901412],\n            [69.262522, 32.501944],\n            [69.687147, 33.105499],\n            [70.323594, 33.358533],\n            [69.930543, 34.02012],\n            [70.881803, 33.988856],\n            [71.156773, 34.348911],\n            [71.115019, 34.733126],\n            [71.613076, 35.153203],\n            [71.498768, 35.650563],\n            [71.262348, 36.074388],\n            [71.846292, 36.509942],\n            [72.920025, 36.720007],\n            [74.067552, 36.836176],\n            [74.575893, 37.020841],\n            [75.158028, 37.133031],\n            [75.896897, 36.666806],\n            [76.192848, 35.898403],\n            [77.837451, 35.49401],\n            [76.871722, 34.653544],\n            [75.757061, 34.504923],\n            [74.240203, 34.748887],\n            [73.749948, 34.317699],\n            [74.104294, 33.441473],\n            [74.451559, 32.7649],\n            [75.258642, 32.271105],\n            [74.405929, 31.692639],\n            [74.42138, 30.979815],\n            [73.450638, 29.976413],\n            [72.823752, 28.961592],\n            [71.777666, 27.91318],\n            [70.616496, 27.989196],\n            [69.514393, 26.940966],\n            [70.168927, 26.491872],\n            [70.282873, 25.722229],\n            [70.844699, 25.215102],\n            [71.04324, 24.356524],\n            [68.842599, 24.359134],\n            [68.176645, 23.691965],\n            [67.443667, 23.944844],\n            [67.145442, 24.663611],\n            [66.372828, 25.425141],\n            [64.530408, 25.237039],\n            [62.905701, 25.218409],\n            [61.497363, 25.078237],\n            [61.874187, 26.239975],\n            [63.316632, 26.756532],\n            [63.233898, 27.217047],\n            [62.755426, 27.378923],\n            [62.72783, 28.259645],\n            [61.771868, 28.699334],\n            [61.369309, 29.303276],\n            [60.874248, 29.829239]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Panama\",\n        \"SOV_A3\": \"PAN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Panama\",\n        \"ADM0_A3\": \"PAN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Panama\",\n        \"GU_A3\": \"PAN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Panama\",\n        \"SU_A3\": \"PAN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Panama\",\n        \"NAME_LONG\": \"Panama\",\n        \"BRK_A3\": \"PAN\",\n        \"BRK_NAME\": \"Panama\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Pan.\",\n        \"POSTAL\": \"PA\",\n        \"FORMAL_EN\": \"Republic of Panama\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Panama\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Panama\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 3753142,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 93120,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PM\",\n        \"ISO_A2\": \"PA\",\n        \"ISO_A3\": \"PAN\",\n        \"ISO_A3_EH\": \"PAN\",\n        \"ISO_N3\": \"591\",\n        \"UN_A3\": \"591\",\n        \"WB_A2\": \"PA\",\n        \"WB_A3\": \"PAN\",\n        \"WOE_ID\": 23424924,\n        \"WOE_ID_EH\": 23424924,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PAN\",\n        \"ADM0_A3_US\": \"PAN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-82.965783, 7.220541, -77.242566, 9.61161],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-77.353361, 8.670505],\n            [-77.474723, 8.524286],\n            [-77.242566, 7.935278],\n            [-77.431108, 7.638061],\n            [-77.753414, 7.70984],\n            [-77.881571, 7.223771],\n            [-78.214936, 7.512255],\n            [-78.429161, 8.052041],\n            [-78.182096, 8.319182],\n            [-78.435465, 8.387705],\n            [-78.622121, 8.718124],\n            [-79.120307, 8.996092],\n            [-79.557877, 8.932375],\n            [-79.760578, 8.584515],\n            [-80.164481, 8.333316],\n            [-80.382659, 8.298409],\n            [-80.480689, 8.090308],\n            [-80.00369, 7.547524],\n            [-80.276671, 7.419754],\n            [-80.421158, 7.271572],\n            [-80.886401, 7.220541],\n            [-81.059543, 7.817921],\n            [-81.189716, 7.647906],\n            [-81.519515, 7.70661],\n            [-81.721311, 8.108963],\n            [-82.131441, 8.175393],\n            [-82.390934, 8.292362],\n            [-82.820081, 8.290864],\n            [-82.850958, 8.073823],\n            [-82.965783, 8.225028],\n            [-82.913176, 8.423517],\n            [-82.829771, 8.626295],\n            [-82.868657, 8.807266],\n            [-82.719183, 8.925709],\n            [-82.927155, 9.07433],\n            [-82.932891, 9.476812],\n            [-82.546196, 9.566135],\n            [-82.187123, 9.207449],\n            [-82.207586, 8.995575],\n            [-81.808567, 8.950617],\n            [-81.714154, 9.031955],\n            [-81.439287, 8.786234],\n            [-80.947302, 8.858504],\n            [-80.521901, 9.111072],\n            [-79.9146, 9.312765],\n            [-79.573303, 9.61161],\n            [-79.021192, 9.552931],\n            [-79.05845, 9.454565],\n            [-78.500888, 9.420459],\n            [-78.055928, 9.24773],\n            [-77.729514, 8.946844],\n            [-77.353361, 8.670505]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Peru\",\n        \"SOV_A3\": \"PER\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Peru\",\n        \"ADM0_A3\": \"PER\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Peru\",\n        \"GU_A3\": \"PER\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Peru\",\n        \"SU_A3\": \"PER\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Peru\",\n        \"NAME_LONG\": \"Peru\",\n        \"BRK_A3\": \"PER\",\n        \"BRK_NAME\": \"Peru\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Peru\",\n        \"POSTAL\": \"PE\",\n        \"FORMAL_EN\": \"Republic of Peru\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Peru\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Peru\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 31036656,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 410400,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PE\",\n        \"ISO_A2\": \"PE\",\n        \"ISO_A3\": \"PER\",\n        \"ISO_A3_EH\": \"PER\",\n        \"ISO_N3\": \"604\",\n        \"UN_A3\": \"604\",\n        \"WB_A2\": \"PE\",\n        \"WB_A3\": \"PER\",\n        \"WOE_ID\": 23424919,\n        \"WOE_ID_EH\": 23424919,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PER\",\n        \"ADM0_A3_US\": \"PER\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [-81.410943, -18.347975, -68.66508, -0.057205],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-69.529678, -10.951734],\n            [-68.66508, -12.5613],\n            [-68.88008, -12.899729],\n            [-68.929224, -13.602684],\n            [-68.948887, -14.453639],\n            [-69.339535, -14.953195],\n            [-69.160347, -15.323974],\n            [-69.389764, -15.660129],\n            [-68.959635, -16.500698],\n            [-69.590424, -17.580012],\n            [-69.858444, -18.092694],\n            [-70.372572, -18.347975],\n            [-71.37525, -17.773799],\n            [-71.462041, -17.363488],\n            [-73.44453, -16.359363],\n            [-75.237883, -15.265683],\n            [-76.009205, -14.649286],\n            [-76.423469, -13.823187],\n            [-76.259242, -13.535039],\n            [-77.106192, -12.222716],\n            [-78.092153, -10.377712],\n            [-79.036953, -8.386568],\n            [-79.44592, -7.930833],\n            [-79.760578, -7.194341],\n            [-80.537482, -6.541668],\n            [-81.249996, -6.136834],\n            [-80.926347, -5.690557],\n            [-81.410943, -4.736765],\n            [-81.09967, -4.036394],\n            [-80.302561, -3.404856],\n            [-80.184015, -3.821162],\n            [-80.469295, -4.059287],\n            [-80.442242, -4.425724],\n            [-80.028908, -4.346091],\n            [-79.624979, -4.454198],\n            [-79.205289, -4.959129],\n            [-78.639897, -4.547784],\n            [-78.450684, -3.873097],\n            [-77.837905, -3.003021],\n            [-76.635394, -2.608678],\n            [-75.544996, -1.56161],\n            [-75.233723, -0.911417],\n            [-75.373223, -0.152032],\n            [-75.106625, -0.057205],\n            [-74.441601, -0.53082],\n            [-74.122395, -1.002833],\n            [-73.659504, -1.260491],\n            [-73.070392, -2.308954],\n            [-72.325787, -2.434218],\n            [-71.774761, -2.16979],\n            [-71.413646, -2.342802],\n            [-70.813476, -2.256865],\n            [-70.047709, -2.725156],\n            [-70.692682, -3.742872],\n            [-70.394044, -3.766591],\n            [-69.893635, -4.298187],\n            [-70.794769, -4.251265],\n            [-70.928843, -4.401591],\n            [-71.748406, -4.593983],\n            [-72.891928, -5.274561],\n            [-72.964507, -5.741251],\n            [-73.219711, -6.089189],\n            [-73.120027, -6.629931],\n            [-73.724487, -6.918595],\n            [-73.723401, -7.340999],\n            [-73.987235, -7.52383],\n            [-73.571059, -8.424447],\n            [-73.015383, -9.032833],\n            [-73.226713, -9.462213],\n            [-72.563033, -9.520194],\n            [-72.184891, -10.053598],\n            [-71.302412, -10.079436],\n            [-70.481894, -9.490118],\n            [-70.548686, -11.009147],\n            [-70.093752, -11.123972],\n            [-69.529678, -10.951734]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Philippines\",\n        \"SOV_A3\": \"PHL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Philippines\",\n        \"ADM0_A3\": \"PHL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Philippines\",\n        \"GU_A3\": \"PHL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Philippines\",\n        \"SU_A3\": \"PHL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Philippines\",\n        \"NAME_LONG\": \"Philippines\",\n        \"BRK_A3\": \"PHL\",\n        \"BRK_NAME\": \"Philippines\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Phil.\",\n        \"POSTAL\": \"PH\",\n        \"FORMAL_EN\": \"Republic of the Philippines\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Philippines\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Philippines\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 104256076,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 801900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"RP\",\n        \"ISO_A2\": \"PH\",\n        \"ISO_A3\": \"PHL\",\n        \"ISO_A3_EH\": \"PHL\",\n        \"ISO_N3\": \"608\",\n        \"UN_A3\": \"608\",\n        \"WB_A2\": \"PH\",\n        \"WB_A3\": \"PHL\",\n        \"WOE_ID\": 23424934,\n        \"WOE_ID_EH\": 23424934,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PHL\",\n        \"ADM0_A3_US\": \"PHL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [117.174275, 5.581003, 126.537424, 18.505227],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [126.376814, 8.414706],\n              [126.478513, 7.750354],\n              [126.537424, 7.189381],\n              [126.196773, 6.274294],\n              [125.831421, 7.293715],\n              [125.363852, 6.786485],\n              [125.683161, 6.049657],\n              [125.396512, 5.581003],\n              [124.219788, 6.161355],\n              [123.93872, 6.885136],\n              [124.243662, 7.36061],\n              [123.610212, 7.833527],\n              [123.296071, 7.418876],\n              [122.825506, 7.457375],\n              [122.085499, 6.899424],\n              [121.919928, 7.192119],\n              [122.312359, 8.034962],\n              [122.942398, 8.316237],\n              [123.487688, 8.69301],\n              [123.841154, 8.240324],\n              [124.60147, 8.514158],\n              [124.764612, 8.960409],\n              [125.471391, 8.986997],\n              [125.412118, 9.760335],\n              [126.222714, 9.286074],\n              [126.306637, 8.782487],\n              [126.376814, 8.414706]\n            ]\n          ],\n          [\n            [\n              [123.982438, 10.278779],\n              [123.623183, 9.950091],\n              [123.309921, 9.318269],\n              [122.995883, 9.022189],\n              [122.380055, 9.713361],\n              [122.586089, 9.981045],\n              [122.837081, 10.261157],\n              [122.947411, 10.881868],\n              [123.49885, 10.940624],\n              [123.337774, 10.267384],\n              [124.077936, 11.232726],\n              [123.982438, 10.278779]\n            ]\n          ],\n          [\n            [\n              [118.504581, 9.316383],\n              [117.174275, 8.3675],\n              [117.664477, 9.066889],\n              [118.386914, 9.6845],\n              [118.987342, 10.376292],\n              [119.511496, 11.369668],\n              [119.689677, 10.554291],\n              [119.029458, 10.003653],\n              [118.504581, 9.316383]\n            ]\n          ],\n          [\n            [\n              [121.883548, 11.891755],\n              [122.483821, 11.582187],\n              [123.120217, 11.58366],\n              [123.100838, 11.165934],\n              [122.637714, 10.741308],\n              [122.00261, 10.441017],\n              [121.967367, 10.905691],\n              [122.03837, 11.415841],\n              [121.883548, 11.891755]\n            ]\n          ],\n          [\n            [\n              [125.502552, 12.162695],\n              [125.783465, 11.046122],\n              [125.011884, 11.311455],\n              [125.032761, 10.975816],\n              [125.277449, 10.358722],\n              [124.801819, 10.134679],\n              [124.760168, 10.837995],\n              [124.459101, 10.88993],\n              [124.302522, 11.495371],\n              [124.891013, 11.415583],\n              [124.87799, 11.79419],\n              [124.266762, 12.557761],\n              [125.227116, 12.535721],\n              [125.502552, 12.162695]\n            ]\n          ],\n          [\n            [\n              [121.527394, 13.06959],\n              [121.26219, 12.20556],\n              [120.833896, 12.704496],\n              [120.323436, 13.466413],\n              [121.180128, 13.429697],\n              [121.527394, 13.06959]\n            ]\n          ],\n          [\n            [\n              [121.321308, 18.504065],\n              [121.937601, 18.218552],\n              [122.246006, 18.47895],\n              [122.336957, 18.224883],\n              [122.174279, 17.810283],\n              [122.515654, 17.093505],\n              [122.252311, 16.262444],\n              [121.662786, 15.931018],\n              [121.50507, 15.124814],\n              [121.728829, 14.328376],\n              [122.258925, 14.218202],\n              [122.701276, 14.336541],\n              [123.950295, 13.782131],\n              [123.855107, 13.237771],\n              [124.181289, 12.997527],\n              [124.077419, 12.536677],\n              [123.298035, 13.027526],\n              [122.928652, 13.55292],\n              [122.671355, 13.185836],\n              [122.03465, 13.784482],\n              [121.126385, 13.636687],\n              [120.628637, 13.857656],\n              [120.679384, 14.271016],\n              [120.991819, 14.525393],\n              [120.693336, 14.756671],\n              [120.564145, 14.396279],\n              [120.070429, 14.970869],\n              [119.920929, 15.406347],\n              [119.883773, 16.363704],\n              [120.286488, 16.034629],\n              [120.390047, 17.599081],\n              [120.715867, 18.505227],\n              [121.321308, 18.504065]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Papua New Guinea\",\n        \"SOV_A3\": \"PNG\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Papua New Guinea\",\n        \"ADM0_A3\": \"PNG\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Papua New Guinea\",\n        \"GU_A3\": \"PNG\",\n        \"SU_DIF\": 1,\n        \"SUBUNIT\": \"Papua New Guinea\",\n        \"SU_A3\": \"PN1\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Papua New Guinea\",\n        \"NAME_LONG\": \"Papua New Guinea\",\n        \"BRK_A3\": \"PN1\",\n        \"BRK_NAME\": \"Papua New Guinea\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"P.N.G.\",\n        \"POSTAL\": \"PG\",\n        \"FORMAL_EN\": \"Independent State of Papua New Guinea\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Papua New Guinea\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Papua New Guinea\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 6909701,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 28020,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2000,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PP\",\n        \"ISO_A2\": \"PG\",\n        \"ISO_A3\": \"PNG\",\n        \"ISO_A3_EH\": \"PNG\",\n        \"ISO_N3\": \"598\",\n        \"UN_A3\": \"598\",\n        \"WB_A2\": \"PG\",\n        \"WB_A3\": \"PNG\",\n        \"WOE_ID\": 23424926,\n        \"WOE_ID_EH\": 23424926,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PNG\",\n        \"ADM0_A3_US\": \"PNG\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Melanesia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 16,\n        \"LONG_LEN\": 16,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2.5,\n        \"MAX_LABEL\": 7.5\n      },\n      \"bbox\": [141.00021, -10.652476, 156.019965, -2.500002],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [141.033852, -9.117893],\n              [141.017057, -5.859022],\n              [141.00021, -2.600151],\n              [142.735247, -3.289153],\n              [144.583971, -3.861418],\n              [145.27318, -4.373738],\n              [145.829786, -4.876498],\n              [145.981922, -5.465609],\n              [147.648073, -6.083659],\n              [147.891108, -6.614015],\n              [146.970905, -6.721657],\n              [147.191874, -7.388024],\n              [148.084636, -8.044108],\n              [148.734105, -9.104664],\n              [149.306835, -9.071436],\n              [149.266631, -9.514406],\n              [150.038728, -9.684318],\n              [149.738798, -9.872937],\n              [150.801628, -10.293687],\n              [150.690575, -10.582713],\n              [150.028393, -10.652476],\n              [149.78231, -10.393267],\n              [148.923138, -10.280923],\n              [147.913018, -10.130441],\n              [147.135443, -9.492444],\n              [146.567881, -8.942555],\n              [146.048481, -8.067414],\n              [144.744168, -7.630128],\n              [143.897088, -7.91533],\n              [143.286376, -8.245491],\n              [143.413913, -8.983069],\n              [142.628431, -9.326821],\n              [142.068259, -9.159596],\n              [141.033852, -9.117893]\n            ]\n          ],\n          [\n            [\n              [155.880026, -6.819997],\n              [155.599991, -6.919991],\n              [155.166994, -6.535931],\n              [154.729192, -5.900828],\n              [154.514114, -5.139118],\n              [154.652504, -5.042431],\n              [154.759991, -5.339984],\n              [155.062918, -5.566792],\n              [155.547746, -6.200655],\n              [156.019965, -6.540014],\n              [155.880026, -6.819997]\n            ]\n          ],\n          [\n            [\n              [151.982796, -5.478063],\n              [151.459107, -5.56028],\n              [151.30139, -5.840728],\n              [150.754447, -6.083763],\n              [150.241197, -6.317754],\n              [149.709963, -6.316513],\n              [148.890065, -6.02604],\n              [148.318937, -5.747142],\n              [148.401826, -5.437756],\n              [149.298412, -5.583742],\n              [149.845562, -5.505503],\n              [149.99625, -5.026101],\n              [150.139756, -5.001348],\n              [150.236908, -5.53222],\n              [150.807467, -5.455842],\n              [151.089672, -5.113693],\n              [151.647881, -4.757074],\n              [151.537862, -4.167807],\n              [152.136792, -4.14879],\n              [152.338743, -4.312966],\n              [152.318693, -4.867661],\n              [151.982796, -5.478063]\n            ]\n          ],\n          [\n            [\n              [153.140038, -4.499983],\n              [152.827292, -4.766427],\n              [152.638673, -4.176127],\n              [152.406026, -3.789743],\n              [151.953237, -3.462062],\n              [151.384279, -3.035422],\n              [150.66205, -2.741486],\n              [150.939965, -2.500002],\n              [151.479984, -2.779985],\n              [151.820015, -2.999972],\n              [152.239989, -3.240009],\n              [152.640017, -3.659983],\n              [153.019994, -3.980015],\n              [153.140038, -4.499983]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Poland\",\n        \"SOV_A3\": \"POL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Poland\",\n        \"ADM0_A3\": \"POL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Poland\",\n        \"GU_A3\": \"POL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Poland\",\n        \"SU_A3\": \"POL\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Poland\",\n        \"NAME_LONG\": \"Poland\",\n        \"BRK_A3\": \"POL\",\n        \"BRK_NAME\": \"Poland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Pol.\",\n        \"POSTAL\": \"PL\",\n        \"FORMAL_EN\": \"Republic of Poland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Poland\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Poland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 38476269,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 1052000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PL\",\n        \"ISO_A2\": \"PL\",\n        \"ISO_A3\": \"POL\",\n        \"ISO_A3_EH\": \"POL\",\n        \"ISO_N3\": \"616\",\n        \"UN_A3\": \"616\",\n        \"WB_A2\": \"PL\",\n        \"WB_A3\": \"POL\",\n        \"WOE_ID\": 23424923,\n        \"WOE_ID_EH\": 23424923,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"POL\",\n        \"ADM0_A3_US\": \"POL\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [14.074521, 49.027395, 24.029986, 54.851536],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [23.484128, 53.912498],\n            [23.527536, 53.470122],\n            [23.804935, 53.089731],\n            [23.799199, 52.691099],\n            [23.199494, 52.486977],\n            [23.508002, 52.023647],\n            [23.527071, 51.578454],\n            [24.029986, 50.705407],\n            [23.922757, 50.424881],\n            [23.426508, 50.308506],\n            [22.51845, 49.476774],\n            [22.776419, 49.027395],\n            [22.558138, 49.085738],\n            [21.607808, 49.470107],\n            [20.887955, 49.328772],\n            [20.415839, 49.431453],\n            [19.825023, 49.217125],\n            [19.320713, 49.571574],\n            [18.909575, 49.435846],\n            [18.853144, 49.49623],\n            [18.392914, 49.988629],\n            [17.649445, 50.049038],\n            [17.554567, 50.362146],\n            [16.868769, 50.473974],\n            [16.719476, 50.215747],\n            [16.176253, 50.422607],\n            [16.238627, 50.697733],\n            [15.490972, 50.78473],\n            [15.016996, 51.106674],\n            [14.607098, 51.745188],\n            [14.685026, 52.089947],\n            [14.4376, 52.62485],\n            [14.074521, 52.981263],\n            [14.353315, 53.248171],\n            [14.119686, 53.757029],\n            [14.8029, 54.050706],\n            [16.363477, 54.513159],\n            [17.622832, 54.851536],\n            [18.620859, 54.682606],\n            [18.696255, 54.438719],\n            [19.66064, 54.426084],\n            [20.892245, 54.312525],\n            [22.731099, 54.327537],\n            [23.243987, 54.220567],\n            [23.484128, 53.912498]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"United States of America\",\n        \"SOV_A3\": \"US1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Dependency\",\n        \"ADMIN\": \"Puerto Rico\",\n        \"ADM0_A3\": \"PRI\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Puerto Rico\",\n        \"GU_A3\": \"PRI\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Puerto Rico\",\n        \"SU_A3\": \"PRI\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Puerto Rico\",\n        \"NAME_LONG\": \"Puerto Rico\",\n        \"BRK_A3\": \"PRI\",\n        \"BRK_NAME\": \"Puerto Rico\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"P.R.\",\n        \"POSTAL\": \"PR\",\n        \"FORMAL_EN\": \"Commonwealth of Puerto Rico\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Puerto Rico\",\n        \"NOTE_ADM0\": \"Commonwealth of U.S.A.\",\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Puerto Rico\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 3351827,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 131000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"RQ\",\n        \"ISO_A2\": \"PR\",\n        \"ISO_A3\": \"PRI\",\n        \"ISO_A3_EH\": \"PRI\",\n        \"ISO_N3\": \"630\",\n        \"UN_A3\": \"630\",\n        \"WB_A2\": \"PR\",\n        \"WB_A3\": \"PRI\",\n        \"WOE_ID\": 23424935,\n        \"WOE_ID_EH\": 23424935,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PRI\",\n        \"ADM0_A3_US\": \"PRI\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": -99,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-67.242428, 17.946553, -65.591004, 18.520601],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-66.282434, 18.514762],\n            [-65.771303, 18.426679],\n            [-65.591004, 18.228035],\n            [-65.847164, 17.975906],\n            [-66.599934, 17.981823],\n            [-67.184162, 17.946553],\n            [-67.242428, 18.37446],\n            [-67.100679, 18.520601],\n            [-66.282434, 18.514762]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"North Korea\",\n        \"SOV_A3\": \"PRK\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"North Korea\",\n        \"ADM0_A3\": \"PRK\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"North Korea\",\n        \"GU_A3\": \"PRK\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"North Korea\",\n        \"SU_A3\": \"PRK\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"North Korea\",\n        \"NAME_LONG\": \"Dem. Rep. Korea\",\n        \"BRK_A3\": \"PRK\",\n        \"BRK_NAME\": \"Dem. Rep. Korea\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"N.K.\",\n        \"POSTAL\": \"KP\",\n        \"FORMAL_EN\": \"Democratic People's Republic of Korea\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Korea, North\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Korea, Dem. Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 25248140,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 40000,\n        \"POP_YEAR\": 2013,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"KN\",\n        \"ISO_A2\": \"KP\",\n        \"ISO_A3\": \"PRK\",\n        \"ISO_A3_EH\": \"PRK\",\n        \"ISO_N3\": \"408\",\n        \"UN_A3\": \"408\",\n        \"WB_A2\": \"KP\",\n        \"WB_A3\": \"PRK\",\n        \"WOE_ID\": 23424865,\n        \"WOE_ID_EH\": 23424865,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PRK\",\n        \"ADM0_A3_US\": \"PRK\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 15,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [124.265625, 37.669071, 130.780007, 42.985387],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [124.265625, 39.928493],\n              [125.079942, 40.569824],\n              [126.182045, 41.107336],\n              [126.869083, 41.816569],\n              [127.343783, 41.503152],\n              [128.208433, 41.466772],\n              [128.052215, 41.994285],\n              [129.596669, 42.424982],\n              [129.994267, 42.985387],\n              [130.64, 42.395024],\n              [130.64, 42.395],\n              [130.779992, 42.22001],\n              [130.400031, 42.280004],\n              [129.965949, 41.941368],\n              [129.667362, 41.601104],\n              [129.705189, 40.882828],\n              [129.188115, 40.661808],\n              [129.0104, 40.485436],\n              [128.633368, 40.189847],\n              [127.967414, 40.025413],\n              [127.533436, 39.75685],\n              [127.50212, 39.323931],\n              [127.385434, 39.213472],\n              [127.783343, 39.050898],\n              [128.349716, 38.612243],\n              [128.205746, 38.370397],\n              [127.780035, 38.304536],\n              [127.073309, 38.256115],\n              [126.68372, 37.804773],\n              [126.237339, 37.840378],\n              [126.174759, 37.749686],\n              [125.689104, 37.94001],\n              [125.568439, 37.752089],\n              [125.27533, 37.669071],\n              [125.240087, 37.857224],\n              [124.981033, 37.948821],\n              [124.712161, 38.108346],\n              [124.985994, 38.548474],\n              [125.221949, 38.665857],\n              [125.132859, 38.848559],\n              [125.38659, 39.387958],\n              [125.321116, 39.551385],\n              [124.737482, 39.660344],\n              [124.265625, 39.928493]\n            ]\n          ],\n          [\n            [\n              [130.780005, 42.22001],\n              [130.780007, 42.220007],\n              [130.780004, 42.220008],\n              [130.780005, 42.22001]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Portugal\",\n        \"SOV_A3\": \"PRT\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Portugal\",\n        \"ADM0_A3\": \"PRT\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Portugal\",\n        \"GU_A3\": \"PRT\",\n        \"SU_DIF\": 1,\n        \"SUBUNIT\": \"Portugal\",\n        \"SU_A3\": \"PR1\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Portugal\",\n        \"NAME_LONG\": \"Portugal\",\n        \"BRK_A3\": \"PR1\",\n        \"BRK_NAME\": \"Portugal\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Port.\",\n        \"POSTAL\": \"P\",\n        \"FORMAL_EN\": \"Portuguese Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Portugal\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Portugal\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 10839514,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 297100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PO\",\n        \"ISO_A2\": \"PT\",\n        \"ISO_A3\": \"PRT\",\n        \"ISO_A3_EH\": \"PRT\",\n        \"ISO_N3\": \"620\",\n        \"UN_A3\": \"620\",\n        \"WB_A2\": \"PT\",\n        \"WB_A3\": \"PRT\",\n        \"WOE_ID\": 23424925,\n        \"WOE_ID_EH\": 23424925,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PRT\",\n        \"ADM0_A3_US\": \"PRT\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-9.526571, 36.838269, -6.389088, 42.280469],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-9.034818, 41.880571],\n            [-8.671946, 42.134689],\n            [-8.263857, 42.280469],\n            [-8.013175, 41.790886],\n            [-7.422513, 41.792075],\n            [-7.251309, 41.918346],\n            [-6.668606, 41.883387],\n            [-6.389088, 41.381815],\n            [-6.851127, 41.111083],\n            [-6.86402, 40.330872],\n            [-7.026413, 40.184524],\n            [-7.066592, 39.711892],\n            [-7.498632, 39.629571],\n            [-7.098037, 39.030073],\n            [-7.374092, 38.373059],\n            [-7.029281, 38.075764],\n            [-7.166508, 37.803894],\n            [-7.537105, 37.428904],\n            [-7.453726, 37.097788],\n            [-7.855613, 36.838269],\n            [-8.382816, 36.97888],\n            [-8.898857, 36.868809],\n            [-8.746101, 37.651346],\n            [-8.839998, 38.266243],\n            [-9.287464, 38.358486],\n            [-9.526571, 38.737429],\n            [-9.446989, 39.392066],\n            [-9.048305, 39.755093],\n            [-8.977353, 40.159306],\n            [-8.768684, 40.760639],\n            [-8.790853, 41.184334],\n            [-8.990789, 41.543459],\n            [-9.034818, 41.880571]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Paraguay\",\n        \"SOV_A3\": \"PRY\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Paraguay\",\n        \"ADM0_A3\": \"PRY\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Paraguay\",\n        \"GU_A3\": \"PRY\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Paraguay\",\n        \"SU_A3\": \"PRY\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Paraguay\",\n        \"NAME_LONG\": \"Paraguay\",\n        \"BRK_A3\": \"PRY\",\n        \"BRK_NAME\": \"Paraguay\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Para.\",\n        \"POSTAL\": \"PY\",\n        \"FORMAL_EN\": \"Republic of Paraguay\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Paraguay\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Paraguay\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 6943739,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 64670,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"PA\",\n        \"ISO_A2\": \"PY\",\n        \"ISO_A3\": \"PRY\",\n        \"ISO_A3_EH\": \"PRY\",\n        \"ISO_N3\": \"600\",\n        \"UN_A3\": \"600\",\n        \"WB_A2\": \"PY\",\n        \"WB_A3\": \"PRY\",\n        \"WOE_ID\": 23424917,\n        \"WOE_ID_EH\": 23424917,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PRY\",\n        \"ADM0_A3_US\": \"PRY\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-62.685057, -27.548499, -54.29296, -19.342747],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-54.625291, -25.739255],\n            [-54.788795, -26.621786],\n            [-55.695846, -27.387837],\n            [-56.486702, -27.548499],\n            [-57.60976, -27.395899],\n            [-58.618174, -27.123719],\n            [-57.63366, -25.603657],\n            [-57.777217, -25.16234],\n            [-58.807128, -24.771459],\n            [-60.028966, -24.032796],\n            [-60.846565, -23.880713],\n            [-62.685057, -22.249029],\n            [-62.291179, -21.051635],\n            [-62.265961, -20.513735],\n            [-61.786326, -19.633737],\n            [-60.043565, -19.342747],\n            [-59.115042, -19.356906],\n            [-58.183471, -19.868399],\n            [-58.166392, -20.176701],\n            [-57.870674, -20.732688],\n            [-57.937156, -22.090176],\n            [-56.88151, -22.282154],\n            [-56.473317, -22.0863],\n            [-55.797958, -22.35693],\n            [-55.610683, -22.655619],\n            [-55.517639, -23.571998],\n            [-55.400747, -23.956935],\n            [-55.027902, -24.001274],\n            [-54.652834, -23.839578],\n            [-54.29296, -24.021014],\n            [-54.293476, -24.5708],\n            [-54.428946, -25.162185],\n            [-54.625291, -25.739255]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Israel\",\n        \"SOV_A3\": \"IS1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Disputed\",\n        \"ADMIN\": \"Palestine\",\n        \"ADM0_A3\": \"PSX\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Palestine\",\n        \"GU_A3\": \"PSX\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Palestine\",\n        \"SU_A3\": \"PSX\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Palestine\",\n        \"NAME_LONG\": \"Palestine\",\n        \"BRK_A3\": \"PSX\",\n        \"BRK_NAME\": \"Palestine\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Pal.\",\n        \"POSTAL\": \"PAL\",\n        \"FORMAL_EN\": \"West Bank and Gaza\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": null,\n        \"NOTE_ADM0\": \"Partial self-admin.\",\n        \"NOTE_BRK\": \"Partial self-admin.\",\n        \"NAME_SORT\": \"Palestine (West Bank and Gaza)\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 4543126,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 21220.77,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"-99\",\n        \"ISO_A2\": \"PS\",\n        \"ISO_A3\": \"PSE\",\n        \"ISO_A3_EH\": \"PSE\",\n        \"ISO_N3\": \"275\",\n        \"UN_A3\": \"275\",\n        \"WB_A2\": \"GZ\",\n        \"WB_A3\": \"WBG\",\n        \"WOE_ID\": 28289408,\n        \"WOE_ID_EH\": 28289408,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"PSE\",\n        \"ADM0_A3_US\": \"PSX\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": -99,\n        \"MIN_ZOOM\": 7,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9.5\n      },\n      \"bbox\": [34.927408, 31.353435, 35.545665, 32.532511],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [35.397561, 31.489086],\n            [34.927408, 31.353435],\n            [34.970507, 31.616778],\n            [35.225892, 31.754341],\n            [34.974641, 31.866582],\n            [35.18393, 32.532511],\n            [35.545665, 32.393992],\n            [35.545252, 31.782505],\n            [35.397561, 31.489086]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Qatar\",\n        \"SOV_A3\": \"QAT\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Qatar\",\n        \"ADM0_A3\": \"QAT\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Qatar\",\n        \"GU_A3\": \"QAT\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Qatar\",\n        \"SU_A3\": \"QAT\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Qatar\",\n        \"NAME_LONG\": \"Qatar\",\n        \"BRK_A3\": \"QAT\",\n        \"BRK_NAME\": \"Qatar\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Qatar\",\n        \"POSTAL\": \"QA\",\n        \"FORMAL_EN\": \"State of Qatar\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Qatar\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Qatar\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 2314307,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 334500,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"QA\",\n        \"ISO_A2\": \"QA\",\n        \"ISO_A3\": \"QAT\",\n        \"ISO_A3_EH\": \"QAT\",\n        \"ISO_N3\": \"634\",\n        \"UN_A3\": \"634\",\n        \"WB_A2\": \"QA\",\n        \"WB_A3\": \"QAT\",\n        \"WOE_ID\": 23424930,\n        \"WOE_ID_EH\": 23424930,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"QAT\",\n        \"ADM0_A3_US\": \"QAT\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [50.743911, 24.556331, 51.6067, 26.114582],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [50.810108, 24.754743],\n            [50.743911, 25.482424],\n            [51.013352, 26.006992],\n            [51.286462, 26.114582],\n            [51.589079, 25.801113],\n            [51.6067, 25.21567],\n            [51.389608, 24.627386],\n            [51.112415, 24.556331],\n            [50.810108, 24.754743]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Romania\",\n        \"SOV_A3\": \"ROU\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Romania\",\n        \"ADM0_A3\": \"ROU\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Romania\",\n        \"GU_A3\": \"ROU\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Romania\",\n        \"SU_A3\": \"ROU\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Romania\",\n        \"NAME_LONG\": \"Romania\",\n        \"BRK_A3\": \"ROU\",\n        \"BRK_NAME\": \"Romania\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Rom.\",\n        \"POSTAL\": \"RO\",\n        \"FORMAL_EN\": \"Romania\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Romania\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Romania\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 21529967,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 441000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"RO\",\n        \"ISO_A2\": \"RO\",\n        \"ISO_A3\": \"ROU\",\n        \"ISO_A3_EH\": \"ROU\",\n        \"ISO_N3\": \"642\",\n        \"UN_A3\": \"642\",\n        \"WB_A2\": \"RO\",\n        \"WB_A3\": \"ROM\",\n        \"WOE_ID\": 23424933,\n        \"WOE_ID_EH\": 23424933,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ROU\",\n        \"ADM0_A3_US\": \"ROU\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [20.220192, 43.688445, 29.626543, 48.220881],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [28.558081, 43.707462],\n            [27.970107, 43.812468],\n            [27.2424, 44.175986],\n            [26.065159, 43.943494],\n            [25.569272, 43.688445],\n            [24.100679, 43.741051],\n            [23.332302, 43.897011],\n            [22.944832, 43.823785],\n            [22.65715, 44.234923],\n            [22.474008, 44.409228],\n            [22.705726, 44.578003],\n            [22.459022, 44.702517],\n            [22.145088, 44.478422],\n            [21.562023, 44.768947],\n            [21.483526, 45.18117],\n            [20.874313, 45.416375],\n            [20.762175, 45.734573],\n            [20.220192, 46.127469],\n            [21.021952, 46.316088],\n            [21.626515, 46.994238],\n            [22.099768, 47.672439],\n            [22.710531, 47.882194],\n            [23.142236, 48.096341],\n            [23.760958, 47.985598],\n            [24.402056, 47.981878],\n            [24.866317, 47.737526],\n            [25.207743, 47.891056],\n            [25.945941, 47.987149],\n            [26.19745, 48.220881],\n            [26.619337, 48.220726],\n            [26.924176, 48.123264],\n            [27.233873, 47.826771],\n            [27.551166, 47.405117],\n            [28.12803, 46.810476],\n            [28.160018, 46.371563],\n            [28.054443, 45.944586],\n            [28.233554, 45.488283],\n            [28.679779, 45.304031],\n            [29.149725, 45.464925],\n            [29.603289, 45.293308],\n            [29.626543, 45.035391],\n            [29.141612, 44.82021],\n            [28.837858, 44.913874],\n            [28.558081, 43.707462]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Russia\",\n        \"SOV_A3\": \"RUS\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Russia\",\n        \"ADM0_A3\": \"RUS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Russia\",\n        \"GU_A3\": \"RUS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Russia\",\n        \"SU_A3\": \"RUS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Russia\",\n        \"NAME_LONG\": \"Russian Federation\",\n        \"BRK_A3\": \"RUS\",\n        \"BRK_NAME\": \"Russia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Rus.\",\n        \"POSTAL\": \"RUS\",\n        \"FORMAL_EN\": \"Russian Federation\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Russia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Russian Federation\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 142257519,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 3745000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"3. Emerging region: BRIC\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"RS\",\n        \"ISO_A2\": \"RU\",\n        \"ISO_A3\": \"RUS\",\n        \"ISO_A3_EH\": \"RUS\",\n        \"ISO_N3\": \"643\",\n        \"UN_A3\": \"643\",\n        \"WB_A2\": \"RU\",\n        \"WB_A3\": \"RUS\",\n        \"WOE_ID\": 23424936,\n        \"WOE_ID_EH\": 23424936,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"RUS\",\n        \"ADM0_A3_US\": \"RUS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 18,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 5.2\n      },\n      \"bbox\": [-180, 41.151416, 180, 81.2504],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [48.584353, 41.808869],\n              [47.987283, 41.405819],\n              [47.815666, 41.151416],\n              [47.373315, 41.219732],\n              [46.686071, 41.827137],\n              [46.404951, 41.860675],\n              [45.7764, 42.09244],\n              [45.470279, 42.502781],\n              [44.537623, 42.711993],\n              [43.93121, 42.55496],\n              [43.75599, 42.74083],\n              [42.3944, 43.2203],\n              [40.92219, 43.38215],\n              [40.076965, 43.553104],\n              [39.955009, 43.434998],\n              [38.68, 44.28],\n              [37.53912, 44.65721],\n              [36.67546, 45.24469],\n              [37.40317, 45.40451],\n              [38.23295, 46.24087],\n              [37.67372, 46.63657],\n              [39.14767, 47.04475],\n              [39.1212, 47.26336],\n              [38.223538, 47.10219],\n              [38.255112, 47.5464],\n              [38.77057, 47.82562],\n              [39.738278, 47.898937],\n              [39.89562, 48.23241],\n              [39.67465, 48.78382],\n              [40.080789, 49.30743],\n              [40.06904, 49.60105],\n              [38.594988, 49.926462],\n              [38.010631, 49.915662],\n              [37.39346, 50.383953],\n              [36.626168, 50.225591],\n              [35.356116, 50.577197],\n              [35.37791, 50.77394],\n              [35.022183, 51.207572],\n              [34.224816, 51.255993],\n              [34.141978, 51.566413],\n              [34.391731, 51.768882],\n              [33.7527, 52.335075],\n              [32.715761, 52.238465],\n              [32.412058, 52.288695],\n              [32.15944, 52.06125],\n              [31.785992, 52.101678],\n              [31.78597, 52.10168],\n              [31.540018, 52.742052],\n              [31.305201, 53.073996],\n              [31.49764, 53.16743],\n              [32.304519, 53.132726],\n              [32.693643, 53.351421],\n              [32.405599, 53.618045],\n              [31.731273, 53.794029],\n              [31.791424, 53.974639],\n              [31.384472, 54.157056],\n              [30.757534, 54.811771],\n              [30.971836, 55.081548],\n              [30.873909, 55.550976],\n              [29.896294, 55.789463],\n              [29.371572, 55.670091],\n              [29.229513, 55.918344],\n              [28.176709, 56.16913],\n              [27.855282, 56.759326],\n              [27.770016, 57.244258],\n              [27.288185, 57.474528],\n              [27.716686, 57.791899],\n              [27.42015, 58.72457],\n              [28.131699, 59.300825],\n              [27.98112, 59.47537],\n              [27.981127, 59.475373],\n              [29.1177, 60.02805],\n              [28.070002, 60.503519],\n              [28.07, 60.50352],\n              [30.211107, 61.780028],\n              [31.139991, 62.357693],\n              [31.516092, 62.867687],\n              [30.035872, 63.552814],\n              [30.444685, 64.204453],\n              [29.54443, 64.948672],\n              [30.21765, 65.80598],\n              [29.054589, 66.944286],\n              [29.977426, 67.698297],\n              [28.445944, 68.364613],\n              [28.59193, 69.064777],\n              [29.39955, 69.15692],\n              [31.101042, 69.558101],\n              [31.10108, 69.55811],\n              [32.13272, 69.90595],\n              [33.77547, 69.30142],\n              [36.51396, 69.06342],\n              [40.29234, 67.9324],\n              [41.05987, 67.45713],\n              [41.12595, 66.79158],\n              [40.01583, 66.26618],\n              [38.38295, 65.99953],\n              [33.91871, 66.75961],\n              [33.18444, 66.63253],\n              [34.81477, 65.90015],\n              [34.878574, 65.436213],\n              [34.94391, 64.41437],\n              [36.23129, 64.10945],\n              [37.01273, 63.84983],\n              [37.14197, 64.33471],\n              [36.539579, 64.76446],\n              [37.17604, 65.14322],\n              [39.59345, 64.52079],\n              [40.4356, 64.76446],\n              [39.7626, 65.49682],\n              [42.09309, 66.47623],\n              [43.01604, 66.41858],\n              [43.94975, 66.06908],\n              [44.53226, 66.75634],\n              [43.69839, 67.35245],\n              [44.18795, 67.95051],\n              [43.45282, 68.57079],\n              [46.25, 68.25],\n              [46.82134, 67.68997],\n              [45.55517, 67.56652],\n              [45.56202, 67.01005],\n              [46.34915, 66.66767],\n              [47.89416, 66.88455],\n              [48.13876, 67.52238],\n              [50.22766, 67.99867],\n              [53.71743, 68.85738],\n              [54.47171, 68.80815],\n              [53.48582, 68.20131],\n              [54.72628, 68.09702],\n              [55.44268, 68.43866],\n              [57.31702, 68.46628],\n              [58.802, 68.88082],\n              [59.94142, 68.27844],\n              [61.07784, 68.94069],\n              [60.03, 69.52],\n              [60.55, 69.85],\n              [63.504, 69.54739],\n              [64.888115, 69.234835],\n              [68.51216, 68.09233],\n              [69.18068, 68.61563],\n              [68.16444, 69.14436],\n              [68.13522, 69.35649],\n              [66.93008, 69.45461],\n              [67.25976, 69.92873],\n              [66.72492, 70.70889],\n              [66.69466, 71.02897],\n              [68.54006, 71.9345],\n              [69.19636, 72.84336],\n              [69.94, 73.04],\n              [72.58754, 72.77629],\n              [72.79603, 72.22006],\n              [71.84811, 71.40898],\n              [72.47011, 71.09019],\n              [72.79188, 70.39114],\n              [72.5647, 69.02085],\n              [73.66787, 68.4079],\n              [73.2387, 67.7404],\n              [71.28, 66.32],\n              [72.42301, 66.17267],\n              [72.82077, 66.53267],\n              [73.92099, 66.78946],\n              [74.18651, 67.28429],\n              [75.052, 67.76047],\n              [74.46926, 68.32899],\n              [74.93584, 68.98918],\n              [73.84236, 69.07146],\n              [73.60187, 69.62763],\n              [74.3998, 70.63175],\n              [73.1011, 71.44717],\n              [74.89082, 72.12119],\n              [74.65926, 72.83227],\n              [75.15801, 72.85497],\n              [75.68351, 72.30056],\n              [75.28898, 71.33556],\n              [76.35911, 71.15287],\n              [75.90313, 71.87401],\n              [77.57665, 72.26717],\n              [79.65202, 72.32011],\n              [81.5, 71.75],\n              [80.61071, 72.58285],\n              [80.51109, 73.6482],\n              [82.25, 73.85],\n              [84.65526, 73.80591],\n              [86.8223, 73.93688],\n              [86.00956, 74.45967],\n              [87.16682, 75.11643],\n              [88.31571, 75.14393],\n              [90.26, 75.64],\n              [92.90058, 75.77333],\n              [93.23421, 76.0472],\n              [95.86, 76.14],\n              [96.67821, 75.91548],\n              [98.92254, 76.44689],\n              [100.75967, 76.43028],\n              [101.03532, 76.86189],\n              [101.99084, 77.28754],\n              [104.3516, 77.69792],\n              [106.06664, 77.37389],\n              [104.705, 77.1274],\n              [106.97013, 76.97419],\n              [107.24, 76.48],\n              [108.1538, 76.72335],\n              [111.07726, 76.71],\n              [113.33151, 76.22224],\n              [114.13417, 75.84764],\n              [113.88539, 75.32779],\n              [112.77918, 75.03186],\n              [110.15125, 74.47673],\n              [109.4, 74.18],\n              [110.64, 74.04],\n              [112.11919, 73.78774],\n              [113.01954, 73.97693],\n              [113.52958, 73.33505],\n              [113.96881, 73.59488],\n              [115.56782, 73.75285],\n              [118.77633, 73.58772],\n              [119.02, 73.12],\n              [123.20066, 72.97122],\n              [123.25777, 73.73503],\n              [125.38, 73.56],\n              [126.97644, 73.56549],\n              [128.59126, 73.03871],\n              [129.05157, 72.39872],\n              [128.46, 71.98],\n              [129.71599, 71.19304],\n              [131.28858, 70.78699],\n              [132.2535, 71.8363],\n              [133.85766, 71.38642],\n              [135.56193, 71.65525],\n              [137.49755, 71.34763],\n              [138.23409, 71.62803],\n              [139.86983, 71.48783],\n              [139.14791, 72.41619],\n              [140.46817, 72.84941],\n              [149.5, 72.2],\n              [150.35118, 71.60643],\n              [152.9689, 70.84222],\n              [157.00688, 71.03141],\n              [158.99779, 70.86672],\n              [159.83031, 70.45324],\n              [159.70866, 69.72198],\n              [160.94053, 69.43728],\n              [162.27907, 69.64204],\n              [164.05248, 69.66823],\n              [165.94037, 69.47199],\n              [167.83567, 69.58269],\n              [169.57763, 68.6938],\n              [170.81688, 69.01363],\n              [170.0082, 69.65276],\n              [170.45345, 70.09703],\n              [173.64391, 69.81743],\n              [175.72403, 69.87725],\n              [178.6, 69.4],\n              [180, 68.963636],\n              [180, 64.979709],\n              [179.99281, 64.97433],\n              [178.7072, 64.53493],\n              [177.41128, 64.60821],\n              [178.313, 64.07593],\n              [178.90825, 63.25197],\n              [179.37034, 62.98262],\n              [179.48636, 62.56894],\n              [179.22825, 62.3041],\n              [177.3643, 62.5219],\n              [174.56929, 61.76915],\n              [173.68013, 61.65261],\n              [172.15, 60.95],\n              [170.6985, 60.33618],\n              [170.33085, 59.88177],\n              [168.90046, 60.57355],\n              [166.29498, 59.78855],\n              [165.84, 60.16],\n              [164.87674, 59.7316],\n              [163.53929, 59.86871],\n              [163.21711, 59.21101],\n              [162.01733, 58.24328],\n              [162.05297, 57.83912],\n              [163.19191, 57.61503],\n              [163.05794, 56.15924],\n              [162.12958, 56.12219],\n              [161.70146, 55.28568],\n              [162.11749, 54.85514],\n              [160.36877, 54.34433],\n              [160.02173, 53.20257],\n              [158.53094, 52.95868],\n              [158.23118, 51.94269],\n              [156.78979, 51.01105],\n              [156.42, 51.7],\n              [155.99182, 53.15895],\n              [155.43366, 55.38103],\n              [155.91442, 56.76792],\n              [156.75815, 57.3647],\n              [156.81035, 57.83204],\n              [158.36433, 58.05575],\n              [160.15064, 59.31477],\n              [161.87204, 60.343],\n              [163.66969, 61.1409],\n              [164.47355, 62.55061],\n              [163.25842, 62.46627],\n              [162.65791, 61.6425],\n              [160.12148, 60.54423],\n              [159.30232, 61.77396],\n              [156.72068, 61.43442],\n              [154.21806, 59.75818],\n              [155.04375, 59.14495],\n              [152.81185, 58.88385],\n              [151.26573, 58.78089],\n              [151.33815, 59.50396],\n              [149.78371, 59.65573],\n              [148.54481, 59.16448],\n              [145.48722, 59.33637],\n              [142.19782, 59.03998],\n              [138.95848, 57.08805],\n              [135.12619, 54.72959],\n              [136.70171, 54.60355],\n              [137.19342, 53.97732],\n              [138.1647, 53.75501],\n              [138.80463, 54.25455],\n              [139.90151, 54.18968],\n              [141.34531, 53.08957],\n              [141.37923, 52.23877],\n              [140.59742, 51.23967],\n              [140.51308, 50.04553],\n              [140.06193, 48.44671],\n              [138.55472, 46.99965],\n              [138.21971, 46.30795],\n              [136.86232, 45.1435],\n              [135.51535, 43.989],\n              [134.86939, 43.39821],\n              [133.53687, 42.81147],\n              [132.90627, 42.79849],\n              [132.27807, 43.28456],\n              [130.93587, 42.55274],\n              [130.780005, 42.22001],\n              [130.780004, 42.220008],\n              [130.78, 42.22],\n              [130.779992, 42.22001],\n              [130.64, 42.395],\n              [130.64, 42.395024],\n              [130.633866, 42.903015],\n              [131.144688, 42.92999],\n              [131.288555, 44.11152],\n              [131.02519, 44.96796],\n              [131.883454, 45.321162],\n              [133.09712, 45.14409],\n              [133.769644, 46.116927],\n              [134.11235, 47.21248],\n              [134.50081, 47.57845],\n              [135.026311, 48.47823],\n              [133.373596, 48.183442],\n              [132.50669, 47.78896],\n              [130.98726, 47.79013],\n              [130.582293, 48.729687],\n              [129.397818, 49.4406],\n              [127.6574, 49.76027],\n              [127.287456, 50.739797],\n              [126.939157, 51.353894],\n              [126.564399, 51.784255],\n              [125.946349, 52.792799],\n              [125.068211, 53.161045],\n              [123.57147, 53.4588],\n              [122.245748, 53.431726],\n              [121.003085, 53.251401],\n              [120.177089, 52.753886],\n              [120.725789, 52.516226],\n              [120.7382, 51.96411],\n              [120.18208, 51.64355],\n              [119.27939, 50.58292],\n              [119.288461, 50.142883],\n              [117.879244, 49.510983],\n              [116.678801, 49.888531],\n              [115.485695, 49.805177],\n              [114.96211, 50.140247],\n              [114.362456, 50.248303],\n              [112.89774, 49.543565],\n              [111.581231, 49.377968],\n              [110.662011, 49.130128],\n              [109.402449, 49.292961],\n              [108.475167, 49.282548],\n              [107.868176, 49.793705],\n              [106.888804, 50.274296],\n              [105.886591, 50.406019],\n              [104.62158, 50.27532],\n              [103.676545, 50.089966],\n              [102.25589, 50.51056],\n              [102.06521, 51.25991],\n              [100.88948, 51.516856],\n              [99.981732, 51.634006],\n              [98.861491, 52.047366],\n              [97.82574, 51.010995],\n              [98.231762, 50.422401],\n              [97.25976, 49.72605],\n              [95.81402, 49.97746],\n              [94.815949, 50.013433],\n              [94.147566, 50.480537],\n              [93.10421, 50.49529],\n              [92.234712, 50.802171],\n              [90.713667, 50.331812],\n              [88.805567, 49.470521],\n              [87.751264, 49.297198],\n              [87.35997, 49.214981],\n              [86.829357, 49.826675],\n              [85.54127, 49.692859],\n              [85.11556, 50.117303],\n              [84.416377, 50.3114],\n              [83.935115, 50.889246],\n              [83.383004, 51.069183],\n              [81.945986, 50.812196],\n              [80.568447, 51.388336],\n              [80.03556, 50.864751],\n              [77.800916, 53.404415],\n              [76.525179, 54.177003],\n              [76.8911, 54.490524],\n              [74.38482, 53.54685],\n              [73.425679, 53.48981],\n              [73.508516, 54.035617],\n              [72.22415, 54.376655],\n              [71.180131, 54.133285],\n              [70.865267, 55.169734],\n              [69.068167, 55.38525],\n              [68.1691, 54.970392],\n              [65.66687, 54.60125],\n              [65.178534, 54.354228],\n              [61.4366, 54.00625],\n              [60.978066, 53.664993],\n              [61.699986, 52.979996],\n              [60.739993, 52.719986],\n              [60.927269, 52.447548],\n              [59.967534, 51.96042],\n              [61.588003, 51.272659],\n              [61.337424, 50.79907],\n              [59.932807, 50.842194],\n              [59.642282, 50.545442],\n              [58.36332, 51.06364],\n              [56.77798, 51.04355],\n              [55.71694, 50.62171],\n              [54.532878, 51.02624],\n              [52.328724, 51.718652],\n              [50.766648, 51.692762],\n              [48.702382, 50.605128],\n              [48.577841, 49.87476],\n              [47.54948, 50.454698],\n              [46.751596, 49.356006],\n              [47.043672, 49.152039],\n              [46.466446, 48.394152],\n              [47.31524, 47.71585],\n              [48.05725, 47.74377],\n              [48.694734, 47.075628],\n              [48.59325, 46.56104],\n              [49.10116, 46.39933],\n              [48.64541, 45.80629],\n              [47.67591, 45.64149],\n              [46.68201, 44.6092],\n              [47.59094, 43.66016],\n              [47.49252, 42.98658],\n              [48.58437, 41.80888],\n              [48.584353, 41.808869]\n            ]\n          ],\n          [\n            [\n              [21.268449, 55.190482],\n              [22.315724, 55.015299],\n              [22.757764, 54.856574],\n              [22.651052, 54.582741],\n              [22.731099, 54.327537],\n              [20.892245, 54.312525],\n              [19.66064, 54.426084],\n              [19.888481, 54.86616],\n              [21.268449, 55.190482]\n            ]\n          ],\n          [\n            [\n              [143.648007, 50.7476],\n              [144.654148, 48.976391],\n              [143.173928, 49.306551],\n              [142.558668, 47.861575],\n              [143.533492, 46.836728],\n              [143.505277, 46.137908],\n              [142.747701, 46.740765],\n              [142.09203, 45.966755],\n              [141.906925, 46.805929],\n              [142.018443, 47.780133],\n              [141.904445, 48.859189],\n              [142.1358, 49.615163],\n              [142.179983, 50.952342],\n              [141.594076, 51.935435],\n              [141.682546, 53.301966],\n              [142.606934, 53.762145],\n              [142.209749, 54.225476],\n              [142.654786, 54.365881],\n              [142.914616, 53.704578],\n              [143.260848, 52.74076],\n              [143.235268, 51.75666],\n              [143.648007, 50.7476]\n            ]\n          ],\n          [\n            [\n              [-175.01425, 66.58435],\n              [-174.33983, 66.33556],\n              [-174.57182, 67.06219],\n              [-171.85731, 66.91308],\n              [-169.89958, 65.97724],\n              [-170.89107, 65.54139],\n              [-172.53025, 65.43791],\n              [-172.555, 64.46079],\n              [-172.95533, 64.25269],\n              [-173.89184, 64.2826],\n              [-174.65392, 64.63125],\n              [-175.98353, 64.92288],\n              [-176.20716, 65.35667],\n              [-177.22266, 65.52024],\n              [-178.35993, 65.39052],\n              [-178.90332, 65.74044],\n              [-178.68611, 66.11211],\n              [-179.88377, 65.87456],\n              [-179.43268, 65.40411],\n              [-180, 64.979709],\n              [-180, 68.963636],\n              [-177.55, 68.2],\n              [-174.92825, 67.20589],\n              [-175.01425, 66.58435]\n            ]\n          ],\n          [\n            [\n              [180, 70.832199],\n              [178.903425, 70.78114],\n              [178.7253, 71.0988],\n              [180, 71.515714],\n              [180, 70.832199]\n            ]\n          ],\n          [\n            [\n              [-178.69378, 70.89302],\n              [-180, 70.832199],\n              [-180, 71.515714],\n              [-179.871875, 71.55762],\n              [-179.02433, 71.55553],\n              [-177.577945, 71.26948],\n              [-177.663575, 71.13277],\n              [-178.69378, 70.89302]\n            ]\n          ],\n          [\n            [\n              [143.60385, 73.21244],\n              [142.08763, 73.20544],\n              [140.038155, 73.31692],\n              [139.86312, 73.36983],\n              [140.81171, 73.76506],\n              [142.06207, 73.85758],\n              [143.48283, 73.47525],\n              [143.60385, 73.21244]\n            ]\n          ],\n          [\n            [\n              [150.73167, 75.08406],\n              [149.575925, 74.68892],\n              [147.977465, 74.778355],\n              [146.11919, 75.17298],\n              [146.358485, 75.49682],\n              [148.22223, 75.345845],\n              [150.73167, 75.08406]\n            ]\n          ],\n          [\n            [\n              [145.086285, 75.562625],\n              [144.3, 74.82],\n              [140.61381, 74.84768],\n              [138.95544, 74.61148],\n              [136.97439, 75.26167],\n              [137.51176, 75.94917],\n              [138.831075, 76.13676],\n              [141.471615, 76.09289],\n              [145.086285, 75.562625]\n            ]\n          ],\n          [\n            [\n              [57.535693, 70.720464],\n              [56.944979, 70.632743],\n              [53.677375, 70.762658],\n              [53.412017, 71.206662],\n              [51.601895, 71.474759],\n              [51.455754, 72.014881],\n              [52.478275, 72.229442],\n              [52.444169, 72.774731],\n              [54.427614, 73.627548],\n              [53.50829, 73.749814],\n              [55.902459, 74.627486],\n              [55.631933, 75.081412],\n              [57.868644, 75.60939],\n              [61.170044, 76.251883],\n              [64.498368, 76.439055],\n              [66.210977, 76.809782],\n              [68.15706, 76.939697],\n              [68.852211, 76.544811],\n              [68.180573, 76.233642],\n              [64.637326, 75.737755],\n              [61.583508, 75.260885],\n              [58.477082, 74.309056],\n              [56.986786, 73.333044],\n              [55.419336, 72.371268],\n              [55.622838, 71.540595],\n              [57.535693, 70.720464]\n            ]\n          ],\n          [\n            [\n              [105.07547, 78.30689],\n              [99.43814, 77.921],\n              [101.2649, 79.23399],\n              [102.08635, 79.34641],\n              [102.837815, 79.28129],\n              [105.37243, 78.71334],\n              [105.07547, 78.30689]\n            ]\n          ],\n          [\n            [\n              [51.136187, 80.54728],\n              [49.793685, 80.415428],\n              [48.894411, 80.339567],\n              [48.754937, 80.175468],\n              [47.586119, 80.010181],\n              [46.502826, 80.247247],\n              [47.072455, 80.559424],\n              [44.846958, 80.58981],\n              [46.799139, 80.771918],\n              [48.318477, 80.78401],\n              [48.522806, 80.514569],\n              [49.09719, 80.753986],\n              [50.039768, 80.918885],\n              [51.522933, 80.699726],\n              [51.136187, 80.54728]\n            ]\n          ],\n          [\n            [\n              [99.93976, 78.88094],\n              [97.75794, 78.7562],\n              [94.97259, 79.044745],\n              [93.31288, 79.4265],\n              [92.5454, 80.14379],\n              [91.18107, 80.34146],\n              [93.77766, 81.0246],\n              [95.940895, 81.2504],\n              [97.88385, 80.746975],\n              [100.186655, 79.780135],\n              [99.93976, 78.88094]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Rwanda\",\n        \"SOV_A3\": \"RWA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Rwanda\",\n        \"ADM0_A3\": \"RWA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Rwanda\",\n        \"GU_A3\": \"RWA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Rwanda\",\n        \"SU_A3\": \"RWA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Rwanda\",\n        \"NAME_LONG\": \"Rwanda\",\n        \"BRK_A3\": \"RWA\",\n        \"BRK_NAME\": \"Rwanda\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Rwa.\",\n        \"POSTAL\": \"RW\",\n        \"FORMAL_EN\": \"Republic of Rwanda\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Rwanda\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Rwanda\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 11901484,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 21970,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"RW\",\n        \"ISO_A2\": \"RW\",\n        \"ISO_A3\": \"RWA\",\n        \"ISO_A3_EH\": \"RWA\",\n        \"ISO_N3\": \"646\",\n        \"UN_A3\": \"646\",\n        \"WB_A2\": \"RW\",\n        \"WB_A3\": \"RWA\",\n        \"WOE_ID\": 23424937,\n        \"WOE_ID_EH\": 23424937,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"RWA\",\n        \"ADM0_A3_US\": \"RWA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [29.024926, -2.917858, 30.816135, -1.134659],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [30.469674, -2.413855],\n            [29.938359, -2.348487],\n            [29.632176, -2.917858],\n            [29.024926, -2.839258],\n            [29.117479, -2.292211],\n            [29.254835, -2.21511],\n            [29.291887, -1.620056],\n            [29.579466, -1.341313],\n            [29.821519, -1.443322],\n            [30.419105, -1.134659],\n            [30.816135, -1.698914],\n            [30.758309, -2.28725],\n            [30.46967, -2.41383],\n            [30.469674, -2.413855]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 7,\n        \"SOVEREIGNT\": \"Western Sahara\",\n        \"SOV_A3\": \"SAH\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Indeterminate\",\n        \"ADMIN\": \"Western Sahara\",\n        \"ADM0_A3\": \"SAH\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Western Sahara\",\n        \"GU_A3\": \"SAH\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Western Sahara\",\n        \"SU_A3\": \"SAH\",\n        \"BRK_DIFF\": 1,\n        \"NAME\": \"W. Sahara\",\n        \"NAME_LONG\": \"Western Sahara\",\n        \"BRK_A3\": \"B28\",\n        \"BRK_NAME\": \"W. Sahara\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"W. Sah.\",\n        \"POSTAL\": \"WS\",\n        \"FORMAL_EN\": \"Sahrawi Arab Democratic Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Western Sahara\",\n        \"NOTE_ADM0\": \"Self admin.\",\n        \"NOTE_BRK\": \"Self admin.; Claimed by Morocco\",\n        \"NAME_SORT\": \"Western Sahara\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 7,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 603253,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 906.5,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2007,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"WI\",\n        \"ISO_A2\": \"EH\",\n        \"ISO_A3\": \"ESH\",\n        \"ISO_A3_EH\": \"ESH\",\n        \"ISO_N3\": \"732\",\n        \"UN_A3\": \"732\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": 23424990,\n        \"WOE_ID_EH\": 23424990,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"MAR\",\n        \"ADM0_A3_US\": \"SAH\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 14,\n        \"ABBREV_LEN\": 7,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 4.7,\n        \"MIN_LABEL\": 6,\n        \"MAX_LABEL\": 11\n      },\n      \"bbox\": [-17.063423, 20.999752, -8.665124, 27.656426],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-8.66559, 27.656426],\n            [-8.665124, 27.589479],\n            [-8.6844, 27.395744],\n            [-8.687294, 25.881056],\n            [-11.969419, 25.933353],\n            [-11.937224, 23.374594],\n            [-12.874222, 23.284832],\n            [-13.118754, 22.77122],\n            [-12.929102, 21.327071],\n            [-16.845194, 21.333323],\n            [-17.063423, 20.999752],\n            [-17.020428, 21.42231],\n            [-17.002962, 21.420734],\n            [-14.750955, 21.5006],\n            [-14.630833, 21.86094],\n            [-14.221168, 22.310163],\n            [-13.89111, 23.691009],\n            [-12.500963, 24.770116],\n            [-12.030759, 26.030866],\n            [-11.71822, 26.104092],\n            [-11.392555, 26.883424],\n            [-10.551263, 26.990808],\n            [-10.189424, 26.860945],\n            [-9.735343, 26.860945],\n            [-9.413037, 27.088476],\n            [-8.794884, 27.120696],\n            [-8.817828, 27.656426],\n            [-8.66559, 27.656426]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Saudi Arabia\",\n        \"SOV_A3\": \"SAU\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Saudi Arabia\",\n        \"ADM0_A3\": \"SAU\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Saudi Arabia\",\n        \"GU_A3\": \"SAU\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Saudi Arabia\",\n        \"SU_A3\": \"SAU\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Saudi Arabia\",\n        \"NAME_LONG\": \"Saudi Arabia\",\n        \"BRK_A3\": \"SAU\",\n        \"BRK_NAME\": \"Saudi Arabia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Saud.\",\n        \"POSTAL\": \"SA\",\n        \"FORMAL_EN\": \"Kingdom of Saudi Arabia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Saudi Arabia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Saudi Arabia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 28571770,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 1731000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SA\",\n        \"ISO_A2\": \"SA\",\n        \"ISO_A3\": \"SAU\",\n        \"ISO_A3_EH\": \"SAU\",\n        \"ISO_N3\": \"682\",\n        \"UN_A3\": \"682\",\n        \"WB_A2\": \"SA\",\n        \"WB_A3\": \"SAU\",\n        \"WOE_ID\": 23424938,\n        \"WOE_ID_EH\": 23424938,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SAU\",\n        \"ADM0_A3_US\": \"SAU\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 12,\n        \"LONG_LEN\": 12,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2.7,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [34.632336, 16.347891, 55.666659, 32.161009],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [51.579519, 24.245497],\n            [51.617708, 24.014219],\n            [52.000733, 23.001154],\n            [55.006803, 22.496948],\n            [55.208341, 22.70833],\n            [55.666659, 22.000001],\n            [54.999982, 19.999994],\n            [52.00001, 19.000003],\n            [49.116672, 18.616668],\n            [48.183344, 18.166669],\n            [47.466695, 17.116682],\n            [47.000005, 16.949999],\n            [46.749994, 17.283338],\n            [46.366659, 17.233315],\n            [45.399999, 17.333335],\n            [45.216651, 17.433329],\n            [44.062613, 17.410359],\n            [43.791519, 17.319977],\n            [43.380794, 17.579987],\n            [43.115798, 17.08844],\n            [43.218375, 16.66689],\n            [42.779332, 16.347891],\n            [42.649573, 16.774635],\n            [42.347989, 17.075806],\n            [42.270888, 17.474722],\n            [41.754382, 17.833046],\n            [41.221391, 18.6716],\n            [40.939341, 19.486485],\n            [40.247652, 20.174635],\n            [39.801685, 20.338862],\n            [39.139399, 21.291905],\n            [39.023696, 21.986875],\n            [39.066329, 22.579656],\n            [38.492772, 23.688451],\n            [38.02386, 24.078686],\n            [37.483635, 24.285495],\n            [37.154818, 24.858483],\n            [37.209491, 25.084542],\n            [36.931627, 25.602959],\n            [36.639604, 25.826228],\n            [36.249137, 26.570136],\n            [35.640182, 27.37652],\n            [35.130187, 28.063352],\n            [34.632336, 28.058546],\n            [34.787779, 28.607427],\n            [34.83222, 28.957483],\n            [34.956037, 29.356555],\n            [36.068941, 29.197495],\n            [36.501214, 29.505254],\n            [36.740528, 29.865283],\n            [37.503582, 30.003776],\n            [37.66812, 30.338665],\n            [37.998849, 30.5085],\n            [37.002166, 31.508413],\n            [39.004886, 32.010217],\n            [39.195468, 32.161009],\n            [40.399994, 31.889992],\n            [41.889981, 31.190009],\n            [44.709499, 29.178891],\n            [46.568713, 29.099025],\n            [47.459822, 29.002519],\n            [47.708851, 28.526063],\n            [48.416094, 28.552004],\n            [48.807595, 27.689628],\n            [49.299554, 27.461218],\n            [49.470914, 27.109999],\n            [50.152422, 26.689663],\n            [50.212935, 26.277027],\n            [50.113303, 25.943972],\n            [50.239859, 25.60805],\n            [50.527387, 25.327808],\n            [50.660557, 24.999896],\n            [50.810108, 24.754743],\n            [51.112415, 24.556331],\n            [51.389608, 24.627386],\n            [51.579519, 24.245497]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Sudan\",\n        \"SOV_A3\": \"SDN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Sudan\",\n        \"ADM0_A3\": \"SDN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Sudan\",\n        \"GU_A3\": \"SDN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Sudan\",\n        \"SU_A3\": \"SDN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Sudan\",\n        \"NAME_LONG\": \"Sudan\",\n        \"BRK_A3\": \"SDN\",\n        \"BRK_NAME\": \"Sudan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Sudan\",\n        \"POSTAL\": \"SD\",\n        \"FORMAL_EN\": \"Republic of the Sudan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Sudan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Sudan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 37345935,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 176300,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SU\",\n        \"ISO_A2\": \"SD\",\n        \"ISO_A3\": \"SDN\",\n        \"ISO_A3_EH\": \"SDN\",\n        \"ISO_N3\": \"729\",\n        \"UN_A3\": \"729\",\n        \"WB_A2\": \"SD\",\n        \"WB_A3\": \"SDN\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424952,\n        \"WOE_NOTE\": \"Almost all FLickr photos are in the north.\",\n        \"ADM0_A3_IS\": \"SDN\",\n        \"ADM0_A3_US\": \"SDN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [21.93681, 8.229188, 38.41009, 22],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [24.567369, 8.229188],\n            [23.805813, 8.666319],\n            [23.459013, 8.954286],\n            [23.394779, 9.265068],\n            [23.55725, 9.681218],\n            [23.554304, 10.089255],\n            [22.977544, 10.714463],\n            [22.864165, 11.142395],\n            [22.87622, 11.38461],\n            [22.50869, 11.67936],\n            [22.49762, 12.26024],\n            [22.28801, 12.64605],\n            [21.93681, 12.58818],\n            [22.03759, 12.95546],\n            [22.29658, 13.37232],\n            [22.18329, 13.78648],\n            [22.51202, 14.09318],\n            [22.30351, 14.32682],\n            [22.56795, 14.94429],\n            [23.02459, 15.68072],\n            [23.88689, 15.61084],\n            [23.83766, 19.58047],\n            [23.85, 20],\n            [25, 20.00304],\n            [25, 22],\n            [29.02, 22],\n            [32.9, 22],\n            [36.86623, 22],\n            [37.18872, 21.01885],\n            [36.96941, 20.83744],\n            [37.1147, 19.80796],\n            [37.48179, 18.61409],\n            [37.86276, 18.36786],\n            [38.41009, 17.998307],\n            [37.904, 17.42754],\n            [37.16747, 17.26314],\n            [36.85253, 16.95655],\n            [36.75389, 16.29186],\n            [36.32322, 14.82249],\n            [36.42951, 14.42211],\n            [36.27022, 13.56333],\n            [35.86363, 12.57828],\n            [35.26049, 12.08286],\n            [34.83163, 11.31896],\n            [34.73115, 10.91017],\n            [34.25745, 10.63009],\n            [33.96162, 9.58358],\n            [33.97498, 8.68456],\n            [33.963393, 9.464285],\n            [33.824963, 9.484061],\n            [33.842131, 9.981915],\n            [33.721959, 10.325262],\n            [33.206938, 10.720112],\n            [33.086766, 11.441141],\n            [33.206938, 12.179338],\n            [32.743419, 12.248008],\n            [32.67475, 12.024832],\n            [32.073892, 11.97333],\n            [32.314235, 11.681484],\n            [32.400072, 11.080626],\n            [31.850716, 10.531271],\n            [31.352862, 9.810241],\n            [30.837841, 9.707237],\n            [29.996639, 10.290927],\n            [29.618957, 10.084919],\n            [29.515953, 9.793074],\n            [29.000932, 9.604232],\n            [28.966597, 9.398224],\n            [27.97089, 9.398224],\n            [27.833551, 9.604232],\n            [27.112521, 9.638567],\n            [26.752006, 9.466893],\n            [26.477328, 9.55273],\n            [25.962307, 10.136421],\n            [25.790633, 10.411099],\n            [25.069604, 10.27376],\n            [24.794926, 9.810241],\n            [24.537415, 8.917538],\n            [24.194068, 8.728696],\n            [23.88698, 8.61973],\n            [24.567369, 8.229188]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"South Sudan\",\n        \"SOV_A3\": \"SDS\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"South Sudan\",\n        \"ADM0_A3\": \"SDS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"South Sudan\",\n        \"GU_A3\": \"SDS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"South Sudan\",\n        \"SU_A3\": \"SDS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"S. Sudan\",\n        \"NAME_LONG\": \"South Sudan\",\n        \"BRK_A3\": \"SDS\",\n        \"BRK_NAME\": \"S. Sudan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"S. Sud.\",\n        \"POSTAL\": \"SS\",\n        \"FORMAL_EN\": \"Republic of South Sudan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"South Sudan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"South Sudan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 13026129,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 20880,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2008,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"-99\",\n        \"ISO_A2\": \"SS\",\n        \"ISO_A3\": \"SSD\",\n        \"ISO_A3_EH\": \"SSD\",\n        \"ISO_N3\": \"728\",\n        \"UN_A3\": \"728\",\n        \"WB_A2\": \"SS\",\n        \"WB_A3\": \"SSD\",\n        \"WOE_ID\": -99,\n        \"WOE_ID_EH\": -99,\n        \"WOE_NOTE\": \"Includes states of 20069899, 20069897, 20069898, 20069901, 20069909, and 20069908 but maybe more?\",\n        \"ADM0_A3_IS\": \"SSD\",\n        \"ADM0_A3_US\": \"SDS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 7,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [23.88698, 3.509172, 35.298007, 12.248008],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [27.374226, 5.233944],\n            [27.213409, 5.550953],\n            [26.465909, 5.946717],\n            [26.213418, 6.546603],\n            [25.796648, 6.979316],\n            [25.124131, 7.500085],\n            [25.114932, 7.825104],\n            [24.567369, 8.229188],\n            [23.88698, 8.61973],\n            [24.194068, 8.728696],\n            [24.537415, 8.917538],\n            [24.794926, 9.810241],\n            [25.069604, 10.27376],\n            [25.790633, 10.411099],\n            [25.962307, 10.136421],\n            [26.477328, 9.55273],\n            [26.752006, 9.466893],\n            [27.112521, 9.638567],\n            [27.833551, 9.604232],\n            [27.97089, 9.398224],\n            [28.966597, 9.398224],\n            [29.000932, 9.604232],\n            [29.515953, 9.793074],\n            [29.618957, 10.084919],\n            [29.996639, 10.290927],\n            [30.837841, 9.707237],\n            [31.352862, 9.810241],\n            [31.850716, 10.531271],\n            [32.400072, 11.080626],\n            [32.314235, 11.681484],\n            [32.073892, 11.97333],\n            [32.67475, 12.024832],\n            [32.743419, 12.248008],\n            [33.206938, 12.179338],\n            [33.086766, 11.441141],\n            [33.206938, 10.720112],\n            [33.721959, 10.325262],\n            [33.842131, 9.981915],\n            [33.824963, 9.484061],\n            [33.963393, 9.464285],\n            [33.97498, 8.68456],\n            [33.8255, 8.37916],\n            [33.2948, 8.35458],\n            [32.95418, 7.78497],\n            [33.56829, 7.71334],\n            [34.0751, 7.22595],\n            [34.25032, 6.82607],\n            [34.70702, 6.59422],\n            [35.298007, 5.506],\n            [34.620196, 4.847123],\n            [34.005, 4.249885],\n            [33.39, 3.79],\n            [32.68642, 3.79232],\n            [31.88145, 3.55827],\n            [31.24556, 3.7819],\n            [30.833852, 3.509172],\n            [29.9535, 4.173699],\n            [29.715995, 4.600805],\n            [29.159078, 4.389267],\n            [28.696678, 4.455077],\n            [28.428994, 4.287155],\n            [27.979977, 4.408413],\n            [27.374226, 5.233944]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Senegal\",\n        \"SOV_A3\": \"SEN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Senegal\",\n        \"ADM0_A3\": \"SEN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Senegal\",\n        \"GU_A3\": \"SEN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Senegal\",\n        \"SU_A3\": \"SEN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Senegal\",\n        \"NAME_LONG\": \"Senegal\",\n        \"BRK_A3\": \"SEN\",\n        \"BRK_NAME\": \"Senegal\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Sen.\",\n        \"POSTAL\": \"SN\",\n        \"FORMAL_EN\": \"Republic of Senegal\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Senegal\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Senegal\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 14668522,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 39720,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SG\",\n        \"ISO_A2\": \"SN\",\n        \"ISO_A3\": \"SEN\",\n        \"ISO_A3_EH\": \"SEN\",\n        \"ISO_N3\": \"686\",\n        \"UN_A3\": \"686\",\n        \"WB_A2\": \"SN\",\n        \"WB_A3\": \"SEN\",\n        \"WOE_ID\": 23424943,\n        \"WOE_ID_EH\": 23424943,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SEN\",\n        \"ADM0_A3_US\": \"SEN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-17.625043, 12.33209, -11.467899, 16.598264],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-11.513943, 12.442988],\n            [-11.658301, 12.386583],\n            [-12.203565, 12.465648],\n            [-12.278599, 12.35444],\n            [-12.499051, 12.33209],\n            [-13.217818, 12.575874],\n            [-13.700476, 12.586183],\n            [-15.548477, 12.62817],\n            [-15.816574, 12.515567],\n            [-16.147717, 12.547762],\n            [-16.677452, 12.384852],\n            [-16.841525, 13.151394],\n            [-15.931296, 13.130284],\n            [-15.691001, 13.270353],\n            [-15.511813, 13.27857],\n            [-15.141163, 13.509512],\n            [-14.712197, 13.298207],\n            [-14.277702, 13.280585],\n            [-13.844963, 13.505042],\n            [-14.046992, 13.794068],\n            [-14.376714, 13.62568],\n            [-14.687031, 13.630357],\n            [-15.081735, 13.876492],\n            [-15.39877, 13.860369],\n            [-15.624596, 13.623587],\n            [-16.713729, 13.594959],\n            [-17.126107, 14.373516],\n            [-17.625043, 14.729541],\n            [-17.185173, 14.919477],\n            [-16.700706, 15.621527],\n            [-16.463098, 16.135036],\n            [-16.12069, 16.455663],\n            [-15.623666, 16.369337],\n            [-15.135737, 16.587282],\n            [-14.577348, 16.598264],\n            [-14.099521, 16.304302],\n            [-13.435738, 16.039383],\n            [-12.830658, 15.303692],\n            [-12.17075, 14.616834],\n            [-12.124887, 13.994727],\n            [-11.927716, 13.422075],\n            [-11.553398, 13.141214],\n            [-11.467899, 12.754519],\n            [-11.513943, 12.442988]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Solomon Islands\",\n        \"SOV_A3\": \"SLB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Solomon Islands\",\n        \"ADM0_A3\": \"SLB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Solomon Islands\",\n        \"GU_A3\": \"SLB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Solomon Islands\",\n        \"SU_A3\": \"SLB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Solomon Is.\",\n        \"NAME_LONG\": \"Solomon Islands\",\n        \"BRK_A3\": \"SLB\",\n        \"BRK_NAME\": \"Solomon Is.\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"S. Is.\",\n        \"POSTAL\": \"SB\",\n        \"FORMAL_EN\": null,\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Solomon Islands\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Solomon Islands\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 647581,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 1198,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"BP\",\n        \"ISO_A2\": \"SB\",\n        \"ISO_A3\": \"SLB\",\n        \"ISO_A3_EH\": \"SLB\",\n        \"ISO_N3\": \"090\",\n        \"UN_A3\": \"090\",\n        \"WB_A2\": \"SB\",\n        \"WB_A3\": \"SLB\",\n        \"WOE_ID\": 23424766,\n        \"WOE_ID_EH\": 23424766,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SLB\",\n        \"ADM0_A3_US\": \"SLB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Melanesia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 15,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [156.491358, -10.826367, 162.398646, -6.599338],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [162.119025, -10.482719],\n              [162.398646, -10.826367],\n              [161.700032, -10.820011],\n              [161.319797, -10.204751],\n              [161.917383, -10.446701],\n              [162.119025, -10.482719]\n            ]\n          ],\n          [\n            [\n              [160.852229, -9.872937],\n              [160.462588, -9.89521],\n              [159.849447, -9.794027],\n              [159.640003, -9.63998],\n              [159.702945, -9.24295],\n              [160.362956, -9.400304],\n              [160.688518, -9.610162],\n              [160.852229, -9.872937]\n            ]\n          ],\n          [\n            [\n              [161.679982, -9.599982],\n              [161.529397, -9.784312],\n              [160.788253, -8.917543],\n              [160.579997, -8.320009],\n              [160.920028, -8.320009],\n              [161.280006, -9.120011],\n              [161.679982, -9.599982]\n            ]\n          ],\n          [\n            [\n              [159.875027, -8.33732],\n              [159.917402, -8.53829],\n              [159.133677, -8.114181],\n              [158.586114, -7.754824],\n              [158.21115, -7.421872],\n              [158.359978, -7.320018],\n              [158.820001, -7.560003],\n              [159.640003, -8.020027],\n              [159.875027, -8.33732]\n            ]\n          ],\n          [\n            [\n              [157.538426, -7.34782],\n              [157.33942, -7.404767],\n              [156.90203, -7.176874],\n              [156.491358, -6.765943],\n              [156.542828, -6.599338],\n              [157.14, -7.021638],\n              [157.538426, -7.34782]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Sierra Leone\",\n        \"SOV_A3\": \"SLE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Sierra Leone\",\n        \"ADM0_A3\": \"SLE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Sierra Leone\",\n        \"GU_A3\": \"SLE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Sierra Leone\",\n        \"SU_A3\": \"SLE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Sierra Leone\",\n        \"NAME_LONG\": \"Sierra Leone\",\n        \"BRK_A3\": \"SLE\",\n        \"BRK_NAME\": \"Sierra Leone\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"S.L.\",\n        \"POSTAL\": \"SL\",\n        \"FORMAL_EN\": \"Republic of Sierra Leone\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Sierra Leone\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Sierra Leone\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 6163195,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 10640,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SL\",\n        \"ISO_A2\": \"SL\",\n        \"ISO_A3\": \"SLE\",\n        \"ISO_A3_EH\": \"SLE\",\n        \"ISO_N3\": \"694\",\n        \"UN_A3\": \"694\",\n        \"WB_A2\": \"SL\",\n        \"WB_A3\": \"SLE\",\n        \"WOE_ID\": 23424946,\n        \"WOE_ID_EH\": 23424946,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SLE\",\n        \"ADM0_A3_US\": \"SLE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 12,\n        \"LONG_LEN\": 12,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-13.24655, 6.785917, -10.230094, 10.046984],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-13.24655, 8.903049],\n            [-12.711958, 9.342712],\n            [-12.596719, 9.620188],\n            [-12.425929, 9.835834],\n            [-12.150338, 9.858572],\n            [-11.917277, 10.046984],\n            [-11.117481, 10.045873],\n            [-10.839152, 9.688246],\n            [-10.622395, 9.26791],\n            [-10.65477, 8.977178],\n            [-10.494315, 8.715541],\n            [-10.505477, 8.348896],\n            [-10.230094, 8.406206],\n            [-10.695595, 7.939464],\n            [-11.146704, 7.396706],\n            [-11.199802, 7.105846],\n            [-11.438779, 6.785917],\n            [-11.708195, 6.860098],\n            [-12.428099, 7.262942],\n            [-12.949049, 7.798646],\n            [-13.124025, 8.163946],\n            [-13.24655, 8.903049]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"El Salvador\",\n        \"SOV_A3\": \"SLV\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"El Salvador\",\n        \"ADM0_A3\": \"SLV\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"El Salvador\",\n        \"GU_A3\": \"SLV\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"El Salvador\",\n        \"SU_A3\": \"SLV\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"El Salvador\",\n        \"NAME_LONG\": \"El Salvador\",\n        \"BRK_A3\": \"SLV\",\n        \"BRK_NAME\": \"El Salvador\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"El. S.\",\n        \"POSTAL\": \"SV\",\n        \"FORMAL_EN\": \"Republic of El Salvador\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"El Salvador\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"El Salvador\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 8,\n        \"POP_EST\": 6172011,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 54790,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ES\",\n        \"ISO_A2\": \"SV\",\n        \"ISO_A3\": \"SLV\",\n        \"ISO_A3_EH\": \"SLV\",\n        \"ISO_N3\": \"222\",\n        \"UN_A3\": \"222\",\n        \"WB_A2\": \"SV\",\n        \"WB_A3\": \"SLV\",\n        \"WOE_ID\": 23424807,\n        \"WOE_ID_EH\": 23424807,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SLV\",\n        \"ADM0_A3_US\": \"SLV\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Central America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [-90.095555, 13.149017, -87.723503, 14.424133],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-90.095555, 13.735338],\n            [-90.064678, 13.88197],\n            [-89.721934, 14.134228],\n            [-89.534219, 14.244816],\n            [-89.587343, 14.362586],\n            [-89.353326, 14.424133],\n            [-89.058512, 14.340029],\n            [-88.843073, 14.140507],\n            [-88.541231, 13.980155],\n            [-88.503998, 13.845486],\n            [-88.065343, 13.964626],\n            [-87.859515, 13.893312],\n            [-87.723503, 13.78505],\n            [-87.793111, 13.38448],\n            [-87.904112, 13.149017],\n            [-88.483302, 13.163951],\n            [-88.843228, 13.259734],\n            [-89.256743, 13.458533],\n            [-89.812394, 13.520622],\n            [-90.095555, 13.735338]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Somaliland\",\n        \"SOV_A3\": \"SOL\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Indeterminate\",\n        \"ADMIN\": \"Somaliland\",\n        \"ADM0_A3\": \"SOL\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Somaliland\",\n        \"GU_A3\": \"SOL\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Somaliland\",\n        \"SU_A3\": \"SOL\",\n        \"BRK_DIFF\": 1,\n        \"NAME\": \"Somaliland\",\n        \"NAME_LONG\": \"Somaliland\",\n        \"BRK_A3\": \"B30\",\n        \"BRK_NAME\": \"Somaliland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Solnd.\",\n        \"POSTAL\": \"SL\",\n        \"FORMAL_EN\": \"Republic of Somaliland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": null,\n        \"NOTE_ADM0\": \"Self admin.\",\n        \"NOTE_BRK\": \"Self admin.; Claimed by Somalia\",\n        \"NAME_SORT\": \"Somaliland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 3500000,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 12250,\n        \"POP_YEAR\": 2013,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2013,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"-99\",\n        \"ISO_A2\": \"-99\",\n        \"ISO_A3\": \"-99\",\n        \"ISO_A3_EH\": \"-99\",\n        \"ISO_N3\": \"-99\",\n        \"UN_A3\": \"-099\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": -99,\n        \"WOE_ID_EH\": -99,\n        \"WOE_NOTE\": \"Includes old states of 2347021, 2347020, 2347017 and portion of 2347016.\",\n        \"ADM0_A3_IS\": \"SOM\",\n        \"ADM0_A3_US\": \"SOM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 4,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [42.55876, 7.99688, 48.948206, 11.46204],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [42.776852, 10.926879],\n            [43.145305, 11.46204],\n            [43.47066, 11.27771],\n            [43.666668, 10.864169],\n            [44.117804, 10.445538],\n            [44.614259, 10.442205],\n            [45.556941, 10.698029],\n            [46.645401, 10.816549],\n            [47.525658, 11.127228],\n            [48.021596, 11.193064],\n            [48.378784, 11.375482],\n            [48.948206, 11.410622],\n            [48.948205, 11.410617],\n            [48.948205, 11.410617],\n            [48.942005, 11.394266],\n            [48.938491, 10.982327],\n            [48.938233, 9.9735],\n            [48.93813, 9.451749],\n            [48.486736, 8.837626],\n            [47.78942, 8.003],\n            [46.94834, 7.99688],\n            [43.67875, 9.18358],\n            [43.29699, 9.54048],\n            [42.92812, 10.02194],\n            [42.55876, 10.57258],\n            [42.776852, 10.926879]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Somalia\",\n        \"SOV_A3\": \"SOM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Somalia\",\n        \"ADM0_A3\": \"SOM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Somalia\",\n        \"GU_A3\": \"SOM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Somalia\",\n        \"SU_A3\": \"SOM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Somalia\",\n        \"NAME_LONG\": \"Somalia\",\n        \"BRK_A3\": \"SOM\",\n        \"BRK_NAME\": \"Somalia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Som.\",\n        \"POSTAL\": \"SO\",\n        \"FORMAL_EN\": \"Federal Republic of Somalia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Somalia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Somalia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 8,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 7,\n        \"POP_EST\": 7531386,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 4719,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1987,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SO\",\n        \"ISO_A2\": \"SO\",\n        \"ISO_A3\": \"SOM\",\n        \"ISO_A3_EH\": \"SOM\",\n        \"ISO_N3\": \"706\",\n        \"UN_A3\": \"706\",\n        \"WB_A2\": \"SO\",\n        \"WB_A3\": \"SOM\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 23424949,\n        \"WOE_NOTE\": \"Includes Somaliland (2347021, 2347020, 2347017 and portion of 2347016)\",\n        \"ADM0_A3_IS\": \"SOM\",\n        \"ADM0_A3_US\": \"SOM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [40.98105, -1.68325, 51.13387, 12.02464],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [41.855083, 3.918912],\n            [42.12861, 4.23413],\n            [42.76967, 4.25259],\n            [43.66087, 4.95755],\n            [44.9636, 5.00162],\n            [47.78942, 8.003],\n            [48.486736, 8.837626],\n            [48.93813, 9.451749],\n            [48.938233, 9.9735],\n            [48.938491, 10.982327],\n            [48.942005, 11.394266],\n            [48.948205, 11.410617],\n            [48.948205, 11.410617],\n            [49.26776, 11.43033],\n            [49.72862, 11.5789],\n            [50.25878, 11.67957],\n            [50.73202, 12.0219],\n            [51.1112, 12.02464],\n            [51.13387, 11.74815],\n            [51.04153, 11.16651],\n            [51.04531, 10.6409],\n            [50.83418, 10.27972],\n            [50.55239, 9.19874],\n            [50.07092, 8.08173],\n            [49.4527, 6.80466],\n            [48.59455, 5.33911],\n            [47.74079, 4.2194],\n            [46.56476, 2.85529],\n            [45.56399, 2.04576],\n            [44.06815, 1.05283],\n            [43.13597, 0.2922],\n            [42.04157, -0.91916],\n            [41.81095, -1.44647],\n            [41.58513, -1.68325],\n            [40.993, -0.85829],\n            [40.98105, 2.78452],\n            [41.855083, 3.918912]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Republic of Serbia\",\n        \"SOV_A3\": \"SRB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Republic of Serbia\",\n        \"ADM0_A3\": \"SRB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Republic of Serbia\",\n        \"GU_A3\": \"SRB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Republic of Serbia\",\n        \"SU_A3\": \"SRB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Serbia\",\n        \"NAME_LONG\": \"Serbia\",\n        \"BRK_A3\": \"SRB\",\n        \"BRK_NAME\": \"Serbia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Serb.\",\n        \"POSTAL\": \"RS\",\n        \"FORMAL_EN\": \"Republic of Serbia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Serbia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Serbia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 7111024,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 101800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"RI\",\n        \"ISO_A2\": \"RS\",\n        \"ISO_A3\": \"SRB\",\n        \"ISO_A3_EH\": \"SRB\",\n        \"ISO_N3\": \"688\",\n        \"UN_A3\": \"688\",\n        \"WB_A2\": \"YF\",\n        \"WB_A3\": \"SRB\",\n        \"WOE_ID\": -90,\n        \"WOE_ID_EH\": 20069818,\n        \"WOE_NOTE\": \"Expired WOE also contains Kosovo.\",\n        \"ADM0_A3_IS\": \"SRB\",\n        \"ADM0_A3_US\": \"SRB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [18.829825, 42.245224, 22.986019, 46.17173],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [22.65715, 44.234923],\n            [22.410446, 44.008063],\n            [22.500157, 43.642814],\n            [22.986019, 43.211161],\n            [22.604801, 42.898519],\n            [22.436595, 42.580321],\n            [22.545012, 42.461362],\n            [22.380526, 42.32026],\n            [21.91708, 42.30364],\n            [21.576636, 42.245224],\n            [21.54332, 42.32025],\n            [21.66292, 42.43922],\n            [21.77505, 42.6827],\n            [21.63302, 42.67717],\n            [21.43866, 42.86255],\n            [21.27421, 42.90959],\n            [21.143395, 43.068685],\n            [20.95651, 43.13094],\n            [20.81448, 43.27205],\n            [20.63508, 43.21671],\n            [20.49679, 42.88469],\n            [20.25758, 42.81275],\n            [20.3398, 42.89852],\n            [19.95857, 43.10604],\n            [19.63, 43.21378],\n            [19.48389, 43.35229],\n            [19.21852, 43.52384],\n            [19.454, 43.5681],\n            [19.59976, 44.03847],\n            [19.11761, 44.42307],\n            [19.36803, 44.863],\n            [19.00548, 44.86023],\n            [19.005485, 44.860234],\n            [19.390476, 45.236516],\n            [19.072769, 45.521511],\n            [18.829825, 45.908872],\n            [18.829838, 45.908878],\n            [19.596045, 46.17173],\n            [20.220192, 46.127469],\n            [20.762175, 45.734573],\n            [20.874313, 45.416375],\n            [21.483526, 45.18117],\n            [21.562023, 44.768947],\n            [22.145088, 44.478422],\n            [22.459022, 44.702517],\n            [22.705726, 44.578003],\n            [22.474008, 44.409228],\n            [22.65715, 44.234923]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Suriname\",\n        \"SOV_A3\": \"SUR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Suriname\",\n        \"ADM0_A3\": \"SUR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Suriname\",\n        \"GU_A3\": \"SUR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Suriname\",\n        \"SU_A3\": \"SUR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Suriname\",\n        \"NAME_LONG\": \"Suriname\",\n        \"BRK_A3\": \"SUR\",\n        \"BRK_NAME\": \"Suriname\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Sur.\",\n        \"POSTAL\": \"SR\",\n        \"FORMAL_EN\": \"Republic of Suriname\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Suriname\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Suriname\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 591919,\n        \"POP_RANK\": 11,\n        \"GDP_MD_EST\": 8547,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NS\",\n        \"ISO_A2\": \"SR\",\n        \"ISO_A3\": \"SUR\",\n        \"ISO_A3_EH\": \"SUR\",\n        \"ISO_N3\": \"740\",\n        \"UN_A3\": \"740\",\n        \"WB_A2\": \"SR\",\n        \"WB_A3\": \"SUR\",\n        \"WOE_ID\": 23424913,\n        \"WOE_ID_EH\": 23424913,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SUR\",\n        \"ADM0_A3_US\": \"SUR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [-58.044694, 1.817667, -53.958045, 6.025291],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-54.524754, 2.311849],\n            [-55.097587, 2.523748],\n            [-55.569755, 2.421506],\n            [-55.973322, 2.510364],\n            [-56.073342, 2.220795],\n            [-55.9056, 2.021996],\n            [-55.995698, 1.817667],\n            [-56.539386, 1.899523],\n            [-57.150098, 2.768927],\n            [-57.281433, 3.333492],\n            [-57.601569, 3.334655],\n            [-58.044694, 4.060864],\n            [-57.86021, 4.576801],\n            [-57.914289, 4.812626],\n            [-57.307246, 5.073567],\n            [-57.147436, 5.97315],\n            [-55.949318, 5.772878],\n            [-55.84178, 5.953125],\n            [-55.03325, 6.025291],\n            [-53.958045, 5.756548],\n            [-54.478633, 4.896756],\n            [-54.399542, 4.212611],\n            [-54.011504, 3.62257],\n            [-54.184284, 3.194172],\n            [-54.27123, 2.738748],\n            [-54.524754, 2.311849]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Slovakia\",\n        \"SOV_A3\": \"SVK\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Slovakia\",\n        \"ADM0_A3\": \"SVK\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Slovakia\",\n        \"GU_A3\": \"SVK\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Slovakia\",\n        \"SU_A3\": \"SVK\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Slovakia\",\n        \"NAME_LONG\": \"Slovakia\",\n        \"BRK_A3\": \"SVK\",\n        \"BRK_NAME\": \"Slovakia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Svk.\",\n        \"POSTAL\": \"SK\",\n        \"FORMAL_EN\": \"Slovak Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Slovakia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Slovak Republic\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 5445829,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 168800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"LO\",\n        \"ISO_A2\": \"SK\",\n        \"ISO_A3\": \"SVK\",\n        \"ISO_A3_EH\": \"SVK\",\n        \"ISO_N3\": \"703\",\n        \"UN_A3\": \"703\",\n        \"WB_A2\": \"SK\",\n        \"WB_A3\": \"SVK\",\n        \"WOE_ID\": 23424877,\n        \"WOE_ID_EH\": 23424877,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SVK\",\n        \"ADM0_A3_US\": \"SVK\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [16.879983, 47.758429, 22.558138, 49.571574],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [16.979667, 48.123497],\n            [16.879983, 48.470013],\n            [16.960288, 48.596982],\n            [17.101985, 48.816969],\n            [17.545007, 48.800019],\n            [17.886485, 48.903475],\n            [17.913512, 48.996493],\n            [18.104973, 49.043983],\n            [18.170498, 49.271515],\n            [18.399994, 49.315001],\n            [18.554971, 49.495015],\n            [18.853144, 49.49623],\n            [18.909575, 49.435846],\n            [19.320713, 49.571574],\n            [19.825023, 49.217125],\n            [20.415839, 49.431453],\n            [20.887955, 49.328772],\n            [21.607808, 49.470107],\n            [22.558138, 49.085738],\n            [22.280842, 48.825392],\n            [22.085608, 48.422264],\n            [21.872236, 48.319971],\n            [20.801294, 48.623854],\n            [20.473562, 48.56285],\n            [20.239054, 48.327567],\n            [19.769471, 48.202691],\n            [19.661364, 48.266615],\n            [19.174365, 48.111379],\n            [18.777025, 48.081768],\n            [18.696513, 47.880954],\n            [17.857133, 47.758429],\n            [17.488473, 47.867466],\n            [16.979667, 48.123497]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Slovenia\",\n        \"SOV_A3\": \"SVN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Slovenia\",\n        \"ADM0_A3\": \"SVN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Slovenia\",\n        \"GU_A3\": \"SVN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Slovenia\",\n        \"SU_A3\": \"SVN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Slovenia\",\n        \"NAME_LONG\": \"Slovenia\",\n        \"BRK_A3\": \"SVN\",\n        \"BRK_NAME\": \"Slovenia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Slo.\",\n        \"POSTAL\": \"SLO\",\n        \"FORMAL_EN\": \"Republic of Slovenia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Slovenia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Slovenia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 12,\n        \"POP_EST\": 1972126,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 68350,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SI\",\n        \"ISO_A2\": \"SI\",\n        \"ISO_A3\": \"SVN\",\n        \"ISO_A3_EH\": \"SVN\",\n        \"ISO_N3\": \"705\",\n        \"UN_A3\": \"705\",\n        \"WB_A2\": \"SI\",\n        \"WB_A3\": \"SVN\",\n        \"WOE_ID\": 23424945,\n        \"WOE_ID_EH\": 23424945,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SVN\",\n        \"ADM0_A3_US\": \"SVN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Southern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [13.69811, 45.452316, 16.564808, 46.852386],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [13.806475, 46.509306],\n            [14.632472, 46.431817],\n            [15.137092, 46.658703],\n            [16.011664, 46.683611],\n            [16.202298, 46.852386],\n            [16.370505, 46.841327],\n            [16.564808, 46.503751],\n            [15.768733, 46.238108],\n            [15.67153, 45.834154],\n            [15.323954, 45.731783],\n            [15.327675, 45.452316],\n            [14.935244, 45.471695],\n            [14.595109, 45.634941],\n            [14.411968, 45.466166],\n            [13.71506, 45.500324],\n            [13.93763, 45.591016],\n            [13.69811, 46.016778],\n            [13.806475, 46.509306]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Sweden\",\n        \"SOV_A3\": \"SWE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Sweden\",\n        \"ADM0_A3\": \"SWE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Sweden\",\n        \"GU_A3\": \"SWE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Sweden\",\n        \"SU_A3\": \"SWE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Sweden\",\n        \"NAME_LONG\": \"Sweden\",\n        \"BRK_A3\": \"SWE\",\n        \"BRK_NAME\": \"Sweden\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Swe.\",\n        \"POSTAL\": \"S\",\n        \"FORMAL_EN\": \"Kingdom of Sweden\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Sweden\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Sweden\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 4,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 9960487,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 498100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SW\",\n        \"ISO_A2\": \"SE\",\n        \"ISO_A3\": \"SWE\",\n        \"ISO_A3_EH\": \"SWE\",\n        \"ISO_N3\": \"752\",\n        \"UN_A3\": \"752\",\n        \"WB_A2\": \"SE\",\n        \"WB_A3\": \"SWE\",\n        \"WOE_ID\": 23424954,\n        \"WOE_ID_EH\": 23424954,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SWE\",\n        \"ADM0_A3_US\": \"SWE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Northern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [11.027369, 55.361737, 23.903379, 69.106247],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [20.645593, 69.106247],\n            [21.978535, 68.616846],\n            [23.539473, 67.936009],\n            [23.56588, 66.396051],\n            [23.903379, 66.006927],\n            [22.183173, 65.723741],\n            [21.213517, 65.026005],\n            [21.369631, 64.413588],\n            [19.778876, 63.609554],\n            [17.847779, 62.7494],\n            [17.119555, 61.341166],\n            [17.831346, 60.636583],\n            [18.787722, 60.081914],\n            [17.869225, 58.953766],\n            [16.829185, 58.719827],\n            [16.44771, 57.041118],\n            [15.879786, 56.104302],\n            [14.666681, 56.200885],\n            [14.100721, 55.407781],\n            [12.942911, 55.361737],\n            [12.625101, 56.30708],\n            [11.787942, 57.441817],\n            [11.027369, 58.856149],\n            [11.468272, 59.432393],\n            [12.300366, 60.117933],\n            [12.631147, 61.293572],\n            [11.992064, 61.800362],\n            [11.930569, 63.128318],\n            [12.579935, 64.066219],\n            [13.571916, 64.049114],\n            [13.919905, 64.445421],\n            [13.55569, 64.787028],\n            [15.108411, 66.193867],\n            [16.108712, 67.302456],\n            [16.768879, 68.013937],\n            [17.729182, 68.010552],\n            [17.993868, 68.567391],\n            [19.87856, 68.407194],\n            [20.025269, 69.065139],\n            [20.645593, 69.106247]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Swaziland\",\n        \"SOV_A3\": \"SWZ\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Swaziland\",\n        \"ADM0_A3\": \"SWZ\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Swaziland\",\n        \"GU_A3\": \"SWZ\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Swaziland\",\n        \"SU_A3\": \"SWZ\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Swaziland\",\n        \"NAME_LONG\": \"Swaziland\",\n        \"BRK_A3\": \"SWZ\",\n        \"BRK_NAME\": \"Swaziland\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Swz.\",\n        \"POSTAL\": \"SW\",\n        \"FORMAL_EN\": \"Kingdom of Swaziland\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Swaziland\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Swaziland\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 1467152,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 11060,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2007,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"WZ\",\n        \"ISO_A2\": \"SZ\",\n        \"ISO_A3\": \"SWZ\",\n        \"ISO_A3_EH\": \"SWZ\",\n        \"ISO_N3\": \"748\",\n        \"UN_A3\": \"748\",\n        \"WB_A2\": \"SZ\",\n        \"WB_A3\": \"SWZ\",\n        \"WOE_ID\": 23424993,\n        \"WOE_ID_EH\": 23424993,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SWZ\",\n        \"ADM0_A3_US\": \"SWZ\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Southern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [30.676609, -27.285879, 32.071665, -25.660191],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [31.837778, -25.843332],\n            [31.985779, -26.29178],\n            [32.071665, -26.73382],\n            [31.86806, -27.177927],\n            [31.282773, -27.285879],\n            [30.685962, -26.743845],\n            [30.676609, -26.398078],\n            [30.949667, -26.022649],\n            [31.04408, -25.731452],\n            [31.333158, -25.660191],\n            [31.837778, -25.843332]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Syria\",\n        \"SOV_A3\": \"SYR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Syria\",\n        \"ADM0_A3\": \"SYR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Syria\",\n        \"GU_A3\": \"SYR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Syria\",\n        \"SU_A3\": \"SYR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Syria\",\n        \"NAME_LONG\": \"Syria\",\n        \"BRK_A3\": \"SYR\",\n        \"BRK_NAME\": \"Syria\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Syria\",\n        \"POSTAL\": \"SYR\",\n        \"FORMAL_EN\": \"Syrian Arab Republic\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Syria\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Syrian Arab Republic\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 18028549,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 50280,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2015,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SY\",\n        \"ISO_A2\": \"SY\",\n        \"ISO_A3\": \"SYR\",\n        \"ISO_A3_EH\": \"SYR\",\n        \"ISO_N3\": \"760\",\n        \"UN_A3\": \"760\",\n        \"WB_A2\": \"SY\",\n        \"WB_A3\": \"SYR\",\n        \"WOE_ID\": 23424956,\n        \"WOE_ID_EH\": 23424956,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"SYR\",\n        \"ADM0_A3_US\": \"SYR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [35.700798, 32.312938, 42.349591, 37.229873],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [42.349591, 37.229873],\n            [41.837064, 36.605854],\n            [41.289707, 36.358815],\n            [41.383965, 35.628317],\n            [41.006159, 34.419372],\n            [38.792341, 33.378686],\n            [36.834062, 32.312938],\n            [35.719918, 32.709192],\n            [35.700798, 32.716014],\n            [35.836397, 32.868123],\n            [35.821101, 33.277426],\n            [36.06646, 33.824912],\n            [36.61175, 34.201789],\n            [36.448194, 34.593935],\n            [35.998403, 34.644914],\n            [35.905023, 35.410009],\n            [36.149763, 35.821535],\n            [36.41755, 36.040617],\n            [36.685389, 36.259699],\n            [36.739494, 36.81752],\n            [37.066761, 36.623036],\n            [38.167727, 36.90121],\n            [38.699891, 36.712927],\n            [39.52258, 36.716054],\n            [40.673259, 37.091276],\n            [41.212089, 37.074352],\n            [42.349591, 37.229873]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Chad\",\n        \"SOV_A3\": \"TCD\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Chad\",\n        \"ADM0_A3\": \"TCD\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Chad\",\n        \"GU_A3\": \"TCD\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Chad\",\n        \"SU_A3\": \"TCD\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Chad\",\n        \"NAME_LONG\": \"Chad\",\n        \"BRK_A3\": \"TCD\",\n        \"BRK_NAME\": \"Chad\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Chad\",\n        \"POSTAL\": \"TD\",\n        \"FORMAL_EN\": \"Republic of Chad\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Chad\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Chad\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 8,\n        \"MAPCOLOR13\": 6,\n        \"POP_EST\": 12075985,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 30590,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"CD\",\n        \"ISO_A2\": \"TD\",\n        \"ISO_A3\": \"TCD\",\n        \"ISO_A3_EH\": \"TCD\",\n        \"ISO_N3\": \"148\",\n        \"UN_A3\": \"148\",\n        \"WB_A2\": \"TD\",\n        \"WB_A3\": \"TCD\",\n        \"WOE_ID\": 23424777,\n        \"WOE_ID_EH\": 23424777,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TCD\",\n        \"ADM0_A3_US\": \"TCD\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Middle Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [13.540394, 7.421925, 23.88689, 23.40972],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [22.864165, 11.142395],\n            [22.231129, 10.971889],\n            [21.723822, 10.567056],\n            [21.000868, 9.475985],\n            [20.059685, 9.012706],\n            [19.094008, 9.074847],\n            [18.81201, 8.982915],\n            [18.911022, 8.630895],\n            [18.389555, 8.281304],\n            [17.96493, 7.890914],\n            [16.705988, 7.508328],\n            [16.456185, 7.734774],\n            [16.290562, 7.754307],\n            [16.106232, 7.497088],\n            [15.27946, 7.421925],\n            [15.436092, 7.692812],\n            [15.120866, 8.38215],\n            [14.979996, 8.796104],\n            [14.544467, 8.965861],\n            [13.954218, 9.549495],\n            [14.171466, 10.021378],\n            [14.627201, 9.920919],\n            [14.909354, 9.992129],\n            [15.467873, 9.982337],\n            [14.923565, 10.891325],\n            [14.960152, 11.555574],\n            [14.89336, 12.21905],\n            [14.495787, 12.859396],\n            [14.595781, 13.330427],\n            [13.954477, 13.353449],\n            [13.956699, 13.996691],\n            [13.540394, 14.367134],\n            [13.97217, 15.68437],\n            [15.247731, 16.627306],\n            [15.300441, 17.92795],\n            [15.685741, 19.95718],\n            [15.903247, 20.387619],\n            [15.487148, 20.730415],\n            [15.47106, 21.04845],\n            [15.096888, 21.308519],\n            [14.8513, 22.86295],\n            [15.86085, 23.40972],\n            [19.84926, 21.49509],\n            [23.83766, 19.58047],\n            [23.88689, 15.61084],\n            [23.02459, 15.68072],\n            [22.56795, 14.94429],\n            [22.30351, 14.32682],\n            [22.51202, 14.09318],\n            [22.18329, 13.78648],\n            [22.29658, 13.37232],\n            [22.03759, 12.95546],\n            [21.93681, 12.58818],\n            [22.28801, 12.64605],\n            [22.49762, 12.26024],\n            [22.50869, 11.67936],\n            [22.87622, 11.38461],\n            [22.864165, 11.142395]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 6,\n        \"SOVEREIGNT\": \"Togo\",\n        \"SOV_A3\": \"TGO\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Togo\",\n        \"ADM0_A3\": \"TGO\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Togo\",\n        \"GU_A3\": \"TGO\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Togo\",\n        \"SU_A3\": \"TGO\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Togo\",\n        \"NAME_LONG\": \"Togo\",\n        \"BRK_A3\": \"TGO\",\n        \"BRK_NAME\": \"Togo\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Togo\",\n        \"POSTAL\": \"TG\",\n        \"FORMAL_EN\": \"Togolese Republic\",\n        \"FORMAL_FR\": \"République Togolaise\",\n        \"NAME_CIAWF\": \"Togo\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Togo\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 7965055,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 11610,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TO\",\n        \"ISO_A2\": \"TG\",\n        \"ISO_A3\": \"TGO\",\n        \"ISO_A3_EH\": \"TGO\",\n        \"ISO_N3\": \"768\",\n        \"UN_A3\": \"768\",\n        \"WB_A2\": \"TG\",\n        \"WB_A3\": \"TGO\",\n        \"WOE_ID\": 23424965,\n        \"WOE_ID_EH\": 23424965,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TGO\",\n        \"ADM0_A3_US\": \"TGO\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Western Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 4,\n        \"LONG_LEN\": 4,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 5,\n        \"MAX_LABEL\": 10\n      },\n      \"bbox\": [-0.049785, 5.928837, 1.865241, 11.018682],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [0.899563, 10.997339],\n            [0.772336, 10.470808],\n            [1.077795, 10.175607],\n            [1.425061, 9.825395],\n            [1.463043, 9.334624],\n            [1.664478, 9.12859],\n            [1.618951, 6.832038],\n            [1.865241, 6.142158],\n            [1.060122, 5.928837],\n            [0.836931, 6.279979],\n            [0.570384, 6.914359],\n            [0.490957, 7.411744],\n            [0.712029, 8.312465],\n            [0.461192, 8.677223],\n            [0.365901, 9.465004],\n            [0.36758, 10.191213],\n            [-0.049785, 10.706918],\n            [0.023803, 11.018682],\n            [0.899563, 10.997339]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Thailand\",\n        \"SOV_A3\": \"THA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Thailand\",\n        \"ADM0_A3\": \"THA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Thailand\",\n        \"GU_A3\": \"THA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Thailand\",\n        \"SU_A3\": \"THA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Thailand\",\n        \"NAME_LONG\": \"Thailand\",\n        \"BRK_A3\": \"THA\",\n        \"BRK_NAME\": \"Thailand\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Thai.\",\n        \"POSTAL\": \"TH\",\n        \"FORMAL_EN\": \"Kingdom of Thailand\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Thailand\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Thailand\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 8,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 68414135,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 1161000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TH\",\n        \"ISO_A2\": \"TH\",\n        \"ISO_A3\": \"THA\",\n        \"ISO_A3_EH\": \"THA\",\n        \"ISO_N3\": \"764\",\n        \"UN_A3\": \"764\",\n        \"WB_A2\": \"TH\",\n        \"WB_A3\": \"THA\",\n        \"WOE_ID\": 23424960,\n        \"WOE_ID_EH\": 23424960,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"THA\",\n        \"ADM0_A3_US\": \"THA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [97.375896, 5.691384, 105.589039, 20.41785],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [105.218777, 14.273212],\n            [104.281418, 14.416743],\n            [102.988422, 14.225721],\n            [102.348099, 13.394247],\n            [102.584932, 12.186595],\n            [101.687158, 12.64574],\n            [100.83181, 12.627085],\n            [100.978467, 13.412722],\n            [100.097797, 13.406856],\n            [100.018733, 12.307001],\n            [99.478921, 10.846367],\n            [99.153772, 9.963061],\n            [99.222399, 9.239255],\n            [99.873832, 9.207862],\n            [100.279647, 8.295153],\n            [100.459274, 7.429573],\n            [101.017328, 6.856869],\n            [101.623079, 6.740622],\n            [102.141187, 6.221636],\n            [101.814282, 5.810808],\n            [101.154219, 5.691384],\n            [101.075516, 6.204867],\n            [100.259596, 6.642825],\n            [100.085757, 6.464489],\n            [99.690691, 6.848213],\n            [99.519642, 7.343454],\n            [98.988253, 7.907993],\n            [98.503786, 8.382305],\n            [98.339662, 7.794512],\n            [98.150009, 8.350007],\n            [98.25915, 8.973923],\n            [98.553551, 9.93296],\n            [99.038121, 10.960546],\n            [99.587286, 11.892763],\n            [99.196354, 12.804748],\n            [99.212012, 13.269294],\n            [99.097755, 13.827503],\n            [98.430819, 14.622028],\n            [98.192074, 15.123703],\n            [98.537376, 15.308497],\n            [98.903348, 16.177824],\n            [98.493761, 16.837836],\n            [97.859123, 17.567946],\n            [97.375896, 18.445438],\n            [97.797783, 18.62708],\n            [98.253724, 19.708203],\n            [98.959676, 19.752981],\n            [99.543309, 20.186598],\n            [100.115988, 20.41785],\n            [100.548881, 20.109238],\n            [100.606294, 19.508344],\n            [101.282015, 19.462585],\n            [101.035931, 18.408928],\n            [101.059548, 17.512497],\n            [102.113592, 18.109102],\n            [102.413005, 17.932782],\n            [102.998706, 17.961695],\n            [103.200192, 18.309632],\n            [103.956477, 18.240954],\n            [104.716947, 17.428859],\n            [104.779321, 16.441865],\n            [105.589039, 15.570316],\n            [105.544338, 14.723934],\n            [105.218777, 14.273212]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Tajikistan\",\n        \"SOV_A3\": \"TJK\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Tajikistan\",\n        \"ADM0_A3\": \"TJK\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Tajikistan\",\n        \"GU_A3\": \"TJK\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Tajikistan\",\n        \"SU_A3\": \"TJK\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Tajikistan\",\n        \"NAME_LONG\": \"Tajikistan\",\n        \"BRK_A3\": \"TJK\",\n        \"BRK_NAME\": \"Tajikistan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Tjk.\",\n        \"POSTAL\": \"TJ\",\n        \"FORMAL_EN\": \"Republic of Tajikistan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Tajikistan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Tajikistan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 8468555,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 25810,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TI\",\n        \"ISO_A2\": \"TJ\",\n        \"ISO_A3\": \"TJK\",\n        \"ISO_A3_EH\": \"TJK\",\n        \"ISO_N3\": \"762\",\n        \"UN_A3\": \"762\",\n        \"WB_A2\": \"TJ\",\n        \"WB_A3\": \"TJK\",\n        \"WOE_ID\": 23424961,\n        \"WOE_ID_EH\": 23424961,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TJK\",\n        \"ADM0_A3_US\": \"TJK\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Central Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [67.44222, 36.738171, 74.980002, 40.960213],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [74.980002, 37.41999],\n            [73.948696, 37.421566],\n            [73.260056, 37.495257],\n            [72.63689, 37.047558],\n            [72.193041, 36.948288],\n            [71.844638, 36.738171],\n            [71.448693, 37.065645],\n            [71.541918, 37.905774],\n            [71.239404, 37.953265],\n            [71.348131, 38.258905],\n            [70.806821, 38.486282],\n            [70.376304, 38.138396],\n            [70.270574, 37.735165],\n            [70.116578, 37.588223],\n            [69.518785, 37.608997],\n            [69.196273, 37.151144],\n            [68.859446, 37.344336],\n            [68.135562, 37.023115],\n            [67.83, 37.144994],\n            [68.392033, 38.157025],\n            [68.176025, 38.901553],\n            [67.44222, 39.140144],\n            [67.701429, 39.580478],\n            [68.536416, 39.533453],\n            [69.011633, 40.086158],\n            [69.329495, 40.727824],\n            [70.666622, 40.960213],\n            [70.45816, 40.496495],\n            [70.601407, 40.218527],\n            [71.014198, 40.244366],\n            [70.648019, 39.935754],\n            [69.55961, 40.103211],\n            [69.464887, 39.526683],\n            [70.549162, 39.604198],\n            [71.784694, 39.279463],\n            [73.675379, 39.431237],\n            [73.928852, 38.505815],\n            [74.257514, 38.606507],\n            [74.864816, 38.378846],\n            [74.829986, 37.990007],\n            [74.980002, 37.41999]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Turkmenistan\",\n        \"SOV_A3\": \"TKM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Turkmenistan\",\n        \"ADM0_A3\": \"TKM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Turkmenistan\",\n        \"GU_A3\": \"TKM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Turkmenistan\",\n        \"SU_A3\": \"TKM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Turkmenistan\",\n        \"NAME_LONG\": \"Turkmenistan\",\n        \"BRK_A3\": \"TKM\",\n        \"BRK_NAME\": \"Turkmenistan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Turkm.\",\n        \"POSTAL\": \"TM\",\n        \"FORMAL_EN\": \"Turkmenistan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Turkmenistan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Turkmenistan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 5351277,\n        \"POP_RANK\": 13,\n        \"GDP_MD_EST\": 94720,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1995,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TX\",\n        \"ISO_A2\": \"TM\",\n        \"ISO_A3\": \"TKM\",\n        \"ISO_A3_EH\": \"TKM\",\n        \"ISO_N3\": \"795\",\n        \"UN_A3\": \"795\",\n        \"WB_A2\": \"TM\",\n        \"WB_A3\": \"TKM\",\n        \"WOE_ID\": 23424972,\n        \"WOE_ID_EH\": 23424972,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TKM\",\n        \"ADM0_A3_US\": \"TKM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Central Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 12,\n        \"LONG_LEN\": 12,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [52.50246, 35.270664, 66.54615, 42.751551],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [66.518607, 37.362784],\n            [66.217385, 37.39379],\n            [65.745631, 37.661164],\n            [65.588948, 37.305217],\n            [64.746105, 37.111818],\n            [64.546479, 36.312073],\n            [63.982896, 36.007957],\n            [63.193538, 35.857166],\n            [62.984662, 35.404041],\n            [62.230651, 35.270664],\n            [61.210817, 35.650072],\n            [61.123071, 36.491597],\n            [60.377638, 36.527383],\n            [59.234762, 37.412988],\n            [58.436154, 37.522309],\n            [57.330434, 38.029229],\n            [56.619366, 38.121394],\n            [56.180375, 37.935127],\n            [55.511578, 37.964117],\n            [54.800304, 37.392421],\n            [53.921598, 37.198918],\n            [53.735511, 37.906136],\n            [53.880929, 38.952093],\n            [53.101028, 39.290574],\n            [53.357808, 39.975286],\n            [52.693973, 40.033629],\n            [52.915251, 40.876523],\n            [53.858139, 40.631034],\n            [54.736845, 40.951015],\n            [54.008311, 41.551211],\n            [53.721713, 42.123191],\n            [52.91675, 41.868117],\n            [52.814689, 41.135371],\n            [52.50246, 41.783316],\n            [52.944293, 42.116034],\n            [54.079418, 42.324109],\n            [54.755345, 42.043971],\n            [55.455251, 41.259859],\n            [55.968191, 41.308642],\n            [57.096391, 41.32231],\n            [56.932215, 41.826026],\n            [57.78653, 42.170553],\n            [58.629011, 42.751551],\n            [59.976422, 42.223082],\n            [60.083341, 41.425146],\n            [60.465953, 41.220327],\n            [61.547179, 41.26637],\n            [61.882714, 41.084857],\n            [62.37426, 40.053886],\n            [63.518015, 39.363257],\n            [64.170223, 38.892407],\n            [65.215999, 38.402695],\n            [66.54615, 37.974685],\n            [66.518607, 37.362784]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"East Timor\",\n        \"SOV_A3\": \"TLS\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"East Timor\",\n        \"ADM0_A3\": \"TLS\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"East Timor\",\n        \"GU_A3\": \"TLS\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"East Timor\",\n        \"SU_A3\": \"TLS\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Timor-Leste\",\n        \"NAME_LONG\": \"Timor-Leste\",\n        \"BRK_A3\": \"TLS\",\n        \"BRK_NAME\": \"Timor-Leste\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"T.L.\",\n        \"POSTAL\": \"TL\",\n        \"FORMAL_EN\": \"Democratic Republic of Timor-Leste\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Timor-Leste\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Timor-Leste\",\n        \"NAME_ALT\": \"East Timor\",\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 1291358,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 4975,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TT\",\n        \"ISO_A2\": \"TL\",\n        \"ISO_A3\": \"TLS\",\n        \"ISO_A3_EH\": \"TLS\",\n        \"ISO_N3\": \"626\",\n        \"UN_A3\": \"626\",\n        \"WB_A2\": \"TP\",\n        \"WB_A3\": \"TMP\",\n        \"WOE_ID\": 23424968,\n        \"WOE_ID_EH\": 23424968,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TLS\",\n        \"ADM0_A3_US\": \"TLS\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 11,\n        \"LONG_LEN\": 11,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [124.968682, -9.393173, 127.335928, -8.273345],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [125.08852, -9.393173],\n            [125.07002, -9.089987],\n            [124.968682, -8.89279],\n            [125.086246, -8.656887],\n            [125.947072, -8.432095],\n            [126.644704, -8.398247],\n            [126.957243, -8.273345],\n            [127.335928, -8.397317],\n            [126.967992, -8.668256],\n            [125.925885, -9.106007],\n            [125.08852, -9.393173]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 5,\n        \"SOVEREIGNT\": \"Trinidad and Tobago\",\n        \"SOV_A3\": \"TTO\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Trinidad and Tobago\",\n        \"ADM0_A3\": \"TTO\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Trinidad and Tobago\",\n        \"GU_A3\": \"TTO\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Trinidad and Tobago\",\n        \"SU_A3\": \"TTO\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Trinidad and Tobago\",\n        \"NAME_LONG\": \"Trinidad and Tobago\",\n        \"BRK_A3\": \"TTO\",\n        \"BRK_NAME\": \"Trinidad and Tobago\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Tr.T.\",\n        \"POSTAL\": \"TT\",\n        \"FORMAL_EN\": \"Republic of Trinidad and Tobago\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Trinidad and Tobago\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Trinidad and Tobago\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 5,\n        \"POP_EST\": 1218208,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 43570,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2011,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TD\",\n        \"ISO_A2\": \"TT\",\n        \"ISO_A3\": \"TTO\",\n        \"ISO_A3_EH\": \"TTO\",\n        \"ISO_N3\": \"780\",\n        \"UN_A3\": \"780\",\n        \"WB_A2\": \"TT\",\n        \"WB_A3\": \"TTO\",\n        \"WOE_ID\": 23424958,\n        \"WOE_ID_EH\": 23424958,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TTO\",\n        \"ADM0_A3_US\": \"TTO\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Caribbean\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 19,\n        \"LONG_LEN\": 19,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": 2,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4.5,\n        \"MAX_LABEL\": 9.5\n      },\n      \"bbox\": [-61.95, 10, -60.895, 10.89],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-61.68, 10.76],\n            [-61.105, 10.89],\n            [-60.895, 10.855],\n            [-60.935, 10.11],\n            [-61.77, 10],\n            [-61.95, 10.09],\n            [-61.66, 10.365],\n            [-61.68, 10.76]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Tunisia\",\n        \"SOV_A3\": \"TUN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Tunisia\",\n        \"ADM0_A3\": \"TUN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Tunisia\",\n        \"GU_A3\": \"TUN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Tunisia\",\n        \"SU_A3\": \"TUN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Tunisia\",\n        \"NAME_LONG\": \"Tunisia\",\n        \"BRK_A3\": \"TUN\",\n        \"BRK_NAME\": \"Tunisia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Tun.\",\n        \"POSTAL\": \"TN\",\n        \"FORMAL_EN\": \"Republic of Tunisia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Tunisia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Tunisia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 11403800,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 130800,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TS\",\n        \"ISO_A2\": \"TN\",\n        \"ISO_A3\": \"TUN\",\n        \"ISO_A3_EH\": \"TUN\",\n        \"ISO_N3\": \"788\",\n        \"UN_A3\": \"788\",\n        \"WB_A2\": \"TN\",\n        \"WB_A3\": \"TUN\",\n        \"WOE_ID\": 23424967,\n        \"WOE_ID_EH\": 23424967,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TUN\",\n        \"ADM0_A3_US\": \"TUN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Northern Africa\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [7.524482, 30.307556, 11.488787, 37.349994],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [9.48214, 30.307556],\n            [9.055603, 32.102692],\n            [8.439103, 32.506285],\n            [8.430473, 32.748337],\n            [7.612642, 33.344115],\n            [7.524482, 34.097376],\n            [8.140981, 34.655146],\n            [8.376368, 35.479876],\n            [8.217824, 36.433177],\n            [8.420964, 36.946427],\n            [9.509994, 37.349994],\n            [10.210002, 37.230002],\n            [10.18065, 36.724038],\n            [11.028867, 37.092103],\n            [11.100026, 36.899996],\n            [10.600005, 36.41],\n            [10.593287, 35.947444],\n            [10.939519, 35.698984],\n            [10.807847, 34.833507],\n            [10.149593, 34.330773],\n            [10.339659, 33.785742],\n            [10.856836, 33.76874],\n            [11.108501, 33.293343],\n            [11.488787, 33.136996],\n            [11.432253, 32.368903],\n            [10.94479, 32.081815],\n            [10.636901, 31.761421],\n            [9.950225, 31.37607],\n            [10.056575, 30.961831],\n            [9.970017, 30.539325],\n            [9.48214, 30.307556]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Turkey\",\n        \"SOV_A3\": \"TUR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Turkey\",\n        \"ADM0_A3\": \"TUR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Turkey\",\n        \"GU_A3\": \"TUR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Turkey\",\n        \"SU_A3\": \"TUR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Turkey\",\n        \"NAME_LONG\": \"Turkey\",\n        \"BRK_A3\": \"TUR\",\n        \"BRK_NAME\": \"Turkey\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Tur.\",\n        \"POSTAL\": \"TR\",\n        \"FORMAL_EN\": \"Republic of Turkey\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Turkey\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Turkey\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 8,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 80845215,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 1670000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2000,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"4. Emerging region: MIKT\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TU\",\n        \"ISO_A2\": \"TR\",\n        \"ISO_A3\": \"TUR\",\n        \"ISO_A3_EH\": \"TUR\",\n        \"ISO_N3\": \"792\",\n        \"UN_A3\": \"792\",\n        \"WB_A2\": \"TR\",\n        \"WB_A3\": \"TUR\",\n        \"WOE_ID\": 23424969,\n        \"WOE_ID_EH\": 23424969,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TUR\",\n        \"ADM0_A3_US\": \"TUR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [26.043351, 35.821535, 44.79399, 42.141485],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [43.582746, 41.092143],\n              [43.752658, 40.740201],\n              [43.656436, 40.253564],\n              [44.400009, 40.005],\n              [44.79399, 39.713003],\n              [44.109225, 39.428136],\n              [44.421403, 38.281281],\n              [44.225756, 37.971584],\n              [44.77267, 37.17045],\n              [44.772677, 37.170437],\n              [44.293452, 37.001514],\n              [43.942259, 37.256228],\n              [42.779126, 37.385264],\n              [42.349591, 37.229873],\n              [41.212089, 37.074352],\n              [40.673259, 37.091276],\n              [39.52258, 36.716054],\n              [38.699891, 36.712927],\n              [38.167727, 36.90121],\n              [37.066761, 36.623036],\n              [36.739494, 36.81752],\n              [36.685389, 36.259699],\n              [36.41755, 36.040617],\n              [36.149763, 35.821535],\n              [35.782085, 36.274995],\n              [36.160822, 36.650606],\n              [35.550936, 36.565443],\n              [34.714553, 36.795532],\n              [34.026895, 36.21996],\n              [32.509158, 36.107564],\n              [31.699595, 36.644275],\n              [30.621625, 36.677865],\n              [30.391096, 36.262981],\n              [29.699976, 36.144357],\n              [28.732903, 36.676831],\n              [27.641187, 36.658822],\n              [27.048768, 37.653361],\n              [26.318218, 38.208133],\n              [26.8047, 38.98576],\n              [26.170785, 39.463612],\n              [27.28002, 40.420014],\n              [28.819978, 40.460011],\n              [29.240004, 41.219991],\n              [31.145934, 41.087622],\n              [32.347979, 41.736264],\n              [33.513283, 42.01896],\n              [35.167704, 42.040225],\n              [36.913127, 41.335358],\n              [38.347665, 40.948586],\n              [39.512607, 41.102763],\n              [40.373433, 41.013673],\n              [41.554084, 41.535656],\n              [42.619549, 41.583173],\n              [43.582746, 41.092143]\n            ]\n          ],\n          [\n            [\n              [26.117042, 41.826905],\n              [27.135739, 42.141485],\n              [27.99672, 42.007359],\n              [28.115525, 41.622886],\n              [28.988443, 41.299934],\n              [28.806438, 41.054962],\n              [27.619017, 40.999823],\n              [27.192377, 40.690566],\n              [26.358009, 40.151994],\n              [26.043351, 40.617754],\n              [26.056942, 40.824123],\n              [26.294602, 40.936261],\n              [26.604196, 41.562115],\n              [26.117042, 41.826905]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Taiwan\",\n        \"SOV_A3\": \"TWN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Taiwan\",\n        \"ADM0_A3\": \"TWN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Taiwan\",\n        \"GU_A3\": \"TWN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Taiwan\",\n        \"SU_A3\": \"TWN\",\n        \"BRK_DIFF\": 1,\n        \"NAME\": \"Taiwan\",\n        \"NAME_LONG\": \"Taiwan\",\n        \"BRK_A3\": \"B77\",\n        \"BRK_NAME\": \"Taiwan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Taiwan\",\n        \"POSTAL\": \"TW\",\n        \"FORMAL_EN\": null,\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Taiwan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": \"Self admin.; Claimed by China\",\n        \"NAME_SORT\": \"Taiwan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 23508428,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 1127000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": -99,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"2. Developed region: nonG7\",\n        \"INCOME_GRP\": \"2. High income: nonOECD\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TW\",\n        \"ISO_A2\": \"TW\",\n        \"ISO_A3\": \"TWN\",\n        \"ISO_A3_EH\": \"TWN\",\n        \"ISO_N3\": \"158\",\n        \"UN_A3\": \"-099\",\n        \"WB_A2\": \"-99\",\n        \"WB_A3\": \"-99\",\n        \"WOE_ID\": 23424971,\n        \"WOE_ID_EH\": 23424971,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TWN\",\n        \"ADM0_A3_US\": \"TWN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [120.106189, 21.970571, 121.951244, 25.295459],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [121.777818, 24.394274],\n            [121.175632, 22.790857],\n            [120.74708, 21.970571],\n            [120.220083, 22.814861],\n            [120.106189, 23.556263],\n            [120.69468, 24.538451],\n            [121.495044, 25.295459],\n            [121.951244, 24.997596],\n            [121.777818, 24.394274]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"United Republic of Tanzania\",\n        \"SOV_A3\": \"TZA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"United Republic of Tanzania\",\n        \"ADM0_A3\": \"TZA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Tanzania\",\n        \"GU_A3\": \"TZA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Tanzania\",\n        \"SU_A3\": \"TZA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Tanzania\",\n        \"NAME_LONG\": \"Tanzania\",\n        \"BRK_A3\": \"TZA\",\n        \"BRK_NAME\": \"Tanzania\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Tanz.\",\n        \"POSTAL\": \"TZ\",\n        \"FORMAL_EN\": \"United Republic of Tanzania\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Tanzania\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Tanzania\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 3,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 53950935,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 150600,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"TZ\",\n        \"ISO_A2\": \"TZ\",\n        \"ISO_A3\": \"TZA\",\n        \"ISO_A3_EH\": \"TZA\",\n        \"ISO_N3\": \"834\",\n        \"UN_A3\": \"834\",\n        \"WB_A2\": \"TZ\",\n        \"WB_A3\": \"TZA\",\n        \"WOE_ID\": 23424973,\n        \"WOE_ID_EH\": 23424973,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"TZA\",\n        \"ADM0_A3_US\": \"TZA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [29.339998, -11.720938, 40.31659, -0.95],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [29.339998, -4.499983],\n            [29.753512, -4.452389],\n            [30.11632, -4.09012],\n            [30.50554, -3.56858],\n            [30.75224, -3.35931],\n            [30.74301, -3.03431],\n            [30.52766, -2.80762],\n            [30.469674, -2.413855],\n            [30.46967, -2.41383],\n            [30.758309, -2.28725],\n            [30.816135, -1.698914],\n            [30.419105, -1.134659],\n            [30.76986, -1.01455],\n            [31.86617, -1.02736],\n            [33.903711, -0.95],\n            [34.07262, -1.05982],\n            [37.69869, -3.09699],\n            [37.7669, -3.67712],\n            [39.20222, -4.67677],\n            [38.74054, -5.90895],\n            [38.79977, -6.47566],\n            [39.44, -6.84],\n            [39.47, -7.1],\n            [39.19469, -7.7039],\n            [39.25203, -8.00781],\n            [39.18652, -8.48551],\n            [39.53574, -9.11237],\n            [39.9496, -10.0984],\n            [40.316586, -10.317098],\n            [40.31659, -10.3171],\n            [39.521, -10.89688],\n            [38.427557, -11.285202],\n            [37.82764, -11.26879],\n            [37.47129, -11.56876],\n            [36.775151, -11.594537],\n            [36.514082, -11.720938],\n            [35.312398, -11.439146],\n            [34.559989, -11.52002],\n            [34.28, -10.16],\n            [33.940838, -9.693674],\n            [33.73972, -9.41715],\n            [32.759375, -9.230599],\n            [32.191865, -8.930359],\n            [31.556348, -8.762049],\n            [31.157751, -8.594579],\n            [30.74001, -8.340006],\n            [30.740015, -8.340007],\n            [30.199997, -7.079981],\n            [29.620032, -6.520015],\n            [29.419993, -5.939999],\n            [29.519987, -5.419979],\n            [29.339998, -4.499983]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Uganda\",\n        \"SOV_A3\": \"UGA\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Uganda\",\n        \"ADM0_A3\": \"UGA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Uganda\",\n        \"GU_A3\": \"UGA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Uganda\",\n        \"SU_A3\": \"UGA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Uganda\",\n        \"NAME_LONG\": \"Uganda\",\n        \"BRK_A3\": \"UGA\",\n        \"BRK_NAME\": \"Uganda\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Uga.\",\n        \"POSTAL\": \"UG\",\n        \"FORMAL_EN\": \"Republic of Uganda\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Uganda\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Uganda\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 39570125,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 84930,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"UG\",\n        \"ISO_A2\": \"UG\",\n        \"ISO_A3\": \"UGA\",\n        \"ISO_A3_EH\": \"UGA\",\n        \"ISO_N3\": \"800\",\n        \"UN_A3\": \"800\",\n        \"WB_A2\": \"UG\",\n        \"WB_A3\": \"UGA\",\n        \"WOE_ID\": 23424974,\n        \"WOE_ID_EH\": 23424974,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"UGA\",\n        \"ADM0_A3_US\": \"UGA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [29.579466, -1.443322, 35.03599, 4.249885],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [29.579466, -1.341313],\n            [29.587838, -0.587406],\n            [29.819503, -0.20531],\n            [29.875779, 0.59738],\n            [30.086154, 1.062313],\n            [30.468508, 1.583805],\n            [30.85267, 1.849396],\n            [31.174149, 2.204465],\n            [30.773347, 2.339883],\n            [30.83386, 3.509166],\n            [30.833852, 3.509172],\n            [31.24556, 3.7819],\n            [31.88145, 3.55827],\n            [32.68642, 3.79232],\n            [33.39, 3.79],\n            [34.005, 4.249885],\n            [34.47913, 3.5556],\n            [34.59607, 3.05374],\n            [35.03599, 1.90584],\n            [34.6721, 1.17694],\n            [34.18, 0.515],\n            [33.893569, 0.109814],\n            [33.903711, -0.95],\n            [31.86617, -1.02736],\n            [30.76986, -1.01455],\n            [30.419105, -1.134659],\n            [29.821519, -1.443322],\n            [29.579466, -1.341313]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Ukraine\",\n        \"SOV_A3\": \"UKR\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Ukraine\",\n        \"ADM0_A3\": \"UKR\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Ukraine\",\n        \"GU_A3\": \"UKR\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Ukraine\",\n        \"SU_A3\": \"UKR\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Ukraine\",\n        \"NAME_LONG\": \"Ukraine\",\n        \"BRK_A3\": \"UKR\",\n        \"BRK_NAME\": \"Ukraine\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ukr.\",\n        \"POSTAL\": \"UA\",\n        \"FORMAL_EN\": \"Ukraine\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Ukraine\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Ukraine\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 1,\n        \"MAPCOLOR9\": 6,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 44033874,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 352600,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"UP\",\n        \"ISO_A2\": \"UA\",\n        \"ISO_A3\": \"UKR\",\n        \"ISO_A3_EH\": \"UKR\",\n        \"ISO_N3\": \"804\",\n        \"UN_A3\": \"804\",\n        \"WB_A2\": \"UA\",\n        \"WB_A3\": \"UKR\",\n        \"WOE_ID\": 23424976,\n        \"WOE_ID_EH\": 23424976,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"UKR\",\n        \"ADM0_A3_US\": \"UKR\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Europe\",\n        \"REGION_UN\": \"Europe\",\n        \"SUBREGION\": \"Eastern Europe\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [22.085608, 45.293308, 40.080789, 52.335075],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [23.527071, 51.578454],\n            [24.005078, 51.617444],\n            [24.553106, 51.888461],\n            [25.327788, 51.910656],\n            [26.337959, 51.832289],\n            [27.454066, 51.592303],\n            [28.241615, 51.572227],\n            [28.617613, 51.427714],\n            [28.992835, 51.602044],\n            [29.254938, 51.368234],\n            [30.157364, 51.416138],\n            [30.555117, 51.319503],\n            [30.619454, 51.822806],\n            [30.927549, 52.042353],\n            [31.785992, 52.101678],\n            [32.15944, 52.06125],\n            [32.412058, 52.288695],\n            [32.715761, 52.238465],\n            [33.7527, 52.335075],\n            [34.391731, 51.768882],\n            [34.141978, 51.566413],\n            [34.224816, 51.255993],\n            [35.022183, 51.207572],\n            [35.37791, 50.77394],\n            [35.356116, 50.577197],\n            [36.626168, 50.225591],\n            [37.39346, 50.383953],\n            [38.010631, 49.915662],\n            [38.594988, 49.926462],\n            [40.06904, 49.60105],\n            [40.080789, 49.30743],\n            [39.67465, 48.78382],\n            [39.89562, 48.23241],\n            [39.738278, 47.898937],\n            [38.77057, 47.82562],\n            [38.255112, 47.5464],\n            [38.223538, 47.10219],\n            [37.425137, 47.022221],\n            [36.759855, 46.6987],\n            [35.823685, 46.645964],\n            [34.962342, 46.273197],\n            [35.012659, 45.737725],\n            [35.020788, 45.651219],\n            [35.510009, 45.409993],\n            [36.529998, 45.46999],\n            [36.334713, 45.113216],\n            [35.239999, 44.939996],\n            [33.882511, 44.361479],\n            [33.326421, 44.564877],\n            [33.546924, 45.034771],\n            [32.454174, 45.327466],\n            [32.630804, 45.519186],\n            [33.588162, 45.851569],\n            [33.435988, 45.971917],\n            [33.298567, 46.080598],\n            [31.74414, 46.333348],\n            [31.675307, 46.706245],\n            [30.748749, 46.5831],\n            [30.377609, 46.03241],\n            [29.603289, 45.293308],\n            [29.149725, 45.464925],\n            [28.679779, 45.304031],\n            [28.233554, 45.488283],\n            [28.485269, 45.596907],\n            [28.659987, 45.939987],\n            [28.933717, 46.25883],\n            [28.862972, 46.437889],\n            [29.072107, 46.517678],\n            [29.170654, 46.379262],\n            [29.759972, 46.349988],\n            [30.024659, 46.423937],\n            [29.83821, 46.525326],\n            [29.908852, 46.674361],\n            [29.559674, 46.928583],\n            [29.415135, 47.346645],\n            [29.050868, 47.510227],\n            [29.122698, 47.849095],\n            [28.670891, 48.118149],\n            [28.259547, 48.155562],\n            [27.522537, 48.467119],\n            [26.857824, 48.368211],\n            [26.619337, 48.220726],\n            [26.19745, 48.220881],\n            [25.945941, 47.987149],\n            [25.207743, 47.891056],\n            [24.866317, 47.737526],\n            [24.402056, 47.981878],\n            [23.760958, 47.985598],\n            [23.142236, 48.096341],\n            [22.710531, 47.882194],\n            [22.64082, 48.15024],\n            [22.085608, 48.422264],\n            [22.280842, 48.825392],\n            [22.558138, 49.085738],\n            [22.776419, 49.027395],\n            [22.51845, 49.476774],\n            [23.426508, 50.308506],\n            [23.922757, 50.424881],\n            [24.029986, 50.705407],\n            [23.527071, 51.578454]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Uruguay\",\n        \"SOV_A3\": \"URY\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Uruguay\",\n        \"ADM0_A3\": \"URY\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Uruguay\",\n        \"GU_A3\": \"URY\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Uruguay\",\n        \"SU_A3\": \"URY\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Uruguay\",\n        \"NAME_LONG\": \"Uruguay\",\n        \"BRK_A3\": \"URY\",\n        \"BRK_NAME\": \"Uruguay\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ury.\",\n        \"POSTAL\": \"UY\",\n        \"FORMAL_EN\": \"Oriental Republic of Uruguay\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Uruguay\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Uruguay\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 2,\n        \"MAPCOLOR9\": 2,\n        \"MAPCOLOR13\": 10,\n        \"POP_EST\": 3360148,\n        \"POP_RANK\": 12,\n        \"GDP_MD_EST\": 73250,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"UY\",\n        \"ISO_A2\": \"UY\",\n        \"ISO_A3\": \"URY\",\n        \"ISO_A3_EH\": \"URY\",\n        \"ISO_N3\": \"858\",\n        \"UN_A3\": \"858\",\n        \"WB_A2\": \"UY\",\n        \"WB_A3\": \"URY\",\n        \"WOE_ID\": 23424979,\n        \"WOE_ID_EH\": 23424979,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"URY\",\n        \"ADM0_A3_US\": \"URY\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [-58.427074, -34.952647, -53.209589, -30.109686],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-58.427074, -33.909454],\n            [-58.349611, -33.263189],\n            [-58.132648, -33.040567],\n            [-58.14244, -32.044504],\n            [-57.874937, -31.016556],\n            [-57.625133, -30.216295],\n            [-56.976026, -30.109686],\n            [-55.973245, -30.883076],\n            [-55.60151, -30.853879],\n            [-54.572452, -31.494511],\n            [-53.787952, -32.047243],\n            [-53.209589, -32.727666],\n            [-53.650544, -33.202004],\n            [-53.373662, -33.768378],\n            [-53.806426, -34.396815],\n            [-54.935866, -34.952647],\n            [-55.67409, -34.752659],\n            [-56.215297, -34.859836],\n            [-57.139685, -34.430456],\n            [-57.817861, -34.462547],\n            [-58.427074, -33.909454]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"United States of America\",\n        \"SOV_A3\": \"US1\",\n        \"ADM0_DIF\": 1,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Country\",\n        \"ADMIN\": \"United States of America\",\n        \"ADM0_A3\": \"USA\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"United States of America\",\n        \"GU_A3\": \"USA\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"United States\",\n        \"SU_A3\": \"USA\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"United States of America\",\n        \"NAME_LONG\": \"United States\",\n        \"BRK_A3\": \"USA\",\n        \"BRK_NAME\": \"United States\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"U.S.A.\",\n        \"POSTAL\": \"US\",\n        \"FORMAL_EN\": \"United States of America\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"United States\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"United States of America\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 4,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 1,\n        \"POP_EST\": 326625791,\n        \"POP_RANK\": 17,\n        \"GDP_MD_EST\": 18560000,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"1. Developed region: G7\",\n        \"INCOME_GRP\": \"1. High income: OECD\",\n        \"WIKIPEDIA\": 0,\n        \"FIPS_10_\": \"US\",\n        \"ISO_A2\": \"US\",\n        \"ISO_A3\": \"USA\",\n        \"ISO_A3_EH\": \"USA\",\n        \"ISO_N3\": \"840\",\n        \"UN_A3\": \"840\",\n        \"WB_A2\": \"US\",\n        \"WB_A3\": \"USA\",\n        \"WOE_ID\": 23424977,\n        \"WOE_ID_EH\": 23424977,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"USA\",\n        \"ADM0_A3_US\": \"USA\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"North America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"Northern America\",\n        \"REGION_WB\": \"North America\",\n        \"NAME_LEN\": 24,\n        \"LONG_LEN\": 13,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 5.7\n      },\n      \"bbox\": [-171.791111, 18.91619, -66.96466, 71.357764],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [-122.84, 49],\n              [-120, 49],\n              [-117.03121, 49],\n              [-116.04818, 49],\n              [-113, 49],\n              [-110.05, 49],\n              [-107.05, 49],\n              [-104.04826, 48.99986],\n              [-100.65, 49],\n              [-97.22872, 49.0007],\n              [-95.15907, 49],\n              [-95.15609, 49.38425],\n              [-94.81758, 49.38905],\n              [-94.64, 48.84],\n              [-94.32914, 48.67074],\n              [-93.63087, 48.60926],\n              [-92.61, 48.45],\n              [-91.64, 48.14],\n              [-90.83, 48.27],\n              [-89.6, 48.01],\n              [-89.272917, 48.019808],\n              [-88.378114, 48.302918],\n              [-87.439793, 47.94],\n              [-86.461991, 47.553338],\n              [-85.652363, 47.220219],\n              [-84.87608, 46.900083],\n              [-84.779238, 46.637102],\n              [-84.543749, 46.538684],\n              [-84.6049, 46.4396],\n              [-84.3367, 46.40877],\n              [-84.14212, 46.512226],\n              [-84.091851, 46.275419],\n              [-83.890765, 46.116927],\n              [-83.616131, 46.116927],\n              [-83.469551, 45.994686],\n              [-83.592851, 45.816894],\n              [-82.550925, 45.347517],\n              [-82.337763, 44.44],\n              [-82.137642, 43.571088],\n              [-82.43, 42.98],\n              [-82.9, 42.43],\n              [-83.12, 42.08],\n              [-83.142, 41.975681],\n              [-83.02981, 41.832796],\n              [-82.690089, 41.675105],\n              [-82.439278, 41.675105],\n              [-81.277747, 42.209026],\n              [-80.247448, 42.3662],\n              [-78.939362, 42.863611],\n              [-78.92, 42.965],\n              [-79.01, 43.27],\n              [-79.171674, 43.466339],\n              [-78.72028, 43.625089],\n              [-77.737885, 43.629056],\n              [-76.820034, 43.628784],\n              [-76.5, 44.018459],\n              [-76.375, 44.09631],\n              [-75.31821, 44.81645],\n              [-74.867, 45.00048],\n              [-73.34783, 45.00738],\n              [-71.50506, 45.0082],\n              [-71.405, 45.255],\n              [-71.08482, 45.30524],\n              [-70.66, 45.46],\n              [-70.305, 45.915],\n              [-69.99997, 46.69307],\n              [-69.237216, 47.447781],\n              [-68.905, 47.185],\n              [-68.23444, 47.35486],\n              [-67.79046, 47.06636],\n              [-67.79134, 45.70281],\n              [-67.13741, 45.13753],\n              [-66.96466, 44.8097],\n              [-68.03252, 44.3252],\n              [-69.06, 43.98],\n              [-70.11617, 43.68405],\n              [-70.645476, 43.090238],\n              [-70.81489, 42.8653],\n              [-70.825, 42.335],\n              [-70.495, 41.805],\n              [-70.08, 41.78],\n              [-70.185, 42.145],\n              [-69.88497, 41.92283],\n              [-69.96503, 41.63717],\n              [-70.64, 41.475],\n              [-71.12039, 41.49445],\n              [-71.86, 41.32],\n              [-72.295, 41.27],\n              [-72.87643, 41.22065],\n              [-73.71, 40.931102],\n              [-72.24126, 41.11948],\n              [-71.945, 40.93],\n              [-73.345, 40.63],\n              [-73.982, 40.628],\n              [-73.952325, 40.75075],\n              [-74.25671, 40.47351],\n              [-73.96244, 40.42763],\n              [-74.17838, 39.70926],\n              [-74.90604, 38.93954],\n              [-74.98041, 39.1964],\n              [-75.20002, 39.24845],\n              [-75.52805, 39.4985],\n              [-75.32, 38.96],\n              [-75.071835, 38.782032],\n              [-75.05673, 38.40412],\n              [-75.37747, 38.01551],\n              [-75.94023, 37.21689],\n              [-76.03127, 37.2566],\n              [-75.72205, 37.93705],\n              [-76.23287, 38.319215],\n              [-76.35, 39.15],\n              [-76.542725, 38.717615],\n              [-76.32933, 38.08326],\n              [-76.989998, 38.239992],\n              [-76.30162, 37.917945],\n              [-76.25874, 36.9664],\n              [-75.9718, 36.89726],\n              [-75.86804, 36.55125],\n              [-75.72749, 35.55074],\n              [-76.36318, 34.80854],\n              [-77.397635, 34.51201],\n              [-78.05496, 33.92547],\n              [-78.55435, 33.86133],\n              [-79.06067, 33.49395],\n              [-79.20357, 33.15839],\n              [-80.301325, 32.509355],\n              [-80.86498, 32.0333],\n              [-81.33629, 31.44049],\n              [-81.49042, 30.72999],\n              [-81.31371, 30.03552],\n              [-80.98, 29.18],\n              [-80.535585, 28.47213],\n              [-80.53, 28.04],\n              [-80.056539, 26.88],\n              [-80.088015, 26.205765],\n              [-80.13156, 25.816775],\n              [-80.38103, 25.20616],\n              [-80.68, 25.08],\n              [-81.17213, 25.20126],\n              [-81.33, 25.64],\n              [-81.71, 25.87],\n              [-82.24, 26.73],\n              [-82.70515, 27.49504],\n              [-82.85526, 27.88624],\n              [-82.65, 28.55],\n              [-82.93, 29.1],\n              [-83.70959, 29.93656],\n              [-84.1, 30.09],\n              [-85.10882, 29.63615],\n              [-85.28784, 29.68612],\n              [-85.7731, 30.15261],\n              [-86.4, 30.4],\n              [-87.53036, 30.27433],\n              [-88.41782, 30.3849],\n              [-89.18049, 30.31598],\n              [-89.593831, 30.159994],\n              [-89.413735, 29.89419],\n              [-89.43, 29.48864],\n              [-89.21767, 29.29108],\n              [-89.40823, 29.15961],\n              [-89.77928, 29.30714],\n              [-90.15463, 29.11743],\n              [-90.880225, 29.148535],\n              [-91.626785, 29.677],\n              [-92.49906, 29.5523],\n              [-93.22637, 29.78375],\n              [-93.84842, 29.71363],\n              [-94.69, 29.48],\n              [-95.60026, 28.73863],\n              [-96.59404, 28.30748],\n              [-97.14, 27.83],\n              [-97.37, 27.38],\n              [-97.38, 26.69],\n              [-97.33, 26.21],\n              [-97.14, 25.87],\n              [-97.53, 25.84],\n              [-98.24, 26.06],\n              [-99.02, 26.37],\n              [-99.3, 26.84],\n              [-99.52, 27.54],\n              [-100.11, 28.11],\n              [-100.45584, 28.69612],\n              [-100.9576, 29.38071],\n              [-101.6624, 29.7793],\n              [-102.48, 29.76],\n              [-103.11, 28.97],\n              [-103.94, 29.27],\n              [-104.45697, 29.57196],\n              [-104.70575, 30.12173],\n              [-105.03737, 30.64402],\n              [-105.63159, 31.08383],\n              [-106.1429, 31.39995],\n              [-106.50759, 31.75452],\n              [-108.24, 31.754854],\n              [-108.24194, 31.34222],\n              [-109.035, 31.34194],\n              [-111.02361, 31.33472],\n              [-113.30498, 32.03914],\n              [-114.815, 32.52528],\n              [-114.72139, 32.72083],\n              [-115.99135, 32.61239],\n              [-117.12776, 32.53534],\n              [-117.295938, 33.046225],\n              [-117.944, 33.621236],\n              [-118.410602, 33.740909],\n              [-118.519895, 34.027782],\n              [-119.081, 34.078],\n              [-119.438841, 34.348477],\n              [-120.36778, 34.44711],\n              [-120.62286, 34.60855],\n              [-120.74433, 35.15686],\n              [-121.71457, 36.16153],\n              [-122.54747, 37.55176],\n              [-122.51201, 37.78339],\n              [-122.95319, 38.11371],\n              [-123.7272, 38.95166],\n              [-123.86517, 39.76699],\n              [-124.39807, 40.3132],\n              [-124.17886, 41.14202],\n              [-124.2137, 41.99964],\n              [-124.53284, 42.76599],\n              [-124.14214, 43.70838],\n              [-124.020535, 44.615895],\n              [-123.89893, 45.52341],\n              [-124.079635, 46.86475],\n              [-124.39567, 47.72017],\n              [-124.68721, 48.184433],\n              [-124.566101, 48.379715],\n              [-123.12, 48.04],\n              [-122.58736, 47.096],\n              [-122.34, 47.36],\n              [-122.5, 48.18],\n              [-122.84, 49]\n            ]\n          ],\n          [\n            [\n              [-140.985988, 69.711998],\n              [-140.986, 69.712],\n              [-140.9925, 66.00003],\n              [-140.99778, 60.30639],\n              [-140.013, 60.27682],\n              [-139.039, 60],\n              [-138.34089, 59.56211],\n              [-137.4525, 58.905],\n              [-136.47972, 59.46389],\n              [-135.47583, 59.78778],\n              [-134.945, 59.27056],\n              [-134.27111, 58.86111],\n              [-133.35556, 58.41028],\n              [-132.73042, 57.69289],\n              [-131.70781, 56.55212],\n              [-130.00778, 55.91583],\n              [-129.98, 55.285],\n              [-130.53611, 54.80278],\n              [-130.536109, 54.802754],\n              [-130.53611, 54.802753],\n              [-131.085818, 55.178906],\n              [-131.967211, 55.497776],\n              [-132.250011, 56.369996],\n              [-133.539181, 57.178887],\n              [-134.078063, 58.123068],\n              [-135.038211, 58.187715],\n              [-136.628062, 58.212209],\n              [-137.800006, 58.499995],\n              [-139.867787, 59.537762],\n              [-140.825274, 59.727517],\n              [-142.574444, 60.084447],\n              [-143.958881, 59.99918],\n              [-145.925557, 60.45861],\n              [-147.114374, 60.884656],\n              [-148.224306, 60.672989],\n              [-148.018066, 59.978329],\n              [-148.570823, 59.914173],\n              [-149.727858, 59.705658],\n              [-150.608243, 59.368211],\n              [-151.716393, 59.155821],\n              [-151.859433, 59.744984],\n              [-151.409719, 60.725803],\n              [-150.346941, 61.033588],\n              [-150.621111, 61.284425],\n              [-151.895839, 60.727198],\n              [-152.57833, 60.061657],\n              [-154.019172, 59.350279],\n              [-153.287511, 58.864728],\n              [-154.232492, 58.146374],\n              [-155.307491, 57.727795],\n              [-156.308335, 57.422774],\n              [-156.556097, 56.979985],\n              [-158.117217, 56.463608],\n              [-158.433321, 55.994154],\n              [-159.603327, 55.566686],\n              [-160.28972, 55.643581],\n              [-161.223048, 55.364735],\n              [-162.237766, 55.024187],\n              [-163.069447, 54.689737],\n              [-164.785569, 54.404173],\n              [-164.942226, 54.572225],\n              [-163.84834, 55.039431],\n              [-162.870001, 55.348043],\n              [-161.804175, 55.894986],\n              [-160.563605, 56.008055],\n              [-160.07056, 56.418055],\n              [-158.684443, 57.016675],\n              [-158.461097, 57.216921],\n              [-157.72277, 57.570001],\n              [-157.550274, 58.328326],\n              [-157.041675, 58.918885],\n              [-158.194731, 58.615802],\n              [-158.517218, 58.787781],\n              [-159.058606, 58.424186],\n              [-159.711667, 58.93139],\n              [-159.981289, 58.572549],\n              [-160.355271, 59.071123],\n              [-161.355003, 58.670838],\n              [-161.968894, 58.671665],\n              [-162.054987, 59.266925],\n              [-161.874171, 59.633621],\n              [-162.518059, 59.989724],\n              [-163.818341, 59.798056],\n              [-164.662218, 60.267484],\n              [-165.346388, 60.507496],\n              [-165.350832, 61.073895],\n              [-166.121379, 61.500019],\n              [-165.734452, 62.074997],\n              [-164.919179, 62.633076],\n              [-164.562508, 63.146378],\n              [-163.753332, 63.219449],\n              [-163.067224, 63.059459],\n              [-162.260555, 63.541936],\n              [-161.53445, 63.455817],\n              [-160.772507, 63.766108],\n              [-160.958335, 64.222799],\n              [-161.518068, 64.402788],\n              [-160.777778, 64.788604],\n              [-161.391926, 64.777235],\n              [-162.45305, 64.559445],\n              [-162.757786, 64.338605],\n              [-163.546394, 64.55916],\n              [-164.96083, 64.446945],\n              [-166.425288, 64.686672],\n              [-166.845004, 65.088896],\n              [-168.11056, 65.669997],\n              [-166.705271, 66.088318],\n              [-164.47471, 66.57666],\n              [-163.652512, 66.57666],\n              [-163.788602, 66.077207],\n              [-161.677774, 66.11612],\n              [-162.489715, 66.735565],\n              [-163.719717, 67.116395],\n              [-164.430991, 67.616338],\n              [-165.390287, 68.042772],\n              [-166.764441, 68.358877],\n              [-166.204707, 68.883031],\n              [-164.430811, 68.915535],\n              [-163.168614, 69.371115],\n              [-162.930566, 69.858062],\n              [-161.908897, 70.33333],\n              [-160.934797, 70.44769],\n              [-159.039176, 70.891642],\n              [-158.119723, 70.824721],\n              [-156.580825, 71.357764],\n              [-155.06779, 71.147776],\n              [-154.344165, 70.696409],\n              [-153.900006, 70.889989],\n              [-152.210006, 70.829992],\n              [-152.270002, 70.600006],\n              [-150.739992, 70.430017],\n              [-149.720003, 70.53001],\n              [-147.613362, 70.214035],\n              [-145.68999, 70.12001],\n              [-144.920011, 69.989992],\n              [-143.589446, 70.152514],\n              [-142.07251, 69.851938],\n              [-140.985988, 69.711998],\n              [-140.985988, 69.711998]\n            ]\n          ],\n          [\n            [\n              [-155.54211, 19.08348],\n              [-155.68817, 18.91619],\n              [-155.93665, 19.05939],\n              [-155.90806, 19.33888],\n              [-156.07347, 19.70294],\n              [-156.02368, 19.81422],\n              [-155.85008, 19.97729],\n              [-155.91907, 20.17395],\n              [-155.86108, 20.26721],\n              [-155.78505, 20.2487],\n              [-155.40214, 20.07975],\n              [-155.22452, 19.99302],\n              [-155.06226, 19.8591],\n              [-154.80741, 19.50871],\n              [-154.83147, 19.45328],\n              [-155.22217, 19.23972],\n              [-155.54211, 19.08348]\n            ]\n          ],\n          [\n            [\n              [-156.07926, 20.64397],\n              [-156.41445, 20.57241],\n              [-156.58673, 20.783],\n              [-156.70167, 20.8643],\n              [-156.71055, 20.92676],\n              [-156.61258, 21.01249],\n              [-156.25711, 20.91745],\n              [-155.99566, 20.76404],\n              [-156.07926, 20.64397]\n            ]\n          ],\n          [\n            [\n              [-156.75824, 21.17684],\n              [-156.78933, 21.06873],\n              [-157.32521, 21.09777],\n              [-157.25027, 21.21958],\n              [-156.75824, 21.17684]\n            ]\n          ],\n          [\n            [\n              [-157.65283, 21.32217],\n              [-157.70703, 21.26442],\n              [-157.7786, 21.27729],\n              [-158.12667, 21.31244],\n              [-158.2538, 21.53919],\n              [-158.29265, 21.57912],\n              [-158.0252, 21.71696],\n              [-157.94161, 21.65272],\n              [-157.65283, 21.32217]\n            ]\n          ],\n          [\n            [\n              [-159.34512, 21.982],\n              [-159.46372, 21.88299],\n              [-159.80051, 22.06533],\n              [-159.74877, 22.1382],\n              [-159.5962, 22.23618],\n              [-159.36569, 22.21494],\n              [-159.34512, 21.982]\n            ]\n          ],\n          [\n            [\n              [-153.006314, 57.115842],\n              [-154.00509, 56.734677],\n              [-154.516403, 56.992749],\n              [-154.670993, 57.461196],\n              [-153.76278, 57.816575],\n              [-153.228729, 57.968968],\n              [-152.564791, 57.901427],\n              [-152.141147, 57.591059],\n              [-153.006314, 57.115842]\n            ]\n          ],\n          [\n            [\n              [-165.579164, 59.909987],\n              [-166.19277, 59.754441],\n              [-166.848337, 59.941406],\n              [-167.455277, 60.213069],\n              [-166.467792, 60.38417],\n              [-165.67443, 60.293607],\n              [-165.579164, 59.909987]\n            ]\n          ],\n          [\n            [\n              [-171.731657, 63.782515],\n              [-171.114434, 63.592191],\n              [-170.491112, 63.694975],\n              [-169.682505, 63.431116],\n              [-168.689439, 63.297506],\n              [-168.771941, 63.188598],\n              [-169.52944, 62.976931],\n              [-170.290556, 63.194438],\n              [-170.671386, 63.375822],\n              [-171.553063, 63.317789],\n              [-171.791111, 63.405846],\n              [-171.731657, 63.782515]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Uzbekistan\",\n        \"SOV_A3\": \"UZB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Uzbekistan\",\n        \"ADM0_A3\": \"UZB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Uzbekistan\",\n        \"GU_A3\": \"UZB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Uzbekistan\",\n        \"SU_A3\": \"UZB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Uzbekistan\",\n        \"NAME_LONG\": \"Uzbekistan\",\n        \"BRK_A3\": \"UZB\",\n        \"BRK_NAME\": \"Uzbekistan\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Uzb.\",\n        \"POSTAL\": \"UZ\",\n        \"FORMAL_EN\": \"Republic of Uzbekistan\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Uzbekistan\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Uzbekistan\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 29748859,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 202300,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 1989,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"6. Developing region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"UZ\",\n        \"ISO_A2\": \"UZ\",\n        \"ISO_A3\": \"UZB\",\n        \"ISO_A3_EH\": \"UZB\",\n        \"ISO_N3\": \"860\",\n        \"UN_A3\": \"860\",\n        \"WB_A2\": \"UZ\",\n        \"WB_A3\": \"UZB\",\n        \"WOE_ID\": 23424980,\n        \"WOE_ID_EH\": 23424980,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"UZB\",\n        \"ADM0_A3_US\": \"UZB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Central Asia\",\n        \"REGION_WB\": \"Europe & Central Asia\",\n        \"NAME_LEN\": 10,\n        \"LONG_LEN\": 10,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": 5,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [55.928917, 37.144994, 73.055417, 45.586804],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [67.83, 37.144994],\n            [67.075782, 37.356144],\n            [66.518607, 37.362784],\n            [66.54615, 37.974685],\n            [65.215999, 38.402695],\n            [64.170223, 38.892407],\n            [63.518015, 39.363257],\n            [62.37426, 40.053886],\n            [61.882714, 41.084857],\n            [61.547179, 41.26637],\n            [60.465953, 41.220327],\n            [60.083341, 41.425146],\n            [59.976422, 42.223082],\n            [58.629011, 42.751551],\n            [57.78653, 42.170553],\n            [56.932215, 41.826026],\n            [57.096391, 41.32231],\n            [55.968191, 41.308642],\n            [55.928917, 44.995858],\n            [58.503127, 45.586804],\n            [58.689989, 45.500014],\n            [60.239972, 44.784037],\n            [61.05832, 44.405817],\n            [62.0133, 43.504477],\n            [63.185787, 43.650075],\n            [64.900824, 43.728081],\n            [66.098012, 42.99766],\n            [66.023392, 41.994646],\n            [66.510649, 41.987644],\n            [66.714047, 41.168444],\n            [67.985856, 41.135991],\n            [68.259896, 40.662325],\n            [68.632483, 40.668681],\n            [69.070027, 41.384244],\n            [70.388965, 42.081308],\n            [70.962315, 42.266154],\n            [71.259248, 42.167711],\n            [70.420022, 41.519998],\n            [71.157859, 41.143587],\n            [71.870115, 41.3929],\n            [73.055417, 40.866033],\n            [71.774875, 40.145844],\n            [71.014198, 40.244366],\n            [70.601407, 40.218527],\n            [70.45816, 40.496495],\n            [70.666622, 40.960213],\n            [69.329495, 40.727824],\n            [69.011633, 40.086158],\n            [68.536416, 39.533453],\n            [67.701429, 39.580478],\n            [67.44222, 39.140144],\n            [68.176025, 38.901553],\n            [68.392033, 38.157025],\n            [67.83, 37.144994]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Venezuela\",\n        \"SOV_A3\": \"VEN\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Venezuela\",\n        \"ADM0_A3\": \"VEN\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Venezuela\",\n        \"GU_A3\": \"VEN\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Venezuela\",\n        \"SU_A3\": \"VEN\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Venezuela\",\n        \"NAME_LONG\": \"Venezuela\",\n        \"BRK_A3\": \"VEN\",\n        \"BRK_NAME\": \"Venezuela\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Ven.\",\n        \"POSTAL\": \"VE\",\n        \"FORMAL_EN\": \"Bolivarian Republic of Venezuela\",\n        \"FORMAL_FR\": \"República Bolivariana de Venezuela\",\n        \"NAME_CIAWF\": \"Venezuela\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Venezuela, RB\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 1,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 31304016,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 468600,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"VE\",\n        \"ISO_A2\": \"VE\",\n        \"ISO_A3\": \"VEN\",\n        \"ISO_A3_EH\": \"VEN\",\n        \"ISO_N3\": \"862\",\n        \"UN_A3\": \"862\",\n        \"WB_A2\": \"VE\",\n        \"WB_A3\": \"VEN\",\n        \"WOE_ID\": 23424982,\n        \"WOE_ID_EH\": 23424982,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"VEN\",\n        \"ADM0_A3_US\": \"VEN\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"South America\",\n        \"REGION_UN\": \"Americas\",\n        \"SUBREGION\": \"South America\",\n        \"REGION_WB\": \"Latin America & Caribbean\",\n        \"NAME_LEN\": 9,\n        \"LONG_LEN\": 9,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 7.5\n      },\n      \"bbox\": [-73.304952, 0.724452, -59.758285, 12.162307],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [-60.733574, 5.200277],\n            [-60.601179, 4.918098],\n            [-60.966893, 4.536468],\n            [-62.08543, 4.162124],\n            [-62.804533, 4.006965],\n            [-63.093198, 3.770571],\n            [-63.888343, 4.02053],\n            [-64.628659, 4.148481],\n            [-64.816064, 4.056445],\n            [-64.368494, 3.79721],\n            [-64.408828, 3.126786],\n            [-64.269999, 2.497006],\n            [-63.422867, 2.411068],\n            [-63.368788, 2.2009],\n            [-64.083085, 1.916369],\n            [-64.199306, 1.492855],\n            [-64.611012, 1.328731],\n            [-65.354713, 1.095282],\n            [-65.548267, 0.789254],\n            [-66.325765, 0.724452],\n            [-66.876326, 1.253361],\n            [-67.181294, 2.250638],\n            [-67.447092, 2.600281],\n            [-67.809938, 2.820655],\n            [-67.303173, 3.318454],\n            [-67.337564, 3.542342],\n            [-67.621836, 3.839482],\n            [-67.823012, 4.503937],\n            [-67.744697, 5.221129],\n            [-67.521532, 5.55687],\n            [-67.34144, 6.095468],\n            [-67.695087, 6.267318],\n            [-68.265052, 6.153268],\n            [-68.985319, 6.206805],\n            [-69.38948, 6.099861],\n            [-70.093313, 6.960376],\n            [-70.674234, 7.087785],\n            [-71.960176, 6.991615],\n            [-72.198352, 7.340431],\n            [-72.444487, 7.423785],\n            [-72.479679, 7.632506],\n            [-72.360901, 8.002638],\n            [-72.439862, 8.405275],\n            [-72.660495, 8.625288],\n            [-72.78873, 9.085027],\n            [-73.304952, 9.152],\n            [-73.027604, 9.73677],\n            [-72.905286, 10.450344],\n            [-72.614658, 10.821975],\n            [-72.227575, 11.108702],\n            [-71.973922, 11.608672],\n            [-71.331584, 11.776284],\n            [-71.360006, 11.539994],\n            [-71.94705, 11.423282],\n            [-71.620868, 10.96946],\n            [-71.633064, 10.446494],\n            [-72.074174, 9.865651],\n            [-71.695644, 9.072263],\n            [-71.264559, 9.137195],\n            [-71.039999, 9.859993],\n            [-71.350084, 10.211935],\n            [-71.400623, 10.968969],\n            [-70.155299, 11.375482],\n            [-70.293843, 11.846822],\n            [-69.943245, 12.162307],\n            [-69.5843, 11.459611],\n            [-68.882999, 11.443385],\n            [-68.233271, 10.885744],\n            [-68.194127, 10.554653],\n            [-67.296249, 10.545868],\n            [-66.227864, 10.648627],\n            [-65.655238, 10.200799],\n            [-64.890452, 10.077215],\n            [-64.329479, 10.389599],\n            [-64.318007, 10.641418],\n            [-63.079322, 10.701724],\n            [-61.880946, 10.715625],\n            [-62.730119, 10.420269],\n            [-62.388512, 9.948204],\n            [-61.588767, 9.873067],\n            [-60.830597, 9.38134],\n            [-60.671252, 8.580174],\n            [-60.150096, 8.602757],\n            [-59.758285, 8.367035],\n            [-60.550588, 7.779603],\n            [-60.637973, 7.415],\n            [-60.295668, 7.043911],\n            [-60.543999, 6.856584],\n            [-61.159336, 6.696077],\n            [-61.139415, 6.234297],\n            [-61.410303, 5.959068],\n            [-60.733574, 5.200277]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"Vietnam\",\n        \"SOV_A3\": \"VNM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Vietnam\",\n        \"ADM0_A3\": \"VNM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Vietnam\",\n        \"GU_A3\": \"VNM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Vietnam\",\n        \"SU_A3\": \"VNM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Vietnam\",\n        \"NAME_LONG\": \"Vietnam\",\n        \"BRK_A3\": \"VNM\",\n        \"BRK_NAME\": \"Vietnam\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Viet.\",\n        \"POSTAL\": \"VN\",\n        \"FORMAL_EN\": \"Socialist Republic of Vietnam\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Vietnam\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Vietnam\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 6,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 4,\n        \"POP_EST\": 96160163,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 594900,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"VM\",\n        \"ISO_A2\": \"VN\",\n        \"ISO_A3\": \"VNM\",\n        \"ISO_A3_EH\": \"VNM\",\n        \"ISO_N3\": \"704\",\n        \"UN_A3\": \"704\",\n        \"WB_A2\": \"VN\",\n        \"WB_A3\": \"VNM\",\n        \"WOE_ID\": 23424984,\n        \"WOE_ID_EH\": 23424984,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"VNM\",\n        \"ADM0_A3_US\": \"VNM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"South-Eastern Asia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": 2,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 2,\n        \"MAX_LABEL\": 7\n      },\n      \"bbox\": [102.170436, 8.59976, 109.33527, 23.352063],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [102.170436, 22.464753],\n            [102.706992, 22.708795],\n            [103.504515, 22.703757],\n            [104.476858, 22.81915],\n            [105.329209, 23.352063],\n            [105.811247, 22.976892],\n            [106.725403, 22.794268],\n            [106.567273, 22.218205],\n            [107.04342, 21.811899],\n            [108.05018, 21.55238],\n            [106.715068, 20.696851],\n            [105.881682, 19.75205],\n            [105.662006, 19.058165],\n            [106.426817, 18.004121],\n            [107.361954, 16.697457],\n            [108.269495, 16.079742],\n            [108.877107, 15.276691],\n            [109.33527, 13.426028],\n            [109.200136, 11.666859],\n            [108.36613, 11.008321],\n            [107.220929, 10.364484],\n            [106.405113, 9.53084],\n            [105.158264, 8.59976],\n            [104.795185, 9.241038],\n            [105.076202, 9.918491],\n            [104.334335, 10.486544],\n            [105.199915, 10.88931],\n            [106.24967, 10.961812],\n            [105.810524, 11.567615],\n            [107.491403, 12.337206],\n            [107.614548, 13.535531],\n            [107.382727, 14.202441],\n            [107.564525, 15.202173],\n            [107.312706, 15.908538],\n            [106.556008, 16.604284],\n            [105.925762, 17.485315],\n            [105.094598, 18.666975],\n            [103.896532, 19.265181],\n            [104.183388, 19.624668],\n            [104.822574, 19.886642],\n            [104.435, 20.758733],\n            [103.203861, 20.766562],\n            [102.754896, 21.675137],\n            [102.170436, 22.464753]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 4,\n        \"SOVEREIGNT\": \"Vanuatu\",\n        \"SOV_A3\": \"VUT\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Vanuatu\",\n        \"ADM0_A3\": \"VUT\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Vanuatu\",\n        \"GU_A3\": \"VUT\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Vanuatu\",\n        \"SU_A3\": \"VUT\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Vanuatu\",\n        \"NAME_LONG\": \"Vanuatu\",\n        \"BRK_A3\": \"VUT\",\n        \"BRK_NAME\": \"Vanuatu\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Van.\",\n        \"POSTAL\": \"VU\",\n        \"FORMAL_EN\": \"Republic of Vanuatu\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Vanuatu\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Vanuatu\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 6,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 7,\n        \"MAPCOLOR13\": 3,\n        \"POP_EST\": 282814,\n        \"POP_RANK\": 10,\n        \"GDP_MD_EST\": 723,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2009,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"NH\",\n        \"ISO_A2\": \"VU\",\n        \"ISO_A3\": \"VUT\",\n        \"ISO_A3_EH\": \"VUT\",\n        \"ISO_N3\": \"548\",\n        \"UN_A3\": \"548\",\n        \"WB_A2\": \"VU\",\n        \"WB_A3\": \"VUT\",\n        \"WOE_ID\": 23424907,\n        \"WOE_ID_EH\": 23424907,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"VUT\",\n        \"ADM0_A3_US\": \"VUT\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Oceania\",\n        \"REGION_UN\": \"Oceania\",\n        \"SUBREGION\": \"Melanesia\",\n        \"REGION_WB\": \"East Asia & Pacific\",\n        \"NAME_LEN\": 7,\n        \"LONG_LEN\": 7,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": 2,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 4,\n        \"MAX_LABEL\": 9\n      },\n      \"bbox\": [166.629137, -16.59785, 167.844877, -14.626497],\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [167.844877, -16.466333],\n              [167.515181, -16.59785],\n              [167.180008, -16.159995],\n              [167.216801, -15.891846],\n              [167.844877, -16.466333]\n            ]\n          ],\n          [\n            [\n              [167.107712, -14.93392],\n              [167.270028, -15.740021],\n              [167.001207, -15.614602],\n              [166.793158, -15.668811],\n              [166.649859, -15.392704],\n              [166.629137, -14.626497],\n              [167.107712, -14.93392]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Yemen\",\n        \"SOV_A3\": \"YEM\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Yemen\",\n        \"ADM0_A3\": \"YEM\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Yemen\",\n        \"GU_A3\": \"YEM\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Yemen\",\n        \"SU_A3\": \"YEM\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Yemen\",\n        \"NAME_LONG\": \"Yemen\",\n        \"BRK_A3\": \"YEM\",\n        \"BRK_NAME\": \"Yemen\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Yem.\",\n        \"POSTAL\": \"YE\",\n        \"FORMAL_EN\": \"Republic of Yemen\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Yemen\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Yemen, Rep.\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 11,\n        \"POP_EST\": 28036829,\n        \"POP_RANK\": 15,\n        \"GDP_MD_EST\": 73450,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2004,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"YM\",\n        \"ISO_A2\": \"YE\",\n        \"ISO_A3\": \"YEM\",\n        \"ISO_A3_EH\": \"YEM\",\n        \"ISO_N3\": \"887\",\n        \"UN_A3\": \"887\",\n        \"WB_A2\": \"RY\",\n        \"WB_A3\": \"YEM\",\n        \"WOE_ID\": 23425002,\n        \"WOE_ID_EH\": 23425002,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"YEM\",\n        \"ADM0_A3_US\": \"YEM\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Asia\",\n        \"REGION_UN\": \"Asia\",\n        \"SUBREGION\": \"Western Asia\",\n        \"REGION_WB\": \"Middle East & North Africa\",\n        \"NAME_LEN\": 5,\n        \"LONG_LEN\": 5,\n        \"ABBREV_LEN\": 4,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [42.604873, 12.58595, 53.108573, 19.000003],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [52.00001, 19.000003],\n            [52.782184, 17.349742],\n            [53.108573, 16.651051],\n            [52.385206, 16.382411],\n            [52.191729, 15.938433],\n            [52.168165, 15.59742],\n            [51.172515, 15.17525],\n            [49.574576, 14.708767],\n            [48.679231, 14.003202],\n            [48.238947, 13.94809],\n            [47.938914, 14.007233],\n            [47.354454, 13.59222],\n            [46.717076, 13.399699],\n            [45.877593, 13.347764],\n            [45.62505, 13.290946],\n            [45.406459, 13.026905],\n            [45.144356, 12.953938],\n            [44.989533, 12.699587],\n            [44.494576, 12.721653],\n            [44.175113, 12.58595],\n            [43.482959, 12.6368],\n            [43.222871, 13.22095],\n            [43.251448, 13.767584],\n            [43.087944, 14.06263],\n            [42.892245, 14.802249],\n            [42.604873, 15.213335],\n            [42.805015, 15.261963],\n            [42.702438, 15.718886],\n            [42.823671, 15.911742],\n            [42.779332, 16.347891],\n            [43.218375, 16.66689],\n            [43.115798, 17.08844],\n            [43.380794, 17.579987],\n            [43.791519, 17.319977],\n            [44.062613, 17.410359],\n            [45.216651, 17.433329],\n            [45.399999, 17.333335],\n            [46.366659, 17.233315],\n            [46.749994, 17.283338],\n            [47.000005, 16.949999],\n            [47.466695, 17.116682],\n            [48.183344, 18.166669],\n            [49.116672, 18.616668],\n            [52.00001, 19.000003]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 2,\n        \"SOVEREIGNT\": \"South Africa\",\n        \"SOV_A3\": \"ZAF\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"South Africa\",\n        \"ADM0_A3\": \"ZAF\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"South Africa\",\n        \"GU_A3\": \"ZAF\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"South Africa\",\n        \"SU_A3\": \"ZAF\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"South Africa\",\n        \"NAME_LONG\": \"South Africa\",\n        \"BRK_A3\": \"ZAF\",\n        \"BRK_NAME\": \"South Africa\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"S.Af.\",\n        \"POSTAL\": \"ZA\",\n        \"FORMAL_EN\": \"Republic of South Africa\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"South Africa\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"South Africa\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 2,\n        \"MAPCOLOR8\": 3,\n        \"MAPCOLOR9\": 4,\n        \"MAPCOLOR13\": 2,\n        \"POP_EST\": 54841552,\n        \"POP_RANK\": 16,\n        \"GDP_MD_EST\": 739100,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2001,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"3. Upper middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"SF\",\n        \"ISO_A2\": \"ZA\",\n        \"ISO_A3\": \"ZAF\",\n        \"ISO_A3_EH\": \"ZAF\",\n        \"ISO_N3\": \"710\",\n        \"UN_A3\": \"710\",\n        \"WB_A2\": \"ZA\",\n        \"WB_A3\": \"ZAF\",\n        \"WOE_ID\": 23424942,\n        \"WOE_ID_EH\": 23424942,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ZAF\",\n        \"ADM0_A3_US\": \"ZAF\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Southern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 12,\n        \"LONG_LEN\": 12,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 1.7,\n        \"MAX_LABEL\": 6.7\n      },\n      \"bbox\": [16.344977, -34.819166, 32.83012, -22.091313],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [19.895768, -24.76779],\n            [20.165726, -24.917962],\n            [20.758609, -25.868136],\n            [20.66647, -26.477453],\n            [20.889609, -26.828543],\n            [21.605896, -26.726534],\n            [22.105969, -26.280256],\n            [22.579532, -25.979448],\n            [22.824271, -25.500459],\n            [23.312097, -25.26869],\n            [23.73357, -25.390129],\n            [24.211267, -25.670216],\n            [25.025171, -25.71967],\n            [25.664666, -25.486816],\n            [25.765849, -25.174845],\n            [25.941652, -24.696373],\n            [26.485753, -24.616327],\n            [26.786407, -24.240691],\n            [27.11941, -23.574323],\n            [28.017236, -22.827754],\n            [29.432188, -22.091313],\n            [29.839037, -22.102216],\n            [30.322883, -22.271612],\n            [30.659865, -22.151567],\n            [31.191409, -22.25151],\n            [31.670398, -23.658969],\n            [31.930589, -24.369417],\n            [31.752408, -25.484284],\n            [31.837778, -25.843332],\n            [31.333158, -25.660191],\n            [31.04408, -25.731452],\n            [30.949667, -26.022649],\n            [30.676609, -26.398078],\n            [30.685962, -26.743845],\n            [31.282773, -27.285879],\n            [31.86806, -27.177927],\n            [32.071665, -26.73382],\n            [32.83012, -26.742192],\n            [32.580265, -27.470158],\n            [32.462133, -28.301011],\n            [32.203389, -28.752405],\n            [31.521001, -29.257387],\n            [31.325561, -29.401978],\n            [30.901763, -29.909957],\n            [30.622813, -30.423776],\n            [30.055716, -31.140269],\n            [28.925553, -32.172041],\n            [28.219756, -32.771953],\n            [27.464608, -33.226964],\n            [26.419452, -33.61495],\n            [25.909664, -33.66704],\n            [25.780628, -33.944646],\n            [25.172862, -33.796851],\n            [24.677853, -33.987176],\n            [23.594043, -33.794474],\n            [22.988189, -33.916431],\n            [22.574157, -33.864083],\n            [21.542799, -34.258839],\n            [20.689053, -34.417175],\n            [20.071261, -34.795137],\n            [19.616405, -34.819166],\n            [19.193278, -34.462599],\n            [18.855315, -34.444306],\n            [18.424643, -33.997873],\n            [18.377411, -34.136521],\n            [18.244499, -33.867752],\n            [18.25008, -33.281431],\n            [17.92519, -32.611291],\n            [18.24791, -32.429131],\n            [18.221762, -31.661633],\n            [17.566918, -30.725721],\n            [17.064416, -29.878641],\n            [17.062918, -29.875954],\n            [16.344977, -28.576705],\n            [16.824017, -28.082162],\n            [17.218929, -28.355943],\n            [17.387497, -28.783514],\n            [17.836152, -28.856378],\n            [18.464899, -29.045462],\n            [19.002127, -28.972443],\n            [19.894734, -28.461105],\n            [19.895768, -24.76779]\n          ],\n          [\n            [28.978263, -28.955597],\n            [28.5417, -28.647502],\n            [28.074338, -28.851469],\n            [27.532511, -29.242711],\n            [26.999262, -29.875954],\n            [27.749397, -30.645106],\n            [28.107205, -30.545732],\n            [28.291069, -30.226217],\n            [28.8484, -30.070051],\n            [29.018415, -29.743766],\n            [29.325166, -29.257387],\n            [28.978263, -28.955597]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Zambia\",\n        \"SOV_A3\": \"ZMB\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Zambia\",\n        \"ADM0_A3\": \"ZMB\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Zambia\",\n        \"GU_A3\": \"ZMB\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Zambia\",\n        \"SU_A3\": \"ZMB\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Zambia\",\n        \"NAME_LONG\": \"Zambia\",\n        \"BRK_A3\": \"ZMB\",\n        \"BRK_NAME\": \"Zambia\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Zambia\",\n        \"POSTAL\": \"ZM\",\n        \"FORMAL_EN\": \"Republic of Zambia\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Zambia\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Zambia\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 5,\n        \"MAPCOLOR8\": 8,\n        \"MAPCOLOR9\": 5,\n        \"MAPCOLOR13\": 13,\n        \"POP_EST\": 15972000,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 65170,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2010,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"7. Least developed region\",\n        \"INCOME_GRP\": \"4. Lower middle income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ZA\",\n        \"ISO_A2\": \"ZM\",\n        \"ISO_A3\": \"ZMB\",\n        \"ISO_A3_EH\": \"ZMB\",\n        \"ISO_N3\": \"894\",\n        \"UN_A3\": \"894\",\n        \"WB_A2\": \"ZM\",\n        \"WB_A3\": \"ZMB\",\n        \"WOE_ID\": 23425003,\n        \"WOE_ID_EH\": 23425003,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ZMB\",\n        \"ADM0_A3_US\": \"ZMB\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 6,\n        \"LONG_LEN\": 6,\n        \"ABBREV_LEN\": 6,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [21.887843, -17.961229, 33.485688, -8.238257],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [23.215048, -17.523116],\n            [22.562478, -16.898451],\n            [21.887843, -16.08031],\n            [21.933886, -12.898437],\n            [24.016137, -12.911046],\n            [23.930922, -12.565848],\n            [24.079905, -12.191297],\n            [23.904154, -11.722282],\n            [24.017894, -11.237298],\n            [23.912215, -10.926826],\n            [24.257155, -10.951993],\n            [24.314516, -11.262826],\n            [24.78317, -11.238694],\n            [25.418118, -11.330936],\n            [25.75231, -11.784965],\n            [26.553088, -11.92444],\n            [27.16442, -11.608748],\n            [27.388799, -12.132747],\n            [28.155109, -12.272481],\n            [28.523562, -12.698604],\n            [28.934286, -13.248958],\n            [29.699614, -13.257227],\n            [29.616001, -12.178895],\n            [29.341548, -12.360744],\n            [28.642417, -11.971569],\n            [28.372253, -11.793647],\n            [28.49607, -10.789884],\n            [28.673682, -9.605925],\n            [28.449871, -9.164918],\n            [28.734867, -8.526559],\n            [29.002912, -8.407032],\n            [30.346086, -8.238257],\n            [30.74001, -8.340006],\n            [31.157751, -8.594579],\n            [31.556348, -8.762049],\n            [32.191865, -8.930359],\n            [32.759375, -9.230599],\n            [33.231388, -9.676722],\n            [33.485688, -10.525559],\n            [33.31531, -10.79655],\n            [33.114289, -11.607198],\n            [33.306422, -12.435778],\n            [32.991764, -12.783871],\n            [32.688165, -13.712858],\n            [33.214025, -13.97186],\n            [30.179481, -14.796099],\n            [30.274256, -15.507787],\n            [29.516834, -15.644678],\n            [28.947463, -16.043051],\n            [28.825869, -16.389749],\n            [28.467906, -16.4684],\n            [27.598243, -17.290831],\n            [27.044427, -17.938026],\n            [26.706773, -17.961229],\n            [26.381935, -17.846042],\n            [25.264226, -17.73654],\n            [25.084443, -17.661816],\n            [25.07695, -17.578823],\n            [24.682349, -17.353411],\n            [24.033862, -17.295843],\n            [23.215048, -17.523116]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"scalerank\": 1,\n        \"featurecla\": \"Admin-0 country\",\n        \"LABELRANK\": 3,\n        \"SOVEREIGNT\": \"Zimbabwe\",\n        \"SOV_A3\": \"ZWE\",\n        \"ADM0_DIF\": 0,\n        \"LEVEL\": 2,\n        \"TYPE\": \"Sovereign country\",\n        \"ADMIN\": \"Zimbabwe\",\n        \"ADM0_A3\": \"ZWE\",\n        \"GEOU_DIF\": 0,\n        \"GEOUNIT\": \"Zimbabwe\",\n        \"GU_A3\": \"ZWE\",\n        \"SU_DIF\": 0,\n        \"SUBUNIT\": \"Zimbabwe\",\n        \"SU_A3\": \"ZWE\",\n        \"BRK_DIFF\": 0,\n        \"NAME\": \"Zimbabwe\",\n        \"NAME_LONG\": \"Zimbabwe\",\n        \"BRK_A3\": \"ZWE\",\n        \"BRK_NAME\": \"Zimbabwe\",\n        \"BRK_GROUP\": null,\n        \"ABBREV\": \"Zimb.\",\n        \"POSTAL\": \"ZW\",\n        \"FORMAL_EN\": \"Republic of Zimbabwe\",\n        \"FORMAL_FR\": null,\n        \"NAME_CIAWF\": \"Zimbabwe\",\n        \"NOTE_ADM0\": null,\n        \"NOTE_BRK\": null,\n        \"NAME_SORT\": \"Zimbabwe\",\n        \"NAME_ALT\": null,\n        \"MAPCOLOR7\": 1,\n        \"MAPCOLOR8\": 5,\n        \"MAPCOLOR9\": 3,\n        \"MAPCOLOR13\": 9,\n        \"POP_EST\": 13805084,\n        \"POP_RANK\": 14,\n        \"GDP_MD_EST\": 28330,\n        \"POP_YEAR\": 2017,\n        \"LASTCENSUS\": 2002,\n        \"GDP_YEAR\": 2016,\n        \"ECONOMY\": \"5. Emerging region: G20\",\n        \"INCOME_GRP\": \"5. Low income\",\n        \"WIKIPEDIA\": -99,\n        \"FIPS_10_\": \"ZI\",\n        \"ISO_A2\": \"ZW\",\n        \"ISO_A3\": \"ZWE\",\n        \"ISO_A3_EH\": \"ZWE\",\n        \"ISO_N3\": \"716\",\n        \"UN_A3\": \"716\",\n        \"WB_A2\": \"ZW\",\n        \"WB_A3\": \"ZWE\",\n        \"WOE_ID\": 23425004,\n        \"WOE_ID_EH\": 23425004,\n        \"WOE_NOTE\": \"Exact WOE match as country\",\n        \"ADM0_A3_IS\": \"ZWE\",\n        \"ADM0_A3_US\": \"ZWE\",\n        \"ADM0_A3_UN\": -99,\n        \"ADM0_A3_WB\": -99,\n        \"CONTINENT\": \"Africa\",\n        \"REGION_UN\": \"Africa\",\n        \"SUBREGION\": \"Eastern Africa\",\n        \"REGION_WB\": \"Sub-Saharan Africa\",\n        \"NAME_LEN\": 8,\n        \"LONG_LEN\": 8,\n        \"ABBREV_LEN\": 5,\n        \"TINY\": -99,\n        \"HOMEPART\": 1,\n        \"MIN_ZOOM\": 0,\n        \"MIN_LABEL\": 3,\n        \"MAX_LABEL\": 8\n      },\n      \"bbox\": [25.264226, -22.271612, 32.849861, -15.507787],\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [29.432188, -22.091313],\n            [28.794656, -21.639454],\n            [28.02137, -21.485975],\n            [27.727228, -20.851802],\n            [27.724747, -20.499059],\n            [27.296505, -20.39152],\n            [26.164791, -19.293086],\n            [25.850391, -18.714413],\n            [25.649163, -18.536026],\n            [25.264226, -17.73654],\n            [26.381935, -17.846042],\n            [26.706773, -17.961229],\n            [27.044427, -17.938026],\n            [27.598243, -17.290831],\n            [28.467906, -16.4684],\n            [28.825869, -16.389749],\n            [28.947463, -16.043051],\n            [29.516834, -15.644678],\n            [30.274256, -15.507787],\n            [30.338955, -15.880839],\n            [31.173064, -15.860944],\n            [31.636498, -16.07199],\n            [31.852041, -16.319417],\n            [32.328239, -16.392074],\n            [32.847639, -16.713398],\n            [32.849861, -17.979057],\n            [32.654886, -18.67209],\n            [32.611994, -19.419383],\n            [32.772708, -19.715592],\n            [32.659743, -20.30429],\n            [32.508693, -20.395292],\n            [32.244988, -21.116489],\n            [31.191409, -22.25151],\n            [30.659865, -22.151567],\n            [30.322883, -22.271612],\n            [29.839037, -22.102216],\n            [29.432188, -22.091313]\n          ]\n        ]\n      }\n    }\n  ],\n  \"bbox\": [-180, -90, 180, 83.64513]\n}"
  },
  {
    "path": "public/robots.txt",
    "content": "User-agent: *\nAllow: /\nDisallow: /dashboard/\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"extends\": [\n    \"github>nuxt/renovate-config-nuxt\"\n  ]\n}\n"
  },
  {
    "path": "scripts/build-colo.js",
    "content": "import { writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nasync function main() {\n  const locations = await fetch('https://raw.githubusercontent.com/Netrvin/cloudflare-colo-list/refs/heads/main/locations.json')\n  if (!locations.ok) {\n    throw new Error('Failed to fetch locations')\n  }\n  const colos = await locations.json()\n  writeFileSync(join(import.meta.dirname, '../public/colos.json'), JSON.stringify(colos.reduce((acc, c) => {\n    acc[c.iata] = {\n      lat: c.lat,\n      lon: c.lon,\n    }\n    return acc\n  }, {}), 'utf8'))\n}\n\nmain().catch(console.error)\n"
  },
  {
    "path": "scripts/build-map.js",
    "content": "import { writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { WorldMapSimplestTopoJSON } from '@unovis/ts/maps.js'\n\nwriteFileSync(join(import.meta.dirname, '../public/world.json'), JSON.stringify(WorldMapSimplestTopoJSON), 'utf8')\n"
  },
  {
    "path": "scripts/build-sphere.js",
    "content": "import { Buffer } from 'node:buffer'\nimport { writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { createOctahedronSphere } from '../layers/dashboard/app/composables/globe/sphere.ts'\n\n/**\n * Binary format:\n * [header: 12 bytes] 3x Uint32 (positions_bytes, texcoords_bytes, indices_bytes)\n * [body] Float32Array | Float32Array | Uint16Array\n */\nfunction main() {\n  const { position, texcoord, indices } = createOctahedronSphere(6)\n\n  const posBytes = position.byteLength\n  const texBytes = texcoord.byteLength\n  const idxBytes = indices.byteLength\n\n  const header = new Uint32Array([posBytes, texBytes, idxBytes])\n  const totalSize = header.byteLength + posBytes + texBytes + idxBytes\n\n  const buffer = new ArrayBuffer(totalSize)\n  const view = new Uint8Array(buffer)\n\n  let offset = 0\n  view.set(new Uint8Array(header.buffer), offset)\n  offset += header.byteLength\n  view.set(new Uint8Array(position.buffer), offset)\n  offset += posBytes\n  view.set(new Uint8Array(texcoord.buffer), offset)\n  offset += texBytes\n  view.set(new Uint8Array(indices.buffer), offset)\n\n  const outPath = join(import.meta.dirname, '../public/sphere.bin')\n  writeFileSync(outPath, Buffer.from(buffer))\n\n  const sizeKB = (totalSize / 1024).toFixed(1)\n  console.log(`sphere.bin: ${position.length / 3} vertices, ${indices.length / 3} triangles, ${sizeKB} KB`)\n}\n\nmain()\n"
  },
  {
    "path": "scripts/build-testimonials.js",
    "content": "import { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\n// Tweet IDs to fetch\nconst TWEET_IDS = [\n  '1990813013247492308', // @xmok_\n  '1876990358250246628', // @ianhowells\n  '1931199560489251052', // @indie_maker_fox\n  '1944683470627741966', // @allentown521\n  '1925250262870237555', // @Mokkapps\n  '1795169172873413116', // @GitHubGPT\n  '1953003326317920422', // @ossalternative\n  '1809763345320624271', // @f_sugar\n  '1833125667568804284', // @bitdoze\n  '1817702576629985685', // @HiTw93\n  '1846465874389356916', // @luoleiorg\n  '1796478331522781460', // @LuoSays\n  '1905626254931624281', // @DeBill_me\n  '1930301401323975179', // @lakphy\n  '1961700003459862799', // @wey_gu\n  '1794746047136411723', // @NoPeople404\n  '1988243083558035901', // @yeahwong\n  '1808150012058390969', // @m1ssuo\n  '1893594908147270073', // @hellokaton\n  '1857623546606080350', // @TooooooBug\n]\n\nconst API_BASE = 'https://react-tweet.vercel.app/api/tweet'\n\nasync function fetchTweet(id) {\n  const res = await fetch(`${API_BASE}/${id}`)\n  if (!res.ok) {\n    console.warn(`Failed to fetch tweet ${id}: ${res.status}`)\n    return null\n  }\n\n  const json = await res.json()\n  const tweet = json.data\n\n  if (!tweet) {\n    console.warn(`No data for tweet ${id}`)\n    return null\n  }\n\n  // Clean up tweet text: remove t.co links and extra whitespace\n  const cleanContent = tweet.text\n    .replace(/https:\\/\\/t\\.co\\/\\w+/g, '')\n    .replace(/\\s+/g, ' ')\n    .trim()\n\n  return {\n    id: tweet.id_str,\n    name: tweet.user.name,\n    username: tweet.user.screen_name,\n    content: cleanContent,\n    url: `https://x.com/${tweet.user.screen_name}/status/${tweet.id_str}`,\n    verified: tweet.user.is_blue_verified || false,\n    date: tweet.created_at,\n  }\n}\n\nasync function main() {\n  console.log('Fetching testimonials from Twitter...')\n\n  // Shuffle TWEET_IDS for random order\n  const shuffledIds = [...TWEET_IDS].sort(() => Math.random() - 0.5)\n\n  const results = await Promise.all(shuffledIds.map(fetchTweet))\n  const testimonials = results.filter(Boolean)\n\n  if (testimonials.length === 0) {\n    console.error('No testimonials fetched!')\n    process.exit(1)\n  }\n\n  // Ensure data directory exists\n  const dataDir = join(import.meta.dirname, '../app/data')\n  mkdirSync(dataDir, { recursive: true })\n\n  const outputPath = join(dataDir, 'testimonials.json')\n  writeFileSync(outputPath, JSON.stringify(testimonials, null, 2), 'utf8')\n\n  console.log(`✓ Generated ${testimonials.length} testimonials to ${outputPath}`)\n}\n\nmain().catch((err) => {\n  console.error('Failed to build testimonials:', err)\n  process.exit(1)\n})\n"
  },
  {
    "path": "server/api/backup.post.ts",
    "content": "defineRouteMeta({\n  openAPI: {\n    description: 'Manually trigger a backup to R2',\n    security: [{ bearerAuth: [] }],\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const env = event.context.cloudflare.env\n\n  if (!env.R2) {\n    throw createError({\n      status: 500,\n      message: 'R2 binding not configured',\n    })\n  }\n\n  await backupKVToR2(env, true)\n\n  return {\n    success: true,\n    message: 'Backup completed successfully',\n  }\n})\n"
  },
  {
    "path": "server/api/link/ai.get.ts",
    "content": "import { destr } from 'destr'\nimport { z } from 'zod'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Generate a slug using AI based on the URL',\n    security: [{ bearerAuth: [] }],\n    parameters: [\n      {\n        name: 'url',\n        in: 'query',\n        required: true,\n        schema: { type: 'string', format: 'uri' },\n        description: 'The URL to generate a slug for',\n      },\n    ],\n  },\n})\n\ninterface AiChatResponse {\n  response?: string\n  choices?: { message?: { content?: string } }[]\n}\n\nexport default eventHandler(async (event) => {\n  const url = (await getValidatedQuery(event, z.object({\n    url: z.string().url(),\n  }).parse)).url\n  const { cloudflare } = event.context\n  const { AI } = cloudflare.env\n\n  if (!AI) {\n    throw createError({ status: 501, statusText: 'AI not enabled' })\n  }\n\n  const { aiPrompt, aiModel } = useRuntimeConfig(event)\n  const { slugRegex } = useAppConfig()\n\n  const markdown = await fetchPageMarkdown(event, url, AI)\n  const userContent = markdown\n    ? `URL: ${url}\\n\\nPage content:\\n${markdown}`\n    : url\n\n  const messages = [\n    { role: 'system', content: aiPrompt.replace('{slugRegex}', slugRegex.toString()) },\n\n    { role: 'user', content: 'https://www.cloudflare.com/' },\n    { role: 'assistant', content: '{\"slug\": \"cloudflare\"}' },\n\n    { role: 'user', content: 'https://github.com/nuxt/' },\n    { role: 'assistant', content: '{\"slug\": \"nuxt\"}' },\n\n    { role: 'user', content: 'https://sink.cool/' },\n    { role: 'assistant', content: '{\"slug\": \"sink-cool\"}' },\n\n    { role: 'user', content: 'https://github.com/miantiao-me/sink' },\n    { role: 'assistant', content: '{\"slug\": \"sink\"}' },\n\n    { role: 'user', content: userContent },\n  ]\n\n  const response = await AI.run(aiModel as keyof AiModels, { messages }) as AiChatResponse\n\n  let content = response.response ?? response.choices?.[0]?.message?.content ?? ''\n  // Strip markdown code block wrapper (e.g. ```json\\n{...}\\n```)\n  const codeBlockMatch = content.match(/```\\w*\\n([^`]+)```/)\n  if (codeBlockMatch?.[1]) {\n    content = codeBlockMatch[1].trim()\n  }\n\n  return destr(content)\n})\n"
  },
  {
    "path": "server/api/link/create.post.ts",
    "content": "import { LinkSchema } from '#shared/schemas/link'\n\ndefineRouteMeta({\n  openAPI: {\n    $global: {\n      components: {\n        securitySchemes: {\n          bearerAuth: {\n            type: 'http',\n            scheme: 'bearer',\n            description: 'Use NUXT_SITE_TOKEN as the bearer token',\n          },\n        },\n      },\n    },\n    description: 'Create a new short link',\n    security: [{ bearerAuth: [] }],\n    requestBody: {\n      required: true,\n      content: {\n        'application/json': {\n          schema: {\n            type: 'object',\n            required: ['url'],\n            properties: {\n              url: { type: 'string', description: 'The target URL' },\n              slug: { type: 'string', description: 'Custom slug (auto-generated if not provided)' },\n              comment: { type: 'string', description: 'Optional comment' },\n              expiration: { type: 'integer', description: 'Expiration timestamp (unix seconds)' },\n              title: { type: 'string', description: 'Custom title for link preview' },\n              description: { type: 'string', description: 'Custom description for link preview' },\n              image: { type: 'string', description: 'Custom image for link preview' },\n              apple: { type: 'string', description: 'Apple App Store redirect URL' },\n              google: { type: 'string', description: 'Google Play Store redirect URL' },\n              cloaking: { type: 'boolean', description: 'Enable link cloaking (mask destination URL)' },\n              redirectWithQuery: { type: 'boolean', description: 'Append query parameters to destination URL' },\n              password: { type: 'string', description: 'Password protection for the link' },\n              unsafe: { type: 'boolean', description: 'Mark link as unsafe, showing a warning page before redirect' },\n            },\n          },\n        },\n      },\n    },\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const link = await readValidatedBody(event, LinkSchema.parse)\n\n  link.slug = normalizeSlug(event, link.slug)\n\n  // Auto-detect unsafe URL via Safe Browsing DoH\n  if (link.unsafe === undefined) {\n    const safe = await isSafeUrl(event, link.url)\n    if (!safe) {\n      link.unsafe = true\n    }\n  }\n\n  const existingLink = await getLink(event, link.slug)\n  if (existingLink) {\n    throw createError({\n      status: 409,\n      statusText: 'Link already exists',\n    })\n  }\n\n  await putLink(event, link)\n  setResponseStatus(event, 201)\n  const shortLink = buildShortLink(event, link.slug)\n  return { link, shortLink }\n})\n"
  },
  {
    "path": "server/api/link/delete.post.ts",
    "content": "import { LinkSchema } from '#shared/schemas/link'\nimport { z } from 'zod'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Delete a short link',\n    security: [{ bearerAuth: [] }],\n    requestBody: {\n      required: true,\n      content: {\n        'application/json': {\n          schema: {\n            type: 'object',\n            required: ['slug'],\n            properties: {\n              slug: { type: 'string', description: 'The slug of the link to delete' },\n            },\n          },\n        },\n      },\n    },\n  },\n})\n\nconst DeleteSchema = z.object({\n  slug: LinkSchema.shape.slug.removeDefault().min(1),\n})\n\nexport default eventHandler(async (event) => {\n  const { previewMode } = useRuntimeConfig(event).public\n  if (previewMode) {\n    throw createError({\n      status: 403,\n      statusText: 'Preview mode cannot delete links.',\n    })\n  }\n\n  const { slug } = await readValidatedBody(event, DeleteSchema.parse)\n  await deleteLink(event, slug)\n})\n"
  },
  {
    "path": "server/api/link/edit.put.ts",
    "content": "import type { z } from 'zod'\nimport { LinkSchema } from '#shared/schemas/link'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Edit an existing short link',\n    security: [{ bearerAuth: [] }],\n    requestBody: {\n      required: true,\n      content: {\n        'application/json': {\n          schema: {\n            type: 'object',\n            required: ['url', 'slug'],\n            properties: {\n              url: { type: 'string', description: 'The target URL' },\n              slug: { type: 'string', description: 'The slug of the link to edit' },\n              comment: { type: 'string', description: 'Optional comment' },\n              expiration: { type: 'integer', description: 'Expiration timestamp (unix seconds)' },\n              title: { type: 'string', description: 'Custom title for link preview' },\n              description: { type: 'string', description: 'Custom description for link preview' },\n              image: { type: 'string', description: 'Custom image for link preview' },\n              apple: { type: 'string', description: 'Apple App Store redirect URL' },\n              google: { type: 'string', description: 'Google Play Store redirect URL' },\n              cloaking: { type: 'boolean', description: 'Enable link cloaking (mask destination URL)' },\n              redirectWithQuery: { type: 'boolean', description: 'Append query parameters to destination URL' },\n              password: { type: 'string', description: 'Password protection for the link' },\n              unsafe: { type: 'boolean', description: 'Mark link as unsafe, showing a warning page before redirect' },\n            },\n          },\n        },\n      },\n    },\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const { previewMode } = useRuntimeConfig(event).public\n  if (previewMode) {\n    throw createError({\n      status: 403,\n      statusText: 'Preview mode cannot edit links.',\n    })\n  }\n  const link = await readValidatedBody(event, LinkSchema.parse)\n\n  const existingLink: z.infer<typeof LinkSchema> | null = await getLink(event, link.slug)\n  if (!existingLink) {\n    throw createError({\n      status: 404,\n      statusText: 'Link not found',\n    })\n  }\n\n  // Auto-detect unsafe URL when URL changes and unsafe not explicitly set\n  if (link.unsafe === undefined && link.url !== existingLink.url) {\n    const safe = await isSafeUrl(event, link.url)\n    if (!safe) {\n      link.unsafe = true\n    }\n  }\n\n  const newLink = {\n    ...existingLink,\n    ...link,\n    id: existingLink.id,\n    createdAt: existingLink.createdAt,\n    updatedAt: Math.floor(Date.now() / 1000),\n  }\n  const optionalFields = [\n    'comment',\n    'title',\n    'description',\n    'image',\n    'apple',\n    'google',\n    'cloaking',\n    'redirectWithQuery',\n    'password',\n    'expiration',\n    'unsafe',\n  ] as const\n  for (const field of optionalFields) {\n    if (link[field] === undefined) {\n      delete newLink[field]\n    }\n  }\n  await putLink(event, newLink)\n  setResponseStatus(event, 201)\n  const shortLink = buildShortLink(event, newLink.slug)\n  return { link: newLink, shortLink }\n})\n"
  },
  {
    "path": "server/api/link/export.get.ts",
    "content": "import type { ExportData, Link } from '#shared/schemas/link'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Export all links with pagination',\n    security: [{ bearerAuth: [] }],\n    parameters: [\n      {\n        name: 'cursor',\n        in: 'query',\n        required: false,\n        schema: { type: 'string' },\n        description: 'Pagination cursor from previous response',\n      },\n    ],\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const query = getQuery(event)\n  const cursor = query.cursor as string | undefined\n  const kvBatchLimit = useRuntimeConfig(event).public.kvBatchLimit as string\n  const limit = +kvBatchLimit\n\n  const list = await listLinks(event, { limit, cursor })\n  const links = list.links.filter((link): link is Link => link !== null)\n\n  const exportData: ExportData = {\n    version: '1.0',\n    exportedAt: new Date().toISOString(),\n    count: links.length,\n    links,\n    cursor: list.cursor,\n    list_complete: list.list_complete,\n  }\n\n  setResponseHeader(event, 'Content-Type', 'application/json')\n  setResponseHeader(event, 'Cache-Control', 'no-store')\n\n  return exportData\n})\n"
  },
  {
    "path": "server/api/link/import.post.ts",
    "content": "import type { ImportResult } from '#shared/schemas/import'\nimport { ImportDataSchema } from '#shared/schemas/import'\nimport { nanoid } from '#shared/schemas/link'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Import links from exported data',\n    security: [{ bearerAuth: [] }],\n    requestBody: {\n      required: true,\n      content: {\n        'application/json': {\n          schema: {\n            type: 'object',\n            required: ['version', 'links'],\n            properties: {\n              version: { type: 'string', description: 'Export format version' },\n              exportedAt: { type: 'string', description: 'Export timestamp (ISO 8601)' },\n              count: { type: 'integer', description: 'Number of links in export' },\n              links: {\n                type: 'array',\n                description: 'Array of links to import',\n                items: {\n                  type: 'object',\n                  required: ['url', 'slug'],\n                  properties: {\n                    id: { type: 'string', description: 'Link ID (auto-generated if not provided)' },\n                    url: { type: 'string', description: 'The target URL' },\n                    slug: { type: 'string', description: 'The slug for the short link' },\n                    comment: { type: 'string', description: 'Optional comment' },\n                    createdAt: { type: 'integer', description: 'Creation timestamp (unix seconds)' },\n                    updatedAt: { type: 'integer', description: 'Last update timestamp (unix seconds)' },\n                    expiration: { type: 'integer', description: 'Expiration timestamp (unix seconds)' },\n                    title: { type: 'string', description: 'Custom title for link preview' },\n                    description: { type: 'string', description: 'Custom description for link preview' },\n                    image: { type: 'string', description: 'Custom image for link preview' },\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    },\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const kvBatchLimit = useRuntimeConfig(event).public.kvBatchLimit as string\n  const maxLinks = Math.floor(+kvBatchLimit / 2)\n\n  const importData = await readValidatedBody(event, ImportDataSchema.parse)\n\n  if (importData.links.length > maxLinks) {\n    throw createError({\n      status: 400,\n      statusText: `Too many links. Maximum ${maxLinks} links per request.`,\n    })\n  }\n\n  const result: ImportResult = {\n    success: 0,\n    skipped: 0,\n    failed: 0,\n    successItems: [],\n    skippedItems: [],\n    failedItems: [],\n  }\n\n  for (let i = 0; i < importData.links.length; i++) {\n    const linkData = importData.links[i]\n\n    if (!linkData) {\n      result.failed++\n      result.failedItems.push({\n        index: i,\n        slug: '',\n        url: '',\n        reason: 'Missing link data',\n      })\n      continue\n    }\n\n    try {\n      const slug = normalizeSlug(event, linkData.slug)\n      const existingLink = await getLink(event, slug)\n\n      if (existingLink) {\n        result.skippedItems.push({ index: i, slug, url: linkData.url })\n        result.skipped++\n        continue\n      }\n\n      const now = Math.floor(Date.now() / 1000)\n      const link = {\n        id: linkData.id || nanoid(10)(),\n        url: linkData.url,\n        slug,\n        comment: linkData.comment,\n        createdAt: linkData.createdAt || now,\n        updatedAt: linkData.updatedAt || now,\n        expiration: linkData.expiration,\n        title: linkData.title,\n        description: linkData.description,\n        image: linkData.image,\n      }\n\n      await putLink(event, link)\n      result.successItems.push({ index: i, slug, url: linkData.url })\n      result.success++\n    }\n    catch (error) {\n      result.failed++\n      result.failedItems.push({\n        index: i,\n        slug: linkData.slug,\n        url: linkData.url,\n        reason: error instanceof Error ? error.message : 'Unknown error',\n      })\n    }\n  }\n\n  setResponseHeader(event, 'Cache-Control', 'no-store')\n\n  return result\n})\n"
  },
  {
    "path": "server/api/link/list.get.ts",
    "content": "import { z } from 'zod'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'List all short links with pagination',\n    security: [{ bearerAuth: [] }],\n    parameters: [\n      {\n        name: 'limit',\n        in: 'query',\n        required: false,\n        schema: { type: 'integer', default: 20, maximum: 1024 },\n        description: 'Maximum number of links to return',\n      },\n      {\n        name: 'cursor',\n        in: 'query',\n        required: false,\n        schema: { type: 'string' },\n        description: 'Pagination cursor from previous response',\n      },\n    ],\n  },\n})\n\nconst ListQuerySchema = z.object({\n  limit: z.coerce.number().max(1024).default(20),\n  cursor: z.string().trim().max(1024).optional(),\n})\n\nexport default eventHandler(async (event) => {\n  const { limit, cursor } = await getValidatedQuery(event, ListQuerySchema.parse)\n\n  const list = await listLinks(event, { limit, cursor })\n  return list\n})\n"
  },
  {
    "path": "server/api/link/query.get.ts",
    "content": "import { z } from 'zod'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Query a short link by slug',\n    security: [{ bearerAuth: [] }],\n    parameters: [\n      {\n        name: 'slug',\n        in: 'query',\n        required: true,\n        schema: { type: 'string' },\n        description: 'The slug of the link to query',\n      },\n    ],\n  },\n})\n\nconst QueryParamsSchema = z.object({\n  slug: z.string().trim().min(1).max(2048),\n})\n\nexport default eventHandler(async (event) => {\n  const { slug } = await getValidatedQuery(event, QueryParamsSchema.parse)\n\n  const { link, metadata } = await getLinkWithMetadata(event, slug)\n  if (link) {\n    return {\n      ...metadata,\n      ...link,\n    }\n  }\n\n  throw createError({\n    status: 404,\n    statusText: 'Not Found',\n  })\n})\n"
  },
  {
    "path": "server/api/link/search.get.ts",
    "content": "defineRouteMeta({\n  openAPI: {\n    description: 'Search all links (returns slug, url, comment for each link)',\n    security: [{ bearerAuth: [] }],\n  },\n})\n\ninterface Link {\n  slug: string\n  url: string\n  comment?: string\n}\n\ninterface LinkMetadata {\n  url?: string\n  comment?: string\n  expiration?: number\n}\n\ninterface LinkData {\n  url: string\n  comment?: string\n}\n\nexport default eventHandler(async (event) => {\n  const { cloudflare } = event.context\n  const { KV } = cloudflare.env\n  const list: Link[] = []\n  let finalCursor: string | undefined\n\n  try {\n    while (true) {\n      const result = await KV.list({\n        prefix: `link:`,\n        limit: 1000,\n        cursor: finalCursor,\n      }) as { keys: Array<{ name: string, metadata?: LinkMetadata }>, list_complete: boolean, cursor?: string }\n\n      finalCursor = result.cursor\n\n      if (Array.isArray(result.keys)) {\n        for (const key of result.keys) {\n          try {\n            if (key.metadata?.url) {\n              list.push({\n                slug: key.name.replace('link:', ''),\n                url: key.metadata.url,\n                comment: key.metadata.comment,\n              })\n            }\n            else {\n              // Forward compatible with links without metadata\n              const { metadata, value: link } = await KV.getWithMetadata(key.name, { type: 'json' }) as { metadata: LinkMetadata | null, value: LinkData | null }\n              if (link) {\n                list.push({\n                  slug: key.name.replace('link:', ''),\n                  url: link.url,\n                  comment: link.comment,\n                })\n                await KV.put(key.name, JSON.stringify(link), {\n                  expiration: metadata?.expiration,\n                  metadata: {\n                    ...(metadata ?? {}),\n                    url: withoutQuery(link.url),\n                    comment: link.comment,\n                  },\n                })\n              }\n            }\n          }\n          catch (err) {\n            console.error(`Error processing key ${key.name}:`, err)\n            continue // Skip this key and continue with the next one\n          }\n        }\n      }\n\n      if (!result.keys || result.list_complete) {\n        break\n      }\n    }\n    return list\n  }\n  catch (err) {\n    console.error('Error fetching link list:', err)\n    throw createError({\n      status: 500,\n      statusText: 'Failed to fetch link list',\n    })\n  }\n})\n"
  },
  {
    "path": "server/api/link/upsert.post.ts",
    "content": "import { LinkSchema } from '#shared/schemas/link'\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Create or update a short link (upsert)',\n    security: [{ bearerAuth: [] }],\n    requestBody: {\n      required: true,\n      content: {\n        'application/json': {\n          schema: {\n            type: 'object',\n            required: ['url'],\n            properties: {\n              url: { type: 'string', description: 'The target URL' },\n              slug: { type: 'string', description: 'Custom slug (auto-generated if not provided)' },\n              comment: { type: 'string', description: 'Optional comment' },\n              expiration: { type: 'integer', description: 'Expiration timestamp (unix seconds)' },\n              title: { type: 'string', description: 'Custom title for link preview' },\n              description: { type: 'string', description: 'Custom description for link preview' },\n              image: { type: 'string', description: 'Custom image for link preview' },\n              apple: { type: 'string', description: 'Apple App Store redirect URL' },\n              google: { type: 'string', description: 'Google Play Store redirect URL' },\n              unsafe: { type: 'boolean', description: 'Mark link as unsafe, showing a warning page before redirect' },\n            },\n          },\n        },\n      },\n    },\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const link = await readValidatedBody(event, LinkSchema.parse)\n\n  link.slug = normalizeSlug(event, link.slug)\n\n  // Auto-detect unsafe URL via Safe Browsing DoH\n  if (link.unsafe === undefined) {\n    const safe = await isSafeUrl(event, link.url)\n    if (!safe) {\n      link.unsafe = true\n    }\n  }\n\n  const existingLink = await getLink(event, link.slug)\n  if (existingLink) {\n    const shortLink = buildShortLink(event, link.slug)\n    return { link: existingLink, shortLink, status: 'existing' }\n  }\n\n  await putLink(event, link)\n  setResponseStatus(event, 201)\n  const shortLink = buildShortLink(event, link.slug)\n  return { link, shortLink, status: 'created' }\n})\n"
  },
  {
    "path": "server/api/location.get.ts",
    "content": "defineRouteMeta({\n  openAPI: {\n    description: 'Get the location of the user',\n    responses: {\n      200: {\n        description: 'The location of the user',\n      },\n    },\n  },\n})\n\nexport default eventHandler((event) => {\n  const { cloudflare } = event.context\n  const { request: { cf } } = cloudflare\n  return {\n    latitude: cf?.latitude,\n    longitude: cf?.longitude,\n  }\n})\n"
  },
  {
    "path": "server/api/logs/events.get.ts",
    "content": "import type { H3Event } from 'h3'\nimport { QuerySchema } from '#shared/schemas/query'\nimport { date2unix } from '@/utils/time'\n\nconst { select } = SqlBricks\n\nfunction query2sql(query: Query, event: H3Event): string {\n  const filter = query2filter(query)\n  const { dataset } = useRuntimeConfig(event)\n  const sql = select(`*`).from(dataset).where(filter).orderBy('timestamp DESC')\n  appendTimeFilter(sql, query)\n  return sql.toString()\n}\n\ninterface WAEEvents {\n  [key: string]: string\n}\n\nfunction events2logs(events: WAEEvents[]) {\n  return events.map((event) => {\n    const blobs = Array.from({ length: Object.keys(blobsMap).length }).fill(0).reduce<string[]>((_, _c, i) => {\n      _.push(event[`blob${i + 1}`] ?? '')\n      return _\n    }, [])\n    const doubles = Array.from({ length: Object.keys(doublesMap).length }).fill(0).reduce<number[]>((_, _c, i) => {\n      _.push(+(event[`double${i + 1}`] ?? 0))\n      return _\n    }, [])\n    return {\n      ...blobs2logs(blobs),\n      ...doubles2logs(doubles),\n      ip: undefined,\n      id: crypto.randomUUID(),\n      timestamp: date2unix(new Date(`${event.timestamp}Z`)),\n    }\n  })\n}\n\nexport default eventHandler(async (event) => {\n  const query = await getValidatedQuery(event, QuerySchema.parse)\n  const sql = query2sql(query, event)\n\n  const logs = await useWAE(event, sql) as { data: WAEEvents[] }\n  return events2logs(logs?.data || [])\n})\n"
  },
  {
    "path": "server/api/logs/locations.get.ts",
    "content": "import type { H3Event } from 'h3'\nimport { QuerySchema } from '#shared/schemas/query'\n\nconst { select, and, notEq } = SqlBricks\n\nfunction query2sql(query: Query, event: H3Event): string {\n  const filter = query2filter(query)\n  const { dataset } = useRuntimeConfig(event)\n  // Use SUM(_sample_interval) instead of count() to account for sampling\n  const sql = select(`blob8 as ${blobsMap.blob8},double1 as ${doublesMap.double1},double2 as ${doublesMap.double2},SUM(_sample_interval) as count`)\n    .from(dataset)\n    .where(and([notEq('double1', 0), notEq('double2', 0), filter]))\n    .groupBy([blobsMap.blob8, doublesMap.double1, doublesMap.double2])\n  appendTimeFilter(sql, query)\n  return sql.toString()\n}\n\nexport default eventHandler(async (event) => {\n  const query = await getValidatedQuery(event, QuerySchema.parse)\n  const sql = query2sql(query, event)\n\n  return useWAE(event, sql)\n})\n"
  },
  {
    "path": "server/api/stats/counters.get.ts",
    "content": "import type { H3Event } from 'h3'\nimport { QuerySchema } from '#shared/schemas/query'\n\nconst { select } = SqlBricks\n\nfunction query2sql(query: Query, event: H3Event): string {\n  const filter = query2filter(query)\n  const { dataset } = useRuntimeConfig(event)\n  // Weighted distinct count: COUNT(DISTINCT col) * SUM(_sample_interval) / COUNT() ≈ actual distinct count\n  const weightedDistinct = (col: string) => `ROUND(COUNT(DISTINCT ${col}) * SUM(_sample_interval) / COUNT())`\n  const columns = [\n    query.id && 'index1 as id',\n    'SUM(_sample_interval) as visits',\n    `${weightedDistinct(logsMap.ip!)} as visitors`,\n    `${weightedDistinct(logsMap.referer!)} as referers`,\n  ].filter(Boolean).join(', ')\n  const sql = select(columns).from(dataset).where(filter)\n  if (query.id)\n    sql.groupBy('index1')\n  appendTimeFilter(sql, query)\n  return sql.toString()\n}\n\nexport default eventHandler(async (event) => {\n  const query = await getValidatedQuery(event, QuerySchema.parse)\n  const sql = query2sql(query, event)\n  return useWAE(event, sql)\n})\n"
  },
  {
    "path": "server/api/stats/heatmap.get.ts",
    "content": "import type { H3Event } from 'h3'\nimport { QuerySchema } from '#shared/schemas/query'\nimport { z } from 'zod'\n\nconst { select } = SqlBricks\n\nconst HeatmapQuerySchema = QuerySchema.extend({\n  clientTimezone: z.string()\n    .regex(/^[A-Z_]+(?:\\/[A-Z_-]+)*$/i)\n    .max(64)\n    .default('Etc/UTC'),\n})\n\nfunction query2sql(query: z.infer<typeof HeatmapQuerySchema>, event: H3Event): string {\n  const filter = query2filter(query)\n  const { dataset } = useRuntimeConfig(event)\n  const timezone = getSafeTimezone(query.clientTimezone)\n  const tzTimestamp = `toDateTime(toUnixTimestamp(timestamp), '${timezone}')`\n  const sql = select(`toDayOfWeek(${tzTimestamp}) as weekday, toHour(${tzTimestamp}) as hour, SUM(_sample_interval) as visits, COUNT(DISTINCT ${logsMap.ip}) as visitors`).from(dataset).where(filter).groupBy('weekday', 'hour').orderBy('weekday', 'hour')\n  appendTimeFilter(sql, query)\n  return sql.toString()\n}\n\nexport default eventHandler(async (event) => {\n  const query = await getValidatedQuery(event, HeatmapQuerySchema.parse)\n  const sql = query2sql(query, event)\n  return useWAE(event, sql)\n})\n"
  },
  {
    "path": "server/api/stats/metrics.get.ts",
    "content": "import type { BlobsMap, DoublesMap } from '#server/utils/access-log'\nimport type { H3Event } from 'h3'\nimport { QuerySchema } from '#shared/schemas/query'\nimport { z } from 'zod'\n\nconst { select } = SqlBricks\n\ntype MetricType = BlobsMap[keyof BlobsMap] | DoublesMap[keyof DoublesMap]\nconst validMetricTypes = [...Object.values(blobsMap), ...Object.values(doublesMap)] as [MetricType, ...MetricType[]]\n\nconst MetricsQuerySchema = QuerySchema.extend({\n  type: z.enum(validMetricTypes),\n})\n\nfunction query2sql(query: z.infer<typeof MetricsQuerySchema>, event: H3Event): string {\n  const filter = query2filter(query)\n  const { dataset } = useRuntimeConfig(event)\n\n  const sql = select(`${logsMap[query.type]} as name, SUM(_sample_interval) as count`)\n    .from(dataset)\n    .where(filter)\n    .groupBy('name')\n    .orderBy('count DESC')\n\n  appendTimeFilter(sql, query)\n\n  const limit = Math.max(0, Math.floor(query.limit))\n  return `${sql.toString()} LIMIT ${limit}`\n}\n\nexport default eventHandler(async (event) => {\n  const query = await getValidatedQuery(event, MetricsQuerySchema.parse)\n  const sql = query2sql(query, event)\n  return useWAE(event, sql)\n})\n"
  },
  {
    "path": "server/api/stats/views.get.ts",
    "content": "import type { H3Event } from 'h3'\nimport { QuerySchema } from '#shared/schemas/query'\nimport { z } from 'zod'\n\nconst { select } = SqlBricks\n\nconst unitMap: { [x: string]: string } = {\n  minute: '%Y-%m-%d %H:%i',\n  hour: '%Y-%m-%d %H',\n  day: '%Y-%m-%d',\n}\n\nconst ViewsQuerySchema = QuerySchema.extend({\n  unit: z.enum(['minute', 'hour', 'day']),\n  clientTimezone: z.string()\n    .regex(/^[A-Z_]+(?:\\/[A-Z_-]+)*$/i)\n    .max(64)\n    .default('Etc/UTC'),\n})\n\nfunction query2sql(query: z.infer<typeof ViewsQuerySchema>, event: H3Event): string {\n  const filter = query2filter(query)\n  const { dataset } = useRuntimeConfig(event)\n  const timezone = getSafeTimezone(query.clientTimezone)\n  const sql = select(`formatDateTime(timestamp, '${unitMap[query.unit]}', '${timezone}') as time, SUM(_sample_interval) as visits, COUNT(DISTINCT ${logsMap.ip}) as visitors`).from(dataset).where(filter).groupBy('time').orderBy('time')\n  appendTimeFilter(sql, query)\n  return sql.toString()\n}\n\nexport default eventHandler(async (event) => {\n  const query = await getValidatedQuery(event, ViewsQuerySchema.parse)\n  const sql = query2sql(query, event)\n  return useWAE(event, sql)\n})\n"
  },
  {
    "path": "server/api/upload/image.post.ts",
    "content": "import { LinkSchema, nanoid } from '#shared/schemas/link'\nimport { IMAGE_ALLOWED_TYPES, IMAGE_MAX_SIZE } from '@/utils/image'\n\nconst slugValidator = LinkSchema.shape.slug\n\ndefineRouteMeta({\n  openAPI: {\n    description: 'Upload an image to R2 storage',\n    requestBody: {\n      required: true,\n      content: {\n        'multipart/form-data': {\n          schema: {\n            type: 'object',\n            required: ['file', 'slug'],\n            properties: {\n              file: { type: 'string', format: 'binary' },\n              slug: { type: 'string' },\n            },\n          },\n        },\n      },\n    },\n  },\n})\n\nexport default eventHandler(async (event) => {\n  const { cloudflare } = event.context\n  const { R2 } = cloudflare.env\n\n  const formData = await readFormData(event)\n  const file = formData.get('file') as File | null\n  const slug = formData.get('slug') as string | null\n\n  if (!file) {\n    throw createError({ status: 400, statusText: 'File is required' })\n  }\n\n  if (!slug) {\n    throw createError({ status: 400, statusText: 'Slug is required' })\n  }\n\n  const slugResult = slugValidator.safeParse(slug)\n  if (!slugResult.success) {\n    throw createError({ status: 400, statusText: 'Invalid slug format' })\n  }\n\n  if (!IMAGE_ALLOWED_TYPES.includes(file.type)) {\n    throw createError({ status: 400, statusText: 'Invalid file type. Allowed: jpeg, png, webp, gif' })\n  }\n\n  if (file.size > IMAGE_MAX_SIZE) {\n    throw createError({ status: 400, statusText: 'File size exceeds 5MB limit' })\n  }\n\n  const ext = file.type.split('/')[1]\n  const key = `images/${slug}/${nanoid(10)()}.${ext}`\n\n  const arrayBuffer = await file.arrayBuffer()\n  await R2.put(key, arrayBuffer, {\n    httpMetadata: {\n      contentType: file.type,\n    },\n  })\n\n  const imageUrl = `/_assets/${key}`\n  return { url: imageUrl, key }\n})\n"
  },
  {
    "path": "server/api/verify.get.ts",
    "content": "defineRouteMeta({\n  openAPI: {\n    description: 'Verify the site token',\n    responses: {\n      200: {\n        description: 'The site token is valid',\n      },\n      default: {\n        description: 'The site token is invalid',\n      },\n    },\n  },\n})\n\nexport default eventHandler(() => {\n  return {\n    name: 'Sink',\n    url: 'https://sink.cool',\n  }\n})\n"
  },
  {
    "path": "server/middleware/1.redirect.ts",
    "content": "import type { Link } from '@/types'\nimport { parsePath, withQuery } from 'ufo'\n\nconst SOCIAL_BOTS = [\n  'applebot',\n  'discordbot',\n  'facebot',\n  'facebookexternalhit',\n  'linkedinbot',\n  'linkexpanding',\n  'mastodon',\n  'skypeuripreview',\n  'slackbot',\n  'slackbot-linkexpanding',\n  'snapchat',\n  'telegrambot',\n  'tiktok',\n  'twitterbot',\n  'whatsapp',\n]\n\nfunction isSocialBot(userAgent: string): boolean {\n  const ua = userAgent.toLowerCase()\n  return SOCIAL_BOTS.some(bot => ua.includes(bot))\n}\n\nfunction getDeviceRedirectUrl(userAgent: string, link: Link): string | null {\n  if (!link.apple && !link.google)\n    return null\n\n  const ua = userAgent.toLowerCase()\n\n  if (link.google && ua.includes('android')) {\n    return link.google\n  }\n\n  if (link.apple && (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod'))) {\n    return link.apple\n  }\n\n  return null\n}\n\nfunction hasOgConfig(link: Link): boolean {\n  return !!(link.title || link.image)\n}\n\nexport default eventHandler(async (event) => {\n  const { pathname: slug } = parsePath(event.path.replace(/^\\/|\\/$/g, ''))\n  const { slugRegex, reserveSlug } = useAppConfig()\n  const { homeURL, linkCacheTtl, caseSensitive, redirectWithQuery, redirectStatusCode } = useRuntimeConfig(event)\n  const { cloudflare } = event.context\n\n  if (event.path === '/' && homeURL)\n    return sendRedirect(event, homeURL)\n\n  const { notFoundRedirect } = useRuntimeConfig(event)\n  // Bypass redirect check for notFoundRedirect path to prevent infinite loop\n  if (notFoundRedirect && event.path === notFoundRedirect) {\n    return\n  }\n\n  if (slug && !reserveSlug.includes(slug) && slugRegex.test(slug) && cloudflare) {\n    let link: Link | null = null\n\n    const lowerCaseSlug = slug.toLowerCase()\n    link = await getLink(event, caseSensitive ? slug : lowerCaseSlug, linkCacheTtl)\n\n    if (!caseSensitive && !link && lowerCaseSlug !== slug) {\n      console.log('original slug fallback:', `slug:${slug} lowerCaseSlug:${lowerCaseSlug}`)\n      link = await getLink(event, slug, linkCacheTtl)\n    }\n\n    if (link) {\n      let locale: RedirectLocale | undefined\n      const getLocale = () => {\n        locale ??= resolveRedirectLocale(getHeader(event, 'accept-language'))\n        return locale\n      }\n      const sendNoStoreHtml = (html: string) => {\n        setHeader(event, 'Content-Type', 'text/html; charset=utf-8')\n        setHeader(event, 'Cache-Control', 'no-store')\n        return html\n      }\n\n      // Password protection check\n      if (link.password) {\n        const headerPassword = getHeader(event, 'x-link-password')\n\n        if (event.method === 'POST') {\n          const body = await readBody(event)\n          const submittedPassword = body?.password\n\n          if (submittedPassword !== link.password) {\n            return sendNoStoreHtml(generatePasswordHtml(slug, { hasError: true, locale: getLocale() }))\n          }\n\n          // Password correct - show unsafe warning if needed\n          if (link.unsafe && body?.confirm !== 'true') {\n            return sendNoStoreHtml(generateUnsafeWarningHtml(slug, link.url, { password: link.password, locale: getLocale() }))\n          }\n        }\n        else if (headerPassword) {\n          if (headerPassword !== link.password) {\n            throw createError({ status: 403, statusText: 'Incorrect password' })\n          }\n          // Header-password path: check unsafe warning via x-link-confirm header\n          if (link.unsafe && getHeader(event, 'x-link-confirm') !== 'true') {\n            throw createError({ status: 403, statusText: 'Unsafe link: confirmation required (set x-link-confirm: true header)' })\n          }\n        }\n        else {\n          return sendNoStoreHtml(generatePasswordHtml(slug, { locale: getLocale() }))\n        }\n      }\n\n      // Unsafe link warning (for links without password)\n      if (!link.password && link.unsafe) {\n        if (event.method === 'POST') {\n          const body = await readBody(event)\n          if (body?.confirm !== 'true') {\n            return sendNoStoreHtml(generateUnsafeWarningHtml(slug, link.url, { locale: getLocale() }))\n          }\n        }\n        else {\n          return sendNoStoreHtml(generateUnsafeWarningHtml(slug, link.url, { locale: getLocale() }))\n        }\n      }\n\n      event.context.link = link\n      try {\n        await useAccessLog(event)\n      }\n      catch (error) {\n        console.error('Failed write access log:', error)\n      }\n\n      const userAgent = getHeader(event, 'user-agent') || ''\n      const query = getQuery(event)\n      const shouldRedirectWithQuery = link.redirectWithQuery ?? redirectWithQuery\n      const buildTarget = (url: string) => shouldRedirectWithQuery ? withQuery(url, query) : url\n\n      const deviceRedirectUrl = getDeviceRedirectUrl(userAgent, link)\n      if (deviceRedirectUrl) {\n        return sendRedirect(event, deviceRedirectUrl, +redirectStatusCode)\n      }\n\n      if (isSocialBot(userAgent) && hasOgConfig(link)) {\n        const baseUrl = `${getRequestProtocol(event)}://${getRequestHost(event)}`\n        const html = generateOgHtml(link, buildTarget(link.url), baseUrl)\n        setHeader(event, 'Content-Type', 'text/html; charset=utf-8')\n        return html\n      }\n\n      if (link.cloaking) {\n        const baseUrl = `${getRequestProtocol(event)}://${getRequestHost(event)}`\n        const html = generateCloakingHtml(link, buildTarget(link.url), baseUrl)\n        setHeader(event, 'Content-Type', 'text/html; charset=utf-8')\n        setHeader(event, 'Cache-Control', 'no-store, private')\n        return html\n      }\n\n      return sendRedirect(event, buildTarget(link.url), +redirectStatusCode)\n    }\n    else {\n      if (notFoundRedirect) {\n        return sendRedirect(event, notFoundRedirect, 302)\n      }\n\n      throw createError({ status: 404, statusText: 'Link not found' })\n    }\n  }\n})\n"
  },
  {
    "path": "server/middleware/2.auth.ts",
    "content": "export default eventHandler((event) => {\n  const token = getHeader(event, 'Authorization')?.replace(/^Bearer\\s+/, '')\n  if (event.path.startsWith('/api/') && token !== useRuntimeConfig(event).siteToken) {\n    throw createError({\n      status: 401,\n      statusText: 'Unauthorized',\n    })\n  }\n  if (token && token.length < 8) {\n    throw createError({\n      status: 401,\n      statusText: 'Token is too short',\n    })\n  }\n})\n"
  },
  {
    "path": "server/plugins/backup.ts",
    "content": "/// <reference path=\"../../worker-configuration.d.ts\" />\n\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook('cloudflare:scheduled', async (event) => {\n    const config = useRuntimeConfig()\n\n    if (config.disableAutoBackup) {\n      console.info('[backup:kv] Auto backup is disabled by configuration')\n      return\n    }\n\n    const env = event.env as Cloudflare.Env\n    await backupKVToR2(env)\n  })\n})\n"
  },
  {
    "path": "server/routes/_assets/[...key].get.ts",
    "content": "import { LinkSchema } from '#shared/schemas/link'\n\nconst slugValidator = LinkSchema.shape.slug\n\nexport default eventHandler(async (event) => {\n  const { cloudflare } = event.context\n  const { R2 } = cloudflare.env\n  const key = getRouterParam(event, 'key')\n\n  if (!key) {\n    throw createError({ status: 400, statusText: 'Key is required' })\n  }\n\n  // Only allow access to images/ path\n  if (!key.startsWith('images/')) {\n    throw createError({ status: 403, statusText: 'Access denied' })\n  }\n\n  // Validate slug in path: images/{slug}/{filename}\n  const parts = key.split('/')\n  if (parts.length < 3) {\n    throw createError({ status: 400, statusText: 'Invalid path format' })\n  }\n\n  const slug = parts[1]\n  const slugResult = slugValidator.safeParse(slug)\n  if (!slugResult.success) {\n    throw createError({ status: 400, statusText: 'Invalid slug format' })\n  }\n\n  const object = await R2.get(key)\n\n  if (!object) {\n    throw createError({ status: 404, statusText: 'Image not found' })\n  }\n\n  const contentType = object.httpMetadata?.contentType || 'application/octet-stream'\n\n  setHeader(event, 'Content-Type', contentType)\n  setHeader(event, 'Cache-Control', 'public, max-age=31536000, immutable')\n  setHeader(event, 'ETag', object.etag)\n\n  return object.body\n})\n"
  },
  {
    "path": "server/utils/access-log.ts",
    "content": "import type { H3Event } from 'h3'\nimport { parseAcceptLanguage } from 'intl-parse-accept-language'\nimport { UAParser } from 'ua-parser-js'\nimport {\n  CLIs,\n  Crawlers,\n  Emails,\n  ExtraDevices,\n  Fetchers,\n  InApps,\n  MediaPlayers,\n  Vehicles,\n} from 'ua-parser-js/extensions'\nimport { parseURL } from 'ufo'\nimport { getFlag } from '@/utils/flag'\n\nfunction toBlobNumber(blob: string) {\n  return +blob.replace(/\\D/g, '')\n}\n\nexport const blobsMap = {\n  blob1: 'slug',\n  blob2: 'url',\n  blob3: 'ua',\n  blob4: 'ip',\n  blob5: 'referer',\n  blob6: 'country',\n  blob7: 'region',\n  blob8: 'city',\n  blob9: 'timezone',\n  blob10: 'language',\n  blob11: 'os',\n  blob12: 'browser',\n  blob13: 'browserType',\n  blob14: 'device',\n  blob15: 'deviceType',\n  blob16: 'COLO',\n} as const\n\nexport const doublesMap = {\n  double1: 'latitude',\n  double2: 'longitude',\n} as const\n\nexport type BlobsMap = typeof blobsMap\nexport type BlobsKey = keyof BlobsMap\n\nexport type DoublesMap = typeof doublesMap\nexport type DoublesKey = keyof DoublesMap\n\nexport type LogsKey = BlobsMap[BlobsKey] | DoublesMap[DoublesKey]\nexport type LogsMap = {\n  [key in BlobsMap[BlobsKey]]: string | undefined\n} & {\n  [key in DoublesMap[DoublesKey]]?: number | undefined\n}\n\nexport const logsMap = Object.fromEntries([\n  ...Object.entries(blobsMap).map(([k, v]) => [v, k]),\n  ...Object.entries(doublesMap).map(([k, v]) => [v, k]),\n]) as LogsMap\n\nexport function logs2blobs(logs: LogsMap) {\n  return (Object.keys(blobsMap) as BlobsKey[])\n    .sort((a, b) => toBlobNumber(a) - toBlobNumber(b))\n    .map(key => String(logs[blobsMap[key] as LogsKey] || ''))\n}\n\nexport function blobs2logs(blobs: string[]) {\n  const logsList = Object.keys(blobsMap)\n\n  return blobs.reduce((logs, blob, i) => {\n    const key = blobsMap[logsList[i] as BlobsKey]\n    logs[key] = blob\n    return logs\n  }, {} as Partial<LogsMap>)\n}\n\nexport function logs2doubles(logs: LogsMap) {\n  return (Object.keys(doublesMap) as DoublesKey[])\n    .sort((a, b) => toBlobNumber(a) - toBlobNumber(b))\n    .map(key => Number(logs[doublesMap[key] as LogsKey] || 0))\n}\n\nexport function doubles2logs(doubles: number[]) {\n  const logsList = Object.keys(doublesMap)\n\n  return doubles.reduce((logs, double, i) => {\n    const key = doublesMap[logsList[i] as DoublesKey]\n    logs[key] = double\n    return logs\n  }, {} as Partial<LogsMap>)\n}\n\nexport function useAccessLog(event: H3Event) {\n  const ip = getHeader(event, 'cf-connecting-ip') || getHeader(event, 'x-real-ip') || getRequestIP(event, { xForwardedFor: true })\n\n  const { host: referer } = parseURL(getHeader(event, 'referer'))\n\n  const acceptLanguage = getHeader(event, 'accept-language') || ''\n  const language = (parseAcceptLanguage(acceptLanguage) || [])[0]\n\n  const userAgent = getHeader(event, 'user-agent') || ''\n  const uaInfo = (new UAParser(userAgent, {\n\n    // @ts-expect-error\n    browser: [Crawlers.browser || [], CLIs.browser || [], Emails.browser || [], Fetchers.browser || [], InApps.browser || [], MediaPlayers.browser || [], Vehicles.browser || []].flat(),\n\n    // @ts-expect-error\n    device: [ExtraDevices.device || []].flat(),\n  })).getResult()\n\n  const { cloudflare } = event.context\n  const { request: { cf }, env } = cloudflare\n  const link = event.context.link || {}\n\n  const isBot = cf?.botManagement?.verifiedBot\n    || ['crawler', 'fetcher'].includes(uaInfo?.browser?.type || '')\n    || ['spider', 'bot'].includes(uaInfo?.browser?.name?.toLowerCase() || '')\n\n  const { disableBotAccessLog } = useRuntimeConfig(event)\n  if (isBot && disableBotAccessLog) {\n    console.log('bot access log disabled:', userAgent)\n    return Promise.resolve()\n  }\n\n  const regionNames = new Intl.DisplayNames(['en'], { type: 'region' })\n  const countryName = regionNames.of(cf?.country || 'WD') // fallback to \"Worldwide\"\n  const accessLogs = {\n    url: link.url,\n    slug: link.slug,\n    ua: userAgent,\n    ip,\n    referer,\n    country: cf?.country,\n    region: `${getFlag(cf?.country)} ${[cf?.region, countryName].filter(Boolean).join(',')}`,\n    city: `${getFlag(cf?.country)} ${[cf?.city, countryName].filter(Boolean).join(',')}`,\n    timezone: cf?.timezone,\n    language,\n    os: uaInfo?.os?.name,\n    browser: uaInfo?.browser?.name,\n    browserType: uaInfo?.browser?.type,\n    device: uaInfo?.device?.model,\n    deviceType: uaInfo?.device?.type,\n    COLO: cf?.colo,\n\n    // For RealTime Globe\n    latitude: Number(cf?.latitude || getHeader(event, 'cf-iplatitude') || 0),\n    longitude: Number(cf?.longitude || getHeader(event, 'cf-iplongitude') || 0),\n  }\n\n  if (process.env.NODE_ENV === 'production') {\n    return env.ANALYTICS.writeDataPoint({\n      indexes: [link.id], // only one index\n      blobs: logs2blobs(accessLogs),\n      doubles: logs2doubles(accessLogs),\n    })\n  }\n\n  console.log('access logs:', accessLogs, logs2blobs(accessLogs), logs2doubles(accessLogs), { ...blobs2logs(logs2blobs(accessLogs)), ...doubles2logs(logs2doubles(accessLogs)) })\n  return Promise.resolve()\n}\n"
  },
  {
    "path": "server/utils/backup.ts",
    "content": "/// <reference path=\"../../worker-configuration.d.ts\" />\n\nimport type { Link } from '#shared/schemas/link'\nimport pLimit from 'p-limit'\n\nexport interface BackupData {\n  version: string\n  exportedAt: string\n  count: number\n  links: Link[]\n}\n\nexport async function backupKVToR2(env: Cloudflare.Env, isManual: boolean = false): Promise<void> {\n  if (!env.R2) {\n    console.info('[backup:kv] R2 binding not configured, skipping backup')\n    return\n  }\n\n  const allLinks: Link[] = []\n  let cursor: string | undefined\n\n  do {\n    const list = await env.KV.list({\n      prefix: 'link:',\n      limit: 1000,\n      cursor,\n    })\n\n    const limit = pLimit(10)\n    const links = await Promise.all(\n      list.keys.map(key =>\n        limit(async () => {\n          const value = await env.KV.get(key.name, { type: 'json' })\n          return value as Link | null\n        }),\n      ),\n    )\n\n    allLinks.push(...links.filter((link): link is Link => link !== null))\n    cursor = list.list_complete ? undefined : list.cursor\n  } while (cursor)\n\n  const now = new Date()\n  const backupData: BackupData = {\n    version: '1.0',\n    exportedAt: now.toISOString(),\n    count: allLinks.length,\n    links: allLinks,\n  }\n\n  const timestamp = now.toISOString().replace(/:/g, '-')\n  const prefix = isManual ? 'manual-links-' : 'links-'\n  const filename = `backups/${prefix}${timestamp}.json`\n\n  await env.R2.put(filename, JSON.stringify(backupData, null, 2), {\n    httpMetadata: {\n      contentType: 'application/json',\n    },\n    customMetadata: {\n      count: String(allLinks.length),\n      exportedAt: backupData.exportedAt,\n    },\n  })\n\n  console.info(`[backup:kv] Backup completed: ${filename}, ${allLinks.length} links`)\n}\n"
  },
  {
    "path": "server/utils/cloudflare.ts",
    "content": "import type { H3Event } from 'h3'\n\nexport function useWAE(event: H3Event, query: string) {\n  const { cfAccountId, cfApiToken } = useRuntimeConfig(event)\n  console.info('useWAE', query)\n  return $fetch(`https://api.cloudflare.com/client/v4/accounts/${cfAccountId}/analytics_engine/sql`, {\n    method: 'POST',\n    headers: {\n      Authorization: `Bearer ${cfApiToken}`,\n    },\n    body: query,\n    retry: 1,\n    retryDelay: 100, // ms\n  })\n}\n"
  },
  {
    "path": "server/utils/link-store.ts",
    "content": "import type { LinkSchema } from '#shared/schemas/link'\nimport type { H3Event } from 'h3'\nimport type { z } from 'zod'\nimport { parseURL, stringifyParsedURL } from 'ufo'\n\ntype Link = z.infer<typeof LinkSchema>\n\nexport function withoutQuery(url: string): string {\n  const parsed = parseURL(url)\n  return stringifyParsedURL({ ...parsed, search: '' })\n}\n\nexport function normalizeSlug(event: H3Event, slug: string): string {\n  const { caseSensitive } = useRuntimeConfig(event)\n  return caseSensitive ? slug : slug.toLowerCase()\n}\n\nexport function buildShortLink(event: H3Event, slug: string): string {\n  return `${getRequestProtocol(event)}://${getRequestHost(event)}/${slug}`\n}\n\nexport async function putLink(event: H3Event, link: Link): Promise<void> {\n  const { cloudflare } = event.context\n  const { KV } = cloudflare.env\n  const expiration = getExpiration(event, link.expiration)\n\n  await KV.put(`link:${link.slug}`, JSON.stringify(link), {\n    expiration,\n    metadata: {\n      expiration,\n      url: withoutQuery(link.url),\n      comment: link.comment,\n    },\n  })\n}\n\nexport async function getLink(event: H3Event, slug: string, cacheTtl?: number): Promise<Link | null> {\n  const { cloudflare } = event.context\n  const { KV } = cloudflare.env\n  return await KV.get(`link:${slug}`, { type: 'json', cacheTtl }) as Link | null\n}\n\nexport async function getLinkWithMetadata(event: H3Event, slug: string): Promise<{ link: Link | null, metadata: Record<string, unknown> | null }> {\n  const { cloudflare } = event.context\n  const { KV } = cloudflare.env\n  const { metadata, value: link } = await KV.getWithMetadata(`link:${slug}`, { type: 'json' })\n  return { link: link as Link | null, metadata: metadata as Record<string, unknown> | null }\n}\n\nexport async function deleteLink(event: H3Event, slug: string): Promise<void> {\n  const { cloudflare } = event.context\n  const { KV } = cloudflare.env\n  await KV.delete(`link:${slug}`)\n}\n\nexport async function linkExists(event: H3Event, slug: string): Promise<boolean> {\n  const link = await getLink(event, slug)\n  return link !== null\n}\n\ninterface ListLinksOptions {\n  limit: number\n  cursor?: string\n}\n\ninterface ListLinksResult {\n  links: (Link | null)[]\n  list_complete: boolean\n  cursor?: string\n}\n\nexport async function listLinks(event: H3Event, options: ListLinksOptions): Promise<ListLinksResult> {\n  const { cloudflare } = event.context\n  const { KV } = cloudflare.env\n  const list = await KV.list({\n    prefix: 'link:',\n    limit: options.limit,\n    cursor: options.cursor || undefined,\n  })\n\n  const links = await Promise.all(\n    (list.keys || []).map(async (key: { name: string }) => {\n      const { metadata, value: link } = await KV.getWithMetadata(key.name, { type: 'json' }) as { metadata: Record<string, unknown> | null, value: Link | null }\n      if (link) {\n        return {\n          ...(metadata ?? {}),\n          ...link,\n        }\n      }\n      return link\n    }),\n  )\n\n  return {\n    links,\n    list_complete: list.list_complete,\n    cursor: 'cursor' in list ? list.cursor : undefined,\n  }\n}\n"
  },
  {
    "path": "server/utils/markdown.ts",
    "content": "import type { H3Event } from 'h3'\nimport { ofetch } from 'ofetch'\n\nexport async function fetchPageMarkdown(event: H3Event, url: string, AI: Ai): Promise<string | null> {\n  const reqHeaders = Object.fromEntries(\n    Object.entries(getHeaders(event)).filter(([_, v]) => v !== undefined),\n  ) as Record<string, string>\n\n  try {\n    const response = await ofetch.raw(url, {\n      headers: {\n        ...reqHeaders,\n        Accept: 'text/markdown, text/html;q=0.9, */*;q=0.8',\n      },\n      timeout: 5000,\n      responseType: 'text',\n    })\n\n    const contentType = response.headers.get('content-type') || ''\n    const body = response._data as string\n\n    if (!body) {\n      return null\n    }\n\n    // Markdown for Agents: site returns markdown directly\n    if (contentType.includes('text/markdown')) {\n      return body.slice(0, 4096)\n    }\n\n    // Fallback: convert HTML to markdown via AI.toMarkdown()\n    if (contentType.includes('text/html')) {\n      try {\n        const result = await AI.toMarkdown({\n          name: 'page.html',\n          blob: new Blob([body], { type: 'text/html' }),\n        })\n        if (result.format === 'markdown' && result.data) {\n          return result.data.slice(0, 4096)\n        }\n      }\n      catch (err) {\n        console.warn(`[markdown] AI.toMarkdown() failed for ${url}:`, err)\n      }\n    }\n  }\n  catch (err) {\n    console.warn(`[markdown] Failed to fetch ${url}:`, err)\n  }\n\n  return null\n}\n"
  },
  {
    "path": "server/utils/query-filter.ts",
    "content": "import type { Query } from '#shared/schemas/query'\nimport type { SelectStatement } from 'sql-bricks'\nimport type { BlobsKey } from './access-log'\n\nconst { in: $in, and } = SqlBricks\n\nexport type { Query }\n\nexport function query2filter(query: Query) {\n  const filter = []\n  if (query.id)\n    filter.push($in('index1', query.id.split(',').filter(Boolean)))\n\n  const blobKeys = Object.keys(blobsMap) as BlobsKey[]\n  for (const blobKey of blobKeys) {\n    const queryKey = blobsMap[blobKey] as keyof Query\n    const value = query[queryKey]\n    if (typeof value === 'string' && value) {\n      filter.push($in(blobKey, value.split(',')))\n    }\n  }\n\n  return filter.length ? and(...filter) : []\n}\n\nexport function appendTimeFilter(sql: SelectStatement, query: Query): SelectStatement {\n  if (query.startAt) {\n    const startTimestamp = Math.floor(Number(query.startAt))\n    sql.where(SqlBricks.gte('timestamp', SqlBricks(`toDateTime(${startTimestamp})`)))\n  }\n\n  if (query.endAt) {\n    const endTimestamp = Math.floor(Number(query.endAt))\n    sql.where(SqlBricks.lte('timestamp', SqlBricks(`toDateTime(${endTimestamp})`)))\n  }\n\n  return sql\n}\n"
  },
  {
    "path": "server/utils/redirect-i18n.ts",
    "content": "export const REDIRECT_TRANSLATIONS = {\n  'en': {\n    passwordTitle: 'Password Required',\n    passwordLabel: 'Password',\n    passwordPlaceholder: 'Enter password',\n    passwordError: 'Incorrect password',\n    continue: 'Continue',\n    unsafeTitle: 'Potentially Unsafe Link',\n    unsafeDesc: 'This link has been flagged as potentially unsafe. Proceed with caution.',\n    goBack: 'Go Back',\n  },\n  'zh-CN': {\n    passwordTitle: '需要密码',\n    passwordLabel: '密码',\n    passwordPlaceholder: '请输入密码',\n    passwordError: '密码错误',\n    continue: '继续',\n    unsafeTitle: '潜在不安全链接',\n    unsafeDesc: '此链接已被标记为潜在不安全。请谨慎访问。',\n    goBack: '返回',\n  },\n  'zh-TW': {\n    passwordTitle: '需要密碼',\n    passwordLabel: '密碼',\n    passwordPlaceholder: '請輸入密碼',\n    passwordError: '密碼錯誤',\n    continue: '繼續',\n    unsafeTitle: '潛在不安全連結',\n    unsafeDesc: '此連結已被標記為潛在不安全。請謹慎訪問。',\n    goBack: '返回',\n  },\n  'ja': {\n    passwordTitle: 'パスワードが必要です',\n    passwordLabel: 'パスワード',\n    passwordPlaceholder: 'パスワードを入力',\n    passwordError: 'パスワードが間違っています',\n    continue: '続ける',\n    unsafeTitle: '安全でない可能性のあるリンク',\n    unsafeDesc: 'このリンクは安全でない可能性があるとしてフラグが付けられています。注意して進んでください。',\n    goBack: '戻る',\n  },\n  'ko': {\n    passwordTitle: '비밀번호 필요',\n    passwordLabel: '비밀번호',\n    passwordPlaceholder: '비밀번호 입력',\n    passwordError: '비밀번호가 올바르지 않습니다',\n    continue: '계속',\n    unsafeTitle: '잠재적으로 안전하지 않은 링크',\n    unsafeDesc: '이 링크는 잠재적으로 안전하지 않은 것으로 표시되었습니다. 주의해서 진행하십시오.',\n    goBack: '뒤로 가기',\n  },\n  'fr': {\n    passwordTitle: 'Mot de passe requis',\n    passwordLabel: 'Mot de passe',\n    passwordPlaceholder: 'Entrez le mot de passe',\n    passwordError: 'Mot de passe incorrect',\n    continue: 'Continuer',\n    unsafeTitle: 'Lien potentiellement dangereux',\n    unsafeDesc: 'Ce lien a été signalé comme potentiellement dangereux. Procédez avec prudence.',\n    goBack: 'Retour',\n  },\n  'de': {\n    passwordTitle: 'Passwort erforderlich',\n    passwordLabel: 'Passwort',\n    passwordPlaceholder: 'Passwort eingeben',\n    passwordError: 'Falsches Passwort',\n    continue: 'Weiter',\n    unsafeTitle: 'Potenziell unsicherer Link',\n    unsafeDesc: 'Dieser Link wurde als potenziell unsicher markiert. Gehen Sie mit Vorsicht vor.',\n    goBack: 'Zurück',\n  },\n  'es': {\n    passwordTitle: 'Contraseña requerida',\n    passwordLabel: 'Contraseña',\n    passwordPlaceholder: 'Introducir contraseña',\n    passwordError: 'Contraseña incorrecta',\n    continue: 'Continuar',\n    unsafeTitle: 'Enlace potencialmente inseguro',\n    unsafeDesc: 'Este enlace ha sido marcado como potencialmente inseguro. Proceda con precaución.',\n    goBack: 'Volver',\n  },\n  'pt': {\n    passwordTitle: 'Senha necessária',\n    passwordLabel: 'Senha',\n    passwordPlaceholder: 'Digite a senha',\n    passwordError: 'Senha incorreta',\n    continue: 'Continuar',\n    unsafeTitle: 'Link potencialmente inseguro',\n    unsafeDesc: 'Este link foi sinalizado como potencialmente inseguro. Prossiga com cuidado.',\n    goBack: 'Voltar',\n  },\n  'ru': {\n    passwordTitle: 'Требуется пароль',\n    passwordLabel: 'Пароль',\n    passwordPlaceholder: 'Введите пароль',\n    passwordError: 'Неверный пароль',\n    continue: 'Продолжить',\n    unsafeTitle: 'Потенциально небезопасная ссылка',\n    unsafeDesc: 'Эта ссылка была отмечена как потенциально небезопасная. Будьте осторожны.',\n    goBack: 'Назад',\n  },\n} as const\n\nexport type RedirectLocale = keyof typeof REDIRECT_TRANSLATIONS\n\nconst SUPPORTED_LOCALES = Object.keys(REDIRECT_TRANSLATIONS) as RedirectLocale[]\n\n// Normalize locale codes that may use underscores or inconsistent casing (e.g. zh_cn -> zh-CN)\nfunction normalizeLocaleCode(code: string): string {\n  const normalized = code.replace('_', '-')\n  try {\n    return Intl.getCanonicalLocales(normalized)[0] || ''\n  }\n  catch {\n    return ''\n  }\n}\n\nexport function resolveRedirectLocale(header: string | undefined): RedirectLocale {\n  if (!header)\n    return 'en'\n\n  const languages = header.split(',')\n    .map((lang) => {\n      const parts = lang.trim().split(';q=')\n      const code = parts[0] || ''\n      const q = parts[1]\n      return { code: normalizeLocaleCode(code), q: q ? Number.parseFloat(q) : 1.0 }\n    })\n    .sort((a, b) => b.q - a.q)\n\n  for (const { code } of languages) {\n    if (!code)\n      continue\n    // Exact match (e.g. zh-CN)\n    if (SUPPORTED_LOCALES.includes(code as RedirectLocale))\n      return code as RedirectLocale\n    // Prefix match (e.g. en-US -> en)\n    const prefix = code.split('-')[0]\n    if (prefix && SUPPORTED_LOCALES.includes(prefix as RedirectLocale))\n      return prefix as RedirectLocale\n  }\n\n  return 'en'\n}\n"
  },
  {
    "path": "server/utils/safe-browsing.ts",
    "content": "import type { H3Event } from 'h3'\nimport { ofetch } from 'ofetch'\n\nexport async function isSafeUrl(event: H3Event, url: string): Promise<boolean> {\n  const { safeBrowsingDoh } = useRuntimeConfig(event)\n  if (!safeBrowsingDoh)\n    return true\n\n  try {\n    const { hostname } = new URL(url)\n    const dohUrl = new URL(safeBrowsingDoh)\n    dohUrl.searchParams.set('type', 'A')\n    dohUrl.searchParams.set('name', hostname)\n\n    const dnsResult = await ofetch<{ Answer?: Array<{ data: string }> }>(dohUrl.toString(), {\n      headers: { accept: 'application/dns-json' },\n      timeout: 5000,\n      responseType: 'json',\n      cf: {\n        cacheEverything: true,\n        cacheTtlByStatus: { '200-299': 3600 },\n      },\n    } as RequestInit)\n    if (dnsResult && Array.isArray(dnsResult.Answer)) {\n      const isBlocked = dnsResult.Answer.some(answer => answer.data === '0.0.0.0')\n      return !isBlocked\n    }\n  }\n  catch (e) {\n    const { hostname } = new URL(url)\n    console.warn('isSafeUrl check failed:', hostname, e)\n  }\n  return true\n}\n"
  },
  {
    "path": "server/utils/sql-bricks.ts",
    "content": "import MySqlBricks from 'mysql-bricks'\n\nexport { MySqlBricks as SqlBricks }\n"
  },
  {
    "path": "server/utils/template.ts",
    "content": "import type { Link } from '#shared/schemas/link'\nimport { escape } from 'es-toolkit/string'\nimport { parseURL } from 'ufo'\n\nfunction buildMetaTags(link: Link, baseUrl: string) {\n  const { host: hostname } = parseURL(link.url)\n  const title = link.title || hostname || 'Link'\n  const hasImage = !!link.image\n  const imageUrl = hasImage && link.image!.startsWith('/')\n    ? `${baseUrl}${link.image}`\n    : link.image\n  const twitterCard = hasImage ? 'summary_large_image' : 'summary'\n\n  const tags = [\n    link.description ? `<meta name=\"description\" content=\"${escape(link.description)}\">` : '',\n    `<meta property=\"og:type\" content=\"website\">`,\n    `<meta property=\"og:url\" content=\"${escape(baseUrl)}/${escape(link.slug)}\">`,\n    `<meta property=\"og:title\" content=\"${escape(title)}\">`,\n    link.description ? `<meta property=\"og:description\" content=\"${escape(link.description)}\">` : '',\n    hasImage ? `<meta property=\"og:image\" content=\"${escape(imageUrl!)}\">` : '',\n    `<meta name=\"twitter:card\" content=\"${twitterCard}\">`,\n    `<meta name=\"twitter:title\" content=\"${escape(title)}\">`,\n    link.description ? `<meta name=\"twitter:description\" content=\"${escape(link.description)}\">` : '',\n    hasImage ? `<meta name=\"twitter:image\" content=\"${escape(imageUrl!)}\">` : '',\n  ].filter(Boolean).join('\\n    ')\n\n  return { title, tags }\n}\n\nexport function generateCloakingHtml(link: Link, targetUrl: string, baseUrl: string): string {\n  const { title, tags } = buildMetaTags(link, baseUrl)\n\n  return `<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>${escape(title)}</title>\n    ${tags}\n</head>\n<body style=\"margin:0;overflow:hidden\">\n    <iframe src=\"${escape(targetUrl)}\" style=\"width:100vw;height:100vh;border:none\" sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\" allowfullscreen referrerpolicy=\"no-referrer\"></iframe>\n    <noscript><meta http-equiv=\"refresh\" content=\"0;url=${escape(targetUrl)}\"></noscript>\n</body>\n</html>`\n}\n\ninterface PasswordHtmlOptions {\n  hasError?: boolean\n  locale?: RedirectLocale\n}\n\nexport function generatePasswordHtml(slug: string, options: PasswordHtmlOptions = {}): string {\n  const { hasError = false, locale = 'en' } = options\n  const t = REDIRECT_TRANSLATIONS[locale]\n  return `<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <meta name=\"robots\" content=\"noindex\">\n    <title>${escape(t.passwordTitle)}</title>\n    <style>\n      *{margin:0;padding:0;box-sizing:border-box}\n      body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#09090b;color:#fafafa}\n      .card{background:#0a0a0a;border:1px solid #27272a;border-radius:8px;padding:2rem;width:100%;max-width:360px;margin:1rem;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}\n      h1{font-size:1.125rem;font-weight:600;margin-bottom:1.5rem;text-align:center;letter-spacing:-.025em}\n      .error{color:#ef4444;font-size:.875rem;margin-bottom:1rem;text-align:center;font-weight:500}\n      label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.5rem;color:#fafafa}\n      input[type=password]{width:100%;padding:.5rem .75rem;background:#09090b;border:1px solid #27272a;border-radius:6px;font-size:.875rem;outline:none;color:#fafafa;margin-bottom:1rem;transition:border-color .15s ease}\n      input[type=password]:focus{border-color:#52525b;box-shadow:0 0 0 2px rgba(82,82,91,.3)}\n      input[type=password]::placeholder{color:#52525b}\n      button{width:100%;padding:.5rem;background:#fafafa;color:#18181b;border:none;border-radius:6px;font-size:.875rem;font-weight:500;cursor:pointer;transition:background-color .15s ease}\n      button:hover{background:#e4e4e7}\n    </style>\n</head>\n<body>\n    <div class=\"card\">\n        <h1>${escape(t.passwordTitle)}</h1>${hasError ? `\\n        <p class=\"error\">${escape(t.passwordError)}</p>` : ''}\n        <form method=\"POST\" action=\"/${escape(slug)}\">\n            <label for=\"password\">${escape(t.passwordLabel)}</label>\n            <input type=\"password\" id=\"password\" name=\"password\" required autofocus placeholder=\"${escape(t.passwordPlaceholder)}\">\n            <button type=\"submit\">${escape(t.continue)}</button>\n        </form>\n    </div>\n</body>\n</html>`\n}\n\ninterface UnsafeWarningHtmlOptions {\n  password?: string\n  locale?: RedirectLocale\n}\n\nexport function generateUnsafeWarningHtml(slug: string, targetUrl: string, options: UnsafeWarningHtmlOptions = {}): string {\n  const { password, locale = 'en' } = options\n  const t = REDIRECT_TRANSLATIONS[locale]\n  return `<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <meta name=\"robots\" content=\"noindex\">\n    <title>${escape(t.unsafeTitle)}</title>\n    <style>\n      *{margin:0;padding:0;box-sizing:border-box}\n      body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#09090b;color:#fafafa}\n      .card{background:#0a0a0a;border:1px solid #27272a;border-radius:8px;padding:2rem;width:100%;max-width:420px;margin:1rem;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}\n      .warning{display:flex;align-items:center;justify-content:center;gap:.75rem;margin-bottom:1rem}\n      .warning svg{width:1.5rem;height:1.5rem;color:#ef4444;flex-shrink:0}\n      h1{font-size:1.125rem;font-weight:600;letter-spacing:-.025em;color:#ef4444}\n      .desc{font-size:.875rem;color:#a1a1aa;margin-bottom:1rem;line-height:1.5;text-align:center}\n      .url{font-size:.8125rem;color:#a1a1aa;background:#18181b;border:1px solid #27272a;border-radius:6px;padding:.5rem .75rem;word-break:break-all;margin-bottom:1.5rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}\n      .actions{display:flex;gap:.75rem}\n      .btn{flex:1;padding:.5rem;border-radius:6px;font-size:.875rem;font-weight:500;cursor:pointer;text-align:center;text-decoration:none;border:none;transition:background-color .15s ease}\n      .btn-back{border:1px solid #27272a;background:#18181b;color:#fafafa}\n      .btn-back:hover{background:#27272a}\n      .btn-continue{background:#fafafa;color:#18181b}\n      .btn-continue:hover{background:#e4e4e7}\n    </style>\n</head>\n<body>\n    <div class=\"card\">\n        <div class=\"warning\">\n            <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><line x1=\"12\" x2=\"12\" y1=\"9\" y2=\"13\"/><line x1=\"12\" x2=\"12.01\" y1=\"17\" y2=\"17\"/></svg>\n            <h1>${escape(t.unsafeTitle)}</h1>\n        </div>\n        <p class=\"desc\">${escape(t.unsafeDesc)}</p>\n        <div class=\"url\">${escape(targetUrl)}</div>\n        <div class=\"actions\">\n            <a href=\"javascript:history.back()\" class=\"btn btn-back\">${escape(t.goBack)}</a>\n            <form method=\"POST\" action=\"/${escape(slug)}\" style=\"flex:1;display:flex\">\n                <input type=\"hidden\" name=\"confirm\" value=\"true\">${password ? `\\n                <input type=\"hidden\" name=\"password\" value=\"${escape(password)}\">` : ''}\n                <button type=\"submit\" class=\"btn btn-continue\" style=\"width:100%\">${escape(t.continue)}</button>\n            </form>\n        </div>\n    </div>\n</body>\n</html>`\n}\n\nexport function generateOgHtml(link: Link, targetUrl: string, baseUrl: string): string {\n  const { title, tags } = buildMetaTags(link, baseUrl)\n\n  return `<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>${escape(title)}</title>\n    ${tags}\n    <meta http-equiv=\"refresh\" content=\"1;url=${escape(targetUrl)}\">\n</head>\n<body>\n    <p>Redirecting to <a href=\"${escape(targetUrl)}\">${escape(targetUrl)}</a>...</p>\n</body>\n</html>`\n}\n"
  },
  {
    "path": "server/utils/time.ts",
    "content": "import type { H3Event } from 'h3'\n\nexport function getExpiration(event: H3Event, expiration: number | undefined) {\n  const { previewMode } = useRuntimeConfig(event).public\n  if (previewMode) {\n    const { previewTTL } = useAppConfig()\n    const previewExpiration = Math.floor(Date.now() / 1000) + previewTTL\n    if (!expiration || expiration > previewExpiration)\n      expiration = Math.floor(Date.now() / 1000) + previewTTL\n  }\n\n  return expiration\n}\n\nexport function isValidTimezone(tz: string): boolean {\n  try {\n    Intl.DateTimeFormat(undefined, { timeZone: tz })\n    return true\n  }\n  catch {\n    return false\n  }\n}\n\nexport function getSafeTimezone(tz: string): string {\n  return isValidTimezone(tz) ? tz : 'Etc/UTC'\n}\n"
  },
  {
    "path": "shared/schemas/import.ts",
    "content": "import { z } from 'zod'\n\nimport { LinkSchema } from './link'\n\n// Import uses LinkSchema but:\n// - Removes defaults (id, slug, createdAt, updatedAt have defaults in LinkSchema)\n// - Removes expiration refinement (imported links may have past expiration)\n// - Makes id optional, slug required\nconst ImportLinkSchema = LinkSchema\n  .omit({ expiration: true })\n  .extend({\n    id: z.string().trim().max(26).optional(),\n    expiration: z.number().int().safe().optional(),\n  })\n\nexport const ImportDataSchema = z.object({\n  version: z.string(),\n  exportedAt: z.string().optional(),\n  count: z.number().int().optional(),\n  links: z.array(ImportLinkSchema).min(1),\n})\n\nexport type ImportData = z.infer<typeof ImportDataSchema>\nexport type ImportLink = z.infer<typeof ImportLinkSchema>\n\nexport interface ImportResultItem {\n  index: number\n  slug: string\n  url: string\n}\n\nexport interface ImportResult {\n  success: number\n  skipped: number\n  failed: number\n  successItems: ImportResultItem[]\n  skippedItems: ImportResultItem[]\n  failedItems: (ImportResultItem & { reason: string })[]\n}\n"
  },
  {
    "path": "shared/schemas/link.ts",
    "content": "import { customAlphabet } from 'nanoid'\nimport { z } from 'zod'\n\nconst { slugRegex } = useAppConfig()\n\nconst slugDefaultLength = +useRuntimeConfig().public.slugDefaultLength\n\nexport const nanoid = (length: number = slugDefaultLength) => customAlphabet('23456789abcdefghjkmnpqrstuvwxyz', length)\n\nexport const LinkSchema = z.object({\n  id: z.string().trim().max(26).default(nanoid(10)),\n  url: z.string().trim().url().max(2048),\n  slug: z.string().trim().max(2048).regex(new RegExp(slugRegex)).default(nanoid()),\n  comment: z.string().trim().max(2048).optional(),\n  createdAt: z.number().int().safe().default(() => Math.floor(Date.now() / 1000)),\n  updatedAt: z.number().int().safe().default(() => Math.floor(Date.now() / 1000)),\n  expiration: z.number().int().safe().refine(expiration => expiration > Math.floor(Date.now() / 1000), {\n    message: 'expiration must be greater than current time',\n    path: ['expiration'],\n  }).optional(),\n  title: z.string().trim().max(256).optional(),\n  description: z.string().trim().max(2048).optional(),\n  image: z.string().trim().max(128).optional(),\n  apple: z.string().trim().url().max(2048).optional(),\n  google: z.string().trim().url().max(2048).optional(),\n  cloaking: z.boolean().optional(),\n  redirectWithQuery: z.boolean().optional(),\n  password: z.string().trim().min(1).max(128).optional(),\n  unsafe: z.boolean().optional(),\n})\n\nexport type Link = z.infer<typeof LinkSchema>\n\nexport interface ExportData {\n  version: string\n  exportedAt: string\n  count: number\n  links: Link[]\n  cursor?: string\n  list_complete: boolean\n}\n"
  },
  {
    "path": "shared/schemas/query.ts",
    "content": "import { z } from 'zod'\n\nconst listQueryLimit = +useRuntimeConfig().listQueryLimit\n\nexport const QuerySchema = z.object({\n  id: z.string().optional(),\n  startAt: z.coerce.number().int().safe().optional(),\n  endAt: z.coerce.number().int().safe().optional(),\n  url: z.string().optional(),\n  slug: z.string().optional(),\n  referer: z.string().optional(),\n  country: z.string().optional(),\n  region: z.string().optional(),\n  city: z.string().optional(),\n  timezone: z.string().optional(),\n  language: z.string().optional(),\n  os: z.string().optional(),\n  browser: z.string().optional(),\n  browserType: z.string().optional(),\n  device: z.string().optional(),\n  deviceType: z.string().optional(),\n  limit: z.coerce.number().int().safe().default(listQueryLimit),\n})\n\nexport type Query = z.infer<typeof QuerySchema>\n\n// export const FilterSchema = QuerySchema.omit({ id: true, startAt: true, endAt: true, limit: true }).extend({\n//   index1: z.string().optional(),\n// })\n"
  },
  {
    "path": "shared/types/cloudflare.d.ts",
    "content": "declare module 'h3' {\n  interface H3EventContext {\n    cloudflare: {\n      request: Request<unknown, IncomingRequestCfProperties>\n      env: Cloudflare.Env\n      context: ExecutionContext\n    }\n  }\n}\n\nexport {}\n"
  },
  {
    "path": "shared/types/link.ts",
    "content": "import type { Link } from '#shared/schemas/link'\nimport type { DateValue } from '@internationalized/date'\nimport type { AnyFieldApi } from '@tanstack/vue-form'\n\nexport type { Link }\n\nexport type LinkUpdateType = 'create' | 'edit' | 'delete'\n\n// Form data derived from Link, with DateValue for expiration and required strings for optional fields\ntype LinkFormFields = Omit<Link, 'id' | 'createdAt' | 'updatedAt' | 'expiration'> & {\n  expiration: DateValue | undefined\n}\n\nexport type LinkFormData = {\n  [K in keyof LinkFormFields]-?: LinkFormFields[K] extends string | undefined ? string : LinkFormFields[K]\n}\n\nexport type { AnyFieldApi }\n\nexport interface LinkListResponse {\n  links: Link[]\n  cursor: string\n  list_complete: boolean\n}\n\nexport type LinkSortBy = 'newest' | 'oldest' | 'az' | 'za'\n"
  },
  {
    "path": "shared/types/mysql-bricks.d.ts",
    "content": "declare module 'mysql-bricks' {\n  import type SqlBricks from 'sql-bricks'\n\n  const mysqlBricks: typeof SqlBricks\n  export = mysqlBricks\n}\n"
  },
  {
    "path": "shared/types/traffic.ts",
    "content": "export interface TrafficEventParams {\n  props: {\n    item: {\n      latitude?: number\n      longitude?: number\n      COLO?: string\n      city?: string\n    }\n  }\n}\n"
  },
  {
    "path": "skills/sink/SKILL.md",
    "content": "---\nname: sink\ndescription: |\n  Sink short link API operations via OpenAPI. Use when managing short links: creating, querying, updating, deleting, listing, importing, or exporting links. Also covers AI-powered slug generation and link analytics.\n  Triggers: \"create short link\", \"shorten URL\", \"delete link\", \"edit link\", \"list links\", \"export links\", \"import links\", \"link analytics\", \"AI slug\".\n---\n\n# Sink API\n\nSink is a link shortener running on Cloudflare. Manage links via REST API.\n\n## Authentication\n\nAll endpoints require Bearer token authentication:\n\n```http\nAuthorization: Bearer YOUR_SITE_TOKEN\n```\n\nToken = `NUXT_SITE_TOKEN` environment variable.\n\n## Base URL\n\n```\nhttps://your-sink-domain\n```\n\n## API Reference\n\n### Create Link\n\n```http\nPOST /api/link/create\nContent-Type: application/json\n\n{\n  \"url\": \"https://example.com/long-url\",\n  \"slug\": \"custom-slug\",\n  \"comment\": \"optional note\",\n  \"expiration\": 1735689599,\n  \"apple\": \"https://apps.apple.com/app/id123\",\n  \"google\": \"https://play.google.com/store/apps/details?id=com.example\"\n}\n```\n\n**Required**: `url`\n**Optional**: `slug` (auto-generated if omitted), `comment`, `expiration` (unix timestamp), `apple` (iOS redirect), `google` (Android redirect), `password`, `unsafe`, `title`, `description`, `image`, `cloaking`, `redirectWithQuery`\n\n> If `NUXT_SAFE_BROWSING_DOH` is configured and `unsafe` is not explicitly set, the server auto-detects via DoH and marks unsafe links automatically.\n\n**Response** (201):\n\n```json\n{\n  \"link\": {\n    \"id\": \"abc123\",\n    \"url\": \"https://example.com/long-url\",\n    \"slug\": \"custom-slug\",\n    \"createdAt\": 1718119809,\n    \"updatedAt\": 1718119809\n  },\n  \"shortLink\": \"https://your-domain/custom-slug\"\n}\n```\n\n**Errors**: 409 (slug exists)\n\n### Query Link\n\n```http\nGET /api/link/query?slug=custom-slug\n```\n\n**Response** (200):\n\n```json\n{\n  \"id\": \"abc123\",\n  \"url\": \"https://example.com\",\n  \"slug\": \"custom-slug\",\n  \"createdAt\": 1718119809,\n  \"updatedAt\": 1718119809\n}\n```\n\n**Errors**: 404 (not found)\n\n### Edit Link\n\n```http\nPUT /api/link/edit\nContent-Type: application/json\n\n{\n  \"slug\": \"existing-slug\",\n  \"url\": \"https://new-url.com\",\n  \"comment\": \"updated note\"\n}\n```\n\n**Required**: `slug` (identifies which link to edit), `url`\n**Optional**: other fields to update\n\n**Response** (201): Same as create\n\n**Errors**: 404 (not found)\n\n### Delete Link\n\n```http\nPOST /api/link/delete\nContent-Type: application/json\n\n{\n  \"slug\": \"slug-to-delete\"\n}\n```\n\n**Response**: 200 (empty body)\n\n### List Links\n\n```http\nGET /api/link/list?limit=20&cursor=abc123\n```\n\n**Parameters**:\n\n- `limit`: max 1024, default 20\n- `cursor`: pagination cursor from previous response\n\n**Response**:\n\n```json\n{\n  \"keys\": [],\n  \"list_complete\": false,\n  \"cursor\": \"next-cursor\"\n}\n```\n\n### Export Links\n\n```http\nGET /api/link/export\n```\n\n**Response**:\n\n```json\n{\n  \"version\": \"1.0\",\n  \"exportedAt\": \"2024-01-01T00:00:00Z\",\n  \"count\": 100,\n  \"links\": [],\n  \"list_complete\": true\n}\n```\n\n### Import Links\n\n```http\nPOST /api/link/import\nContent-Type: application/json\n\n{\n  \"links\": [\n    {\"url\": \"https://example1.com\", \"slug\": \"ex1\"},\n    {\"url\": \"https://example2.com\", \"slug\": \"ex2\"}\n  ]\n}\n```\n\n**Response**: imported links array\n\n### AI Slug Generation\n\n```http\nGET /api/link/ai?url=https://example.com/article\n```\n\n**Response**:\n\n```json\n{\n  \"slug\": \"ai-generated-slug\"\n}\n```\n\n### Verify Token\n\n```http\nGET /api/verify\n```\n\nVerify if the site token is valid.\n\n**Response** (200):\n\n```json\n{\n  \"name\": \"Sink\",\n  \"url\": \"https://sink.cool\"\n}\n```\n\n**Errors**: 401 (invalid token)\n\n## Link Fields\n\n| Field               | Type    | Required | Description                                                                          |\n| ------------------- | ------- | -------- | ------------------------------------------------------------------------------------ |\n| `url`               | string  | Yes      | Target URL (max 2048)                                                                |\n| `slug`              | string  | No       | Custom slug (auto-generated)                                                         |\n| `comment`           | string  | No       | Internal note                                                                        |\n| `expiration`        | number  | No       | Unix timestamp                                                                       |\n| `apple`             | string  | No       | iOS/macOS redirect URL                                                               |\n| `google`            | string  | No       | Android redirect URL                                                                 |\n| `title`             | string  | No       | Custom title (max 256)                                                               |\n| `description`       | string  | No       | Custom description                                                                   |\n| `image`             | string  | No       | Custom image path                                                                    |\n| `cloaking`          | boolean | No       | Enable link cloaking                                                                 |\n| `redirectWithQuery` | boolean | No       | Append query params to destination URL (overrides global `NUXT_REDIRECT_WITH_QUERY`) |\n| `password`          | string  | No       | Password protection for the link                                                     |\n| `unsafe`            | boolean | No       | Mark as unsafe (shows warning page before redirect)                                  |\n\n## Analytics Endpoints\n\n### Summary\n\n```http\nGET /api/stats/summary\n```\n\n### Metrics\n\n```http\nGET /api/stats/metrics\n```\n\n### Realtime\n\n```http\nGET /api/stats/realtime\n```\n\n## OpenAPI Docs\n\n- JSON: `/_docs/openapi.json`\n- Scalar UI: `/_docs/scalar`\n- Swagger UI: `/_docs/swagger`\n\n## cURL Examples\n\nCreate link:\n\n```bash\ncurl -X POST https://your-domain/api/link/create \\\n  -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"url\": \"https://github.com/example\"}'\n```\n\nList links:\n\n```bash\ncurl https://your-domain/api/link/list \\\n  -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\nDelete link:\n\n```bash\ncurl -X POST https://your-domain/api/link/delete \\\n  -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"slug\": \"my-slug\"}'\n```\n"
  },
  {
    "path": "tests/api/export-import.spec.ts",
    "content": "import type { ImportResult } from '../../shared/schemas/import'\nimport type { ExportData } from '../../shared/schemas/link'\nimport { generateMock } from '@anatine/zod-mock'\nimport { describe, expect, it } from 'vitest'\nimport { z } from 'zod'\nimport { fetch, fetchWithAuth, postJson } from '../utils'\n\nconst linkSchema = z.object({\n  url: z.string().url(),\n  slug: z.string().min(1).max(50),\n})\n\nconst testLinkPayload = generateMock(linkSchema)\n\ndescribe.sequential('/api/link/export', () => {\n  it('exports links with valid auth', async () => {\n    await postJson('/api/link/create', testLinkPayload)\n\n    const response = await fetchWithAuth('/api/link/export')\n    expect(response.status).toBe(200)\n\n    const data: ExportData = await response.json()\n    expect(data).toHaveProperty('version')\n    expect(data).toHaveProperty('exportedAt')\n    expect(data).toHaveProperty('count')\n    expect(data).toHaveProperty('links')\n    expect(data).toHaveProperty('list_complete')\n    expect(data.links).toBeInstanceOf(Array)\n  })\n\n  it('supports cursor pagination', async () => {\n    const response = await fetchWithAuth('/api/link/export?cursor=test')\n    expect(response.status).toBe(200)\n\n    const data: ExportData = await response.json()\n    expect(data).toHaveProperty('links')\n  })\n\n  it('returns correct response headers', async () => {\n    const response = await fetchWithAuth('/api/link/export')\n    expect(response.status).toBe(200)\n    expect(response.headers.get('Content-Type')).toContain('application/json')\n    expect(response.headers.get('Cache-Control')).toBe('no-store')\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/link/export')\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/import', () => {\n  it('imports links with valid data', async () => {\n    const importPayload = {\n      version: '1.0',\n      links: [generateMock(linkSchema)],\n    }\n\n    const response = await postJson('/api/link/import', importPayload)\n    expect(response.status).toBe(200)\n\n    const data: ImportResult = await response.json()\n    expect(data).toHaveProperty('success')\n    expect(data).toHaveProperty('skipped')\n    expect(data).toHaveProperty('failed')\n    expect(data).toHaveProperty('successItems')\n    expect(data).toHaveProperty('skippedItems')\n    expect(data).toHaveProperty('failedItems')\n    expect(data.success).toBeGreaterThanOrEqual(0)\n  })\n\n  it('skips existing links during import', async () => {\n    const importPayload = { version: '1.0', links: [testLinkPayload] }\n    const response = await postJson('/api/link/import', importPayload)\n    expect(response.status).toBe(200)\n\n    const data: ImportResult = await response.json()\n    expect(data.skipped).toBeGreaterThanOrEqual(0)\n  })\n\n  it('returns 400 for invalid import data', async () => {\n    const response = await postJson('/api/link/import', { invalid: 'data' })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 for empty links array', async () => {\n    const response = await postJson('/api/link/import', { version: '1.0', links: [] })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 for invalid url in links', async () => {\n    const response = await postJson('/api/link/import', {\n      version: '1.0',\n      links: [{ url: 'not-a-valid-url', slug: 'test-slug' }],\n    })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await postJson('/api/link/import', {}, false)\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/api/link.spec.ts",
    "content": "import { generateMock } from '@anatine/zod-mock'\nimport { describe, expect, it } from 'vitest'\nimport { z } from 'zod'\nimport { fetch, fetchWithAuth, postJson, putJson } from '../utils'\n\nconst linkSchema = z.object({\n  url: z.string().url(),\n  slug: z.string().min(1).max(50),\n})\n\nconst testLinkPayload = generateMock(linkSchema)\n\ndescribe('/api/link/ai', () => {\n  it('generates AI slug for valid URL', async () => {\n    const response = await fetchWithAuth(`/api/link/ai?url=${encodeURIComponent('https://sink.cool')}`)\n\n    // AI binding may not be enabled (501) or request may timeout\n    expect([200, 501]).toContain(response.status)\n\n    if (response.status === 200) {\n      const data = await response.json() as { slug: string }\n      expect(data).toHaveProperty('slug')\n      expect(typeof data.slug).toBe('string')\n    }\n  }, 30000)\n\n  it('returns 400 when url parameter is missing', async () => {\n    const response = await fetchWithAuth('/api/link/ai')\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 when url parameter is invalid', async () => {\n    const response = await fetchWithAuth('/api/link/ai?url=not-a-valid-url')\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/link/ai')\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/create', () => {\n  it('creates new link with valid data', async () => {\n    const response = await postJson('/api/link/create', testLinkPayload)\n    expect(response.status).toBe(201)\n\n    const data = await response.json() as { link: typeof testLinkPayload, shortLink: string }\n    expect(data.link).toBeDefined()\n    expect(data.link.url).toBe(testLinkPayload.url)\n    expect(data.link.slug).toBe(testLinkPayload.slug)\n    expect(data.shortLink).toContain(testLinkPayload.slug)\n  })\n\n  it('returns 409 when slug already exists', async () => {\n    const payload = generateMock(linkSchema)\n    await postJson('/api/link/create', payload)\n\n    const duplicateResponse = await postJson('/api/link/create', payload)\n    expect(duplicateResponse.status).toBe(409)\n  })\n\n  it('returns 400 when url is missing', async () => {\n    const response = await postJson('/api/link/create', { slug: 'test-slug' })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 when url is invalid', async () => {\n    const response = await postJson('/api/link/create', { url: 'not-a-valid-url', slug: 'test-slug' })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await postJson('/api/link/create', {}, false)\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/upsert', () => {\n  it('creates new link with valid data', async () => {\n    const payload = generateMock(linkSchema)\n    const response = await postJson('/api/link/upsert', payload)\n    expect(response.status).toBe(201)\n  })\n\n  it('updates existing link with valid data', async () => {\n    const response = await postJson('/api/link/upsert', testLinkPayload)\n    expect(response.status).toBe(200)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await postJson('/api/link/upsert', {}, false)\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/query', () => {\n  it('returns link data for valid slug', async () => {\n    const response = await fetchWithAuth(`/api/link/query?slug=${testLinkPayload.slug}`)\n    expect(response.status).toBe(200)\n\n    const data = await response.json() as { url: string, slug: string }\n    expect(data).toHaveProperty('url')\n    expect(data).toHaveProperty('slug')\n  })\n\n  it('returns 404 when slug does not exist', async () => {\n    const response = await fetchWithAuth('/api/link/query?slug=non-existent-slug-12345')\n    expect(response.status).toBe(404)\n  })\n\n  it('returns 400 when slug parameter is missing', async () => {\n    const response = await fetchWithAuth('/api/link/query')\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch(`/api/link/query?slug=${testLinkPayload.slug}`)\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/list', () => {\n  it('returns paginated link list with valid auth', async () => {\n    const response = await fetchWithAuth('/api/link/list')\n    expect(response.status).toBe(200)\n\n    const data = await response.json() as { links: unknown[], list_complete: boolean }\n    expect(data).toHaveProperty('links')\n    expect(data).toHaveProperty('list_complete')\n    expect(data.links).toBeInstanceOf(Array)\n  })\n\n  it('supports limit parameter', async () => {\n    const response = await fetchWithAuth('/api/link/list?limit=5')\n    expect(response.status).toBe(200)\n\n    const data = await response.json() as { links: unknown[] }\n    expect(data.links.length).toBeLessThanOrEqual(5)\n  })\n\n  it('returns 400 when limit exceeds maximum', async () => {\n    const response = await fetchWithAuth('/api/link/list?limit=2000')\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/link/list')\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/search', () => {\n  it('returns link array with valid auth', async () => {\n    const response = await fetchWithAuth('/api/link/search')\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toBeInstanceOf(Array)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/link/search')\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/edit', () => {\n  it('updates existing link with valid data', async () => {\n    const response = await putJson('/api/link/edit', testLinkPayload)\n    expect(response.status).toBe(201)\n\n    const data = await response.json() as { link: unknown, shortLink: string }\n    expect(data).toHaveProperty('link')\n    expect(data).toHaveProperty('shortLink')\n  })\n\n  it('removes password when not provided in edit', async () => {\n    const slug = testLinkPayload.slug\n\n    // Set a password on the link\n    const setPasswordResponse = await putJson('/api/link/edit', { ...testLinkPayload, password: 'secret123' })\n    expect(setPasswordResponse.status).toBe(201)\n    const setData = await setPasswordResponse.json() as { link: { password?: string } }\n    expect(setData.link.password).toBe('secret123')\n\n    // Edit the link without providing a password (user cleared the field)\n    const removePasswordResponse = await putJson('/api/link/edit', { url: testLinkPayload.url, slug })\n    expect(removePasswordResponse.status).toBe(201)\n    const removeData = await removePasswordResponse.json() as { link: { password?: string } }\n    expect(removeData.link.password).toBeUndefined()\n  })\n\n  it('removes optional fields when not provided in edit', async () => {\n    const slug = testLinkPayload.slug\n\n    // Set optional fields\n    const setResponse = await putJson('/api/link/edit', {\n      ...testLinkPayload,\n      comment: 'test comment',\n      title: 'test title',\n      cloaking: true,\n      redirectWithQuery: true,\n    })\n    expect(setResponse.status).toBe(201)\n    const setData = await setResponse.json() as { link: { comment?: string, title?: string, cloaking?: boolean, redirectWithQuery?: boolean } }\n    expect(setData.link.comment).toBe('test comment')\n    expect(setData.link.title).toBe('test title')\n    expect(setData.link.cloaking).toBe(true)\n    expect(setData.link.redirectWithQuery).toBe(true)\n\n    // Edit without optional fields (user cleared them)\n    const removeResponse = await putJson('/api/link/edit', { url: testLinkPayload.url, slug })\n    expect(removeResponse.status).toBe(201)\n    const removeData = await removeResponse.json() as { link: { comment?: string, title?: string, cloaking?: boolean, redirectWithQuery?: boolean } }\n    expect(removeData.link.comment).toBeUndefined()\n    expect(removeData.link.title).toBeUndefined()\n    expect(removeData.link.cloaking).toBeUndefined()\n    expect(removeData.link.redirectWithQuery).toBeUndefined()\n  })\n\n  it('returns 404 when editing non-existent link', async () => {\n    const payload = { url: 'https://example.com', slug: 'non-existent-slug-for-edit-12345' }\n    const response = await putJson('/api/link/edit', payload)\n    expect(response.status).toBe(404)\n  })\n\n  it('returns 400 when body is invalid', async () => {\n    const response = await putJson('/api/link/edit', { url: 'invalid-url' })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await putJson('/api/link/edit', {}, false)\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe.sequential('/api/link/edit unsafe', () => {\n  const unsafePayload = { ...testLinkPayload, url: 'https://example.com', slug: 'unsafe-test-link' }\n\n  it('creates link with unsafe flag', async () => {\n    const response = await postJson('/api/link/create', { ...unsafePayload, unsafe: true })\n    expect(response.status).toBe(201)\n\n    const data = await response.json() as { link: { unsafe?: boolean } }\n    expect(data.link.unsafe).toBe(true)\n  })\n\n  it('queries link with unsafe flag', async () => {\n    const response = await fetchWithAuth(`/api/link/query?slug=${unsafePayload.slug}`)\n    expect(response.status).toBe(200)\n\n    const data = await response.json() as { unsafe?: boolean }\n    expect(data.unsafe).toBe(true)\n  })\n\n  it('removes unsafe flag when not provided in edit', async () => {\n    const response = await putJson('/api/link/edit', { url: unsafePayload.url, slug: unsafePayload.slug })\n    expect(response.status).toBe(201)\n\n    const data = await response.json() as { link: { unsafe?: boolean } }\n    expect(data.link.unsafe).toBeUndefined()\n  })\n\n  it('sets unsafe flag via edit', async () => {\n    const response = await putJson('/api/link/edit', { ...unsafePayload, unsafe: true })\n    expect(response.status).toBe(201)\n\n    const data = await response.json() as { link: { unsafe?: boolean } }\n    expect(data.link.unsafe).toBe(true)\n  })\n\n  it('deletes unsafe test link', async () => {\n    const response = await postJson('/api/link/delete', { slug: unsafePayload.slug })\n    expect(response.status).toBe(204)\n  })\n})\n\ndescribe.sequential('/api/link/delete', () => {\n  it('deletes link with valid slug and auth', async () => {\n    const response = await postJson('/api/link/delete', { slug: testLinkPayload.slug })\n    expect(response.status).toBe(204)\n  })\n\n  it('returns 400 when slug is missing', async () => {\n    const response = await postJson('/api/link/delete', {})\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 when slug is empty', async () => {\n    const response = await postJson('/api/link/delete', { slug: '' })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await postJson('/api/link/delete', {}, false)\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/api/location.spec.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { fetch, fetchWithAuth } from '../utils'\n\ndescribe('/api/location', () => {\n  it('returns location data with valid auth', async () => {\n    const response = await fetchWithAuth('/api/location')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    // In test environment, cf object may be undefined, so response could be empty or have undefined values\n    expect(data).toBeTypeOf('object')\n  })\n\n  it('returns correct response structure', async () => {\n    const response = await fetchWithAuth('/api/location')\n\n    expect(response.status).toBe(200)\n    expect(response.headers.get('Content-Type')).toContain('application/json')\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/location')\n\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/api/logs.spec.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { fetch, fetchWithAuth } from '../utils'\n\ndescribe('/api/logs/events', () => {\n  it('returns events data with valid auth', async () => {\n    const response = await fetchWithAuth('/api/logs/events?slug=0')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toBeInstanceOf(Array)\n  })\n\n  it('returns events with time filter', async () => {\n    const now = Math.floor(Date.now() / 1000)\n    const response = await fetchWithAuth(`/api/logs/events?slug=1&startAt=${now - 86400}&endAt=${now}`)\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toBeInstanceOf(Array)\n  })\n\n  it('supports limit parameter', async () => {\n    const response = await fetchWithAuth('/api/logs/events?slug=0&limit=10')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toBeInstanceOf(Array)\n  })\n\n  it('returns data without slug filter', async () => {\n    const response = await fetchWithAuth('/api/logs/events')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toBeInstanceOf(Array)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/logs/events?slug=0')\n\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe('/api/logs/locations', () => {\n  it('returns locations data with valid auth', async () => {\n    const response = await fetchWithAuth('/api/logs/locations?slug=0')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns locations with time filter', async () => {\n    const now = Math.floor(Date.now() / 1000)\n    const response = await fetchWithAuth(`/api/logs/locations?slug=1&startAt=${now - 86400}&endAt=${now}`)\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('supports limit parameter', async () => {\n    const response = await fetchWithAuth('/api/logs/locations?slug=0&limit=10')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns data without slug filter', async () => {\n    const response = await fetchWithAuth('/api/logs/locations')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/logs/locations?slug=0')\n\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/api/stats.spec.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { fetch, fetchWithAuth } from '../utils'\n\ndescribe('/api/stats/counters', () => {\n  it('returns counters data with valid auth', async () => {\n    const response = await fetchWithAuth('/api/stats/counters?slug=0')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns counters with time filter', async () => {\n    const now = Math.floor(Date.now() / 1000)\n    const response = await fetchWithAuth(`/api/stats/counters?slug=1&startAt=${now - 86400}&endAt=${now}`)\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns data without slug filter', async () => {\n    const response = await fetchWithAuth('/api/stats/counters')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/stats/counters?slug=0')\n\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe('/api/stats/metrics', () => {\n  it('returns metrics data with valid auth and type', async () => {\n    const response = await fetchWithAuth('/api/stats/metrics?slug=0&type=browser')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns metrics for different types', async () => {\n    const types = ['browser', 'os', 'device', 'country', 'referer']\n\n    for (const type of types) {\n      const response = await fetchWithAuth(`/api/stats/metrics?slug=1&type=${type}`)\n      expect(response.status).toBe(200)\n    }\n  })\n\n  it('returns 400 for invalid metric type', async () => {\n    const response = await fetchWithAuth('/api/stats/metrics?slug=0&type=invalid')\n\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 when type parameter is missing', async () => {\n    const response = await fetchWithAuth('/api/stats/metrics?slug=0')\n\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/stats/metrics?slug=0&type=browser')\n\n    expect(response.status).toBe(401)\n  })\n})\n\ndescribe('/api/stats/views', () => {\n  it('returns views data with valid auth and unit', async () => {\n    const response = await fetchWithAuth('/api/stats/views?slug=0&unit=day')\n\n    expect(response.status).toBe(200)\n\n    const data = await response.json()\n    expect(data).toHaveProperty('data')\n  })\n\n  it('returns views for different units', async () => {\n    const units = ['minute', 'hour', 'day']\n\n    for (const unit of units) {\n      const response = await fetchWithAuth(`/api/stats/views?slug=1&unit=${unit}`)\n      expect(response.status).toBe(200)\n    }\n  })\n\n  it('supports clientTimezone parameter', async () => {\n    const response = await fetchWithAuth('/api/stats/views?slug=0&unit=day&clientTimezone=Asia/Shanghai')\n\n    expect(response.status).toBe(200)\n  })\n\n  it('returns 400 for invalid clientTimezone format', async () => {\n    const response = await fetchWithAuth('/api/stats/views?slug=0&unit=day&clientTimezone=invalid<>timezone')\n\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 for invalid unit', async () => {\n    const response = await fetchWithAuth('/api/stats/views?slug=0&unit=invalid')\n\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 when unit parameter is missing', async () => {\n    const response = await fetchWithAuth('/api/stats/views?slug=0')\n\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/stats/views?slug=0&unit=day')\n\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/api/upload.spec.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { fetch, fetchWithAuth, TEST_PNG_BYTES } from '../utils'\n\ndescribe('/api/upload/image', () => {\n  it('uploads image with valid file and slug', async () => {\n    const formData = new FormData()\n    const file = new File([TEST_PNG_BYTES], 'test.png', { type: 'image/png' })\n    formData.append('file', file)\n    formData.append('slug', 'test-upload-slug')\n\n    const response = await fetchWithAuth('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(200)\n\n    const data = await response.json() as { url: string, key: string }\n    expect(data).toHaveProperty('url')\n    expect(data).toHaveProperty('key')\n    expect(data.url).toContain('/_assets/')\n  })\n\n  it('returns 400 when file is missing', async () => {\n    const formData = new FormData()\n    formData.append('slug', 'test-slug')\n\n    const response = await fetchWithAuth('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 when slug is missing', async () => {\n    const formData = new FormData()\n    const file = new File(['test'], 'test.png', { type: 'image/png' })\n    formData.append('file', file)\n\n    const response = await fetchWithAuth('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 for invalid file type', async () => {\n    const formData = new FormData()\n    const file = new File(['test content'], 'test.txt', { type: 'text/plain' })\n    formData.append('file', file)\n    formData.append('slug', 'test-slug')\n\n    const response = await fetchWithAuth('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 for file exceeding 5MB limit', async () => {\n    const formData = new FormData()\n    const largeContent = new Uint8Array(5 * 1024 * 1024 + 1)\n    const file = new File([largeContent], 'large.png', { type: 'image/png' })\n    formData.append('file', file)\n    formData.append('slug', 'test-slug')\n\n    const response = await fetchWithAuth('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 400 for invalid slug format', async () => {\n    const formData = new FormData()\n    const file = new File([TEST_PNG_BYTES], 'test.png', { type: 'image/png' })\n    formData.append('file', file)\n    formData.append('slug', 'invalid<>slug/path')\n\n    const response = await fetchWithAuth('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(400)\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const formData = new FormData()\n    formData.append('slug', 'test-slug')\n\n    const response = await fetch('/api/upload/image', {\n      method: 'POST',\n      body: formData,\n    })\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/api/verify.spec.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { fetch, fetchWithAuth } from '../utils'\n\ninterface VerifyResponse {\n  name: string\n  url: string\n}\n\ndescribe('/api/verify', () => {\n  it('returns user data with valid auth', async () => {\n    const response = await fetchWithAuth('/api/verify')\n    expect(response.status).toBe(200)\n\n    const data = await response.json() as VerifyResponse\n    expect(data).toHaveProperty('name')\n    expect(data).toHaveProperty('url')\n    expect(data.name).toBeTypeOf('string')\n    expect(data.url).toBeTypeOf('string')\n  })\n\n  it('returns correct response structure', async () => {\n    const response = await fetchWithAuth('/api/verify')\n    expect(response.status).toBe(200)\n    expect(response.headers.get('Content-Type')).toContain('application/json')\n\n    const data = await response.json() as VerifyResponse\n    expect(data.name).toBe('Sink')\n    expect(data.url).toBe('https://sink.cool')\n  })\n\n  it('returns 401 when accessing without auth', async () => {\n    const response = await fetch('/api/verify')\n    expect(response.status).toBe(401)\n  })\n\n  it('returns 401 with invalid token', async () => {\n    const response = await fetch('/api/verify', {\n      headers: { Authorization: 'Bearer invalid-token-12345' },\n    })\n    expect(response.status).toBe(401)\n  })\n})\n"
  },
  {
    "path": "tests/sink.spec.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { fetch } from './utils'\n\ndescribe('/', () => {\n  it('returns 200 for homepage request', async () => {\n    const response = await fetch('/')\n    expect(response.status).toBe(200)\n  })\n})\n"
  },
  {
    "path": "tests/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"types\": [\"@cloudflare/vitest-pool-workers\", \"vite/client\"]\n  },\n  \"include\": [\"./**/*.ts\", \"../worker-configuration.d.ts\"]\n}\n"
  },
  {
    "path": "tests/utils.ts",
    "content": "import { SELF } from 'cloudflare:test'\n\nexport function fetchWithAuth(path: string, options?: RequestInit): Promise<Response> {\n  return SELF.fetch(`http://localhost${path}`, {\n    ...options,\n    headers: {\n      ...options?.headers,\n      Authorization: `Bearer ${import.meta.env.NUXT_SITE_TOKEN}`,\n    },\n  })\n}\n\nexport function fetch(path: string, options?: RequestInit): Promise<Response> {\n  return SELF.fetch(`http://localhost${path}`, options)\n}\n\nexport function postJson(path: string, body: unknown, withAuth = true): Promise<Response> {\n  const fn = withAuth ? fetchWithAuth : fetch\n  return fn(path, {\n    method: 'POST',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' },\n  })\n}\n\nexport function putJson(path: string, body: unknown, withAuth = true): Promise<Response> {\n  const fn = withAuth ? fetchWithAuth : fetch\n  return fn(path, {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' },\n  })\n}\n\n// 1x1 transparent PNG for testing\nexport const TEST_PNG_BYTES = new Uint8Array([\n  0x89,\n  0x50,\n  0x4E,\n  0x47,\n  0x0D,\n  0x0A,\n  0x1A,\n  0x0A,\n  0x00,\n  0x00,\n  0x00,\n  0x0D,\n  0x49,\n  0x48,\n  0x44,\n  0x52,\n  0x00,\n  0x00,\n  0x00,\n  0x01,\n  0x00,\n  0x00,\n  0x00,\n  0x01,\n  0x08,\n  0x06,\n  0x00,\n  0x00,\n  0x00,\n  0x1F,\n  0x15,\n  0xC4,\n  0x89,\n  0x00,\n  0x00,\n  0x00,\n  0x0A,\n  0x49,\n  0x44,\n  0x41,\n  0x54,\n  0x78,\n  0x9C,\n  0x63,\n  0x00,\n  0x01,\n  0x00,\n  0x00,\n  0x05,\n  0x00,\n  0x01,\n  0x0D,\n  0x0A,\n  0x2D,\n  0xB4,\n  0x00,\n  0x00,\n  0x00,\n  0x00,\n  0x49,\n  0x45,\n  0x4E,\n  0x44,\n  0xAE,\n  0x42,\n  0x60,\n  0x82,\n])\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"references\": [\n    {\n      \"path\": \"./.nuxt/tsconfig.app.json\"\n    },\n    {\n      \"path\": \"./.nuxt/tsconfig.server.json\"\n    },\n    {\n      \"path\": \"./.nuxt/tsconfig.shared.json\"\n    },\n    {\n      \"path\": \"./.nuxt/tsconfig.node.json\"\n    }\n  ],\n  // https://nuxt.com/docs/guide/concepts/typescript\n  \"files\": []\n}\n"
  },
  {
    "path": "vitest.config.ts",
    "content": "import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'\nimport { loadEnv } from 'vite'\n\nexport default defineWorkersConfig(({ mode }) => ({\n  test: {\n    env: loadEnv(mode, process.cwd(), ''),\n    poolOptions: {\n      workers: {\n        singleWorker: true,\n        isolatedStorage: false,\n        wrangler: {\n          configPath: './wrangler.jsonc',\n        },\n        miniflare: {\n          cf: true,\n        },\n      },\n    },\n  },\n}))\n"
  },
  {
    "path": "worker-configuration.d.ts",
    "content": "/* eslint-disable */\n// Generated by Wrangler by running `wrangler types` (hash: ef234eb6a934fd9f4789281754fdc4f3)\n// Runtime types generated with workerd@1.20260107.1 2025-05-08 nodejs_compat\ndeclare namespace Cloudflare {\n\tinterface Env {\n\t\tKV: KVNamespace;\n\t\tNUXT_SITE_TOKEN: string;\n\t\tNUXT_CF_ACCOUNT_ID: string;\n\t\tNUXT_CF_API_TOKEN: string;\n\t\tR2: R2Bucket;\n\t\tANALYTICS: AnalyticsEngineDataset;\n\t\tAI: Ai;\n\t\tASSETS: Fetcher;\n\t}\n}\ninterface Env extends Cloudflare.Env {}\ntype StringifyValues<EnvType extends Record<string, unknown>> = {\n\t[Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;\n};\ndeclare namespace NodeJS {\n\tinterface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, \"NUXT_SITE_TOKEN\" | \"NUXT_CF_ACCOUNT_ID\" | \"NUXT_CF_API_TOKEN\">> {}\n}\n\n// Begin runtime types\n/*! *****************************************************************************\nCopyright (c) Cloudflare. All rights reserved.\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use\nthis file except in compliance with the License. You may obtain a copy of the\nLicense at http://www.apache.org/licenses/LICENSE-2.0\nTHIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\nKIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED\nWARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\nMERCHANTABLITY OR NON-INFRINGEMENT.\nSee the Apache Version 2.0 License for specific language governing permissions\nand limitations under the License.\n***************************************************************************** */\n/* eslint-disable */\n// noinspection JSUnusedGlobalSymbols\ndeclare var onmessage: never;\n/**\n * The **`DOMException`** interface represents an abnormal event (called an **exception**) that occurs as a result of calling a method or accessing a property of a web API.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException)\n */\ndeclare class DOMException extends Error {\n    constructor(message?: string, name?: string);\n    /**\n     * The **`message`** read-only property of the a message or description associated with the given error name.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message)\n     */\n    readonly message: string;\n    /**\n     * The **`name`** read-only property of the one of the strings associated with an error name.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name)\n     */\n    readonly name: string;\n    /**\n     * The **`code`** read-only property of the DOMException interface returns one of the legacy error code constants, or `0` if none match.\n     * @deprecated\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code)\n     */\n    readonly code: number;\n    static readonly INDEX_SIZE_ERR: number;\n    static readonly DOMSTRING_SIZE_ERR: number;\n    static readonly HIERARCHY_REQUEST_ERR: number;\n    static readonly WRONG_DOCUMENT_ERR: number;\n    static readonly INVALID_CHARACTER_ERR: number;\n    static readonly NO_DATA_ALLOWED_ERR: number;\n    static readonly NO_MODIFICATION_ALLOWED_ERR: number;\n    static readonly NOT_FOUND_ERR: number;\n    static readonly NOT_SUPPORTED_ERR: number;\n    static readonly INUSE_ATTRIBUTE_ERR: number;\n    static readonly INVALID_STATE_ERR: number;\n    static readonly SYNTAX_ERR: number;\n    static readonly INVALID_MODIFICATION_ERR: number;\n    static readonly NAMESPACE_ERR: number;\n    static readonly INVALID_ACCESS_ERR: number;\n    static readonly VALIDATION_ERR: number;\n    static readonly TYPE_MISMATCH_ERR: number;\n    static readonly SECURITY_ERR: number;\n    static readonly NETWORK_ERR: number;\n    static readonly ABORT_ERR: number;\n    static readonly URL_MISMATCH_ERR: number;\n    static readonly QUOTA_EXCEEDED_ERR: number;\n    static readonly TIMEOUT_ERR: number;\n    static readonly INVALID_NODE_TYPE_ERR: number;\n    static readonly DATA_CLONE_ERR: number;\n    get stack(): any;\n    set stack(value: any);\n}\ntype WorkerGlobalScopeEventMap = {\n    fetch: FetchEvent;\n    scheduled: ScheduledEvent;\n    queue: QueueEvent;\n    unhandledrejection: PromiseRejectionEvent;\n    rejectionhandled: PromiseRejectionEvent;\n};\ndeclare abstract class WorkerGlobalScope extends EventTarget<WorkerGlobalScopeEventMap> {\n    EventTarget: typeof EventTarget;\n}\n/* The **`console`** object provides access to the debugging console (e.g., the Web console in Firefox). *\n * The **`console`** object provides access to the debugging console (e.g., the Web console in Firefox).\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console)\n */\ninterface Console {\n    \"assert\"(condition?: boolean, ...data: any[]): void;\n    /**\n     * The **`console.clear()`** static method clears the console if possible.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear_static)\n     */\n    clear(): void;\n    /**\n     * The **`console.count()`** static method logs the number of times that this particular call to `count()` has been called.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count_static)\n     */\n    count(label?: string): void;\n    /**\n     * The **`console.countReset()`** static method resets counter used with console/count_static.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/countReset_static)\n     */\n    countReset(label?: string): void;\n    /**\n     * The **`console.debug()`** static method outputs a message to the console at the 'debug' log level.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug_static)\n     */\n    debug(...data: any[]): void;\n    /**\n     * The **`console.dir()`** static method displays a list of the properties of the specified JavaScript object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir_static)\n     */\n    dir(item?: any, options?: any): void;\n    /**\n     * The **`console.dirxml()`** static method displays an interactive tree of the descendant elements of the specified XML/HTML element.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml_static)\n     */\n    dirxml(...data: any[]): void;\n    /**\n     * The **`console.error()`** static method outputs a message to the console at the 'error' log level.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error_static)\n     */\n    error(...data: any[]): void;\n    /**\n     * The **`console.group()`** static method creates a new inline group in the Web console log, causing any subsequent console messages to be indented by an additional level, until console/groupEnd_static is called.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group_static)\n     */\n    group(...data: any[]): void;\n    /**\n     * The **`console.groupCollapsed()`** static method creates a new inline group in the console.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupCollapsed_static)\n     */\n    groupCollapsed(...data: any[]): void;\n    /**\n     * The **`console.groupEnd()`** static method exits the current inline group in the console.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupEnd_static)\n     */\n    groupEnd(): void;\n    /**\n     * The **`console.info()`** static method outputs a message to the console at the 'info' log level.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info_static)\n     */\n    info(...data: any[]): void;\n    /**\n     * The **`console.log()`** static method outputs a message to the console.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)\n     */\n    log(...data: any[]): void;\n    /**\n     * The **`console.table()`** static method displays tabular data as a table.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table_static)\n     */\n    table(tabularData?: any, properties?: string[]): void;\n    /**\n     * The **`console.time()`** static method starts a timer you can use to track how long an operation takes.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time_static)\n     */\n    time(label?: string): void;\n    /**\n     * The **`console.timeEnd()`** static method stops a timer that was previously started by calling console/time_static.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeEnd_static)\n     */\n    timeEnd(label?: string): void;\n    /**\n     * The **`console.timeLog()`** static method logs the current value of a timer that was previously started by calling console/time_static.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeLog_static)\n     */\n    timeLog(label?: string, ...data: any[]): void;\n    timeStamp(label?: string): void;\n    /**\n     * The **`console.trace()`** static method outputs a stack trace to the console.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace_static)\n     */\n    trace(...data: any[]): void;\n    /**\n     * The **`console.warn()`** static method outputs a warning message to the console at the 'warning' log level.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static)\n     */\n    warn(...data: any[]): void;\n}\ndeclare const console: Console;\ntype BufferSource = ArrayBufferView | ArrayBuffer;\ntype TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array;\ndeclare namespace WebAssembly {\n    class CompileError extends Error {\n        constructor(message?: string);\n    }\n    class RuntimeError extends Error {\n        constructor(message?: string);\n    }\n    type ValueType = \"anyfunc\" | \"externref\" | \"f32\" | \"f64\" | \"i32\" | \"i64\" | \"v128\";\n    interface GlobalDescriptor {\n        value: ValueType;\n        mutable?: boolean;\n    }\n    class Global {\n        constructor(descriptor: GlobalDescriptor, value?: any);\n        value: any;\n        valueOf(): any;\n    }\n    type ImportValue = ExportValue | number;\n    type ModuleImports = Record<string, ImportValue>;\n    type Imports = Record<string, ModuleImports>;\n    type ExportValue = Function | Global | Memory | Table;\n    type Exports = Record<string, ExportValue>;\n    class Instance {\n        constructor(module: Module, imports?: Imports);\n        readonly exports: Exports;\n    }\n    interface MemoryDescriptor {\n        initial: number;\n        maximum?: number;\n        shared?: boolean;\n    }\n    class Memory {\n        constructor(descriptor: MemoryDescriptor);\n        readonly buffer: ArrayBuffer;\n        grow(delta: number): number;\n    }\n    type ImportExportKind = \"function\" | \"global\" | \"memory\" | \"table\";\n    interface ModuleExportDescriptor {\n        kind: ImportExportKind;\n        name: string;\n    }\n    interface ModuleImportDescriptor {\n        kind: ImportExportKind;\n        module: string;\n        name: string;\n    }\n    abstract class Module {\n        static customSections(module: Module, sectionName: string): ArrayBuffer[];\n        static exports(module: Module): ModuleExportDescriptor[];\n        static imports(module: Module): ModuleImportDescriptor[];\n    }\n    type TableKind = \"anyfunc\" | \"externref\";\n    interface TableDescriptor {\n        element: TableKind;\n        initial: number;\n        maximum?: number;\n    }\n    class Table {\n        constructor(descriptor: TableDescriptor, value?: any);\n        readonly length: number;\n        get(index: number): any;\n        grow(delta: number, value?: any): number;\n        set(index: number, value?: any): void;\n    }\n    function instantiate(module: Module, imports?: Imports): Promise<Instance>;\n    function validate(bytes: BufferSource): boolean;\n}\n/**\n * The **`ServiceWorkerGlobalScope`** interface of the Service Worker API represents the global execution context of a service worker.\n * Available only in secure contexts.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope)\n */\ninterface ServiceWorkerGlobalScope extends WorkerGlobalScope {\n    DOMException: typeof DOMException;\n    WorkerGlobalScope: typeof WorkerGlobalScope;\n    btoa(data: string): string;\n    atob(data: string): string;\n    setTimeout(callback: (...args: any[]) => void, msDelay?: number): number;\n    setTimeout<Args extends any[]>(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number;\n    clearTimeout(timeoutId: number | null): void;\n    setInterval(callback: (...args: any[]) => void, msDelay?: number): number;\n    setInterval<Args extends any[]>(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number;\n    clearInterval(timeoutId: number | null): void;\n    queueMicrotask(task: Function): void;\n    structuredClone<T>(value: T, options?: StructuredSerializeOptions): T;\n    reportError(error: any): void;\n    fetch(input: RequestInfo | URL, init?: RequestInit<RequestInitCfProperties>): Promise<Response>;\n    self: ServiceWorkerGlobalScope;\n    crypto: Crypto;\n    caches: CacheStorage;\n    scheduler: Scheduler;\n    performance: Performance;\n    Cloudflare: Cloudflare;\n    readonly origin: string;\n    Event: typeof Event;\n    ExtendableEvent: typeof ExtendableEvent;\n    CustomEvent: typeof CustomEvent;\n    PromiseRejectionEvent: typeof PromiseRejectionEvent;\n    FetchEvent: typeof FetchEvent;\n    TailEvent: typeof TailEvent;\n    TraceEvent: typeof TailEvent;\n    ScheduledEvent: typeof ScheduledEvent;\n    MessageEvent: typeof MessageEvent;\n    CloseEvent: typeof CloseEvent;\n    ReadableStreamDefaultReader: typeof ReadableStreamDefaultReader;\n    ReadableStreamBYOBReader: typeof ReadableStreamBYOBReader;\n    ReadableStream: typeof ReadableStream;\n    WritableStream: typeof WritableStream;\n    WritableStreamDefaultWriter: typeof WritableStreamDefaultWriter;\n    TransformStream: typeof TransformStream;\n    ByteLengthQueuingStrategy: typeof ByteLengthQueuingStrategy;\n    CountQueuingStrategy: typeof CountQueuingStrategy;\n    ErrorEvent: typeof ErrorEvent;\n    EventSource: typeof EventSource;\n    ReadableStreamBYOBRequest: typeof ReadableStreamBYOBRequest;\n    ReadableStreamDefaultController: typeof ReadableStreamDefaultController;\n    ReadableByteStreamController: typeof ReadableByteStreamController;\n    WritableStreamDefaultController: typeof WritableStreamDefaultController;\n    TransformStreamDefaultController: typeof TransformStreamDefaultController;\n    CompressionStream: typeof CompressionStream;\n    DecompressionStream: typeof DecompressionStream;\n    TextEncoderStream: typeof TextEncoderStream;\n    TextDecoderStream: typeof TextDecoderStream;\n    Headers: typeof Headers;\n    Body: typeof Body;\n    Request: typeof Request;\n    Response: typeof Response;\n    WebSocket: typeof WebSocket;\n    WebSocketPair: typeof WebSocketPair;\n    WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair;\n    AbortController: typeof AbortController;\n    AbortSignal: typeof AbortSignal;\n    TextDecoder: typeof TextDecoder;\n    TextEncoder: typeof TextEncoder;\n    navigator: Navigator;\n    Navigator: typeof Navigator;\n    URL: typeof URL;\n    URLSearchParams: typeof URLSearchParams;\n    URLPattern: typeof URLPattern;\n    Blob: typeof Blob;\n    File: typeof File;\n    FormData: typeof FormData;\n    Crypto: typeof Crypto;\n    SubtleCrypto: typeof SubtleCrypto;\n    CryptoKey: typeof CryptoKey;\n    CacheStorage: typeof CacheStorage;\n    Cache: typeof Cache;\n    FixedLengthStream: typeof FixedLengthStream;\n    IdentityTransformStream: typeof IdentityTransformStream;\n    HTMLRewriter: typeof HTMLRewriter;\n}\ndeclare function addEventListener<Type extends keyof WorkerGlobalScopeEventMap>(type: Type, handler: EventListenerOrEventListenerObject<WorkerGlobalScopeEventMap[Type]>, options?: EventTargetAddEventListenerOptions | boolean): void;\ndeclare function removeEventListener<Type extends keyof WorkerGlobalScopeEventMap>(type: Type, handler: EventListenerOrEventListenerObject<WorkerGlobalScopeEventMap[Type]>, options?: EventTargetEventListenerOptions | boolean): void;\n/**\n * The **`dispatchEvent()`** method of the EventTarget sends an Event to the object, (synchronously) invoking the affected event listeners in the appropriate order.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent)\n */\ndeclare function dispatchEvent(event: WorkerGlobalScopeEventMap[keyof WorkerGlobalScopeEventMap]): boolean;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/btoa) */\ndeclare function btoa(data: string): string;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/atob) */\ndeclare function atob(data: string): string;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout) */\ndeclare function setTimeout(callback: (...args: any[]) => void, msDelay?: number): number;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout) */\ndeclare function setTimeout<Args extends any[]>(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/clearTimeout) */\ndeclare function clearTimeout(timeoutId: number | null): void;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setInterval) */\ndeclare function setInterval(callback: (...args: any[]) => void, msDelay?: number): number;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setInterval) */\ndeclare function setInterval<Args extends any[]>(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/clearInterval) */\ndeclare function clearInterval(timeoutId: number | null): void;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/queueMicrotask) */\ndeclare function queueMicrotask(task: Function): void;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/structuredClone) */\ndeclare function structuredClone<T>(value: T, options?: StructuredSerializeOptions): T;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/reportError) */\ndeclare function reportError(error: any): void;\n/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch) */\ndeclare function fetch(input: RequestInfo | URL, init?: RequestInit<RequestInitCfProperties>): Promise<Response>;\ndeclare const self: ServiceWorkerGlobalScope;\n/**\n* The Web Crypto API provides a set of low-level functions for common cryptographic tasks.\n* The Workers runtime implements the full surface of this API, but with some differences in\n* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms)\n* compared to those implemented in most browsers.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/)\n*/\ndeclare const crypto: Crypto;\n/**\n* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)\n*/\ndeclare const caches: CacheStorage;\ndeclare const scheduler: Scheduler;\n/**\n* The Workers runtime supports a subset of the Performance API, used to measure timing and performance,\n* as well as timing of subrequests and other operations.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/)\n*/\ndeclare const performance: Performance;\ndeclare const Cloudflare: Cloudflare;\ndeclare const origin: string;\ndeclare const navigator: Navigator;\ninterface TestController {\n}\ninterface ExecutionContext<Props = unknown> {\n    waitUntil(promise: Promise<any>): void;\n    passThroughOnException(): void;\n    readonly props: Props;\n}\ntype ExportedHandlerFetchHandler<Env = unknown, CfHostMetadata = unknown> = (request: Request<CfHostMetadata, IncomingRequestCfProperties<CfHostMetadata>>, env: Env, ctx: ExecutionContext) => Response | Promise<Response>;\ntype ExportedHandlerTailHandler<Env = unknown> = (events: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise<void>;\ntype ExportedHandlerTraceHandler<Env = unknown> = (traces: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise<void>;\ntype ExportedHandlerTailStreamHandler<Env = unknown> = (event: TailStream.TailEvent<TailStream.Onset>, env: Env, ctx: ExecutionContext) => TailStream.TailEventHandlerType | Promise<TailStream.TailEventHandlerType>;\ntype ExportedHandlerScheduledHandler<Env = unknown> = (controller: ScheduledController, env: Env, ctx: ExecutionContext) => void | Promise<void>;\ntype ExportedHandlerQueueHandler<Env = unknown, Message = unknown> = (batch: MessageBatch<Message>, env: Env, ctx: ExecutionContext) => void | Promise<void>;\ntype ExportedHandlerTestHandler<Env = unknown> = (controller: TestController, env: Env, ctx: ExecutionContext) => void | Promise<void>;\ninterface ExportedHandler<Env = unknown, QueueHandlerMessage = unknown, CfHostMetadata = unknown> {\n    fetch?: ExportedHandlerFetchHandler<Env, CfHostMetadata>;\n    tail?: ExportedHandlerTailHandler<Env>;\n    trace?: ExportedHandlerTraceHandler<Env>;\n    tailStream?: ExportedHandlerTailStreamHandler<Env>;\n    scheduled?: ExportedHandlerScheduledHandler<Env>;\n    test?: ExportedHandlerTestHandler<Env>;\n    email?: EmailExportedHandler<Env>;\n    queue?: ExportedHandlerQueueHandler<Env, QueueHandlerMessage>;\n}\ninterface StructuredSerializeOptions {\n    transfer?: any[];\n}\ndeclare abstract class Navigator {\n    sendBeacon(url: string, body?: BodyInit): boolean;\n    readonly userAgent: string;\n    readonly hardwareConcurrency: number;\n}\ninterface AlarmInvocationInfo {\n    readonly isRetry: boolean;\n    readonly retryCount: number;\n}\ninterface Cloudflare {\n    readonly compatibilityFlags: Record<string, boolean>;\n}\ninterface DurableObject {\n    fetch(request: Request): Response | Promise<Response>;\n    alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise<void>;\n    webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise<void>;\n    webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise<void>;\n    webSocketError?(ws: WebSocket, error: unknown): void | Promise<void>;\n}\ntype DurableObjectStub<T extends Rpc.DurableObjectBranded | undefined = undefined> = Fetcher<T, \"alarm\" | \"webSocketMessage\" | \"webSocketClose\" | \"webSocketError\"> & {\n    readonly id: DurableObjectId;\n    readonly name?: string;\n};\ninterface DurableObjectId {\n    toString(): string;\n    equals(other: DurableObjectId): boolean;\n    readonly name?: string;\n}\ndeclare abstract class DurableObjectNamespace<T extends Rpc.DurableObjectBranded | undefined = undefined> {\n    newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions): DurableObjectId;\n    idFromName(name: string): DurableObjectId;\n    idFromString(id: string): DurableObjectId;\n    get(id: DurableObjectId, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub<T>;\n    getByName(name: string, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub<T>;\n    jurisdiction(jurisdiction: DurableObjectJurisdiction): DurableObjectNamespace<T>;\n}\ntype DurableObjectJurisdiction = \"eu\" | \"fedramp\" | \"fedramp-high\";\ninterface DurableObjectNamespaceNewUniqueIdOptions {\n    jurisdiction?: DurableObjectJurisdiction;\n}\ntype DurableObjectLocationHint = \"wnam\" | \"enam\" | \"sam\" | \"weur\" | \"eeur\" | \"apac\" | \"oc\" | \"afr\" | \"me\";\ntype DurableObjectRoutingMode = \"primary-only\";\ninterface DurableObjectNamespaceGetDurableObjectOptions {\n    locationHint?: DurableObjectLocationHint;\n    routingMode?: DurableObjectRoutingMode;\n}\ninterface DurableObjectClass<_T extends Rpc.DurableObjectBranded | undefined = undefined> {\n}\ninterface DurableObjectState<Props = unknown> {\n    waitUntil(promise: Promise<any>): void;\n    readonly props: Props;\n    readonly id: DurableObjectId;\n    readonly storage: DurableObjectStorage;\n    container?: Container;\n    blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>;\n    acceptWebSocket(ws: WebSocket, tags?: string[]): void;\n    getWebSockets(tag?: string): WebSocket[];\n    setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void;\n    getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;\n    getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null;\n    setHibernatableWebSocketEventTimeout(timeoutMs?: number): void;\n    getHibernatableWebSocketEventTimeout(): number | null;\n    getTags(ws: WebSocket): string[];\n    abort(reason?: string): void;\n}\ninterface DurableObjectTransaction {\n    get<T = unknown>(key: string, options?: DurableObjectGetOptions): Promise<T | undefined>;\n    get<T = unknown>(keys: string[], options?: DurableObjectGetOptions): Promise<Map<string, T>>;\n    list<T = unknown>(options?: DurableObjectListOptions): Promise<Map<string, T>>;\n    put<T>(key: string, value: T, options?: DurableObjectPutOptions): Promise<void>;\n    put<T>(entries: Record<string, T>, options?: DurableObjectPutOptions): Promise<void>;\n    delete(key: string, options?: DurableObjectPutOptions): Promise<boolean>;\n    delete(keys: string[], options?: DurableObjectPutOptions): Promise<number>;\n    rollback(): void;\n    getAlarm(options?: DurableObjectGetAlarmOptions): Promise<number | null>;\n    setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise<void>;\n    deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise<void>;\n}\ninterface DurableObjectStorage {\n    get<T = unknown>(key: string, options?: DurableObjectGetOptions): Promise<T | undefined>;\n    get<T = unknown>(keys: string[], options?: DurableObjectGetOptions): Promise<Map<string, T>>;\n    list<T = unknown>(options?: DurableObjectListOptions): Promise<Map<string, T>>;\n    put<T>(key: string, value: T, options?: DurableObjectPutOptions): Promise<void>;\n    put<T>(entries: Record<string, T>, options?: DurableObjectPutOptions): Promise<void>;\n    delete(key: string, options?: DurableObjectPutOptions): Promise<boolean>;\n    delete(keys: string[], options?: DurableObjectPutOptions): Promise<number>;\n    deleteAll(options?: DurableObjectPutOptions): Promise<void>;\n    transaction<T>(closure: (txn: DurableObjectTransaction) => Promise<T>): Promise<T>;\n    getAlarm(options?: DurableObjectGetAlarmOptions): Promise<number | null>;\n    setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise<void>;\n    deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise<void>;\n    sync(): Promise<void>;\n    sql: SqlStorage;\n    kv: SyncKvStorage;\n    transactionSync<T>(closure: () => T): T;\n    getCurrentBookmark(): Promise<string>;\n    getBookmarkForTime(timestamp: number | Date): Promise<string>;\n    onNextSessionRestoreBookmark(bookmark: string): Promise<string>;\n}\ninterface DurableObjectListOptions {\n    start?: string;\n    startAfter?: string;\n    end?: string;\n    prefix?: string;\n    reverse?: boolean;\n    limit?: number;\n    allowConcurrency?: boolean;\n    noCache?: boolean;\n}\ninterface DurableObjectGetOptions {\n    allowConcurrency?: boolean;\n    noCache?: boolean;\n}\ninterface DurableObjectGetAlarmOptions {\n    allowConcurrency?: boolean;\n}\ninterface DurableObjectPutOptions {\n    allowConcurrency?: boolean;\n    allowUnconfirmed?: boolean;\n    noCache?: boolean;\n}\ninterface DurableObjectSetAlarmOptions {\n    allowConcurrency?: boolean;\n    allowUnconfirmed?: boolean;\n}\ndeclare class WebSocketRequestResponsePair {\n    constructor(request: string, response: string);\n    get request(): string;\n    get response(): string;\n}\ninterface AnalyticsEngineDataset {\n    writeDataPoint(event?: AnalyticsEngineDataPoint): void;\n}\ninterface AnalyticsEngineDataPoint {\n    indexes?: ((ArrayBuffer | string) | null)[];\n    doubles?: number[];\n    blobs?: ((ArrayBuffer | string) | null)[];\n}\n/**\n * The **`Event`** interface represents an event which takes place on an `EventTarget`.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event)\n */\ndeclare class Event {\n    constructor(type: string, init?: EventInit);\n    /**\n     * The **`type`** read-only property of the Event interface returns a string containing the event's type.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type)\n     */\n    get type(): string;\n    /**\n     * The **`eventPhase`** read-only property of the being evaluated.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase)\n     */\n    get eventPhase(): number;\n    /**\n     * The read-only **`composed`** property of the or not the event will propagate across the shadow DOM boundary into the standard DOM.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed)\n     */\n    get composed(): boolean;\n    /**\n     * The **`bubbles`** read-only property of the Event interface indicates whether the event bubbles up through the DOM tree or not.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles)\n     */\n    get bubbles(): boolean;\n    /**\n     * The **`cancelable`** read-only property of the Event interface indicates whether the event can be canceled, and therefore prevented as if the event never happened.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable)\n     */\n    get cancelable(): boolean;\n    /**\n     * The **`defaultPrevented`** read-only property of the Event interface returns a boolean value indicating whether or not the call to Event.preventDefault() canceled the event.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented)\n     */\n    get defaultPrevented(): boolean;\n    /**\n     * The Event property **`returnValue`** indicates whether the default action for this event has been prevented or not.\n     * @deprecated\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue)\n     */\n    get returnValue(): boolean;\n    /**\n     * The **`currentTarget`** read-only property of the Event interface identifies the element to which the event handler has been attached.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget)\n     */\n    get currentTarget(): EventTarget | undefined;\n    /**\n     * The read-only **`target`** property of the dispatched.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target)\n     */\n    get target(): EventTarget | undefined;\n    /**\n     * The deprecated **`Event.srcElement`** is an alias for the Event.target property.\n     * @deprecated\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement)\n     */\n    get srcElement(): EventTarget | undefined;\n    /**\n     * The **`timeStamp`** read-only property of the Event interface returns the time (in milliseconds) at which the event was created.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp)\n     */\n    get timeStamp(): number;\n    /**\n     * The **`isTrusted`** read-only property of the when the event was generated by the user agent (including via user actions and programmatic methods such as HTMLElement.focus()), and `false` when the event was dispatched via The only exception is the `click` event, which initializes the `isTrusted` property to `false` in user agents.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted)\n     */\n    get isTrusted(): boolean;\n    /**\n     * The **`cancelBubble`** property of the Event interface is deprecated.\n     * @deprecated\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble)\n     */\n    get cancelBubble(): boolean;\n    /**\n     * The **`cancelBubble`** property of the Event interface is deprecated.\n     * @deprecated\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble)\n     */\n    set cancelBubble(value: boolean);\n    /**\n     * The **`stopImmediatePropagation()`** method of the If several listeners are attached to the same element for the same event type, they are called in the order in which they were added.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation)\n     */\n    stopImmediatePropagation(): void;\n    /**\n     * The **`preventDefault()`** method of the Event interface tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault)\n     */\n    preventDefault(): void;\n    /**\n     * The **`stopPropagation()`** method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation)\n     */\n    stopPropagation(): void;\n    /**\n     * The **`composedPath()`** method of the Event interface returns the event's path which is an array of the objects on which listeners will be invoked.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath)\n     */\n    composedPath(): EventTarget[];\n    static readonly NONE: number;\n    static readonly CAPTURING_PHASE: number;\n    static readonly AT_TARGET: number;\n    static readonly BUBBLING_PHASE: number;\n}\ninterface EventInit {\n    bubbles?: boolean;\n    cancelable?: boolean;\n    composed?: boolean;\n}\ntype EventListener<EventType extends Event = Event> = (event: EventType) => void;\ninterface EventListenerObject<EventType extends Event = Event> {\n    handleEvent(event: EventType): void;\n}\ntype EventListenerOrEventListenerObject<EventType extends Event = Event> = EventListener<EventType> | EventListenerObject<EventType>;\n/**\n * The **`EventTarget`** interface is implemented by objects that can receive events and may have listeners for them.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget)\n */\ndeclare class EventTarget<EventMap extends Record<string, Event> = Record<string, Event>> {\n    constructor();\n    /**\n     * The **`addEventListener()`** method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)\n     */\n    addEventListener<Type extends keyof EventMap>(type: Type, handler: EventListenerOrEventListenerObject<EventMap[Type]>, options?: EventTargetAddEventListenerOptions | boolean): void;\n    /**\n     * The **`removeEventListener()`** method of the EventTarget interface removes an event listener previously registered with EventTarget.addEventListener() from the target.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener)\n     */\n    removeEventListener<Type extends keyof EventMap>(type: Type, handler: EventListenerOrEventListenerObject<EventMap[Type]>, options?: EventTargetEventListenerOptions | boolean): void;\n    /**\n     * The **`dispatchEvent()`** method of the EventTarget sends an Event to the object, (synchronously) invoking the affected event listeners in the appropriate order.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent)\n     */\n    dispatchEvent(event: EventMap[keyof EventMap]): boolean;\n}\ninterface EventTargetEventListenerOptions {\n    capture?: boolean;\n}\ninterface EventTargetAddEventListenerOptions {\n    capture?: boolean;\n    passive?: boolean;\n    once?: boolean;\n    signal?: AbortSignal;\n}\ninterface EventTargetHandlerObject {\n    handleEvent: (event: Event) => any | undefined;\n}\n/**\n * The **`AbortController`** interface represents a controller object that allows you to abort one or more Web requests as and when desired.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController)\n */\ndeclare class AbortController {\n    constructor();\n    /**\n     * The **`signal`** read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation as desired.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal)\n     */\n    get signal(): AbortSignal;\n    /**\n     * The **`abort()`** method of the AbortController interface aborts an asynchronous operation before it has completed.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort)\n     */\n    abort(reason?: any): void;\n}\n/**\n * The **`AbortSignal`** interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal)\n */\ndeclare abstract class AbortSignal extends EventTarget {\n    /**\n     * The **`AbortSignal.abort()`** static method returns an AbortSignal that is already set as aborted (and which does not trigger an AbortSignal/abort_event event).\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static)\n     */\n    static abort(reason?: any): AbortSignal;\n    /**\n     * The **`AbortSignal.timeout()`** static method returns an AbortSignal that will automatically abort after a specified time.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static)\n     */\n    static timeout(delay: number): AbortSignal;\n    /**\n     * The **`AbortSignal.any()`** static method takes an iterable of abort signals and returns an AbortSignal.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static)\n     */\n    static any(signals: AbortSignal[]): AbortSignal;\n    /**\n     * The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (`true`) or not (`false`).\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)\n     */\n    get aborted(): boolean;\n    /**\n     * The **`reason`** read-only property returns a JavaScript value that indicates the abort reason.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason)\n     */\n    get reason(): any;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */\n    get onabort(): any | null;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */\n    set onabort(value: any | null);\n    /**\n     * The **`throwIfAborted()`** method throws the signal's abort AbortSignal.reason if the signal has been aborted; otherwise it does nothing.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted)\n     */\n    throwIfAborted(): void;\n}\ninterface Scheduler {\n    wait(delay: number, maybeOptions?: SchedulerWaitOptions): Promise<void>;\n}\ninterface SchedulerWaitOptions {\n    signal?: AbortSignal;\n}\n/**\n * The **`ExtendableEvent`** interface extends the lifetime of the `install` and `activate` events dispatched on the global scope as part of the service worker lifecycle.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent)\n */\ndeclare abstract class ExtendableEvent extends Event {\n    /**\n     * The **`ExtendableEvent.waitUntil()`** method tells the event dispatcher that work is ongoing.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil)\n     */\n    waitUntil(promise: Promise<any>): void;\n}\n/**\n * The **`CustomEvent`** interface represents events initialized by an application for any purpose.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent)\n */\ndeclare class CustomEvent<T = any> extends Event {\n    constructor(type: string, init?: CustomEventCustomEventInit);\n    /**\n     * The read-only **`detail`** property of the CustomEvent interface returns any data passed when initializing the event.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail)\n     */\n    get detail(): T;\n}\ninterface CustomEventCustomEventInit {\n    bubbles?: boolean;\n    cancelable?: boolean;\n    composed?: boolean;\n    detail?: any;\n}\n/**\n * The **`Blob`** interface represents a blob, which is a file-like object of immutable, raw data; they can be read as text or binary data, or converted into a ReadableStream so its methods can be used for processing the data.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob)\n */\ndeclare class Blob {\n    constructor(type?: ((ArrayBuffer | ArrayBufferView) | string | Blob)[], options?: BlobOptions);\n    /**\n     * The **`size`** read-only property of the Blob interface returns the size of the Blob or File in bytes.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size)\n     */\n    get size(): number;\n    /**\n     * The **`type`** read-only property of the Blob interface returns the MIME type of the file.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type)\n     */\n    get type(): string;\n    /**\n     * The **`slice()`** method of the Blob interface creates and returns a new `Blob` object which contains data from a subset of the blob on which it's called.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice)\n     */\n    slice(start?: number, end?: number, type?: string): Blob;\n    /**\n     * The **`arrayBuffer()`** method of the Blob interface returns a Promise that resolves with the contents of the blob as binary data contained in an ArrayBuffer.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/arrayBuffer)\n     */\n    arrayBuffer(): Promise<ArrayBuffer>;\n    /**\n     * The **`bytes()`** method of the Blob interface returns a Promise that resolves with a Uint8Array containing the contents of the blob as an array of bytes.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/bytes)\n     */\n    bytes(): Promise<Uint8Array>;\n    /**\n     * The **`text()`** method of the string containing the contents of the blob, interpreted as UTF-8.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text)\n     */\n    text(): Promise<string>;\n    /**\n     * The **`stream()`** method of the Blob interface returns a ReadableStream which upon reading returns the data contained within the `Blob`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/stream)\n     */\n    stream(): ReadableStream;\n}\ninterface BlobOptions {\n    type?: string;\n}\n/**\n * The **`File`** interface provides information about files and allows JavaScript in a web page to access their content.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File)\n */\ndeclare class File extends Blob {\n    constructor(bits: ((ArrayBuffer | ArrayBufferView) | string | Blob)[] | undefined, name: string, options?: FileOptions);\n    /**\n     * The **`name`** read-only property of the File interface returns the name of the file represented by a File object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name)\n     */\n    get name(): string;\n    /**\n     * The **`lastModified`** read-only property of the File interface provides the last modified date of the file as the number of milliseconds since the Unix epoch (January 1, 1970 at midnight).\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified)\n     */\n    get lastModified(): number;\n}\ninterface FileOptions {\n    type?: string;\n    lastModified?: number;\n}\n/**\n* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)\n*/\ndeclare abstract class CacheStorage {\n    /**\n     * The **`open()`** method of the the Cache object matching the `cacheName`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CacheStorage/open)\n     */\n    open(cacheName: string): Promise<Cache>;\n    readonly default: Cache;\n}\n/**\n* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)\n*/\ndeclare abstract class Cache {\n    /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */\n    delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<boolean>;\n    /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#match) */\n    match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<Response | undefined>;\n    /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#put) */\n    put(request: RequestInfo | URL, response: Response): Promise<void>;\n}\ninterface CacheQueryOptions {\n    ignoreMethod?: boolean;\n}\n/**\n* The Web Crypto API provides a set of low-level functions for common cryptographic tasks.\n* The Workers runtime implements the full surface of this API, but with some differences in\n* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms)\n* compared to those implemented in most browsers.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/)\n*/\ndeclare abstract class Crypto {\n    /**\n     * The **`Crypto.subtle`** read-only property returns a cryptographic operations.\n     * Available only in secure contexts.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle)\n     */\n    get subtle(): SubtleCrypto;\n    /**\n     * The **`Crypto.getRandomValues()`** method lets you get cryptographically strong random values.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues)\n     */\n    getRandomValues<T extends Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array>(buffer: T): T;\n    /**\n     * The **`randomUUID()`** method of the Crypto interface is used to generate a v4 UUID using a cryptographically secure random number generator.\n     * Available only in secure contexts.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID)\n     */\n    randomUUID(): string;\n    DigestStream: typeof DigestStream;\n}\n/**\n * The **`SubtleCrypto`** interface of the Web Crypto API provides a number of low-level cryptographic functions.\n * Available only in secure contexts.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto)\n */\ndeclare abstract class SubtleCrypto {\n    /**\n     * The **`encrypt()`** method of the SubtleCrypto interface encrypts data.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt)\n     */\n    encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, plainText: ArrayBuffer | ArrayBufferView): Promise<ArrayBuffer>;\n    /**\n     * The **`decrypt()`** method of the SubtleCrypto interface decrypts some encrypted data.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt)\n     */\n    decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, cipherText: ArrayBuffer | ArrayBufferView): Promise<ArrayBuffer>;\n    /**\n     * The **`sign()`** method of the SubtleCrypto interface generates a digital signature.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign)\n     */\n    sign(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, data: ArrayBuffer | ArrayBufferView): Promise<ArrayBuffer>;\n    /**\n     * The **`verify()`** method of the SubtleCrypto interface verifies a digital signature.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify)\n     */\n    verify(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, signature: ArrayBuffer | ArrayBufferView, data: ArrayBuffer | ArrayBufferView): Promise<boolean>;\n    /**\n     * The **`digest()`** method of the SubtleCrypto interface generates a _digest_ of the given data, using the specified hash function.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest)\n     */\n    digest(algorithm: string | SubtleCryptoHashAlgorithm, data: ArrayBuffer | ArrayBufferView): Promise<ArrayBuffer>;\n    /**\n     * The **`generateKey()`** method of the SubtleCrypto interface is used to generate a new key (for symmetric algorithms) or key pair (for public-key algorithms).\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey)\n     */\n    generateKey(algorithm: string | SubtleCryptoGenerateKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise<CryptoKey | CryptoKeyPair>;\n    /**\n     * The **`deriveKey()`** method of the SubtleCrypto interface can be used to derive a secret key from a master key.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey)\n     */\n    deriveKey(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, derivedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise<CryptoKey>;\n    /**\n     * The **`deriveBits()`** method of the key.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits)\n     */\n    deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, length?: number | null): Promise<ArrayBuffer>;\n    /**\n     * The **`importKey()`** method of the SubtleCrypto interface imports a key: that is, it takes as input a key in an external, portable format and gives you a CryptoKey object that you can use in the Web Crypto API.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey)\n     */\n    importKey(format: string, keyData: (ArrayBuffer | ArrayBufferView) | JsonWebKey, algorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise<CryptoKey>;\n    /**\n     * The **`exportKey()`** method of the SubtleCrypto interface exports a key: that is, it takes as input a CryptoKey object and gives you the key in an external, portable format.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey)\n     */\n    exportKey(format: string, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey>;\n    /**\n     * The **`wrapKey()`** method of the SubtleCrypto interface 'wraps' a key.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey)\n     */\n    wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm): Promise<ArrayBuffer>;\n    /**\n     * The **`unwrapKey()`** method of the SubtleCrypto interface 'unwraps' a key.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey)\n     */\n    unwrapKey(format: string, wrappedKey: ArrayBuffer | ArrayBufferView, unwrappingKey: CryptoKey, unwrapAlgorithm: string | SubtleCryptoEncryptAlgorithm, unwrappedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise<CryptoKey>;\n    timingSafeEqual(a: ArrayBuffer | ArrayBufferView, b: ArrayBuffer | ArrayBufferView): boolean;\n}\n/**\n * The **`CryptoKey`** interface of the Web Crypto API represents a cryptographic key obtained from one of the SubtleCrypto methods SubtleCrypto.generateKey, SubtleCrypto.deriveKey, SubtleCrypto.importKey, or SubtleCrypto.unwrapKey.\n * Available only in secure contexts.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey)\n */\ndeclare abstract class CryptoKey {\n    /**\n     * The read-only **`type`** property of the CryptoKey interface indicates which kind of key is represented by the object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type)\n     */\n    readonly type: string;\n    /**\n     * The read-only **`extractable`** property of the CryptoKey interface indicates whether or not the key may be extracted using `SubtleCrypto.exportKey()` or `SubtleCrypto.wrapKey()`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable)\n     */\n    readonly extractable: boolean;\n    /**\n     * The read-only **`algorithm`** property of the CryptoKey interface returns an object describing the algorithm for which this key can be used, and any associated extra parameters.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm)\n     */\n    readonly algorithm: CryptoKeyKeyAlgorithm | CryptoKeyAesKeyAlgorithm | CryptoKeyHmacKeyAlgorithm | CryptoKeyRsaKeyAlgorithm | CryptoKeyEllipticKeyAlgorithm | CryptoKeyArbitraryKeyAlgorithm;\n    /**\n     * The read-only **`usages`** property of the CryptoKey interface indicates what can be done with the key.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages)\n     */\n    readonly usages: string[];\n}\ninterface CryptoKeyPair {\n    publicKey: CryptoKey;\n    privateKey: CryptoKey;\n}\ninterface JsonWebKey {\n    kty: string;\n    use?: string;\n    key_ops?: string[];\n    alg?: string;\n    ext?: boolean;\n    crv?: string;\n    x?: string;\n    y?: string;\n    d?: string;\n    n?: string;\n    e?: string;\n    p?: string;\n    q?: string;\n    dp?: string;\n    dq?: string;\n    qi?: string;\n    oth?: RsaOtherPrimesInfo[];\n    k?: string;\n}\ninterface RsaOtherPrimesInfo {\n    r?: string;\n    d?: string;\n    t?: string;\n}\ninterface SubtleCryptoDeriveKeyAlgorithm {\n    name: string;\n    salt?: (ArrayBuffer | ArrayBufferView);\n    iterations?: number;\n    hash?: (string | SubtleCryptoHashAlgorithm);\n    $public?: CryptoKey;\n    info?: (ArrayBuffer | ArrayBufferView);\n}\ninterface SubtleCryptoEncryptAlgorithm {\n    name: string;\n    iv?: (ArrayBuffer | ArrayBufferView);\n    additionalData?: (ArrayBuffer | ArrayBufferView);\n    tagLength?: number;\n    counter?: (ArrayBuffer | ArrayBufferView);\n    length?: number;\n    label?: (ArrayBuffer | ArrayBufferView);\n}\ninterface SubtleCryptoGenerateKeyAlgorithm {\n    name: string;\n    hash?: (string | SubtleCryptoHashAlgorithm);\n    modulusLength?: number;\n    publicExponent?: (ArrayBuffer | ArrayBufferView);\n    length?: number;\n    namedCurve?: string;\n}\ninterface SubtleCryptoHashAlgorithm {\n    name: string;\n}\ninterface SubtleCryptoImportKeyAlgorithm {\n    name: string;\n    hash?: (string | SubtleCryptoHashAlgorithm);\n    length?: number;\n    namedCurve?: string;\n    compressed?: boolean;\n}\ninterface SubtleCryptoSignAlgorithm {\n    name: string;\n    hash?: (string | SubtleCryptoHashAlgorithm);\n    dataLength?: number;\n    saltLength?: number;\n}\ninterface CryptoKeyKeyAlgorithm {\n    name: string;\n}\ninterface CryptoKeyAesKeyAlgorithm {\n    name: string;\n    length: number;\n}\ninterface CryptoKeyHmacKeyAlgorithm {\n    name: string;\n    hash: CryptoKeyKeyAlgorithm;\n    length: number;\n}\ninterface CryptoKeyRsaKeyAlgorithm {\n    name: string;\n    modulusLength: number;\n    publicExponent: ArrayBuffer | ArrayBufferView;\n    hash?: CryptoKeyKeyAlgorithm;\n}\ninterface CryptoKeyEllipticKeyAlgorithm {\n    name: string;\n    namedCurve: string;\n}\ninterface CryptoKeyArbitraryKeyAlgorithm {\n    name: string;\n    hash?: CryptoKeyKeyAlgorithm;\n    namedCurve?: string;\n    length?: number;\n}\ndeclare class DigestStream extends WritableStream<ArrayBuffer | ArrayBufferView> {\n    constructor(algorithm: string | SubtleCryptoHashAlgorithm);\n    readonly digest: Promise<ArrayBuffer>;\n    get bytesWritten(): number | bigint;\n}\n/**\n * The **`TextDecoder`** interface represents a decoder for a specific text encoding, such as `UTF-8`, `ISO-8859-2`, `KOI8-R`, `GBK`, etc.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder)\n */\ndeclare class TextDecoder {\n    constructor(label?: string, options?: TextDecoderConstructorOptions);\n    /**\n     * The **`TextDecoder.decode()`** method returns a string containing text decoded from the buffer passed as a parameter.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode)\n     */\n    decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string;\n    get encoding(): string;\n    get fatal(): boolean;\n    get ignoreBOM(): boolean;\n}\n/**\n * The **`TextEncoder`** interface takes a stream of code points as input and emits a stream of UTF-8 bytes.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder)\n */\ndeclare class TextEncoder {\n    constructor();\n    /**\n     * The **`TextEncoder.encode()`** method takes a string as input, and returns a Global_Objects/Uint8Array containing the text given in parameters encoded with the specific method for that TextEncoder object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encode)\n     */\n    encode(input?: string): Uint8Array;\n    /**\n     * The **`TextEncoder.encodeInto()`** method takes a string to encode and a destination Uint8Array to put resulting UTF-8 encoded text into, and returns a dictionary object indicating the progress of the encoding.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encodeInto)\n     */\n    encodeInto(input: string, buffer: Uint8Array): TextEncoderEncodeIntoResult;\n    get encoding(): string;\n}\ninterface TextDecoderConstructorOptions {\n    fatal: boolean;\n    ignoreBOM: boolean;\n}\ninterface TextDecoderDecodeOptions {\n    stream: boolean;\n}\ninterface TextEncoderEncodeIntoResult {\n    read: number;\n    written: number;\n}\n/**\n * The **`ErrorEvent`** interface represents events providing information related to errors in scripts or in files.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent)\n */\ndeclare class ErrorEvent extends Event {\n    constructor(type: string, init?: ErrorEventErrorEventInit);\n    /**\n     * The **`filename`** read-only property of the ErrorEvent interface returns a string containing the name of the script file in which the error occurred.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/filename)\n     */\n    get filename(): string;\n    /**\n     * The **`message`** read-only property of the ErrorEvent interface returns a string containing a human-readable error message describing the problem.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/message)\n     */\n    get message(): string;\n    /**\n     * The **`lineno`** read-only property of the ErrorEvent interface returns an integer containing the line number of the script file on which the error occurred.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/lineno)\n     */\n    get lineno(): number;\n    /**\n     * The **`colno`** read-only property of the ErrorEvent interface returns an integer containing the column number of the script file on which the error occurred.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/colno)\n     */\n    get colno(): number;\n    /**\n     * The **`error`** read-only property of the ErrorEvent interface returns a JavaScript value, such as an Error or DOMException, representing the error associated with this event.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/error)\n     */\n    get error(): any;\n}\ninterface ErrorEventErrorEventInit {\n    message?: string;\n    filename?: string;\n    lineno?: number;\n    colno?: number;\n    error?: any;\n}\n/**\n * The **`MessageEvent`** interface represents a message received by a target object.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent)\n */\ndeclare class MessageEvent extends Event {\n    constructor(type: string, initializer: MessageEventInit);\n    /**\n     * The **`data`** read-only property of the The data sent by the message emitter; this can be any data type, depending on what originated this event.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data)\n     */\n    readonly data: any;\n    /**\n     * The **`origin`** read-only property of the origin of the message emitter.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/origin)\n     */\n    readonly origin: string | null;\n    /**\n     * The **`lastEventId`** read-only property of the unique ID for the event.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/lastEventId)\n     */\n    readonly lastEventId: string;\n    /**\n     * The **`source`** read-only property of the a WindowProxy, MessagePort, or a `MessageEventSource` (which can be a WindowProxy, message emitter.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/source)\n     */\n    readonly source: MessagePort | null;\n    /**\n     * The **`ports`** read-only property of the containing all MessagePort objects sent with the message, in order.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/ports)\n     */\n    readonly ports: MessagePort[];\n}\ninterface MessageEventInit {\n    data: ArrayBuffer | string;\n}\n/**\n * The **`PromiseRejectionEvent`** interface represents events which are sent to the global script context when JavaScript Promises are rejected.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent)\n */\ndeclare abstract class PromiseRejectionEvent extends Event {\n    /**\n     * The PromiseRejectionEvent interface's **`promise`** read-only property indicates the JavaScript rejected.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/promise)\n     */\n    readonly promise: Promise<any>;\n    /**\n     * The PromiseRejectionEvent **`reason`** read-only property is any JavaScript value or Object which provides the reason passed into Promise.reject().\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/reason)\n     */\n    readonly reason: any;\n}\n/**\n * The **`FormData`** interface provides a way to construct a set of key/value pairs representing form fields and their values, which can be sent using the Window/fetch, XMLHttpRequest.send() or navigator.sendBeacon() methods.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData)\n */\ndeclare class FormData {\n    constructor();\n    /**\n     * The **`append()`** method of the FormData interface appends a new value onto an existing key inside a `FormData` object, or adds the key if it does not already exist.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append)\n     */\n    append(name: string, value: string): void;\n    /**\n     * The **`append()`** method of the FormData interface appends a new value onto an existing key inside a `FormData` object, or adds the key if it does not already exist.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append)\n     */\n    append(name: string, value: Blob, filename?: string): void;\n    /**\n     * The **`delete()`** method of the FormData interface deletes a key and its value(s) from a `FormData` object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/delete)\n     */\n    delete(name: string): void;\n    /**\n     * The **`get()`** method of the FormData interface returns the first value associated with a given key from within a `FormData` object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get)\n     */\n    get(name: string): (File | string) | null;\n    /**\n     * The **`getAll()`** method of the FormData interface returns all the values associated with a given key from within a `FormData` object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/getAll)\n     */\n    getAll(name: string): (File | string)[];\n    /**\n     * The **`has()`** method of the FormData interface returns whether a `FormData` object contains a certain key.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/has)\n     */\n    has(name: string): boolean;\n    /**\n     * The **`set()`** method of the FormData interface sets a new value for an existing key inside a `FormData` object, or adds the key/value if it does not already exist.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set)\n     */\n    set(name: string, value: string): void;\n    /**\n     * The **`set()`** method of the FormData interface sets a new value for an existing key inside a `FormData` object, or adds the key/value if it does not already exist.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set)\n     */\n    set(name: string, value: Blob, filename?: string): void;\n    /* Returns an array of key, value pairs for every entry in the list. */\n    entries(): IterableIterator<[\n        key: string,\n        value: File | string\n    ]>;\n    /* Returns a list of keys in the list. */\n    keys(): IterableIterator<string>;\n    /* Returns a list of values in the list. */\n    values(): IterableIterator<(File | string)>;\n    forEach<This = unknown>(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void;\n    [Symbol.iterator](): IterableIterator<[\n        key: string,\n        value: File | string\n    ]>;\n}\ninterface ContentOptions {\n    html?: boolean;\n}\ndeclare class HTMLRewriter {\n    constructor();\n    on(selector: string, handlers: HTMLRewriterElementContentHandlers): HTMLRewriter;\n    onDocument(handlers: HTMLRewriterDocumentContentHandlers): HTMLRewriter;\n    transform(response: Response): Response;\n}\ninterface HTMLRewriterElementContentHandlers {\n    element?(element: Element): void | Promise<void>;\n    comments?(comment: Comment): void | Promise<void>;\n    text?(element: Text): void | Promise<void>;\n}\ninterface HTMLRewriterDocumentContentHandlers {\n    doctype?(doctype: Doctype): void | Promise<void>;\n    comments?(comment: Comment): void | Promise<void>;\n    text?(text: Text): void | Promise<void>;\n    end?(end: DocumentEnd): void | Promise<void>;\n}\ninterface Doctype {\n    readonly name: string | null;\n    readonly publicId: string | null;\n    readonly systemId: string | null;\n}\ninterface Element {\n    tagName: string;\n    readonly attributes: IterableIterator<string[]>;\n    readonly removed: boolean;\n    readonly namespaceURI: string;\n    getAttribute(name: string): string | null;\n    hasAttribute(name: string): boolean;\n    setAttribute(name: string, value: string): Element;\n    removeAttribute(name: string): Element;\n    before(content: string | ReadableStream | Response, options?: ContentOptions): Element;\n    after(content: string | ReadableStream | Response, options?: ContentOptions): Element;\n    prepend(content: string | ReadableStream | Response, options?: ContentOptions): Element;\n    append(content: string | ReadableStream | Response, options?: ContentOptions): Element;\n    replace(content: string | ReadableStream | Response, options?: ContentOptions): Element;\n    remove(): Element;\n    removeAndKeepContent(): Element;\n    setInnerContent(content: string | ReadableStream | Response, options?: ContentOptions): Element;\n    onEndTag(handler: (tag: EndTag) => void | Promise<void>): void;\n}\ninterface EndTag {\n    name: string;\n    before(content: string | ReadableStream | Response, options?: ContentOptions): EndTag;\n    after(content: string | ReadableStream | Response, options?: ContentOptions): EndTag;\n    remove(): EndTag;\n}\ninterface Comment {\n    text: string;\n    readonly removed: boolean;\n    before(content: string, options?: ContentOptions): Comment;\n    after(content: string, options?: ContentOptions): Comment;\n    replace(content: string, options?: ContentOptions): Comment;\n    remove(): Comment;\n}\ninterface Text {\n    readonly text: string;\n    readonly lastInTextNode: boolean;\n    readonly removed: boolean;\n    before(content: string | ReadableStream | Response, options?: ContentOptions): Text;\n    after(content: string | ReadableStream | Response, options?: ContentOptions): Text;\n    replace(content: string | ReadableStream | Response, options?: ContentOptions): Text;\n    remove(): Text;\n}\ninterface DocumentEnd {\n    append(content: string, options?: ContentOptions): DocumentEnd;\n}\n/**\n * This is the event type for `fetch` events dispatched on the ServiceWorkerGlobalScope.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent)\n */\ndeclare abstract class FetchEvent extends ExtendableEvent {\n    /**\n     * The **`request`** read-only property of the the event handler.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/request)\n     */\n    readonly request: Request;\n    /**\n     * The **`respondWith()`** method of allows you to provide a promise for a Response yourself.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith)\n     */\n    respondWith(promise: Response | Promise<Response>): void;\n    passThroughOnException(): void;\n}\ntype HeadersInit = Headers | Iterable<Iterable<string>> | Record<string, string>;\n/**\n * The **`Headers`** interface of the Fetch API allows you to perform various actions on HTTP request and response headers.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers)\n */\ndeclare class Headers {\n    constructor(init?: HeadersInit);\n    /**\n     * The **`get()`** method of the Headers interface returns a byte string of all the values of a header within a `Headers` object with a given name.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get)\n     */\n    get(name: string): string | null;\n    getAll(name: string): string[];\n    /**\n     * The **`getSetCookie()`** method of the Headers interface returns an array containing the values of all Set-Cookie headers associated with a response.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie)\n     */\n    getSetCookie(): string[];\n    /**\n     * The **`has()`** method of the Headers interface returns a boolean stating whether a `Headers` object contains a certain header.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has)\n     */\n    has(name: string): boolean;\n    /**\n     * The **`set()`** method of the Headers interface sets a new value for an existing header inside a `Headers` object, or adds the header if it does not already exist.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set)\n     */\n    set(name: string, value: string): void;\n    /**\n     * The **`append()`** method of the Headers interface appends a new value onto an existing header inside a `Headers` object, or adds the header if it does not already exist.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append)\n     */\n    append(name: string, value: string): void;\n    /**\n     * The **`delete()`** method of the Headers interface deletes a header from the current `Headers` object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete)\n     */\n    delete(name: string): void;\n    forEach<This = unknown>(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void;\n    /* Returns an iterator allowing to go through all key/value pairs contained in this object. */\n    entries(): IterableIterator<[\n        key: string,\n        value: string\n    ]>;\n    /* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */\n    keys(): IterableIterator<string>;\n    /* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */\n    values(): IterableIterator<string>;\n    [Symbol.iterator](): IterableIterator<[\n        key: string,\n        value: string\n    ]>;\n}\ntype BodyInit = ReadableStream<Uint8Array> | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData;\ndeclare abstract class Body {\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */\n    get body(): ReadableStream | null;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */\n    get bodyUsed(): boolean;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */\n    arrayBuffer(): Promise<ArrayBuffer>;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) */\n    bytes(): Promise<Uint8Array>;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/text) */\n    text(): Promise<string>;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */\n    json<T>(): Promise<T>;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */\n    formData(): Promise<FormData>;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */\n    blob(): Promise<Blob>;\n}\n/**\n * The **`Response`** interface of the Fetch API represents the response to a request.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response)\n */\ndeclare var Response: {\n    prototype: Response;\n    new (body?: BodyInit | null, init?: ResponseInit): Response;\n    error(): Response;\n    redirect(url: string, status?: number): Response;\n    json(any: any, maybeInit?: (ResponseInit | Response)): Response;\n};\n/**\n * The **`Response`** interface of the Fetch API represents the response to a request.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response)\n */\ninterface Response extends Body {\n    /**\n     * The **`clone()`** method of the Response interface creates a clone of a response object, identical in every way, but stored in a different variable.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/clone)\n     */\n    clone(): Response;\n    /**\n     * The **`status`** read-only property of the Response interface contains the HTTP status codes of the response.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status)\n     */\n    status: number;\n    /**\n     * The **`statusText`** read-only property of the Response interface contains the status message corresponding to the HTTP status code in Response.status.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText)\n     */\n    statusText: string;\n    /**\n     * The **`headers`** read-only property of the with the response.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/headers)\n     */\n    headers: Headers;\n    /**\n     * The **`ok`** read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok)\n     */\n    ok: boolean;\n    /**\n     * The **`redirected`** read-only property of the Response interface indicates whether or not the response is the result of a request you made which was redirected.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected)\n     */\n    redirected: boolean;\n    /**\n     * The **`url`** read-only property of the Response interface contains the URL of the response.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url)\n     */\n    url: string;\n    webSocket: WebSocket | null;\n    cf: any | undefined;\n    /**\n     * The **`type`** read-only property of the Response interface contains the type of the response.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/type)\n     */\n    type: \"default\" | \"error\";\n}\ninterface ResponseInit {\n    status?: number;\n    statusText?: string;\n    headers?: HeadersInit;\n    cf?: any;\n    webSocket?: (WebSocket | null);\n    encodeBody?: \"automatic\" | \"manual\";\n}\ntype RequestInfo<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> = Request<CfHostMetadata, Cf> | string;\n/**\n * The **`Request`** interface of the Fetch API represents a resource request.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request)\n */\ndeclare var Request: {\n    prototype: Request;\n    new <CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>>(input: RequestInfo<CfProperties> | URL, init?: RequestInit<Cf>): Request<CfHostMetadata, Cf>;\n};\n/**\n * The **`Request`** interface of the Fetch API represents a resource request.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request)\n */\ninterface Request<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> extends Body {\n    /**\n     * The **`clone()`** method of the Request interface creates a copy of the current `Request` object.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/clone)\n     */\n    clone(): Request<CfHostMetadata, Cf>;\n    /**\n     * The **`method`** read-only property of the `POST`, etc.) A String indicating the method of the request.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method)\n     */\n    method: string;\n    /**\n     * The **`url`** read-only property of the Request interface contains the URL of the request.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url)\n     */\n    url: string;\n    /**\n     * The **`headers`** read-only property of the with the request.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/headers)\n     */\n    headers: Headers;\n    /**\n     * The **`redirect`** read-only property of the Request interface contains the mode for how redirects are handled.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/redirect)\n     */\n    redirect: string;\n    fetcher: Fetcher | null;\n    /**\n     * The read-only **`signal`** property of the Request interface returns the AbortSignal associated with the request.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/signal)\n     */\n    signal: AbortSignal;\n    cf: Cf | undefined;\n    /**\n     * The **`integrity`** read-only property of the Request interface contains the subresource integrity value of the request.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity)\n     */\n    integrity: string;\n    /**\n     * The **`keepalive`** read-only property of the Request interface contains the request's `keepalive` setting (`true` or `false`), which indicates whether the browser will keep the associated request alive if the page that initiated it is unloaded before the request is complete.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/keepalive)\n     */\n    keepalive: boolean;\n    /**\n     * The **`cache`** read-only property of the Request interface contains the cache mode of the request.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/cache)\n     */\n    cache?: \"no-store\";\n}\ninterface RequestInit<Cf = CfProperties> {\n    /* A string to set request's method. */\n    method?: string;\n    /* A Headers object, an object literal, or an array of two-item arrays to set request's headers. */\n    headers?: HeadersInit;\n    /* A BodyInit object or null to set request's body. */\n    body?: BodyInit | null;\n    /* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */\n    redirect?: string;\n    fetcher?: (Fetcher | null);\n    cf?: Cf;\n    /* A string indicating how the request will interact with the browser's cache to set request's cache. */\n    cache?: \"no-store\";\n    /* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */\n    integrity?: string;\n    /* An AbortSignal to set request's signal. */\n    signal?: (AbortSignal | null);\n    encodeResponseBody?: \"automatic\" | \"manual\";\n}\ntype Service<T extends (new (...args: any[]) => Rpc.WorkerEntrypointBranded) | Rpc.WorkerEntrypointBranded | ExportedHandler<any, any, any> | undefined = undefined> = T extends new (...args: any[]) => Rpc.WorkerEntrypointBranded ? Fetcher<InstanceType<T>> : T extends Rpc.WorkerEntrypointBranded ? Fetcher<T> : T extends Exclude<Rpc.EntrypointBranded, Rpc.WorkerEntrypointBranded> ? never : Fetcher<undefined>;\ntype Fetcher<T extends Rpc.EntrypointBranded | undefined = undefined, Reserved extends string = never> = (T extends Rpc.EntrypointBranded ? Rpc.Provider<T, Reserved | \"fetch\" | \"connect\"> : unknown) & {\n    fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;\n    connect(address: SocketAddress | string, options?: SocketOptions): Socket;\n};\ninterface KVNamespaceListKey<Metadata, Key extends string = string> {\n    name: Key;\n    expiration?: number;\n    metadata?: Metadata;\n}\ntype KVNamespaceListResult<Metadata, Key extends string = string> = {\n    list_complete: false;\n    keys: KVNamespaceListKey<Metadata, Key>[];\n    cursor: string;\n    cacheStatus: string | null;\n} | {\n    list_complete: true;\n    keys: KVNamespaceListKey<Metadata, Key>[];\n    cacheStatus: string | null;\n};\ninterface KVNamespace<Key extends string = string> {\n    get(key: Key, options?: Partial<KVNamespaceGetOptions<undefined>>): Promise<string | null>;\n    get(key: Key, type: \"text\"): Promise<string | null>;\n    get<ExpectedValue = unknown>(key: Key, type: \"json\"): Promise<ExpectedValue | null>;\n    get(key: Key, type: \"arrayBuffer\"): Promise<ArrayBuffer | null>;\n    get(key: Key, type: \"stream\"): Promise<ReadableStream | null>;\n    get(key: Key, options?: KVNamespaceGetOptions<\"text\">): Promise<string | null>;\n    get<ExpectedValue = unknown>(key: Key, options?: KVNamespaceGetOptions<\"json\">): Promise<ExpectedValue | null>;\n    get(key: Key, options?: KVNamespaceGetOptions<\"arrayBuffer\">): Promise<ArrayBuffer | null>;\n    get(key: Key, options?: KVNamespaceGetOptions<\"stream\">): Promise<ReadableStream | null>;\n    get(key: Array<Key>, type: \"text\"): Promise<Map<string, string | null>>;\n    get<ExpectedValue = unknown>(key: Array<Key>, type: \"json\"): Promise<Map<string, ExpectedValue | null>>;\n    get(key: Array<Key>, options?: Partial<KVNamespaceGetOptions<undefined>>): Promise<Map<string, string | null>>;\n    get(key: Array<Key>, options?: KVNamespaceGetOptions<\"text\">): Promise<Map<string, string | null>>;\n    get<ExpectedValue = unknown>(key: Array<Key>, options?: KVNamespaceGetOptions<\"json\">): Promise<Map<string, ExpectedValue | null>>;\n    list<Metadata = unknown>(options?: KVNamespaceListOptions): Promise<KVNamespaceListResult<Metadata, Key>>;\n    put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise<void>;\n    getWithMetadata<Metadata = unknown>(key: Key, options?: Partial<KVNamespaceGetOptions<undefined>>): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Key, type: \"text\"): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>;\n    getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: Key, type: \"json\"): Promise<KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Key, type: \"arrayBuffer\"): Promise<KVNamespaceGetWithMetadataResult<ArrayBuffer, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Key, type: \"stream\"): Promise<KVNamespaceGetWithMetadataResult<ReadableStream, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Key, options: KVNamespaceGetOptions<\"text\">): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>;\n    getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: Key, options: KVNamespaceGetOptions<\"json\">): Promise<KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Key, options: KVNamespaceGetOptions<\"arrayBuffer\">): Promise<KVNamespaceGetWithMetadataResult<ArrayBuffer, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Key, options: KVNamespaceGetOptions<\"stream\">): Promise<KVNamespaceGetWithMetadataResult<ReadableStream, Metadata>>;\n    getWithMetadata<Metadata = unknown>(key: Array<Key>, type: \"text\"): Promise<Map<string, KVNamespaceGetWithMetadataResult<string, Metadata>>>;\n    getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: Array<Key>, type: \"json\"): Promise<Map<string, KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>>;\n    getWithMetadata<Metadata = unknown>(key: Array<Key>, options?: Partial<KVNamespaceGetOptions<undefined>>): Promise<Map<string, KVNamespaceGetWithMetadataResult<string, Metadata>>>;\n    getWithMetadata<Metadata = unknown>(key: Array<Key>, options?: KVNamespaceGetOptions<\"text\">): Promise<Map<string, KVNamespaceGetWithMetadataResult<string, Metadata>>>;\n    getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: Array<Key>, options?: KVNamespaceGetOptions<\"json\">): Promise<Map<string, KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>>;\n    delete(key: Key): Promise<void>;\n}\ninterface KVNamespaceListOptions {\n    limit?: number;\n    prefix?: (string | null);\n    cursor?: (string | null);\n}\ninterface KVNamespaceGetOptions<Type> {\n    type: Type;\n    cacheTtl?: number;\n}\ninterface KVNamespacePutOptions {\n    expiration?: number;\n    expirationTtl?: number;\n    metadata?: (any | null);\n}\ninterface KVNamespaceGetWithMetadataResult<Value, Metadata> {\n    value: Value | null;\n    metadata: Metadata | null;\n    cacheStatus: string | null;\n}\ntype QueueContentType = \"text\" | \"bytes\" | \"json\" | \"v8\";\ninterface Queue<Body = unknown> {\n    send(message: Body, options?: QueueSendOptions): Promise<void>;\n    sendBatch(messages: Iterable<MessageSendRequest<Body>>, options?: QueueSendBatchOptions): Promise<void>;\n}\ninterface QueueSendOptions {\n    contentType?: QueueContentType;\n    delaySeconds?: number;\n}\ninterface QueueSendBatchOptions {\n    delaySeconds?: number;\n}\ninterface MessageSendRequest<Body = unknown> {\n    body: Body;\n    contentType?: QueueContentType;\n    delaySeconds?: number;\n}\ninterface QueueRetryOptions {\n    delaySeconds?: number;\n}\ninterface Message<Body = unknown> {\n    readonly id: string;\n    readonly timestamp: Date;\n    readonly body: Body;\n    readonly attempts: number;\n    retry(options?: QueueRetryOptions): void;\n    ack(): void;\n}\ninterface QueueEvent<Body = unknown> extends ExtendableEvent {\n    readonly messages: readonly Message<Body>[];\n    readonly queue: string;\n    retryAll(options?: QueueRetryOptions): void;\n    ackAll(): void;\n}\ninterface MessageBatch<Body = unknown> {\n    readonly messages: readonly Message<Body>[];\n    readonly queue: string;\n    retryAll(options?: QueueRetryOptions): void;\n    ackAll(): void;\n}\ninterface R2Error extends Error {\n    readonly name: string;\n    readonly code: number;\n    readonly message: string;\n    readonly action: string;\n    readonly stack: any;\n}\ninterface R2ListOptions {\n    limit?: number;\n    prefix?: string;\n    cursor?: string;\n    delimiter?: string;\n    startAfter?: string;\n    include?: (\"httpMetadata\" | \"customMetadata\")[];\n}\ndeclare abstract class R2Bucket {\n    head(key: string): Promise<R2Object | null>;\n    get(key: string, options: R2GetOptions & {\n        onlyIf: R2Conditional | Headers;\n    }): Promise<R2ObjectBody | R2Object | null>;\n    get(key: string, options?: R2GetOptions): Promise<R2ObjectBody | null>;\n    put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & {\n        onlyIf: R2Conditional | Headers;\n    }): Promise<R2Object | null>;\n    put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise<R2Object>;\n    createMultipartUpload(key: string, options?: R2MultipartOptions): Promise<R2MultipartUpload>;\n    resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload;\n    delete(keys: string | string[]): Promise<void>;\n    list(options?: R2ListOptions): Promise<R2Objects>;\n}\ninterface R2MultipartUpload {\n    readonly key: string;\n    readonly uploadId: string;\n    uploadPart(partNumber: number, value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, options?: R2UploadPartOptions): Promise<R2UploadedPart>;\n    abort(): Promise<void>;\n    complete(uploadedParts: R2UploadedPart[]): Promise<R2Object>;\n}\ninterface R2UploadedPart {\n    partNumber: number;\n    etag: string;\n}\ndeclare abstract class R2Object {\n    readonly key: string;\n    readonly version: string;\n    readonly size: number;\n    readonly etag: string;\n    readonly httpEtag: string;\n    readonly checksums: R2Checksums;\n    readonly uploaded: Date;\n    readonly httpMetadata?: R2HTTPMetadata;\n    readonly customMetadata?: Record<string, string>;\n    readonly range?: R2Range;\n    readonly storageClass: string;\n    readonly ssecKeyMd5?: string;\n    writeHttpMetadata(headers: Headers): void;\n}\ninterface R2ObjectBody extends R2Object {\n    get body(): ReadableStream;\n    get bodyUsed(): boolean;\n    arrayBuffer(): Promise<ArrayBuffer>;\n    bytes(): Promise<Uint8Array>;\n    text(): Promise<string>;\n    json<T>(): Promise<T>;\n    blob(): Promise<Blob>;\n}\ntype R2Range = {\n    offset: number;\n    length?: number;\n} | {\n    offset?: number;\n    length: number;\n} | {\n    suffix: number;\n};\ninterface R2Conditional {\n    etagMatches?: string;\n    etagDoesNotMatch?: string;\n    uploadedBefore?: Date;\n    uploadedAfter?: Date;\n    secondsGranularity?: boolean;\n}\ninterface R2GetOptions {\n    onlyIf?: (R2Conditional | Headers);\n    range?: (R2Range | Headers);\n    ssecKey?: (ArrayBuffer | string);\n}\ninterface R2PutOptions {\n    onlyIf?: (R2Conditional | Headers);\n    httpMetadata?: (R2HTTPMetadata | Headers);\n    customMetadata?: Record<string, string>;\n    md5?: ((ArrayBuffer | ArrayBufferView) | string);\n    sha1?: ((ArrayBuffer | ArrayBufferView) | string);\n    sha256?: ((ArrayBuffer | ArrayBufferView) | string);\n    sha384?: ((ArrayBuffer | ArrayBufferView) | string);\n    sha512?: ((ArrayBuffer | ArrayBufferView) | string);\n    storageClass?: string;\n    ssecKey?: (ArrayBuffer | string);\n}\ninterface R2MultipartOptions {\n    httpMetadata?: (R2HTTPMetadata | Headers);\n    customMetadata?: Record<string, string>;\n    storageClass?: string;\n    ssecKey?: (ArrayBuffer | string);\n}\ninterface R2Checksums {\n    readonly md5?: ArrayBuffer;\n    readonly sha1?: ArrayBuffer;\n    readonly sha256?: ArrayBuffer;\n    readonly sha384?: ArrayBuffer;\n    readonly sha512?: ArrayBuffer;\n    toJSON(): R2StringChecksums;\n}\ninterface R2StringChecksums {\n    md5?: string;\n    sha1?: string;\n    sha256?: string;\n    sha384?: string;\n    sha512?: string;\n}\ninterface R2HTTPMetadata {\n    contentType?: string;\n    contentLanguage?: string;\n    contentDisposition?: string;\n    contentEncoding?: string;\n    cacheControl?: string;\n    cacheExpiry?: Date;\n}\ntype R2Objects = {\n    objects: R2Object[];\n    delimitedPrefixes: string[];\n} & ({\n    truncated: true;\n    cursor: string;\n} | {\n    truncated: false;\n});\ninterface R2UploadPartOptions {\n    ssecKey?: (ArrayBuffer | string);\n}\ndeclare abstract class ScheduledEvent extends ExtendableEvent {\n    readonly scheduledTime: number;\n    readonly cron: string;\n    noRetry(): void;\n}\ninterface ScheduledController {\n    readonly scheduledTime: number;\n    readonly cron: string;\n    noRetry(): void;\n}\ninterface QueuingStrategy<T = any> {\n    highWaterMark?: (number | bigint);\n    size?: (chunk: T) => number | bigint;\n}\ninterface UnderlyingSink<W = any> {\n    type?: string;\n    start?: (controller: WritableStreamDefaultController) => void | Promise<void>;\n    write?: (chunk: W, controller: WritableStreamDefaultController) => void | Promise<void>;\n    abort?: (reason: any) => void | Promise<void>;\n    close?: () => void | Promise<void>;\n}\ninterface UnderlyingByteSource {\n    type: \"bytes\";\n    autoAllocateChunkSize?: number;\n    start?: (controller: ReadableByteStreamController) => void | Promise<void>;\n    pull?: (controller: ReadableByteStreamController) => void | Promise<void>;\n    cancel?: (reason: any) => void | Promise<void>;\n}\ninterface UnderlyingSource<R = any> {\n    type?: \"\" | undefined;\n    start?: (controller: ReadableStreamDefaultController<R>) => void | Promise<void>;\n    pull?: (controller: ReadableStreamDefaultController<R>) => void | Promise<void>;\n    cancel?: (reason: any) => void | Promise<void>;\n    expectedLength?: (number | bigint);\n}\ninterface Transformer<I = any, O = any> {\n    readableType?: string;\n    writableType?: string;\n    start?: (controller: TransformStreamDefaultController<O>) => void | Promise<void>;\n    transform?: (chunk: I, controller: TransformStreamDefaultController<O>) => void | Promise<void>;\n    flush?: (controller: TransformStreamDefaultController<O>) => void | Promise<void>;\n    cancel?: (reason: any) => void | Promise<void>;\n    expectedLength?: number;\n}\ninterface StreamPipeOptions {\n    preventAbort?: boolean;\n    preventCancel?: boolean;\n    /**\n     * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered.\n     *\n     * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.\n     *\n     * Errors and closures of the source and destination streams propagate as follows:\n     *\n     * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination.\n     *\n     * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source.\n     *\n     * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error.\n     *\n     * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source.\n     *\n     * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set.\n     */\n    preventClose?: boolean;\n    signal?: AbortSignal;\n}\ntype ReadableStreamReadResult<R = any> = {\n    done: false;\n    value: R;\n} | {\n    done: true;\n    value?: undefined;\n};\n/**\n * The `ReadableStream` interface of the Streams API represents a readable stream of byte data.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream)\n */\ninterface ReadableStream<R = any> {\n    /**\n     * The **`locked`** read-only property of the ReadableStream interface returns whether or not the readable stream is locked to a reader.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked)\n     */\n    get locked(): boolean;\n    /**\n     * The **`cancel()`** method of the ReadableStream interface returns a Promise that resolves when the stream is canceled.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel)\n     */\n    cancel(reason?: any): Promise<void>;\n    /**\n     * The **`getReader()`** method of the ReadableStream interface creates a reader and locks the stream to it.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader)\n     */\n    getReader(): ReadableStreamDefaultReader<R>;\n    /**\n     * The **`getReader()`** method of the ReadableStream interface creates a reader and locks the stream to it.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader)\n     */\n    getReader(options: ReadableStreamGetReaderOptions): ReadableStreamBYOBReader;\n    /**\n     * The **`pipeThrough()`** method of the ReadableStream interface provides a chainable way of piping the current stream through a transform stream or any other writable/readable pair.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough)\n     */\n    pipeThrough<T>(transform: ReadableWritablePair<T, R>, options?: StreamPipeOptions): ReadableStream<T>;\n    /**\n     * The **`pipeTo()`** method of the ReadableStream interface pipes the current `ReadableStream` to a given WritableStream and returns a Promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo)\n     */\n    pipeTo(destination: WritableStream<R>, options?: StreamPipeOptions): Promise<void>;\n    /**\n     * The **`tee()`** method of the two-element array containing the two resulting branches as new ReadableStream instances.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee)\n     */\n    tee(): [\n        ReadableStream<R>,\n        ReadableStream<R>\n    ];\n    values(options?: ReadableStreamValuesOptions): AsyncIterableIterator<R>;\n    [Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator<R>;\n}\n/**\n * The `ReadableStream` interface of the Streams API represents a readable stream of byte data.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream)\n */\ndeclare const ReadableStream: {\n    prototype: ReadableStream;\n    new (underlyingSource: UnderlyingByteSource, strategy?: QueuingStrategy<Uint8Array>): ReadableStream<Uint8Array>;\n    new <R = any>(underlyingSource?: UnderlyingSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R>;\n};\n/**\n * The **`ReadableStreamDefaultReader`** interface of the Streams API represents a default reader that can be used to read stream data supplied from a network (such as a fetch request).\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader)\n */\ndeclare class ReadableStreamDefaultReader<R = any> {\n    constructor(stream: ReadableStream);\n    get closed(): Promise<void>;\n    cancel(reason?: any): Promise<void>;\n    /**\n     * The **`read()`** method of the ReadableStreamDefaultReader interface returns a Promise providing access to the next chunk in the stream's internal queue.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read)\n     */\n    read(): Promise<ReadableStreamReadResult<R>>;\n    /**\n     * The **`releaseLock()`** method of the ReadableStreamDefaultReader interface releases the reader's lock on the stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock)\n     */\n    releaseLock(): void;\n}\n/**\n * The `ReadableStreamBYOBReader` interface of the Streams API defines a reader for a ReadableStream that supports zero-copy reading from an underlying byte source.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader)\n */\ndeclare class ReadableStreamBYOBReader {\n    constructor(stream: ReadableStream);\n    get closed(): Promise<void>;\n    cancel(reason?: any): Promise<void>;\n    /**\n     * The **`read()`** method of the ReadableStreamBYOBReader interface is used to read data into a view on a user-supplied buffer from an associated readable byte stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read)\n     */\n    read<T extends ArrayBufferView>(view: T): Promise<ReadableStreamReadResult<T>>;\n    /**\n     * The **`releaseLock()`** method of the ReadableStreamBYOBReader interface releases the reader's lock on the stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock)\n     */\n    releaseLock(): void;\n    readAtLeast<T extends ArrayBufferView>(minElements: number, view: T): Promise<ReadableStreamReadResult<T>>;\n}\ninterface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions {\n    min?: number;\n}\ninterface ReadableStreamGetReaderOptions {\n    /**\n     * Creates a ReadableStreamBYOBReader and locks the stream to the new reader.\n     *\n     * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle \"bring your own buffer\" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation.\n     */\n    mode: \"byob\";\n}\n/**\n * The **`ReadableStreamBYOBRequest`** interface of the Streams API represents a 'pull request' for data from an underlying source that will made as a zero-copy transfer to a consumer (bypassing the stream's internal queues).\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest)\n */\ndeclare abstract class ReadableStreamBYOBRequest {\n    /**\n     * The **`view`** getter property of the ReadableStreamBYOBRequest interface returns the current view.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/view)\n     */\n    get view(): Uint8Array | null;\n    /**\n     * The **`respond()`** method of the ReadableStreamBYOBRequest interface is used to signal to the associated readable byte stream that the specified number of bytes were written into the ReadableStreamBYOBRequest.view.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respond)\n     */\n    respond(bytesWritten: number): void;\n    /**\n     * The **`respondWithNewView()`** method of the ReadableStreamBYOBRequest interface specifies a new view that the consumer of the associated readable byte stream should write to instead of ReadableStreamBYOBRequest.view.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respondWithNewView)\n     */\n    respondWithNewView(view: ArrayBuffer | ArrayBufferView): void;\n    get atLeast(): number | null;\n}\n/**\n * The **`ReadableStreamDefaultController`** interface of the Streams API represents a controller allowing control of a ReadableStream's state and internal queue.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController)\n */\ndeclare abstract class ReadableStreamDefaultController<R = any> {\n    /**\n     * The **`desiredSize`** read-only property of the required to fill the stream's internal queue.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/desiredSize)\n     */\n    get desiredSize(): number | null;\n    /**\n     * The **`close()`** method of the ReadableStreamDefaultController interface closes the associated stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/close)\n     */\n    close(): void;\n    /**\n     * The **`enqueue()`** method of the ```js-nolint enqueue(chunk) ``` - `chunk` - : The chunk to enqueue.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/enqueue)\n     */\n    enqueue(chunk?: R): void;\n    /**\n     * The **`error()`** method of the with the associated stream to error.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/error)\n     */\n    error(reason: any): void;\n}\n/**\n * The **`ReadableByteStreamController`** interface of the Streams API represents a controller for a readable byte stream.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController)\n */\ndeclare abstract class ReadableByteStreamController {\n    /**\n     * The **`byobRequest`** read-only property of the ReadableByteStreamController interface returns the current BYOB request, or `null` if there are no pending requests.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/byobRequest)\n     */\n    get byobRequest(): ReadableStreamBYOBRequest | null;\n    /**\n     * The **`desiredSize`** read-only property of the ReadableByteStreamController interface returns the number of bytes required to fill the stream's internal queue to its 'desired size'.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/desiredSize)\n     */\n    get desiredSize(): number | null;\n    /**\n     * The **`close()`** method of the ReadableByteStreamController interface closes the associated stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/close)\n     */\n    close(): void;\n    /**\n     * The **`enqueue()`** method of the ReadableByteStreamController interface enqueues a given chunk on the associated readable byte stream (the chunk is copied into the stream's internal queues).\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/enqueue)\n     */\n    enqueue(chunk: ArrayBuffer | ArrayBufferView): void;\n    /**\n     * The **`error()`** method of the ReadableByteStreamController interface causes any future interactions with the associated stream to error with the specified reason.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/error)\n     */\n    error(reason: any): void;\n}\n/**\n * The **`WritableStreamDefaultController`** interface of the Streams API represents a controller allowing control of a WritableStream's state.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController)\n */\ndeclare abstract class WritableStreamDefaultController {\n    /**\n     * The read-only **`signal`** property of the WritableStreamDefaultController interface returns the AbortSignal associated with the controller.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/signal)\n     */\n    get signal(): AbortSignal;\n    /**\n     * The **`error()`** method of the with the associated stream to error.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/error)\n     */\n    error(reason?: any): void;\n}\n/**\n * The **`TransformStreamDefaultController`** interface of the Streams API provides methods to manipulate the associated ReadableStream and WritableStream.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController)\n */\ndeclare abstract class TransformStreamDefaultController<O = any> {\n    /**\n     * The **`desiredSize`** read-only property of the TransformStreamDefaultController interface returns the desired size to fill the queue of the associated ReadableStream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/desiredSize)\n     */\n    get desiredSize(): number | null;\n    /**\n     * The **`enqueue()`** method of the TransformStreamDefaultController interface enqueues the given chunk in the readable side of the stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/enqueue)\n     */\n    enqueue(chunk?: O): void;\n    /**\n     * The **`error()`** method of the TransformStreamDefaultController interface errors both sides of the stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/error)\n     */\n    error(reason: any): void;\n    /**\n     * The **`terminate()`** method of the TransformStreamDefaultController interface closes the readable side and errors the writable side of the stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/terminate)\n     */\n    terminate(): void;\n}\ninterface ReadableWritablePair<R = any, W = any> {\n    readable: ReadableStream<R>;\n    /**\n     * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use.\n     *\n     * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.\n     */\n    writable: WritableStream<W>;\n}\n/**\n * The **`WritableStream`** interface of the Streams API provides a standard abstraction for writing streaming data to a destination, known as a sink.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream)\n */\ndeclare class WritableStream<W = any> {\n    constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: QueuingStrategy);\n    /**\n     * The **`locked`** read-only property of the WritableStream interface returns a boolean indicating whether the `WritableStream` is locked to a writer.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked)\n     */\n    get locked(): boolean;\n    /**\n     * The **`abort()`** method of the WritableStream interface aborts the stream, signaling that the producer can no longer successfully write to the stream and it is to be immediately moved to an error state, with any queued writes discarded.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort)\n     */\n    abort(reason?: any): Promise<void>;\n    /**\n     * The **`close()`** method of the WritableStream interface closes the associated stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close)\n     */\n    close(): Promise<void>;\n    /**\n     * The **`getWriter()`** method of the WritableStream interface returns a new instance of WritableStreamDefaultWriter and locks the stream to that instance.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter)\n     */\n    getWriter(): WritableStreamDefaultWriter<W>;\n}\n/**\n * The **`WritableStreamDefaultWriter`** interface of the Streams API is the object returned by WritableStream.getWriter() and once created locks the writer to the `WritableStream` ensuring that no other streams can write to the underlying sink.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter)\n */\ndeclare class WritableStreamDefaultWriter<W = any> {\n    constructor(stream: WritableStream);\n    /**\n     * The **`closed`** read-only property of the the stream errors or the writer's lock is released.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/closed)\n     */\n    get closed(): Promise<void>;\n    /**\n     * The **`ready`** read-only property of the that resolves when the desired size of the stream's internal queue transitions from non-positive to positive, signaling that it is no longer applying backpressure.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/ready)\n     */\n    get ready(): Promise<void>;\n    /**\n     * The **`desiredSize`** read-only property of the to fill the stream's internal queue.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/desiredSize)\n     */\n    get desiredSize(): number | null;\n    /**\n     * The **`abort()`** method of the the producer can no longer successfully write to the stream and it is to be immediately moved to an error state, with any queued writes discarded.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/abort)\n     */\n    abort(reason?: any): Promise<void>;\n    /**\n     * The **`close()`** method of the stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/close)\n     */\n    close(): Promise<void>;\n    /**\n     * The **`write()`** method of the operation.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/write)\n     */\n    write(chunk?: W): Promise<void>;\n    /**\n     * The **`releaseLock()`** method of the corresponding stream.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/releaseLock)\n     */\n    releaseLock(): void;\n}\n/**\n * The **`TransformStream`** interface of the Streams API represents a concrete implementation of the pipe chain _transform stream_ concept.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream)\n */\ndeclare class TransformStream<I = any, O = any> {\n    constructor(transformer?: Transformer<I, O>, writableStrategy?: QueuingStrategy<I>, readableStrategy?: QueuingStrategy<O>);\n    /**\n     * The **`readable`** read-only property of the TransformStream interface returns the ReadableStream instance controlled by this `TransformStream`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/readable)\n     */\n    get readable(): ReadableStream<O>;\n    /**\n     * The **`writable`** read-only property of the TransformStream interface returns the WritableStream instance controlled by this `TransformStream`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/writable)\n     */\n    get writable(): WritableStream<I>;\n}\ndeclare class FixedLengthStream extends IdentityTransformStream {\n    constructor(expectedLength: number | bigint, queuingStrategy?: IdentityTransformStreamQueuingStrategy);\n}\ndeclare class IdentityTransformStream extends TransformStream<ArrayBuffer | ArrayBufferView, Uint8Array> {\n    constructor(queuingStrategy?: IdentityTransformStreamQueuingStrategy);\n}\ninterface IdentityTransformStreamQueuingStrategy {\n    highWaterMark?: (number | bigint);\n}\ninterface ReadableStreamValuesOptions {\n    preventCancel?: boolean;\n}\n/**\n * The **`CompressionStream`** interface of the Compression Streams API is an API for compressing a stream of data.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CompressionStream)\n */\ndeclare class CompressionStream extends TransformStream<ArrayBuffer | ArrayBufferView, Uint8Array> {\n    constructor(format: \"gzip\" | \"deflate\" | \"deflate-raw\");\n}\n/**\n * The **`DecompressionStream`** interface of the Compression Streams API is an API for decompressing a stream of data.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DecompressionStream)\n */\ndeclare class DecompressionStream extends TransformStream<ArrayBuffer | ArrayBufferView, Uint8Array> {\n    constructor(format: \"gzip\" | \"deflate\" | \"deflate-raw\");\n}\n/**\n * The **`TextEncoderStream`** interface of the Encoding API converts a stream of strings into bytes in the UTF-8 encoding.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoderStream)\n */\ndeclare class TextEncoderStream extends TransformStream<string, Uint8Array> {\n    constructor();\n    get encoding(): string;\n}\n/**\n * The **`TextDecoderStream`** interface of the Encoding API converts a stream of text in a binary encoding, such as UTF-8 etc., to a stream of strings.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoderStream)\n */\ndeclare class TextDecoderStream extends TransformStream<ArrayBuffer | ArrayBufferView, string> {\n    constructor(label?: string, options?: TextDecoderStreamTextDecoderStreamInit);\n    get encoding(): string;\n    get fatal(): boolean;\n    get ignoreBOM(): boolean;\n}\ninterface TextDecoderStreamTextDecoderStreamInit {\n    fatal?: boolean;\n    ignoreBOM?: boolean;\n}\n/**\n * The **`ByteLengthQueuingStrategy`** interface of the Streams API provides a built-in byte length queuing strategy that can be used when constructing streams.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy)\n */\ndeclare class ByteLengthQueuingStrategy implements QueuingStrategy<ArrayBufferView> {\n    constructor(init: QueuingStrategyInit);\n    /**\n     * The read-only **`ByteLengthQueuingStrategy.highWaterMark`** property returns the total number of bytes that can be contained in the internal queue before backpressure is applied.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/highWaterMark)\n     */\n    get highWaterMark(): number;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/size) */\n    get size(): (chunk?: any) => number;\n}\n/**\n * The **`CountQueuingStrategy`** interface of the Streams API provides a built-in chunk counting queuing strategy that can be used when constructing streams.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy)\n */\ndeclare class CountQueuingStrategy implements QueuingStrategy {\n    constructor(init: QueuingStrategyInit);\n    /**\n     * The read-only **`CountQueuingStrategy.highWaterMark`** property returns the total number of chunks that can be contained in the internal queue before backpressure is applied.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/highWaterMark)\n     */\n    get highWaterMark(): number;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/size) */\n    get size(): (chunk?: any) => number;\n}\ninterface QueuingStrategyInit {\n    /**\n     * Creates a new ByteLengthQueuingStrategy with the provided high water mark.\n     *\n     * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw.\n     */\n    highWaterMark: number;\n}\ninterface ScriptVersion {\n    id?: string;\n    tag?: string;\n    message?: string;\n}\ndeclare abstract class TailEvent extends ExtendableEvent {\n    readonly events: TraceItem[];\n    readonly traces: TraceItem[];\n}\ninterface TraceItem {\n    readonly event: (TraceItemFetchEventInfo | TraceItemJsRpcEventInfo | TraceItemScheduledEventInfo | TraceItemAlarmEventInfo | TraceItemQueueEventInfo | TraceItemEmailEventInfo | TraceItemTailEventInfo | TraceItemCustomEventInfo | TraceItemHibernatableWebSocketEventInfo) | null;\n    readonly eventTimestamp: number | null;\n    readonly logs: TraceLog[];\n    readonly exceptions: TraceException[];\n    readonly diagnosticsChannelEvents: TraceDiagnosticChannelEvent[];\n    readonly scriptName: string | null;\n    readonly entrypoint?: string;\n    readonly scriptVersion?: ScriptVersion;\n    readonly dispatchNamespace?: string;\n    readonly scriptTags?: string[];\n    readonly durableObjectId?: string;\n    readonly outcome: string;\n    readonly executionModel: string;\n    readonly truncated: boolean;\n    readonly cpuTime: number;\n    readonly wallTime: number;\n}\ninterface TraceItemAlarmEventInfo {\n    readonly scheduledTime: Date;\n}\ninterface TraceItemCustomEventInfo {\n}\ninterface TraceItemScheduledEventInfo {\n    readonly scheduledTime: number;\n    readonly cron: string;\n}\ninterface TraceItemQueueEventInfo {\n    readonly queue: string;\n    readonly batchSize: number;\n}\ninterface TraceItemEmailEventInfo {\n    readonly mailFrom: string;\n    readonly rcptTo: string;\n    readonly rawSize: number;\n}\ninterface TraceItemTailEventInfo {\n    readonly consumedEvents: TraceItemTailEventInfoTailItem[];\n}\ninterface TraceItemTailEventInfoTailItem {\n    readonly scriptName: string | null;\n}\ninterface TraceItemFetchEventInfo {\n    readonly response?: TraceItemFetchEventInfoResponse;\n    readonly request: TraceItemFetchEventInfoRequest;\n}\ninterface TraceItemFetchEventInfoRequest {\n    readonly cf?: any;\n    readonly headers: Record<string, string>;\n    readonly method: string;\n    readonly url: string;\n    getUnredacted(): TraceItemFetchEventInfoRequest;\n}\ninterface TraceItemFetchEventInfoResponse {\n    readonly status: number;\n}\ninterface TraceItemJsRpcEventInfo {\n    readonly rpcMethod: string;\n}\ninterface TraceItemHibernatableWebSocketEventInfo {\n    readonly getWebSocketEvent: TraceItemHibernatableWebSocketEventInfoMessage | TraceItemHibernatableWebSocketEventInfoClose | TraceItemHibernatableWebSocketEventInfoError;\n}\ninterface TraceItemHibernatableWebSocketEventInfoMessage {\n    readonly webSocketEventType: string;\n}\ninterface TraceItemHibernatableWebSocketEventInfoClose {\n    readonly webSocketEventType: string;\n    readonly code: number;\n    readonly wasClean: boolean;\n}\ninterface TraceItemHibernatableWebSocketEventInfoError {\n    readonly webSocketEventType: string;\n}\ninterface TraceLog {\n    readonly timestamp: number;\n    readonly level: string;\n    readonly message: any;\n}\ninterface TraceException {\n    readonly timestamp: number;\n    readonly message: string;\n    readonly name: string;\n    readonly stack?: string;\n}\ninterface TraceDiagnosticChannelEvent {\n    readonly timestamp: number;\n    readonly channel: string;\n    readonly message: any;\n}\ninterface TraceMetrics {\n    readonly cpuTime: number;\n    readonly wallTime: number;\n}\ninterface UnsafeTraceMetrics {\n    fromTrace(item: TraceItem): TraceMetrics;\n}\n/**\n * The **`URL`** interface is used to parse, construct, normalize, and encode URL.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL)\n */\ndeclare class URL {\n    constructor(url: string | URL, base?: string | URL);\n    /**\n     * The **`origin`** read-only property of the URL interface returns a string containing the Unicode serialization of the origin of the represented URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/origin)\n     */\n    get origin(): string;\n    /**\n     * The **`href`** property of the URL interface is a string containing the whole URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href)\n     */\n    get href(): string;\n    /**\n     * The **`href`** property of the URL interface is a string containing the whole URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href)\n     */\n    set href(value: string);\n    /**\n     * The **`protocol`** property of the URL interface is a string containing the protocol or scheme of the URL, including the final `':'`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol)\n     */\n    get protocol(): string;\n    /**\n     * The **`protocol`** property of the URL interface is a string containing the protocol or scheme of the URL, including the final `':'`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol)\n     */\n    set protocol(value: string);\n    /**\n     * The **`username`** property of the URL interface is a string containing the username component of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username)\n     */\n    get username(): string;\n    /**\n     * The **`username`** property of the URL interface is a string containing the username component of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username)\n     */\n    set username(value: string);\n    /**\n     * The **`password`** property of the URL interface is a string containing the password component of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password)\n     */\n    get password(): string;\n    /**\n     * The **`password`** property of the URL interface is a string containing the password component of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password)\n     */\n    set password(value: string);\n    /**\n     * The **`host`** property of the URL interface is a string containing the host, which is the URL.hostname, and then, if the port of the URL is nonempty, a `':'`, followed by the URL.port of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host)\n     */\n    get host(): string;\n    /**\n     * The **`host`** property of the URL interface is a string containing the host, which is the URL.hostname, and then, if the port of the URL is nonempty, a `':'`, followed by the URL.port of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host)\n     */\n    set host(value: string);\n    /**\n     * The **`hostname`** property of the URL interface is a string containing either the domain name or IP address of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname)\n     */\n    get hostname(): string;\n    /**\n     * The **`hostname`** property of the URL interface is a string containing either the domain name or IP address of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname)\n     */\n    set hostname(value: string);\n    /**\n     * The **`port`** property of the URL interface is a string containing the port number of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port)\n     */\n    get port(): string;\n    /**\n     * The **`port`** property of the URL interface is a string containing the port number of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port)\n     */\n    set port(value: string);\n    /**\n     * The **`pathname`** property of the URL interface represents a location in a hierarchical structure.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname)\n     */\n    get pathname(): string;\n    /**\n     * The **`pathname`** property of the URL interface represents a location in a hierarchical structure.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname)\n     */\n    set pathname(value: string);\n    /**\n     * The **`search`** property of the URL interface is a search string, also called a _query string_, that is a string containing a `'?'` followed by the parameters of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search)\n     */\n    get search(): string;\n    /**\n     * The **`search`** property of the URL interface is a search string, also called a _query string_, that is a string containing a `'?'` followed by the parameters of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search)\n     */\n    set search(value: string);\n    /**\n     * The **`hash`** property of the URL interface is a string containing a `'#'` followed by the fragment identifier of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash)\n     */\n    get hash(): string;\n    /**\n     * The **`hash`** property of the URL interface is a string containing a `'#'` followed by the fragment identifier of the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash)\n     */\n    set hash(value: string);\n    /**\n     * The **`searchParams`** read-only property of the access to the [MISSING: httpmethod('GET')] decoded query arguments contained in the URL.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/searchParams)\n     */\n    get searchParams(): URLSearchParams;\n    /**\n     * The **`toJSON()`** method of the URL interface returns a string containing a serialized version of the URL, although in practice it seems to have the same effect as ```js-nolint toJSON() ``` None.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/toJSON)\n     */\n    toJSON(): string;\n    /*function toString() { [native code] }*/\n    toString(): string;\n    /**\n     * The **`URL.canParse()`** static method of the URL interface returns a boolean indicating whether or not an absolute URL, or a relative URL combined with a base URL, are parsable and valid.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/canParse_static)\n     */\n    static canParse(url: string, base?: string): boolean;\n    /**\n     * The **`URL.parse()`** static method of the URL interface returns a newly created URL object representing the URL defined by the parameters.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/parse_static)\n     */\n    static parse(url: string, base?: string): URL | null;\n    /**\n     * The **`createObjectURL()`** static method of the URL interface creates a string containing a URL representing the object given in the parameter.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static)\n     */\n    static createObjectURL(object: File | Blob): string;\n    /**\n     * The **`revokeObjectURL()`** static method of the URL interface releases an existing object URL which was previously created by calling Call this method when you've finished using an object URL to let the browser know not to keep the reference to the file any longer.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static)\n     */\n    static revokeObjectURL(object_url: string): void;\n}\n/**\n * The **`URLSearchParams`** interface defines utility methods to work with the query string of a URL.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams)\n */\ndeclare class URLSearchParams {\n    constructor(init?: (Iterable<Iterable<string>> | Record<string, string> | string));\n    /**\n     * The **`size`** read-only property of the URLSearchParams interface indicates the total number of search parameter entries.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/size)\n     */\n    get size(): number;\n    /**\n     * The **`append()`** method of the URLSearchParams interface appends a specified key/value pair as a new search parameter.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append)\n     */\n    append(name: string, value: string): void;\n    /**\n     * The **`delete()`** method of the URLSearchParams interface deletes specified parameters and their associated value(s) from the list of all search parameters.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete)\n     */\n    delete(name: string, value?: string): void;\n    /**\n     * The **`get()`** method of the URLSearchParams interface returns the first value associated to the given search parameter.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get)\n     */\n    get(name: string): string | null;\n    /**\n     * The **`getAll()`** method of the URLSearchParams interface returns all the values associated with a given search parameter as an array.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll)\n     */\n    getAll(name: string): string[];\n    /**\n     * The **`has()`** method of the URLSearchParams interface returns a boolean value that indicates whether the specified parameter is in the search parameters.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has)\n     */\n    has(name: string, value?: string): boolean;\n    /**\n     * The **`set()`** method of the URLSearchParams interface sets the value associated with a given search parameter to the given value.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set)\n     */\n    set(name: string, value: string): void;\n    /**\n     * The **`URLSearchParams.sort()`** method sorts all key/value pairs contained in this object in place and returns `undefined`.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/sort)\n     */\n    sort(): void;\n    /* Returns an array of key, value pairs for every entry in the search params. */\n    entries(): IterableIterator<[\n        key: string,\n        value: string\n    ]>;\n    /* Returns a list of keys in the search params. */\n    keys(): IterableIterator<string>;\n    /* Returns a list of values in the search params. */\n    values(): IterableIterator<string>;\n    forEach<This = unknown>(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void;\n    /*function toString() { [native code] }*/\n    toString(): string;\n    [Symbol.iterator](): IterableIterator<[\n        key: string,\n        value: string\n    ]>;\n}\ndeclare class URLPattern {\n    constructor(input?: (string | URLPatternInit), baseURL?: (string | URLPatternOptions), patternOptions?: URLPatternOptions);\n    get protocol(): string;\n    get username(): string;\n    get password(): string;\n    get hostname(): string;\n    get port(): string;\n    get pathname(): string;\n    get search(): string;\n    get hash(): string;\n    get hasRegExpGroups(): boolean;\n    test(input?: (string | URLPatternInit), baseURL?: string): boolean;\n    exec(input?: (string | URLPatternInit), baseURL?: string): URLPatternResult | null;\n}\ninterface URLPatternInit {\n    protocol?: string;\n    username?: string;\n    password?: string;\n    hostname?: string;\n    port?: string;\n    pathname?: string;\n    search?: string;\n    hash?: string;\n    baseURL?: string;\n}\ninterface URLPatternComponentResult {\n    input: string;\n    groups: Record<string, string>;\n}\ninterface URLPatternResult {\n    inputs: (string | URLPatternInit)[];\n    protocol: URLPatternComponentResult;\n    username: URLPatternComponentResult;\n    password: URLPatternComponentResult;\n    hostname: URLPatternComponentResult;\n    port: URLPatternComponentResult;\n    pathname: URLPatternComponentResult;\n    search: URLPatternComponentResult;\n    hash: URLPatternComponentResult;\n}\ninterface URLPatternOptions {\n    ignoreCase?: boolean;\n}\n/**\n * A `CloseEvent` is sent to clients using WebSockets when the connection is closed.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent)\n */\ndeclare class CloseEvent extends Event {\n    constructor(type: string, initializer?: CloseEventInit);\n    /**\n     * The **`code`** read-only property of the CloseEvent interface returns a WebSocket connection close code indicating the reason the connection was closed.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/code)\n     */\n    readonly code: number;\n    /**\n     * The **`reason`** read-only property of the CloseEvent interface returns the WebSocket connection close reason the server gave for closing the connection; that is, a concise human-readable prose explanation for the closure.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/reason)\n     */\n    readonly reason: string;\n    /**\n     * The **`wasClean`** read-only property of the CloseEvent interface returns `true` if the connection closed cleanly.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/wasClean)\n     */\n    readonly wasClean: boolean;\n}\ninterface CloseEventInit {\n    code?: number;\n    reason?: string;\n    wasClean?: boolean;\n}\ntype WebSocketEventMap = {\n    close: CloseEvent;\n    message: MessageEvent;\n    open: Event;\n    error: ErrorEvent;\n};\n/**\n * The `WebSocket` object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket)\n */\ndeclare var WebSocket: {\n    prototype: WebSocket;\n    new (url: string, protocols?: (string[] | string)): WebSocket;\n    readonly READY_STATE_CONNECTING: number;\n    readonly CONNECTING: number;\n    readonly READY_STATE_OPEN: number;\n    readonly OPEN: number;\n    readonly READY_STATE_CLOSING: number;\n    readonly CLOSING: number;\n    readonly READY_STATE_CLOSED: number;\n    readonly CLOSED: number;\n};\n/**\n * The `WebSocket` object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket)\n */\ninterface WebSocket extends EventTarget<WebSocketEventMap> {\n    accept(): void;\n    /**\n     * The **`WebSocket.send()`** method enqueues the specified data to be transmitted to the server over the WebSocket connection, increasing the value of `bufferedAmount` by the number of bytes needed to contain the data.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/send)\n     */\n    send(message: (ArrayBuffer | ArrayBufferView) | string): void;\n    /**\n     * The **`WebSocket.close()`** method closes the already `CLOSED`, this method does nothing.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/close)\n     */\n    close(code?: number, reason?: string): void;\n    serializeAttachment(attachment: any): void;\n    deserializeAttachment(): any | null;\n    /**\n     * The **`WebSocket.readyState`** read-only property returns the current state of the WebSocket connection.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/readyState)\n     */\n    readyState: number;\n    /**\n     * The **`WebSocket.url`** read-only property returns the absolute URL of the WebSocket as resolved by the constructor.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/url)\n     */\n    url: string | null;\n    /**\n     * The **`WebSocket.protocol`** read-only property returns the name of the sub-protocol the server selected; this will be one of the strings specified in the `protocols` parameter when creating the WebSocket object, or the empty string if no connection is established.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/protocol)\n     */\n    protocol: string | null;\n    /**\n     * The **`WebSocket.extensions`** read-only property returns the extensions selected by the server.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/extensions)\n     */\n    extensions: string | null;\n}\ndeclare const WebSocketPair: {\n    new (): {\n        0: WebSocket;\n        1: WebSocket;\n    };\n};\ninterface SqlStorage {\n    exec<T extends Record<string, SqlStorageValue>>(query: string, ...bindings: any[]): SqlStorageCursor<T>;\n    get databaseSize(): number;\n    Cursor: typeof SqlStorageCursor;\n    Statement: typeof SqlStorageStatement;\n}\ndeclare abstract class SqlStorageStatement {\n}\ntype SqlStorageValue = ArrayBuffer | string | number | null;\ndeclare abstract class SqlStorageCursor<T extends Record<string, SqlStorageValue>> {\n    next(): {\n        done?: false;\n        value: T;\n    } | {\n        done: true;\n        value?: never;\n    };\n    toArray(): T[];\n    one(): T;\n    raw<U extends SqlStorageValue[]>(): IterableIterator<U>;\n    columnNames: string[];\n    get rowsRead(): number;\n    get rowsWritten(): number;\n    [Symbol.iterator](): IterableIterator<T>;\n}\ninterface Socket {\n    get readable(): ReadableStream;\n    get writable(): WritableStream;\n    get closed(): Promise<void>;\n    get opened(): Promise<SocketInfo>;\n    get upgraded(): boolean;\n    get secureTransport(): \"on\" | \"off\" | \"starttls\";\n    close(): Promise<void>;\n    startTls(options?: TlsOptions): Socket;\n}\ninterface SocketOptions {\n    secureTransport?: string;\n    allowHalfOpen: boolean;\n    highWaterMark?: (number | bigint);\n}\ninterface SocketAddress {\n    hostname: string;\n    port: number;\n}\ninterface TlsOptions {\n    expectedServerHostname?: string;\n}\ninterface SocketInfo {\n    remoteAddress?: string;\n    localAddress?: string;\n}\n/**\n * The **`EventSource`** interface is web content's interface to server-sent events.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource)\n */\ndeclare class EventSource extends EventTarget {\n    constructor(url: string, init?: EventSourceEventSourceInit);\n    /**\n     * The **`close()`** method of the EventSource interface closes the connection, if one is made, and sets the ```js-nolint close() ``` None.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close)\n     */\n    close(): void;\n    /**\n     * The **`url`** read-only property of the URL of the source.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url)\n     */\n    get url(): string;\n    /**\n     * The **`withCredentials`** read-only property of the the `EventSource` object was instantiated with CORS credentials set.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials)\n     */\n    get withCredentials(): boolean;\n    /**\n     * The **`readyState`** read-only property of the connection.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState)\n     */\n    get readyState(): number;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */\n    get onopen(): any | null;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */\n    set onopen(value: any | null);\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */\n    get onmessage(): any | null;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */\n    set onmessage(value: any | null);\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */\n    get onerror(): any | null;\n    /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */\n    set onerror(value: any | null);\n    static readonly CONNECTING: number;\n    static readonly OPEN: number;\n    static readonly CLOSED: number;\n    static from(stream: ReadableStream): EventSource;\n}\ninterface EventSourceEventSourceInit {\n    withCredentials?: boolean;\n    fetcher?: Fetcher;\n}\ninterface Container {\n    get running(): boolean;\n    start(options?: ContainerStartupOptions): void;\n    monitor(): Promise<void>;\n    destroy(error?: any): Promise<void>;\n    signal(signo: number): void;\n    getTcpPort(port: number): Fetcher;\n    setInactivityTimeout(durationMs: number | bigint): Promise<void>;\n}\ninterface ContainerStartupOptions {\n    entrypoint?: string[];\n    enableInternet: boolean;\n    env?: Record<string, string>;\n    hardTimeout?: (number | bigint);\n}\n/**\n * The **`MessagePort`** interface of the Channel Messaging API represents one of the two ports of a MessageChannel, allowing messages to be sent from one port and listening out for them arriving at the other.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessagePort)\n */\ninterface MessagePort extends EventTarget {\n    /**\n     * The **`postMessage()`** method of the transfers ownership of objects to other browsing contexts.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessagePort/postMessage)\n     */\n    postMessage(data?: any, options?: (any[] | MessagePortPostMessageOptions)): void;\n    /**\n     * The **`close()`** method of the MessagePort interface disconnects the port, so it is no longer active.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessagePort/close)\n     */\n    close(): void;\n    /**\n     * The **`start()`** method of the MessagePort interface starts the sending of messages queued on the port.\n     *\n     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessagePort/start)\n     */\n    start(): void;\n    get onmessage(): any | null;\n    set onmessage(value: any | null);\n}\ninterface MessagePortPostMessageOptions {\n    transfer?: any[];\n}\ntype LoopbackForExport<T extends (new (...args: any[]) => Rpc.EntrypointBranded) | ExportedHandler<any, any, any> | undefined = undefined> = T extends new (...args: any[]) => Rpc.WorkerEntrypointBranded ? LoopbackServiceStub<InstanceType<T>> : T extends new (...args: any[]) => Rpc.DurableObjectBranded ? LoopbackDurableObjectClass<InstanceType<T>> : T extends ExportedHandler<any, any, any> ? LoopbackServiceStub<undefined> : undefined;\ntype LoopbackServiceStub<T extends Rpc.WorkerEntrypointBranded | undefined = undefined> = Fetcher<T> & (T extends CloudflareWorkersModule.WorkerEntrypoint<any, infer Props> ? (opts: {\n    props?: Props;\n}) => Fetcher<T> : (opts: {\n    props?: any;\n}) => Fetcher<T>);\ntype LoopbackDurableObjectClass<T extends Rpc.DurableObjectBranded | undefined = undefined> = DurableObjectClass<T> & (T extends CloudflareWorkersModule.DurableObject<any, infer Props> ? (opts: {\n    props?: Props;\n}) => DurableObjectClass<T> : (opts: {\n    props?: any;\n}) => DurableObjectClass<T>);\ninterface SyncKvStorage {\n    get<T = unknown>(key: string): T | undefined;\n    list<T = unknown>(options?: SyncKvListOptions): Iterable<[\n        string,\n        T\n    ]>;\n    put<T>(key: string, value: T): void;\n    delete(key: string): boolean;\n}\ninterface SyncKvListOptions {\n    start?: string;\n    startAfter?: string;\n    end?: string;\n    prefix?: string;\n    reverse?: boolean;\n    limit?: number;\n}\ninterface WorkerStub {\n    getEntrypoint<T extends Rpc.WorkerEntrypointBranded | undefined>(name?: string, options?: WorkerStubEntrypointOptions): Fetcher<T>;\n}\ninterface WorkerStubEntrypointOptions {\n    props?: any;\n}\ninterface WorkerLoader {\n    get(name: string | null, getCode: () => WorkerLoaderWorkerCode | Promise<WorkerLoaderWorkerCode>): WorkerStub;\n}\ninterface WorkerLoaderModule {\n    js?: string;\n    cjs?: string;\n    text?: string;\n    data?: ArrayBuffer;\n    json?: any;\n    py?: string;\n    wasm?: ArrayBuffer;\n}\ninterface WorkerLoaderWorkerCode {\n    compatibilityDate: string;\n    compatibilityFlags?: string[];\n    allowExperimental?: boolean;\n    mainModule: string;\n    modules: Record<string, WorkerLoaderModule | string>;\n    env?: any;\n    globalOutbound?: (Fetcher | null);\n    tails?: Fetcher[];\n    streamingTails?: Fetcher[];\n}\n/**\n* The Workers runtime supports a subset of the Performance API, used to measure timing and performance,\n* as well as timing of subrequests and other operations.\n*\n* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/)\n*/\ndeclare abstract class Performance {\n    /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */\n    get timeOrigin(): number;\n    /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancenow) */\n    now(): number;\n}\ntype AiImageClassificationInput = {\n    image: number[];\n};\ntype AiImageClassificationOutput = {\n    score?: number;\n    label?: string;\n}[];\ndeclare abstract class BaseAiImageClassification {\n    inputs: AiImageClassificationInput;\n    postProcessedOutputs: AiImageClassificationOutput;\n}\ntype AiImageToTextInput = {\n    image: number[];\n    prompt?: string;\n    max_tokens?: number;\n    temperature?: number;\n    top_p?: number;\n    top_k?: number;\n    seed?: number;\n    repetition_penalty?: number;\n    frequency_penalty?: number;\n    presence_penalty?: number;\n    raw?: boolean;\n    messages?: RoleScopedChatInput[];\n};\ntype AiImageToTextOutput = {\n    description: string;\n};\ndeclare abstract class BaseAiImageToText {\n    inputs: AiImageToTextInput;\n    postProcessedOutputs: AiImageToTextOutput;\n}\ntype AiImageTextToTextInput = {\n    image: string;\n    prompt?: string;\n    max_tokens?: number;\n    temperature?: number;\n    ignore_eos?: boolean;\n    top_p?: number;\n    top_k?: number;\n    seed?: number;\n    repetition_penalty?: number;\n    frequency_penalty?: number;\n    presence_penalty?: number;\n    raw?: boolean;\n    messages?: RoleScopedChatInput[];\n};\ntype AiImageTextToTextOutput = {\n    description: string;\n};\ndeclare abstract class BaseAiImageTextToText {\n    inputs: AiImageTextToTextInput;\n    postProcessedOutputs: AiImageTextToTextOutput;\n}\ntype AiMultimodalEmbeddingsInput = {\n    image: string;\n    text: string[];\n};\ntype AiIMultimodalEmbeddingsOutput = {\n    data: number[][];\n    shape: number[];\n};\ndeclare abstract class BaseAiMultimodalEmbeddings {\n    inputs: AiImageTextToTextInput;\n    postProcessedOutputs: AiImageTextToTextOutput;\n}\ntype AiObjectDetectionInput = {\n    image: number[];\n};\ntype AiObjectDetectionOutput = {\n    score?: number;\n    label?: string;\n}[];\ndeclare abstract class BaseAiObjectDetection {\n    inputs: AiObjectDetectionInput;\n    postProcessedOutputs: AiObjectDetectionOutput;\n}\ntype AiSentenceSimilarityInput = {\n    source: string;\n    sentences: string[];\n};\ntype AiSentenceSimilarityOutput = number[];\ndeclare abstract class BaseAiSentenceSimilarity {\n    inputs: AiSentenceSimilarityInput;\n    postProcessedOutputs: AiSentenceSimilarityOutput;\n}\ntype AiAutomaticSpeechRecognitionInput = {\n    audio: number[];\n};\ntype AiAutomaticSpeechRecognitionOutput = {\n    text?: string;\n    words?: {\n        word: string;\n        start: number;\n        end: number;\n    }[];\n    vtt?: string;\n};\ndeclare abstract class BaseAiAutomaticSpeechRecognition {\n    inputs: AiAutomaticSpeechRecognitionInput;\n    postProcessedOutputs: AiAutomaticSpeechRecognitionOutput;\n}\ntype AiSummarizationInput = {\n    input_text: string;\n    max_length?: number;\n};\ntype AiSummarizationOutput = {\n    summary: string;\n};\ndeclare abstract class BaseAiSummarization {\n    inputs: AiSummarizationInput;\n    postProcessedOutputs: AiSummarizationOutput;\n}\ntype AiTextClassificationInput = {\n    text: string;\n};\ntype AiTextClassificationOutput = {\n    score?: number;\n    label?: string;\n}[];\ndeclare abstract class BaseAiTextClassification {\n    inputs: AiTextClassificationInput;\n    postProcessedOutputs: AiTextClassificationOutput;\n}\ntype AiTextEmbeddingsInput = {\n    text: string | string[];\n};\ntype AiTextEmbeddingsOutput = {\n    shape: number[];\n    data: number[][];\n};\ndeclare abstract class BaseAiTextEmbeddings {\n    inputs: AiTextEmbeddingsInput;\n    postProcessedOutputs: AiTextEmbeddingsOutput;\n}\ntype RoleScopedChatInput = {\n    role: \"user\" | \"assistant\" | \"system\" | \"tool\" | (string & NonNullable<unknown>);\n    content: string;\n    name?: string;\n};\ntype AiTextGenerationToolLegacyInput = {\n    name: string;\n    description: string;\n    parameters?: {\n        type: \"object\" | (string & NonNullable<unknown>);\n        properties: {\n            [key: string]: {\n                type: string;\n                description?: string;\n            };\n        };\n        required: string[];\n    };\n};\ntype AiTextGenerationToolInput = {\n    type: \"function\" | (string & NonNullable<unknown>);\n    function: {\n        name: string;\n        description: string;\n        parameters?: {\n            type: \"object\" | (string & NonNullable<unknown>);\n            properties: {\n                [key: string]: {\n                    type: string;\n                    description?: string;\n                };\n            };\n            required: string[];\n        };\n    };\n};\ntype AiTextGenerationFunctionsInput = {\n    name: string;\n    code: string;\n};\ntype AiTextGenerationResponseFormat = {\n    type: string;\n    json_schema?: any;\n};\ntype AiTextGenerationInput = {\n    prompt?: string;\n    raw?: boolean;\n    stream?: boolean;\n    max_tokens?: number;\n    temperature?: number;\n    top_p?: number;\n    top_k?: number;\n    seed?: number;\n    repetition_penalty?: number;\n    frequency_penalty?: number;\n    presence_penalty?: number;\n    messages?: RoleScopedChatInput[];\n    response_format?: AiTextGenerationResponseFormat;\n    tools?: AiTextGenerationToolInput[] | AiTextGenerationToolLegacyInput[] | (object & NonNullable<unknown>);\n    functions?: AiTextGenerationFunctionsInput[];\n};\ntype AiTextGenerationToolLegacyOutput = {\n    name: string;\n    arguments: unknown;\n};\ntype AiTextGenerationToolOutput = {\n    id: string;\n    type: \"function\";\n    function: {\n        name: string;\n        arguments: string;\n    };\n};\ntype UsageTags = {\n    prompt_tokens: number;\n    completion_tokens: number;\n    total_tokens: number;\n};\ntype AiTextGenerationOutput = {\n    response?: string;\n    tool_calls?: AiTextGenerationToolLegacyOutput[] & AiTextGenerationToolOutput[];\n    usage?: UsageTags;\n};\ndeclare abstract class BaseAiTextGeneration {\n    inputs: AiTextGenerationInput;\n    postProcessedOutputs: AiTextGenerationOutput;\n}\ntype AiTextToSpeechInput = {\n    prompt: string;\n    lang?: string;\n};\ntype AiTextToSpeechOutput = Uint8Array | {\n    audio: string;\n};\ndeclare abstract class BaseAiTextToSpeech {\n    inputs: AiTextToSpeechInput;\n    postProcessedOutputs: AiTextToSpeechOutput;\n}\ntype AiTextToImageInput = {\n    prompt: string;\n    negative_prompt?: string;\n    height?: number;\n    width?: number;\n    image?: number[];\n    image_b64?: string;\n    mask?: number[];\n    num_steps?: number;\n    strength?: number;\n    guidance?: number;\n    seed?: number;\n};\ntype AiTextToImageOutput = ReadableStream<Uint8Array>;\ndeclare abstract class BaseAiTextToImage {\n    inputs: AiTextToImageInput;\n    postProcessedOutputs: AiTextToImageOutput;\n}\ntype AiTranslationInput = {\n    text: string;\n    target_lang: string;\n    source_lang?: string;\n};\ntype AiTranslationOutput = {\n    translated_text?: string;\n};\ndeclare abstract class BaseAiTranslation {\n    inputs: AiTranslationInput;\n    postProcessedOutputs: AiTranslationOutput;\n}\n/**\n * Workers AI support for OpenAI's Responses API\n * Reference: https://github.com/openai/openai-node/blob/master/src/resources/responses/responses.ts\n *\n * It's a stripped down version from its source.\n * It currently supports basic function calling, json mode and accepts images as input.\n *\n * It does not include types for WebSearch, CodeInterpreter, FileInputs, MCP, CustomTools.\n * We plan to add those incrementally as model + platform capabilities evolve.\n */\ntype ResponsesInput = {\n    background?: boolean | null;\n    conversation?: string | ResponseConversationParam | null;\n    include?: Array<ResponseIncludable> | null;\n    input?: string | ResponseInput;\n    instructions?: string | null;\n    max_output_tokens?: number | null;\n    parallel_tool_calls?: boolean | null;\n    previous_response_id?: string | null;\n    prompt_cache_key?: string;\n    reasoning?: Reasoning | null;\n    safety_identifier?: string;\n    service_tier?: \"auto\" | \"default\" | \"flex\" | \"scale\" | \"priority\" | null;\n    stream?: boolean | null;\n    stream_options?: StreamOptions | null;\n    temperature?: number | null;\n    text?: ResponseTextConfig;\n    tool_choice?: ToolChoiceOptions | ToolChoiceFunction;\n    tools?: Array<Tool>;\n    top_p?: number | null;\n    truncation?: \"auto\" | \"disabled\" | null;\n};\ntype ResponsesOutput = {\n    id?: string;\n    created_at?: number;\n    output_text?: string;\n    error?: ResponseError | null;\n    incomplete_details?: ResponseIncompleteDetails | null;\n    instructions?: string | Array<ResponseInputItem> | null;\n    object?: \"response\";\n    output?: Array<ResponseOutputItem>;\n    parallel_tool_calls?: boolean;\n    temperature?: number | null;\n    tool_choice?: ToolChoiceOptions | ToolChoiceFunction;\n    tools?: Array<Tool>;\n    top_p?: number | null;\n    max_output_tokens?: number | null;\n    previous_response_id?: string | null;\n    prompt?: ResponsePrompt | null;\n    reasoning?: Reasoning | null;\n    safety_identifier?: string;\n    service_tier?: \"auto\" | \"default\" | \"flex\" | \"scale\" | \"priority\" | null;\n    status?: ResponseStatus;\n    text?: ResponseTextConfig;\n    truncation?: \"auto\" | \"disabled\" | null;\n    usage?: ResponseUsage;\n};\ntype EasyInputMessage = {\n    content: string | ResponseInputMessageContentList;\n    role: \"user\" | \"assistant\" | \"system\" | \"developer\";\n    type?: \"message\";\n};\ntype ResponsesFunctionTool = {\n    name: string;\n    parameters: {\n        [key: string]: unknown;\n    } | null;\n    strict: boolean | null;\n    type: \"function\";\n    description?: string | null;\n};\ntype ResponseIncompleteDetails = {\n    reason?: \"max_output_tokens\" | \"content_filter\";\n};\ntype ResponsePrompt = {\n    id: string;\n    variables?: {\n        [key: string]: string | ResponseInputText | ResponseInputImage;\n    } | null;\n    version?: string | null;\n};\ntype Reasoning = {\n    effort?: ReasoningEffort | null;\n    generate_summary?: \"auto\" | \"concise\" | \"detailed\" | null;\n    summary?: \"auto\" | \"concise\" | \"detailed\" | null;\n};\ntype ResponseContent = ResponseInputText | ResponseInputImage | ResponseOutputText | ResponseOutputRefusal | ResponseContentReasoningText;\ntype ResponseContentReasoningText = {\n    text: string;\n    type: \"reasoning_text\";\n};\ntype ResponseConversationParam = {\n    id: string;\n};\ntype ResponseCreatedEvent = {\n    response: Response;\n    sequence_number: number;\n    type: \"response.created\";\n};\ntype ResponseCustomToolCallOutput = {\n    call_id: string;\n    output: string | Array<ResponseInputText | ResponseInputImage>;\n    type: \"custom_tool_call_output\";\n    id?: string;\n};\ntype ResponseError = {\n    code: \"server_error\" | \"rate_limit_exceeded\" | \"invalid_prompt\" | \"vector_store_timeout\" | \"invalid_image\" | \"invalid_image_format\" | \"invalid_base64_image\" | \"invalid_image_url\" | \"image_too_large\" | \"image_too_small\" | \"image_parse_error\" | \"image_content_policy_violation\" | \"invalid_image_mode\" | \"image_file_too_large\" | \"unsupported_image_media_type\" | \"empty_image_file\" | \"failed_to_download_image\" | \"image_file_not_found\";\n    message: string;\n};\ntype ResponseErrorEvent = {\n    code: string | null;\n    message: string;\n    param: string | null;\n    sequence_number: number;\n    type: \"error\";\n};\ntype ResponseFailedEvent = {\n    response: Response;\n    sequence_number: number;\n    type: \"response.failed\";\n};\ntype ResponseFormatText = {\n    type: \"text\";\n};\ntype ResponseFormatJSONObject = {\n    type: \"json_object\";\n};\ntype ResponseFormatTextConfig = ResponseFormatText | ResponseFormatTextJSONSchemaConfig | ResponseFormatJSONObject;\ntype ResponseFormatTextJSONSchemaConfig = {\n    name: string;\n    schema: {\n        [key: string]: unknown;\n    };\n    type: \"json_schema\";\n    description?: string;\n    strict?: boolean | null;\n};\ntype ResponseFunctionCallArgumentsDeltaEvent = {\n    delta: string;\n    item_id: string;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.function_call_arguments.delta\";\n};\ntype ResponseFunctionCallArgumentsDoneEvent = {\n    arguments: string;\n    item_id: string;\n    name: string;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.function_call_arguments.done\";\n};\ntype ResponseFunctionCallOutputItem = ResponseInputTextContent | ResponseInputImageContent;\ntype ResponseFunctionCallOutputItemList = Array<ResponseFunctionCallOutputItem>;\ntype ResponseFunctionToolCall = {\n    arguments: string;\n    call_id: string;\n    name: string;\n    type: \"function_call\";\n    id?: string;\n    status?: \"in_progress\" | \"completed\" | \"incomplete\";\n};\ninterface ResponseFunctionToolCallItem extends ResponseFunctionToolCall {\n    id: string;\n}\ntype ResponseFunctionToolCallOutputItem = {\n    id: string;\n    call_id: string;\n    output: string | Array<ResponseInputText | ResponseInputImage>;\n    type: \"function_call_output\";\n    status?: \"in_progress\" | \"completed\" | \"incomplete\";\n};\ntype ResponseIncludable = \"message.input_image.image_url\" | \"message.output_text.logprobs\";\ntype ResponseIncompleteEvent = {\n    response: Response;\n    sequence_number: number;\n    type: \"response.incomplete\";\n};\ntype ResponseInput = Array<ResponseInputItem>;\ntype ResponseInputContent = ResponseInputText | ResponseInputImage;\ntype ResponseInputImage = {\n    detail: \"low\" | \"high\" | \"auto\";\n    type: \"input_image\";\n    /**\n     * Base64 encoded image\n     */\n    image_url?: string | null;\n};\ntype ResponseInputImageContent = {\n    type: \"input_image\";\n    detail?: \"low\" | \"high\" | \"auto\" | null;\n    /**\n     * Base64 encoded image\n     */\n    image_url?: string | null;\n};\ntype ResponseInputItem = EasyInputMessage | ResponseInputItemMessage | ResponseOutputMessage | ResponseFunctionToolCall | ResponseInputItemFunctionCallOutput | ResponseReasoningItem;\ntype ResponseInputItemFunctionCallOutput = {\n    call_id: string;\n    output: string | ResponseFunctionCallOutputItemList;\n    type: \"function_call_output\";\n    id?: string | null;\n    status?: \"in_progress\" | \"completed\" | \"incomplete\" | null;\n};\ntype ResponseInputItemMessage = {\n    content: ResponseInputMessageContentList;\n    role: \"user\" | \"system\" | \"developer\";\n    status?: \"in_progress\" | \"completed\" | \"incomplete\";\n    type?: \"message\";\n};\ntype ResponseInputMessageContentList = Array<ResponseInputContent>;\ntype ResponseInputMessageItem = {\n    id: string;\n    content: ResponseInputMessageContentList;\n    role: \"user\" | \"system\" | \"developer\";\n    status?: \"in_progress\" | \"completed\" | \"incomplete\";\n    type?: \"message\";\n};\ntype ResponseInputText = {\n    text: string;\n    type: \"input_text\";\n};\ntype ResponseInputTextContent = {\n    text: string;\n    type: \"input_text\";\n};\ntype ResponseItem = ResponseInputMessageItem | ResponseOutputMessage | ResponseFunctionToolCallItem | ResponseFunctionToolCallOutputItem;\ntype ResponseOutputItem = ResponseOutputMessage | ResponseFunctionToolCall | ResponseReasoningItem;\ntype ResponseOutputItemAddedEvent = {\n    item: ResponseOutputItem;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.output_item.added\";\n};\ntype ResponseOutputItemDoneEvent = {\n    item: ResponseOutputItem;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.output_item.done\";\n};\ntype ResponseOutputMessage = {\n    id: string;\n    content: Array<ResponseOutputText | ResponseOutputRefusal>;\n    role: \"assistant\";\n    status: \"in_progress\" | \"completed\" | \"incomplete\";\n    type: \"message\";\n};\ntype ResponseOutputRefusal = {\n    refusal: string;\n    type: \"refusal\";\n};\ntype ResponseOutputText = {\n    text: string;\n    type: \"output_text\";\n    logprobs?: Array<Logprob>;\n};\ntype ResponseReasoningItem = {\n    id: string;\n    summary: Array<ResponseReasoningSummaryItem>;\n    type: \"reasoning\";\n    content?: Array<ResponseReasoningContentItem>;\n    encrypted_content?: string | null;\n    status?: \"in_progress\" | \"completed\" | \"incomplete\";\n};\ntype ResponseReasoningSummaryItem = {\n    text: string;\n    type: \"summary_text\";\n};\ntype ResponseReasoningContentItem = {\n    text: string;\n    type: \"reasoning_text\";\n};\ntype ResponseReasoningTextDeltaEvent = {\n    content_index: number;\n    delta: string;\n    item_id: string;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.reasoning_text.delta\";\n};\ntype ResponseReasoningTextDoneEvent = {\n    content_index: number;\n    item_id: string;\n    output_index: number;\n    sequence_number: number;\n    text: string;\n    type: \"response.reasoning_text.done\";\n};\ntype ResponseRefusalDeltaEvent = {\n    content_index: number;\n    delta: string;\n    item_id: string;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.refusal.delta\";\n};\ntype ResponseRefusalDoneEvent = {\n    content_index: number;\n    item_id: string;\n    output_index: number;\n    refusal: string;\n    sequence_number: number;\n    type: \"response.refusal.done\";\n};\ntype ResponseStatus = \"completed\" | \"failed\" | \"in_progress\" | \"cancelled\" | \"queued\" | \"incomplete\";\ntype ResponseStreamEvent = ResponseCompletedEvent | ResponseCreatedEvent | ResponseErrorEvent | ResponseFunctionCallArgumentsDeltaEvent | ResponseFunctionCallArgumentsDoneEvent | ResponseFailedEvent | ResponseIncompleteEvent | ResponseOutputItemAddedEvent | ResponseOutputItemDoneEvent | ResponseReasoningTextDeltaEvent | ResponseReasoningTextDoneEvent | ResponseRefusalDeltaEvent | ResponseRefusalDoneEvent | ResponseTextDeltaEvent | ResponseTextDoneEvent;\ntype ResponseCompletedEvent = {\n    response: Response;\n    sequence_number: number;\n    type: \"response.completed\";\n};\ntype ResponseTextConfig = {\n    format?: ResponseFormatTextConfig;\n    verbosity?: \"low\" | \"medium\" | \"high\" | null;\n};\ntype ResponseTextDeltaEvent = {\n    content_index: number;\n    delta: string;\n    item_id: string;\n    logprobs: Array<Logprob>;\n    output_index: number;\n    sequence_number: number;\n    type: \"response.output_text.delta\";\n};\ntype ResponseTextDoneEvent = {\n    content_index: number;\n    item_id: string;\n    logprobs: Array<Logprob>;\n    output_index: number;\n    sequence_number: number;\n    text: string;\n    type: \"response.output_text.done\";\n};\ntype Logprob = {\n    token: string;\n    logprob: number;\n    top_logprobs?: Array<TopLogprob>;\n};\ntype TopLogprob = {\n    token?: string;\n    logprob?: number;\n};\ntype ResponseUsage = {\n    input_tokens: number;\n    output_tokens: number;\n    total_tokens: number;\n};\ntype Tool = ResponsesFunctionTool;\ntype ToolChoiceFunction = {\n    name: string;\n    type: \"function\";\n};\ntype ToolChoiceOptions = \"none\";\ntype ReasoningEffort = \"minimal\" | \"low\" | \"medium\" | \"high\" | null;\ntype StreamOptions = {\n    include_obfuscation?: boolean;\n};\ntype Ai_Cf_Baai_Bge_Base_En_V1_5_Input = {\n    text: string | string[];\n    /**\n     * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.\n     */\n    pooling?: \"mean\" | \"cls\";\n} | {\n    /**\n     * Batch of the embeddings requests to run using async-queue\n     */\n    requests: {\n        text: string | string[];\n        /**\n         * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.\n         */\n        pooling?: \"mean\" | \"cls\";\n    }[];\n};\ntype Ai_Cf_Baai_Bge_Base_En_V1_5_Output = {\n    shape?: number[];\n    /**\n     * Embeddings of the requested text values\n     */\n    data?: number[][];\n    /**\n     * The pooling method used in the embedding process.\n     */\n    pooling?: \"mean\" | \"cls\";\n} | Ai_Cf_Baai_Bge_Base_En_V1_5_AsyncResponse;\ninterface Ai_Cf_Baai_Bge_Base_En_V1_5_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Baai_Bge_Base_En_V1_5 {\n    inputs: Ai_Cf_Baai_Bge_Base_En_V1_5_Input;\n    postProcessedOutputs: Ai_Cf_Baai_Bge_Base_En_V1_5_Output;\n}\ntype Ai_Cf_Openai_Whisper_Input = string | {\n    /**\n     * An array of integers that represent the audio data constrained to 8-bit unsigned integer values\n     */\n    audio: number[];\n};\ninterface Ai_Cf_Openai_Whisper_Output {\n    /**\n     * The transcription\n     */\n    text: string;\n    word_count?: number;\n    words?: {\n        word?: string;\n        /**\n         * The second this word begins in the recording\n         */\n        start?: number;\n        /**\n         * The ending second when the word completes\n         */\n        end?: number;\n    }[];\n    vtt?: string;\n}\ndeclare abstract class Base_Ai_Cf_Openai_Whisper {\n    inputs: Ai_Cf_Openai_Whisper_Input;\n    postProcessedOutputs: Ai_Cf_Openai_Whisper_Output;\n}\ntype Ai_Cf_Meta_M2M100_1_2B_Input = {\n    /**\n     * The text to be translated\n     */\n    text: string;\n    /**\n     * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified\n     */\n    source_lang?: string;\n    /**\n     * The language code to translate the text into (e.g., 'es' for Spanish)\n     */\n    target_lang: string;\n} | {\n    /**\n     * Batch of the embeddings requests to run using async-queue\n     */\n    requests: {\n        /**\n         * The text to be translated\n         */\n        text: string;\n        /**\n         * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified\n         */\n        source_lang?: string;\n        /**\n         * The language code to translate the text into (e.g., 'es' for Spanish)\n         */\n        target_lang: string;\n    }[];\n};\ntype Ai_Cf_Meta_M2M100_1_2B_Output = {\n    /**\n     * The translated text in the target language\n     */\n    translated_text?: string;\n} | Ai_Cf_Meta_M2M100_1_2B_AsyncResponse;\ninterface Ai_Cf_Meta_M2M100_1_2B_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Meta_M2M100_1_2B {\n    inputs: Ai_Cf_Meta_M2M100_1_2B_Input;\n    postProcessedOutputs: Ai_Cf_Meta_M2M100_1_2B_Output;\n}\ntype Ai_Cf_Baai_Bge_Small_En_V1_5_Input = {\n    text: string | string[];\n    /**\n     * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.\n     */\n    pooling?: \"mean\" | \"cls\";\n} | {\n    /**\n     * Batch of the embeddings requests to run using async-queue\n     */\n    requests: {\n        text: string | string[];\n        /**\n         * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.\n         */\n        pooling?: \"mean\" | \"cls\";\n    }[];\n};\ntype Ai_Cf_Baai_Bge_Small_En_V1_5_Output = {\n    shape?: number[];\n    /**\n     * Embeddings of the requested text values\n     */\n    data?: number[][];\n    /**\n     * The pooling method used in the embedding process.\n     */\n    pooling?: \"mean\" | \"cls\";\n} | Ai_Cf_Baai_Bge_Small_En_V1_5_AsyncResponse;\ninterface Ai_Cf_Baai_Bge_Small_En_V1_5_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Baai_Bge_Small_En_V1_5 {\n    inputs: Ai_Cf_Baai_Bge_Small_En_V1_5_Input;\n    postProcessedOutputs: Ai_Cf_Baai_Bge_Small_En_V1_5_Output;\n}\ntype Ai_Cf_Baai_Bge_Large_En_V1_5_Input = {\n    text: string | string[];\n    /**\n     * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.\n     */\n    pooling?: \"mean\" | \"cls\";\n} | {\n    /**\n     * Batch of the embeddings requests to run using async-queue\n     */\n    requests: {\n        text: string | string[];\n        /**\n         * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.\n         */\n        pooling?: \"mean\" | \"cls\";\n    }[];\n};\ntype Ai_Cf_Baai_Bge_Large_En_V1_5_Output = {\n    shape?: number[];\n    /**\n     * Embeddings of the requested text values\n     */\n    data?: number[][];\n    /**\n     * The pooling method used in the embedding process.\n     */\n    pooling?: \"mean\" | \"cls\";\n} | Ai_Cf_Baai_Bge_Large_En_V1_5_AsyncResponse;\ninterface Ai_Cf_Baai_Bge_Large_En_V1_5_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Baai_Bge_Large_En_V1_5 {\n    inputs: Ai_Cf_Baai_Bge_Large_En_V1_5_Input;\n    postProcessedOutputs: Ai_Cf_Baai_Bge_Large_En_V1_5_Output;\n}\ntype Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt?: string;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n    image: number[] | (string & NonNullable<unknown>);\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n};\ninterface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output {\n    description?: string;\n}\ndeclare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M {\n    inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input;\n    postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output;\n}\ntype Ai_Cf_Openai_Whisper_Tiny_En_Input = string | {\n    /**\n     * An array of integers that represent the audio data constrained to 8-bit unsigned integer values\n     */\n    audio: number[];\n};\ninterface Ai_Cf_Openai_Whisper_Tiny_En_Output {\n    /**\n     * The transcription\n     */\n    text: string;\n    word_count?: number;\n    words?: {\n        word?: string;\n        /**\n         * The second this word begins in the recording\n         */\n        start?: number;\n        /**\n         * The ending second when the word completes\n         */\n        end?: number;\n    }[];\n    vtt?: string;\n}\ndeclare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En {\n    inputs: Ai_Cf_Openai_Whisper_Tiny_En_Input;\n    postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output;\n}\ninterface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input {\n    /**\n     * Base64 encoded value of the audio data.\n     */\n    audio: string;\n    /**\n     * Supported tasks are 'translate' or 'transcribe'.\n     */\n    task?: string;\n    /**\n     * The language of the audio being transcribed or translated.\n     */\n    language?: string;\n    /**\n     * Preprocess the audio with a voice activity detection model.\n     */\n    vad_filter?: boolean;\n    /**\n     * A text prompt to help provide context to the model on the contents of the audio.\n     */\n    initial_prompt?: string;\n    /**\n     * The prefix it appended the the beginning of the output of the transcription and can guide the transcription result.\n     */\n    prefix?: string;\n}\ninterface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output {\n    transcription_info?: {\n        /**\n         * The language of the audio being transcribed or translated.\n         */\n        language?: string;\n        /**\n         * The confidence level or probability of the detected language being accurate, represented as a decimal between 0 and 1.\n         */\n        language_probability?: number;\n        /**\n         * The total duration of the original audio file, in seconds.\n         */\n        duration?: number;\n        /**\n         * The duration of the audio after applying Voice Activity Detection (VAD) to remove silent or irrelevant sections, in seconds.\n         */\n        duration_after_vad?: number;\n    };\n    /**\n     * The complete transcription of the audio.\n     */\n    text: string;\n    /**\n     * The total number of words in the transcription.\n     */\n    word_count?: number;\n    segments?: {\n        /**\n         * The starting time of the segment within the audio, in seconds.\n         */\n        start?: number;\n        /**\n         * The ending time of the segment within the audio, in seconds.\n         */\n        end?: number;\n        /**\n         * The transcription of the segment.\n         */\n        text?: string;\n        /**\n         * The temperature used in the decoding process, controlling randomness in predictions. Lower values result in more deterministic outputs.\n         */\n        temperature?: number;\n        /**\n         * The average log probability of the predictions for the words in this segment, indicating overall confidence.\n         */\n        avg_logprob?: number;\n        /**\n         * The compression ratio of the input to the output, measuring how much the text was compressed during the transcription process.\n         */\n        compression_ratio?: number;\n        /**\n         * The probability that the segment contains no speech, represented as a decimal between 0 and 1.\n         */\n        no_speech_prob?: number;\n        words?: {\n            /**\n             * The individual word transcribed from the audio.\n             */\n            word?: string;\n            /**\n             * The starting time of the word within the audio, in seconds.\n             */\n            start?: number;\n            /**\n             * The ending time of the word within the audio, in seconds.\n             */\n            end?: number;\n        }[];\n    }[];\n    /**\n     * The transcription in WebVTT format, which includes timing and text information for use in subtitles.\n     */\n    vtt?: string;\n}\ndeclare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo {\n    inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input;\n    postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output;\n}\ntype Ai_Cf_Baai_Bge_M3_Input = Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts | Ai_Cf_Baai_Bge_M3_Input_Embedding | {\n    /**\n     * Batch of the embeddings requests to run using async-queue\n     */\n    requests: (Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts_1 | Ai_Cf_Baai_Bge_M3_Input_Embedding_1)[];\n};\ninterface Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts {\n    /**\n     * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts\n     */\n    query?: string;\n    /**\n     * List of provided contexts. Note that the index in this array is important, as the response will refer to it.\n     */\n    contexts: {\n        /**\n         * One of the provided context content\n         */\n        text?: string;\n    }[];\n    /**\n     * When provided with too long context should the model error out or truncate the context to fit?\n     */\n    truncate_inputs?: boolean;\n}\ninterface Ai_Cf_Baai_Bge_M3_Input_Embedding {\n    text: string | string[];\n    /**\n     * When provided with too long context should the model error out or truncate the context to fit?\n     */\n    truncate_inputs?: boolean;\n}\ninterface Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts_1 {\n    /**\n     * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts\n     */\n    query?: string;\n    /**\n     * List of provided contexts. Note that the index in this array is important, as the response will refer to it.\n     */\n    contexts: {\n        /**\n         * One of the provided context content\n         */\n        text?: string;\n    }[];\n    /**\n     * When provided with too long context should the model error out or truncate the context to fit?\n     */\n    truncate_inputs?: boolean;\n}\ninterface Ai_Cf_Baai_Bge_M3_Input_Embedding_1 {\n    text: string | string[];\n    /**\n     * When provided with too long context should the model error out or truncate the context to fit?\n     */\n    truncate_inputs?: boolean;\n}\ntype Ai_Cf_Baai_Bge_M3_Output = Ai_Cf_Baai_Bge_M3_Ouput_Query | Ai_Cf_Baai_Bge_M3_Output_EmbeddingFor_Contexts | Ai_Cf_Baai_Bge_M3_Ouput_Embedding | Ai_Cf_Baai_Bge_M3_AsyncResponse;\ninterface Ai_Cf_Baai_Bge_M3_Ouput_Query {\n    response?: {\n        /**\n         * Index of the context in the request\n         */\n        id?: number;\n        /**\n         * Score of the context under the index.\n         */\n        score?: number;\n    }[];\n}\ninterface Ai_Cf_Baai_Bge_M3_Output_EmbeddingFor_Contexts {\n    response?: number[][];\n    shape?: number[];\n    /**\n     * The pooling method used in the embedding process.\n     */\n    pooling?: \"mean\" | \"cls\";\n}\ninterface Ai_Cf_Baai_Bge_M3_Ouput_Embedding {\n    shape?: number[];\n    /**\n     * Embeddings of the requested text values\n     */\n    data?: number[][];\n    /**\n     * The pooling method used in the embedding process.\n     */\n    pooling?: \"mean\" | \"cls\";\n}\ninterface Ai_Cf_Baai_Bge_M3_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Baai_Bge_M3 {\n    inputs: Ai_Cf_Baai_Bge_M3_Input;\n    postProcessedOutputs: Ai_Cf_Baai_Bge_M3_Output;\n}\ninterface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input {\n    /**\n     * A text description of the image you want to generate.\n     */\n    prompt: string;\n    /**\n     * The number of diffusion steps; higher values can improve quality but take longer.\n     */\n    steps?: number;\n}\ninterface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output {\n    /**\n     * The generated image in Base64 format.\n     */\n    image?: string;\n}\ndeclare abstract class Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell {\n    inputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input;\n    postProcessedOutputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output;\n}\ntype Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Prompt | Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Messages;\ninterface Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    image?: number[] | (string & NonNullable<unknown>);\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n}\ninterface Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role?: string;\n        /**\n         * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001\n         */\n        tool_call_id?: string;\n        content?: string | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        }[] | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        };\n    }[];\n    image?: number[] | (string & NonNullable<unknown>);\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    /**\n     * If true, the response will be streamed back incrementally.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ntype Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response?: string;\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The arguments passed to be passed to the tool call request\n         */\n        arguments?: object;\n        /**\n         * The name of the tool to be called\n         */\n        name?: string;\n    }[];\n};\ndeclare abstract class Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct {\n    inputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input;\n    postProcessedOutputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output;\n}\ntype Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Input = Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Prompt | Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Messages | Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Async_Batch;\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n    response_format?: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role: string;\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode_1;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode_1 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Async_Batch {\n    requests?: {\n        /**\n         * User-supplied reference. This field will be present in the response as well it can be used to reference the request and response. It's NOT validated to be unique.\n         */\n        external_reference?: string;\n        /**\n         * Prompt for the text generation model\n         */\n        prompt?: string;\n        /**\n         * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n         */\n        stream?: boolean;\n        /**\n         * The maximum number of tokens to generate in the response.\n         */\n        max_tokens?: number;\n        /**\n         * Controls the randomness of the output; higher values produce more random results.\n         */\n        temperature?: number;\n        /**\n         * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n         */\n        top_p?: number;\n        /**\n         * Random seed for reproducibility of the generation.\n         */\n        seed?: number;\n        /**\n         * Penalty for repeated tokens; higher values discourage repetition.\n         */\n        repetition_penalty?: number;\n        /**\n         * Decreases the likelihood of the model repeating the same lines verbatim.\n         */\n        frequency_penalty?: number;\n        /**\n         * Increases the likelihood of the model introducing new topics.\n         */\n        presence_penalty?: number;\n        response_format?: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode_2;\n    }[];\n}\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode_2 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ntype Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response: string;\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The arguments passed to be passed to the tool call request\n         */\n        arguments?: object;\n        /**\n         * The name of the tool to be called\n         */\n        name?: string;\n    }[];\n} | string | Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_AsyncResponse;\ninterface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast {\n    inputs: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Input;\n    postProcessedOutputs: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Output;\n}\ninterface Ai_Cf_Meta_Llama_Guard_3_8B_Input {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender must alternate between 'user' and 'assistant'.\n         */\n        role: \"user\" | \"assistant\";\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Dictate the output format of the generated response.\n     */\n    response_format?: {\n        /**\n         * Set to json_object to process and output generated text as JSON.\n         */\n        type?: string;\n    };\n}\ninterface Ai_Cf_Meta_Llama_Guard_3_8B_Output {\n    response?: string | {\n        /**\n         * Whether the conversation is safe or not.\n         */\n        safe?: boolean;\n        /**\n         * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe.\n         */\n        categories?: string[];\n    };\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n}\ndeclare abstract class Base_Ai_Cf_Meta_Llama_Guard_3_8B {\n    inputs: Ai_Cf_Meta_Llama_Guard_3_8B_Input;\n    postProcessedOutputs: Ai_Cf_Meta_Llama_Guard_3_8B_Output;\n}\ninterface Ai_Cf_Baai_Bge_Reranker_Base_Input {\n    /**\n     * A query you wish to perform against the provided contexts.\n     */\n    /**\n     * Number of returned results starting with the best score.\n     */\n    top_k?: number;\n    /**\n     * List of provided contexts. Note that the index in this array is important, as the response will refer to it.\n     */\n    contexts: {\n        /**\n         * One of the provided context content\n         */\n        text?: string;\n    }[];\n}\ninterface Ai_Cf_Baai_Bge_Reranker_Base_Output {\n    response?: {\n        /**\n         * Index of the context in the request\n         */\n        id?: number;\n        /**\n         * Score of the context under the index.\n         */\n        score?: number;\n    }[];\n}\ndeclare abstract class Base_Ai_Cf_Baai_Bge_Reranker_Base {\n    inputs: Ai_Cf_Baai_Bge_Reranker_Base_Input;\n    postProcessedOutputs: Ai_Cf_Baai_Bge_Reranker_Base_Output;\n}\ntype Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Input = Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Prompt | Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Messages;\ninterface Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n    response_format?: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_JSON_Mode;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_JSON_Mode {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role: string;\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_JSON_Mode_1;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_JSON_Mode_1 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ntype Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response: string;\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The arguments passed to be passed to the tool call request\n         */\n        arguments?: object;\n        /**\n         * The name of the tool to be called\n         */\n        name?: string;\n    }[];\n};\ndeclare abstract class Base_Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct {\n    inputs: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Input;\n    postProcessedOutputs: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Output;\n}\ntype Ai_Cf_Qwen_Qwq_32B_Input = Ai_Cf_Qwen_Qwq_32B_Prompt | Ai_Cf_Qwen_Qwq_32B_Messages;\ninterface Ai_Cf_Qwen_Qwq_32B_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * JSON schema that should be fulfilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwq_32B_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role?: string;\n        /**\n         * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001\n         */\n        tool_call_id?: string;\n        content?: string | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        }[] | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        };\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    /**\n     * JSON schema that should be fufilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ntype Ai_Cf_Qwen_Qwq_32B_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response: string;\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The arguments passed to be passed to the tool call request\n         */\n        arguments?: object;\n        /**\n         * The name of the tool to be called\n         */\n        name?: string;\n    }[];\n};\ndeclare abstract class Base_Ai_Cf_Qwen_Qwq_32B {\n    inputs: Ai_Cf_Qwen_Qwq_32B_Input;\n    postProcessedOutputs: Ai_Cf_Qwen_Qwq_32B_Output;\n}\ntype Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Input = Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Prompt | Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Messages;\ninterface Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * JSON schema that should be fulfilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role?: string;\n        /**\n         * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001\n         */\n        tool_call_id?: string;\n        content?: string | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        }[] | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        };\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    /**\n     * JSON schema that should be fufilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ntype Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response: string;\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The arguments passed to be passed to the tool call request\n         */\n        arguments?: object;\n        /**\n         * The name of the tool to be called\n         */\n        name?: string;\n    }[];\n};\ndeclare abstract class Base_Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct {\n    inputs: Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Input;\n    postProcessedOutputs: Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Output;\n}\ntype Ai_Cf_Google_Gemma_3_12B_It_Input = Ai_Cf_Google_Gemma_3_12B_It_Prompt | Ai_Cf_Google_Gemma_3_12B_It_Messages;\ninterface Ai_Cf_Google_Gemma_3_12B_It_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * JSON schema that should be fufilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Google_Gemma_3_12B_It_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role?: string;\n        content?: string | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        }[];\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    /**\n     * JSON schema that should be fufilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ntype Ai_Cf_Google_Gemma_3_12B_It_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response: string;\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The arguments passed to be passed to the tool call request\n         */\n        arguments?: object;\n        /**\n         * The name of the tool to be called\n         */\n        name?: string;\n    }[];\n};\ndeclare abstract class Base_Ai_Cf_Google_Gemma_3_12B_It {\n    inputs: Ai_Cf_Google_Gemma_3_12B_It_Input;\n    postProcessedOutputs: Ai_Cf_Google_Gemma_3_12B_It_Output;\n}\ntype Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input = Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Prompt | Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages | Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Async_Batch;\ninterface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * JSON schema that should be fulfilled for the response.\n     */\n    guided_json?: object;\n    response_format?: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role?: string;\n        /**\n         * The tool call id. If you don't know what to put here you can fall back to 000000001\n         */\n        tool_call_id?: string;\n        content?: string | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        }[] | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        };\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode;\n    /**\n     * JSON schema that should be fufilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Async_Batch {\n    requests: (Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Prompt_Inner | Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages_Inner)[];\n}\ninterface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Prompt_Inner {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * JSON schema that should be fulfilled for the response.\n     */\n    guided_json?: object;\n    response_format?: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages_Inner {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role?: string;\n        /**\n         * The tool call id. If you don't know what to put here you can fall back to 000000001\n         */\n        tool_call_id?: string;\n        content?: string | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        }[] | {\n            /**\n             * Type of the content provided\n             */\n            type?: string;\n            text?: string;\n            image_url?: {\n                /**\n                 * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted\n                 */\n                url?: string;\n            };\n        };\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode;\n    /**\n     * JSON schema that should be fufilled for the response.\n     */\n    guided_json?: object;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ntype Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output = {\n    /**\n     * The generated text response from the model\n     */\n    response: string;\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * An array of tool calls requests made during the response generation\n     */\n    tool_calls?: {\n        /**\n         * The tool call id.\n         */\n        id?: string;\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type?: string;\n        /**\n         * Details of the function tool.\n         */\n        function?: {\n            /**\n             * The name of the tool to be called\n             */\n            name?: string;\n            /**\n             * The arguments passed to be passed to the tool call request\n             */\n            arguments?: object;\n        };\n    }[];\n};\ndeclare abstract class Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct {\n    inputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input;\n    postProcessedOutputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output;\n}\ntype Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Input = Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Prompt | Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages | Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Async_Batch;\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n    response_format?: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role: string;\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_1;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_1 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Async_Batch {\n    requests: (Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Prompt_1 | Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages_1)[];\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Prompt_1 {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n    response_format?: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_2;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_2 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages_1 {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role: string;\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_3;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_3 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ntype Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Output = Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Chat_Completion_Response | Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Text_Completion_Response | string | Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_AsyncResponse;\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Chat_Completion_Response {\n    /**\n     * Unique identifier for the completion\n     */\n    id?: string;\n    /**\n     * Object type identifier\n     */\n    object?: \"chat.completion\";\n    /**\n     * Unix timestamp of when the completion was created\n     */\n    created?: number;\n    /**\n     * Model used for the completion\n     */\n    model?: string;\n    /**\n     * List of completion choices\n     */\n    choices?: {\n        /**\n         * Index of the choice in the list\n         */\n        index?: number;\n        /**\n         * The message generated by the model\n         */\n        message?: {\n            /**\n             * Role of the message author\n             */\n            role: string;\n            /**\n             * The content of the message\n             */\n            content: string;\n            /**\n             * Internal reasoning content (if available)\n             */\n            reasoning_content?: string;\n            /**\n             * Tool calls made by the assistant\n             */\n            tool_calls?: {\n                /**\n                 * Unique identifier for the tool call\n                 */\n                id: string;\n                /**\n                 * Type of tool call\n                 */\n                type: \"function\";\n                function: {\n                    /**\n                     * Name of the function to call\n                     */\n                    name: string;\n                    /**\n                     * JSON string of arguments for the function\n                     */\n                    arguments: string;\n                };\n            }[];\n        };\n        /**\n         * Reason why the model stopped generating\n         */\n        finish_reason?: string;\n        /**\n         * Stop reason (may be null)\n         */\n        stop_reason?: string | null;\n        /**\n         * Log probabilities (if requested)\n         */\n        logprobs?: {} | null;\n    }[];\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * Log probabilities for the prompt (if requested)\n     */\n    prompt_logprobs?: {} | null;\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Text_Completion_Response {\n    /**\n     * Unique identifier for the completion\n     */\n    id?: string;\n    /**\n     * Object type identifier\n     */\n    object?: \"text_completion\";\n    /**\n     * Unix timestamp of when the completion was created\n     */\n    created?: number;\n    /**\n     * Model used for the completion\n     */\n    model?: string;\n    /**\n     * List of completion choices\n     */\n    choices?: {\n        /**\n         * Index of the choice in the list\n         */\n        index: number;\n        /**\n         * The generated text completion\n         */\n        text: string;\n        /**\n         * Reason why the model stopped generating\n         */\n        finish_reason: string;\n        /**\n         * Stop reason (may be null)\n         */\n        stop_reason?: string | null;\n        /**\n         * Log probabilities (if requested)\n         */\n        logprobs?: {} | null;\n        /**\n         * Log probabilities for the prompt (if requested)\n         */\n        prompt_logprobs?: {} | null;\n    }[];\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n}\ninterface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8 {\n    inputs: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Input;\n    postProcessedOutputs: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Output;\n}\ninterface Ai_Cf_Deepgram_Nova_3_Input {\n    audio: {\n        body: object;\n        contentType: string;\n    };\n    /**\n     * Sets how the model will interpret strings submitted to the custom_topic param. When strict, the model will only return topics submitted using the custom_topic param. When extended, the model will return its own detected topics in addition to those submitted using the custom_topic param.\n     */\n    custom_topic_mode?: \"extended\" | \"strict\";\n    /**\n     * Custom topics you want the model to detect within your input audio or text if present Submit up to 100\n     */\n    custom_topic?: string;\n    /**\n     * Sets how the model will interpret intents submitted to the custom_intent param. When strict, the model will only return intents submitted using the custom_intent param. When extended, the model will return its own detected intents in addition those submitted using the custom_intents param\n     */\n    custom_intent_mode?: \"extended\" | \"strict\";\n    /**\n     * Custom intents you want the model to detect within your input audio if present\n     */\n    custom_intent?: string;\n    /**\n     * Identifies and extracts key entities from content in submitted audio\n     */\n    detect_entities?: boolean;\n    /**\n     * Identifies the dominant language spoken in submitted audio\n     */\n    detect_language?: boolean;\n    /**\n     * Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0\n     */\n    diarize?: boolean;\n    /**\n     * Identify and extract key entities from content in submitted audio\n     */\n    dictation?: boolean;\n    /**\n     * Specify the expected encoding of your submitted audio\n     */\n    encoding?: \"linear16\" | \"flac\" | \"mulaw\" | \"amr-nb\" | \"amr-wb\" | \"opus\" | \"speex\" | \"g729\";\n    /**\n     * Arbitrary key-value pairs that are attached to the API response for usage in downstream processing\n     */\n    extra?: string;\n    /**\n     * Filler Words can help transcribe interruptions in your audio, like 'uh' and 'um'\n     */\n    filler_words?: boolean;\n    /**\n     * Key term prompting can boost or suppress specialized terminology and brands.\n     */\n    keyterm?: string;\n    /**\n     * Keywords can boost or suppress specialized terminology and brands.\n     */\n    keywords?: string;\n    /**\n     * The BCP-47 language tag that hints at the primary spoken language. Depending on the Model and API endpoint you choose only certain languages are available.\n     */\n    language?: string;\n    /**\n     * Spoken measurements will be converted to their corresponding abbreviations.\n     */\n    measurements?: boolean;\n    /**\n     * Opts out requests from the Deepgram Model Improvement Program. Refer to our Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip.\n     */\n    mip_opt_out?: boolean;\n    /**\n     * Mode of operation for the model representing broad area of topic that will be talked about in the supplied audio\n     */\n    mode?: \"general\" | \"medical\" | \"finance\";\n    /**\n     * Transcribe each audio channel independently.\n     */\n    multichannel?: boolean;\n    /**\n     * Numerals converts numbers from written format to numerical format.\n     */\n    numerals?: boolean;\n    /**\n     * Splits audio into paragraphs to improve transcript readability.\n     */\n    paragraphs?: boolean;\n    /**\n     * Profanity Filter looks for recognized profanity and converts it to the nearest recognized non-profane word or removes it from the transcript completely.\n     */\n    profanity_filter?: boolean;\n    /**\n     * Add punctuation and capitalization to the transcript.\n     */\n    punctuate?: boolean;\n    /**\n     * Redaction removes sensitive information from your transcripts.\n     */\n    redact?: string;\n    /**\n     * Search for terms or phrases in submitted audio and replaces them.\n     */\n    replace?: string;\n    /**\n     * Search for terms or phrases in submitted audio.\n     */\n    search?: string;\n    /**\n     * Recognizes the sentiment throughout a transcript or text.\n     */\n    sentiment?: boolean;\n    /**\n     * Apply formatting to transcript output. When set to true, additional formatting will be applied to transcripts to improve readability.\n     */\n    smart_format?: boolean;\n    /**\n     * Detect topics throughout a transcript or text.\n     */\n    topics?: boolean;\n    /**\n     * Segments speech into meaningful semantic units.\n     */\n    utterances?: boolean;\n    /**\n     * Seconds to wait before detecting a pause between words in submitted audio.\n     */\n    utt_split?: number;\n    /**\n     * The number of channels in the submitted audio\n     */\n    channels?: number;\n    /**\n     * Specifies whether the streaming endpoint should provide ongoing transcription updates as more audio is received. When set to true, the endpoint sends continuous updates, meaning transcription results may evolve over time. Note: Supported only for webosockets.\n     */\n    interim_results?: boolean;\n    /**\n     * Indicates how long model will wait to detect whether a speaker has finished speaking or pauses for a significant period of time. When set to a value, the streaming endpoint immediately finalizes the transcription for the processed time range and returns the transcript with a speech_final parameter set to true. Can also be set to false to disable endpointing\n     */\n    endpointing?: string;\n    /**\n     * Indicates that speech has started. You'll begin receiving Speech Started messages upon speech starting. Note: Supported only for webosockets.\n     */\n    vad_events?: boolean;\n    /**\n     * Indicates how long model will wait to send an UtteranceEnd message after a word has been transcribed. Use with interim_results. Note: Supported only for webosockets.\n     */\n    utterance_end_ms?: boolean;\n}\ninterface Ai_Cf_Deepgram_Nova_3_Output {\n    results?: {\n        channels?: {\n            alternatives?: {\n                confidence?: number;\n                transcript?: string;\n                words?: {\n                    confidence?: number;\n                    end?: number;\n                    start?: number;\n                    word?: string;\n                }[];\n            }[];\n        }[];\n        summary?: {\n            result?: string;\n            short?: string;\n        };\n        sentiments?: {\n            segments?: {\n                text?: string;\n                start_word?: number;\n                end_word?: number;\n                sentiment?: string;\n                sentiment_score?: number;\n            }[];\n            average?: {\n                sentiment?: string;\n                sentiment_score?: number;\n            };\n        };\n    };\n}\ndeclare abstract class Base_Ai_Cf_Deepgram_Nova_3 {\n    inputs: Ai_Cf_Deepgram_Nova_3_Input;\n    postProcessedOutputs: Ai_Cf_Deepgram_Nova_3_Output;\n}\ninterface Ai_Cf_Qwen_Qwen3_Embedding_0_6B_Input {\n    queries?: string | string[];\n    /**\n     * Optional instruction for the task\n     */\n    instruction?: string;\n    documents?: string | string[];\n    text?: string | string[];\n}\ninterface Ai_Cf_Qwen_Qwen3_Embedding_0_6B_Output {\n    data?: number[][];\n    shape?: number[];\n}\ndeclare abstract class Base_Ai_Cf_Qwen_Qwen3_Embedding_0_6B {\n    inputs: Ai_Cf_Qwen_Qwen3_Embedding_0_6B_Input;\n    postProcessedOutputs: Ai_Cf_Qwen_Qwen3_Embedding_0_6B_Output;\n}\ntype Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Input = {\n    /**\n     * readable stream with audio data and content-type specified for that data\n     */\n    audio: {\n        body: object;\n        contentType: string;\n    };\n    /**\n     * type of data PCM data that's sent to the inference server as raw array\n     */\n    dtype?: \"uint8\" | \"float32\" | \"float64\";\n} | {\n    /**\n     * base64 encoded audio data\n     */\n    audio: string;\n    /**\n     * type of data PCM data that's sent to the inference server as raw array\n     */\n    dtype?: \"uint8\" | \"float32\" | \"float64\";\n};\ninterface Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Output {\n    /**\n     * if true, end-of-turn was detected\n     */\n    is_complete?: boolean;\n    /**\n     * probability of the end-of-turn detection\n     */\n    probability?: number;\n}\ndeclare abstract class Base_Ai_Cf_Pipecat_Ai_Smart_Turn_V2 {\n    inputs: Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Input;\n    postProcessedOutputs: Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Output;\n}\ndeclare abstract class Base_Ai_Cf_Openai_Gpt_Oss_120B {\n    inputs: ResponsesInput;\n    postProcessedOutputs: ResponsesOutput;\n}\ndeclare abstract class Base_Ai_Cf_Openai_Gpt_Oss_20B {\n    inputs: ResponsesInput;\n    postProcessedOutputs: ResponsesOutput;\n}\ninterface Ai_Cf_Leonardo_Phoenix_1_0_Input {\n    /**\n     * A text description of the image you want to generate.\n     */\n    prompt: string;\n    /**\n     * Controls how closely the generated image should adhere to the prompt; higher values make the image more aligned with the prompt\n     */\n    guidance?: number;\n    /**\n     * Random seed for reproducibility of the image generation\n     */\n    seed?: number;\n    /**\n     * The height of the generated image in pixels\n     */\n    height?: number;\n    /**\n     * The width of the generated image in pixels\n     */\n    width?: number;\n    /**\n     * The number of diffusion steps; higher values can improve quality but take longer\n     */\n    num_steps?: number;\n    /**\n     * Specify what to exclude from the generated images\n     */\n    negative_prompt?: string;\n}\n/**\n * The generated image in JPEG format\n */\ntype Ai_Cf_Leonardo_Phoenix_1_0_Output = string;\ndeclare abstract class Base_Ai_Cf_Leonardo_Phoenix_1_0 {\n    inputs: Ai_Cf_Leonardo_Phoenix_1_0_Input;\n    postProcessedOutputs: Ai_Cf_Leonardo_Phoenix_1_0_Output;\n}\ninterface Ai_Cf_Leonardo_Lucid_Origin_Input {\n    /**\n     * A text description of the image you want to generate.\n     */\n    prompt: string;\n    /**\n     * Controls how closely the generated image should adhere to the prompt; higher values make the image more aligned with the prompt\n     */\n    guidance?: number;\n    /**\n     * Random seed for reproducibility of the image generation\n     */\n    seed?: number;\n    /**\n     * The height of the generated image in pixels\n     */\n    height?: number;\n    /**\n     * The width of the generated image in pixels\n     */\n    width?: number;\n    /**\n     * The number of diffusion steps; higher values can improve quality but take longer\n     */\n    num_steps?: number;\n    /**\n     * The number of diffusion steps; higher values can improve quality but take longer\n     */\n    steps?: number;\n}\ninterface Ai_Cf_Leonardo_Lucid_Origin_Output {\n    /**\n     * The generated image in Base64 format.\n     */\n    image?: string;\n}\ndeclare abstract class Base_Ai_Cf_Leonardo_Lucid_Origin {\n    inputs: Ai_Cf_Leonardo_Lucid_Origin_Input;\n    postProcessedOutputs: Ai_Cf_Leonardo_Lucid_Origin_Output;\n}\ninterface Ai_Cf_Deepgram_Aura_1_Input {\n    /**\n     * Speaker used to produce the audio.\n     */\n    speaker?: \"angus\" | \"asteria\" | \"arcas\" | \"orion\" | \"orpheus\" | \"athena\" | \"luna\" | \"zeus\" | \"perseus\" | \"helios\" | \"hera\" | \"stella\";\n    /**\n     * Encoding of the output audio.\n     */\n    encoding?: \"linear16\" | \"flac\" | \"mulaw\" | \"alaw\" | \"mp3\" | \"opus\" | \"aac\";\n    /**\n     * Container specifies the file format wrapper for the output audio. The available options depend on the encoding type..\n     */\n    container?: \"none\" | \"wav\" | \"ogg\";\n    /**\n     * The text content to be converted to speech\n     */\n    text: string;\n    /**\n     * Sample Rate specifies the sample rate for the output audio. Based on the encoding, different sample rates are supported. For some encodings, the sample rate is not configurable\n     */\n    sample_rate?: number;\n    /**\n     * The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type.\n     */\n    bit_rate?: number;\n}\n/**\n * The generated audio in MP3 format\n */\ntype Ai_Cf_Deepgram_Aura_1_Output = string;\ndeclare abstract class Base_Ai_Cf_Deepgram_Aura_1 {\n    inputs: Ai_Cf_Deepgram_Aura_1_Input;\n    postProcessedOutputs: Ai_Cf_Deepgram_Aura_1_Output;\n}\ninterface Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B_Input {\n    /**\n     * Input text to translate. Can be a single string or a list of strings.\n     */\n    text: string | string[];\n    /**\n     * Target langauge to translate to\n     */\n    target_language: \"asm_Beng\" | \"awa_Deva\" | \"ben_Beng\" | \"bho_Deva\" | \"brx_Deva\" | \"doi_Deva\" | \"eng_Latn\" | \"gom_Deva\" | \"gon_Deva\" | \"guj_Gujr\" | \"hin_Deva\" | \"hne_Deva\" | \"kan_Knda\" | \"kas_Arab\" | \"kas_Deva\" | \"kha_Latn\" | \"lus_Latn\" | \"mag_Deva\" | \"mai_Deva\" | \"mal_Mlym\" | \"mar_Deva\" | \"mni_Beng\" | \"mni_Mtei\" | \"npi_Deva\" | \"ory_Orya\" | \"pan_Guru\" | \"san_Deva\" | \"sat_Olck\" | \"snd_Arab\" | \"snd_Deva\" | \"tam_Taml\" | \"tel_Telu\" | \"urd_Arab\" | \"unr_Deva\";\n}\ninterface Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B_Output {\n    /**\n     * Translated texts\n     */\n    translations: string[];\n}\ndeclare abstract class Base_Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B {\n    inputs: Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B_Input;\n    postProcessedOutputs: Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B_Output;\n}\ntype Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Input = Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Prompt | Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages | Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Async_Batch;\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Prompt {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n    response_format?: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role: string;\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_1;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_1 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Async_Batch {\n    requests: (Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Prompt_1 | Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages_1)[];\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Prompt_1 {\n    /**\n     * The input text prompt for the model to generate a response.\n     */\n    prompt: string;\n    /**\n     * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model.\n     */\n    lora?: string;\n    response_format?: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_2;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_2 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages_1 {\n    /**\n     * An array of message objects representing the conversation history.\n     */\n    messages: {\n        /**\n         * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').\n         */\n        role: string;\n        /**\n         * The content of the message as a string.\n         */\n        content: string;\n    }[];\n    functions?: {\n        name: string;\n        code: string;\n    }[];\n    /**\n     * A list of tools available for the assistant to use.\n     */\n    tools?: ({\n        /**\n         * The name of the tool. More descriptive the better.\n         */\n        name: string;\n        /**\n         * A brief description of what the tool does.\n         */\n        description: string;\n        /**\n         * Schema defining the parameters accepted by the tool.\n         */\n        parameters: {\n            /**\n             * The type of the parameters object (usually 'object').\n             */\n            type: string;\n            /**\n             * List of required parameter names.\n             */\n            required?: string[];\n            /**\n             * Definitions of each parameter.\n             */\n            properties: {\n                [k: string]: {\n                    /**\n                     * The data type of the parameter.\n                     */\n                    type: string;\n                    /**\n                     * A description of the expected parameter.\n                     */\n                    description: string;\n                };\n            };\n        };\n    } | {\n        /**\n         * Specifies the type of tool (e.g., 'function').\n         */\n        type: string;\n        /**\n         * Details of the function tool.\n         */\n        function: {\n            /**\n             * The name of the function.\n             */\n            name: string;\n            /**\n             * A brief description of what the function does.\n             */\n            description: string;\n            /**\n             * Schema defining the parameters accepted by the function.\n             */\n            parameters: {\n                /**\n                 * The type of the parameters object (usually 'object').\n                 */\n                type: string;\n                /**\n                 * List of required parameter names.\n                 */\n                required?: string[];\n                /**\n                 * Definitions of each parameter.\n                 */\n                properties: {\n                    [k: string]: {\n                        /**\n                         * The data type of the parameter.\n                         */\n                        type: string;\n                        /**\n                         * A description of the expected parameter.\n                         */\n                        description: string;\n                    };\n                };\n            };\n        };\n    })[];\n    response_format?: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_3;\n    /**\n     * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.\n     */\n    raw?: boolean;\n    /**\n     * If true, the response will be streamed back incrementally using SSE, Server Sent Events.\n     */\n    stream?: boolean;\n    /**\n     * The maximum number of tokens to generate in the response.\n     */\n    max_tokens?: number;\n    /**\n     * Controls the randomness of the output; higher values produce more random results.\n     */\n    temperature?: number;\n    /**\n     * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.\n     */\n    top_p?: number;\n    /**\n     * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.\n     */\n    top_k?: number;\n    /**\n     * Random seed for reproducibility of the generation.\n     */\n    seed?: number;\n    /**\n     * Penalty for repeated tokens; higher values discourage repetition.\n     */\n    repetition_penalty?: number;\n    /**\n     * Decreases the likelihood of the model repeating the same lines verbatim.\n     */\n    frequency_penalty?: number;\n    /**\n     * Increases the likelihood of the model introducing new topics.\n     */\n    presence_penalty?: number;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_3 {\n    type?: \"json_object\" | \"json_schema\";\n    json_schema?: unknown;\n}\ntype Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Output = Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Chat_Completion_Response | Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Text_Completion_Response | string | Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_AsyncResponse;\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Chat_Completion_Response {\n    /**\n     * Unique identifier for the completion\n     */\n    id?: string;\n    /**\n     * Object type identifier\n     */\n    object?: \"chat.completion\";\n    /**\n     * Unix timestamp of when the completion was created\n     */\n    created?: number;\n    /**\n     * Model used for the completion\n     */\n    model?: string;\n    /**\n     * List of completion choices\n     */\n    choices?: {\n        /**\n         * Index of the choice in the list\n         */\n        index?: number;\n        /**\n         * The message generated by the model\n         */\n        message?: {\n            /**\n             * Role of the message author\n             */\n            role: string;\n            /**\n             * The content of the message\n             */\n            content: string;\n            /**\n             * Internal reasoning content (if available)\n             */\n            reasoning_content?: string;\n            /**\n             * Tool calls made by the assistant\n             */\n            tool_calls?: {\n                /**\n                 * Unique identifier for the tool call\n                 */\n                id: string;\n                /**\n                 * Type of tool call\n                 */\n                type: \"function\";\n                function: {\n                    /**\n                     * Name of the function to call\n                     */\n                    name: string;\n                    /**\n                     * JSON string of arguments for the function\n                     */\n                    arguments: string;\n                };\n            }[];\n        };\n        /**\n         * Reason why the model stopped generating\n         */\n        finish_reason?: string;\n        /**\n         * Stop reason (may be null)\n         */\n        stop_reason?: string | null;\n        /**\n         * Log probabilities (if requested)\n         */\n        logprobs?: {} | null;\n    }[];\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n    /**\n     * Log probabilities for the prompt (if requested)\n     */\n    prompt_logprobs?: {} | null;\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Text_Completion_Response {\n    /**\n     * Unique identifier for the completion\n     */\n    id?: string;\n    /**\n     * Object type identifier\n     */\n    object?: \"text_completion\";\n    /**\n     * Unix timestamp of when the completion was created\n     */\n    created?: number;\n    /**\n     * Model used for the completion\n     */\n    model?: string;\n    /**\n     * List of completion choices\n     */\n    choices?: {\n        /**\n         * Index of the choice in the list\n         */\n        index: number;\n        /**\n         * The generated text completion\n         */\n        text: string;\n        /**\n         * Reason why the model stopped generating\n         */\n        finish_reason: string;\n        /**\n         * Stop reason (may be null)\n         */\n        stop_reason?: string | null;\n        /**\n         * Log probabilities (if requested)\n         */\n        logprobs?: {} | null;\n        /**\n         * Log probabilities for the prompt (if requested)\n         */\n        prompt_logprobs?: {} | null;\n    }[];\n    /**\n     * Usage statistics for the inference request\n     */\n    usage?: {\n        /**\n         * Total number of tokens in input\n         */\n        prompt_tokens?: number;\n        /**\n         * Total number of tokens in output\n         */\n        completion_tokens?: number;\n        /**\n         * Total number of input and output tokens\n         */\n        total_tokens?: number;\n    };\n}\ninterface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_AsyncResponse {\n    /**\n     * The async request id that can be used to obtain the results.\n     */\n    request_id?: string;\n}\ndeclare abstract class Base_Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It {\n    inputs: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Input;\n    postProcessedOutputs: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Output;\n}\ninterface Ai_Cf_Pfnet_Plamo_Embedding_1B_Input {\n    /**\n     * Input text to embed. Can be a single string or a list of strings.\n     */\n    text: string | string[];\n}\ninterface Ai_Cf_Pfnet_Plamo_Embedding_1B_Output {\n    /**\n     * Embedding vectors, where each vector is a list of floats.\n     */\n    data: number[][];\n    /**\n     * Shape of the embedding data as [number_of_embeddings, embedding_dimension].\n     *\n     * @minItems 2\n     * @maxItems 2\n     */\n    shape: [\n        number,\n        number\n    ];\n}\ndeclare abstract class Base_Ai_Cf_Pfnet_Plamo_Embedding_1B {\n    inputs: Ai_Cf_Pfnet_Plamo_Embedding_1B_Input;\n    postProcessedOutputs: Ai_Cf_Pfnet_Plamo_Embedding_1B_Output;\n}\ninterface Ai_Cf_Deepgram_Flux_Input {\n    /**\n     * Encoding of the audio stream. Currently only supports raw signed little-endian 16-bit PCM.\n     */\n    encoding: \"linear16\";\n    /**\n     * Sample rate of the audio stream in Hz.\n     */\n    sample_rate: string;\n    /**\n     * End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid Values 0.3 - 0.9.\n     */\n    eager_eot_threshold?: string;\n    /**\n     * End-of-turn confidence required to finish a turn. Valid Values 0.5 - 0.9.\n     */\n    eot_threshold?: string;\n    /**\n     * A turn will be finished when this much time has passed after speech, regardless of EOT confidence.\n     */\n    eot_timeout_ms?: string;\n    /**\n     * Keyterm prompting can improve recognition of specialized terminology. Pass multiple keyterm query parameters to boost multiple keyterms.\n     */\n    keyterm?: string;\n    /**\n     * Opts out requests from the Deepgram Model Improvement Program. Refer to Deepgram Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip\n     */\n    mip_opt_out?: \"true\" | \"false\";\n    /**\n     * Label your requests for the purpose of identification during usage reporting\n     */\n    tag?: string;\n}\n/**\n * Output will be returned as websocket messages.\n */\ninterface Ai_Cf_Deepgram_Flux_Output {\n    /**\n     * The unique identifier of the request (uuid)\n     */\n    request_id?: string;\n    /**\n     * Starts at 0 and increments for each message the server sends to the client.\n     */\n    sequence_id?: number;\n    /**\n     * The type of event being reported.\n     */\n    event?: \"Update\" | \"StartOfTurn\" | \"EagerEndOfTurn\" | \"TurnResumed\" | \"EndOfTurn\";\n    /**\n     * The index of the current turn\n     */\n    turn_index?: number;\n    /**\n     * Start time in seconds of the audio range that was transcribed\n     */\n    audio_window_start?: number;\n    /**\n     * End time in seconds of the audio range that was transcribed\n     */\n    audio_window_end?: number;\n    /**\n     * Text that was said over the course of the current turn\n     */\n    transcript?: string;\n    /**\n     * The words in the transcript\n     */\n    words?: {\n        /**\n         * The individual punctuated, properly-cased word from the transcript\n         */\n        word: string;\n        /**\n         * Confidence that this word was transcribed correctly\n         */\n        confidence: number;\n    }[];\n    /**\n     * Confidence that no more speech is coming in this turn\n     */\n    end_of_turn_confidence?: number;\n}\ndeclare abstract class Base_Ai_Cf_Deepgram_Flux {\n    inputs: Ai_Cf_Deepgram_Flux_Input;\n    postProcessedOutputs: Ai_Cf_Deepgram_Flux_Output;\n}\ninterface Ai_Cf_Deepgram_Aura_2_En_Input {\n    /**\n     * Speaker used to produce the audio.\n     */\n    speaker?: \"amalthea\" | \"andromeda\" | \"apollo\" | \"arcas\" | \"aries\" | \"asteria\" | \"athena\" | \"atlas\" | \"aurora\" | \"callista\" | \"cora\" | \"cordelia\" | \"delia\" | \"draco\" | \"electra\" | \"harmonia\" | \"helena\" | \"hera\" | \"hermes\" | \"hyperion\" | \"iris\" | \"janus\" | \"juno\" | \"jupiter\" | \"luna\" | \"mars\" | \"minerva\" | \"neptune\" | \"odysseus\" | \"ophelia\" | \"orion\" | \"orpheus\" | \"pandora\" | \"phoebe\" | \"pluto\" | \"saturn\" | \"thalia\" | \"theia\" | \"vesta\" | \"zeus\";\n    /**\n     * Encoding of the output audio.\n     */\n    encoding?: \"linear16\" | \"flac\" | \"mulaw\" | \"alaw\" | \"mp3\" | \"opus\" | \"aac\";\n    /**\n     * Container specifies the file format wrapper for the output audio. The available options depend on the encoding type..\n     */\n    container?: \"none\" | \"wav\" | \"ogg\";\n    /**\n     * The text content to be converted to speech\n     */\n    text: string;\n    /**\n     * Sample Rate specifies the sample rate for the output audio. Based on the encoding, different sample rates are supported. For some encodings, the sample rate is not configurable\n     */\n    sample_rate?: number;\n    /**\n     * The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type.\n     */\n    bit_rate?: number;\n}\n/**\n * The generated audio in MP3 format\n */\ntype Ai_Cf_Deepgram_Aura_2_En_Output = string;\ndeclare abstract class Base_Ai_Cf_Deepgram_Aura_2_En {\n    inputs: Ai_Cf_Deepgram_Aura_2_En_Input;\n    postProcessedOutputs: Ai_Cf_Deepgram_Aura_2_En_Output;\n}\ninterface Ai_Cf_Deepgram_Aura_2_Es_Input {\n    /**\n     * Speaker used to produce the audio.\n     */\n    speaker?: \"sirio\" | \"nestor\" | \"carina\" | \"celeste\" | \"alvaro\" | \"diana\" | \"aquila\" | \"selena\" | \"estrella\" | \"javier\";\n    /**\n     * Encoding of the output audio.\n     */\n    encoding?: \"linear16\" | \"flac\" | \"mulaw\" | \"alaw\" | \"mp3\" | \"opus\" | \"aac\";\n    /**\n     * Container specifies the file format wrapper for the output audio. The available options depend on the encoding type..\n     */\n    container?: \"none\" | \"wav\" | \"ogg\";\n    /**\n     * The text content to be converted to speech\n     */\n    text: string;\n    /**\n     * Sample Rate specifies the sample rate for the output audio. Based on the encoding, different sample rates are supported. For some encodings, the sample rate is not configurable\n     */\n    sample_rate?: number;\n    /**\n     * The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type.\n     */\n    bit_rate?: number;\n}\n/**\n * The generated audio in MP3 format\n */\ntype Ai_Cf_Deepgram_Aura_2_Es_Output = string;\ndeclare abstract class Base_Ai_Cf_Deepgram_Aura_2_Es {\n    inputs: Ai_Cf_Deepgram_Aura_2_Es_Input;\n    postProcessedOutputs: Ai_Cf_Deepgram_Aura_2_Es_Output;\n}\ninterface AiModels {\n    \"@cf/huggingface/distilbert-sst-2-int8\": BaseAiTextClassification;\n    \"@cf/stabilityai/stable-diffusion-xl-base-1.0\": BaseAiTextToImage;\n    \"@cf/runwayml/stable-diffusion-v1-5-inpainting\": BaseAiTextToImage;\n    \"@cf/runwayml/stable-diffusion-v1-5-img2img\": BaseAiTextToImage;\n    \"@cf/lykon/dreamshaper-8-lcm\": BaseAiTextToImage;\n    \"@cf/bytedance/stable-diffusion-xl-lightning\": BaseAiTextToImage;\n    \"@cf/myshell-ai/melotts\": BaseAiTextToSpeech;\n    \"@cf/google/embeddinggemma-300m\": BaseAiTextEmbeddings;\n    \"@cf/microsoft/resnet-50\": BaseAiImageClassification;\n    \"@cf/meta/llama-2-7b-chat-int8\": BaseAiTextGeneration;\n    \"@cf/mistral/mistral-7b-instruct-v0.1\": BaseAiTextGeneration;\n    \"@cf/meta/llama-2-7b-chat-fp16\": BaseAiTextGeneration;\n    \"@hf/thebloke/llama-2-13b-chat-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/mistral-7b-instruct-v0.1-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/zephyr-7b-beta-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/openhermes-2.5-mistral-7b-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/neural-chat-7b-v3-1-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/llamaguard-7b-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/deepseek-coder-6.7b-base-awq\": BaseAiTextGeneration;\n    \"@hf/thebloke/deepseek-coder-6.7b-instruct-awq\": BaseAiTextGeneration;\n    \"@cf/deepseek-ai/deepseek-math-7b-instruct\": BaseAiTextGeneration;\n    \"@cf/defog/sqlcoder-7b-2\": BaseAiTextGeneration;\n    \"@cf/openchat/openchat-3.5-0106\": BaseAiTextGeneration;\n    \"@cf/tiiuae/falcon-7b-instruct\": BaseAiTextGeneration;\n    \"@cf/thebloke/discolm-german-7b-v1-awq\": BaseAiTextGeneration;\n    \"@cf/qwen/qwen1.5-0.5b-chat\": BaseAiTextGeneration;\n    \"@cf/qwen/qwen1.5-7b-chat-awq\": BaseAiTextGeneration;\n    \"@cf/qwen/qwen1.5-14b-chat-awq\": BaseAiTextGeneration;\n    \"@cf/tinyllama/tinyllama-1.1b-chat-v1.0\": BaseAiTextGeneration;\n    \"@cf/microsoft/phi-2\": BaseAiTextGeneration;\n    \"@cf/qwen/qwen1.5-1.8b-chat\": BaseAiTextGeneration;\n    \"@cf/mistral/mistral-7b-instruct-v0.2-lora\": BaseAiTextGeneration;\n    \"@hf/nousresearch/hermes-2-pro-mistral-7b\": BaseAiTextGeneration;\n    \"@hf/nexusflow/starling-lm-7b-beta\": BaseAiTextGeneration;\n    \"@hf/google/gemma-7b-it\": BaseAiTextGeneration;\n    \"@cf/meta-llama/llama-2-7b-chat-hf-lora\": BaseAiTextGeneration;\n    \"@cf/google/gemma-2b-it-lora\": BaseAiTextGeneration;\n    \"@cf/google/gemma-7b-it-lora\": BaseAiTextGeneration;\n    \"@hf/mistral/mistral-7b-instruct-v0.2\": BaseAiTextGeneration;\n    \"@cf/meta/llama-3-8b-instruct\": BaseAiTextGeneration;\n    \"@cf/fblgit/una-cybertron-7b-v2-bf16\": BaseAiTextGeneration;\n    \"@cf/meta/llama-3-8b-instruct-awq\": BaseAiTextGeneration;\n    \"@cf/meta/llama-3.1-8b-instruct-fp8\": BaseAiTextGeneration;\n    \"@cf/meta/llama-3.1-8b-instruct-awq\": BaseAiTextGeneration;\n    \"@cf/meta/llama-3.2-3b-instruct\": BaseAiTextGeneration;\n    \"@cf/meta/llama-3.2-1b-instruct\": BaseAiTextGeneration;\n    \"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b\": BaseAiTextGeneration;\n    \"@cf/ibm-granite/granite-4.0-h-micro\": BaseAiTextGeneration;\n    \"@cf/facebook/bart-large-cnn\": BaseAiSummarization;\n    \"@cf/llava-hf/llava-1.5-7b-hf\": BaseAiImageToText;\n    \"@cf/baai/bge-base-en-v1.5\": Base_Ai_Cf_Baai_Bge_Base_En_V1_5;\n    \"@cf/openai/whisper\": Base_Ai_Cf_Openai_Whisper;\n    \"@cf/meta/m2m100-1.2b\": Base_Ai_Cf_Meta_M2M100_1_2B;\n    \"@cf/baai/bge-small-en-v1.5\": Base_Ai_Cf_Baai_Bge_Small_En_V1_5;\n    \"@cf/baai/bge-large-en-v1.5\": Base_Ai_Cf_Baai_Bge_Large_En_V1_5;\n    \"@cf/unum/uform-gen2-qwen-500m\": Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M;\n    \"@cf/openai/whisper-tiny-en\": Base_Ai_Cf_Openai_Whisper_Tiny_En;\n    \"@cf/openai/whisper-large-v3-turbo\": Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo;\n    \"@cf/baai/bge-m3\": Base_Ai_Cf_Baai_Bge_M3;\n    \"@cf/black-forest-labs/flux-1-schnell\": Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell;\n    \"@cf/meta/llama-3.2-11b-vision-instruct\": Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct;\n    \"@cf/meta/llama-3.3-70b-instruct-fp8-fast\": Base_Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast;\n    \"@cf/meta/llama-guard-3-8b\": Base_Ai_Cf_Meta_Llama_Guard_3_8B;\n    \"@cf/baai/bge-reranker-base\": Base_Ai_Cf_Baai_Bge_Reranker_Base;\n    \"@cf/qwen/qwen2.5-coder-32b-instruct\": Base_Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct;\n    \"@cf/qwen/qwq-32b\": Base_Ai_Cf_Qwen_Qwq_32B;\n    \"@cf/mistralai/mistral-small-3.1-24b-instruct\": Base_Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct;\n    \"@cf/google/gemma-3-12b-it\": Base_Ai_Cf_Google_Gemma_3_12B_It;\n    \"@cf/meta/llama-4-scout-17b-16e-instruct\": Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct;\n    \"@cf/qwen/qwen3-30b-a3b-fp8\": Base_Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8;\n    \"@cf/deepgram/nova-3\": Base_Ai_Cf_Deepgram_Nova_3;\n    \"@cf/qwen/qwen3-embedding-0.6b\": Base_Ai_Cf_Qwen_Qwen3_Embedding_0_6B;\n    \"@cf/pipecat-ai/smart-turn-v2\": Base_Ai_Cf_Pipecat_Ai_Smart_Turn_V2;\n    \"@cf/openai/gpt-oss-120b\": Base_Ai_Cf_Openai_Gpt_Oss_120B;\n    \"@cf/openai/gpt-oss-20b\": Base_Ai_Cf_Openai_Gpt_Oss_20B;\n    \"@cf/leonardo/phoenix-1.0\": Base_Ai_Cf_Leonardo_Phoenix_1_0;\n    \"@cf/leonardo/lucid-origin\": Base_Ai_Cf_Leonardo_Lucid_Origin;\n    \"@cf/deepgram/aura-1\": Base_Ai_Cf_Deepgram_Aura_1;\n    \"@cf/ai4bharat/indictrans2-en-indic-1B\": Base_Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B;\n    \"@cf/aisingapore/gemma-sea-lion-v4-27b-it\": Base_Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It;\n    \"@cf/pfnet/plamo-embedding-1b\": Base_Ai_Cf_Pfnet_Plamo_Embedding_1B;\n    \"@cf/deepgram/flux\": Base_Ai_Cf_Deepgram_Flux;\n    \"@cf/deepgram/aura-2-en\": Base_Ai_Cf_Deepgram_Aura_2_En;\n    \"@cf/deepgram/aura-2-es\": Base_Ai_Cf_Deepgram_Aura_2_Es;\n}\ntype AiOptions = {\n    /**\n     * Send requests as an asynchronous batch job, only works for supported models\n     * https://developers.cloudflare.com/workers-ai/features/batch-api\n     */\n    queueRequest?: boolean;\n    /**\n     * Establish websocket connections, only works for supported models\n     */\n    websocket?: boolean;\n    /**\n     * Tag your requests to group and view them in Cloudflare dashboard.\n     *\n     * Rules:\n     * Tags must only contain letters, numbers, and the symbols: : - . / @\n     * Each tag can have maximum 50 characters.\n     * Maximum 5 tags are allowed each request.\n     * Duplicate tags will removed.\n     */\n    tags?: string[];\n    gateway?: GatewayOptions;\n    returnRawResponse?: boolean;\n    prefix?: string;\n    extraHeaders?: object;\n};\ntype AiModelsSearchParams = {\n    author?: string;\n    hide_experimental?: boolean;\n    page?: number;\n    per_page?: number;\n    search?: string;\n    source?: number;\n    task?: string;\n};\ntype AiModelsSearchObject = {\n    id: string;\n    source: number;\n    name: string;\n    description: string;\n    task: {\n        id: string;\n        name: string;\n        description: string;\n    };\n    tags: string[];\n    properties: {\n        property_id: string;\n        value: string;\n    }[];\n};\ninterface InferenceUpstreamError extends Error {\n}\ninterface AiInternalError extends Error {\n}\ntype AiModelListType = Record<string, any>;\ndeclare abstract class Ai<AiModelList extends AiModelListType = AiModels> {\n    aiGatewayLogId: string | null;\n    gateway(gatewayId: string): AiGateway;\n    autorag(autoragId: string): AutoRAG;\n    run<Name extends keyof AiModelList, Options extends AiOptions, InputOptions extends AiModelList[Name][\"inputs\"]>(model: Name, inputs: InputOptions, options?: Options): Promise<Options extends {\n        returnRawResponse: true;\n    } | {\n        websocket: true;\n    } ? Response : InputOptions extends {\n        stream: true;\n    } ? ReadableStream : AiModelList[Name][\"postProcessedOutputs\"]>;\n    models(params?: AiModelsSearchParams): Promise<AiModelsSearchObject[]>;\n    toMarkdown(): ToMarkdownService;\n    toMarkdown(files: MarkdownDocument[], options?: ConversionRequestOptions): Promise<ConversionResponse[]>;\n    toMarkdown(files: MarkdownDocument, options?: ConversionRequestOptions): Promise<ConversionResponse>;\n}\ntype GatewayRetries = {\n    maxAttempts?: 1 | 2 | 3 | 4 | 5;\n    retryDelayMs?: number;\n    backoff?: 'constant' | 'linear' | 'exponential';\n};\ntype GatewayOptions = {\n    id: string;\n    cacheKey?: string;\n    cacheTtl?: number;\n    skipCache?: boolean;\n    metadata?: Record<string, number | string | boolean | null | bigint>;\n    collectLog?: boolean;\n    eventId?: string;\n    requestTimeoutMs?: number;\n    retries?: GatewayRetries;\n};\ntype UniversalGatewayOptions = Exclude<GatewayOptions, 'id'> & {\n    /**\n     ** @deprecated\n     */\n    id?: string;\n};\ntype AiGatewayPatchLog = {\n    score?: number | null;\n    feedback?: -1 | 1 | null;\n    metadata?: Record<string, number | string | boolean | null | bigint> | null;\n};\ntype AiGatewayLog = {\n    id: string;\n    provider: string;\n    model: string;\n    model_type?: string;\n    path: string;\n    duration: number;\n    request_type?: string;\n    request_content_type?: string;\n    status_code: number;\n    response_content_type?: string;\n    success: boolean;\n    cached: boolean;\n    tokens_in?: number;\n    tokens_out?: number;\n    metadata?: Record<string, number | string | boolean | null | bigint>;\n    step?: number;\n    cost?: number;\n    custom_cost?: boolean;\n    request_size: number;\n    request_head?: string;\n    request_head_complete: boolean;\n    response_size: number;\n    response_head?: string;\n    response_head_complete: boolean;\n    created_at: Date;\n};\ntype AIGatewayProviders = 'workers-ai' | 'anthropic' | 'aws-bedrock' | 'azure-openai' | 'google-vertex-ai' | 'huggingface' | 'openai' | 'perplexity-ai' | 'replicate' | 'groq' | 'cohere' | 'google-ai-studio' | 'mistral' | 'grok' | 'openrouter' | 'deepseek' | 'cerebras' | 'cartesia' | 'elevenlabs' | 'adobe-firefly';\ntype AIGatewayHeaders = {\n    'cf-aig-metadata': Record<string, number | string | boolean | null | bigint> | string;\n    'cf-aig-custom-cost': {\n        per_token_in?: number;\n        per_token_out?: number;\n    } | {\n        total_cost?: number;\n    } | string;\n    'cf-aig-cache-ttl': number | string;\n    'cf-aig-skip-cache': boolean | string;\n    'cf-aig-cache-key': string;\n    'cf-aig-event-id': string;\n    'cf-aig-request-timeout': number | string;\n    'cf-aig-max-attempts': number | string;\n    'cf-aig-retry-delay': number | string;\n    'cf-aig-backoff': string;\n    'cf-aig-collect-log': boolean | string;\n    Authorization: string;\n    'Content-Type': string;\n    [key: string]: string | number | boolean | object;\n};\ntype AIGatewayUniversalRequest = {\n    provider: AIGatewayProviders | string;  \n    endpoint: string;\n    headers: Partial<AIGatewayHeaders>;\n    query: unknown;\n};\ninterface AiGatewayInternalError extends Error {\n}\ninterface AiGatewayLogNotFound extends Error {\n}\ndeclare abstract class AiGateway {\n    patchLog(logId: string, data: AiGatewayPatchLog): Promise<void>;\n    getLog(logId: string): Promise<AiGatewayLog>;\n    run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[], options?: {\n        gateway?: UniversalGatewayOptions;\n        extraHeaders?: object;\n    }): Promise<Response>;\n    getUrl(provider?: AIGatewayProviders | string): Promise<string>;  \n}\ninterface AutoRAGInternalError extends Error {\n}\ninterface AutoRAGNotFoundError extends Error {\n}\ninterface AutoRAGUnauthorizedError extends Error {\n}\ninterface AutoRAGNameNotSetError extends Error {\n}\ntype ComparisonFilter = {\n    key: string;\n    type: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';\n    value: string | number | boolean;\n};\ntype CompoundFilter = {\n    type: 'and' | 'or';\n    filters: ComparisonFilter[];\n};\ntype AutoRagSearchRequest = {\n    query: string;\n    filters?: CompoundFilter | ComparisonFilter;\n    max_num_results?: number;\n    ranking_options?: {\n        ranker?: string;\n        score_threshold?: number;\n    };\n    reranking?: {\n        enabled?: boolean;\n        model?: string;\n    };\n    rewrite_query?: boolean;\n};\ntype AutoRagAiSearchRequest = AutoRagSearchRequest & {\n    stream?: boolean;\n    system_prompt?: string;\n};\ntype AutoRagAiSearchRequestStreaming = Omit<AutoRagAiSearchRequest, 'stream'> & {\n    stream: true;\n};\ntype AutoRagSearchResponse = {\n    object: 'vector_store.search_results.page';\n    search_query: string;\n    data: {\n        file_id: string;\n        filename: string;\n        score: number;\n        attributes: Record<string, string | number | boolean | null>;\n        content: {\n            type: 'text';\n            text: string;\n        }[];\n    }[];\n    has_more: boolean;\n    next_page: string | null;\n};\ntype AutoRagListResponse = {\n    id: string;\n    enable: boolean;\n    type: string;\n    source: string;\n    vectorize_name: string;\n    paused: boolean;\n    status: string;\n}[];\ntype AutoRagAiSearchResponse = AutoRagSearchResponse & {\n    response: string;\n};\ndeclare abstract class AutoRAG {\n    list(): Promise<AutoRagListResponse>;\n    search(params: AutoRagSearchRequest): Promise<AutoRagSearchResponse>;\n    aiSearch(params: AutoRagAiSearchRequestStreaming): Promise<Response>;\n    aiSearch(params: AutoRagAiSearchRequest): Promise<AutoRagAiSearchResponse>;\n    aiSearch(params: AutoRagAiSearchRequest): Promise<AutoRagAiSearchResponse | Response>;\n}\ninterface BasicImageTransformations {\n    /**\n     * Maximum width in image pixels. The value must be an integer.\n     */\n    width?: number;\n    /**\n     * Maximum height in image pixels. The value must be an integer.\n     */\n    height?: number;\n    /**\n     * Resizing mode as a string. It affects interpretation of width and height\n     * options:\n     *  - scale-down: Similar to contain, but the image is never enlarged. If\n     *    the image is larger than given width or height, it will be resized.\n     *    Otherwise its original size will be kept.\n     *  - contain: Resizes to maximum size that fits within the given width and\n     *    height. If only a single dimension is given (e.g. only width), the\n     *    image will be shrunk or enlarged to exactly match that dimension.\n     *    Aspect ratio is always preserved.\n     *  - cover: Resizes (shrinks or enlarges) to fill the entire area of width\n     *    and height. If the image has an aspect ratio different from the ratio\n     *    of width and height, it will be cropped to fit.\n     *  - crop: The image will be shrunk and cropped to fit within the area\n     *    specified by width and height. The image will not be enlarged. For images\n     *    smaller than the given dimensions it's the same as scale-down. For\n     *    images larger than the given dimensions, it's the same as cover.\n     *    See also trim.\n     *  - pad: Resizes to the maximum size that fits within the given width and\n     *    height, and then fills the remaining area with a background color\n     *    (white by default). Use of this mode is not recommended, as the same\n     *    effect can be more efficiently achieved with the contain mode and the\n     *    CSS object-fit: contain property.\n     *  - squeeze: Stretches and deforms to the width and height given, even if it\n     *    breaks aspect ratio\n     */\n    fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\" | \"squeeze\";\n    /**\n     * Image segmentation using artificial intelligence models. Sets pixels not\n     * within selected segment area to transparent e.g \"foreground\" sets every\n     * background pixel as transparent.\n     */\n    segment?: \"foreground\";\n    /**\n     * When cropping with fit: \"cover\", this defines the side or point that should\n     * be left uncropped. The value is either a string\n     * \"left\", \"right\", \"top\", \"bottom\", \"auto\", or \"center\" (the default),\n     * or an object {x, y} containing focal point coordinates in the original\n     * image expressed as fractions ranging from 0.0 (top or left) to 1.0\n     * (bottom or right), 0.5 being the center. {fit: \"cover\", gravity: \"top\"} will\n     * crop bottom or left and right sides as necessary, but won’t crop anything\n     * from the top. {fit: \"cover\", gravity: {x:0.5, y:0.2}} will crop each side to\n     * preserve as much as possible around a point at 20% of the height of the\n     * source image.\n     */\n    gravity?: 'face' | 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | BasicImageTransformationsGravityCoordinates;\n    /**\n     * Background color to add underneath the image. Applies only to images with\n     * transparency (such as PNG). Accepts any CSS color (#RRGGBB, rgba(…),\n     * hsl(…), etc.)\n     */\n    background?: string;\n    /**\n     * Number of degrees (90, 180, 270) to rotate the image by. width and height\n     * options refer to axes after rotation.\n     */\n    rotate?: 0 | 90 | 180 | 270 | 360;\n}\ninterface BasicImageTransformationsGravityCoordinates {\n    x?: number;\n    y?: number;\n    mode?: 'remainder' | 'box-center';\n}\n/**\n * In addition to the properties you can set in the RequestInit dict\n * that you pass as an argument to the Request constructor, you can\n * set certain properties of a `cf` object to control how Cloudflare\n * features are applied to that new Request.\n *\n * Note: Currently, these properties cannot be tested in the\n * playground.\n */\ninterface RequestInitCfProperties extends Record<string, unknown> {\n    cacheEverything?: boolean;\n    /**\n     * A request's cache key is what determines if two requests are\n     * \"the same\" for caching purposes. If a request has the same cache key\n     * as some previous request, then we can serve the same cached response for\n     * both. (e.g. 'some-key')\n     *\n     * Only available for Enterprise customers.\n     */\n    cacheKey?: string;\n    /**\n     * This allows you to append additional Cache-Tag response headers\n     * to the origin response without modifications to the origin server.\n     * This will allow for greater control over the Purge by Cache Tag feature\n     * utilizing changes only in the Workers process.\n     *\n     * Only available for Enterprise customers.\n     */\n    cacheTags?: string[];\n    /**\n     * Force response to be cached for a given number of seconds. (e.g. 300)\n     */\n    cacheTtl?: number;\n    /**\n     * Force response to be cached for a given number of seconds based on the Origin status code.\n     * (e.g. { '200-299': 86400, '404': 1, '500-599': 0 })\n     */\n    cacheTtlByStatus?: Record<string, number>;\n    scrapeShield?: boolean;\n    apps?: boolean;\n    image?: RequestInitCfPropertiesImage;\n    minify?: RequestInitCfPropertiesImageMinify;\n    mirage?: boolean;\n    polish?: \"lossy\" | \"lossless\" | \"off\";\n    r2?: RequestInitCfPropertiesR2;\n    /**\n     * Redirects the request to an alternate origin server. You can use this,\n     * for example, to implement load balancing across several origins.\n     * (e.g.us-east.example.com)\n     *\n     * Note - For security reasons, the hostname set in resolveOverride must\n     * be proxied on the same Cloudflare zone of the incoming request.\n     * Otherwise, the setting is ignored. CNAME hosts are allowed, so to\n     * resolve to a host under a different domain or a DNS only domain first\n     * declare a CNAME record within your own zone’s DNS mapping to the\n     * external hostname, set proxy on Cloudflare, then set resolveOverride\n     * to point to that CNAME record.\n     */\n    resolveOverride?: string;\n}\ninterface RequestInitCfPropertiesImageDraw extends BasicImageTransformations {\n    /**\n     * Absolute URL of the image file to use for the drawing. It can be any of\n     * the supported file formats. For drawing of watermarks or non-rectangular\n     * overlays we recommend using PNG or WebP images.\n     */\n    url: string;\n    /**\n     * Floating-point number between 0 (transparent) and 1 (opaque).\n     * For example, opacity: 0.5 makes overlay semitransparent.\n     */\n    opacity?: number;\n    /**\n     * - If set to true, the overlay image will be tiled to cover the entire\n     *   area. This is useful for stock-photo-like watermarks.\n     * - If set to \"x\", the overlay image will be tiled horizontally only\n     *   (form a line).\n     * - If set to \"y\", the overlay image will be tiled vertically only\n     *   (form a line).\n     */\n    repeat?: true | \"x\" | \"y\";\n    /**\n     * Position of the overlay image relative to a given edge. Each property is\n     * an offset in pixels. 0 aligns exactly to the edge. For example, left: 10\n     * positions left side of the overlay 10 pixels from the left edge of the\n     * image it's drawn over. bottom: 0 aligns bottom of the overlay with bottom\n     * of the background image.\n     *\n     * Setting both left & right, or both top & bottom is an error.\n     *\n     * If no position is specified, the image will be centered.\n     */\n    top?: number;\n    left?: number;\n    bottom?: number;\n    right?: number;\n}\ninterface RequestInitCfPropertiesImage extends BasicImageTransformations {\n    /**\n     * Device Pixel Ratio. Default 1. Multiplier for width/height that makes it\n     * easier to specify higher-DPI sizes in <img srcset>.\n     */\n    dpr?: number;\n    /**\n     * Allows you to trim your image. Takes dpr into account and is performed before\n     * resizing or rotation.\n     *\n     * It can be used as:\n     * - left, top, right, bottom - it will specify the number of pixels to cut\n     *   off each side\n     * - width, height - the width/height you'd like to end up with - can be used\n     *   in combination with the properties above\n     * - border - this will automatically trim the surroundings of an image based on\n     *   it's color. It consists of three properties:\n     *    - color: rgb or hex representation of the color you wish to trim (todo: verify the rgba bit)\n     *    - tolerance: difference from color to treat as color\n     *    - keep: the number of pixels of border to keep\n     */\n    trim?: \"border\" | {\n        top?: number;\n        bottom?: number;\n        left?: number;\n        right?: number;\n        width?: number;\n        height?: number;\n        border?: boolean | {\n            color?: string;\n            tolerance?: number;\n            keep?: number;\n        };\n    };\n    /**\n     * Quality setting from 1-100 (useful values are in 60-90 range). Lower values\n     * make images look worse, but load faster. The default is 85. It applies only\n     * to JPEG and WebP images. It doesn’t have any effect on PNG.\n     */\n    quality?: number | \"low\" | \"medium-low\" | \"medium-high\" | \"high\";\n    /**\n     * Output format to generate. It can be:\n     *  - avif: generate images in AVIF format.\n     *  - webp: generate images in Google WebP format. Set quality to 100 to get\n     *    the WebP-lossless format.\n     *  - json: instead of generating an image, outputs information about the\n     *    image, in JSON format. The JSON object will contain image size\n     *    (before and after resizing), source image’s MIME type, file size, etc.\n     * - jpeg: generate images in JPEG format.\n     * - png: generate images in PNG format.\n     */\n    format?: \"avif\" | \"webp\" | \"json\" | \"jpeg\" | \"png\" | \"baseline-jpeg\" | \"png-force\" | \"svg\";\n    /**\n     * Whether to preserve animation frames from input files. Default is true.\n     * Setting it to false reduces animations to still images. This setting is\n     * recommended when enlarging images or processing arbitrary user content,\n     * because large GIF animations can weigh tens or even hundreds of megabytes.\n     * It is also useful to set anim:false when using format:\"json\" to get the\n     * response quicker without the number of frames.\n     */\n    anim?: boolean;\n    /**\n     * What EXIF data should be preserved in the output image. Note that EXIF\n     * rotation and embedded color profiles are always applied (\"baked in\" into\n     * the image), and aren't affected by this option. Note that if the Polish\n     * feature is enabled, all metadata may have been removed already and this\n     * option may have no effect.\n     *  - keep: Preserve most of EXIF metadata, including GPS location if there's\n     *    any.\n     *  - copyright: Only keep the copyright tag, and discard everything else.\n     *    This is the default behavior for JPEG files.\n     *  - none: Discard all invisible EXIF metadata. Currently WebP and PNG\n     *    output formats always discard metadata.\n     */\n    metadata?: \"keep\" | \"copyright\" | \"none\";\n    /**\n     * Strength of sharpening filter to apply to the image. Floating-point\n     * number between 0 (no sharpening, default) and 10 (maximum). 1.0 is a\n     * recommended value for downscaled images.\n     */\n    sharpen?: number;\n    /**\n     * Radius of a blur filter (approximate gaussian). Maximum supported radius\n     * is 250.\n     */\n    blur?: number;\n    /**\n     * Overlays are drawn in the order they appear in the array (last array\n     * entry is the topmost layer).\n     */\n    draw?: RequestInitCfPropertiesImageDraw[];\n    /**\n     * Fetching image from authenticated origin. Setting this property will\n     * pass authentication headers (Authorization, Cookie, etc.) through to\n     * the origin.\n     */\n    \"origin-auth\"?: \"share-publicly\";\n    /**\n     * Adds a border around the image. The border is added after resizing. Border\n     * width takes dpr into account, and can be specified either using a single\n     * width property, or individually for each side.\n     */\n    border?: {\n        color: string;\n        width: number;\n    } | {\n        color: string;\n        top: number;\n        right: number;\n        bottom: number;\n        left: number;\n    };\n    /**\n     * Increase brightness by a factor. A value of 1.0 equals no change, a value\n     * of 0.5 equals half brightness, and a value of 2.0 equals twice as bright.\n     * 0 is ignored.\n     */\n    brightness?: number;\n    /**\n     * Increase contrast by a factor. A value of 1.0 equals no change, a value of\n     * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is\n     * ignored.\n     */\n    contrast?: number;\n    /**\n     * Increase exposure by a factor. A value of 1.0 equals no change, a value of\n     * 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored.\n     */\n    gamma?: number;\n    /**\n     * Increase contrast by a factor. A value of 1.0 equals no change, a value of\n     * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is\n     * ignored.\n     */\n    saturation?: number;\n    /**\n     * Flips the images horizontally, vertically, or both. Flipping is applied before\n     * rotation, so if you apply flip=h,rotate=90 then the image will be flipped\n     * horizontally, then rotated by 90 degrees.\n     */\n    flip?: 'h' | 'v' | 'hv';\n    /**\n     * Slightly reduces latency on a cache miss by selecting a\n     * quickest-to-compress file format, at a cost of increased file size and\n     * lower image quality. It will usually override the format option and choose\n     * JPEG over WebP or AVIF. We do not recommend using this option, except in\n     * unusual circumstances like resizing uncacheable dynamically-generated\n     * images.\n     */\n    compression?: \"fast\";\n}\ninterface RequestInitCfPropertiesImageMinify {\n    javascript?: boolean;\n    css?: boolean;\n    html?: boolean;\n}\ninterface RequestInitCfPropertiesR2 {\n    /**\n     * Colo id of bucket that an object is stored in\n     */\n    bucketColoId?: number;\n}\n/**\n * Request metadata provided by Cloudflare's edge.\n */\ntype IncomingRequestCfProperties<HostMetadata = unknown> = IncomingRequestCfPropertiesBase & IncomingRequestCfPropertiesBotManagementEnterprise & IncomingRequestCfPropertiesCloudflareForSaaSEnterprise<HostMetadata> & IncomingRequestCfPropertiesGeographicInformation & IncomingRequestCfPropertiesCloudflareAccessOrApiShield;\ninterface IncomingRequestCfPropertiesBase extends Record<string, unknown> {\n    /**\n     * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request.\n     *\n     * @example 395747\n     */\n    asn?: number;\n    /**\n     * The organization which owns the ASN of the incoming request.\n     *\n     * @example \"Google Cloud\"\n     */\n    asOrganization?: string;\n    /**\n     * The original value of the `Accept-Encoding` header if Cloudflare modified it.\n     *\n     * @example \"gzip, deflate, br\"\n     */\n    clientAcceptEncoding?: string;\n    /**\n     * The number of milliseconds it took for the request to reach your worker.\n     *\n     * @example 22\n     */\n    clientTcpRtt?: number;\n    /**\n     * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code)\n     * airport code of the data center that the request hit.\n     *\n     * @example \"DFW\"\n     */\n    colo: string;\n    /**\n     * Represents the upstream's response to a\n     * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html)\n     * from cloudflare.\n     *\n     * For workers with no upstream, this will always be `1`.\n     *\n     * @example 3\n     */\n    edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus;\n    /**\n     * The HTTP Protocol the request used.\n     *\n     * @example \"HTTP/2\"\n     */\n    httpProtocol: string;\n    /**\n     * The browser-requested prioritization information in the request object.\n     *\n     * If no information was set, defaults to the empty string `\"\"`\n     *\n     * @example \"weight=192;exclusive=0;group=3;group-weight=127\"\n     * @default \"\"\n     */\n    requestPriority: string;\n    /**\n     * The TLS version of the connection to Cloudflare.\n     * In requests served over plaintext (without TLS), this property is the empty string `\"\"`.\n     *\n     * @example \"TLSv1.3\"\n     */\n    tlsVersion: string;\n    /**\n     * The cipher for the connection to Cloudflare.\n     * In requests served over plaintext (without TLS), this property is the empty string `\"\"`.\n     *\n     * @example \"AEAD-AES128-GCM-SHA256\"\n     */\n    tlsCipher: string;\n    /**\n     * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake.\n     *\n     * If the incoming request was served over plaintext (without TLS) this field is undefined.\n     */\n    tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata;\n}\ninterface IncomingRequestCfPropertiesBotManagementBase {\n    /**\n     * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot,\n     * represented as an integer percentage between `1` (almost certainly a bot) and `99` (almost certainly human).\n     *\n     * @example 54\n     */\n    score: number;\n    /**\n     * A boolean value that is true if the request comes from a good bot, like Google or Bing.\n     * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots).\n     */\n    verifiedBot: boolean;\n    /**\n     * A boolean value that is true if the request originates from a\n     * Cloudflare-verified proxy service.\n     */\n    corporateProxy: boolean;\n    /**\n     * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources.\n     */\n    staticResource: boolean;\n    /**\n     * List of IDs that correlate to the Bot Management heuristic detections made on a request (you can have multiple heuristic detections on the same request).\n     */\n    detectionIds: number[];\n}\ninterface IncomingRequestCfPropertiesBotManagement {\n    /**\n     * Results of Cloudflare's Bot Management analysis\n     */\n    botManagement: IncomingRequestCfPropertiesBotManagementBase;\n    /**\n     * Duplicate of `botManagement.score`.\n     *\n     * @deprecated\n     */\n    clientTrustScore: number;\n}\ninterface IncomingRequestCfPropertiesBotManagementEnterprise extends IncomingRequestCfPropertiesBotManagement {\n    /**\n     * Results of Cloudflare's Bot Management analysis\n     */\n    botManagement: IncomingRequestCfPropertiesBotManagementBase & {\n        /**\n         * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients\n         * across different destination IPs, Ports, and X509 certificates.\n         */\n        ja3Hash: string;\n    };\n}\ninterface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise<HostMetadata> {\n    /**\n     * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/).\n     *\n     * This field is only present if you have Cloudflare for SaaS enabled on your account\n     * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)).\n     */\n    hostMetadata?: HostMetadata;\n}\ninterface IncomingRequestCfPropertiesCloudflareAccessOrApiShield {\n    /**\n     * Information about the client certificate presented to Cloudflare.\n     *\n     * This is populated when the incoming request is served over TLS using\n     * either Cloudflare Access or API Shield (mTLS)\n     * and the presented SSL certificate has a valid\n     * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number)\n     * (i.e., not `null` or `\"\"`).\n     *\n     * Otherwise, a set of placeholder values are used.\n     *\n     * The property `certPresented` will be set to `\"1\"` when\n     * the object is populated (i.e. the above conditions were met).\n     */\n    tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth | IncomingRequestCfPropertiesTLSClientAuthPlaceholder;\n}\n/**\n * Metadata about the request's TLS handshake\n */\ninterface IncomingRequestCfPropertiesExportedAuthenticatorMetadata {\n    /**\n     * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal\n     *\n     * @example \"44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d\"\n     */\n    clientHandshake: string;\n    /**\n     * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal\n     *\n     * @example \"44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d\"\n     */\n    serverHandshake: string;\n    /**\n     * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal\n     *\n     * @example \"084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b\"\n     */\n    clientFinished: string;\n    /**\n     * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal\n     *\n     * @example \"084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b\"\n     */\n    serverFinished: string;\n}\n/**\n * Geographic data about the request's origin.\n */\ninterface IncomingRequestCfPropertiesGeographicInformation {\n    /**\n     * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from.\n     *\n     * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `\"T1\"`, indicating a request that originated over TOR.\n     *\n     * If Cloudflare is unable to determine where the request originated this property is omitted.\n     *\n     * The country code `\"T1\"` is used for requests originating on TOR.\n     *\n     * @example \"GB\"\n     */\n    country?: Iso3166Alpha2Code | \"T1\";\n    /**\n     * If present, this property indicates that the request originated in the EU\n     *\n     * @example \"1\"\n     */\n    isEUCountry?: \"1\";\n    /**\n     * A two-letter code indicating the continent the request originated from.\n     *\n     * @example \"AN\"\n     */\n    continent?: ContinentCode;\n    /**\n     * The city the request originated from\n     *\n     * @example \"Austin\"\n     */\n    city?: string;\n    /**\n     * Postal code of the incoming request\n     *\n     * @example \"78701\"\n     */\n    postalCode?: string;\n    /**\n     * Latitude of the incoming request\n     *\n     * @example \"30.27130\"\n     */\n    latitude?: string;\n    /**\n     * Longitude of the incoming request\n     *\n     * @example \"-97.74260\"\n     */\n    longitude?: string;\n    /**\n     * Timezone of the incoming request\n     *\n     * @example \"America/Chicago\"\n     */\n    timezone?: string;\n    /**\n     * If known, the ISO 3166-2 name for the first level region associated with\n     * the IP address of the incoming request\n     *\n     * @example \"Texas\"\n     */\n    region?: string;\n    /**\n     * If known, the ISO 3166-2 code for the first-level region associated with\n     * the IP address of the incoming request\n     *\n     * @example \"TX\"\n     */\n    regionCode?: string;\n    /**\n     * Metro code (DMA) of the incoming request\n     *\n     * @example \"635\"\n     */\n    metroCode?: string;\n}\n/** Data about the incoming request's TLS certificate */\ninterface IncomingRequestCfPropertiesTLSClientAuth {\n    /** Always `\"1\"`, indicating that the certificate was presented */\n    certPresented: \"1\";\n    /**\n     * Result of certificate verification.\n     *\n     * @example \"FAILED:self signed certificate\"\n     */\n    certVerified: Exclude<CertVerificationStatus, \"NONE\">;\n    /** The presented certificate's revokation status.\n     *\n     * - A value of `\"1\"` indicates the certificate has been revoked\n     * - A value of `\"0\"` indicates the certificate has not been revoked\n     */\n    certRevoked: \"1\" | \"0\";\n    /**\n     * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html)\n     *\n     * @example \"CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare\"\n     */\n    certIssuerDN: string;\n    /**\n     * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html)\n     *\n     * @example \"CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare\"\n     */\n    certSubjectDN: string;\n    /**\n     * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted)\n     *\n     * @example \"CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare\"\n     */\n    certIssuerDNRFC2253: string;\n    /**\n     * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted)\n     *\n     * @example \"CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare\"\n     */\n    certSubjectDNRFC2253: string;\n    /** The certificate issuer's distinguished name (legacy policies) */\n    certIssuerDNLegacy: string;\n    /** The certificate subject's distinguished name (legacy policies) */\n    certSubjectDNLegacy: string;\n    /**\n     * The certificate's serial number\n     *\n     * @example \"00936EACBE07F201DF\"\n     */\n    certSerial: string;\n    /**\n     * The certificate issuer's serial number\n     *\n     * @example \"2489002934BDFEA34\"\n     */\n    certIssuerSerial: string;\n    /**\n     * The certificate's Subject Key Identifier\n     *\n     * @example \"BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4\"\n     */\n    certSKI: string;\n    /**\n     * The certificate issuer's Subject Key Identifier\n     *\n     * @example \"BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4\"\n     */\n    certIssuerSKI: string;\n    /**\n     * The certificate's SHA-1 fingerprint\n     *\n     * @example \"6b9109f323999e52259cda7373ff0b4d26bd232e\"\n     */\n    certFingerprintSHA1: string;\n    /**\n     * The certificate's SHA-256 fingerprint\n     *\n     * @example \"acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea\"\n     */\n    certFingerprintSHA256: string;\n    /**\n     * The effective starting date of the certificate\n     *\n     * @example \"Dec 22 19:39:00 2018 GMT\"\n     */\n    certNotBefore: string;\n    /**\n     * The effective expiration date of the certificate\n     *\n     * @example \"Dec 22 19:39:00 2018 GMT\"\n     */\n    certNotAfter: string;\n}\n/** Placeholder values for TLS Client Authorization */\ninterface IncomingRequestCfPropertiesTLSClientAuthPlaceholder {\n    certPresented: \"0\";\n    certVerified: \"NONE\";\n    certRevoked: \"0\";\n    certIssuerDN: \"\";\n    certSubjectDN: \"\";\n    certIssuerDNRFC2253: \"\";\n    certSubjectDNRFC2253: \"\";\n    certIssuerDNLegacy: \"\";\n    certSubjectDNLegacy: \"\";\n    certSerial: \"\";\n    certIssuerSerial: \"\";\n    certSKI: \"\";\n    certIssuerSKI: \"\";\n    certFingerprintSHA1: \"\";\n    certFingerprintSHA256: \"\";\n    certNotBefore: \"\";\n    certNotAfter: \"\";\n}\n/** Possible outcomes of TLS verification */\ndeclare type CertVerificationStatus = \n/** Authentication succeeded */\n\"SUCCESS\"\n/** No certificate was presented */\n | \"NONE\"\n/** Failed because the certificate was self-signed */\n | \"FAILED:self signed certificate\"\n/** Failed because the certificate failed a trust chain check */\n | \"FAILED:unable to verify the first certificate\"\n/** Failed because the certificate not yet valid */\n | \"FAILED:certificate is not yet valid\"\n/** Failed because the certificate is expired */\n | \"FAILED:certificate has expired\"\n/** Failed for another unspecified reason */\n | \"FAILED\";\n/**\n * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare.\n */\ndeclare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = 0 /** Unknown */ | 1 /** no keepalives (not found) */ | 2 /** no connection re-use, opening keepalive connection failed */ | 3 /** no connection re-use, keepalive accepted and saved */ | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ | 5; /** connection re-use, accepted by the origin server */\n/** ISO 3166-1 Alpha-2 codes */\ndeclare type Iso3166Alpha2Code = \"AD\" | \"AE\" | \"AF\" | \"AG\" | \"AI\" | \"AL\" | \"AM\" | \"AO\" | \"AQ\" | \"AR\" | \"AS\" | \"AT\" | \"AU\" | \"AW\" | \"AX\" | \"AZ\" | \"BA\" | \"BB\" | \"BD\" | \"BE\" | \"BF\" | \"BG\" | \"BH\" | \"BI\" | \"BJ\" | \"BL\" | \"BM\" | \"BN\" | \"BO\" | \"BQ\" | \"BR\" | \"BS\" | \"BT\" | \"BV\" | \"BW\" | \"BY\" | \"BZ\" | \"CA\" | \"CC\" | \"CD\" | \"CF\" | \"CG\" | \"CH\" | \"CI\" | \"CK\" | \"CL\" | \"CM\" | \"CN\" | \"CO\" | \"CR\" | \"CU\" | \"CV\" | \"CW\" | \"CX\" | \"CY\" | \"CZ\" | \"DE\" | \"DJ\" | \"DK\" | \"DM\" | \"DO\" | \"DZ\" | \"EC\" | \"EE\" | \"EG\" | \"EH\" | \"ER\" | \"ES\" | \"ET\" | \"FI\" | \"FJ\" | \"FK\" | \"FM\" | \"FO\" | \"FR\" | \"GA\" | \"GB\" | \"GD\" | \"GE\" | \"GF\" | \"GG\" | \"GH\" | \"GI\" | \"GL\" | \"GM\" | \"GN\" | \"GP\" | \"GQ\" | \"GR\" | \"GS\" | \"GT\" | \"GU\" | \"GW\" | \"GY\" | \"HK\" | \"HM\" | \"HN\" | \"HR\" | \"HT\" | \"HU\" | \"ID\" | \"IE\" | \"IL\" | \"IM\" | \"IN\" | \"IO\" | \"IQ\" | \"IR\" | \"IS\" | \"IT\" | \"JE\" | \"JM\" | \"JO\" | \"JP\" | \"KE\" | \"KG\" | \"KH\" | \"KI\" | \"KM\" | \"KN\" | \"KP\" | \"KR\" | \"KW\" | \"KY\" | \"KZ\" | \"LA\" | \"LB\" | \"LC\" | \"LI\" | \"LK\" | \"LR\" | \"LS\" | \"LT\" | \"LU\" | \"LV\" | \"LY\" | \"MA\" | \"MC\" | \"MD\" | \"ME\" | \"MF\" | \"MG\" | \"MH\" | \"MK\" | \"ML\" | \"MM\" | \"MN\" | \"MO\" | \"MP\" | \"MQ\" | \"MR\" | \"MS\" | \"MT\" | \"MU\" | \"MV\" | \"MW\" | \"MX\" | \"MY\" | \"MZ\" | \"NA\" | \"NC\" | \"NE\" | \"NF\" | \"NG\" | \"NI\" | \"NL\" | \"NO\" | \"NP\" | \"NR\" | \"NU\" | \"NZ\" | \"OM\" | \"PA\" | \"PE\" | \"PF\" | \"PG\" | \"PH\" | \"PK\" | \"PL\" | \"PM\" | \"PN\" | \"PR\" | \"PS\" | \"PT\" | \"PW\" | \"PY\" | \"QA\" | \"RE\" | \"RO\" | \"RS\" | \"RU\" | \"RW\" | \"SA\" | \"SB\" | \"SC\" | \"SD\" | \"SE\" | \"SG\" | \"SH\" | \"SI\" | \"SJ\" | \"SK\" | \"SL\" | \"SM\" | \"SN\" | \"SO\" | \"SR\" | \"SS\" | \"ST\" | \"SV\" | \"SX\" | \"SY\" | \"SZ\" | \"TC\" | \"TD\" | \"TF\" | \"TG\" | \"TH\" | \"TJ\" | \"TK\" | \"TL\" | \"TM\" | \"TN\" | \"TO\" | \"TR\" | \"TT\" | \"TV\" | \"TW\" | \"TZ\" | \"UA\" | \"UG\" | \"UM\" | \"US\" | \"UY\" | \"UZ\" | \"VA\" | \"VC\" | \"VE\" | \"VG\" | \"VI\" | \"VN\" | \"VU\" | \"WF\" | \"WS\" | \"YE\" | \"YT\" | \"ZA\" | \"ZM\" | \"ZW\";\n/** The 2-letter continent codes Cloudflare uses */\ndeclare type ContinentCode = \"AF\" | \"AN\" | \"AS\" | \"EU\" | \"NA\" | \"OC\" | \"SA\";\ntype CfProperties<HostMetadata = unknown> = IncomingRequestCfProperties<HostMetadata> | RequestInitCfProperties;\ninterface D1Meta {\n    duration: number;\n    size_after: number;\n    rows_read: number;\n    rows_written: number;\n    last_row_id: number;\n    changed_db: boolean;\n    changes: number;\n    /**\n     * The region of the database instance that executed the query.\n     */\n    served_by_region?: string;\n    /**\n     * True if-and-only-if the database instance that executed the query was the primary.\n     */\n    served_by_primary?: boolean;\n    timings?: {\n        /**\n         * The duration of the SQL query execution by the database instance. It doesn't include any network time.\n         */\n        sql_duration_ms: number;\n    };\n    /**\n     * Number of total attempts to execute the query, due to automatic retries.\n     * Note: All other fields in the response like `timings` only apply to the last attempt.\n     */\n    total_attempts?: number;\n}\ninterface D1Response {\n    success: true;\n    meta: D1Meta & Record<string, unknown>;\n    error?: never;\n}\ntype D1Result<T = unknown> = D1Response & {\n    results: T[];\n};\ninterface D1ExecResult {\n    count: number;\n    duration: number;\n}\ntype D1SessionConstraint = \n// Indicates that the first query should go to the primary, and the rest queries\n// using the same D1DatabaseSession will go to any replica that is consistent with\n// the bookmark maintained by the session (returned by the first query).\n'first-primary'\n// Indicates that the first query can go anywhere (primary or replica), and the rest queries\n// using the same D1DatabaseSession will go to any replica that is consistent with\n// the bookmark maintained by the session (returned by the first query).\n | 'first-unconstrained';\ntype D1SessionBookmark = string;\ndeclare abstract class D1Database {\n    prepare(query: string): D1PreparedStatement;\n    batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;\n    exec(query: string): Promise<D1ExecResult>;\n    /**\n     * Creates a new D1 Session anchored at the given constraint or the bookmark.\n     * All queries executed using the created session will have sequential consistency,\n     * meaning that all writes done through the session will be visible in subsequent reads.\n     *\n     * @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session.\n     */\n    withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession;\n    /**\n     * @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases.\n     */\n    dump(): Promise<ArrayBuffer>;\n}\ndeclare abstract class D1DatabaseSession {\n    prepare(query: string): D1PreparedStatement;\n    batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;\n    /**\n     * @returns The latest session bookmark across all executed queries on the session.\n     *          If no query has been executed yet, `null` is returned.\n     */\n    getBookmark(): D1SessionBookmark | null;\n}\ndeclare abstract class D1PreparedStatement {\n    bind(...values: unknown[]): D1PreparedStatement;\n    first<T = unknown>(colName: string): Promise<T | null>;\n    first<T = Record<string, unknown>>(): Promise<T | null>;\n    run<T = Record<string, unknown>>(): Promise<D1Result<T>>;\n    all<T = Record<string, unknown>>(): Promise<D1Result<T>>;\n    raw<T = unknown[]>(options: {\n        columnNames: true;\n    }): Promise<[\n        string[],\n        ...T[]\n    ]>;\n    raw<T = unknown[]>(options?: {\n        columnNames?: false;\n    }): Promise<T[]>;\n}\n// `Disposable` was added to TypeScript's standard lib types in version 5.2.\n// To support older TypeScript versions, define an empty `Disposable` interface.\n// Users won't be able to use `using`/`Symbol.dispose` without upgrading to 5.2,\n// but this will ensure type checking on older versions still passes.\n// TypeScript's interface merging will ensure our empty interface is effectively\n// ignored when `Disposable` is included in the standard lib.\ninterface Disposable {\n}\n/**\n * An email message that can be sent from a Worker.\n */\ninterface EmailMessage {\n    /**\n     * Envelope From attribute of the email message.\n     */\n    readonly from: string;\n    /**\n     * Envelope To attribute of the email message.\n     */\n    readonly to: string;\n}\n/**\n * An email message that is sent to a consumer Worker and can be rejected/forwarded.\n */\ninterface ForwardableEmailMessage extends EmailMessage {\n    /**\n     * Stream of the email message content.\n     */\n    readonly raw: ReadableStream<Uint8Array>;\n    /**\n     * An [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers).\n     */\n    readonly headers: Headers;\n    /**\n     * Size of the email message content.\n     */\n    readonly rawSize: number;\n    /**\n     * Reject this email message by returning a permanent SMTP error back to the connecting client including the given reason.\n     * @param reason The reject reason.\n     * @returns void\n     */\n    setReject(reason: string): void;\n    /**\n     * Forward this email message to a verified destination address of the account.\n     * @param rcptTo Verified destination address.\n     * @param headers A [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers).\n     * @returns A promise that resolves when the email message is forwarded.\n     */\n    forward(rcptTo: string, headers?: Headers): Promise<void>;\n    /**\n     * Reply to the sender of this email message with a new EmailMessage object.\n     * @param message The reply message.\n     * @returns A promise that resolves when the email message is replied.\n     */\n    reply(message: EmailMessage): Promise<void>;\n}\n/**\n * A binding that allows a Worker to send email messages.\n */\ninterface SendEmail {\n    send(message: EmailMessage): Promise<void>;\n}\ndeclare abstract class EmailEvent extends ExtendableEvent {\n    readonly message: ForwardableEmailMessage;\n}\ndeclare type EmailExportedHandler<Env = unknown> = (message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) => void | Promise<void>;\ndeclare module \"cloudflare:email\" {\n    let _EmailMessage: {\n        prototype: EmailMessage;\n        new (from: string, to: string, raw: ReadableStream | string): EmailMessage;\n    };\n    export { _EmailMessage as EmailMessage };\n}\n/**\n * Hello World binding to serve as an explanatory example. DO NOT USE\n */\ninterface HelloWorldBinding {\n    /**\n     * Retrieve the current stored value\n     */\n    get(): Promise<{\n        value: string;\n        ms?: number;\n    }>;\n    /**\n     * Set a new stored value\n     */\n    set(value: string): Promise<void>;\n}\ninterface Hyperdrive {\n    /**\n     * Connect directly to Hyperdrive as if it's your database, returning a TCP socket.\n     *\n     * Calling this method returns an idential socket to if you call\n     * `connect(\"host:port\")` using the `host` and `port` fields from this object.\n     * Pick whichever approach works better with your preferred DB client library.\n     *\n     * Note that this socket is not yet authenticated -- it's expected that your\n     * code (or preferably, the client library of your choice) will authenticate\n     * using the information in this class's readonly fields.\n     */\n    connect(): Socket;\n    /**\n     * A valid DB connection string that can be passed straight into the typical\n     * client library/driver/ORM. This will typically be the easiest way to use\n     * Hyperdrive.\n     */\n    readonly connectionString: string;\n    /*\n     * A randomly generated hostname that is only valid within the context of the\n     * currently running Worker which, when passed into `connect()` function from\n     * the \"cloudflare:sockets\" module, will connect to the Hyperdrive instance\n     * for your database.\n     */\n    readonly host: string;\n    /*\n     * The port that must be paired the the host field when connecting.\n     */\n    readonly port: number;\n    /*\n     * The username to use when authenticating to your database via Hyperdrive.\n     * Unlike the host and password, this will be the same every time\n     */\n    readonly user: string;\n    /*\n     * The randomly generated password to use when authenticating to your\n     * database via Hyperdrive. Like the host field, this password is only valid\n     * within the context of the currently running Worker instance from which\n     * it's read.\n     */\n    readonly password: string;\n    /*\n     * The name of the database to connect to.\n     */\n    readonly database: string;\n}\n// Copyright (c) 2024 Cloudflare, Inc.\n// Licensed under the Apache 2.0 license found in the LICENSE file or at:\n//     https://opensource.org/licenses/Apache-2.0\ntype ImageInfoResponse = {\n    format: 'image/svg+xml';\n} | {\n    format: string;\n    fileSize: number;\n    width: number;\n    height: number;\n};\ntype ImageTransform = {\n    width?: number;\n    height?: number;\n    background?: string;\n    blur?: number;\n    border?: {\n        color?: string;\n        width?: number;\n    } | {\n        top?: number;\n        bottom?: number;\n        left?: number;\n        right?: number;\n    };\n    brightness?: number;\n    contrast?: number;\n    fit?: 'scale-down' | 'contain' | 'pad' | 'squeeze' | 'cover' | 'crop';\n    flip?: 'h' | 'v' | 'hv';\n    gamma?: number;\n    segment?: 'foreground';\n    gravity?: 'face' | 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | {\n        x?: number;\n        y?: number;\n        mode: 'remainder' | 'box-center';\n    };\n    rotate?: 0 | 90 | 180 | 270;\n    saturation?: number;\n    sharpen?: number;\n    trim?: 'border' | {\n        top?: number;\n        bottom?: number;\n        left?: number;\n        right?: number;\n        width?: number;\n        height?: number;\n        border?: boolean | {\n            color?: string;\n            tolerance?: number;\n            keep?: number;\n        };\n    };\n};\ntype ImageDrawOptions = {\n    opacity?: number;\n    repeat?: boolean | string;\n    top?: number;\n    left?: number;\n    bottom?: number;\n    right?: number;\n};\ntype ImageInputOptions = {\n    encoding?: 'base64';\n};\ntype ImageOutputOptions = {\n    format: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/avif' | 'rgb' | 'rgba';\n    quality?: number;\n    background?: string;\n    anim?: boolean;\n};\ninterface ImagesBinding {\n    /**\n     * Get image metadata (type, width and height)\n     * @throws {@link ImagesError} with code 9412 if input is not an image\n     * @param stream The image bytes\n     */\n    info(stream: ReadableStream<Uint8Array>, options?: ImageInputOptions): Promise<ImageInfoResponse>;\n    /**\n     * Begin applying a series of transformations to an image\n     * @param stream The image bytes\n     * @returns A transform handle\n     */\n    input(stream: ReadableStream<Uint8Array>, options?: ImageInputOptions): ImageTransformer;\n}\ninterface ImageTransformer {\n    /**\n     * Apply transform next, returning a transform handle.\n     * You can then apply more transformations, draw, or retrieve the output.\n     * @param transform\n     */\n    transform(transform: ImageTransform): ImageTransformer;\n    /**\n     * Draw an image on this transformer, returning a transform handle.\n     * You can then apply more transformations, draw, or retrieve the output.\n     * @param image The image (or transformer that will give the image) to draw\n     * @param options The options configuring how to draw the image\n     */\n    draw(image: ReadableStream<Uint8Array> | ImageTransformer, options?: ImageDrawOptions): ImageTransformer;\n    /**\n     * Retrieve the image that results from applying the transforms to the\n     * provided input\n     * @param options Options that apply to the output e.g. output format\n     */\n    output(options: ImageOutputOptions): Promise<ImageTransformationResult>;\n}\ntype ImageTransformationOutputOptions = {\n    encoding?: 'base64';\n};\ninterface ImageTransformationResult {\n    /**\n     * The image as a response, ready to store in cache or return to users\n     */\n    response(): Response;\n    /**\n     * The content type of the returned image\n     */\n    contentType(): string;\n    /**\n     * The bytes of the response\n     */\n    image(options?: ImageTransformationOutputOptions): ReadableStream<Uint8Array>;\n}\ninterface ImagesError extends Error {\n    readonly code: number;\n    readonly message: string;\n    readonly stack?: string;\n}\n/**\n * Media binding for transforming media streams.\n * Provides the entry point for media transformation operations.\n */\ninterface MediaBinding {\n    /**\n     * Creates a media transformer from an input stream.\n     * @param media - The input media bytes\n     * @returns A MediaTransformer instance for applying transformations\n     */\n    input(media: ReadableStream<Uint8Array>): MediaTransformer;\n}\n/**\n * Media transformer for applying transformation operations to media content.\n * Handles sizing, fitting, and other input transformation parameters.\n */\ninterface MediaTransformer {\n    /**\n     * Applies transformation options to the media content.\n     * @param transform - Configuration for how the media should be transformed\n     * @returns A generator for producing the transformed media output\n     */\n    transform(transform: MediaTransformationInputOptions): MediaTransformationGenerator;\n}\n/**\n * Generator for producing media transformation results.\n * Configures the output format and parameters for the transformed media.\n */\ninterface MediaTransformationGenerator {\n    /**\n     * Generates the final media output with specified options.\n     * @param output - Configuration for the output format and parameters\n     * @returns The final transformation result containing the transformed media\n     */\n    output(output: MediaTransformationOutputOptions): MediaTransformationResult;\n}\n/**\n * Result of a media transformation operation.\n * Provides multiple ways to access the transformed media content.\n */\ninterface MediaTransformationResult {\n    /**\n     * Returns the transformed media as a readable stream of bytes.\n     * @returns A stream containing the transformed media data\n     */\n    media(): ReadableStream<Uint8Array>;\n    /**\n     * Returns the transformed media as an HTTP response object.\n     * @returns The transformed media as a Response, ready to store in cache or return to users\n     */\n    response(): Response;\n    /**\n     * Returns the MIME type of the transformed media.\n     * @returns The content type string (e.g., 'image/jpeg', 'video/mp4')\n     */\n    contentType(): string;\n}\n/**\n * Configuration options for transforming media input.\n * Controls how the media should be resized and fitted.\n */\ntype MediaTransformationInputOptions = {\n    /** How the media should be resized to fit the specified dimensions */\n    fit?: 'contain' | 'cover' | 'scale-down';\n    /** Target width in pixels */\n    width?: number;\n    /** Target height in pixels */\n    height?: number;\n};\n/**\n * Configuration options for Media Transformations output.\n * Controls the format, timing, and type of the generated output.\n */\ntype MediaTransformationOutputOptions = {\n    /**\n     * Output mode determining the type of media to generate\n     */\n    mode?: 'video' | 'spritesheet' | 'frame' | 'audio';\n    /** Whether to include audio in the output */\n    audio?: boolean;\n    /**\n     * Starting timestamp for frame extraction or start time for clips. (e.g. '2s').\n     */\n    time?: string;\n    /**\n     * Duration for video clips, audio extraction, and spritesheet generation (e.g. '5s').\n     */\n    duration?: string;\n    /**\n     * Number of frames in the spritesheet.\n     */\n    imageCount?: number;\n    /**\n     * Output format for the generated media.\n     */\n    format?: 'jpg' | 'png' | 'm4a';\n};\n/**\n * Error object for media transformation operations.\n * Extends the standard Error interface with additional media-specific information.\n */\ninterface MediaError extends Error {\n    readonly code: number;\n    readonly message: string;\n    readonly stack?: string;\n}\ndeclare module 'cloudflare:node' {\n    interface NodeStyleServer {\n        listen(...args: unknown[]): this;\n        address(): {\n            port?: number | null | undefined;\n        };\n    }\n    export function httpServerHandler(port: number): ExportedHandler;\n    export function httpServerHandler(options: {\n        port: number;\n    }): ExportedHandler;\n    export function httpServerHandler(server: NodeStyleServer): ExportedHandler;\n}\ntype Params<P extends string = any> = Record<P, string | string[]>;\ntype EventContext<Env, P extends string, Data> = {\n    request: Request<unknown, IncomingRequestCfProperties<unknown>>;\n    functionPath: string;\n    waitUntil: (promise: Promise<any>) => void;\n    passThroughOnException: () => void;\n    next: (input?: Request | string, init?: RequestInit) => Promise<Response>;\n    env: Env & {\n        ASSETS: {\n            fetch: typeof fetch;\n        };\n    };\n    params: Params<P>;\n    data: Data;\n};\ntype PagesFunction<Env = unknown, Params extends string = any, Data extends Record<string, unknown> = Record<string, unknown>> = (context: EventContext<Env, Params, Data>) => Response | Promise<Response>;\ntype EventPluginContext<Env, P extends string, Data, PluginArgs> = {\n    request: Request<unknown, IncomingRequestCfProperties<unknown>>;\n    functionPath: string;\n    waitUntil: (promise: Promise<any>) => void;\n    passThroughOnException: () => void;\n    next: (input?: Request | string, init?: RequestInit) => Promise<Response>;\n    env: Env & {\n        ASSETS: {\n            fetch: typeof fetch;\n        };\n    };\n    params: Params<P>;\n    data: Data;\n    pluginArgs: PluginArgs;\n};\ntype PagesPluginFunction<Env = unknown, Params extends string = any, Data extends Record<string, unknown> = Record<string, unknown>, PluginArgs = unknown> = (context: EventPluginContext<Env, Params, Data, PluginArgs>) => Response | Promise<Response>;\ndeclare module \"assets:*\" {\n    export const onRequest: PagesFunction;\n}\n// Copyright (c) 2022-2023 Cloudflare, Inc.\n// Licensed under the Apache 2.0 license found in the LICENSE file or at:\n//     https://opensource.org/licenses/Apache-2.0\ndeclare module \"cloudflare:pipelines\" {\n    export abstract class PipelineTransformationEntrypoint<Env = unknown, I extends PipelineRecord = PipelineRecord, O extends PipelineRecord = PipelineRecord> {\n        protected env: Env;\n        protected ctx: ExecutionContext;\n        constructor(ctx: ExecutionContext, env: Env);\n        /**\n         * run recieves an array of PipelineRecord which can be\n         * transformed and returned to the pipeline\n         * @param records Incoming records from the pipeline to be transformed\n         * @param metadata Information about the specific pipeline calling the transformation entrypoint\n         * @returns A promise containing the transformed PipelineRecord array\n         */\n        public run(records: I[], metadata: PipelineBatchMetadata): Promise<O[]>;\n    }\n    export type PipelineRecord = Record<string, unknown>;\n    export type PipelineBatchMetadata = {\n        pipelineId: string;\n        pipelineName: string;\n    };\n    export interface Pipeline<T extends PipelineRecord = PipelineRecord> {\n        /**\n         * The Pipeline interface represents the type of a binding to a Pipeline\n         *\n         * @param records The records to send to the pipeline\n         */\n        send(records: T[]): Promise<void>;\n    }\n}\n// PubSubMessage represents an incoming PubSub message.\n// The message includes metadata about the broker, the client, and the payload\n// itself.\n// https://developers.cloudflare.com/pub-sub/\ninterface PubSubMessage {\n    // Message ID\n    readonly mid: number;\n    // MQTT broker FQDN in the form mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:PORT\n    readonly broker: string;\n    // The MQTT topic the message was sent on.\n    readonly topic: string;\n    // The client ID of the client that published this message.\n    readonly clientId: string;\n    // The unique identifier (JWT ID) used by the client to authenticate, if token\n    // auth was used.\n    readonly jti?: string;\n    // A Unix timestamp (seconds from Jan 1, 1970), set when the Pub/Sub Broker\n    // received the message from the client.\n    readonly receivedAt: number;\n    // An (optional) string with the MIME type of the payload, if set by the\n    // client.\n    readonly contentType: string;\n    // Set to 1 when the payload is a UTF-8 string\n    // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901063\n    readonly payloadFormatIndicator: number;\n    // Pub/Sub (MQTT) payloads can be UTF-8 strings, or byte arrays.\n    // You can use payloadFormatIndicator to inspect this before decoding.\n    payload: string | Uint8Array;\n}\n// JsonWebKey extended by kid parameter\ninterface JsonWebKeyWithKid extends JsonWebKey {\n    // Key Identifier of the JWK\n    readonly kid: string;\n}\ninterface RateLimitOptions {\n    key: string;\n}\ninterface RateLimitOutcome {\n    success: boolean;\n}\ninterface RateLimit {\n    /**\n     * Rate limit a request based on the provided options.\n     * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/\n     * @returns A promise that resolves with the outcome of the rate limit.\n     */\n    limit(options: RateLimitOptions): Promise<RateLimitOutcome>;\n}\n// Namespace for RPC utility types. Unfortunately, we can't use a `module` here as these types need\n// to referenced by `Fetcher`. This is included in the \"importable\" version of the types which\n// strips all `module` blocks.\ndeclare namespace Rpc {\n    // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s.\n    // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`.\n    // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to\n    // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape)\n    export const __RPC_STUB_BRAND: '__RPC_STUB_BRAND';\n    export const __RPC_TARGET_BRAND: '__RPC_TARGET_BRAND';\n    export const __WORKER_ENTRYPOINT_BRAND: '__WORKER_ENTRYPOINT_BRAND';\n    export const __DURABLE_OBJECT_BRAND: '__DURABLE_OBJECT_BRAND';\n    export const __WORKFLOW_ENTRYPOINT_BRAND: '__WORKFLOW_ENTRYPOINT_BRAND';\n    export interface RpcTargetBranded {\n        [__RPC_TARGET_BRAND]: never;\n    }\n    export interface WorkerEntrypointBranded {\n        [__WORKER_ENTRYPOINT_BRAND]: never;\n    }\n    export interface DurableObjectBranded {\n        [__DURABLE_OBJECT_BRAND]: never;\n    }\n    export interface WorkflowEntrypointBranded {\n        [__WORKFLOW_ENTRYPOINT_BRAND]: never;\n    }\n    export type EntrypointBranded = WorkerEntrypointBranded | DurableObjectBranded | WorkflowEntrypointBranded;\n    // Types that can be used through `Stub`s\n    export type Stubable = RpcTargetBranded | ((...args: any[]) => any);\n    // Types that can be passed over RPC\n    // The reason for using a generic type here is to build a serializable subset of structured\n    //   cloneable composite types. This allows types defined with the \"interface\" keyword to pass the\n    //   serializable check as well. Otherwise, only types defined with the \"type\" keyword would pass.\n    type Serializable<T> = \n    // Structured cloneables\n    BaseType\n    // Structured cloneable composites\n     | Map<T extends Map<infer U, unknown> ? Serializable<U> : never, T extends Map<unknown, infer U> ? Serializable<U> : never> | Set<T extends Set<infer U> ? Serializable<U> : never> | ReadonlyArray<T extends ReadonlyArray<infer U> ? Serializable<U> : never> | {\n        [K in keyof T]: K extends number | string ? Serializable<T[K]> : never;\n    }\n    // Special types\n     | Stub<Stubable>\n    // Serialized as stubs, see `Stubify`\n     | Stubable;\n    // Base type for all RPC stubs, including common memory management methods.\n    // `T` is used as a marker type for unwrapping `Stub`s later.\n    interface StubBase<T extends Stubable> extends Disposable {\n        [__RPC_STUB_BRAND]: T;\n        dup(): this;\n    }\n    export type Stub<T extends Stubable> = Provider<T> & StubBase<T>;\n    // This represents all the types that can be sent as-is over an RPC boundary\n    type BaseType = void | undefined | null | boolean | number | bigint | string | TypedArray | ArrayBuffer | DataView | Date | Error | RegExp | ReadableStream<Uint8Array> | WritableStream<Uint8Array> | Request | Response | Headers;\n    // Recursively rewrite all `Stubable` types with `Stub`s\n    // prettier-ignore\n    type Stubify<T> = T extends Stubable ? Stub<T> : T extends Map<infer K, infer V> ? Map<Stubify<K>, Stubify<V>> : T extends Set<infer V> ? Set<Stubify<V>> : T extends Array<infer V> ? Array<Stubify<V>> : T extends ReadonlyArray<infer V> ? ReadonlyArray<Stubify<V>> : T extends BaseType ? T : T extends {\n        [key: string | number]: any;\n    } ? {\n        [K in keyof T]: Stubify<T[K]>;\n    } : T;\n    // Recursively rewrite all `Stub<T>`s with the corresponding `T`s.\n    // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies:\n    // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`.\n    // prettier-ignore\n    type Unstubify<T> = T extends StubBase<infer V> ? V : T extends Map<infer K, infer V> ? Map<Unstubify<K>, Unstubify<V>> : T extends Set<infer V> ? Set<Unstubify<V>> : T extends Array<infer V> ? Array<Unstubify<V>> : T extends ReadonlyArray<infer V> ? ReadonlyArray<Unstubify<V>> : T extends BaseType ? T : T extends {\n        [key: string | number]: unknown;\n    } ? {\n        [K in keyof T]: Unstubify<T[K]>;\n    } : T;\n    type UnstubifyAll<A extends any[]> = {\n        [I in keyof A]: Unstubify<A[I]>;\n    };\n    // Utility type for adding `Provider`/`Disposable`s to `object` types only.\n    // Note `unknown & T` is equivalent to `T`.\n    type MaybeProvider<T> = T extends object ? Provider<T> : unknown;\n    type MaybeDisposable<T> = T extends object ? Disposable : unknown;\n    // Type for method return or property on an RPC interface.\n    // - Stubable types are replaced by stubs.\n    // - Serializable types are passed by value, with stubable types replaced by stubs\n    //   and a top-level `Disposer`.\n    // Everything else can't be passed over PRC.\n    // Technically, we use custom thenables here, but they quack like `Promise`s.\n    // Intersecting with `(Maybe)Provider` allows pipelining.\n    // prettier-ignore\n    type Result<R> = R extends Stubable ? Promise<Stub<R>> & Provider<R> : R extends Serializable<R> ? Promise<Stubify<R> & MaybeDisposable<R>> & MaybeProvider<R> : never;\n    // Type for method or property on an RPC interface.\n    // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s.\n    // Unwrapping `Stub`s allows calling with `Stubable` arguments.\n    // For properties, rewrite types to be `Result`s.\n    // In each case, unwrap `Promise`s.\n    type MethodOrProperty<V> = V extends (...args: infer P) => infer R ? (...args: UnstubifyAll<P>) => Result<Awaited<R>> : Result<Awaited<V>>;\n    // Type for the callable part of an `Provider` if `T` is callable.\n    // This is intersected with methods/properties.\n    type MaybeCallableProvider<T> = T extends (...args: any[]) => any ? MethodOrProperty<T> : unknown;\n    // Base type for all other types providing RPC-like interfaces.\n    // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types.\n    // `Reserved` names (e.g. stub method names like `dup()`) and symbols can't be accessed over RPC.\n    export type Provider<T extends object, Reserved extends string = never> = MaybeCallableProvider<T> & Pick<{\n        [K in keyof T]: MethodOrProperty<T[K]>;\n    }, Exclude<keyof T, Reserved | symbol | keyof StubBase<never>>>;\n}\ndeclare namespace Cloudflare {\n    // Type of `env`.\n    //\n    // The specific project can extend `Env` by redeclaring it in project-specific files. Typescript\n    // will merge all declarations.\n    //\n    // You can use `wrangler types` to generate the `Env` type automatically.\n    interface Env {\n    }\n    // Project-specific parameters used to inform types.\n    //\n    // This interface is, again, intended to be declared in project-specific files, and then that\n    // declaration will be merged with this one.\n    //\n    // A project should have a declaration like this:\n    //\n    //     interface GlobalProps {\n    //       // Declares the main module's exports. Used to populate Cloudflare.Exports aka the type\n    //       // of `ctx.exports`.\n    //       mainModule: typeof import(\"my-main-module\");\n    //\n    //       // Declares which of the main module's exports are configured with durable storage, and\n    //       // thus should behave as Durable Object namsepace bindings.\n    //       durableNamespaces: \"MyDurableObject\" | \"AnotherDurableObject\";\n    //     }\n    //\n    // You can use `wrangler types` to generate `GlobalProps` automatically.\n    interface GlobalProps {\n    }\n    // Evaluates to the type of a property in GlobalProps, defaulting to `Default` if it is not\n    // present.\n    type GlobalProp<K extends string, Default> = K extends keyof GlobalProps ? GlobalProps[K] : Default;\n    // The type of the program's main module exports, if known. Requires `GlobalProps` to declare the\n    // `mainModule` property.\n    type MainModule = GlobalProp<\"mainModule\", {}>;\n    // The type of ctx.exports, which contains loopback bindings for all top-level exports.\n    type Exports = {\n        [K in keyof MainModule]: LoopbackForExport<MainModule[K]>\n        // If the export is listed in `durableNamespaces`, then it is also a\n        // DurableObjectNamespace.\n         & (K extends GlobalProp<\"durableNamespaces\", never> ? MainModule[K] extends new (...args: any[]) => infer DoInstance ? DoInstance extends Rpc.DurableObjectBranded ? DurableObjectNamespace<DoInstance> : DurableObjectNamespace<undefined> : DurableObjectNamespace<undefined> : {});\n    };\n}\ndeclare namespace CloudflareWorkersModule {\n    export type RpcStub<T extends Rpc.Stubable> = Rpc.Stub<T>;\n    export const RpcStub: {\n        new <T extends Rpc.Stubable>(value: T): Rpc.Stub<T>;\n    };\n    export abstract class RpcTarget implements Rpc.RpcTargetBranded {\n        [Rpc.__RPC_TARGET_BRAND]: never;\n    }\n    // `protected` fields don't appear in `keyof`s, so can't be accessed over RPC\n    export abstract class WorkerEntrypoint<Env = Cloudflare.Env, Props = {}> implements Rpc.WorkerEntrypointBranded {\n        [Rpc.__WORKER_ENTRYPOINT_BRAND]: never;\n        protected ctx: ExecutionContext<Props>;\n        protected env: Env;\n        constructor(ctx: ExecutionContext, env: Env);\n        email?(message: ForwardableEmailMessage): void | Promise<void>;\n        fetch?(request: Request): Response | Promise<Response>;\n        queue?(batch: MessageBatch<unknown>): void | Promise<void>;\n        scheduled?(controller: ScheduledController): void | Promise<void>;\n        tail?(events: TraceItem[]): void | Promise<void>;\n        tailStream?(event: TailStream.TailEvent<TailStream.Onset>): TailStream.TailEventHandlerType | Promise<TailStream.TailEventHandlerType>;\n        test?(controller: TestController): void | Promise<void>;\n        trace?(traces: TraceItem[]): void | Promise<void>;\n    }\n    export abstract class DurableObject<Env = Cloudflare.Env, Props = {}> implements Rpc.DurableObjectBranded {\n        [Rpc.__DURABLE_OBJECT_BRAND]: never;\n        protected ctx: DurableObjectState<Props>;\n        protected env: Env;\n        constructor(ctx: DurableObjectState, env: Env);\n        alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise<void>;\n        fetch?(request: Request): Response | Promise<Response>;\n        webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise<void>;\n        webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise<void>;\n        webSocketError?(ws: WebSocket, error: unknown): void | Promise<void>;\n    }\n    export type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';\n    export type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number;\n    export type WorkflowDelayDuration = WorkflowSleepDuration;\n    export type WorkflowTimeoutDuration = WorkflowSleepDuration;\n    export type WorkflowRetentionDuration = WorkflowSleepDuration;\n    export type WorkflowBackoff = 'constant' | 'linear' | 'exponential';\n    export type WorkflowStepConfig = {\n        retries?: {\n            limit: number;\n            delay: WorkflowDelayDuration | number;\n            backoff?: WorkflowBackoff;\n        };\n        timeout?: WorkflowTimeoutDuration | number;\n    };\n    export type WorkflowEvent<T> = {\n        payload: Readonly<T>;\n        timestamp: Date;\n        instanceId: string;\n    };\n    export type WorkflowStepEvent<T> = {\n        payload: Readonly<T>;\n        timestamp: Date;\n        type: string;\n    };\n    export abstract class WorkflowStep {\n        do<T extends Rpc.Serializable<T>>(name: string, callback: () => Promise<T>): Promise<T>;\n        do<T extends Rpc.Serializable<T>>(name: string, config: WorkflowStepConfig, callback: () => Promise<T>): Promise<T>;\n        sleep: (name: string, duration: WorkflowSleepDuration) => Promise<void>;\n        sleepUntil: (name: string, timestamp: Date | number) => Promise<void>;\n        waitForEvent<T extends Rpc.Serializable<T>>(name: string, options: {\n            type: string;\n            timeout?: WorkflowTimeoutDuration | number;\n        }): Promise<WorkflowStepEvent<T>>;\n    }\n    export abstract class WorkflowEntrypoint<Env = unknown, T extends Rpc.Serializable<T> | unknown = unknown> implements Rpc.WorkflowEntrypointBranded {\n        [Rpc.__WORKFLOW_ENTRYPOINT_BRAND]: never;\n        protected ctx: ExecutionContext;\n        protected env: Env;\n        constructor(ctx: ExecutionContext, env: Env);\n        run(event: Readonly<WorkflowEvent<T>>, step: WorkflowStep): Promise<unknown>;\n    }\n    export function waitUntil(promise: Promise<unknown>): void;\n    export function withEnv(newEnv: unknown, fn: () => unknown): unknown;\n    export function withExports(newExports: unknown, fn: () => unknown): unknown;\n    export function withEnvAndExports(newEnv: unknown, newExports: unknown, fn: () => unknown): unknown;\n    export const env: Cloudflare.Env;\n    export const exports: Cloudflare.Exports;\n}\ndeclare module 'cloudflare:workers' {\n    export = CloudflareWorkersModule;\n}\ninterface SecretsStoreSecret {\n    /**\n     * Get a secret from the Secrets Store, returning a string of the secret value\n     * if it exists, or throws an error if it does not exist\n     */\n    get(): Promise<string>;\n}\ndeclare module \"cloudflare:sockets\" {\n    function _connect(address: string | SocketAddress, options?: SocketOptions): Socket;\n    export { _connect as connect };\n}\ntype MarkdownDocument = {\n    name: string;\n    blob: Blob;\n};\ntype ConversionResponse = {\n    name: string;\n    mimeType: string;\n    format: 'markdown';\n    tokens: number;\n    data: string;\n} | {\n    name: string;\n    mimeType: string;\n    format: 'error';\n    error: string;\n};\ntype ImageConversionOptions = {\n    descriptionLanguage?: 'en' | 'es' | 'fr' | 'it' | 'pt' | 'de';\n};\ntype EmbeddedImageConversionOptions = ImageConversionOptions & {\n    convert?: boolean;\n    maxConvertedImages?: number;\n};\ntype ConversionOptions = {\n    html?: {\n        images?: EmbeddedImageConversionOptions & {\n            convertOGImage?: boolean;\n        };\n    };\n    docx?: {\n        images?: EmbeddedImageConversionOptions;\n    };\n    image?: ImageConversionOptions;\n    pdf?: {\n        images?: EmbeddedImageConversionOptions;\n        metadata?: boolean;\n    };\n};\ntype ConversionRequestOptions = {\n    gateway?: GatewayOptions;\n    extraHeaders?: object;\n    conversionOptions?: ConversionOptions;\n};\ntype SupportedFileFormat = {\n    mimeType: string;\n    extension: string;\n};\ndeclare abstract class ToMarkdownService {\n    transform(files: MarkdownDocument[], options?: ConversionRequestOptions): Promise<ConversionResponse[]>;\n    transform(files: MarkdownDocument, options?: ConversionRequestOptions): Promise<ConversionResponse>;\n    supported(): Promise<SupportedFileFormat[]>;\n}\ndeclare namespace TailStream {\n    interface Header {\n        readonly name: string;\n        readonly value: string;\n    }\n    interface FetchEventInfo {\n        readonly type: \"fetch\";\n        readonly method: string;\n        readonly url: string;\n        readonly cfJson?: object;\n        readonly headers: Header[];\n    }\n    interface JsRpcEventInfo {\n        readonly type: \"jsrpc\";\n    }\n    interface ScheduledEventInfo {\n        readonly type: \"scheduled\";\n        readonly scheduledTime: Date;\n        readonly cron: string;\n    }\n    interface AlarmEventInfo {\n        readonly type: \"alarm\";\n        readonly scheduledTime: Date;\n    }\n    interface QueueEventInfo {\n        readonly type: \"queue\";\n        readonly queueName: string;\n        readonly batchSize: number;\n    }\n    interface EmailEventInfo {\n        readonly type: \"email\";\n        readonly mailFrom: string;\n        readonly rcptTo: string;\n        readonly rawSize: number;\n    }\n    interface TraceEventInfo {\n        readonly type: \"trace\";\n        readonly traces: (string | null)[];\n    }\n    interface HibernatableWebSocketEventInfoMessage {\n        readonly type: \"message\";\n    }\n    interface HibernatableWebSocketEventInfoError {\n        readonly type: \"error\";\n    }\n    interface HibernatableWebSocketEventInfoClose {\n        readonly type: \"close\";\n        readonly code: number;\n        readonly wasClean: boolean;\n    }\n    interface HibernatableWebSocketEventInfo {\n        readonly type: \"hibernatableWebSocket\";\n        readonly info: HibernatableWebSocketEventInfoClose | HibernatableWebSocketEventInfoError | HibernatableWebSocketEventInfoMessage;\n    }\n    interface CustomEventInfo {\n        readonly type: \"custom\";\n    }\n    interface FetchResponseInfo {\n        readonly type: \"fetch\";\n        readonly statusCode: number;\n    }\n    type EventOutcome = \"ok\" | \"canceled\" | \"exception\" | \"unknown\" | \"killSwitch\" | \"daemonDown\" | \"exceededCpu\" | \"exceededMemory\" | \"loadShed\" | \"responseStreamDisconnected\" | \"scriptNotFound\";\n    interface ScriptVersion {\n        readonly id: string;\n        readonly tag?: string;\n        readonly message?: string;\n    }\n    interface Onset {\n        readonly type: \"onset\";\n        readonly attributes: Attribute[];\n        // id for the span being opened by this Onset event.\n        readonly spanId: string;\n        readonly dispatchNamespace?: string;\n        readonly entrypoint?: string;\n        readonly executionModel: string;\n        readonly scriptName?: string;\n        readonly scriptTags?: string[];\n        readonly scriptVersion?: ScriptVersion;\n        readonly info: FetchEventInfo | JsRpcEventInfo | ScheduledEventInfo | AlarmEventInfo | QueueEventInfo | EmailEventInfo | TraceEventInfo | HibernatableWebSocketEventInfo | CustomEventInfo;\n    }\n    interface Outcome {\n        readonly type: \"outcome\";\n        readonly outcome: EventOutcome;\n        readonly cpuTime: number;\n        readonly wallTime: number;\n    }\n    interface SpanOpen {\n        readonly type: \"spanOpen\";\n        readonly name: string;\n        // id for the span being opened by this SpanOpen event.\n        readonly spanId: string;\n        readonly info?: FetchEventInfo | JsRpcEventInfo | Attributes;\n    }\n    interface SpanClose {\n        readonly type: \"spanClose\";\n        readonly outcome: EventOutcome;\n    }\n    interface DiagnosticChannelEvent {\n        readonly type: \"diagnosticChannel\";\n        readonly channel: string;\n        readonly message: any;\n    }\n    interface Exception {\n        readonly type: \"exception\";\n        readonly name: string;\n        readonly message: string;\n        readonly stack?: string;\n    }\n    interface Log {\n        readonly type: \"log\";\n        readonly level: \"debug\" | \"error\" | \"info\" | \"log\" | \"warn\";\n        readonly message: object;\n    }\n    // This marks the worker handler return information.\n    // This is separate from Outcome because the worker invocation can live for a long time after\n    // returning. For example - Websockets that return an http upgrade response but then continue\n    // streaming information or SSE http connections.\n    interface Return {\n        readonly type: \"return\";\n        readonly info?: FetchResponseInfo;\n    }\n    interface Attribute {\n        readonly name: string;\n        readonly value: string | string[] | boolean | boolean[] | number | number[] | bigint | bigint[];\n    }\n    interface Attributes {\n        readonly type: \"attributes\";\n        readonly info: Attribute[];\n    }\n    type EventType = Onset | Outcome | SpanOpen | SpanClose | DiagnosticChannelEvent | Exception | Log | Return | Attributes;\n    // Context in which this trace event lives.\n    interface SpanContext {\n        // Single id for the entire top-level invocation\n        // This should be a new traceId for the first worker stage invoked in the eyeball request and then\n        // same-account service-bindings should reuse the same traceId but cross-account service-bindings\n        // should use a new traceId.\n        readonly traceId: string;\n        // spanId in which this event is handled\n        // for Onset and SpanOpen events this would be the parent span id\n        // for Outcome and SpanClose these this would be the span id of the opening Onset and SpanOpen events\n        // For Hibernate and Mark this would be the span under which they were emitted.\n        // spanId is not set ONLY if:\n        //  1. This is an Onset event\n        //  2. We are not inherting any SpanContext. (e.g. this is a cross-account service binding or a new top-level invocation)\n        readonly spanId?: string;\n    }\n    interface TailEvent<Event extends EventType> {\n        // invocation id of the currently invoked worker stage.\n        // invocation id will always be unique to every Onset event and will be the same until the Outcome event.\n        readonly invocationId: string;\n        // Inherited spanContext for this event.\n        readonly spanContext: SpanContext;\n        readonly timestamp: Date;\n        readonly sequence: number;\n        readonly event: Event;\n    }\n    type TailEventHandler<Event extends EventType = EventType> = (event: TailEvent<Event>) => void | Promise<void>;\n    type TailEventHandlerObject = {\n        outcome?: TailEventHandler<Outcome>;\n        spanOpen?: TailEventHandler<SpanOpen>;\n        spanClose?: TailEventHandler<SpanClose>;\n        diagnosticChannel?: TailEventHandler<DiagnosticChannelEvent>;\n        exception?: TailEventHandler<Exception>;\n        log?: TailEventHandler<Log>;\n        return?: TailEventHandler<Return>;\n        attributes?: TailEventHandler<Attributes>;\n    };\n    type TailEventHandlerType = TailEventHandler | TailEventHandlerObject;\n}\n// Copyright (c) 2022-2023 Cloudflare, Inc.\n// Licensed under the Apache 2.0 license found in the LICENSE file or at:\n//     https://opensource.org/licenses/Apache-2.0\n/**\n * Data types supported for holding vector metadata.\n */\ntype VectorizeVectorMetadataValue = string | number | boolean | string[];\n/**\n * Additional information to associate with a vector.\n */\ntype VectorizeVectorMetadata = VectorizeVectorMetadataValue | Record<string, VectorizeVectorMetadataValue>;\ntype VectorFloatArray = Float32Array | Float64Array;\ninterface VectorizeError {\n    code?: number;\n    error: string;\n}\n/**\n * Comparison logic/operation to use for metadata filtering.\n *\n * This list is expected to grow as support for more operations are released.\n */\ntype VectorizeVectorMetadataFilterOp = '$eq' | '$ne' | '$lt' | '$lte' | '$gt' | '$gte';\ntype VectorizeVectorMetadataFilterCollectionOp = '$in' | '$nin';\n/**\n * Filter criteria for vector metadata used to limit the retrieved query result set.\n */\ntype VectorizeVectorMetadataFilter = {\n    [field: string]: Exclude<VectorizeVectorMetadataValue, string[]> | null | {\n        [Op in VectorizeVectorMetadataFilterOp]?: Exclude<VectorizeVectorMetadataValue, string[]> | null;\n    } | {\n        [Op in VectorizeVectorMetadataFilterCollectionOp]?: Exclude<VectorizeVectorMetadataValue, string[]>[];\n    };\n};\n/**\n * Supported distance metrics for an index.\n * Distance metrics determine how other \"similar\" vectors are determined.\n */\ntype VectorizeDistanceMetric = \"euclidean\" | \"cosine\" | \"dot-product\";\n/**\n * Metadata return levels for a Vectorize query.\n *\n * Default to \"none\".\n *\n * @property all      Full metadata for the vector return set, including all fields (including those un-indexed) without truncation. This is a more expensive retrieval, as it requires additional fetching & reading of un-indexed data.\n * @property indexed  Return all metadata fields configured for indexing in the vector return set. This level of retrieval is \"free\" in that no additional overhead is incurred returning this data. However, note that indexed metadata is subject to truncation (especially for larger strings).\n * @property none     No indexed metadata will be returned.\n */\ntype VectorizeMetadataRetrievalLevel = \"all\" | \"indexed\" | \"none\";\ninterface VectorizeQueryOptions {\n    topK?: number;\n    namespace?: string;\n    returnValues?: boolean;\n    returnMetadata?: boolean | VectorizeMetadataRetrievalLevel;\n    filter?: VectorizeVectorMetadataFilter;\n}\n/**\n * Information about the configuration of an index.\n */\ntype VectorizeIndexConfig = {\n    dimensions: number;\n    metric: VectorizeDistanceMetric;\n} | {\n    preset: string; // keep this generic, as we'll be adding more presets in the future and this is only in a read capacity\n};\n/**\n * Metadata about an existing index.\n *\n * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released.\n * See {@link VectorizeIndexInfo} for its post-beta equivalent.\n */\ninterface VectorizeIndexDetails {\n    /** The unique ID of the index */\n    readonly id: string;\n    /** The name of the index. */\n    name: string;\n    /** (optional) A human readable description for the index. */\n    description?: string;\n    /** The index configuration, including the dimension size and distance metric. */\n    config: VectorizeIndexConfig;\n    /** The number of records containing vectors within the index. */\n    vectorsCount: number;\n}\n/**\n * Metadata about an existing index.\n */\ninterface VectorizeIndexInfo {\n    /** The number of records containing vectors within the index. */\n    vectorCount: number;\n    /** Number of dimensions the index has been configured for. */\n    dimensions: number;\n    /** ISO 8601 datetime of the last processed mutation on in the index. All changes before this mutation will be reflected in the index state. */\n    processedUpToDatetime: number;\n    /** UUIDv4 of the last mutation processed by the index. All changes before this mutation will be reflected in the index state. */\n    processedUpToMutation: number;\n}\n/**\n * Represents a single vector value set along with its associated metadata.\n */\ninterface VectorizeVector {\n    /** The ID for the vector. This can be user-defined, and must be unique. It should uniquely identify the object, and is best set based on the ID of what the vector represents. */\n    id: string;\n    /** The vector values */\n    values: VectorFloatArray | number[];\n    /** The namespace this vector belongs to. */\n    namespace?: string;\n    /** Metadata associated with the vector. Includes the values of other fields and potentially additional details. */\n    metadata?: Record<string, VectorizeVectorMetadata>;\n}\n/**\n * Represents a matched vector for a query along with its score and (if specified) the matching vector information.\n */\ntype VectorizeMatch = Pick<Partial<VectorizeVector>, \"values\"> & Omit<VectorizeVector, \"values\"> & {\n    /** The score or rank for similarity, when returned as a result */\n    score: number;\n};\n/**\n * A set of matching {@link VectorizeMatch} for a particular query.\n */\ninterface VectorizeMatches {\n    matches: VectorizeMatch[];\n    count: number;\n}\n/**\n * Results of an operation that performed a mutation on a set of vectors.\n * Here, `ids` is a list of vectors that were successfully processed.\n *\n * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released.\n * See {@link VectorizeAsyncMutation} for its post-beta equivalent.\n */\ninterface VectorizeVectorMutation {\n    /* List of ids of vectors that were successfully processed. */\n    ids: string[];\n    /* Total count of the number of processed vectors. */\n    count: number;\n}\n/**\n * Result type indicating a mutation on the Vectorize Index.\n * Actual mutations are processed async where the `mutationId` is the unique identifier for the operation.\n */\ninterface VectorizeAsyncMutation {\n    /** The unique identifier for the async mutation operation containing the changeset. */\n    mutationId: string;\n}\n/**\n * A Vectorize Vector Search Index for querying vectors/embeddings.\n *\n * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released.\n * See {@link Vectorize} for its new implementation.\n */\ndeclare abstract class VectorizeIndex {\n    /**\n     * Get information about the currently bound index.\n     * @returns A promise that resolves with information about the current index.\n     */\n    public describe(): Promise<VectorizeIndexDetails>;\n    /**\n     * Use the provided vector to perform a similarity search across the index.\n     * @param vector Input vector that will be used to drive the similarity search.\n     * @param options Configuration options to massage the returned data.\n     * @returns A promise that resolves with matched and scored vectors.\n     */\n    public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise<VectorizeMatches>;\n    /**\n     * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown.\n     * @param vectors List of vectors that will be inserted.\n     * @returns A promise that resolves with the ids & count of records that were successfully processed.\n     */\n    public insert(vectors: VectorizeVector[]): Promise<VectorizeVectorMutation>;\n    /**\n     * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values.\n     * @param vectors List of vectors that will be upserted.\n     * @returns A promise that resolves with the ids & count of records that were successfully processed.\n     */\n    public upsert(vectors: VectorizeVector[]): Promise<VectorizeVectorMutation>;\n    /**\n     * Delete a list of vectors with a matching id.\n     * @param ids List of vector ids that should be deleted.\n     * @returns A promise that resolves with the ids & count of records that were successfully processed (and thus deleted).\n     */\n    public deleteByIds(ids: string[]): Promise<VectorizeVectorMutation>;\n    /**\n     * Get a list of vectors with a matching id.\n     * @param ids List of vector ids that should be returned.\n     * @returns A promise that resolves with the raw unscored vectors matching the id set.\n     */\n    public getByIds(ids: string[]): Promise<VectorizeVector[]>;\n}\n/**\n * A Vectorize Vector Search Index for querying vectors/embeddings.\n *\n * Mutations in this version are async, returning a mutation id.\n */\ndeclare abstract class Vectorize {\n    /**\n     * Get information about the currently bound index.\n     * @returns A promise that resolves with information about the current index.\n     */\n    public describe(): Promise<VectorizeIndexInfo>;\n    /**\n     * Use the provided vector to perform a similarity search across the index.\n     * @param vector Input vector that will be used to drive the similarity search.\n     * @param options Configuration options to massage the returned data.\n     * @returns A promise that resolves with matched and scored vectors.\n     */\n    public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise<VectorizeMatches>;\n    /**\n     * Use the provided vector-id to perform a similarity search across the index.\n     * @param vectorId Id for a vector in the index against which the index should be queried.\n     * @param options Configuration options to massage the returned data.\n     * @returns A promise that resolves with matched and scored vectors.\n     */\n    public queryById(vectorId: string, options?: VectorizeQueryOptions): Promise<VectorizeMatches>;\n    /**\n     * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown.\n     * @param vectors List of vectors that will be inserted.\n     * @returns A promise that resolves with a unique identifier of a mutation containing the insert changeset.\n     */\n    public insert(vectors: VectorizeVector[]): Promise<VectorizeAsyncMutation>;\n    /**\n     * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values.\n     * @param vectors List of vectors that will be upserted.\n     * @returns A promise that resolves with a unique identifier of a mutation containing the upsert changeset.\n     */\n    public upsert(vectors: VectorizeVector[]): Promise<VectorizeAsyncMutation>;\n    /**\n     * Delete a list of vectors with a matching id.\n     * @param ids List of vector ids that should be deleted.\n     * @returns A promise that resolves with a unique identifier of a mutation containing the delete changeset.\n     */\n    public deleteByIds(ids: string[]): Promise<VectorizeAsyncMutation>;\n    /**\n     * Get a list of vectors with a matching id.\n     * @param ids List of vector ids that should be returned.\n     * @returns A promise that resolves with the raw unscored vectors matching the id set.\n     */\n    public getByIds(ids: string[]): Promise<VectorizeVector[]>;\n}\n/**\n * The interface for \"version_metadata\" binding\n * providing metadata about the Worker Version using this binding.\n */\ntype WorkerVersionMetadata = {\n    /** The ID of the Worker Version using this binding */\n    id: string;\n    /** The tag of the Worker Version using this binding */\n    tag: string;\n    /** The timestamp of when the Worker Version was uploaded */\n    timestamp: string;\n};\ninterface DynamicDispatchLimits {\n    /**\n     * Limit CPU time in milliseconds.\n     */\n    cpuMs?: number;\n    /**\n     * Limit number of subrequests.\n     */\n    subRequests?: number;\n}\ninterface DynamicDispatchOptions {\n    /**\n     * Limit resources of invoked Worker script.\n     */\n    limits?: DynamicDispatchLimits;\n    /**\n     * Arguments for outbound Worker script, if configured.\n     */\n    outbound?: {\n        [key: string]: any;\n    };\n}\ninterface DispatchNamespace {\n    /**\n    * @param name Name of the Worker script.\n    * @param args Arguments to Worker script.\n    * @param options Options for Dynamic Dispatch invocation.\n    * @returns A Fetcher object that allows you to send requests to the Worker script.\n    * @throws If the Worker script does not exist in this dispatch namespace, an error will be thrown.\n    */\n    get(name: string, args?: {\n        [key: string]: any;\n    }, options?: DynamicDispatchOptions): Fetcher;\n}\ndeclare module 'cloudflare:workflows' {\n    /**\n     * NonRetryableError allows for a user to throw a fatal error\n     * that makes a Workflow instance fail immediately without triggering a retry\n     */\n    export class NonRetryableError extends Error {\n        public constructor(message: string, name?: string);\n    }\n}\ndeclare abstract class Workflow<PARAMS = unknown> {\n    /**\n     * Get a handle to an existing instance of the Workflow.\n     * @param id Id for the instance of this Workflow\n     * @returns A promise that resolves with a handle for the Instance\n     */\n    public get(id: string): Promise<WorkflowInstance>;\n    /**\n     * Create a new instance and return a handle to it. If a provided id exists, an error will be thrown.\n     * @param options Options when creating an instance including id and params\n     * @returns A promise that resolves with a handle for the Instance\n     */\n    public create(options?: WorkflowInstanceCreateOptions<PARAMS>): Promise<WorkflowInstance>;\n    /**\n     * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown.\n     * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached.\n     * @param batch List of Options when creating an instance including name and params\n     * @returns A promise that resolves with a list of handles for the created instances.\n     */\n    public createBatch(batch: WorkflowInstanceCreateOptions<PARAMS>[]): Promise<WorkflowInstance[]>;\n}\ntype WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';\ntype WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number;\ntype WorkflowRetentionDuration = WorkflowSleepDuration;\ninterface WorkflowInstanceCreateOptions<PARAMS = unknown> {\n    /**\n     * An id for your Workflow instance. Must be unique within the Workflow.\n     */\n    id?: string;\n    /**\n     * The event payload the Workflow instance is triggered with\n     */\n    params?: PARAMS;\n    /**\n     * The retention policy for Workflow instance.\n     * Defaults to the maximum retention period available for the owner's account.\n     */\n    retention?: {\n        successRetention?: WorkflowRetentionDuration;\n        errorRetention?: WorkflowRetentionDuration;\n    };\n}\ntype InstanceStatus = {\n    status: 'queued' // means that instance is waiting to be started (see concurrency limits)\n     | 'running' | 'paused' | 'errored' | 'terminated' // user terminated the instance while it was running\n     | 'complete' | 'waiting' // instance is hibernating and waiting for sleep or event to finish\n     | 'waitingForPause' // instance is finishing the current work to pause\n     | 'unknown';\n    error?: {\n        name: string;\n        message: string;\n    };\n    output?: unknown;\n};\ninterface WorkflowError {\n    code?: number;\n    message: string;\n}\ndeclare abstract class WorkflowInstance {\n    public id: string;\n    /**\n     * Pause the instance.\n     */\n    public pause(): Promise<void>;\n    /**\n     * Resume the instance. If it is already running, an error will be thrown.\n     */\n    public resume(): Promise<void>;\n    /**\n     * Terminate the instance. If it is errored, terminated or complete, an error will be thrown.\n     */\n    public terminate(): Promise<void>;\n    /**\n     * Restart the instance.\n     */\n    public restart(): Promise<void>;\n    /**\n     * Returns the current status of the instance.\n     */\n    public status(): Promise<InstanceStatus>;\n    /**\n     * Send an event to this instance.\n     */\n    public sendEvent({ type, payload, }: {\n        type: string;\n        payload: unknown;\n    }): Promise<void>;\n}\n"
  },
  {
    "path": "wrangler.jsonc",
    "content": "{\n  \"$schema\": \"node_modules/wrangler/config-schema.json\",\n  \"name\": \"sink\",\n  \"main\": \".output/server/index.mjs\",\n  \"assets\": {\n    \"binding\": \"ASSETS\",\n    \"directory\": \".output/public\",\n    \"html_handling\": \"drop-trailing-slash\"\n  },\n  \"compatibility_date\": \"2025-05-08\",\n  \"compatibility_flags\": [\n    \"nodejs_compat\"\n  ],\n  \"keep_vars\": true,\n  \"upload_source_maps\": true,\n  \"observability\": {\n    \"logs\": {\n      \"enabled\": true\n    },\n    \"traces\": {\n      \"enabled\": true\n    }\n  },\n  \"ai\": {\n    \"binding\": \"AI\",\n    \"remote\": true\n  },\n  \"analytics_engine_datasets\": [\n    {\n      \"binding\": \"ANALYTICS\",\n      \"dataset\": \"sink\"\n    }\n  ],\n  \"kv_namespaces\": [\n    {\n      \"binding\": \"KV\",\n      \"preview_id\": \"ef93d42dc4b34969bab404d2e80f8dd3\",\n      \"id\": \"ef93d42dc4b34969bab404d2e80f8dd3\" // IMPORTANT: Change this to your KV namespace ID\n    }\n  ],\n  \"r2_buckets\": [\n    {\n      \"binding\": \"R2\",\n      \"preview_bucket_name\": \"sink\",\n      \"bucket_name\": \"sink\" // IMPORTANT: Change this to your R2 bucket name, or create one with `wrangler r2 bucket create sink`\n    }\n  ],\n  \"triggers\": {\n    \"crons\": [\"0 0 * * *\"] // Run backup task daily at 00:00 UTC\n  }\n}\n"
  }
]