diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..fcadb2c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text eol=lf
diff --git a/.github/workflows/auto-update-country.yml b/.github/workflows/auto-update-country.yml
index 1f217e9..36afd0d 100644
--- a/.github/workflows/auto-update-country.yml
+++ b/.github/workflows/auto-update-country.yml
@@ -1,31 +1,41 @@
-name: Auto database update browser-country
+name: Auto Update Country Packages
+
on:
schedule:
- cron: '42 19 * * *'
workflow_dispatch:
+
jobs:
release:
- name: Auto database update browser-country
+ if: github.repository == 'sapics/ip-location-api'
runs-on: ubuntu-latest
steps:
- - name: checkout
- uses: actions/checkout@v4
- - name: setup Node
- uses: actions/setup-node@v4
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ - uses: actions/setup-node@v4
with:
- node-version: 20.x
+ node-version-file: package.json
+ cache: pnpm
registry-url: 'https://registry.npmjs.org'
- - name: install dependencies
- run: npm i
- - name: update
- run: npm run updatedb-browser-country
- - name: test
- run: npm run test
- - name: commit
- run: script/auto-update-country.sh
+
+ - name: Install dependencies
+ run: pnpm i
+
+ - name: Build
+ run: pnpm -r --workspace-concurrency=1 --filter "@iplookup/country*..." build
+
+ - name: Bump version
+ shell: bash
+ run: |
+ //* Increments patch version and appends current date (YYYYMMDD) for all country packages
+ for pkg in packages/country*; do
+ [ -f "$pkg/package.json" ] \
+ && cd $pkg \
+ && npm pkg set version="$(pnpm exec semver $(node -p "require('./package.json').version") -i patch)-$(date +'%Y%m%d')" \
+ && cd ../..
+ done
+
+ - name: Publish
+ run: pnpm -r --filter "@iplookup/country*" publish --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- - name: push changes
- uses: ad-m/github-push-action@master
- with:
- github_token: ${{secrets.GTOKEN}}
diff --git a/.github/workflows/auto-update-geocode.yml b/.github/workflows/auto-update-geocode.yml
index c1fa751..97b232a 100644
--- a/.github/workflows/auto-update-geocode.yml
+++ b/.github/workflows/auto-update-geocode.yml
@@ -1,31 +1,41 @@
-name: Auto database update browser-geocode
+name: Auto Update Geocode Packages
+
on:
schedule:
- cron: '2 0 1,2,3 * *'
workflow_dispatch:
+
jobs:
release:
- name: Auto database update browser-geocode
+ if: github.repository == 'sapics/ip-location-api'
runs-on: ubuntu-latest
steps:
- - name: checkout
- uses: actions/checkout@v4
- - name: setup Node
- uses: actions/setup-node@v4
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ - uses: actions/setup-node@v4
with:
- node-version: 20.x
+ node-version-file: package.json
+ cache: pnpm
registry-url: 'https://registry.npmjs.org'
- - name: install dependencies
- run: npm i
- - name: update
- run: npm run updatedb-browser-geocode
- - name: test
- run: npm run test
- - name: commit
- run: script/auto-update-geocode.sh
+
+ - name: Install dependencies
+ run: pnpm i
+
+ - name: Build
+ run: pnpm -r --workspace-concurrency=1 --filter "@iplookup/geocode*..." build
+
+ - name: Bump version
+ shell: bash
+ run: |
+ //* Increments patch version and appends current date (YYYYMMDD) for all geocode packages
+ for pkg in packages/geocode*; do
+ [ -f "$pkg/package.json" ] \
+ && cd $pkg \
+ && npm pkg set version="$(pnpm exec semver $(node -p "require('./package.json').version") -i patch)-$(date +'%Y%m%d')" \
+ && cd ../..
+ done
+
+ - name: Publish
+ run: pnpm -r --filter "@iplookup/geocode*" publish --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- - name: push changes
- uses: ad-m/github-push-action@master
- with:
- github_token: ${{secrets.GTOKEN}}
diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
deleted file mode 100644
index a17d8ae..0000000
--- a/.github/workflows/auto-update.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-name: Auto database update
-on:
- workflow_dispatch:
-jobs:
- release:
- name: latest version
- runs-on: ubuntu-latest
- steps:
- - name: checkout
- uses: actions/checkout@v4
- - name: setup Node
- uses: actions/setup-node@v4
- with:
- node-version: 20.x
- registry-url: 'https://registry.npmjs.org'
- - name: install dependencies
- run: npm i
- - name: update
- run: npm run updatedb
- - name: test
- run: npm run test
- - name: commit
- run: script/auto-update.sh
- env:
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- - name: push changes
- uses: ad-m/github-push-action@master
- with:
- github_token: ${{secrets.GTOKEN}}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..9ab09ef
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,36 @@
+name: Build
+
+on:
+ workflow_call:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+ - closed
+
+jobs:
+ build:
+ name: Build with Node.js ${{ matrix.node-version }}
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [18, 20, 22]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm i
+
+ - name: Lint
+ run: pnpm lint
+
+ - name: Build
+ run: pnpm build
+
+ - name: Test
+ run: pnpm test
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..99b22d1
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,34 @@
+name: Build
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ build:
+ uses: ./.github/workflows/build.yml
+
+ release:
+ if: github.repository == 'sapics/ip-location-api'
+ needs: build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: package.json
+ cache: pnpm
+ registry-url: 'https://registry.npmjs.org'
+
+ - name: Install dependencies
+ run: pnpm i
+
+ - name: Build
+ run: pnpm build
+
+ - name: Publish
+ run: pnpm -r publish --no-git-checks
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index cac1308..0000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Test
-
-on:
- workflow_dispatch:
-
-jobs:
- test-esm:
- name: Test ESM & CJS
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [14, 16, 18, 20, 22]
- steps:
- - uses: actions/checkout@v4
- - name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v4
- with:
- node-version: ${{ matrix.node-version }}
- - name: npm install
- run: npm install
- - name: run test ESM
- run: |
- npm run updatedb
- npx jasmine spec/*.mjs
- - name: run test CJS
- run: |
- npm run updatedb-cjs
- npx jasmine spec/*.cjs
diff --git a/.gitignore b/.gitignore
index 5ec5b4f..ae62023 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,13 @@
-node_modules
-/tmp
-/data
-/browser/country/4
-/browser/country/6
-/browser/country/*.idx
-/browser/country-extra/4
-/browser/country-extra/6
-/browser/country-extra/*.idx
-/browser/geocode/4
-/browser/geocode/6
-/browser/geocode/*.idx
-/browser/geocode-extra/4
-/browser/geocode-extra/6
-/browser/geocode-extra/*.idx
+dist
+indexes
+.DS_Store
+coverage
+*.lcov
+node_modules/
+*.tsbuildinfo
+.npm
+.eslintcache
+*.tgz
+.env
+tmp
+data
\ No newline at end of file
diff --git a/.release-it.json b/.release-it.json
deleted file mode 100644
index c32c43f..0000000
--- a/.release-it.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "$schema": "https://unpkg.com/release-it/schema/release-it.json",
- "github": {
- "release": true
- }
-}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..c484f42
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,49 @@
+{
+ // Disable the default formatter, use eslint instead
+ "prettier.enable": false,
+ "editor.formatOnSave": false,
+
+ // Auto fix
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.organizeImports": "never"
+ },
+
+ // Silent the stylistic rules in you IDE, but still auto fix them
+ "eslint.rules.customizations": [
+ { "rule": "style/*", "severity": "off", "fixable": true },
+ { "rule": "format/*", "severity": "off", "fixable": true },
+ { "rule": "*-indent", "severity": "off", "fixable": true },
+ { "rule": "*-spacing", "severity": "off", "fixable": true },
+ { "rule": "*-spaces", "severity": "off", "fixable": true },
+ { "rule": "*-order", "severity": "off", "fixable": true },
+ { "rule": "*-dangle", "severity": "off", "fixable": true },
+ { "rule": "*-newline", "severity": "off", "fixable": true },
+ { "rule": "*quotes", "severity": "off", "fixable": true },
+ { "rule": "*semi", "severity": "off", "fixable": true }
+ ],
+
+ // Enable eslint for all supported languages
+ "eslint.validate": [
+ "javascript",
+ "javascriptreact",
+ "typescript",
+ "typescriptreact",
+ "vue",
+ "html",
+ "markdown",
+ "json",
+ "jsonc",
+ "yaml",
+ "toml",
+ "xml",
+ "gql",
+ "graphql",
+ "astro",
+ "css",
+ "less",
+ "scss",
+ "pcss",
+ "postcss"
+ ]
+}
diff --git a/README.md b/README.md
index 431c966..47e58a9 100644
--- a/README.md
+++ b/README.md
@@ -1,277 +1,29 @@
-# ip-location-api [](https://www.npmjs.com/package/ip-location-api)
+## 💻 Development
-Fast and customizable nodejs api to get geolocation information from ip address.
-`ip-location-api` make a fast lookup by using in-memory database.
+- Clone this repository
+- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable` (use `npm i -g corepack` for Node.js < 16.10)
+- Install dependencies using `pnpm install`
+- Build packages using `pnpm build`
+- Build packages in watch mode using `pnpm build:watch` (will watch for changes and rebuild)
+- Run interactive tests using `pnpm test:ui`
-This api is created for server-side javascript like Node.js.
-If you need client-side javascript which works in **BROWSER**, please try to use [@iplookup/country](https://github.com/sapics/ip-location-api/tree/main/browser/country) or [@iplookup/geocode](https://github.com/sapics/ip-location-api/tree/main/browser/geocode).
+## 📦 Packages
+- `ip-location-api`: Core package with full functionality
+- `@iplookup/country`: Lightweight package for country lookups (Web-compatible)
+- `@iplookup/country-extra`: Enhanced country data with additional information (Web-compatible)
+- `@iplookup/geocode`: Basic geocoding functionality (Web-compatible)
+- `@iplookup/geocode-extra`: Enhanced geocoding with additional location data (Web-compatible)
+- `@iplookup/util`: Utility functions for updating the database (Private)
-## Synopsis
+## 📝 Commit Convention
-```javascript
-import { lookup } from 'ip-location-api'
-// or CJS format
-// const { lookup } = require('ip-location-api')
+This repository follows [Conventional Commits](https://www.conventionalcommits.org/). All commit messages must be structured as follows:
-var ip = "207.97.227.239"
-var location = lookup(ip)
-// If you use Asynchronouns version which is configured with smallMemory=true,
-// var location = await lookup(ip)
-
-console.log(location)
-{
- country: 'FR',
- region1: 'NOR',
- region1_name: 'Normandy',
- region2: '27',
- region2_name: 'Eure',
- city: 'Heudicourt',
- // metro: Defined only in US (Aug.2024)
- timezone: 'Europe/Paris',
- eu: 1,
- latitude: 49.3335,
- longitude: 1.6566,
- area: 5,
- postcode: 27860,
- country_name: 'France',
- country_native: 'France',
- phone: [ 33 ],
- continent: 'EU',
- capital: 'Paris',
- currency: [ 'EUR' ],
- languages: [ 'fr' ],
- continent_name: 'Europe'
-}
-```
-
-## Benchmark
-
-I make a benchmark for making comparison with intel 12700 (2.1GHz), SSD, nodejs v20.
-You can change the memory usage or lookup time, by customizing location information.
-
-| benchmark | type | in-memory db | startup | lookup ipv4 | lookup ipv6 |
-| ---- | ---- | ---- | ---- | ---- | ---- |
-| ip-location-api
(default) | country | 6.9 MB | 3 ms | 0.362 μs/ip | 0.708 μs/ip |
-| ip-location-api
(async) | country | 2.9 MB | 2 ms | 243 μs/ip | 255 μs/ip |
-| ip-location-api | city | 62.9 MB | 14 ms | 0.751 μs/ip | 1.064 μs/ip |
-| ip-location-api
(async) | city | 15.6 MB | 5 ms | 267 μs/ip | 271 μs/ip |
-| [geoip-lite](https://github.com/geoip-lite/node-geoip) | city | 136 MB | 54 ms | 1.616 μs/ip | 3.890 μs/ip |
-| [fast-geoip](https://github.com/Doc999tor/fast-geoip)
(async) | city | 0MB | 4 ms | 1714 μs/ip | cannot lookup |
-
-
-## Installation
-
-```bash
-$ npm i ip-location-api
-```
-
-
-## API
-
-ip-location-api has two modes which are synchronous and asynchronous.
-Synchronouns one load all data in-memory at startup time, thus it makes fast lookup.
-Asynchronouns one load smaller data in-memory at startup time, and the other data is loaded from the hard drive for each lookup.
-
-| type | memory usage | startup | lookup |
-| ---- | ---- | ---- | ---- |
-| Synchronouns | Large | Slow | Fast |
-| Asynchronouns | Small | Fast | Slow |
-
-If you have a enough memory, I recommend to use synchronouns one because lookup is over 300 times faster than asynchronouns one.
-
-
-## Field description
-
-Note that as far as possible, the same field names as in `geoip-lite` are used, but some different field names are used.
-
-| `ip-location-api` | `geoip-lite` | database |description |
-| ---- | ---- | ---- | ---- |
-| country | country | MaxMind | "2 letter" country code defined at [ISO-3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1) |
-| region1 | region | MaxMind | region code which is short code for region1_name [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) |
-| region1_name | ❌️ | MaxMind | first sub division name (multi language) |
-| region2 | ❌️ | MaxMind | region code which is short code for region2_name [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) |
-| region2_name | ❌️ | MaxMind | second sub division name (multi language) |
-| city | city |MaxMind | city name (multi language) |
-| metro | metro |MaxMind | Geolocation target code from Google |
-| eu | eu | MaxMind | true: the member state of the European Union, undefined: for the other countries. This needs "country" field. |
-| timezone | timezone | MaxMind | time zone associated with location |
-| latitude | ll[0] | MaxMind | approximate [WGS84](https://en.wikipedia.org/wiki/World_Geodetic_System) latitude |
-| longitude | ll[1] | MaxMind | approximate [WGS84](https://en.wikipedia.org/wiki/World_Geodetic_System) longitude |
-| area | area | MaxMind | The radius in kilometers around the specified location where the IP address is likely to be. [maxmind blog](https://blog.maxmind.com/2022/06/using-maxminds-accuracy-radius/) |
-| postcode | ❌️ | MaxMind | region-specific postal code near the IP address |
-| ❌️ | range | MaxMind | We removes range information for optimization |
-| country_name | ❌️ | Countries| country name|
-| country_native | ❌️ | Countries| country name in native language|
-| continent | ❌️ | Countries| continent short code|
-| continent_name | ❌️ | Countries| continent name|
-| capital | ❌️ | Countries | capital name |
-| phone | ❌️ | Countries| international country calling codes |
-| currency | ❌️ | Countries | list of commonly used currencies |
-| languages | ❌️ | Countries | list of commonly used languages |
-
-
-## Setup the configuration
-
-You can configure the api by 3 way.
-- CLI parameters: `ILA_FIELDS=latitude,longitude`
-- Environment variables: `ILA_FIELDS=latitude,longitude`
-- Javascript: `await reload({fields: 'latitude,longitude'})` .
-
-The name of CLI prameter and environment variables are same.
-
-
-Conf key in `reload(conf)` is named with "LOWER CAMEL", CLI or ENV parameter is named with "SNAKE" with adding "ILA_" (come from Ip-Location-Api).
-
-| `reload(conf)` | CLI or ENV | default | description |
-| ---- | ---- | ---- | ---- |
-| fields | ILA_FIELDS | country | You can change the fields to be retrived from [MaxMind](https://www.maxmind.com/). When you set "all", all fields are displayed. |
-| addCountryInfo | ILA_ADD_COUNTRY_INFO | false | "true" make to add the country information from [Countries](https://github.com/annexare/Countries). This needs "country" field. |
-| dataDir | ILA_DATA_DIR | ../data | Directory for database file |
-| tmpDataDir | ILA_TMP_DATA_DIR | ../tmp | Directory for temporary file |
-| apiDir | ILA_API_DIR | .. | Directory for ip-location-api |
-| smallMemory | ILA_SMALL_MEMORY | false | false: synchronouns, ture: asynchronouns |
-| smallMemoryFileSize | ILA_SMALL_MEMORY_FILE_SIZE | 4096 | Max file size for asynchronouns data (no change is recommended) |
-| licenseKey | ILA_LICENSE_KEY | redist | By setting [MaxMind](https://www.maxmind.com/) License key, you can download latest version of database from [MaxMind](https://www.maxmind.com/) server. By setting to "redist", you can download the database from [node-geolite2-redist](https://github.com/sapics/node-geolite2-redist) repository which re-distribute the GeoLite2 database. |
-| ipLocationDb | ILA_IP_LOCATION_DB | | When you need only "country" field, you can use [ip-location-db](https://github.com/sapics/ip-location-db) data |
-| downloadType | ILA_DOWNLOAD_TYPE | reuse | By setting to "false", "tmpDataDir" directory is deleted every update. "reuse" dose not delete "tmpDataDir" and re-use "tmpDataDir"'s database if the database file dose not update. |
-| autoUpdate | ILA_AUTO_UPDATE | default | By setting to "false", it dose not update automatically. "default" updates twice weekly. You can set CRON PATTERN FORMAT which is provided by [cron](https://github.com/kelektiv/node-cron) with UTC timezone (For example, ILA_AUTO_UPDATE="0 1 * * *" for daily update). |
-| multiDbDir | ILA_MULTI_DB_DIR | false | If you use multiple "dataDir", please make this value to "true" |
-| series | ILA_SERIES | GeoLite2 | By setting to "GeoIP2", you can use premium database "GeoIP2" |
-| language | ILA_LANGUAGE | en | You can choose "de", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN". By changing, the language of "region1_name", "region2_name", "city" fields are changed |
-| silent | ILA_SILENT | false | true: deactivate unnecessary console.log |
-
-
-## Update database
-
-```bash
-npm run updatedb
```
+[optional scope]:
-or
+[optional body]
-```javascript
-import { updateDb } from 'ip-location-api'
-await updateDb(setting)
+[optional footer]
```
-
-
-There are three database update way.
-- ILA_LICENSE_KEY=redist
-- ILA_LICENSE_KEY=YOUR_GEOLITE2_LICENSE_KEY
-- ILA_IP_LOCATION_DB=YOUR_CHOOSEN_DATABSE
-
-When you set "ILA_LICENSE_KEY=redist" which is the dafault setting, it downloads GeoLite2 database from the redistribution repository [node-geolite2-redist](https://github.com/sapics/node-geolite2-redist).
-
-When you set "ILA_LICENSE_KEY=YOUR_GEOLITE2_LICENSE_KEY", it downloads GeoLite2 dastabase from the MaxMind provided server.
-`YOUR_GEOLITE2_LICENSE_KEY` should be replaced by a valid GeoLite2 license key. Please [follow instructions](https://dev.maxmind.com/geoip/geoip2/geolite2/) provided by MaxMind to obtain a license key.
-
-When you set "ILA_IP_LOCATION_DB=YOUR_CHOOSEN_DATABSE", it downloads from the [ip-location-db](https://github.com/sapics/ip-location-db) (country type only).
-You can "YOUR_CHOOSEN_DATABSE" from [ip-location-db](https://github.com/sapics/ip-location-db) with country type. For example, "geolite2-geo-whois-asn" is wider IP range country database which is equivalent to GeoLite2 database result for GeoLite2 country covered IP range and geo-whois-asn-country for the other IP range.
-The other example, "geo-whois-asn" is [CC0 licensed database](https://github.com/sapics/ip-location-db/tree/main/geo-asn-country), if you are unable to apply the GeoLite2 License.
-
-
-After v2.0, the database is created automatically at initial startup, and updated automatically by setting `ILA_AUTO_UPDATE` which updates twice weekly with default setting.
-
-
-## How to use with an example
-
-When you need only geographic coordinates, please set "ILA_FIELDS=latitude,longitude".
-You need to create a database for each configuration.
-After v2.0.0, the database is created at initial running (which takes some seconds), and auto update with `ILA_AUTO_UPDATE` which update twice weekly with default setting.
-
-The database is created by following CLI
-
-```bash
-$ npm run updatedb ILA_FIELDS=latitude,longitude
-```
-
-or
-
-```bash
-$ ILA_FIELDS=latitude,longitude # set environment variable
-$ npm run updatedb
-```
-
-or you can create database with 'create.js' which includes the following.
-
-```javascript
-await updateDb({fields:['latitude', 'longitude']})
-```
-
-
-The CLI command for using `app.js` which uses `ip-location-api` is necessary to start with following CLI parameter
-
-```bash
-$ node app.js ILA_FIELDS=latitude,longitude
-```
-
-or environment variable
-
-```bash
-$ ILA_FIELDS=latitude,longitude # set environment variable
-$ node app.js
-```
-
-or you can write down configuration in `reload` function of app.js as
-
-```javascript
-await reload({fields:['latitude', 'longitude']})
-// or await reload({fields:'latitude,longitude'})
-```
-
-
-If you need all the data in above field table, setting "ILA_FIELDS=all" and "ILA_ADD_COUNTRY_INFO=true" is the one.
-
-
-| benchmark | in-memory db | startup | lookup ipv4 | lookup ipv6 |
-| ---- | ---- | ---- | ---- | ---- |
-| longitude,latitude | 46.5 MB | 10 ms | 0.428 μs/ip | 0.776 μs/ip |
-| all | 76.4 MB | 18 ms | 1.054 μs/ip | 1.348 μs/ip |
-
-
-## For module bundler (webpack, vite, next.js, etc)
-
-Some module bundlers cannot work with original database system.
-If module bundlers could not work with `ip-location-api`, please try to import module as following.
-It works almost same as original module.
-
-```js
-import { lookup } from 'ip-location-api/pack'
-```
-
-
-It would be better to set directories for database files which have write permission.
-Without write permission directories, you cannot use this module.
-
-```bash
-ILA_DATA_DIR=/your_database_directory
-ILA_TMP_DATA_DIR=/your_tmporary_directory_for_database
-```
-
-
-## Node.js version
-
-This library supports Node.js >= 14 for ESM and CJS.
-
-
-## License and EULA
-
-There are multiple licenses in this library, one for the software library, and the others for the datadata.
-Please read the LICENSE and EULA files for details.
-
-
-The license for the software itself is published under MIT License by [sapics](https://github.com/sapics).
-
-
-The GeoLite2 database comes with certain restrictions and obligations, most notably:
- - You cannot prevent the library from updating the databases.
- - You cannot use the GeoLite2 data:
- - for FCRA purposes,
- - to identify specific households or individuals.
-
-You can read [the latest version of GeoLite2 EULA](https://www.maxmind.com/en/geolite2/eula).
-GeoLite2 database is provided under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) by [MaxMind](https://www.maxmind.com/), so, you need to create attribusion to [MaxMind](https://www.maxmind.com/) for using GeoLite2 database.
-
-
-The database of [Countries](https://github.com/annexare/Countries) is published under MIT license by [Annexare Studio](https://annexare.com/).
diff --git a/browser/country-extra/README.md b/browser/country-extra/README.md
deleted file mode 100644
index 71e7237..0000000
--- a/browser/country-extra/README.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# @iplookup/country-extra [](https://www.npmjs.com/package/@iplookup/country-extra)
-
-This is an API created to make [ip-location-api](https://github.com/sapics/ip-location-api) available for browsers.
-The database itself is large at 7MB, so it is splitted into over 2000 pieces for fast downloading in a browser.
-
-
-## Synopsis
-
-```html
-
-
-```
-
-#### ESM
-
-```javascript
-import IpLookup from '@iplookup/country-extra'
-await IpLookup("2402:b801:ea8b:23c0::")
-```
-
-#### CJS
-
-```javascript
-const IpLookup = require('@iplookup/country-extra')
-await IpLookup("207.97.227.239")
-```
-
-If you do not need extra information about country, try to use [@iplookup/country](https://github.com/sapics/ip-location-api/tree/main/browser/country).
-
-
-## License
-
-Since each user download a partial database, we use the CC0 Licensed database [geo-whois-asn-country](https://github.com/sapics/ip-location-db/tree/main/geo-whois-asn-country) for ip to country mapping to avoid license problem.
-
-To get extra information about country, we use [Countries](https://github.com/annexare/Countries) which is published under MIT license by [Annexare Studio](https://annexare.com/).
-
-The software itself is published under MIT license by [sapics](https://github.com/sapics).
\ No newline at end of file
diff --git a/browser/country-extra/iplookup.cjs b/browser/country-extra/iplookup.cjs
deleted file mode 100644
index cba7a94..0000000
--- a/browser/country-extra/iplookup.cjs
+++ /dev/null
@@ -1,187 +0,0 @@
-'use strict';
-
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 2 ;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var ip_lookup = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0];
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- }
- }
- return null
-};
-
-/*! countries-list v3.1.1 by Annexare | MIT */
-var a={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"\u062F\u0648\u0644\u0629 \u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062A \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0645\u062A\u062D\u062F\u0629",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"\u0627\u0641\u063A\u0627\u0646\u0633\u062A\u0627\u0646",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqip\xEBria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"\u0540\u0561\u0575\u0561\u057D\u057F\u0561\u0576",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"\xD6sterreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"\xC5land",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Az\u0259rbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"Belgi\xEB",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"\u0411\u044A\u043B\u0433\u0430\u0440\u0438\u044F",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"\u200F\u0627\u0644\u0628\u062D\u0631\u064A\u0646",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"B\xE9nin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barth\xE9lemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Bras\xEDlia",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"\u02BCbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvet\xF8ya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"\u0411\u0435\u043B\u0430\u0440\u0443\u0301\u0441\u044C",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"R\xE9publique d\xE9mocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"K\xF6d\xF6r\xF6s\xEAse t\xEE B\xEAafr\xEEka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"R\xE9publique du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"C\xF4te d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaound\xE9",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"\u4E2D\u56FD",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogot\xE1",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San Jos\xE9",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Cura\xE7ao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"\u039A\u03CD\u03C0\u03C1\u03BF\u03C2",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"\u010Cesk\xE1 republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"Rep\xFAblica Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"\u0645\u0635\u0631\u200E",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"\u0627\u0644\u0635\u062D\u0631\u0627\u0621 \u0627\u0644\u063A\u0631\u0628\u064A\u0629",phone:[212],continent:"AF",capital:"El Aai\xFAn",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"\u12A4\u122D\u1275\u122B",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"Espa\xF1a",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"\u12A2\u1275\u12EE\u1335\u12EB",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"F\xF8royar",phone:[298],continent:"EU",capital:"T\xF3rshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"\u10E1\u10D0\u10E5\u10D0\u10E0\u10D7\u10D5\u10D4\u10DA\u10DD",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane fran\xE7aise",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guin\xE9e",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"\u0395\u03BB\u03BB\u03AC\u03B4\u03B1",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hag\xE5t\xF1a",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guin\xE9-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"\u9999\u6E2F",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Ha\xEFti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarorsz\xE1g",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"\xC9ire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"\u05D9\u05B4\u05E9\u05B0\u05C2\u05E8\u05B8\u05D0\u05B5\u05DC",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"\u092D\u093E\u0930\u0924",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"\u0627\u0644\u0639\u0631\u0627\u0642",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"\u0627\u06CC\u0631\u0627\u0646",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"\xCDsland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"\u0627\u0644\u0623\u0631\u062F\u0646",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"\u65E5\u672C",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"\u041A\u044B\u0440\u0433\u044B\u0437\u0441\u0442\u0430\u043D",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"K\xE2mp\u016Dch\xE9a",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"\uBD81\uD55C",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"\uB300\uD55C\uBBFC\uAD6D",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"\u0627\u0644\u0643\u0648\u064A\u062A",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"\u049A\u0430\u0437\u0430\u049B\u0441\u0442\u0430\u043D",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"\u0EAA\u0E9B\u0E9B\u0EA5\u0EB2\u0EA7",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"\u0644\u0628\u0646\u0627\u0646",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"\u015Br\u012B la\u1E43k\u0101va",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"\u200F\u0644\u064A\u0628\u064A\u0627",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"\u0627\u0644\u0645\u063A\u0631\u0628",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chi\u0219in\u0103u",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"\u0426\u0440\u043D\u0430 \u0413\u043E\u0440\u0430",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M\u0327aje\u013C",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"\u0421\u0435\u0432\u0435\u0440\u043D\u0430 \u041C\u0430\u043A\u0435\u0434\u043E\u043D\u0438\u0458\u0430",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"\u1019\u103C\u1014\u103A\u1019\u102C",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"\u041C\u043E\u043D\u0433\u043E\u043B \u0443\u043B\u0441",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"\u6FB3\u9580",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"\u0645\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u0627",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Mal\xE9",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"M\xE9xico",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Mo\xE7ambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Cal\xE9donie",phone:[687],continent:"OC",capital:"Noum\xE9a",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"\u0928\u0947\u092A\u093E\u0932",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niu\u0113",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"\u0639\u0645\u0627\u0646",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panam\xE1",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Per\xFA",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polyn\xE9sie fran\xE7aise",phone:[689],continent:"OC",capital:"Papeet\u0113",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"\u0641\u0644\u0633\u0637\u064A\u0646",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunci\xF3n",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"\u0642\u0637\u0631",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La R\xE9union",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"Rom\xE2nia",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"\u0421\u0440\u0431\u0438\u0458\u0430",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"\u0420\u043E\u0441\u0441\u0438\u044F",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"\u0627\u0644\u0633\u0648\u062F\u0627\u0646",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"S\xE9n\xE9gal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"S\xE3o Tom\xE9 e Pr\xEDncipe",phone:[239],continent:"AF",capital:"S\xE3o Tom\xE9",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"\u0633\u0648\u0631\u064A\u0627",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Fran\xE7ais",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lom\xE9",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28\u0E44\u0E17\u0E22",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"\u0422\u043E\u04B7\u0438\u043A\u0438\u0441\u0442\u043E\u043D",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"T\xFCrkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"\u062A\u0648\u0646\u0633",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"T\xFCrkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"\u81FA\u7063",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"\u0423\u043A\u0440\u0430\u0457\u043D\u0430",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Vi\u1EC7t Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosov\xEBs",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"\u0627\u0644\u064A\u064E\u0645\u064E\u0646",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}};var r={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};var c=n=>({...a[n],iso2:n,iso3:r[n]}),t=()=>Object.keys(a).map(n=>c(n));t();
-
-var browserExtra = async(ip) => {
- const geodata = await ip_lookup(ip);
- if(geodata && a[geodata.country]){
- const h = a[geodata.country];
- geodata.country_name = h.name;
- geodata.country_native = h.native;
- geodata.continent = h.continent;
- geodata.capital = h.capital;
- geodata.phone = h.phone;
- geodata.currency = h.currency;
- geodata.languages = h.languages;
- }
- return geodata
-};
-
-module.exports = browserExtra;
diff --git a/browser/country-extra/iplookup.js b/browser/country-extra/iplookup.js
deleted file mode 100644
index 150ae28..0000000
--- a/browser/country-extra/iplookup.js
+++ /dev/null
@@ -1,168 +0,0 @@
-var IpLookup = (function () {
- 'use strict';
-
- const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
- };
-
- const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
- };
-
- const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
- };
- const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
- };
-
- const TOP_URL = document.currentScript.src.split('/').slice(0, -1).join('/') + '/';
- const MAIN_RECORD_SIZE = 2 ;
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
- const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
- };
-
- const downloadIdx = downloadArrayBuffer;
-
- const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
- const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[6] = new BigUint64Array(buf)
- })
- };
-
- var ip_lookup = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
- // console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
- // console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0];
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- }
- }
- return null
- };
-
- /*! countries-list v3.1.1 by Annexare | MIT */
- var a={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"\u062F\u0648\u0644\u0629 \u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062A \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0645\u062A\u062D\u062F\u0629",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"\u0627\u0641\u063A\u0627\u0646\u0633\u062A\u0627\u0646",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqip\xEBria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"\u0540\u0561\u0575\u0561\u057D\u057F\u0561\u0576",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"\xD6sterreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"\xC5land",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Az\u0259rbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"Belgi\xEB",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"\u0411\u044A\u043B\u0433\u0430\u0440\u0438\u044F",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"\u200F\u0627\u0644\u0628\u062D\u0631\u064A\u0646",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"B\xE9nin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barth\xE9lemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Bras\xEDlia",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"\u02BCbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvet\xF8ya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"\u0411\u0435\u043B\u0430\u0440\u0443\u0301\u0441\u044C",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"R\xE9publique d\xE9mocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"K\xF6d\xF6r\xF6s\xEAse t\xEE B\xEAafr\xEEka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"R\xE9publique du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"C\xF4te d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaound\xE9",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"\u4E2D\u56FD",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogot\xE1",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San Jos\xE9",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Cura\xE7ao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"\u039A\u03CD\u03C0\u03C1\u03BF\u03C2",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"\u010Cesk\xE1 republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"Rep\xFAblica Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"\u0645\u0635\u0631\u200E",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"\u0627\u0644\u0635\u062D\u0631\u0627\u0621 \u0627\u0644\u063A\u0631\u0628\u064A\u0629",phone:[212],continent:"AF",capital:"El Aai\xFAn",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"\u12A4\u122D\u1275\u122B",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"Espa\xF1a",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"\u12A2\u1275\u12EE\u1335\u12EB",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"F\xF8royar",phone:[298],continent:"EU",capital:"T\xF3rshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"\u10E1\u10D0\u10E5\u10D0\u10E0\u10D7\u10D5\u10D4\u10DA\u10DD",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane fran\xE7aise",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guin\xE9e",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"\u0395\u03BB\u03BB\u03AC\u03B4\u03B1",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hag\xE5t\xF1a",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guin\xE9-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"\u9999\u6E2F",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Ha\xEFti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarorsz\xE1g",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"\xC9ire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"\u05D9\u05B4\u05E9\u05B0\u05C2\u05E8\u05B8\u05D0\u05B5\u05DC",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"\u092D\u093E\u0930\u0924",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"\u0627\u0644\u0639\u0631\u0627\u0642",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"\u0627\u06CC\u0631\u0627\u0646",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"\xCDsland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"\u0627\u0644\u0623\u0631\u062F\u0646",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"\u65E5\u672C",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"\u041A\u044B\u0440\u0433\u044B\u0437\u0441\u0442\u0430\u043D",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"K\xE2mp\u016Dch\xE9a",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"\uBD81\uD55C",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"\uB300\uD55C\uBBFC\uAD6D",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"\u0627\u0644\u0643\u0648\u064A\u062A",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"\u049A\u0430\u0437\u0430\u049B\u0441\u0442\u0430\u043D",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"\u0EAA\u0E9B\u0E9B\u0EA5\u0EB2\u0EA7",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"\u0644\u0628\u0646\u0627\u0646",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"\u015Br\u012B la\u1E43k\u0101va",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"\u200F\u0644\u064A\u0628\u064A\u0627",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"\u0627\u0644\u0645\u063A\u0631\u0628",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chi\u0219in\u0103u",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"\u0426\u0440\u043D\u0430 \u0413\u043E\u0440\u0430",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M\u0327aje\u013C",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"\u0421\u0435\u0432\u0435\u0440\u043D\u0430 \u041C\u0430\u043A\u0435\u0434\u043E\u043D\u0438\u0458\u0430",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"\u1019\u103C\u1014\u103A\u1019\u102C",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"\u041C\u043E\u043D\u0433\u043E\u043B \u0443\u043B\u0441",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"\u6FB3\u9580",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"\u0645\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u0627",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Mal\xE9",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"M\xE9xico",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Mo\xE7ambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Cal\xE9donie",phone:[687],continent:"OC",capital:"Noum\xE9a",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"\u0928\u0947\u092A\u093E\u0932",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niu\u0113",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"\u0639\u0645\u0627\u0646",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panam\xE1",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Per\xFA",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polyn\xE9sie fran\xE7aise",phone:[689],continent:"OC",capital:"Papeet\u0113",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"\u0641\u0644\u0633\u0637\u064A\u0646",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunci\xF3n",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"\u0642\u0637\u0631",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La R\xE9union",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"Rom\xE2nia",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"\u0421\u0440\u0431\u0438\u0458\u0430",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"\u0420\u043E\u0441\u0441\u0438\u044F",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"\u0627\u0644\u0633\u0648\u062F\u0627\u0646",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"S\xE9n\xE9gal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"S\xE3o Tom\xE9 e Pr\xEDncipe",phone:[239],continent:"AF",capital:"S\xE3o Tom\xE9",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"\u0633\u0648\u0631\u064A\u0627",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Fran\xE7ais",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lom\xE9",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28\u0E44\u0E17\u0E22",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"\u0422\u043E\u04B7\u0438\u043A\u0438\u0441\u0442\u043E\u043D",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"T\xFCrkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"\u062A\u0648\u0646\u0633",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"T\xFCrkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"\u81FA\u7063",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"\u0423\u043A\u0440\u0430\u0457\u043D\u0430",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Vi\u1EC7t Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosov\xEBs",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"\u0627\u0644\u064A\u064E\u0645\u064E\u0646",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}};var r={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};var c=n=>({...a[n],iso2:n,iso3:r[n]}),t=()=>Object.keys(a).map(n=>c(n));t();
-
- var browserExtra = async(ip) => {
- const geodata = await ip_lookup(ip);
- if(geodata && a[geodata.country]){
- const h = a[geodata.country];
- geodata.country_name = h.name;
- geodata.country_native = h.native;
- geodata.continent = h.continent;
- geodata.capital = h.capital;
- geodata.phone = h.phone;
- geodata.currency = h.currency;
- geodata.languages = h.languages;
- }
- return geodata
- };
-
- return browserExtra;
-
-})();
diff --git a/browser/country-extra/iplookup.min.js b/browser/country-extra/iplookup.min.js
deleted file mode 100644
index eaa2860..0000000
--- a/browser/country-extra/iplookup.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var IpLookup=function(){"use strict";const n=n=>((n=n.split(/\./))[0]<<24|n[1]<<16|n[2]<<8|n[3])>>>0,a=document.currentScript.src.split("/").slice(0,-1).join("/")+"/",e=async(n,a=3)=>fetch(n,{cache:"no-cache"}).then((async t=>{return t.ok?t.arrayBuffer():404===t.status?null:a?(await(i=100*(4-a)*(4-a),new Promise((n=>setTimeout(n,i)))),e(n,a-1)):null;var i})),t=e,i={},c={4:a,6:a},r={4:t(a+"4.idx").then((n=>{if(n)return i[4]=new Uint32Array(n)})),6:t(a+"4.idx").then((n=>{if(n)return i[6]=new BigUint64Array(n)}))};var o=async a=>{var t,o,u=!0;a.includes(":")?(t=(a=>{if(a.includes("."))return n(a.split(":").pop());var e,t=0n;if((a=a.split(/:/)).length-1<7){const n=a.indexOf("");if(n<4){const t=8-a.length,i=n+t;for(e=7;e>=n;e--)a[e]=e>i?a[e-t]:0}}for(e=0;e<4;e++)a[e]&&(t+=BigInt(parseInt(a[e],16))<=l[0]))return null;for(var g,s=0,p=l.length-1;;)if(t>1]){if(p-s<2)return null;p=g-1}else{if(s===g){p>g&&t>=l[p]&&(g=p);break}s=g}const m=((n,a)=>n.length>a?n:"_".repeat(a-n.length)+n)(g.toString(36),2);const A=await e(c[o]+o+"/"+m);if(!A)return null;const h=2*(o-2),y=2+2*h,S=A.byteLength/y,v=u?new Uint32Array(A.slice(0,4*S)):new BigUint64Array(A.slice(0,8*S));for(s=0,p=S-1;;)if(t>1]){if(p-s<2)return null;p=g-1}else{if(s===g){p>g&&t>=v[p]&&(g=p);break}s=g}const M=u?new Uint32Array(A.slice((S+g)*h,(S+g+1)*h))[0]:new BigUint64Array(A.slice((S+g)*h,(S+g+1)*h))[0];if(t>=v[g]&&t<=M){const n=new Uint16Array(A.slice(S*h*2+2*g,S*h*2+2*(g+1)))[0];return{country:String.fromCharCode(255&n,n>>8)}}return null},u={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"دولة الإمارات العربية المتحدة",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"افغانستان",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqipëria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"Հայաստան",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"Österreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"Åland",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Azərbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"België",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"България",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"البحرين",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"Bénin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barthélemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Brasília",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"ʼbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvetøya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"Белару́сь",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"République démocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"Ködörösêse tî Bêafrîka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"République du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"Côte d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaoundé",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"中国",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogotá",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San José",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Curaçao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"Κύπρος",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"Česká republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"República Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"الجزائر",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"مصر",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"الصحراء الغربية",phone:[212],continent:"AF",capital:"El Aaiún",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"ኤርትራ",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"España",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"ኢትዮጵያ",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"Føroyar",phone:[298],continent:"EU",capital:"Tórshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"საქართველო",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane française",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guinée",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"Ελλάδα",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hagåtña",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guiné-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"香港",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Haïti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarország",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"Éire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"יִשְׂרָאֵל",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"भारत",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"العراق",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"ایران",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"Ísland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"الأردن",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"日本",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"Кыргызстан",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"Kâmpŭchéa",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"북한",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"대한민국",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"الكويت",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"Қазақстан",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"ສປປລາວ",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"لبنان",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"śrī laṃkāva",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"ليبيا",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"المغرب",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chișinău",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"Црна Гора",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M̧ajeļ",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"Северна Македонија",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"မြန်မာ",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"Монгол улс",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"澳門",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"موريتانيا",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Malé",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"México",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Moçambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Calédonie",phone:[687],continent:"OC",capital:"Nouméa",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"नेपाल",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niuē",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"عمان",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panamá",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Perú",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polynésie française",phone:[689],continent:"OC",capital:"Papeetē",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"فلسطين",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunción",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"قطر",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La Réunion",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"România",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"Србија",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"Россия",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"العربية السعودية",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"السودان",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"Sénégal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"São Tomé e Príncipe",phone:[239],continent:"AF",capital:"São Tomé",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"سوريا",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Français",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lomé",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"ประเทศไทย",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"Тоҷикистон",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"Türkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"تونس",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"Türkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"臺灣",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"Україна",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Việt Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosovës",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"اليَمَن",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}},l={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};
-/*! countries-list v3.1.1 by Annexare | MIT */Object.keys(u).map((n=>(n=>({...u[n],iso2:n,iso3:l[n]}))(n)));return async n=>{const a=await o(n);if(a&&u[a.country]){const n=u[a.country];a.country_name=n.name,a.country_native=n.native,a.continent=n.continent,a.capital=n.capital,a.phone=n.phone,a.currency=n.currency,a.languages=n.languages}return a}}();
diff --git a/browser/country-extra/iplookup.mjs b/browser/country-extra/iplookup.mjs
deleted file mode 100644
index 6d33c40..0000000
--- a/browser/country-extra/iplookup.mjs
+++ /dev/null
@@ -1,185 +0,0 @@
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 2 ;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var ip_lookup = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0];
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- }
- }
- return null
-};
-
-/*! countries-list v3.1.1 by Annexare | MIT */
-var a={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"\u062F\u0648\u0644\u0629 \u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062A \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0645\u062A\u062D\u062F\u0629",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"\u0627\u0641\u063A\u0627\u0646\u0633\u062A\u0627\u0646",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqip\xEBria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"\u0540\u0561\u0575\u0561\u057D\u057F\u0561\u0576",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"\xD6sterreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"\xC5land",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Az\u0259rbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"Belgi\xEB",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"\u0411\u044A\u043B\u0433\u0430\u0440\u0438\u044F",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"\u200F\u0627\u0644\u0628\u062D\u0631\u064A\u0646",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"B\xE9nin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barth\xE9lemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Bras\xEDlia",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"\u02BCbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvet\xF8ya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"\u0411\u0435\u043B\u0430\u0440\u0443\u0301\u0441\u044C",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"R\xE9publique d\xE9mocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"K\xF6d\xF6r\xF6s\xEAse t\xEE B\xEAafr\xEEka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"R\xE9publique du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"C\xF4te d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaound\xE9",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"\u4E2D\u56FD",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogot\xE1",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San Jos\xE9",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Cura\xE7ao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"\u039A\u03CD\u03C0\u03C1\u03BF\u03C2",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"\u010Cesk\xE1 republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"Rep\xFAblica Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"\u0645\u0635\u0631\u200E",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"\u0627\u0644\u0635\u062D\u0631\u0627\u0621 \u0627\u0644\u063A\u0631\u0628\u064A\u0629",phone:[212],continent:"AF",capital:"El Aai\xFAn",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"\u12A4\u122D\u1275\u122B",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"Espa\xF1a",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"\u12A2\u1275\u12EE\u1335\u12EB",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"F\xF8royar",phone:[298],continent:"EU",capital:"T\xF3rshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"\u10E1\u10D0\u10E5\u10D0\u10E0\u10D7\u10D5\u10D4\u10DA\u10DD",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane fran\xE7aise",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guin\xE9e",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"\u0395\u03BB\u03BB\u03AC\u03B4\u03B1",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hag\xE5t\xF1a",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guin\xE9-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"\u9999\u6E2F",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Ha\xEFti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarorsz\xE1g",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"\xC9ire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"\u05D9\u05B4\u05E9\u05B0\u05C2\u05E8\u05B8\u05D0\u05B5\u05DC",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"\u092D\u093E\u0930\u0924",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"\u0627\u0644\u0639\u0631\u0627\u0642",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"\u0627\u06CC\u0631\u0627\u0646",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"\xCDsland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"\u0627\u0644\u0623\u0631\u062F\u0646",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"\u65E5\u672C",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"\u041A\u044B\u0440\u0433\u044B\u0437\u0441\u0442\u0430\u043D",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"K\xE2mp\u016Dch\xE9a",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"\uBD81\uD55C",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"\uB300\uD55C\uBBFC\uAD6D",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"\u0627\u0644\u0643\u0648\u064A\u062A",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"\u049A\u0430\u0437\u0430\u049B\u0441\u0442\u0430\u043D",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"\u0EAA\u0E9B\u0E9B\u0EA5\u0EB2\u0EA7",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"\u0644\u0628\u0646\u0627\u0646",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"\u015Br\u012B la\u1E43k\u0101va",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"\u200F\u0644\u064A\u0628\u064A\u0627",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"\u0627\u0644\u0645\u063A\u0631\u0628",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chi\u0219in\u0103u",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"\u0426\u0440\u043D\u0430 \u0413\u043E\u0440\u0430",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M\u0327aje\u013C",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"\u0421\u0435\u0432\u0435\u0440\u043D\u0430 \u041C\u0430\u043A\u0435\u0434\u043E\u043D\u0438\u0458\u0430",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"\u1019\u103C\u1014\u103A\u1019\u102C",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"\u041C\u043E\u043D\u0433\u043E\u043B \u0443\u043B\u0441",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"\u6FB3\u9580",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"\u0645\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u0627",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Mal\xE9",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"M\xE9xico",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Mo\xE7ambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Cal\xE9donie",phone:[687],continent:"OC",capital:"Noum\xE9a",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"\u0928\u0947\u092A\u093E\u0932",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niu\u0113",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"\u0639\u0645\u0627\u0646",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panam\xE1",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Per\xFA",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polyn\xE9sie fran\xE7aise",phone:[689],continent:"OC",capital:"Papeet\u0113",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"\u0641\u0644\u0633\u0637\u064A\u0646",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunci\xF3n",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"\u0642\u0637\u0631",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La R\xE9union",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"Rom\xE2nia",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"\u0421\u0440\u0431\u0438\u0458\u0430",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"\u0420\u043E\u0441\u0441\u0438\u044F",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"\u0627\u0644\u0633\u0648\u062F\u0627\u0646",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"S\xE9n\xE9gal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"S\xE3o Tom\xE9 e Pr\xEDncipe",phone:[239],continent:"AF",capital:"S\xE3o Tom\xE9",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"\u0633\u0648\u0631\u064A\u0627",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Fran\xE7ais",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lom\xE9",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28\u0E44\u0E17\u0E22",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"\u0422\u043E\u04B7\u0438\u043A\u0438\u0441\u0442\u043E\u043D",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"T\xFCrkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"\u062A\u0648\u0646\u0633",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"T\xFCrkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"\u81FA\u7063",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"\u0423\u043A\u0440\u0430\u0457\u043D\u0430",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Vi\u1EC7t Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosov\xEBs",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"\u0627\u0644\u064A\u064E\u0645\u064E\u0646",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}};var r={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};var c=n=>({...a[n],iso2:n,iso3:r[n]}),t=()=>Object.keys(a).map(n=>c(n));t();
-
-var browserExtra = async(ip) => {
- const geodata = await ip_lookup(ip);
- if(geodata && a[geodata.country]){
- const h = a[geodata.country];
- geodata.country_name = h.name;
- geodata.country_native = h.native;
- geodata.continent = h.continent;
- geodata.capital = h.capital;
- geodata.phone = h.phone;
- geodata.currency = h.currency;
- geodata.languages = h.languages;
- }
- return geodata
-};
-
-export { browserExtra as default };
diff --git a/browser/country-extra/package.json b/browser/country-extra/package.json
deleted file mode 100644
index 1368036..0000000
--- a/browser/country-extra/package.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "name": "@iplookup/country-extra",
- "version": "1.0.20241112",
- "description": "Browser api to lookup country from IP address",
- "keywords": [
- "location",
- "iplookup",
- "lookup",
- "geo",
- "geoip",
- "ip",
- "ipv4",
- "ipv6",
- "ip-location-db",
- "country"
- ],
- "author": "sapics",
- "homepage": "https://github.com/sapics/ip-location-api",
- "repository": {
- "type": "git",
- "url": "git://github.com/sapics/ip-location-api.git"
- },
- "publishConfig": {
- "access": "public"
- },
- "license": "MIT",
- "main": "iplookup.cjs",
- "exports": {
- "import": "./iplookup.mjs",
- "require": "./iplookup.cjs"
- }
-}
\ No newline at end of file
diff --git a/browser/country-offline.html b/browser/country-offline.html
deleted file mode 100644
index 6a7071c..0000000
--- a/browser/country-offline.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- TEST
-
-
-
- TEST
-
-
- EXPORT INFORMATION
-
-
-
-
diff --git a/browser/country-online.html b/browser/country-online.html
deleted file mode 100644
index 490be32..0000000
--- a/browser/country-online.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- TEST
-
-
-
- TEST
-
-
- EXPORT INFORMATION
-
-
-
-
diff --git a/browser/country/README.md b/browser/country/README.md
deleted file mode 100644
index ac8fc93..0000000
--- a/browser/country/README.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# @iplookup/country [](https://www.npmjs.com/package/@iplookup/country)
-
-
-This is an API created to make [ip-location-api](https://github.com/sapics/ip-location-api) available for browsers.
-The database itself is large at 7MB, so it is splitted into over 2000 pieces for fast downloading in a browser.
-
-
-## Synopsis
-
-```html
-
-
-```
-
-#### ESM
-
-```javascript
-import IpLookup from '@iplookup/country'
-await IpLookup("2402:b801:ea8b:23c0::")
-```
-
-#### CJS
-
-```javascript
-const IpLookup = require('@iplookup/country')
-await IpLookup("207.97.227.239")
-```
-
-If you need extra information about country, try to use [@iplookup/country-extra](https://github.com/sapics/ip-location-api/tree/main/browser/country-extra).
-
-
-## License
-
-Since each user download a partial database, we use the CC0 Licensed database [geo-whois-asn-country](https://github.com/sapics/ip-location-db/tree/main/geo-whois-asn-country) for ip to country mapping to avoid license problem.
-
-The software itself is published under MIT License by [sapics](https://github.com/sapics).
\ No newline at end of file
diff --git a/browser/country/iplookup.cjs b/browser/country/iplookup.cjs
deleted file mode 100644
index 168c89a..0000000
--- a/browser/country/iplookup.cjs
+++ /dev/null
@@ -1,169 +0,0 @@
-'use strict';
-
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 2 ;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var browser = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0];
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- }
- }
- return null
-};
-
-module.exports = browser;
diff --git a/browser/country/iplookup.js b/browser/country/iplookup.js
deleted file mode 100644
index 3abadec..0000000
--- a/browser/country/iplookup.js
+++ /dev/null
@@ -1,150 +0,0 @@
-var IpLookup = (function () {
- 'use strict';
-
- const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
- };
-
- const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
- };
-
- const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
- };
- const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
- };
-
- const TOP_URL = document.currentScript.src.split('/').slice(0, -1).join('/') + '/';
- const MAIN_RECORD_SIZE = 2 ;
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
- const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
- };
-
- const downloadIdx = downloadArrayBuffer;
-
- const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
- const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[6] = new BigUint64Array(buf)
- })
- };
-
- var browser = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
- // console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
- // console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0];
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- }
- }
- return null
- };
-
- return browser;
-
-})();
diff --git a/browser/country/iplookup.min.js b/browser/country/iplookup.min.js
deleted file mode 100644
index a88f5c9..0000000
--- a/browser/country/iplookup.min.js
+++ /dev/null
@@ -1 +0,0 @@
-var IpLookup=function(){"use strict";const n=n=>((n=n.split(/\./))[0]<<24|n[1]<<16|n[2]<<8|n[3])>>>0,r=document.currentScript.src.split("/").slice(0,-1).join("/")+"/",t=async(n,r=3)=>fetch(n,{cache:"no-cache"}).then((async e=>{return e.ok?e.arrayBuffer():404===e.status?null:r?(await(i=100*(4-r)*(4-r),new Promise((n=>setTimeout(n,i)))),t(n,r-1)):null;var i})),e=t,i={},l={4:r,6:r},s={4:e(r+"4.idx").then((n=>{if(n)return i[4]=new Uint32Array(n)})),6:e(r+"4.idx").then((n=>{if(n)return i[6]=new BigUint64Array(n)}))};return async r=>{var e,c,u=!0;r.includes(":")?6===(c=(e=(r=>{if(r.includes("."))return n(r.split(":").pop());var t,e=0n;if((r=r.split(/:/)).length-1<7){const n=r.indexOf("");if(n<4){const e=8-r.length,i=n+e;for(t=7;t>=n;t--)r[t]=t>i?r[t-e]:0}}for(t=0;t<4;t++)r[t]&&(e+=BigInt(parseInt(r[t],16))<=a[0]))return null;for(var o,f=0,g=a.length-1;;)if(e>1]){if(g-f<2)return null;g=o-1}else{if(f===o){g>o&&e>=a[g]&&(o=g);break}f=o}const h=((n,r)=>n.length>r?n:"_".repeat(r-n.length)+n)(o.toString(36),2);const y=await t(l[c]+c+"/"+h);if(!y)return null;const p=2*(c-2),w=2+2*p,d=y.byteLength/w,A=u?new Uint32Array(y.slice(0,4*d)):new BigUint64Array(y.slice(0,8*d));for(f=0,g=d-1;;)if(e>1]){if(g-f<2)return null;g=o-1}else{if(f===o){g>o&&e>=A[g]&&(o=g);break}f=o}const B=u?new Uint32Array(y.slice((d+o)*p,(d+o+1)*p))[0]:new BigUint64Array(y.slice((d+o)*p,(d+o+1)*p))[0];if(e>=A[o]&&e<=B){const n=new Uint16Array(y.slice(d*p*2+2*o,d*p*2+2*(o+1)))[0];return{country:String.fromCharCode(255&n,n>>8)}}return null}}();
diff --git a/browser/country/iplookup.mjs b/browser/country/iplookup.mjs
deleted file mode 100644
index f7be8e7..0000000
--- a/browser/country/iplookup.mjs
+++ /dev/null
@@ -1,167 +0,0 @@
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 2 ;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var browser = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0];
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- }
- }
- return null
-};
-
-export { browser as default };
diff --git a/browser/country/package.json b/browser/country/package.json
deleted file mode 100644
index 2049270..0000000
--- a/browser/country/package.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "name": "@iplookup/country",
- "version": "1.0.20241112",
- "description": "Browser api to lookup country from IP address",
- "keywords": [
- "location",
- "iplookup",
- "lookup",
- "geo",
- "geoip",
- "ip",
- "ipv4",
- "ipv6",
- "ip-location-db",
- "country"
- ],
- "author": "sapics",
- "homepage": "https://github.com/sapics/ip-location-api",
- "repository": {
- "type": "git",
- "url": "git://github.com/sapics/ip-location-api.git"
- },
- "publishConfig": {
- "access": "public"
- },
- "license": "MIT",
- "main": "iplookup.cjs",
- "exports": {
- "import": "./iplookup.mjs",
- "require": "./iplookup.cjs"
- }
-}
\ No newline at end of file
diff --git a/browser/geocode-extra/README.md b/browser/geocode-extra/README.md
deleted file mode 100644
index 38e17ee..0000000
--- a/browser/geocode-extra/README.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# @iplookup/geocode-extra [](https://www.npmjs.com/package/@iplookup/geocode-extra)
-
-This is an API created to make [ip-location-api](https://github.com/sapics/ip-location-api) available for browsers.
-The database itself is large at 132MB, so it is splitted into over 4000 pieces for fast downloading in a browser.
-
-
-## Synopsis
-
-```html
-
-
-```
-
-#### ESM
-
-```javascript
-import IpLookup from '@iplookup/geocode-extra'
-await IpLookup("2402:b801:ea8b:23c0::")
-```
-
-#### CJS
-
-```javascript
-const IpLookup = require('@iplookup/geocode-extra')
-await IpLookup("207.97.227.239")
-```
-
-If you do not need extra information about country, try to use [@iplookup/geocode](https://github.com/sapics/ip-location-api/tree/main/browser/geocode).
-
-
-## License
-
-The database for mapping ip to geocode is published under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) by [DB-IP](https://db-ip.com/db/download/ip-to-city-lite).
-
-To get extra information about country, we use [Countries](https://github.com/annexare/Countries) which is published under MIT license by [Annexare Studio](https://annexare.com/).
-
-The software itself is published under MIT license by [sapics](https://github.com/sapics).
\ No newline at end of file
diff --git a/browser/geocode-extra/iplookup.cjs b/browser/geocode-extra/iplookup.cjs
deleted file mode 100644
index ccaf614..0000000
--- a/browser/geocode-extra/iplookup.cjs
+++ /dev/null
@@ -1,192 +0,0 @@
-'use strict';
-
-const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
-};
-
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 8;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var ip_lookup = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE));
- const ccCode = numToCountryCode(arr[0] & 1023);
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
-};
-
-/*! countries-list v3.1.1 by Annexare | MIT */
-var a={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"\u062F\u0648\u0644\u0629 \u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062A \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0645\u062A\u062D\u062F\u0629",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"\u0627\u0641\u063A\u0627\u0646\u0633\u062A\u0627\u0646",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqip\xEBria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"\u0540\u0561\u0575\u0561\u057D\u057F\u0561\u0576",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"\xD6sterreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"\xC5land",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Az\u0259rbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"Belgi\xEB",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"\u0411\u044A\u043B\u0433\u0430\u0440\u0438\u044F",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"\u200F\u0627\u0644\u0628\u062D\u0631\u064A\u0646",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"B\xE9nin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barth\xE9lemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Bras\xEDlia",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"\u02BCbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvet\xF8ya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"\u0411\u0435\u043B\u0430\u0440\u0443\u0301\u0441\u044C",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"R\xE9publique d\xE9mocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"K\xF6d\xF6r\xF6s\xEAse t\xEE B\xEAafr\xEEka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"R\xE9publique du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"C\xF4te d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaound\xE9",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"\u4E2D\u56FD",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogot\xE1",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San Jos\xE9",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Cura\xE7ao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"\u039A\u03CD\u03C0\u03C1\u03BF\u03C2",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"\u010Cesk\xE1 republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"Rep\xFAblica Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"\u0645\u0635\u0631\u200E",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"\u0627\u0644\u0635\u062D\u0631\u0627\u0621 \u0627\u0644\u063A\u0631\u0628\u064A\u0629",phone:[212],continent:"AF",capital:"El Aai\xFAn",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"\u12A4\u122D\u1275\u122B",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"Espa\xF1a",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"\u12A2\u1275\u12EE\u1335\u12EB",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"F\xF8royar",phone:[298],continent:"EU",capital:"T\xF3rshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"\u10E1\u10D0\u10E5\u10D0\u10E0\u10D7\u10D5\u10D4\u10DA\u10DD",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane fran\xE7aise",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guin\xE9e",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"\u0395\u03BB\u03BB\u03AC\u03B4\u03B1",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hag\xE5t\xF1a",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guin\xE9-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"\u9999\u6E2F",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Ha\xEFti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarorsz\xE1g",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"\xC9ire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"\u05D9\u05B4\u05E9\u05B0\u05C2\u05E8\u05B8\u05D0\u05B5\u05DC",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"\u092D\u093E\u0930\u0924",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"\u0627\u0644\u0639\u0631\u0627\u0642",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"\u0627\u06CC\u0631\u0627\u0646",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"\xCDsland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"\u0627\u0644\u0623\u0631\u062F\u0646",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"\u65E5\u672C",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"\u041A\u044B\u0440\u0433\u044B\u0437\u0441\u0442\u0430\u043D",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"K\xE2mp\u016Dch\xE9a",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"\uBD81\uD55C",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"\uB300\uD55C\uBBFC\uAD6D",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"\u0627\u0644\u0643\u0648\u064A\u062A",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"\u049A\u0430\u0437\u0430\u049B\u0441\u0442\u0430\u043D",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"\u0EAA\u0E9B\u0E9B\u0EA5\u0EB2\u0EA7",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"\u0644\u0628\u0646\u0627\u0646",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"\u015Br\u012B la\u1E43k\u0101va",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"\u200F\u0644\u064A\u0628\u064A\u0627",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"\u0627\u0644\u0645\u063A\u0631\u0628",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chi\u0219in\u0103u",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"\u0426\u0440\u043D\u0430 \u0413\u043E\u0440\u0430",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M\u0327aje\u013C",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"\u0421\u0435\u0432\u0435\u0440\u043D\u0430 \u041C\u0430\u043A\u0435\u0434\u043E\u043D\u0438\u0458\u0430",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"\u1019\u103C\u1014\u103A\u1019\u102C",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"\u041C\u043E\u043D\u0433\u043E\u043B \u0443\u043B\u0441",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"\u6FB3\u9580",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"\u0645\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u0627",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Mal\xE9",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"M\xE9xico",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Mo\xE7ambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Cal\xE9donie",phone:[687],continent:"OC",capital:"Noum\xE9a",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"\u0928\u0947\u092A\u093E\u0932",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niu\u0113",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"\u0639\u0645\u0627\u0646",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panam\xE1",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Per\xFA",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polyn\xE9sie fran\xE7aise",phone:[689],continent:"OC",capital:"Papeet\u0113",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"\u0641\u0644\u0633\u0637\u064A\u0646",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunci\xF3n",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"\u0642\u0637\u0631",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La R\xE9union",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"Rom\xE2nia",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"\u0421\u0440\u0431\u0438\u0458\u0430",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"\u0420\u043E\u0441\u0441\u0438\u044F",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"\u0627\u0644\u0633\u0648\u062F\u0627\u0646",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"S\xE9n\xE9gal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"S\xE3o Tom\xE9 e Pr\xEDncipe",phone:[239],continent:"AF",capital:"S\xE3o Tom\xE9",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"\u0633\u0648\u0631\u064A\u0627",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Fran\xE7ais",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lom\xE9",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28\u0E44\u0E17\u0E22",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"\u0422\u043E\u04B7\u0438\u043A\u0438\u0441\u0442\u043E\u043D",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"T\xFCrkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"\u062A\u0648\u0646\u0633",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"T\xFCrkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"\u81FA\u7063",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"\u0423\u043A\u0440\u0430\u0457\u043D\u0430",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Vi\u1EC7t Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosov\xEBs",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"\u0627\u0644\u064A\u064E\u0645\u064E\u0646",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}};var r={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};var c=n=>({...a[n],iso2:n,iso3:r[n]}),t=()=>Object.keys(a).map(n=>c(n));t();
-
-var browserExtra = async(ip) => {
- const geodata = await ip_lookup(ip);
- if(geodata && a[geodata.country]){
- const h = a[geodata.country];
- geodata.country_name = h.name;
- geodata.country_native = h.native;
- geodata.continent = h.continent;
- geodata.capital = h.capital;
- geodata.phone = h.phone;
- geodata.currency = h.currency;
- geodata.languages = h.languages;
- }
- return geodata
-};
-
-module.exports = browserExtra;
diff --git a/browser/geocode-extra/iplookup.js b/browser/geocode-extra/iplookup.js
deleted file mode 100644
index 7388b41..0000000
--- a/browser/geocode-extra/iplookup.js
+++ /dev/null
@@ -1,173 +0,0 @@
-var IpLookup = (function () {
- 'use strict';
-
- const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
- };
-
- const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
- };
-
- const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
- };
-
- const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
- };
- const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
- };
-
- const TOP_URL = document.currentScript.src.split('/').slice(0, -1).join('/') + '/';
- const MAIN_RECORD_SIZE = 8;
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
- const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
- };
-
- const downloadIdx = downloadArrayBuffer;
-
- const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
- const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[6] = new BigUint64Array(buf)
- })
- };
-
- var ip_lookup = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
- // console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
- // console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE));
- const ccCode = numToCountryCode(arr[0] & 1023);
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
- };
-
- /*! countries-list v3.1.1 by Annexare | MIT */
- var a={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"\u062F\u0648\u0644\u0629 \u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062A \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0645\u062A\u062D\u062F\u0629",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"\u0627\u0641\u063A\u0627\u0646\u0633\u062A\u0627\u0646",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqip\xEBria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"\u0540\u0561\u0575\u0561\u057D\u057F\u0561\u0576",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"\xD6sterreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"\xC5land",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Az\u0259rbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"Belgi\xEB",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"\u0411\u044A\u043B\u0433\u0430\u0440\u0438\u044F",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"\u200F\u0627\u0644\u0628\u062D\u0631\u064A\u0646",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"B\xE9nin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barth\xE9lemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Bras\xEDlia",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"\u02BCbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvet\xF8ya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"\u0411\u0435\u043B\u0430\u0440\u0443\u0301\u0441\u044C",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"R\xE9publique d\xE9mocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"K\xF6d\xF6r\xF6s\xEAse t\xEE B\xEAafr\xEEka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"R\xE9publique du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"C\xF4te d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaound\xE9",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"\u4E2D\u56FD",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogot\xE1",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San Jos\xE9",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Cura\xE7ao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"\u039A\u03CD\u03C0\u03C1\u03BF\u03C2",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"\u010Cesk\xE1 republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"Rep\xFAblica Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"\u0645\u0635\u0631\u200E",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"\u0627\u0644\u0635\u062D\u0631\u0627\u0621 \u0627\u0644\u063A\u0631\u0628\u064A\u0629",phone:[212],continent:"AF",capital:"El Aai\xFAn",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"\u12A4\u122D\u1275\u122B",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"Espa\xF1a",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"\u12A2\u1275\u12EE\u1335\u12EB",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"F\xF8royar",phone:[298],continent:"EU",capital:"T\xF3rshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"\u10E1\u10D0\u10E5\u10D0\u10E0\u10D7\u10D5\u10D4\u10DA\u10DD",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane fran\xE7aise",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guin\xE9e",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"\u0395\u03BB\u03BB\u03AC\u03B4\u03B1",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hag\xE5t\xF1a",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guin\xE9-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"\u9999\u6E2F",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Ha\xEFti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarorsz\xE1g",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"\xC9ire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"\u05D9\u05B4\u05E9\u05B0\u05C2\u05E8\u05B8\u05D0\u05B5\u05DC",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"\u092D\u093E\u0930\u0924",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"\u0627\u0644\u0639\u0631\u0627\u0642",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"\u0627\u06CC\u0631\u0627\u0646",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"\xCDsland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"\u0627\u0644\u0623\u0631\u062F\u0646",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"\u65E5\u672C",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"\u041A\u044B\u0440\u0433\u044B\u0437\u0441\u0442\u0430\u043D",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"K\xE2mp\u016Dch\xE9a",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"\uBD81\uD55C",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"\uB300\uD55C\uBBFC\uAD6D",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"\u0627\u0644\u0643\u0648\u064A\u062A",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"\u049A\u0430\u0437\u0430\u049B\u0441\u0442\u0430\u043D",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"\u0EAA\u0E9B\u0E9B\u0EA5\u0EB2\u0EA7",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"\u0644\u0628\u0646\u0627\u0646",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"\u015Br\u012B la\u1E43k\u0101va",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"\u200F\u0644\u064A\u0628\u064A\u0627",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"\u0627\u0644\u0645\u063A\u0631\u0628",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chi\u0219in\u0103u",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"\u0426\u0440\u043D\u0430 \u0413\u043E\u0440\u0430",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M\u0327aje\u013C",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"\u0421\u0435\u0432\u0435\u0440\u043D\u0430 \u041C\u0430\u043A\u0435\u0434\u043E\u043D\u0438\u0458\u0430",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"\u1019\u103C\u1014\u103A\u1019\u102C",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"\u041C\u043E\u043D\u0433\u043E\u043B \u0443\u043B\u0441",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"\u6FB3\u9580",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"\u0645\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u0627",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Mal\xE9",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"M\xE9xico",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Mo\xE7ambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Cal\xE9donie",phone:[687],continent:"OC",capital:"Noum\xE9a",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"\u0928\u0947\u092A\u093E\u0932",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niu\u0113",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"\u0639\u0645\u0627\u0646",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panam\xE1",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Per\xFA",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polyn\xE9sie fran\xE7aise",phone:[689],continent:"OC",capital:"Papeet\u0113",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"\u0641\u0644\u0633\u0637\u064A\u0646",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunci\xF3n",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"\u0642\u0637\u0631",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La R\xE9union",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"Rom\xE2nia",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"\u0421\u0440\u0431\u0438\u0458\u0430",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"\u0420\u043E\u0441\u0441\u0438\u044F",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"\u0627\u0644\u0633\u0648\u062F\u0627\u0646",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"S\xE9n\xE9gal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"S\xE3o Tom\xE9 e Pr\xEDncipe",phone:[239],continent:"AF",capital:"S\xE3o Tom\xE9",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"\u0633\u0648\u0631\u064A\u0627",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Fran\xE7ais",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lom\xE9",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28\u0E44\u0E17\u0E22",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"\u0422\u043E\u04B7\u0438\u043A\u0438\u0441\u0442\u043E\u043D",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"T\xFCrkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"\u062A\u0648\u0646\u0633",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"T\xFCrkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"\u81FA\u7063",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"\u0423\u043A\u0440\u0430\u0457\u043D\u0430",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Vi\u1EC7t Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosov\xEBs",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"\u0627\u0644\u064A\u064E\u0645\u064E\u0646",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}};var r={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};var c=n=>({...a[n],iso2:n,iso3:r[n]}),t=()=>Object.keys(a).map(n=>c(n));t();
-
- var browserExtra = async(ip) => {
- const geodata = await ip_lookup(ip);
- if(geodata && a[geodata.country]){
- const h = a[geodata.country];
- geodata.country_name = h.name;
- geodata.country_native = h.native;
- geodata.continent = h.continent;
- geodata.capital = h.capital;
- geodata.phone = h.phone;
- geodata.currency = h.currency;
- geodata.languages = h.languages;
- }
- return geodata
- };
-
- return browserExtra;
-
-})();
diff --git a/browser/geocode-extra/iplookup.min.js b/browser/geocode-extra/iplookup.min.js
deleted file mode 100644
index e1512be..0000000
--- a/browser/geocode-extra/iplookup.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var IpLookup=function(){"use strict";const n=n=>((n=n.split(/\./))[0]<<24|n[1]<<16|n[2]<<8|n[3])>>>0,a=document.currentScript.src.split("/").slice(0,-1).join("/")+"/",e=async(n,a=3)=>fetch(n,{cache:"no-cache"}).then((async t=>{return t.ok?t.arrayBuffer():404===t.status?null:a?(await(i=100*(4-a)*(4-a),new Promise((n=>setTimeout(n,i)))),e(n,a-1)):null;var i})),t=e,i={},c={4:a,6:a},r={4:t(a+"4.idx").then((n=>{if(n)return i[4]=new Uint32Array(n)})),6:t(a+"4.idx").then((n=>{if(n)return i[6]=new BigUint64Array(n)}))};var o=async a=>{var t,o,u=!0;a.includes(":")?(t=(a=>{if(a.includes("."))return n(a.split(":").pop());var e,t=0n;if((a=a.split(/:/)).length-1<7){const n=a.indexOf("");if(n<4){const t=8-a.length,i=n+t;for(e=7;e>=n;e--)a[e]=e>i?a[e-t]:0}}for(e=0;e<4;e++)a[e]&&(t+=BigInt(parseInt(a[e],16))<=l[0]))return null;for(var g,s=0,p=l.length-1;;)if(t>1]){if(p-s<2)return null;p=g-1}else{if(s===g){p>g&&t>=l[p]&&(g=p);break}s=g}const m=((n,a)=>n.length>a?n:"_".repeat(a-n.length)+n)(g.toString(36),2);const A=await e(c[o]+o+"/"+m);if(!A)return null;const h=2*(o-2),y=8+2*h,S=A.byteLength/y,v=u?new Uint32Array(A.slice(0,4*S)):new BigUint64Array(A.slice(0,8*S));for(s=0,p=S-1;;)if(t>1]){if(p-s<2)return null;p=g-1}else{if(s===g){p>g&&t>=v[p]&&(g=p);break}s=g}const M=u?new Uint32Array(A.slice((S+g)*h,(S+g+1)*h))[0]:new BigUint64Array(A.slice((S+g)*h,(S+g+1)*h))[0];if(t>=v[g]&&t<=M){const n=new Int32Array(A.slice(S*h*2+8*g,S*h*2+8*(g+1))),a=(n=>String.fromCharCode(65+(n/26|0),n%26+65))(1023&n[0]);return{latitude:(n[0]>>10)/1e4,longitude:n[1]/1e4,country:a}}return null},u={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"دولة الإمارات العربية المتحدة",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"افغانستان",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqipëria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"Հայաստան",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"Österreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"Åland",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Azərbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"België",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"България",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"البحرين",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"Bénin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barthélemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Brasília",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"ʼbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvetøya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"Белару́сь",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"République démocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"Ködörösêse tî Bêafrîka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"République du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"Côte d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaoundé",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"中国",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogotá",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San José",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Curaçao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"Κύπρος",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"Česká republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"República Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"الجزائر",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"مصر",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"الصحراء الغربية",phone:[212],continent:"AF",capital:"El Aaiún",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"ኤርትራ",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"España",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"ኢትዮጵያ",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"Føroyar",phone:[298],continent:"EU",capital:"Tórshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"საქართველო",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane française",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guinée",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"Ελλάδα",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hagåtña",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guiné-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"香港",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Haïti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarország",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"Éire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"יִשְׂרָאֵל",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"भारत",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"العراق",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"ایران",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"Ísland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"الأردن",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"日本",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"Кыргызстан",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"Kâmpŭchéa",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"북한",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"대한민국",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"الكويت",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"Қазақстан",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"ສປປລາວ",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"لبنان",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"śrī laṃkāva",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"ليبيا",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"المغرب",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chișinău",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"Црна Гора",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M̧ajeļ",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"Северна Македонија",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"မြန်မာ",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"Монгол улс",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"澳門",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"موريتانيا",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Malé",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"México",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Moçambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Calédonie",phone:[687],continent:"OC",capital:"Nouméa",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"नेपाल",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niuē",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"عمان",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panamá",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Perú",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polynésie française",phone:[689],continent:"OC",capital:"Papeetē",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"فلسطين",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunción",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"قطر",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La Réunion",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"România",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"Србија",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"Россия",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"العربية السعودية",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"السودان",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"Sénégal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"São Tomé e Príncipe",phone:[239],continent:"AF",capital:"São Tomé",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"سوريا",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Français",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lomé",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"ประเทศไทย",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"Тоҷикистон",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"Türkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"تونس",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"Türkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"臺灣",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"Україна",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Việt Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosovës",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"اليَمَن",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}},l={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};
-/*! countries-list v3.1.1 by Annexare | MIT */Object.keys(u).map((n=>(n=>({...u[n],iso2:n,iso3:l[n]}))(n)));return async n=>{const a=await o(n);if(a&&u[a.country]){const n=u[a.country];a.country_name=n.name,a.country_native=n.native,a.continent=n.continent,a.capital=n.capital,a.phone=n.phone,a.currency=n.currency,a.languages=n.languages}return a}}();
diff --git a/browser/geocode-extra/iplookup.mjs b/browser/geocode-extra/iplookup.mjs
deleted file mode 100644
index 2d780a1..0000000
--- a/browser/geocode-extra/iplookup.mjs
+++ /dev/null
@@ -1,190 +0,0 @@
-const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
-};
-
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 8;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var ip_lookup = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE));
- const ccCode = numToCountryCode(arr[0] & 1023);
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
-};
-
-/*! countries-list v3.1.1 by Annexare | MIT */
-var a={AD:{name:"Andorra",native:"Andorra",phone:[376],continent:"EU",capital:"Andorra la Vella",currency:["EUR"],languages:["ca"]},AE:{name:"United Arab Emirates",native:"\u062F\u0648\u0644\u0629 \u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062A \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0645\u062A\u062D\u062F\u0629",phone:[971],continent:"AS",capital:"Abu Dhabi",currency:["AED"],languages:["ar"]},AF:{name:"Afghanistan",native:"\u0627\u0641\u063A\u0627\u0646\u0633\u062A\u0627\u0646",phone:[93],continent:"AS",capital:"Kabul",currency:["AFN"],languages:["ps","uz","tk"]},AG:{name:"Antigua and Barbuda",native:"Antigua and Barbuda",phone:[1268],continent:"NA",capital:"Saint John's",currency:["XCD"],languages:["en"]},AI:{name:"Anguilla",native:"Anguilla",phone:[1264],continent:"NA",capital:"The Valley",currency:["XCD"],languages:["en"]},AL:{name:"Albania",native:"Shqip\xEBria",phone:[355],continent:"EU",capital:"Tirana",currency:["ALL"],languages:["sq"]},AM:{name:"Armenia",native:"\u0540\u0561\u0575\u0561\u057D\u057F\u0561\u0576",phone:[374],continent:"AS",capital:"Yerevan",currency:["AMD"],languages:["hy","ru"]},AO:{name:"Angola",native:"Angola",phone:[244],continent:"AF",capital:"Luanda",currency:["AOA"],languages:["pt"]},AQ:{name:"Antarctica",native:"Antarctica",phone:[672],continent:"AN",capital:"",currency:[],languages:[]},AR:{name:"Argentina",native:"Argentina",phone:[54],continent:"SA",capital:"Buenos Aires",currency:["ARS"],languages:["es","gn"]},AS:{name:"American Samoa",native:"American Samoa",phone:[1684],continent:"OC",capital:"Pago Pago",currency:["USD"],languages:["en","sm"]},AT:{name:"Austria",native:"\xD6sterreich",phone:[43],continent:"EU",capital:"Vienna",currency:["EUR"],languages:["de"]},AU:{name:"Australia",native:"Australia",phone:[61],continent:"OC",capital:"Canberra",currency:["AUD"],languages:["en"]},AW:{name:"Aruba",native:"Aruba",phone:[297],continent:"NA",capital:"Oranjestad",currency:["AWG"],languages:["nl","pa"]},AX:{name:"Aland",native:"\xC5land",phone:[358],continent:"EU",capital:"Mariehamn",currency:["EUR"],languages:["sv"],partOf:"FI"},AZ:{name:"Azerbaijan",native:"Az\u0259rbaycan",phone:[994],continent:"AS",continents:["AS","EU"],capital:"Baku",currency:["AZN"],languages:["az"]},BA:{name:"Bosnia and Herzegovina",native:"Bosna i Hercegovina",phone:[387],continent:"EU",capital:"Sarajevo",currency:["BAM"],languages:["bs","hr","sr"]},BB:{name:"Barbados",native:"Barbados",phone:[1246],continent:"NA",capital:"Bridgetown",currency:["BBD"],languages:["en"]},BD:{name:"Bangladesh",native:"Bangladesh",phone:[880],continent:"AS",capital:"Dhaka",currency:["BDT"],languages:["bn"]},BE:{name:"Belgium",native:"Belgi\xEB",phone:[32],continent:"EU",capital:"Brussels",currency:["EUR"],languages:["nl","fr","de"]},BF:{name:"Burkina Faso",native:"Burkina Faso",phone:[226],continent:"AF",capital:"Ouagadougou",currency:["XOF"],languages:["fr","ff"]},BG:{name:"Bulgaria",native:"\u0411\u044A\u043B\u0433\u0430\u0440\u0438\u044F",phone:[359],continent:"EU",capital:"Sofia",currency:["BGN"],languages:["bg"]},BH:{name:"Bahrain",native:"\u200F\u0627\u0644\u0628\u062D\u0631\u064A\u0646",phone:[973],continent:"AS",capital:"Manama",currency:["BHD"],languages:["ar"]},BI:{name:"Burundi",native:"Burundi",phone:[257],continent:"AF",capital:"Bujumbura",currency:["BIF"],languages:["fr","rn"]},BJ:{name:"Benin",native:"B\xE9nin",phone:[229],continent:"AF",capital:"Porto-Novo",currency:["XOF"],languages:["fr"]},BL:{name:"Saint Barthelemy",native:"Saint-Barth\xE9lemy",phone:[590],continent:"NA",capital:"Gustavia",currency:["EUR"],languages:["fr"]},BM:{name:"Bermuda",native:"Bermuda",phone:[1441],continent:"NA",capital:"Hamilton",currency:["BMD"],languages:["en"]},BN:{name:"Brunei",native:"Negara Brunei Darussalam",phone:[673],continent:"AS",capital:"Bandar Seri Begawan",currency:["BND"],languages:["ms"]},BO:{name:"Bolivia",native:"Bolivia",phone:[591],continent:"SA",capital:"Sucre",currency:["BOB","BOV"],languages:["es","ay","qu"]},BQ:{name:"Bonaire",native:"Bonaire",phone:[5997],continent:"NA",capital:"Kralendijk",currency:["USD"],languages:["nl"]},BR:{name:"Brazil",native:"Brasil",phone:[55],continent:"SA",capital:"Bras\xEDlia",currency:["BRL"],languages:["pt"]},BS:{name:"Bahamas",native:"Bahamas",phone:[1242],continent:"NA",capital:"Nassau",currency:["BSD"],languages:["en"]},BT:{name:"Bhutan",native:"\u02BCbrug-yul",phone:[975],continent:"AS",capital:"Thimphu",currency:["BTN","INR"],languages:["dz"]},BV:{name:"Bouvet Island",native:"Bouvet\xF8ya",phone:[47],continent:"AN",capital:"",currency:["NOK"],languages:["no","nb","nn"]},BW:{name:"Botswana",native:"Botswana",phone:[267],continent:"AF",capital:"Gaborone",currency:["BWP"],languages:["en","tn"]},BY:{name:"Belarus",native:"\u0411\u0435\u043B\u0430\u0440\u0443\u0301\u0441\u044C",phone:[375],continent:"EU",capital:"Minsk",currency:["BYN"],languages:["be","ru"]},BZ:{name:"Belize",native:"Belize",phone:[501],continent:"NA",capital:"Belmopan",currency:["BZD"],languages:["en","es"]},CA:{name:"Canada",native:"Canada",phone:[1],continent:"NA",capital:"Ottawa",currency:["CAD"],languages:["en","fr"]},CC:{name:"Cocos (Keeling) Islands",native:"Cocos (Keeling) Islands",phone:[61],continent:"AS",capital:"West Island",currency:["AUD"],languages:["en"]},CD:{name:"Democratic Republic of the Congo",native:"R\xE9publique d\xE9mocratique du Congo",phone:[243],continent:"AF",capital:"Kinshasa",currency:["CDF"],languages:["fr","ln","kg","sw","lu"]},CF:{name:"Central African Republic",native:"K\xF6d\xF6r\xF6s\xEAse t\xEE B\xEAafr\xEEka",phone:[236],continent:"AF",capital:"Bangui",currency:["XAF"],languages:["fr","sg"]},CG:{name:"Republic of the Congo",native:"R\xE9publique du Congo",phone:[242],continent:"AF",capital:"Brazzaville",currency:["XAF"],languages:["fr","ln"]},CH:{name:"Switzerland",native:"Schweiz",phone:[41],continent:"EU",capital:"Bern",currency:["CHE","CHF","CHW"],languages:["de","fr","it"]},CI:{name:"Ivory Coast",native:"C\xF4te d'Ivoire",phone:[225],continent:"AF",capital:"Yamoussoukro",currency:["XOF"],languages:["fr"]},CK:{name:"Cook Islands",native:"Cook Islands",phone:[682],continent:"OC",capital:"Avarua",currency:["NZD"],languages:["en"]},CL:{name:"Chile",native:"Chile",phone:[56],continent:"SA",capital:"Santiago",currency:["CLF","CLP"],languages:["es"]},CM:{name:"Cameroon",native:"Cameroon",phone:[237],continent:"AF",capital:"Yaound\xE9",currency:["XAF"],languages:["en","fr"]},CN:{name:"China",native:"\u4E2D\u56FD",phone:[86],continent:"AS",capital:"Beijing",currency:["CNY"],languages:["zh"]},CO:{name:"Colombia",native:"Colombia",phone:[57],continent:"SA",capital:"Bogot\xE1",currency:["COP"],languages:["es"]},CR:{name:"Costa Rica",native:"Costa Rica",phone:[506],continent:"NA",capital:"San Jos\xE9",currency:["CRC"],languages:["es"]},CU:{name:"Cuba",native:"Cuba",phone:[53],continent:"NA",capital:"Havana",currency:["CUC","CUP"],languages:["es"]},CV:{name:"Cape Verde",native:"Cabo Verde",phone:[238],continent:"AF",capital:"Praia",currency:["CVE"],languages:["pt"]},CW:{name:"Curacao",native:"Cura\xE7ao",phone:[5999],continent:"NA",capital:"Willemstad",currency:["ANG"],languages:["nl","pa","en"]},CX:{name:"Christmas Island",native:"Christmas Island",phone:[61],continent:"AS",capital:"Flying Fish Cove",currency:["AUD"],languages:["en"]},CY:{name:"Cyprus",native:"\u039A\u03CD\u03C0\u03C1\u03BF\u03C2",phone:[357],continent:"EU",capital:"Nicosia",currency:["EUR"],languages:["el","tr","hy"]},CZ:{name:"Czech Republic",native:"\u010Cesk\xE1 republika",phone:[420],continent:"EU",capital:"Prague",currency:["CZK"],languages:["cs"]},DE:{name:"Germany",native:"Deutschland",phone:[49],continent:"EU",capital:"Berlin",currency:["EUR"],languages:["de"]},DJ:{name:"Djibouti",native:"Djibouti",phone:[253],continent:"AF",capital:"Djibouti",currency:["DJF"],languages:["fr","ar"]},DK:{name:"Denmark",native:"Danmark",phone:[45],continent:"EU",continents:["EU","NA"],capital:"Copenhagen",currency:["DKK"],languages:["da"]},DM:{name:"Dominica",native:"Dominica",phone:[1767],continent:"NA",capital:"Roseau",currency:["XCD"],languages:["en"]},DO:{name:"Dominican Republic",native:"Rep\xFAblica Dominicana",phone:[1809,1829,1849],continent:"NA",capital:"Santo Domingo",currency:["DOP"],languages:["es"]},DZ:{name:"Algeria",native:"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",phone:[213],continent:"AF",capital:"Algiers",currency:["DZD"],languages:["ar"]},EC:{name:"Ecuador",native:"Ecuador",phone:[593],continent:"SA",capital:"Quito",currency:["USD"],languages:["es"]},EE:{name:"Estonia",native:"Eesti",phone:[372],continent:"EU",capital:"Tallinn",currency:["EUR"],languages:["et"]},EG:{name:"Egypt",native:"\u0645\u0635\u0631\u200E",phone:[20],continent:"AF",continents:["AF","AS"],capital:"Cairo",currency:["EGP"],languages:["ar"]},EH:{name:"Western Sahara",native:"\u0627\u0644\u0635\u062D\u0631\u0627\u0621 \u0627\u0644\u063A\u0631\u0628\u064A\u0629",phone:[212],continent:"AF",capital:"El Aai\xFAn",currency:["MAD","DZD","MRU"],languages:["es"]},ER:{name:"Eritrea",native:"\u12A4\u122D\u1275\u122B",phone:[291],continent:"AF",capital:"Asmara",currency:["ERN"],languages:["ti","ar","en"]},ES:{name:"Spain",native:"Espa\xF1a",phone:[34],continent:"EU",capital:"Madrid",currency:["EUR"],languages:["es","eu","ca","gl","oc"]},ET:{name:"Ethiopia",native:"\u12A2\u1275\u12EE\u1335\u12EB",phone:[251],continent:"AF",capital:"Addis Ababa",currency:["ETB"],languages:["am"]},FI:{name:"Finland",native:"Suomi",phone:[358],continent:"EU",capital:"Helsinki",currency:["EUR"],languages:["fi","sv"]},FJ:{name:"Fiji",native:"Fiji",phone:[679],continent:"OC",capital:"Suva",currency:["FJD"],languages:["en","fj","hi","ur"]},FK:{name:"Falkland Islands",native:"Falkland Islands",phone:[500],continent:"SA",capital:"Stanley",currency:["FKP"],languages:["en"]},FM:{name:"Micronesia",native:"Micronesia",phone:[691],continent:"OC",capital:"Palikir",currency:["USD"],languages:["en"]},FO:{name:"Faroe Islands",native:"F\xF8royar",phone:[298],continent:"EU",capital:"T\xF3rshavn",currency:["DKK"],languages:["fo"]},FR:{name:"France",native:"France",phone:[33],continent:"EU",capital:"Paris",currency:["EUR"],languages:["fr"]},GA:{name:"Gabon",native:"Gabon",phone:[241],continent:"AF",capital:"Libreville",currency:["XAF"],languages:["fr"]},GB:{name:"United Kingdom",native:"United Kingdom",phone:[44],continent:"EU",capital:"London",currency:["GBP"],languages:["en"]},GD:{name:"Grenada",native:"Grenada",phone:[1473],continent:"NA",capital:"St. George's",currency:["XCD"],languages:["en"]},GE:{name:"Georgia",native:"\u10E1\u10D0\u10E5\u10D0\u10E0\u10D7\u10D5\u10D4\u10DA\u10DD",phone:[995],continent:"AS",continents:["AS","EU"],capital:"Tbilisi",currency:["GEL"],languages:["ka"]},GF:{name:"French Guiana",native:"Guyane fran\xE7aise",phone:[594],continent:"SA",capital:"Cayenne",currency:["EUR"],languages:["fr"]},GG:{name:"Guernsey",native:"Guernsey",phone:[44],continent:"EU",capital:"St. Peter Port",currency:["GBP"],languages:["en","fr"]},GH:{name:"Ghana",native:"Ghana",phone:[233],continent:"AF",capital:"Accra",currency:["GHS"],languages:["en"]},GI:{name:"Gibraltar",native:"Gibraltar",phone:[350],continent:"EU",capital:"Gibraltar",currency:["GIP"],languages:["en"]},GL:{name:"Greenland",native:"Kalaallit Nunaat",phone:[299],continent:"NA",capital:"Nuuk",currency:["DKK"],languages:["kl"]},GM:{name:"Gambia",native:"Gambia",phone:[220],continent:"AF",capital:"Banjul",currency:["GMD"],languages:["en"]},GN:{name:"Guinea",native:"Guin\xE9e",phone:[224],continent:"AF",capital:"Conakry",currency:["GNF"],languages:["fr","ff"]},GP:{name:"Guadeloupe",native:"Guadeloupe",phone:[590],continent:"NA",capital:"Basse-Terre",currency:["EUR"],languages:["fr"]},GQ:{name:"Equatorial Guinea",native:"Guinea Ecuatorial",phone:[240],continent:"AF",capital:"Malabo",currency:["XAF"],languages:["es","fr"]},GR:{name:"Greece",native:"\u0395\u03BB\u03BB\u03AC\u03B4\u03B1",phone:[30],continent:"EU",capital:"Athens",currency:["EUR"],languages:["el"]},GS:{name:"South Georgia and the South Sandwich Islands",native:"South Georgia",phone:[500],continent:"AN",capital:"King Edward Point",currency:["GBP"],languages:["en"]},GT:{name:"Guatemala",native:"Guatemala",phone:[502],continent:"NA",capital:"Guatemala City",currency:["GTQ"],languages:["es"]},GU:{name:"Guam",native:"Guam",phone:[1671],continent:"OC",capital:"Hag\xE5t\xF1a",currency:["USD"],languages:["en","ch","es"]},GW:{name:"Guinea-Bissau",native:"Guin\xE9-Bissau",phone:[245],continent:"AF",capital:"Bissau",currency:["XOF"],languages:["pt"]},GY:{name:"Guyana",native:"Guyana",phone:[592],continent:"SA",capital:"Georgetown",currency:["GYD"],languages:["en"]},HK:{name:"Hong Kong",native:"\u9999\u6E2F",phone:[852],continent:"AS",capital:"City of Victoria",currency:["HKD"],languages:["zh","en"]},HM:{name:"Heard Island and McDonald Islands",native:"Heard Island and McDonald Islands",phone:[61],continent:"AN",capital:"",currency:["AUD"],languages:["en"]},HN:{name:"Honduras",native:"Honduras",phone:[504],continent:"NA",capital:"Tegucigalpa",currency:["HNL"],languages:["es"]},HR:{name:"Croatia",native:"Hrvatska",phone:[385],continent:"EU",capital:"Zagreb",currency:["EUR"],languages:["hr"]},HT:{name:"Haiti",native:"Ha\xEFti",phone:[509],continent:"NA",capital:"Port-au-Prince",currency:["HTG","USD"],languages:["fr","ht"]},HU:{name:"Hungary",native:"Magyarorsz\xE1g",phone:[36],continent:"EU",capital:"Budapest",currency:["HUF"],languages:["hu"]},ID:{name:"Indonesia",native:"Indonesia",phone:[62],continent:"AS",capital:"Jakarta",currency:["IDR"],languages:["id"]},IE:{name:"Ireland",native:"\xC9ire",phone:[353],continent:"EU",capital:"Dublin",currency:["EUR"],languages:["ga","en"]},IL:{name:"Israel",native:"\u05D9\u05B4\u05E9\u05B0\u05C2\u05E8\u05B8\u05D0\u05B5\u05DC",phone:[972],continent:"AS",capital:"Jerusalem",currency:["ILS"],languages:["he","ar"]},IM:{name:"Isle of Man",native:"Isle of Man",phone:[44],continent:"EU",capital:"Douglas",currency:["GBP"],languages:["en","gv"]},IN:{name:"India",native:"\u092D\u093E\u0930\u0924",phone:[91],continent:"AS",capital:"New Delhi",currency:["INR"],languages:["hi","en"]},IO:{name:"British Indian Ocean Territory",native:"British Indian Ocean Territory",phone:[246],continent:"AS",capital:"Diego Garcia",currency:["USD"],languages:["en"]},IQ:{name:"Iraq",native:"\u0627\u0644\u0639\u0631\u0627\u0642",phone:[964],continent:"AS",capital:"Baghdad",currency:["IQD"],languages:["ar","ku"]},IR:{name:"Iran",native:"\u0627\u06CC\u0631\u0627\u0646",phone:[98],continent:"AS",capital:"Tehran",currency:["IRR"],languages:["fa"]},IS:{name:"Iceland",native:"\xCDsland",phone:[354],continent:"EU",capital:"Reykjavik",currency:["ISK"],languages:["is"]},IT:{name:"Italy",native:"Italia",phone:[39],continent:"EU",capital:"Rome",currency:["EUR"],languages:["it"]},JE:{name:"Jersey",native:"Jersey",phone:[44],continent:"EU",capital:"Saint Helier",currency:["GBP"],languages:["en","fr"]},JM:{name:"Jamaica",native:"Jamaica",phone:[1876],continent:"NA",capital:"Kingston",currency:["JMD"],languages:["en"]},JO:{name:"Jordan",native:"\u0627\u0644\u0623\u0631\u062F\u0646",phone:[962],continent:"AS",capital:"Amman",currency:["JOD"],languages:["ar"]},JP:{name:"Japan",native:"\u65E5\u672C",phone:[81],continent:"AS",capital:"Tokyo",currency:["JPY"],languages:["ja"]},KE:{name:"Kenya",native:"Kenya",phone:[254],continent:"AF",capital:"Nairobi",currency:["KES"],languages:["en","sw"]},KG:{name:"Kyrgyzstan",native:"\u041A\u044B\u0440\u0433\u044B\u0437\u0441\u0442\u0430\u043D",phone:[996],continent:"AS",capital:"Bishkek",currency:["KGS"],languages:["ky","ru"]},KH:{name:"Cambodia",native:"K\xE2mp\u016Dch\xE9a",phone:[855],continent:"AS",capital:"Phnom Penh",currency:["KHR"],languages:["km"]},KI:{name:"Kiribati",native:"Kiribati",phone:[686],continent:"OC",capital:"South Tarawa",currency:["AUD"],languages:["en"]},KM:{name:"Comoros",native:"Komori",phone:[269],continent:"AF",capital:"Moroni",currency:["KMF"],languages:["ar","fr"]},KN:{name:"Saint Kitts and Nevis",native:"Saint Kitts and Nevis",phone:[1869],continent:"NA",capital:"Basseterre",currency:["XCD"],languages:["en"]},KP:{name:"North Korea",native:"\uBD81\uD55C",phone:[850],continent:"AS",capital:"Pyongyang",currency:["KPW"],languages:["ko"]},KR:{name:"South Korea",native:"\uB300\uD55C\uBBFC\uAD6D",phone:[82],continent:"AS",capital:"Seoul",currency:["KRW"],languages:["ko"]},KW:{name:"Kuwait",native:"\u0627\u0644\u0643\u0648\u064A\u062A",phone:[965],continent:"AS",capital:"Kuwait City",currency:["KWD"],languages:["ar"]},KY:{name:"Cayman Islands",native:"Cayman Islands",phone:[1345],continent:"NA",capital:"George Town",currency:["KYD"],languages:["en"]},KZ:{name:"Kazakhstan",native:"\u049A\u0430\u0437\u0430\u049B\u0441\u0442\u0430\u043D",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Astana",currency:["KZT"],languages:["kk","ru"]},LA:{name:"Laos",native:"\u0EAA\u0E9B\u0E9B\u0EA5\u0EB2\u0EA7",phone:[856],continent:"AS",capital:"Vientiane",currency:["LAK"],languages:["lo"]},LB:{name:"Lebanon",native:"\u0644\u0628\u0646\u0627\u0646",phone:[961],continent:"AS",capital:"Beirut",currency:["LBP"],languages:["ar","fr"]},LC:{name:"Saint Lucia",native:"Saint Lucia",phone:[1758],continent:"NA",capital:"Castries",currency:["XCD"],languages:["en"]},LI:{name:"Liechtenstein",native:"Liechtenstein",phone:[423],continent:"EU",capital:"Vaduz",currency:["CHF"],languages:["de"]},LK:{name:"Sri Lanka",native:"\u015Br\u012B la\u1E43k\u0101va",phone:[94],continent:"AS",capital:"Colombo",currency:["LKR"],languages:["si","ta"]},LR:{name:"Liberia",native:"Liberia",phone:[231],continent:"AF",capital:"Monrovia",currency:["LRD"],languages:["en"]},LS:{name:"Lesotho",native:"Lesotho",phone:[266],continent:"AF",capital:"Maseru",currency:["LSL","ZAR"],languages:["en","st"]},LT:{name:"Lithuania",native:"Lietuva",phone:[370],continent:"EU",capital:"Vilnius",currency:["EUR"],languages:["lt"]},LU:{name:"Luxembourg",native:"Luxembourg",phone:[352],continent:"EU",capital:"Luxembourg",currency:["EUR"],languages:["fr","de","lb"]},LV:{name:"Latvia",native:"Latvija",phone:[371],continent:"EU",capital:"Riga",currency:["EUR"],languages:["lv"]},LY:{name:"Libya",native:"\u200F\u0644\u064A\u0628\u064A\u0627",phone:[218],continent:"AF",capital:"Tripoli",currency:["LYD"],languages:["ar"]},MA:{name:"Morocco",native:"\u0627\u0644\u0645\u063A\u0631\u0628",phone:[212],continent:"AF",capital:"Rabat",currency:["MAD"],languages:["ar"]},MC:{name:"Monaco",native:"Monaco",phone:[377],continent:"EU",capital:"Monaco",currency:["EUR"],languages:["fr"]},MD:{name:"Moldova",native:"Moldova",phone:[373],continent:"EU",capital:"Chi\u0219in\u0103u",currency:["MDL"],languages:["ro"]},ME:{name:"Montenegro",native:"\u0426\u0440\u043D\u0430 \u0413\u043E\u0440\u0430",phone:[382],continent:"EU",capital:"Podgorica",currency:["EUR"],languages:["sr","bs","sq","hr"]},MF:{name:"Saint Martin",native:"Saint-Martin",phone:[590],continent:"NA",capital:"Marigot",currency:["EUR"],languages:["en","fr","nl"]},MG:{name:"Madagascar",native:"Madagasikara",phone:[261],continent:"AF",capital:"Antananarivo",currency:["MGA"],languages:["fr","mg"]},MH:{name:"Marshall Islands",native:"M\u0327aje\u013C",phone:[692],continent:"OC",capital:"Majuro",currency:["USD"],languages:["en","mh"]},MK:{name:"North Macedonia",native:"\u0421\u0435\u0432\u0435\u0440\u043D\u0430 \u041C\u0430\u043A\u0435\u0434\u043E\u043D\u0438\u0458\u0430",phone:[389],continent:"EU",capital:"Skopje",currency:["MKD"],languages:["mk"]},ML:{name:"Mali",native:"Mali",phone:[223],continent:"AF",capital:"Bamako",currency:["XOF"],languages:["fr"]},MM:{name:"Myanmar (Burma)",native:"\u1019\u103C\u1014\u103A\u1019\u102C",phone:[95],continent:"AS",capital:"Naypyidaw",currency:["MMK"],languages:["my"]},MN:{name:"Mongolia",native:"\u041C\u043E\u043D\u0433\u043E\u043B \u0443\u043B\u0441",phone:[976],continent:"AS",capital:"Ulan Bator",currency:["MNT"],languages:["mn"]},MO:{name:"Macao",native:"\u6FB3\u9580",phone:[853],continent:"AS",capital:"",currency:["MOP"],languages:["zh","pt"]},MP:{name:"Northern Mariana Islands",native:"Northern Mariana Islands",phone:[1670],continent:"OC",capital:"Saipan",currency:["USD"],languages:["en","ch"]},MQ:{name:"Martinique",native:"Martinique",phone:[596],continent:"NA",capital:"Fort-de-France",currency:["EUR"],languages:["fr"]},MR:{name:"Mauritania",native:"\u0645\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u0627",phone:[222],continent:"AF",capital:"Nouakchott",currency:["MRU"],languages:["ar"]},MS:{name:"Montserrat",native:"Montserrat",phone:[1664],continent:"NA",capital:"Plymouth",currency:["XCD"],languages:["en"]},MT:{name:"Malta",native:"Malta",phone:[356],continent:"EU",capital:"Valletta",currency:["EUR"],languages:["mt","en"]},MU:{name:"Mauritius",native:"Maurice",phone:[230],continent:"AF",capital:"Port Louis",currency:["MUR"],languages:["en"]},MV:{name:"Maldives",native:"Maldives",phone:[960],continent:"AS",capital:"Mal\xE9",currency:["MVR"],languages:["dv"]},MW:{name:"Malawi",native:"Malawi",phone:[265],continent:"AF",capital:"Lilongwe",currency:["MWK"],languages:["en","ny"]},MX:{name:"Mexico",native:"M\xE9xico",phone:[52],continent:"NA",capital:"Mexico City",currency:["MXN"],languages:["es"]},MY:{name:"Malaysia",native:"Malaysia",phone:[60],continent:"AS",capital:"Kuala Lumpur",currency:["MYR"],languages:["ms"]},MZ:{name:"Mozambique",native:"Mo\xE7ambique",phone:[258],continent:"AF",capital:"Maputo",currency:["MZN"],languages:["pt"]},NA:{name:"Namibia",native:"Namibia",phone:[264],continent:"AF",capital:"Windhoek",currency:["NAD","ZAR"],languages:["en","af"]},NC:{name:"New Caledonia",native:"Nouvelle-Cal\xE9donie",phone:[687],continent:"OC",capital:"Noum\xE9a",currency:["XPF"],languages:["fr"]},NE:{name:"Niger",native:"Niger",phone:[227],continent:"AF",capital:"Niamey",currency:["XOF"],languages:["fr"]},NF:{name:"Norfolk Island",native:"Norfolk Island",phone:[672],continent:"OC",capital:"Kingston",currency:["AUD"],languages:["en"]},NG:{name:"Nigeria",native:"Nigeria",phone:[234],continent:"AF",capital:"Abuja",currency:["NGN"],languages:["en"]},NI:{name:"Nicaragua",native:"Nicaragua",phone:[505],continent:"NA",capital:"Managua",currency:["NIO"],languages:["es"]},NL:{name:"Netherlands",native:"Nederland",phone:[31],continent:"EU",capital:"Amsterdam",currency:["EUR"],languages:["nl"]},NO:{name:"Norway",native:"Norge",phone:[47],continent:"EU",capital:"Oslo",currency:["NOK"],languages:["no","nb","nn"]},NP:{name:"Nepal",native:"\u0928\u0947\u092A\u093E\u0932",phone:[977],continent:"AS",capital:"Kathmandu",currency:["NPR"],languages:["ne"]},NR:{name:"Nauru",native:"Nauru",phone:[674],continent:"OC",capital:"Yaren",currency:["AUD"],languages:["en","na"]},NU:{name:"Niue",native:"Niu\u0113",phone:[683],continent:"OC",capital:"Alofi",currency:["NZD"],languages:["en"]},NZ:{name:"New Zealand",native:"New Zealand",phone:[64],continent:"OC",capital:"Wellington",currency:["NZD"],languages:["en","mi"]},OM:{name:"Oman",native:"\u0639\u0645\u0627\u0646",phone:[968],continent:"AS",capital:"Muscat",currency:["OMR"],languages:["ar"]},PA:{name:"Panama",native:"Panam\xE1",phone:[507],continent:"NA",capital:"Panama City",currency:["PAB","USD"],languages:["es"]},PE:{name:"Peru",native:"Per\xFA",phone:[51],continent:"SA",capital:"Lima",currency:["PEN"],languages:["es"]},PF:{name:"French Polynesia",native:"Polyn\xE9sie fran\xE7aise",phone:[689],continent:"OC",capital:"Papeet\u0113",currency:["XPF"],languages:["fr"]},PG:{name:"Papua New Guinea",native:"Papua Niugini",phone:[675],continent:"OC",capital:"Port Moresby",currency:["PGK"],languages:["en"]},PH:{name:"Philippines",native:"Pilipinas",phone:[63],continent:"AS",capital:"Manila",currency:["PHP"],languages:["en"]},PK:{name:"Pakistan",native:"Pakistan",phone:[92],continent:"AS",capital:"Islamabad",currency:["PKR"],languages:["en","ur"]},PL:{name:"Poland",native:"Polska",phone:[48],continent:"EU",capital:"Warsaw",currency:["PLN"],languages:["pl"]},PM:{name:"Saint Pierre and Miquelon",native:"Saint-Pierre-et-Miquelon",phone:[508],continent:"NA",capital:"Saint-Pierre",currency:["EUR"],languages:["fr"]},PN:{name:"Pitcairn Islands",native:"Pitcairn Islands",phone:[64],continent:"OC",capital:"Adamstown",currency:["NZD"],languages:["en"]},PR:{name:"Puerto Rico",native:"Puerto Rico",phone:[1787,1939],continent:"NA",capital:"San Juan",currency:["USD"],languages:["es","en"]},PS:{name:"Palestine",native:"\u0641\u0644\u0633\u0637\u064A\u0646",phone:[970],continent:"AS",capital:"Ramallah",currency:["ILS"],languages:["ar"]},PT:{name:"Portugal",native:"Portugal",phone:[351],continent:"EU",capital:"Lisbon",currency:["EUR"],languages:["pt"]},PW:{name:"Palau",native:"Palau",phone:[680],continent:"OC",capital:"Ngerulmud",currency:["USD"],languages:["en"]},PY:{name:"Paraguay",native:"Paraguay",phone:[595],continent:"SA",capital:"Asunci\xF3n",currency:["PYG"],languages:["es","gn"]},QA:{name:"Qatar",native:"\u0642\u0637\u0631",phone:[974],continent:"AS",capital:"Doha",currency:["QAR"],languages:["ar"]},RE:{name:"Reunion",native:"La R\xE9union",phone:[262],continent:"AF",capital:"Saint-Denis",currency:["EUR"],languages:["fr"]},RO:{name:"Romania",native:"Rom\xE2nia",phone:[40],continent:"EU",capital:"Bucharest",currency:["RON"],languages:["ro"]},RS:{name:"Serbia",native:"\u0421\u0440\u0431\u0438\u0458\u0430",phone:[381],continent:"EU",capital:"Belgrade",currency:["RSD"],languages:["sr"]},RU:{name:"Russia",native:"\u0420\u043E\u0441\u0441\u0438\u044F",phone:[7],continent:"AS",continents:["AS","EU"],capital:"Moscow",currency:["RUB"],languages:["ru"]},RW:{name:"Rwanda",native:"Rwanda",phone:[250],continent:"AF",capital:"Kigali",currency:["RWF"],languages:["rw","en","fr"]},SA:{name:"Saudi Arabia",native:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",phone:[966],continent:"AS",capital:"Riyadh",currency:["SAR"],languages:["ar"]},SB:{name:"Solomon Islands",native:"Solomon Islands",phone:[677],continent:"OC",capital:"Honiara",currency:["SBD"],languages:["en"]},SC:{name:"Seychelles",native:"Seychelles",phone:[248],continent:"AF",capital:"Victoria",currency:["SCR"],languages:["fr","en"]},SD:{name:"Sudan",native:"\u0627\u0644\u0633\u0648\u062F\u0627\u0646",phone:[249],continent:"AF",capital:"Khartoum",currency:["SDG"],languages:["ar","en"]},SE:{name:"Sweden",native:"Sverige",phone:[46],continent:"EU",capital:"Stockholm",currency:["SEK"],languages:["sv"]},SG:{name:"Singapore",native:"Singapore",phone:[65],continent:"AS",capital:"Singapore",currency:["SGD"],languages:["en","ms","ta","zh"]},SH:{name:"Saint Helena",native:"Saint Helena",phone:[290],continent:"AF",capital:"Jamestown",currency:["SHP"],languages:["en"]},SI:{name:"Slovenia",native:"Slovenija",phone:[386],continent:"EU",capital:"Ljubljana",currency:["EUR"],languages:["sl"]},SJ:{name:"Svalbard and Jan Mayen",native:"Svalbard og Jan Mayen",phone:[4779],continent:"EU",capital:"Longyearbyen",currency:["NOK"],languages:["no"]},SK:{name:"Slovakia",native:"Slovensko",phone:[421],continent:"EU",capital:"Bratislava",currency:["EUR"],languages:["sk"]},SL:{name:"Sierra Leone",native:"Sierra Leone",phone:[232],continent:"AF",capital:"Freetown",currency:["SLL"],languages:["en"]},SM:{name:"San Marino",native:"San Marino",phone:[378],continent:"EU",capital:"City of San Marino",currency:["EUR"],languages:["it"]},SN:{name:"Senegal",native:"S\xE9n\xE9gal",phone:[221],continent:"AF",capital:"Dakar",currency:["XOF"],languages:["fr"]},SO:{name:"Somalia",native:"Soomaaliya",phone:[252],continent:"AF",capital:"Mogadishu",currency:["SOS"],languages:["so","ar"]},SR:{name:"Suriname",native:"Suriname",phone:[597],continent:"SA",capital:"Paramaribo",currency:["SRD"],languages:["nl"]},SS:{name:"South Sudan",native:"South Sudan",phone:[211],continent:"AF",capital:"Juba",currency:["SSP"],languages:["en"]},ST:{name:"Sao Tome and Principe",native:"S\xE3o Tom\xE9 e Pr\xEDncipe",phone:[239],continent:"AF",capital:"S\xE3o Tom\xE9",currency:["STN"],languages:["pt"]},SV:{name:"El Salvador",native:"El Salvador",phone:[503],continent:"NA",capital:"San Salvador",currency:["SVC","USD"],languages:["es"]},SX:{name:"Sint Maarten",native:"Sint Maarten",phone:[1721],continent:"NA",capital:"Philipsburg",currency:["ANG"],languages:["nl","en"]},SY:{name:"Syria",native:"\u0633\u0648\u0631\u064A\u0627",phone:[963],continent:"AS",capital:"Damascus",currency:["SYP"],languages:["ar"]},SZ:{name:"Eswatini",native:"Eswatini",phone:[268],continent:"AF",capital:"Lobamba",currency:["SZL"],languages:["en","ss"]},TC:{name:"Turks and Caicos Islands",native:"Turks and Caicos Islands",phone:[1649],continent:"NA",capital:"Cockburn Town",currency:["USD"],languages:["en"]},TD:{name:"Chad",native:"Tchad",phone:[235],continent:"AF",capital:"N'Djamena",currency:["XAF"],languages:["fr","ar"]},TF:{name:"French Southern Territories",native:"Territoire des Terres australes et antarctiques fr",phone:[262],continent:"AN",capital:"Port-aux-Fran\xE7ais",currency:["EUR"],languages:["fr"]},TG:{name:"Togo",native:"Togo",phone:[228],continent:"AF",capital:"Lom\xE9",currency:["XOF"],languages:["fr"]},TH:{name:"Thailand",native:"\u0E1B\u0E23\u0E30\u0E40\u0E17\u0E28\u0E44\u0E17\u0E22",phone:[66],continent:"AS",capital:"Bangkok",currency:["THB"],languages:["th"]},TJ:{name:"Tajikistan",native:"\u0422\u043E\u04B7\u0438\u043A\u0438\u0441\u0442\u043E\u043D",phone:[992],continent:"AS",capital:"Dushanbe",currency:["TJS"],languages:["tg","ru"]},TK:{name:"Tokelau",native:"Tokelau",phone:[690],continent:"OC",capital:"Fakaofo",currency:["NZD"],languages:["en"]},TL:{name:"East Timor",native:"Timor-Leste",phone:[670],continent:"OC",capital:"Dili",currency:["USD"],languages:["pt"]},TM:{name:"Turkmenistan",native:"T\xFCrkmenistan",phone:[993],continent:"AS",capital:"Ashgabat",currency:["TMT"],languages:["tk","ru"]},TN:{name:"Tunisia",native:"\u062A\u0648\u0646\u0633",phone:[216],continent:"AF",capital:"Tunis",currency:["TND"],languages:["ar"]},TO:{name:"Tonga",native:"Tonga",phone:[676],continent:"OC",capital:"Nuku'alofa",currency:["TOP"],languages:["en","to"]},TR:{name:"Turkey",native:"T\xFCrkiye",phone:[90],continent:"AS",continents:["AS","EU"],capital:"Ankara",currency:["TRY"],languages:["tr"]},TT:{name:"Trinidad and Tobago",native:"Trinidad and Tobago",phone:[1868],continent:"NA",capital:"Port of Spain",currency:["TTD"],languages:["en"]},TV:{name:"Tuvalu",native:"Tuvalu",phone:[688],continent:"OC",capital:"Funafuti",currency:["AUD"],languages:["en"]},TW:{name:"Taiwan",native:"\u81FA\u7063",phone:[886],continent:"AS",capital:"Taipei",currency:["TWD"],languages:["zh"]},TZ:{name:"Tanzania",native:"Tanzania",phone:[255],continent:"AF",capital:"Dodoma",currency:["TZS"],languages:["sw","en"]},UA:{name:"Ukraine",native:"\u0423\u043A\u0440\u0430\u0457\u043D\u0430",phone:[380],continent:"EU",capital:"Kyiv",currency:["UAH"],languages:["uk"]},UG:{name:"Uganda",native:"Uganda",phone:[256],continent:"AF",capital:"Kampala",currency:["UGX"],languages:["en","sw"]},UM:{name:"U.S. Minor Outlying Islands",native:"United States Minor Outlying Islands",phone:[1],continent:"OC",capital:"",currency:["USD"],languages:["en"]},US:{name:"United States",native:"United States",phone:[1],continent:"NA",capital:"Washington D.C.",currency:["USD","USN","USS"],languages:["en"]},UY:{name:"Uruguay",native:"Uruguay",phone:[598],continent:"SA",capital:"Montevideo",currency:["UYI","UYU"],languages:["es"]},UZ:{name:"Uzbekistan",native:"O'zbekiston",phone:[998],continent:"AS",capital:"Tashkent",currency:["UZS"],languages:["uz","ru"]},VA:{name:"Vatican City",native:"Vaticano",phone:[379],continent:"EU",capital:"Vatican City",currency:["EUR"],languages:["it","la"]},VC:{name:"Saint Vincent and the Grenadines",native:"Saint Vincent and the Grenadines",phone:[1784],continent:"NA",capital:"Kingstown",currency:["XCD"],languages:["en"]},VE:{name:"Venezuela",native:"Venezuela",phone:[58],continent:"SA",capital:"Caracas",currency:["VES"],languages:["es"]},VG:{name:"British Virgin Islands",native:"British Virgin Islands",phone:[1284],continent:"NA",capital:"Road Town",currency:["USD"],languages:["en"]},VI:{name:"U.S. Virgin Islands",native:"United States Virgin Islands",phone:[1340],continent:"NA",capital:"Charlotte Amalie",currency:["USD"],languages:["en"]},VN:{name:"Vietnam",native:"Vi\u1EC7t Nam",phone:[84],continent:"AS",capital:"Hanoi",currency:["VND"],languages:["vi"]},VU:{name:"Vanuatu",native:"Vanuatu",phone:[678],continent:"OC",capital:"Port Vila",currency:["VUV"],languages:["bi","en","fr"]},WF:{name:"Wallis and Futuna",native:"Wallis et Futuna",phone:[681],continent:"OC",capital:"Mata-Utu",currency:["XPF"],languages:["fr"]},WS:{name:"Samoa",native:"Samoa",phone:[685],continent:"OC",capital:"Apia",currency:["WST"],languages:["sm","en"]},XK:{name:"Kosovo",native:"Republika e Kosov\xEBs",phone:[377,381,383,386],continent:"EU",capital:"Pristina",currency:["EUR"],languages:["sq","sr"],userAssigned:!0},YE:{name:"Yemen",native:"\u0627\u0644\u064A\u064E\u0645\u064E\u0646",phone:[967],continent:"AS",capital:"Sana'a",currency:["YER"],languages:["ar"]},YT:{name:"Mayotte",native:"Mayotte",phone:[262],continent:"AF",capital:"Mamoudzou",currency:["EUR"],languages:["fr"]},ZA:{name:"South Africa",native:"South Africa",phone:[27],continent:"AF",capital:"Pretoria",currency:["ZAR"],languages:["af","en","nr","st","ss","tn","ts","ve","xh","zu"]},ZM:{name:"Zambia",native:"Zambia",phone:[260],continent:"AF",capital:"Lusaka",currency:["ZMW"],languages:["en"]},ZW:{name:"Zimbabwe",native:"Zimbabwe",phone:[263],continent:"AF",capital:"Harare",currency:["USD","ZAR","BWP","GBP","AUD","CNY","INR","JPY"],languages:["en","sn","nd"]}};var r={AD:"AND",AE:"ARE",AF:"AFG",AG:"ATG",AI:"AIA",AL:"ALB",AM:"ARM",AO:"AGO",AQ:"ATA",AR:"ARG",AS:"ASM",AT:"AUT",AU:"AUS",AW:"ABW",AX:"ALA",AZ:"AZE",BA:"BIH",BB:"BRB",BD:"BGD",BE:"BEL",BF:"BFA",BG:"BGR",BH:"BHR",BI:"BDI",BJ:"BEN",BL:"BLM",BM:"BMU",BN:"BRN",BO:"BOL",BQ:"BES",BR:"BRA",BS:"BHS",BT:"BTN",BV:"BVT",BW:"BWA",BY:"BLR",BZ:"BLZ",CA:"CAN",CC:"CCK",CD:"COD",CF:"CAF",CG:"COG",CH:"CHE",CI:"CIV",CK:"COK",CL:"CHL",CM:"CMR",CN:"CHN",CO:"COL",CR:"CRI",CU:"CUB",CV:"CPV",CW:"CUW",CX:"CXR",CY:"CYP",CZ:"CZE",DE:"DEU",DJ:"DJI",DK:"DNK",DM:"DMA",DO:"DOM",DZ:"DZA",EC:"ECU",EE:"EST",EG:"EGY",EH:"ESH",ER:"ERI",ES:"ESP",ET:"ETH",FI:"FIN",FJ:"FJI",FK:"FLK",FM:"FSM",FO:"FRO",FR:"FRA",GA:"GAB",GB:"GBR",GD:"GRD",GE:"GEO",GF:"GUF",GG:"GGY",GH:"GHA",GI:"GIB",GL:"GRL",GM:"GMB",GN:"GIN",GP:"GLP",GQ:"GNQ",GR:"GRC",GS:"SGS",GT:"GTM",GU:"GUM",GW:"GNB",GY:"GUY",HK:"HKG",HM:"HMD",HN:"HND",HR:"HRV",HT:"HTI",HU:"HUN",ID:"IDN",IE:"IRL",IL:"ISR",IM:"IMN",IN:"IND",IO:"IOT",IQ:"IRQ",IR:"IRN",IS:"ISL",IT:"ITA",JE:"JEY",JM:"JAM",JO:"JOR",JP:"JPN",KE:"KEN",KG:"KGZ",KH:"KHM",KI:"KIR",KM:"COM",KN:"KNA",KP:"PRK",KR:"KOR",KW:"KWT",KY:"CYM",KZ:"KAZ",LA:"LAO",LB:"LBN",LC:"LCA",LI:"LIE",LK:"LKA",LR:"LBR",LS:"LSO",LT:"LTU",LU:"LUX",LV:"LVA",LY:"LBY",MA:"MAR",MC:"MCO",MD:"MDA",ME:"MNE",MF:"MAF",MG:"MDG",MH:"MHL",MK:"MKD",ML:"MLI",MM:"MMR",MN:"MNG",MO:"MAC",MP:"MNP",MQ:"MTQ",MR:"MRT",MS:"MSR",MT:"MLT",MU:"MUS",MV:"MDV",MW:"MWI",MX:"MEX",MY:"MYS",MZ:"MOZ",NA:"NAM",NC:"NCL",NE:"NER",NF:"NFK",NG:"NGA",NI:"NIC",NL:"NLD",NO:"NOR",NP:"NPL",NR:"NRU",NU:"NIU",NZ:"NZL",OM:"OMN",PA:"PAN",PE:"PER",PF:"PYF",PG:"PNG",PH:"PHL",PK:"PAK",PL:"POL",PM:"SPM",PN:"PCN",PR:"PRI",PS:"PSE",PT:"PRT",PW:"PLW",PY:"PRY",QA:"QAT",RE:"REU",RO:"ROU",RS:"SRB",RU:"RUS",RW:"RWA",SA:"SAU",SB:"SLB",SC:"SYC",SD:"SDN",SE:"SWE",SG:"SGP",SH:"SHN",SI:"SVN",SJ:"SJM",SK:"SVK",SL:"SLE",SM:"SMR",SN:"SEN",SO:"SOM",SR:"SUR",SS:"SSD",ST:"STP",SV:"SLV",SX:"SXM",SY:"SYR",SZ:"SWZ",TC:"TCA",TD:"TCD",TF:"ATF",TG:"TGO",TH:"THA",TJ:"TJK",TK:"TKL",TL:"TLS",TM:"TKM",TN:"TUN",TO:"TON",TR:"TUR",TT:"TTO",TV:"TUV",TW:"TWN",TZ:"TZA",UA:"UKR",UG:"UGA",UM:"UMI",US:"USA",UY:"URY",UZ:"UZB",VA:"VAT",VC:"VCT",VE:"VEN",VG:"VGB",VI:"VIR",VN:"VNM",VU:"VUT",WF:"WLF",WS:"WSM",XK:"XKX",YE:"YEM",YT:"MYT",ZA:"ZAF",ZM:"ZMB",ZW:"ZWE"};var c=n=>({...a[n],iso2:n,iso3:r[n]}),t=()=>Object.keys(a).map(n=>c(n));t();
-
-var browserExtra = async(ip) => {
- const geodata = await ip_lookup(ip);
- if(geodata && a[geodata.country]){
- const h = a[geodata.country];
- geodata.country_name = h.name;
- geodata.country_native = h.native;
- geodata.continent = h.continent;
- geodata.capital = h.capital;
- geodata.phone = h.phone;
- geodata.currency = h.currency;
- geodata.languages = h.languages;
- }
- return geodata
-};
-
-export { browserExtra as default };
diff --git a/browser/geocode-extra/package.json b/browser/geocode-extra/package.json
deleted file mode 100644
index e9e5ff1..0000000
--- a/browser/geocode-extra/package.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "name": "@iplookup/geocode-extra",
- "version": "1.0.20241103",
- "description": "Browser api to lookup geocode from IP address",
- "keywords": [
- "location",
- "iplookup",
- "lookup",
- "geo",
- "geocode",
- "geoip",
- "ip",
- "ipv4",
- "ipv6",
- "ip-location-db",
- "country"
- ],
- "author": "sapics",
- "homepage": "https://github.com/sapics/ip-location-api",
- "repository": {
- "type": "git",
- "url": "git://github.com/sapics/ip-location-api.git"
- },
- "publishConfig": {
- "access": "public"
- },
- "license": "CC BY 4.0 and MIT",
- "main": "iplookup.cjs",
- "exports": {
- "import": "./iplookup.mjs",
- "require": "./iplookup.cjs"
- }
-}
\ No newline at end of file
diff --git a/browser/geocode-offline.html b/browser/geocode-offline.html
deleted file mode 100644
index c7f50c6..0000000
--- a/browser/geocode-offline.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- TEST
-
-
-
- TEST
-
-
- EXPORT INFORMATION
-
-
-
-
diff --git a/browser/geocode-online.html b/browser/geocode-online.html
deleted file mode 100644
index 42703f7..0000000
--- a/browser/geocode-online.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- TEST
-
-
-
- TEST
-
-
- EXPORT INFORMATION
-
-
-
-
diff --git a/browser/geocode/README.md b/browser/geocode/README.md
deleted file mode 100644
index 9d9a220..0000000
--- a/browser/geocode/README.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# @iplookup/geocode [](https://www.npmjs.com/package/@iplookup/geocode)
-
-This is an API created to make [ip-location-api](https://github.com/sapics/ip-location-api) available for browsers.
-The database itself is large at 132MB, so it is splitted into over 4000 pieces for fast downloading in a browser.
-
-
-## Synopsis
-
-```html
-
-
-```
-
-#### ESM
-
-```javascript
-import IpLookup from '@iplookup/geocode'
-await IpLookup("2402:b801:ea8b:23c0::")
-```
-
-#### CJS
-
-```javascript
-const IpLookup = require('@iplookup/geocode')
-await IpLookup("207.97.227.239")
-```
-
-If you need extra information about country, try to use [@iplookup/geocode-extra](https://github.com/sapics/ip-location-api/tree/main/browser/geocode-extra).
-
-
-## License
-
-The database is published under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) by [DB-IP](https://db-ip.com/db/download/ip-to-city-lite).
-
-The software itself is published under MIT license by [sapics](https://github.com/sapics).
\ No newline at end of file
diff --git a/browser/geocode/iplookup.cjs b/browser/geocode/iplookup.cjs
deleted file mode 100644
index 93cea7c..0000000
--- a/browser/geocode/iplookup.cjs
+++ /dev/null
@@ -1,174 +0,0 @@
-'use strict';
-
-const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
-};
-
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 8;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var browser = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE));
- const ccCode = numToCountryCode(arr[0] & 1023);
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
-};
-
-module.exports = browser;
diff --git a/browser/geocode/iplookup.js b/browser/geocode/iplookup.js
deleted file mode 100644
index 428991c..0000000
--- a/browser/geocode/iplookup.js
+++ /dev/null
@@ -1,155 +0,0 @@
-var IpLookup = (function () {
- 'use strict';
-
- const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
- };
-
- const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
- };
-
- const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
- };
-
- const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
- };
- const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
- };
-
- const TOP_URL = document.currentScript.src.split('/').slice(0, -1).join('/') + '/';
- const MAIN_RECORD_SIZE = 8;
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
- const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
- };
-
- const downloadIdx = downloadArrayBuffer;
-
- const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
- const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
- // console.log('ipv6 file cannot download')
- return
- }
- return Idx[6] = new BigUint64Array(buf)
- })
- };
-
- var browser = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
- // console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
- // console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE));
- const ccCode = numToCountryCode(arr[0] & 1023);
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
- };
-
- return browser;
-
-})();
diff --git a/browser/geocode/iplookup.min.js b/browser/geocode/iplookup.min.js
deleted file mode 100644
index b180cee..0000000
--- a/browser/geocode/iplookup.min.js
+++ /dev/null
@@ -1 +0,0 @@
-var IpLookup=function(){"use strict";const n=n=>((n=n.split(/\./))[0]<<24|n[1]<<16|n[2]<<8|n[3])>>>0,t=document.currentScript.src.split("/").slice(0,-1).join("/")+"/",r=async(n,t=3)=>fetch(n,{cache:"no-cache"}).then((async e=>{return e.ok?e.arrayBuffer():404===e.status?null:t?(await(i=100*(4-t)*(4-t),new Promise((n=>setTimeout(n,i)))),r(n,t-1)):null;var i})),e=r,i={},l={4:t,6:t},s={4:e(t+"4.idx").then((n=>{if(n)return i[4]=new Uint32Array(n)})),6:e(t+"4.idx").then((n=>{if(n)return i[6]=new BigUint64Array(n)}))};return async t=>{var e,u,c=!0;t.includes(":")?6===(u=(e=(t=>{if(t.includes("."))return n(t.split(":").pop());var r,e=0n;if((t=t.split(/:/)).length-1<7){const n=t.indexOf("");if(n<4){const e=8-t.length,i=n+e;for(r=7;r>=n;r--)t[r]=r>i?t[r-e]:0}}for(r=0;r<4;r++)t[r]&&(e+=BigInt(parseInt(t[r],16))<=a[0]))return null;for(var o,f=0,g=a.length-1;;)if(e>1]){if(g-f<2)return null;g=o-1}else{if(f===o){g>o&&e>=a[g]&&(o=g);break}f=o}const h=((n,t)=>n.length>t?n:"_".repeat(t-n.length)+n)(o.toString(36),2);const y=await r(l[u]+u+"/"+h);if(!y)return null;const p=2*(u-2),w=8+2*p,d=y.byteLength/w,A=c?new Uint32Array(y.slice(0,4*d)):new BigUint64Array(y.slice(0,8*d));for(f=0,g=d-1;;)if(e>1]){if(g-f<2)return null;g=o-1}else{if(f===o){g>o&&e>=A[g]&&(o=g);break}f=o}const B=c?new Uint32Array(y.slice((d+o)*p,(d+o+1)*p))[0]:new BigUint64Array(y.slice((d+o)*p,(d+o+1)*p))[0];if(e>=A[o]&&e<=B){const n=new Int32Array(y.slice(d*p*2+8*o,d*p*2+8*(o+1))),t=(n=>String.fromCharCode(65+(n/26|0),n%26+65))(1023&n[0]);return{latitude:(n[0]>>10)/1e4,longitude:n[1]/1e4,country:t}}return null}}();
diff --git a/browser/geocode/iplookup.mjs b/browser/geocode/iplookup.mjs
deleted file mode 100644
index b38d526..0000000
--- a/browser/geocode/iplookup.mjs
+++ /dev/null
@@ -1,172 +0,0 @@
-const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
-};
-
-const aton4 = (a) => {
- a = a.split(/\./);
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-};
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/);
- const l = a.length - 1;
- var i, r = 0n;
- if (l < 7) {
- const omitStart = a.indexOf('');
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted;
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0;
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
- }
- return r
-};
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-};
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-};
-
-const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
-const MAIN_RECORD_SIZE = 8;
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-};
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry));
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-};
-
-const downloadIdx = downloadVersionArrayBuffer ;
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL};
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/');
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-};
-
-var browser = async (ipString) => {
- var ip, version, isv4 = true;
- if(ipString.includes(':')) {
- ip = aton6Start(ipString);
- version = ip.constructor === BigInt ? 6 : 4;
- if(version === 6) isv4 = false;
- } else {
- ip = aton4(ipString);
- version = 4;
- }
-
- const ipIndexes = Idx[version] || (await Preload[version]);
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line;
- for(;;){
- line = (fline + cline) >> 1;
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
-
- const fileName = numberToDir(line);
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName);
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2;
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
- const recordCount = dataBuffer.byteLength / recordSize;
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
- fline = 0, cline = recordCount - 1;
- for(;;){
- line = fline + cline >> 1;
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1;
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline;
- }
- break;
- }
- fline = line;
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
- if(ip >= startList[line] && ip <= endIp){
- {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE));
- const ccCode = numToCountryCode(arr[0] & 1023);
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
-};
-
-export { browser as default };
diff --git a/browser/geocode/package.json b/browser/geocode/package.json
deleted file mode 100644
index 968a211..0000000
--- a/browser/geocode/package.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "name": "@iplookup/geocode",
- "version": "1.0.20241103",
- "description": "Browser api to lookup geocode from IP address",
- "keywords": [
- "location",
- "iplookup",
- "lookup",
- "geo",
- "geocode",
- "geoip",
- "ip",
- "ipv4",
- "ipv6",
- "ip-location-db",
- "country"
- ],
- "author": "sapics",
- "homepage": "https://github.com/sapics/ip-location-api",
- "repository": {
- "type": "git",
- "url": "git://github.com/sapics/ip-location-api.git"
- },
- "publishConfig": {
- "access": "public"
- },
- "license": "CC BY 4.0 and MIT",
- "main": "iplookup.cjs",
- "exports": {
- "import": "./iplookup.mjs",
- "require": "./iplookup.cjs"
- }
-}
\ No newline at end of file
diff --git a/cjs/db.cjs b/cjs/db.cjs
deleted file mode 100644
index cc65e37..0000000
--- a/cjs/db.cjs
+++ /dev/null
@@ -1,853 +0,0 @@
-
-const fs = require('fs/promises')
-const fsSync = require('fs')
-const path = require('path')
-const { fileURLToPath } = require('url')
-const { createHash } = require('crypto')
-const { pipeline } = require('stream/promises')
-
-const axios = require('axios')
-const { parse } = require('@fast-csv/parse')
-const { Address4, Address6 } = require('ip-address')
-const dayjs = require('dayjs')
-
-const { setting, consoleLog, consoleWarn } = require('./setting.cjs')
-const { getPostcodeDatabase, strToNum37, aton4, aton6, getSmallMemoryFile, numberToDir, countryCodeToNum } = require('./utils.cjs')
-
-
-const rimraf = (dir) => {
- if(fs.rm){
- return fs.rm(dir, {recursive: true, force: true, maxRetries: 3})
- }
- return fs.rmdir(dir, {recursive: true, maxRetries: 3})
-}
-
-
-
-const DownloadServer = 'https://download.maxmind.com/app/geoip_download'
-const yauzl = require('yauzl')
-const update = async () => {
- var srcList, refreshTmpDir = setting.downloadType !== 'reuse'
- if(refreshTmpDir || !fsSync.existsSync(setting.tmpDataDir)){
-
- await rimraf(setting.tmpDataDir)
- await fs.mkdir(setting.tmpDataDir, {recursive: true})
- }
- if (!fsSync.existsSync(setting.fieldDir)){
- await fs.mkdir(setting.fieldDir, {recursive: true})
- }
-
- consoleLog('Downloading database')
- if(setting.browserType === 'geocode'){
- await dbipLocation()
- return createBrowserIndex(setting.browserType)
- }
-
- if(setting.ipLocationDb){
- srcList = await ipLocationDb(setting.ipLocationDb.replace(/-country$/, ''))
- } else {
- srcList = await downloadZip()
- }
-
- if(!srcList){
- return console.log('ERROR TO UPDATE')
- }
- consoleLog(srcList)
- if(srcList === 'NO NEED TO UPDATE') {
- return
- }
-
- consoleLog('Creating database for ip-location-api')
- await createData(srcList)
- consoleLog('Database update completed!!')
-
- if(refreshTmpDir){
- await rimraf(setting.tmpDataDir, {recursive: true, force: true})
- }
- if(SHA256_RESULT){
-
- await fs.writeFile(path.join(setting.fieldDir, setting.series + '-' + setting.dataType + '-CSV.zip.sha256'), SHA256_RESULT)
- }
-
- var tmpFiles = fsSync.readdirSync(setting.fieldDir).filter(file => file.endsWith('.tmp'))
- for(var tmpFile of tmpFiles){
- await fs.rename(path.join(setting.fieldDir, tmpFile), path.join(setting.fieldDir, tmpFile.replace('.tmp', '')))
- }
- if(setting.smallMemory && !setting.runningUpdate){
- await fs.cp(path.join(setting.fieldDir, 'v4-tmp'), path.join(setting.fieldDir, 'v4'), {recursive: true, force: true})
- await fs.cp(path.join(setting.fieldDir, 'v6-tmp'), path.join(setting.fieldDir, 'v6'), {recursive: true, force: true})
- rimraf(path.join(setting.fieldDir, 'v4-tmp')).catch(consoleWarn)
- rimraf(path.join(setting.fieldDir, 'v6-tmp')).catch(consoleWarn)
- }
-
- if(setting.browserType){
- await createBrowserIndex(setting.browserType)
- }
-
- console.log('SUCCESS TO UPDATE')
-}
-
-const ipLocationDb = async (db) => {
- var preUrl = 'https://cdn.jsdelivr.net/npm/@ip-location-db/'+db+'-country/'+db+'-country'
- var urls = [preUrl+'-ipv4.csv', preUrl+'-ipv6.csv'], fileNames = []
- for(var url of urls){
- fileNames.push(await _ipLocationDb(url))
- }
- return fileNames
-}
-
-const _ipLocationDb = async (url) => {
- var fileEnd = url.split('-').pop()
- return axios({
- method: 'get',
- url: url,
- responseType: 'stream'
- }).then(res => {
- return new Promise((resolve, reject) => {
- var fileName = setting.ipLocationDb + '-Blocks-' + fileEnd
- const ws = fsSync.createWriteStream(path.join(setting.tmpDataDir, fileName))
- ws.write('network1,network2,cc\n')
- res.data.pipe(ws)
- ws.on('finish', () => {
- resolve(fileName)
- })
- ws.on('error', reject)
- })
- })
-}
-
-const dbipLocation = async () => {
- const address = "https://download.db-ip.com/free/dbip-city-lite-" + dayjs().format('YYYY-MM') + ".csv.gz"
- const res = await fetch(address)
- const tmpFile = path.join(setting.tmpDataDir, 'dbip-city-lite.csv')
- const ws = fsSync.createWriteStream(tmpFile)
- await pipeline(res.body.pipeThrough(new DecompressionStream('gzip')), ws)
- return new Promise((resolve, reject) => {
- const v4 = [], v6 = []
- var preData
- fsSync.createReadStream(tmpFile).pipe(parse())
- .on('error', reject)
- .on('end', () => {
- var v4Buf1 = Buffer.alloc(v4.length * 4)
- var v4Buf2 = Buffer.alloc(v4.length * 4)
- var v4Buf3 = Buffer.alloc(v4.length * 8)
- for(var i = 0; i < v4.length; ++i){
- v4Buf1.writeUInt32LE(v4[i][0], i * 4)
- v4Buf2.writeUInt32LE(v4[i][1], i * 4)
- v4Buf3.writeInt32LE(v4[i][2], i * 8)
- v4Buf3.writeInt32LE(v4[i][3], i * 8 + 4)
- }
- fsSync.writeFileSync(path.join(setting.fieldDir, '4-1.dat'), v4Buf1)
- fsSync.writeFileSync(path.join(setting.fieldDir, '4-2.dat'), v4Buf2)
- fsSync.writeFileSync(path.join(setting.fieldDir, '4-3.dat'), v4Buf3)
-
- var v6Buf1 = Buffer.alloc(v6.length * 8)
- var v6Buf2 = Buffer.alloc(v6.length * 8)
- var v6Buf3 = Buffer.alloc(v6.length * 8)
- for(var i = 0; i < v6.length; ++i){
- v6Buf1.writeBigUInt64LE(v6[i][0], i * 8)
- v6Buf2.writeBigUInt64LE(v6[i][1], i * 8)
- v6Buf3.writeInt32LE(v6[i][2], i * 8)
- v6Buf3.writeInt32LE(v6[i][3], i * 8 + 4)
- }
- fsSync.writeFileSync(path.join(setting.fieldDir, '6-1.dat'), v6Buf1)
- fsSync.writeFileSync(path.join(setting.fieldDir, '6-2.dat'), v6Buf2)
- fsSync.writeFileSync(path.join(setting.fieldDir, '6-3.dat'), v6Buf3)
- resolve()
- })
- .on('data', arr => {
- if(!arr[2] || arr[3] === 'ZZ' || arr[3] === 'EU') return;
- var latitude = Math.round((parseFloat(arr[6])) * 10000) // -90 ~ 90 -> 10 ~ 190
-
- var longitude = Math.round((parseFloat(arr[7])) * 10000)// -180 ~ 180 -> 20 ~ 220
-
- var countryCodeNum = countryCodeToNum(arr[3]) // 0 ~ 675
- latitude = (latitude) << 10 | countryCodeNum
- if(arr[0].includes(':')){
- var start = aton6(arr[0])
- if(preData[1].constructor !== BigInt) preData = null
- if(preData && preData[1] + 1n === start && preData[2] === latitude && preData[3] === longitude){
- preData[1] = aton6(arr[1])
- return
- }
- v6.push(preData = [aton6(arr[0]), aton6(arr[1]), latitude, longitude])
- } else {
- var start = aton4(arr[0])
- if(preData && preData[1] + 1 === start && preData[2] === latitude && preData[3] === longitude){
- preData[1] = aton4(arr[1])
- return
- }
- v4.push(preData = [aton4(arr[0]), aton4(arr[1]), latitude, longitude])
- }
- })
- })
-}
-
-const createBrowserIndex = async (type) => {
- const exportDir = path.join(setting.fieldDir, type)
- await fs.rm(path.join(exportDir, '4'), {recursive: true, force: true})
- await fs.mkdir(path.join(exportDir, '4'), {recursive: true})
- await fs.rm(path.join(exportDir, '6'), {recursive: true, force: true})
- await fs.mkdir(path.join(exportDir, '6'), {recursive: true})
-
- const IndexSize = type === 'country' ? 1024 : 2048
-
- var startBuf = await fs.readFile(path.join(setting.fieldDir, '4-1.dat'))
- var startList = new Uint32Array(startBuf.buffer)
- var len = startList.length, indexList = new Uint32Array(IndexSize)
- var i, j, k
- var endBuf = await fs.readFile(path.join(setting.fieldDir, '4-2.dat'))
- var endList = new Uint32Array(endBuf.buffer)
- var dbInfo = await fs.readFile(path.join(setting.fieldDir, '4-3.dat'))
- var dbList = type === 'country' ? new Uint16Array(dbInfo.buffer) : new Int32Array(dbInfo.buffer)
- var recordSize = setting.mainRecordSize + 8
- for(i = 0; i < IndexSize; ++i){
- var index = len * i / IndexSize | 0
- indexList[i] = startList[index]
- var nextIndex = len * (i + 1) / IndexSize | 0
- var count = nextIndex - index
- var exportBuf = Buffer.alloc(recordSize * count)
- for(j = index, k = 0; j < nextIndex; ++j){
- exportBuf.writeUInt32LE(startList[j], k * 4)
- exportBuf.writeUInt32LE(endList[j], 4 * count + k * 4)
- if(type === 'country'){
- exportBuf.writeUInt16LE(dbList[j], 8 * count + k * setting.mainRecordSize)
- } else {
- exportBuf.writeInt32LE(dbList[2*j], 8 * count + k * setting.mainRecordSize)
- exportBuf.writeInt32LE(dbList[2*j+1], 8 * count + k * setting.mainRecordSize + 4)
- }
- ++k
- }
- await fs.writeFile(path.join(exportDir, '4', numberToDir(i)), exportBuf)
- }
- await fs.writeFile(path.join(exportDir, '4.idx'), Buffer.from(indexList.buffer))
-
- startBuf = await fs.readFile(path.join(setting.fieldDir, '6-1.dat'))
- startList = new BigUint64Array(startBuf.buffer)
- len = startList.length
- indexList = new BigUint64Array(IndexSize)
- endBuf = await fs.readFile(path.join(setting.fieldDir, '6-2.dat'))
- endList = new BigUint64Array(endBuf.buffer)
- dbInfo = await fs.readFile(path.join(setting.fieldDir, '6-3.dat'))
- dbList = type === 'country' ? new Uint16Array(dbInfo.buffer) : new Int32Array(dbInfo.buffer)
- recordSize = setting.mainRecordSize + 16
- for(i = 0; i < IndexSize; ++i){
- var index = len * i / IndexSize | 0
- indexList[i] = startList[index]
- var nextIndex = len * (i + 1) / IndexSize | 0
- var exportBuf = Buffer.alloc(recordSize * (nextIndex - index))
- var count = nextIndex - index
- for(j = index, k = 0; j < nextIndex; ++j){
- exportBuf.writeBigUInt64LE(startList[j], k * 8)
- exportBuf.writeBigUInt64LE(endList[j], 8 * count + k * 8)
- if(type === 'country'){
- exportBuf.writeUInt16LE(dbList[j], 16 * count + k * setting.mainRecordSize)
- } else {
- exportBuf.writeInt32LE(dbList[2*j], 16 * count + k * setting.mainRecordSize)
- exportBuf.writeInt32LE(dbList[2*j+1], 16 * count + k * setting.mainRecordSize + 4)
- }
- ++k
- }
- await fs.writeFile(path.join(exportDir, '6', numberToDir(i)), exportBuf)
- }
- await fs.writeFile(path.join(exportDir, '6.idx'), Buffer.from(indexList.buffer))
-
- var exPath = path.join(__dirname, '..', 'browser', type)
- await fs.rm(path.join(exPath, '4'), {recursive: true, force: true})
- await fs.rm(path.join(exPath, '6'), {recursive: true, force: true})
- await fs.cp(exportDir, exPath, {recursive: true})
- exPath = path.join(__dirname, '..', 'browser', type + '-extra')
- await fs.rm(path.join(exPath, '4'), {recursive: true, force: true})
- await fs.rm(path.join(exPath, '6'), {recursive: true, force: true})
- await fs.cp(exportDir, exPath, {recursive: true})
- await fs.rm(exportDir, {recursive: true, force: true})
-}
-
-var SHA256_RESULT
-const downloadZip = async () => {
- SHA256_RESULT = false
- var name = setting.dataType[0].toUpperCase() + setting.dataType.slice(1)
- const database = {
- type: setting.dataType,
- edition: setting.series + '-' + name + '-CSV',
- suffix: 'zip.sha256',
- src: [
- setting.series + '-' + name + '-Locations-en.csv',
- setting.series + '-' + name + '-Blocks-IPv4.csv',
- setting.series + '-' + name + '-Blocks-IPv6.csv'
- ],
- }
- if(setting.language !== 'en' && setting.isCity){
- database.src.push(setting.series + '-' + name + '-Locations-' + setting.language + '.csv')
- }
- if(!setting.licenseKey) {
- return consoleWarn('Please set your license key')
- }
- var url = DownloadServer + '?edition_id=' + database.edition + '&suffix=' + database.suffix + "&license_key=" + setting.licenseKey
- if(setting.licenseKey === 'redist'){
- url = 'https://raw.githubusercontent.com/sapics/node-geolite2-redist/master/redist/'
- url += database.edition + '.' + database.suffix
- }
- var text = await axios.get(url)
- var reg = /\w{50,}/, r = reg.exec(text.data)
- if(!r) {
- return consoleWarn('Cannot download sha256')
- }
- var sha256 = r[0], data = ''
- try{
- data = await fs.readFile(path.join(setting.fieldDir, database.edition + '.zip.sha256'), 'utf8')
- }catch(e){
- data = ''
- }
-
- const zipPath = path.join(setting.tmpDataDir, database.edition + '.zip')
- if(data === sha256){
- if(fsSync.existsSync(zipPath)){
- const zipHash = await sha256Hash(zipPath)
- if(zipHash === sha256){
- if(!setting.multiDbDir) {
- if(setting.sameDbSetting) return 'NO NEED TO UPDATE'
- if(setting.language === 'en') return database.src
- }
- }
- } else if(!setting.multiDbDir){
- if(setting.sameDbSetting) return 'NO NEED TO UPDATE'
- }
- }
-
- SHA256_RESULT = sha256
- url = DownloadServer + '?edition_id=' + database.edition + '&suffix=' + database.suffix.replace('.sha256', '') + "&license_key=" + setting.licenseKey
- if(setting.licenseKey === 'redist'){
- url = 'https://raw.githubusercontent.com/sapics/node-geolite2-redist/master/redist/'
- url += database.edition + '.' + database.suffix.replace('.sha256', '')
- }
- return axios({
- method: 'get',
- url: url,
- responseType: 'stream'
- }).then(res => {
- const dest = fsSync.createWriteStream(zipPath)
- return new Promise((resolve, reject) => {
- consoleLog('Decompressing', database.edition + '.zip')
- res.data.pipe(dest)
- res.data.on('end', () => {
- yauzl.open(zipPath, {lazyEntries: true}, (err, zipfile) => {
- if(err) return reject(err)
- zipfile.readEntry()
- zipfile.on('entry', entry => {
- for(var src of database.src){
- if(!entry.fileName.endsWith(src)) continue;
- consoleLog('Extracting', entry.fileName)
- return (function(src){
- zipfile.openReadStream(entry, (err, readStream) => {
- if(err) return reject(err)
- readStream.pipe(fsSync.createWriteStream(path.join(setting.tmpDataDir, src)))
- readStream.on('end', () => {
- zipfile.readEntry()
- })
- })
- })(src)
- }
- zipfile.readEntry()
- })
- zipfile.on('end', () => resolve(database.src))
- })
- })
- res.data.on('error', reject)
- })
- })
-}
-
-const sha256Hash = async (file) => {
- return new Promise((resolve, reject) => {
- const stream = fsSync.createReadStream(file)
- const hash = createHash('sha256')
- hash.once('finish', () => resolve(hash.digest('hex')))
- stream.on('error', reject)
- stream.pipe(hash)
- })
-}
-
-const createData = async (src) => {
- var mapDatas = []
- var locationSrc = src.filter(file => file.includes('Locations'))
- locationSrc.sort((a,b) => {
-
- if(a.endsWith('-en.csv')) return -1
- if(b.endsWith('-en.csv')) return 1
- })
- for(var file of locationSrc){
- mapDatas.push(await getMapData(file))
- }
- if(setting.locFile){
- minifyMapData(mapDatas)
- }
- var blockSrc = src.filter(file => file.includes('Blocks'))
- mapDatas.push([])
- for(var file of blockSrc){
- await createMainData(file, mapDatas)
- }
- if(setting.locFile){
- await createMapData(mapDatas)
- }
-}
-
-
-const createSmallMemoryFile = (ws, ipv4, line, buffer2, buffer3) => {
- const [ _dir, file, offset ] = getSmallMemoryFile(line, ipv4 ? setting.v4 : setting.v6, true)
- if(offset === 0){
- const dir = path.join(setting.fieldDir, _dir)
- if(ws) ws.end()
- if(file === '_0' && !fsSync.existsSync(dir)){
- fsSync.mkdirSync(dir, {recursive: true})
- }
- if(setting.smallMemoryFileSize <= buffer2.length + buffer3.length){
- var buf = Buffer.alloc(buffer2.length + buffer3.length)
- buffer2.copy(buf)
- buffer3.copy(buf, buffer2.length)
- fsSync.writeFile(path.join(dir, file), buf, () => {})
- return
- }
- ws = fsSync.createWriteStream(path.join(dir, file))
- }
- ws.write(buffer2)
- ws.write(buffer3)
- return ws
-}
-
-const createMainData = async (file, mapDatas) => {
- var ipv4 = file.endsWith('v4.csv')
- var ipv = ipv4 ? 4 : 6
- var rs = fsSync.createReadStream(path.join(setting.tmpDataDir, file))
- var ws1 = fsSync.createWriteStream(path.join(setting.fieldDir, ipv + '-1.dat.tmp'), {highWaterMark: 1024*1024})
- if(!setting.smallMemory){
- var ws2 = fsSync.createWriteStream(path.join(setting.fieldDir, ipv + '-2.dat.tmp'), {highWaterMark: 1024*1024})
- var ws3 = fsSync.createWriteStream(path.join(setting.fieldDir, ipv + '-3.dat.tmp'), {highWaterMark: 1024*1024})
- } else {
- var ws = null
- var dir = path.join(setting.fieldDir, 'v' + ipv + '-tmp')
- if(fsSync.existsSync(dir)){
- await fs.rm(dir, {recursive: true, force: true})
- }
- }
-
- var preBuffer1, preBuffer2, preBuffer3, preCC, preEnd
- var preLocId, preLatitude, preLongitude, preArea, prePostcode
-
- var preLocLocation
- var mapData0 = mapDatas[0], locIdList = mapDatas[mapDatas.length - 1]
- var lineCount = 0
- areaDatabase = {}, areaCount = 0
-
- return new Promise((resolve, reject) => {
- var checkCount = 0
- function check(){
- if(++checkCount === 3)resolve()
- }
- rs.pipe(parse({headers: true}))
- .on('error', reject)
- .on('data', row => {
- var cc, buffer1, buffer2, buffer3, addr, start, end
- if(setting.ipLocationDb){
- if(ipv4){
- start = aton4(row.network1)
- end = aton4(row.network2)
- } else {
- start = aton6(row.network1)
- end = aton6(row.network2)
- }
- } else {
- if(ipv4){
- addr = new Address4(row.network)
- start = aton4(addr.startAddress().correctForm())
- end = aton4(addr.endAddress().correctForm())
- } else {
- addr = new Address6(row.network)
- start = aton6(addr.startAddress().correctForm())
- end = aton6(addr.endAddress().correctForm())
- }
- }
-
- if(setting.isCountry){
- if(setting.ipLocationDb){
- cc = row.cc
- } else {
- cc = mapData0[row.geoname_id]
- }
- if(!cc || cc.length !== 2) {
- return;// console.warn('Invalid country code', cc, row.geoname_id)
- }
- if(cc === preCC && (ipv4 && preEnd + 1 === start || !ipv4 && preEnd + 1n === start)){
- if(ipv4){
- preBuffer2.writeUInt32LE(end)
- } else {
- preBuffer2.writeBigUInt64LE(end)
- }
- } else {
- if(ipv4){
- buffer1 = Buffer.allocUnsafe(4)
- buffer1.writeUInt32LE(start)
- buffer2 = Buffer.allocUnsafe(4)
- buffer2.writeUInt32LE(end)
- } else {
- buffer1 = Buffer.allocUnsafe(8)
- buffer1.writeBigUInt64LE(start)
- buffer2 = Buffer.allocUnsafe(8)
- buffer2.writeBigUInt64LE(end)
- }
- buffer3 = Buffer.allocUnsafe(2)
- buffer3.write(cc)
- if(preBuffer1){
- if(!ws1.write(preBuffer1)) rs.pause()
- if (setting.smallMemory) {
- ws = createSmallMemoryFile(ws, ipv4, lineCount++, preBuffer2, preBuffer3)
- } else {
- if(!ws2.write(preBuffer2)) rs.pause()
- if(!ws3.write(preBuffer3)) rs.pause()
- }
- }
- preCC = cc
- preBuffer1 = buffer1
- preBuffer2 = buffer2
- preBuffer3 = buffer3
- }
- } else {
- var locId = row.geoname_id
- var latitude = Math.round(row.latitude * 10000)
- var longitude = Math.round(row.longitude * 10000)
- var area = row.accuracy_radius
- var postcode = row.postal_code
-
-
- var locLocation = mapData0[locId] && mapData0[locId].counter
-
- var isSame = true
- if(setting.mainFieldHash.latitude && preLatitude !== latitude) isSame = false
- if(setting.mainFieldHash.longitude && preLongitude !== longitude) isSame = false
- if(setting.mainFieldHash.area && preArea !== area) isSame = false
- if(setting.mainFieldHash.postcode && prePostcode !== postcode) isSame = false
-
- if( (locId === preLocId || locLocation > 0 && locLocation === preLocLocation || setting.noLocFile)
- && isSame
-
- && (ipv4 && preEnd + 1 === start || !ipv4 && preEnd + 1n === start)){
- if(ipv4){
- preBuffer2.writeUInt32LE(parseInt(end, 10))
- } else {
- preBuffer2.writeBigUInt64LE(end)
- }
- } else {
- if(!locId){
- return;
-
-
-
-
-
- }
- if(locId && !mapData0[locId]) {
- return consoleWarn('Invalid location id', locId)
- }
- if(locId){
- if(!mapData0[locId].counter){
- locIdList.push(locId)
- locLocation = mapData0[locId].counter = locIdList.length
- }
- }
- if(preBuffer1){
- if(!ws1.write(preBuffer1)) rs.pause()
- if(setting.smallMemory){
- ws = createSmallMemoryFile(ws, ipv4, lineCount++, preBuffer2, preBuffer3)
- } else {
- if(!ws2.write(preBuffer2)) rs.pause()
- if(!ws3.write(preBuffer3)) rs.pause()
- }
- }
- if(ipv4){
- buffer1 = Buffer.allocUnsafe(4)
- buffer1.writeUInt32LE(parseInt(start, 10))
- buffer2 = Buffer.allocUnsafe(4)
- buffer2.writeUInt32LE(parseInt(end, 10))
- } else {
- buffer1 = Buffer.allocUnsafe(8)
- buffer1.writeBigUInt64LE(start)
- buffer2 = Buffer.allocUnsafe(8)
- buffer2.writeBigUInt64LE(end)
- }
-
- buffer3 = Buffer.alloc(setting.mainRecordSize)
-
- var offset = 0
- if(setting.locFile){
- buffer3.writeUInt32LE(mapData0[locId].counter)
- offset += 4
- }
- if(setting.mainFieldHash.latitude) {
- buffer3.writeInt32LE(latitude, offset)
- offset += 4
- }
- if(setting.mainFieldHash.longitude) {
- buffer3.writeInt32LE(longitude, offset)
- offset += 4
- }
- if(setting.mainFieldHash.postcode) {
- var postcodeDb = getPostcodeDatabase(postcode)
- buffer3.writeUInt32LE(postcodeDb[1], offset)
- buffer3.writeInt8(postcodeDb[0], offset + 4)
- offset += 5
- }
- if(setting.mainFieldHash.area) {
- buffer3.writeUInt8(makeAreaDatabase(area), offset)
- }
-
- preLocLocation = locLocation
- preLocId = locId
- preLatitude = latitude
- preLongitude = longitude
- preArea = area
-
- prePostcode = postcode
- preBuffer1 = buffer1
- preBuffer2 = buffer2
- preBuffer3 = buffer3
- }
- }
- preEnd = end
- })
- .on('pause', () => {
- ws1.once('drain', () => rs.resume())
- if(!setting.smallMemory){
- ws2.once('drain', () => rs.resume())
- ws3.once('drain', () => rs.resume())
- }
- })
- .on('end', () => {
- if(setting.smallMemory){
- ws = createSmallMemoryFile(ws, ipv4, lineCount, preBuffer2, preBuffer3)
- if(ws) ws.end(check)
- else ++checkCount
- ++checkCount
- } else {
- ws2.end(preBuffer2, check)
- ws3.end(preBuffer3, check)
- }
- ws1.end(preBuffer1, check)
- })
- })
-}
-
-const minifyMapData = (mapDatas) => {
- var mapData0 = mapDatas[0]
- if(setting.language !== 'en') {
- var mapData1 = mapDatas.splice(1, 1)[0]
- for(var locId in mapData0){
- if(mapData1 && mapData1[locId]){
- if(mapData1[locId].city_name) mapData0[locId].city_name = mapData1[locId].city_name
- if(mapData1[locId].subdivision_1_name) mapData0[locId].subdivision_1_name = mapData1[locId].subdivision_1_name
- if(mapData1[locId].subdivision_2_name) mapData0[locId].subdivision_2_name = mapData1[locId].subdivision_2_name
- }
- }
- }
-
- var locIds = Object.keys(mapData0), locFields = Object.keys(setting.locFieldHash).filter(v => setting.locFieldHash[v])
- locIds.sort((a,b) => a-b)
- const hash = {
- country: 'country_iso_code',
- region1: 'subdivision_1_iso_code',
- region1_name: 'subdivision_1_name',
- region2: 'subdivision_2_iso_code',
- region2_name: 'subdivision_2_name',
- city: 'city_name',
- metro: 'metro_code',
- timezone: 'time_zone'
- }
- const ranking = {
- country: 8,
- region1: 7,
- region1_name: 6,
- region2: 4,
- region2_name: 3,
- city: 9,
- metro: 1,
- timezone: 5
- }
- locFields.sort((a,b) => { return ranking[b] - ranking[a]})
- var checkFields = locFields.map(v => hash[v])
-
- var best1 = checkFields.shift()
- var listHash = {}
- for(var locId of locIds){
- var data = mapData0[locId]
- if(!listHash[data[best1]]){
- listHash[data[best1]] = []
- }
- listHash[data[best1]].push(locId)
- }
-
- var i, j, len, dataI, dataJ, locIdJ, tmpLocIds
- for(var key in listHash){
- tmpLocIds = listHash[key]
- for(i = 0, len = tmpLocIds.length; i < len; ++i){
- dataI = mapData0[tmpLocIds[i]]
- loopj: for(j = i+1; j < len; ++j){
- dataJ = mapData0[locIdJ = tmpLocIds[j]]
- for(var field of checkFields){
- if(dataI[field] !== dataJ[field]){
- continue loopj
- }
- }
- mapData0[locIdJ] = dataI
- tmpLocIds.splice(j, 1)
- --j
- --len
- }
- }
- }
-}
-
-const createMapData = async (mapDatas) => {
- var locIdList = mapDatas.pop()
- var mapData0 = mapDatas[0]
- var ws1 = fsSync.createWriteStream(path.join(setting.fieldDir, 'location.dat.tmp'))
- var ws2 = fsSync.createWriteStream(path.join(setting.fieldDir, 'name.dat.tmp'))
- var cityHash = {}, euHash = {}
- sub1Database = {}, sub2Database = {}, timezoneDatabase = {}
- sub1Count = 0, sub2Count = 0, timezoneCount = 0
-
- for(var locId of locIdList){
- var data0 = mapData0[locId]
-
- var cc = data0.country_iso_code
- var region1 = data0.subdivision_1_iso_code
- var region2 = data0.subdivision_2_iso_code
- var timezone = data0.time_zone
- var metro = data0.metro_code
-
- var region1_name = data0.subdivision_1_name
- var region2_name = data0.subdivision_2_name
- var city = data0.city_name
-
- var offset = 0
- var b = Buffer.alloc(setting.locRecordSize)
- if(setting.locFieldHash.country){
- if(cc && cc.length === 2) {
- b.write(cc, offset); //country code [2 bytes]
- if(setting.locFieldHash.eu && data0.is_in_european_union == 1){
- euHash[cc] = true
- }
- }
- offset += 2
- }
- if(setting.locFieldHash.region1){
- if(region1) b.writeUInt16LE(strToNum37(region1), offset) // subdivision code [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.region1_name){
- if(region1_name) b.writeUInt16LE(makeSub1Database(region1_name), offset) // subdivision name index [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.region2){
- if(region2) b.writeUInt16LE(strToNum37(region2), offset) // subdivision code [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.region2_name){
- if(region2_name) b.writeUInt16LE(makeSub2Database(region2_name), offset) // subdivision name index [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.metro){
- if(metro) b.writeUInt16LE(metro, offset) // metro code [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.timezone){
- if(timezone) b.writeUInt16LE(makeTimezoneDatabase(timezone), offset)// timezone [2 byte]
- offset += 2
- }
- if(setting.locFieldHash.city){
- if(city){
- b.writeUInt32LE(inputBuffer(cityHash, ws2, city), offset) // cityname index [4 bytes]
- }
- }
- ws1.write(b)
- }
- ws1.end()
- ws2.end()
-
- var hash = {
- region1_name: DatabaseToArray(sub1Database),
- region2_name: DatabaseToArray(sub2Database),
- timezone: DatabaseToArray(timezoneDatabase),
- area: DatabaseToArray(areaDatabase).map(v => parseInt(v, 10)||0),
- eu: euHash
- }
- if(!setting.locFieldHash.region1_name) delete hash.region1_name
- if(!setting.locFieldHash.region2_name) delete hash.region2_name
- if(!setting.locFieldHash.timezone) delete hash.timezone
- if(!setting.mainFieldHash.area) delete hash.area
- if(!setting.locFieldHash.eu) delete hash.eu
- if(Object.keys(hash).length > 0){
- await fs.writeFile(path.join(setting.fieldDir, 'sub.json.tmp'), JSON.stringify(hash))
- }
- sub1Database = sub2Database = timezoneDatabase = areaDatabase = null
- mapDatas.length = 0
-}
-
-const DatabaseToArray = (database) => {
- var arr = ['']
- for(var key in database){
- arr[database[key]] = key
- }
- return arr
-}
-
-const inputBuffer = (hash, dataFile, text) => {
- if(hash[text]) return hash[text]
- if(hash.__offsetBB === undefined) {
- var b = Buffer.alloc(1)
- dataFile.write(b)
- hash.__offsetBB = 1
- }
- var offset = hash.__offsetBB
- var b = Buffer.from(text)
- var n = b.length + (offset << 8)
- dataFile.write(b)
- hash.__offsetBB = offset + b.length
- return hash[text] = n
-}
-
-var sub1Database = {}, sub2Database = {}, timezoneDatabase = {}, areaDatabase = {}
-var sub1Count = 0, sub2Count = 0, timezoneCount = 0, areaCount = 0
-const makeSub1Database = (sub1) => {
- if(sub1Database[sub1]) return sub1Database[sub1]
- return sub1Database[sub1] = ++sub1Count
-}
-const makeSub2Database = (sub2) => {
- if(sub2Database[sub2]) return sub2Database[sub2]
- return sub2Database[sub2] = ++sub2Count
-}
-const makeTimezoneDatabase = (tz) => {
- if(timezoneDatabase[tz]) return timezoneDatabase[tz]
- return timezoneDatabase[tz] = ++timezoneCount
-}
-const makeAreaDatabase = (area) => {
- if(areaDatabase[area]) return areaDatabase[area]
- return areaDatabase[area] = ++areaCount
-}
-
-const getMapData = async (file) => {
- const rs = fsSync.createReadStream(path.join(setting.tmpDataDir, file))
- const result = {}
- return new Promise((resolve, reject) => {
- rs.pipe(parse({headers: true}))
- .on('error', reject)
- .on('data', row => {
- if(setting.isCountry){
- result[row.geoname_id] = row.country_iso_code
- } else {
- result[row.geoname_id] = row
- }
- })
- .on('end', () => resolve(result))
- })
-}
-
-module.exports={update:update}
\ No newline at end of file
diff --git a/cjs/main.cjs b/cjs/main.cjs
deleted file mode 100644
index e01c37a..0000000
--- a/cjs/main.cjs
+++ /dev/null
@@ -1,500 +0,0 @@
-
-const fs = require('fs/promises')
-const fsSync = require('fs')
-const path = require('path')
-const { exec, execSync } = require('child_process')
-
-const { countries, continents } = require('countries-list')
-const { CronJob } = require('cron')
-
-const { setting, setSetting, getSettingCmd, consoleLog, consoleWarn } = require('./setting.cjs')
-const { num37ToStr, getSmallMemoryFile, getZeroFill, aton6Start, aton4 } = require('./utils.cjs')
-
-const v4db = setting.v4
-const v6db = setting.v6
-const locFieldHash = setting.locFieldHash
-const mainFieldHash = setting.mainFieldHash
-
-
-
-/**
- * lookup ip address
- * @type {function}
- * @param {string} ip - ipv4 or ipv6 formatted address
- * @return {object|null|Promise} location information
- */
-const lookup = (ip) => {
-
-
-
- var isIpv6
- if(ip.includes(':')){
- ip = aton6Start(ip)
- isIpv6 = ip.constructor === BigInt
- } else {
- ip = aton4(ip)
- isIpv6 = false
- }
- const db = isIpv6 ? v6db : v4db
- if(!(ip >= db.firstIp)) return null
- const list = db.startIps
- var fline = 0, cline = db.lastLine, line
- for(;;){
- line = fline + cline >> 1
- if(ip < list[line]){
- if(cline - fline < 2) return null
- cline = line - 1
- } else {
- if(fline === line) {
- if(cline !== line && ip >= list[cline]) {
- line = cline
- }
- break
- }
- fline = line
- }
- }
-
- if(setting.smallMemory){
-
- return lineToFile(line, db).then(buffer => {
- var endIp = isIpv6 ? buffer.readBigUInt64LE(0) : buffer.readUInt32LE(0)
- if(ip > endIp) return null
- if(setting.isCountry){
- return setCountryInfo({
- country: buffer.toString('latin1', isIpv6 ? 8 : 4, isIpv6 ? 10 : 6)
- })
- }
- return setCityRecord(buffer, {}, isIpv6 ? 8 : 4)
- })
- }
- if(ip > db.endIps[line]) return null
- if(setting.isCountry){
- return setCountryInfo({
- country: db.mainBuffer.toString('latin1', line * db.recordSize, line * db.recordSize + 2)
- })
- }
- return setCityRecord(db.mainBuffer, {}, line * db.recordSize)
-}
-
-/**
- * setup database without reload
- * @param {object} [_setting]
- * @return {void}
- */
-const setupWithoutReload = setSetting
-
-/**
- * clear in-memory database
- * @type {function}
- * @return {void}
- */
-const clear = () => {
- v4db.startIps = v6db.startIps = v4db.endIps = v6db.endIps = v4db.mainBuffer = v6db.mainBuffer = null
- Region1NameJson = Region2NameJson = TimezoneJson = LocBuffer = CityNameBuffer = EuJson = null
-}
-
-var Region1NameJson, Region2NameJson, TimezoneJson, LocBuffer, CityNameBuffer, AreaJson, EuJson
-var updateJob
-/**
- * reload in-memory database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [sync] - sync mode
- * @param {boolean} [_runningUpdate] - if it's running update [internal use]
- * @return {Promise|void}
- */
-const reload = async (_setting, sync, _runningUpdate) => {
- var curSetting = setting
- if(_setting){
- var oldSetting = Object.assign({}, setting)
- setSetting(_setting)
- curSetting = Object.assign({}, setting)
- Object.assign(setting, oldSetting)
- }
- const dataDir = curSetting.fieldDir
- const v4 = v4db, v6 = v6db
- var dataFiles = {
- v41: path.join(dataDir, '4-1.dat'),
- v42: path.join(dataDir, '4-2.dat'),
- v43: path.join(dataDir, '4-3.dat'),
- v61: path.join(dataDir, '6-1.dat'),
- v62: path.join(dataDir, '6-2.dat'),
- v63: path.join(dataDir, '6-3.dat'),
- cityLocation: path.join(dataDir, 'location.dat'),
- cityName: path.join(dataDir, 'name.dat'),
- citySub: path.join(dataDir, 'sub.json')
- }
-
- var locBuffer, cityNameBuffer, subBuffer
- var buffer41, buffer42, buffer43, buffer61, buffer62, buffer63
- var testDir = dataDir
- if(curSetting.smallMemory){
- testDir = path.join(testDir, 'v4')
- }
-
- if(sync){
- if(!fsSync.existsSync(testDir)){
- consoleLog('Database creating ...')
- updateDb(_setting && curSetting, true, true)
- consoleLog('Database created')
- }
- buffer41 = fsSync.readFileSync(dataFiles.v41)
- buffer61 = fsSync.readFileSync(dataFiles.v61)
- if(!curSetting.smallMemory){
- buffer42 = fsSync.readFileSync(dataFiles.v42)
- buffer43 = fsSync.readFileSync(dataFiles.v43)
- buffer62 = fsSync.readFileSync(dataFiles.v62)
- buffer63 = fsSync.readFileSync(dataFiles.v63)
- }
- if(curSetting.locFile){
- locBuffer = fsSync.readFileSync(dataFiles.cityLocation)
- if(locFieldHash.city){
- cityNameBuffer = fsSync.readFileSync(dataFiles.cityName)
- }
- if(locFieldHash.region1_name || locFieldHash.region2_name || locFieldHash.timezone || mainFieldHash.area || locFieldHash.eu){
- subBuffer = fsSync.readFileSync(dataFiles.citySub)
- }
- }
- } else {
- if(!fsSync.existsSync(testDir)){
- consoleLog('Database creating ...')
- await updateDb(_setting && curSetting, true)
- consoleLog('Database created')
- }
- var prs = [
- fs.readFile(dataFiles.v41).then(data => buffer41 = data),
- fs.readFile(dataFiles.v61).then(data => buffer61 = data),
- ]
- if(!curSetting.smallMemory){
- prs.push(
- fs.readFile(dataFiles.v42).then(data => buffer42 = data),
- fs.readFile(dataFiles.v43).then(data => buffer43 = data),
- fs.readFile(dataFiles.v62).then(data => buffer62 = data),
- fs.readFile(dataFiles.v63).then(data => buffer63 = data)
- )
- }
- if(curSetting.locFile){
- prs.push(fs.readFile(dataFiles.cityLocation).then(data => locBuffer = data))
- if(locFieldHash.city){
- prs.push(fs.readFile(dataFiles.cityName).then(data => cityNameBuffer = data))
- }
- if(locFieldHash.region1_name || locFieldHash.region2_name || locFieldHash.timezone || mainFieldHash.area || locFieldHash.eu){
- prs.push(fs.readFile(dataFiles.citySub).then(data => subBuffer = data))
- }
- }
- await Promise.all(prs)
- }
-
- if(_setting){
- Object.assign(setting, curSetting)
- }
-
- v4.startIps = new Uint32Array(buffer41.buffer, 0, buffer41.byteLength >> 2)
- v6.startIps = new BigUint64Array(buffer61.buffer, 0, buffer61.byteLength >> 3)
- if(!curSetting.smallMemory){
- v4.endIps = new Uint32Array(buffer42.buffer, 0, buffer42.byteLength >> 2)
- v4.mainBuffer = buffer43
- v6.endIps = new BigUint64Array(buffer62.buffer, 0, buffer62.byteLength >> 3)
- v6.mainBuffer = buffer63
- }
-
- v4.lastLine = v4.startIps.length - 1
- v6.lastLine = v6.startIps.length - 1
- v4.firstIp = v4.startIps[0]
- v6.firstIp = v6.startIps[0]
- if(curSetting.isCity){
- LocBuffer = locBuffer
- CityNameBuffer = cityNameBuffer
- if(subBuffer){
- var tmpJson = JSON.parse(subBuffer)
- if(locFieldHash.region1_name) Region1NameJson = tmpJson.region1_name
- if(locFieldHash.region2_name) Region2NameJson = tmpJson.region2_name
- if(locFieldHash.timezone) TimezoneJson = tmpJson.timezone
- if(mainFieldHash.area) AreaJson = tmpJson.area
- if(locFieldHash.eu) EuJson = tmpJson.eu
- }
- }
-
-
- if(setting.smallMemory && _runningUpdate){
- const rimraf = (dir) => {
- if(fs.rm){
- return fs.rm(dir, {recursive: true, force: true, maxRetries: 3})
- }
- return fs.rmdir(dir, {recursive: true, maxRetries: 3})
- }
- fsSync.cpSync(path.join(setting.fieldDir, 'v4-tmp'), path.join(setting.fieldDir, 'v4'), {recursive: true, force: true})
- fsSync.cpSync(path.join(setting.fieldDir, 'v6-tmp'), path.join(setting.fieldDir, 'v6'), {recursive: true, force: true})
- rimraf(path.join(setting.fieldDir, 'v4-tmp')).catch(consoleWarn)
- rimraf(path.join(setting.fieldDir, 'v6-tmp')).catch(consoleWarn)
- }
-
- if(!updateJob && setting.autoUpdate){
- updateJob = new CronJob(setting.autoUpdate, () => {
- updateDb().finally(() => {})
- }, null, true, 'UTC')
- } else if(updateJob && !setting.autoUpdate){
- updateJob.stop()
- updateJob = null
- }
-}
-
-const watchHash = {}
-/**
- * Watch database directory.
- * When database file is updated, it reload the database automatically
- * This causes error if you use ILA_SMALL_MEMORY=true
- * @type {function}
- * @param {string} [name] - name of watch. If you want to watch multiple directories, you can set different name for each directory
- */
-const watchDb = (name = 'ILA') => {
- var watchId = null
- watchHash[name] = fsSync.watch(setting.fieldDir, (eventType, filename) => {
- if(!filename.endsWith('.dat')) return;
- if(fsSync.existsSync(path.join(setting.fieldDir, filename))) {
- if(watchId) clearTimeout(watchId)
- watchId = setTimeout(reload, 30 * 1000)
- }
- })
-}
-
-/**
- * Stop watching database directory
- * @type {function}
- * @param {string} [name]
- */
-const stopWatchDb = (name = 'ILA') => {
- if(watchHash[name]){
- watchHash[name].close()
- delete watchHash[name]
- }
-}
-
-/**
- * Update database and auto reload database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [noReload] - if you don't want to reload the database after update
- * @param {boolean} [sync] - if you want to update the database in sync mode
- * @return {Promise} - true if database is updated, false if no need to update
- */
-const updateDb = (_setting, noReload, sync) => {
-
-
-
- var arg, runningUpdate = false
- if(_setting){
- var oldSetting = Object.assign({}, setting)
- setSetting(_setting)
- arg = getSettingCmd()
- Object.assign(setting, oldSetting)
- } else {
- arg = getSettingCmd()
- }
-
- var scriptPath = path.resolve(_setting ? _setting.apiDir : setting.apiDir, 'script', 'updatedb.mjs')
- if(scriptPath.includes(' ')) scriptPath = '"' + scriptPath + '"'
- var cmd = 'node ' + scriptPath
- if(!_setting){
- arg += ' ILA_SAME_DB_SETTING=true'
- }
- if(_setting && _setting.smallmemory || !_setting && setting.smallMemory){
- runningUpdate = true
- arg += ' ILA_RUNNING_UPDATE=true'
- }
-
- if(arg){
- cmd += ' ' + arg
- }
- if(sync){
- try{
- var stdout = execSync(cmd)
- if(stdout.includes('NO NEED TO UPDATE')){
- return true
- }
- if(stdout.includes('SUCCESS TO UPDATE')){
- if(!noReload){
- reload(_setting, sync)
- }
- return true
- }
- return false
- }catch(e){
- consoleWarn(e)
- return false
- }
- }
- return new Promise((resolve, reject) => {
- exec(cmd, (err, stdout, stderr) => {
- if(err) {
- consoleWarn(err)
- }
- if(stderr) {
- consoleWarn(stderr)
- }
- if(stdout) {
- consoleLog(stdout)
- }
- if(err) {
- reject(err)
- } else if(stdout.includes('ERROR TO UPDATE')){
- reject(new Error('ERROR TO UPDATE'))
- } else if(stdout.includes('NO NEED TO UPDATE')){
- resolve(false)
- } else if(stdout.includes('SUCCESS TO UPDATE')){
- if(noReload){
- resolve(true)
- } else {
- reload(_setting, false, runningUpdate).then(() => {
- resolve(true)
- }).catch(reject)
- }
- } else {
- consoleLog('UNKNOWN ERROR')
- reject(new Error('UNKNOWN ERROR'))
- }
- })
- })
-}
-
-/*
--- Remain this code for better performance check
-const lineToFile = (line, db) => {
- const [ dir, file, offset ] = getSmallMemoryFile(line, db)
- return new Promise((resolve, reject) => {
-
-
-
-
-
-
-
- fs.open(path.join(dir, file), 'r').then(fd => {
- const buffer = Buffer.alloc(db.recordSize)
- fd.read(buffer, 0, db.recordSize, offset).then(() => {
- fd.close().catch(reject)
- resolve(buffer)
- }).catch(reject)
- }).catch(reject)
- })
-}
-*/
-const lineToFile = async (line, db) => {
- const [ dir, file, offset ] = getSmallMemoryFile(line, db)
- const fd = await fs.open(path.join(setting.fieldDir, dir, file), 'r')
- const buffer = Buffer.alloc(db.recordSize)
- await fd.read(buffer, 0, db.recordSize, offset)
- fd.close().catch(consoleWarn)
- return buffer
-}
-
-const setCityRecord = (buffer, geodata, offset) => {
- var locId
- if(setting.locFile){
- locId = buffer.readUInt32LE(offset)
- offset += 4
- }
- if(mainFieldHash.latitude){
- geodata.latitude = buffer.readInt32LE(offset) / 10000
- offset += 4
- }
- if(mainFieldHash.longitude){
- geodata.longitude = buffer.readInt32LE(offset) / 10000
- offset += 4
- }
- if(mainFieldHash.postcode){
- var postcode2 = buffer.readUInt32LE(offset)
- var postcode1 = buffer.readInt8(offset + 4)
- if (postcode2) {
- var postcode, tmp
- if(postcode1 < -9){
- tmp = (-postcode1).toString()
- postcode = postcode2.toString(36)
- postcode = getZeroFill(postcode.slice(0, -tmp[1]), tmp[0]-0) + '-' + getZeroFill(postcode.slice(-tmp[1]), tmp[1]-0)
- } else if(postcode1 < 0){
- postcode = getZeroFill(postcode2.toString(36), -postcode1)
- } else if(postcode1 < 10){
- postcode = getZeroFill(postcode2.toString(10), postcode1)
- } else if(postcode1 < 72){
- postcode1 = String(postcode1)
- postcode = getZeroFill(postcode2.toString(10), (postcode1[0]-0) + (postcode1[1]-0))
- postcode = postcode.slice(0, postcode1[0]-0) + '-' + postcode.slice(postcode1[0]-0)
- } else {
- postcode = postcode1.toString(36).slice(1) + postcode2.toString(36)
- }
- geodata.postcode = postcode.toUpperCase()
- }
- offset += 5
- }
- if(mainFieldHash.area){
- geodata.area = AreaJson[buffer.readUInt8(offset)]
- offset += 1
- }
-
- if(locId){
- var locOffset = (locId-1) * setting.locRecordSize
- if(locFieldHash.country){
- geodata.country = LocBuffer.toString('utf8', locOffset, locOffset += 2)
- if(locFieldHash.eu){
- geodata.eu = EuJson[geodata.country]
- }
- }
- if(locFieldHash.region1){
- var region1 = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region1 > 0) geodata.region1 = num37ToStr(region1)
- }
- if(locFieldHash.region1_name){
- var region1_name = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region1_name > 0) geodata.region1_name = Region1NameJson[region1_name]
- }
- if(locFieldHash.region2){
- var region2 = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region2 > 0) geodata.region2 = num37ToStr(region2)
- }
- if(locFieldHash.region2_name){
- var region2_name = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region2_name > 0) geodata.region2_name = Region2NameJson[region2_name]
- }
- if(locFieldHash.metro){
- var metro = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(metro > 0) geodata.metro = metro
- }
- if(locFieldHash.timezone){
- var timezone = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(timezone > 0) geodata.timezone = TimezoneJson[timezone]
- }
- if(locFieldHash.city){
- var city = LocBuffer.readUInt32LE(locOffset)
- locOffset += 4
- if(city > 0){
- var start = city >>> 8
- geodata.city = CityNameBuffer.toString('utf8', start, start + (city & 255))
- }
- }
- }
- return setCountryInfo(geodata)
-}
-const setCountryInfo = (geodata) => {
- if(setting.addCountryInfo){
- var h = countries[geodata.country]
- geodata.country_name = h.name
- geodata.country_native = h.native
- geodata.continent = h.continent
- geodata.continent_name = continents[h.continent]
- geodata.capital = h.capital
- geodata.phone = h.phone
- geodata.currency = h.currency
- geodata.languages = h.languages
- }
- return geodata
-}
-
reload(undefined, true)
-
-module.exports={lookup:lookup,setupWithoutReload:setupWithoutReload,clear:clear,reload:reload,watchDb:watchDb,stopWatchDb:stopWatchDb,updateDb:updateDb}
\ No newline at end of file
diff --git a/cjs/setting.cjs b/cjs/setting.cjs
deleted file mode 100644
index bb8cff5..0000000
--- a/cjs/setting.cjs
+++ /dev/null
@@ -1,179 +0,0 @@
-
-const path = require('path')
-
-const { fileURLToPath } = require('url')
-const { getFieldsSize } = require('./utils.cjs')
-
-const defaultSetting = {
-
- fields: ['country'],
- dataDir: '../data/',
- tmpDataDir: '../tmp/',
- apiDir: '..',
-
- smallMemory: false,
- smallMemoryFileSize: 4096,
-
- addCountryInfo: false,
-
- licenseKey: 'redist',
- ipLocationDb: '',
- downloadType: 'reuse',
- series: 'GeoLite2', // or GeoIP2
- language: 'en',
- fakeData: false,
- autoUpdate: 'default',
-
- sameDbSetting: false,
- multiDbDir: false,
-
- browserType: false,
- silent: false,
-}
-
-const setting = {
- v4: {ipv4: true, ipv6: false, name: 'v4'},
- v6: {ipv4: false, ipv6: true, name: 'v6'},
- mainFieldHash: {},
- locFieldHash: {},
-}
-
-
-const mainFields = ['latitude', 'longitude', 'area', 'postcode']
-const locFields = ['country', 'region1', 'region1_name', 'region2', 'region2_name', 'metro', 'timezone', 'city', 'eu']
-
-const shortNumber = {
- latitude: 1,
- longitude: 2,
- area: 4,
- postcode: 8,
- country: 16,
- region1: 32,
- region1_name: 64,
- region2: 128,
- region2_name: 256,
- metro: 512,
- timezone: 1024,
- city: 2048,
- eu: 4096
-}
-
-const make_key = (key) => {
- return 'ILA_' + key.replace(/([A-Z])/g, char => '_' + char).toUpperCase()
-}
-
-const consoleLog = (...args) => {
- if(setting.silent) return
- console.log(...args)
-}
-
-const consoleWarn = (...args) => {
- if(setting.silent) return
- console.warn(...args)
-}
-
-const getSettingCmd = () => {
- const ret = []
- for(const key in defaultSetting){
- if(setting[key] && setting[key] !== defaultSetting[key]){
- var value = String(setting[key])
- if(value.includes(' ')) value = '"' + value + '"'
- ret.push(make_key(key) + '=' + value)
- }
- }
- return ret.join(' ')
-}
-
-const inputSetting = {}
-var settingKeys = Object.keys(defaultSetting)
-for(var env in process.env){
- for(var key of settingKeys){
- if(env.toUpperCase() === make_key(key)){
- inputSetting[key] = process.env[env]
- }
- }
-}
-for(var arg of process.argv){
- var v = arg.toUpperCase()
- for(var key of settingKeys){
- if(v.includes(make_key(key) + '=')){
- inputSetting[key] = arg.split('=')[1]
- }
- }
-}
-
-const NumReg = /^\d+$/
-const setSetting = (_setting = {}) => {
- for(var key in _setting){
- var value = setting[key] = _setting[key]
- if(value === "false") setting[key] = false
- else if(value === "true") setting[key] = true
- else if(NumReg.test(value)) setting[key] = parseInt(value)
- }
-
- if(setting.autoUpdate === 'default'){
- setting.autoUpdate = Math.floor(Math.random()*59.9) + ' ' + Math.floor(Math.random()*59.9) + ' 0 * * wed,sat'
- }
-
- const windowsDriveReg = /^[a-zA-Z]:\\/
- if(!setting.dataDir.startsWith('/') && !setting.dataDir.startsWith('\\\\') && !windowsDriveReg.test(setting.dataDir)){
- setting.dataDir = path.resolve(__dirname, setting.dataDir)
- }
- if(!setting.tmpDataDir.startsWith('/') && !setting.tmpDataDir.startsWith('\\\\') && !windowsDriveReg.test(setting.tmpDataDir)){
- setting.tmpDataDir = path.resolve(__dirname, setting.tmpDataDir)
- }
- if(!setting.apiDir.startsWith('/') && !setting.apiDir.startsWith('\\\\') && !windowsDriveReg.test(setting.apiDir)){
- setting.apiDir = path.resolve(__dirname, setting.apiDir)
- }
-
- if(typeof setting.fields === 'string'){
- setting.fields = setting.fields.split(/\s*,\s*/)
- }
- if(setting.fields.includes('all')) {
- setting.fields = mainFields.concat(locFields)
- } else {
- setting.fields = setting.fields.filter(v => mainFields.includes(v) || locFields.includes(v))
- }
-
- if(setting.fields.length === 1 && setting.fields[0] === 'country'){
- setting.dataType = 'country'
- } else {
- setting.dataType = 'city'
- }
- setting.isCountry = setting.dataType === 'country'
- setting.isCity = !setting.isCountry
-
- for(var field of mainFields){
- setting.mainFieldHash[field] = setting.fields.includes(field)
- }
-
- setting.noLocFile = true
- for(var field of locFields){
- setting.locFieldHash[field] = setting.fields.includes(field)
- if(setting.locFieldHash[field]){
- setting.noLocFile = false
- }
- }
- if(setting.isCountry) setting.noLocFile = true
- setting.locFile = !setting.noLocFile
-
- setting.fieldDir = path.join(setting.dataDir, setting.fields.reduce((sum, v) => sum + shortNumber[v], 0).toString(36))
-
- var mainRecordSize = setting.isCountry ? 2 : getFieldsSize(setting.fields.filter(v => mainFields.includes(v)))
- if(setting.locFile) mainRecordSize += 4
- setting.v4.recordSize = setting.v6.recordSize = setting.mainRecordSize = mainRecordSize
- setting.locRecordSize = getFieldsSize(setting.fields.filter(v => locFields.includes(v)))
- if(setting.smallMemory){
- setting.v4.recordSize += 4
- setting.v6.recordSize += 8
- setting.v4.fileLineMax = (setting.smallMemoryFileSize / setting.v4.recordSize | 0) || 1
- setting.v6.fileLineMax = (setting.smallMemoryFileSize / setting.v6.recordSize | 0) || 1
- setting.fileMax = 1024
- setting.v4.folderLineMax = setting.v4.fileLineMax * setting.fileMax
- setting.v6.folderLineMax = setting.v6.fileLineMax * setting.fileMax
- }
-}
-
-setSetting(Object.assign({}, defaultSetting, inputSetting))
-
-module.exports={setting:setting,consoleLog:consoleLog,consoleWarn:consoleWarn,getSettingCmd:getSettingCmd,setSetting:setSetting}
\ No newline at end of file
diff --git a/cjs/utils.cjs b/cjs/utils.cjs
deleted file mode 100644
index 1d32e1a..0000000
--- a/cjs/utils.cjs
+++ /dev/null
@@ -1,199 +0,0 @@
-
-const path = require('path')
-
-
-const countryCodeToNum = (code) => { // 0~675
- code = code.toUpperCase()
- return (code.charCodeAt(0)-65)*26 + (code.charCodeAt(1)-65)
-}
-const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
-}
-
-const getFieldsSize = (types) => {
- var size = 0
- for (const type of types) {
- switch (type) {
- case 'postcode':
- size += 5
- break
- case 'area':
- size += 1
- break
- case 'latitude':
- case 'longitude':
- case 'city':
- size += 4
- break
- case 'eu':
- break
- default:
- size += 2
- break
- }
- }
- return size
-}
-
-const ntoa4 = (n) => {
- return [n >>> 24, n >> 16 & 255, n >> 8 & 255, n & 255].join('.')
-}
-
-const aton4 = (a) => {
- a = a.split(/\./)
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-}
-
-const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/)
- const l = a.length - 1
- var i, r = 0n
- if (l < 7) {
- const omitStart = a.indexOf('')
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i))
- }
- return r
-}
-
-const aton6 = (a) => {
- a = a.replace(/"/g, '').split(/:/)
-
- const l = a.length - 1
- var i
- if (a[l] === '') a[l] = 0
- if (l < 7) {
- const omitted = 8 - a.length, omitStart = a.indexOf(''), omitEnd = omitStart + omitted
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0
- }
- }
-
- var r = 0n
- for (i = 0; i < 4; i++) {
- if (a[i]) {
- r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i))
- }
- }
- return r
-}
-
-const v4MappedReg = /^(?:0:0:0:0:0|:):ffff:(\d+\.\d+\.\d+\.\d+)$/i
-const v4Mapped = (addr) => {
- const match = v4MappedReg.exec(addr)
- return match && match[1]
-}
-
-const PrivateIpRegList = [
- /^10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^192\.168\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^172\.16\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^169\.254\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^fc00:/,
- /^fe80:/
-]
-
-const isPrivateIP = function(addr) {
- for(const reg of PrivateIpRegList){
- if(reg.test(addr)){
- return true
- }
- }
- return false
-}
-
-const strToNum37 = (a) => {
- var num = 0
- for(var i = 0; i < a.length; i++){
- num = num * 37 + parseInt(a[i], 36) + 1
- }
- return num
-}
-
-const num37ToStr = (num) => {
- var str = ''
- while(num > 0){
- str = (num % 37 - 1).toString(36) + str
- num = Math.floor(num / 37)
- }
- return str.toUpperCase()
-}
-
-const getZeroFill = (num, len) => {
- return '0'.repeat(len - num.length) + num
-}
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-}
-const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-}
-const getSmallMemoryFile = (line, db, isTmp) => {
- const dbNumber = line / db.folderLineMax | 0
- const fileNumber = (line - dbNumber * db.folderLineMax) / db.fileLineMax | 0
- const lineOffset = line - dbNumber * db.folderLineMax - fileNumber * db.fileLineMax
- var dir = path.join(db.name + (isTmp ? '-tmp' : ''), getUnderberFill(dbNumber.toString(36), 2))
- return [dir, getUnderberFill(fileNumber.toString(36), 2), lineOffset * db.recordSize]
-}
-
-const isPostNumReg = /^\d+$/
-const isPostNumReg2 = /^(\d+)[-\s](\d+)$/
-const isPostStrReg = /^([A-Z\d]+)$/
-const isPostStrReg2 = /^([A-Z\d]+)[-\s]([A-Z\d]+)$/
-const getPostcodeDatabase = (postcode) => {
- if(!postcode) return [0, 0];
-
- if(isPostNumReg.test(postcode)){
- return [
- postcode.length, // 1~9
- parseInt(postcode, 10) // 0~999999999
- ]
- }
- var r = isPostNumReg2.exec(postcode)
- if(r){
- return [
- parseInt(r[1].length + '' + r[2].length, 10), // 11~66
- parseInt(r[1] + r[2], 10) // 0~999999999
- ]
- }
-
- r = isPostStrReg.exec(postcode)
- if(r){
- var num = parseInt(postcode, 36)
- if(num < Math.pow(2, 32)){
- return [
- -postcode.length, // -1~-9
- num
- ]
- } else {
- return [
- parseInt('2' + postcode.slice(0, 1), 36), // 72~107,
- parseInt(postcode.slice(1), 36) // 0~2176782335 MAX: 6char ZZZZZZ
- ]
- }
- }
-
- r = isPostStrReg2.exec(postcode)
- if(!r){
- console.log('Invalid postcode:', postcode)
- }
- return [
- - parseInt(r[1].length + "" + r[2].length, 10),// -11~-55
- parseInt(r[1] + r[2], 36) // 0~2176782335 MAX: 6char ZZZZZZ
- ]
-}
-
-module.exports={countryCodeToNum:countryCodeToNum,numToCountryCode:numToCountryCode,getFieldsSize:getFieldsSize,ntoa4:ntoa4,aton4:aton4,aton6Start:aton6Start,aton6:aton6,v4Mapped:v4Mapped,isPrivateIP:isPrivateIP,strToNum37:strToNum37,num37ToStr:num37ToStr,getZeroFill:getZeroFill,numberToDir:numberToDir,getSmallMemoryFile:getSmallMemoryFile,getPostcodeDatabase:getPostcodeDatabase}
\ No newline at end of file
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..26cd634
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,8 @@
+import antfu from '@antfu/eslint-config'
+
+export default antfu(
+ {
+ formatters: true,
+ typescript: true,
+ },
+)
diff --git a/package.json b/package.json
index 355ff05..f9e9d66 100644
--- a/package.json
+++ b/package.json
@@ -1,130 +1,43 @@
{
- "name": "ip-location-api",
- "version": "2.3.3",
- "description": "Fast location lookup from IP address",
- "keywords": [
- "location",
- "lookup",
- "geo",
- "geoip",
- "geolite",
- "maxmind",
- "ip",
- "ipv4",
- "ipv6",
- "ip-location-db",
- "country",
- "city"
- ],
- "scripts": {
- "test": "npx jasmine",
- "updatedb": "node script/updatedb.mjs",
- "updatedb-browser-country": "node script/updatedb.mjs ILA_BROWSER_TYPE=country ILA_IP_LOCATION_DB=geo-whois-asn",
- "updatedb-browser-geocode": "node script/updatedb.mjs ILA_BROWSER_TYPE=geocode ILA_FIELDS=latitude,longitude",
- "browser": "npx http-server browser -p 8321",
- "cjs": "node script/convert-to-cjs.mjs",
- "ts": "npx -p typescript tsc src/main.mjs cjs/main.cjs --declaration --allowJs --emitDeclarationOnly --skipLibCheck --outDir types",
- "release": "release-it"
- },
- "author": "sapics",
- "homepage": "https://github.com/sapics/ip-location-api",
- "repository": {
- "type": "git",
- "url": "git://github.com/sapics/ip-location-api.git"
- },
- "license": "Multiple licenses",
"type": "module",
+ "private": true,
+ "packageManager": "pnpm@9.11.0",
"engines": {
- "node": ">=14.8.0"
+ "node": ">=14.8.0",
+ "pnpm": ">=9.11.0"
},
- "main": "cjs/main.cjs",
- "types": "types/cjs/main.d.cts",
- "module": "src/main.mjs",
- "exports": {
- ".": {
- "import": {
- "types": "./types/src/main.d.mts",
- "default": "./src/main.mjs"
- },
- "require": {
- "types": "./types/cjs/main.d.cts",
- "default": "./cjs/main.cjs"
- },
- "default": {
- "types": "./types/cjs/main.d.cts",
- "default": "./cjs/main.cjs"
- }
- },
- "./pack": {
- "import": {
- "types": "./types/src/main.d.mts",
- "default": "./src/main-pack.mjs"
- },
- "default": {
- "types": "./types/src/main.d.mts",
- "default": "./src/main-pack.mjs"
- }
- },
- "./country": {
- "import": "./browser/country/lookup.mjs",
- "require": "./browser/country/lookup.cjs",
- "default": "./browser/country/lookup.cjs"
- },
- "./country-extra": {
- "import": "./browser/country-extra/lookup.mjs",
- "require": "./browser/country-extra/lookup.cjs",
- "default": "./browser/country-extra/lookup.cjs"
- },
- "./geocode": {
- "import": "./browser/geocode/lookup.mjs",
- "require": "./browser/geocode/lookup.cjs",
- "default": "./browser/geocode/lookup.cjs"
- },
- "./geocode-extra": {
- "import": "./browser/geocode-extra/lookup.mjs",
- "require": "./browser/geocode-extra/lookup.cjs",
- "default": "./browser/geocode-extra/lookup.cjs"
- }
- },
- "files": [
- "src/*.mjs",
- "!src/brower*.mjs",
- "browser/*/*.cjs",
- "browser/*/*.mjs",
- "browser/*/*.js",
- "cjs/*.cjs",
- "spec",
- "script/*.mjs",
- "script/*.cjs",
- "types/src/main.d.mts",
- "types/cjs/main.d.cts",
- "LICENSE",
- "EULA",
- "README.md"
- ],
- "dependencies": {
- "axios": "^1.7.7",
- "countries-list": "^3.1.1",
- "cron": "^3.1.7",
- "dayjs": "^1.11.13",
- "fast-csv": "^5.0.1",
- "ip-address": "^9.0.5",
- "yauzl": "^3.1.3"
+ "scripts": {
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "build": "pnpm -r --workspace-concurrency=1 build",
+ "build:watch": "concurrently \"npm:build:watch:*\" --kill-others",
+ "build:watch:country": "cd packages/country && pnpm build:watch",
+ "build:watch:country-extra": "cd packages/country-extra && pnpm build:watch",
+ "build:watch:geocode": "cd packages/geocode && pnpm build:watch",
+ "build:watch:geocode-extra": "cd packages/geocode-extra && pnpm build:watch",
+ "build:watch:ip-location-api": "cd packages/ip-location-api && pnpm build:watch",
+ "build:watch:util": "cd packages/util && pnpm build:watch",
+ "test:ui": "vitest --ui --coverage",
+ "test": "vitest --run --coverage",
+ "release:all": "pnpm install && pnpm build && pnpm -r release"
},
"devDependencies": {
- "@rollup/plugin-node-resolve": "^15.2.3",
- "@rollup/plugin-replace": "^5.0.7",
- "@rollup/plugin-strip": "^3.0.4",
- "@rollup/plugin-terser": "^0.4.4",
- "doc999tor-fast-geoip": "^1.1.253",
- "eslint": "^8.57.0",
- "geoip-country": "^4.0.0",
- "geoip-lite": "^1.4.10",
- "globals": "^15.9.0",
- "http-server": "^14.1.1",
- "jasmine": "^5.2.0",
- "release-it": "^17.10.0",
- "rollup-plugin-ignore": "^1.0.10",
- "tsc": "^2.0.4"
+ "@antfu/eslint-config": "^3.7.3",
+ "@rollup/plugin-replace": "^6.0.1",
+ "@total-typescript/tsconfig": "^1.0.4",
+ "@types/node": "^22.7.4",
+ "@unocss/eslint-plugin": "^0.63.0",
+ "@vitest/coverage-v8": "^2.1.1",
+ "@vitest/ui": "^2.1.1",
+ "bumpp": "^9.8.1",
+ "concurrently": "^9.0.1",
+ "eslint": "^9.11.1",
+ "eslint-plugin-format": "^0.1.2",
+ "semver": "^7.6.3",
+ "typescript": "^5.6.2",
+ "vite": "^5.4.8",
+ "vite-plugin-checker": "^0.8.0",
+ "vite-plugin-dts": "^4.2.2",
+ "vitest": "^2.1.1"
}
}
diff --git a/packages/country-extra/README.md b/packages/country-extra/README.md
new file mode 100644
index 0000000..d244602
--- /dev/null
+++ b/packages/country-extra/README.md
@@ -0,0 +1,57 @@
+# @iplookup/country-extra
+
+[](https://badge.fury.io/js/%40iplookup%2Fcountry-extra)
+[](https://www.npmjs.com/package/%40iplookup%2Fcountry-extra)
+[](https://github.com/sapics/ip-location-api/actions/workflows/build.yml)
+
+## Usage
+
+### Browser
+
+```html
+
+
+```
+
+### ESM
+
+```ts
+import IpLookup from '@iplookup/country-extra'
+
+const location = await IpLookup('207.97.227.239')
+```
+
+### CommonJS
+
+```ts
+const IpLookup = require('@iplookup/country-extra')
+
+const location = await IpLookup('207.97.227.239')
+```
+
+If you do not need extra information about country, try to use [@iplookup/country](https://github.com/sapics/ip-location-api/tree/main/browser/country).
+
+## License
+
+Since each user download a partial database, we use the CC0 Licensed database [geo-whois-asn-country](https://github.com/sapics/ip-location-db/tree/main/geo-whois-asn-country) for ip to country mapping to avoid license problem.
+
+To get extra information about country, we use [Countries](https://github.com/annexare/Countries) which is published under MIT license by [Annexare Studio](https://annexare.com/).
+
+The software itself is published under MIT license by [sapics](https://github.com/sapics).
diff --git a/packages/country-extra/package.json b/packages/country-extra/package.json
new file mode 100644
index 0000000..a082e5b
--- /dev/null
+++ b/packages/country-extra/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "@iplookup/country-extra",
+ "type": "module",
+ "version": "2.0.0",
+ "description": "Browser api to lookup country from IP address",
+ "author": "sapics",
+ "license": "MIT",
+ "homepage": "https://github.com/sapics/ip-location-api",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sapics/ip-location-api.git",
+ "directory": "packages/country-extra"
+ },
+ "keywords": [
+ "location",
+ "iplookup",
+ "lookup",
+ "geo",
+ "geoip",
+ "ip",
+ "ipv4",
+ "ipv6",
+ "ip-location-db",
+ "country"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist",
+ "indexes"
+ ],
+ "scripts": {
+ "build": "vite build",
+ "build:watch": "vite build --watch",
+ "release": "bumpp package.json --commit --push --tag"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@iplookup/util": "workspace:*",
+ "countries-list": "^3.1.1"
+ }
+}
diff --git a/packages/country-extra/src/index.test.ts b/packages/country-extra/src/index.test.ts
new file mode 100644
index 0000000..34ed0c7
--- /dev/null
+++ b/packages/country-extra/src/index.test.ts
@@ -0,0 +1,71 @@
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { describe, expect, expectTypeOf, it, vi } from 'vitest'
+import IpLookup from './index'
+
+describe('ipLookup', () => {
+ it('should export the IpLookup function', () => {
+ expect.soft(IpLookup).toBeDefined()
+ expect.soft(typeof IpLookup).toBe('function')
+ expectTypeOf(IpLookup).toEqualTypeOf<(ipInput: string) => Promise<{
+ country: string
+ country_name: string
+ country_native: string
+ continent: string
+ capital: string
+ phone: number[]
+ currency: string[]
+ languages: string[]
+ } | null>>()
+ })
+
+ it('should return the correct country', async () => {
+ globalThis.fetch = vi.fn().mockImplementation((url: URL) => {
+ return Promise.resolve({
+ ok: true,
+ status: 200,
+ headers: new Headers(),
+ arrayBuffer: async () => {
+ const data = await readFile(resolve(__dirname, `../indexes${url.pathname.split('indexes')[1]}`))
+ return data.buffer
+ },
+ })
+ })
+ const result = await IpLookup('207.97.227.239')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({
+ country: 'US',
+ country_name: 'United States',
+ country_native: 'United States',
+ continent: 'NA',
+ capital: 'Washington D.C.',
+ phone: [1],
+ currency: [
+ 'USD',
+ 'USN',
+ 'USS',
+ ],
+ languages: ['en'],
+ })
+
+ const result2 = await IpLookup('2607:F8B0:4005:801::200E')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({
+ country: 'US',
+ country_name: 'United States',
+ country_native: 'United States',
+ continent: 'NA',
+ capital: 'Washington D.C.',
+ phone: [1],
+ currency: [
+ 'USD',
+ 'USN',
+ 'USS',
+ ],
+ languages: ['en'],
+ })
+
+ await expect.soft(IpLookup('invalid')).rejects.toThrow('Invalid IPv4 address: invalid')
+ await expect.soft(IpLookup('0000:0000:0000:0000:0000:0000:0000:0000')).resolves.toBeNull()
+ })
+})
diff --git a/packages/country-extra/src/index.ts b/packages/country-extra/src/index.ts
new file mode 100644
index 0000000..5ee2498
--- /dev/null
+++ b/packages/country-extra/src/index.ts
@@ -0,0 +1,69 @@
+import { setup } from '@iplookup/util/browser'
+import { countries } from 'countries-list'
+
+const BaseLookup = setup<'country'>()
+
+/**
+ * Lookup country from IP address
+ * @param ipInput - IP address to lookup
+ * @returns country code, country name, country native name, continent, capital, phone, currency and languages. `null` if not found.
+ */
+export default async function IpLookup(ipInput: string): Promise<{
+ /**
+ * The country of the IP address
+ * @example 'NL'
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ */
+ country: string
+ /**
+ * The name of the country of the IP address (In English)
+ * @example 'Netherlands'
+ */
+ country_name: string
+ /**
+ * The name of the country of the IP address (In the native language of the country)
+ * @example 'Nederland'
+ */
+ country_native: string
+ /**
+ * The continent of the IP address (alpha-2 code)
+ * @example 'EU'
+ */
+ continent: string
+ /**
+ * The capital of the country of the IP address (In English)
+ * @example 'Amsterdam'
+ */
+ capital: string
+ /**
+ * The phone codes of the country of the IP address
+ * @example ['31']
+ */
+ phone: number[]
+ /**
+ * The currency of the country of the IP address
+ * @example ['EUR']
+ */
+ currency: string[]
+ /**
+ * The languages of the country of the IP address
+ * @example ['nl']
+ */
+ languages: string[]
+} | null> {
+ const result = await BaseLookup(ipInput)
+ if (!result)
+ return null
+
+ const country = countries[result.country as keyof typeof countries]
+ return {
+ country: result.country,
+ country_name: country.name,
+ country_native: country.native,
+ continent: country.continent,
+ capital: country.capital,
+ phone: country.phone,
+ currency: country.currency,
+ languages: country.languages,
+ }
+}
diff --git a/packages/country-extra/tsconfig.json b/packages/country-extra/tsconfig.json
new file mode 100644
index 0000000..0f3afd7
--- /dev/null
+++ b/packages/country-extra/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "noEmit": true
+ }
+}
diff --git a/packages/country-extra/vite.config.ts b/packages/country-extra/vite.config.ts
new file mode 100644
index 0000000..6402849
--- /dev/null
+++ b/packages/country-extra/vite.config.ts
@@ -0,0 +1,62 @@
+import type { IpLocationApiInputSettings } from '@iplookup/util'
+import { resolve } from 'node:path'
+import { createBrowserIndex } from '@iplookup/util/browserIndex'
+import { update } from '@iplookup/util/db'
+import replace from '@rollup/plugin-replace'
+import { defineConfig } from 'vite'
+import checker from 'vite-plugin-checker'
+import dts from 'vite-plugin-dts'
+
+export default defineConfig({
+ build: {
+ emptyOutDir: true,
+ lib: {
+ entry: {
+ index: 'src/index.ts',
+ },
+ formats: ['es', 'cjs', 'iife'],
+ fileName: (format, entryName) => {
+ switch (format) {
+ case 'es':
+ return `${entryName}.mjs`
+ case 'cjs':
+ return `${entryName}.cjs`
+ case 'iife':
+ return `${entryName}.min.js`
+ default:
+ return `${entryName}.js`
+ }
+ },
+ name: 'IpLookup',
+ },
+ sourcemap: true,
+ rollupOptions: {
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/country-extra"',
+ __DATA_TYPE__: '"country"',
+ }),
+ ],
+ },
+ },
+ plugins: [
+ checker({
+ typescript: true,
+ }),
+ dts(),
+ {
+ name: 'createBrowserIndex',
+ async buildStart() {
+ const settings: IpLocationApiInputSettings = {
+ dataDir: resolve('../../data/country'),
+ tmpDataDir: resolve('../../tmp/country'),
+ fields: ['country'],
+ silent: true,
+ }
+ await update(settings)
+ await createBrowserIndex('country', settings, resolve('./indexes'))
+ },
+ },
+ ],
+})
diff --git a/packages/country-extra/vitest.config.ts b/packages/country-extra/vitest.config.ts
new file mode 100644
index 0000000..156ef2a
--- /dev/null
+++ b/packages/country-extra/vitest.config.ts
@@ -0,0 +1,13 @@
+import replace from '@rollup/plugin-replace'
+
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/country-extra"',
+ __DATA_TYPE__: '"country"',
+ }),
+ ],
+})
diff --git a/packages/country/README.md b/packages/country/README.md
new file mode 100644
index 0000000..4d59799
--- /dev/null
+++ b/packages/country/README.md
@@ -0,0 +1,47 @@
+# @iplookup/country
+
+[](https://badge.fury.io/js/%40iplookup%2Fcountry)
+[](https://www.npmjs.com/package/%40iplookup%2Fcountry)
+[](https://github.com/sapics/ip-location-api/actions/workflows/build.yml)
+
+## Usage
+
+### Browser
+
+```html
+
+
+```
+
+### ESM
+
+```ts
+import IpLookup from '@iplookup/country'
+
+const location = await IpLookup('207.97.227.239')
+```
+
+### CommonJS
+
+```ts
+const IpLookup = require('@iplookup/country')
+
+const location = await IpLookup('207.97.227.239')
+```
+
+If you need extra information about country, try to use [@iplookup/country-extra](https://github.com/sapics/ip-location-api/tree/main/browser/country-extra).
+
+## License
+
+Since each user download a partial database, we use the CC0 Licensed database [geo-whois-asn-country](https://github.com/sapics/ip-location-db/tree/main/geo-whois-asn-country) for ip to country mapping to avoid license problem.
+
+The software itself is published under MIT License by [sapics](https://github.com/sapics).
diff --git a/packages/country/package.json b/packages/country/package.json
new file mode 100644
index 0000000..6d0c3df
--- /dev/null
+++ b/packages/country/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "@iplookup/country",
+ "type": "module",
+ "version": "2.0.0",
+ "description": "Browser api to lookup country from IP address",
+ "author": "sapics",
+ "license": "MIT",
+ "homepage": "https://github.com/sapics/ip-location-api",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sapics/ip-location-api.git",
+ "directory": "packages/country"
+ },
+ "keywords": [
+ "location",
+ "iplookup",
+ "lookup",
+ "geo",
+ "geoip",
+ "ip",
+ "ipv4",
+ "ipv6",
+ "ip-location-db",
+ "country"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist",
+ "indexes"
+ ],
+ "scripts": {
+ "build": "vite build",
+ "build:watch": "vite build --watch",
+ "release": "bumpp package.json --commit --push --tag"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@iplookup/util": "workspace:*"
+ }
+}
diff --git a/packages/country/src/index.test.ts b/packages/country/src/index.test.ts
new file mode 100644
index 0000000..25d6516
--- /dev/null
+++ b/packages/country/src/index.test.ts
@@ -0,0 +1,36 @@
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { describe, expect, expectTypeOf, it, vi } from 'vitest'
+import IpLookup from './index'
+
+describe('ipLookup', () => {
+ it('should export the IpLookup function', () => {
+ expect.soft(IpLookup).toBeDefined()
+ expect.soft(typeof IpLookup).toBe('function')
+ expectTypeOf(IpLookup).toEqualTypeOf<(ipInput: string) => Promise<{ country: string } | null>>()
+ })
+
+ it('should return the correct country', async () => {
+ globalThis.fetch = vi.fn().mockImplementation((url: URL) => {
+ return Promise.resolve({
+ ok: true,
+ status: 200,
+ headers: new Headers(),
+ arrayBuffer: async () => {
+ const data = await readFile(resolve(__dirname, `../indexes${url.pathname.split('indexes')[1]}`))
+ return data.buffer
+ },
+ })
+ })
+ const result = await IpLookup('207.97.227.239')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({ country: 'US' })
+
+ const result2 = await IpLookup('2607:F8B0:4005:801::200E')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({ country: 'US' })
+
+ await expect.soft(IpLookup('invalid')).rejects.toThrow('Invalid IPv4 address: invalid')
+ await expect.soft(IpLookup('0000:0000:0000:0000:0000:0000:0000:0000')).resolves.toBeNull()
+ })
+})
diff --git a/packages/country/src/index.ts b/packages/country/src/index.ts
new file mode 100644
index 0000000..f3e40b9
--- /dev/null
+++ b/packages/country/src/index.ts
@@ -0,0 +1,19 @@
+import { setup } from '@iplookup/util/browser'
+
+const BaseLookup = setup<'country'>()
+
+/**
+ * Lookup country from IP address
+ * @param ipInput - IP address to lookup
+ * @returns country code. `null` if not found.
+ */
+export default async function IpLookup(ipInput: string): Promise<{
+ /**
+ * The country of the IP address
+ * @example 'NL'
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ */
+ country: string
+} | null> {
+ return BaseLookup(ipInput)
+}
diff --git a/packages/country/tsconfig.json b/packages/country/tsconfig.json
new file mode 100644
index 0000000..0f3afd7
--- /dev/null
+++ b/packages/country/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "noEmit": true
+ }
+}
diff --git a/packages/country/vite.config.ts b/packages/country/vite.config.ts
new file mode 100644
index 0000000..3e68fac
--- /dev/null
+++ b/packages/country/vite.config.ts
@@ -0,0 +1,62 @@
+import type { IpLocationApiInputSettings } from '@iplookup/util'
+import { resolve } from 'node:path'
+import { createBrowserIndex } from '@iplookup/util/browserIndex'
+import { update } from '@iplookup/util/db'
+import replace from '@rollup/plugin-replace'
+import { defineConfig } from 'vite'
+import checker from 'vite-plugin-checker'
+import dts from 'vite-plugin-dts'
+
+export default defineConfig({
+ build: {
+ emptyOutDir: true,
+ lib: {
+ entry: {
+ index: 'src/index.ts',
+ },
+ formats: ['es', 'cjs', 'iife'],
+ fileName: (format, entryName) => {
+ switch (format) {
+ case 'es':
+ return `${entryName}.mjs`
+ case 'cjs':
+ return `${entryName}.cjs`
+ case 'iife':
+ return `${entryName}.min.js`
+ default:
+ return `${entryName}.js`
+ }
+ },
+ name: 'IpLookup',
+ },
+ sourcemap: true,
+ rollupOptions: {
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/country"',
+ __DATA_TYPE__: '"country"',
+ }),
+ ],
+ },
+ },
+ plugins: [
+ checker({
+ typescript: true,
+ }),
+ dts(),
+ {
+ name: 'createBrowserIndex',
+ async buildStart() {
+ const settings: IpLocationApiInputSettings = {
+ dataDir: resolve('../../data/country'),
+ tmpDataDir: resolve('../../tmp/country'),
+ fields: ['country'],
+ silent: true,
+ }
+ await update(settings)
+ await createBrowserIndex('country', settings, resolve('./indexes'))
+ },
+ },
+ ],
+})
diff --git a/packages/country/vitest.config.ts b/packages/country/vitest.config.ts
new file mode 100644
index 0000000..1cf82d4
--- /dev/null
+++ b/packages/country/vitest.config.ts
@@ -0,0 +1,13 @@
+import replace from '@rollup/plugin-replace'
+
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/country"',
+ __DATA_TYPE__: '"country"',
+ }),
+ ],
+})
diff --git a/packages/geocode-extra/README.md b/packages/geocode-extra/README.md
new file mode 100644
index 0000000..ebf891c
--- /dev/null
+++ b/packages/geocode-extra/README.md
@@ -0,0 +1,59 @@
+# @iplookup/geocode-extra
+
+[](https://badge.fury.io/js/%40iplookup%2Fgeocode-extra)
+[](https://www.npmjs.com/package/%40iplookup%2Fgeocode-extra)
+[](https://github.com/sapics/ip-location-api/actions/workflows/build.yml)
+
+## Usage
+
+### Browser
+
+```html
+
+
+```
+
+### ESM
+
+```ts
+import IpLookup from '@iplookup/geocode-extra'
+
+const location = await IpLookup('207.97.227.239')
+```
+
+### CommonJS
+
+```ts
+const IpLookup = require('@iplookup/geocode-extra')
+
+const location = await IpLookup('207.97.227.239')
+```
+
+If you do not need extra information about country, try to use [@iplookup/geocode](https://github.com/sapics/ip-location-api/tree/main/browser/geocode).
+
+## License
+
+The database for mapping ip to geocode is published under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) by [DB-IP](https://db-ip.com/db/download/ip-to-city-lite).
+
+To get extra information about country, we use [Countries](https://github.com/annexare/Countries) which is published under MIT license by [Annexare Studio](https://annexare.com/).
+
+The software itself is published under MIT license by [sapics](https://github.com/sapics).
diff --git a/packages/geocode-extra/package.json b/packages/geocode-extra/package.json
new file mode 100644
index 0000000..a01b8ef
--- /dev/null
+++ b/packages/geocode-extra/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "@iplookup/geocode-extra",
+ "type": "module",
+ "version": "2.0.0",
+ "description": "Browser api to lookup geocode from IP address",
+ "author": "sapics",
+ "license": "CC BY 4.0 and MIT",
+ "homepage": "https://github.com/sapics/ip-location-api",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sapics/ip-location-api.git",
+ "directory": "packages/geocode-extra"
+ },
+ "keywords": [
+ "location",
+ "iplookup",
+ "lookup",
+ "geo",
+ "geocode",
+ "geoip",
+ "ip",
+ "ipv4",
+ "ipv6",
+ "ip-location-db",
+ "country"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist",
+ "indexes"
+ ],
+ "scripts": {
+ "build": "vite build",
+ "build:watch": "vite build --watch",
+ "release": "bumpp package.json --commit --push --tag"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@iplookup/util": "workspace:*",
+ "countries-list": "^3.1.1"
+ }
+}
diff --git a/packages/geocode-extra/src/index.test.ts b/packages/geocode-extra/src/index.test.ts
new file mode 100644
index 0000000..71ce513
--- /dev/null
+++ b/packages/geocode-extra/src/index.test.ts
@@ -0,0 +1,77 @@
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { describe, expect, expectTypeOf, it, vi } from 'vitest'
+import IpLookup from './index'
+
+describe('ipLookup', () => {
+ it('should export the IpLookup function', () => {
+ expect.soft(IpLookup).toBeDefined()
+ expect.soft(typeof IpLookup).toBe('function')
+ expectTypeOf(IpLookup).toEqualTypeOf<(ipInput: string) => Promise<{
+ latitude: number
+ longitude: number
+ country: string
+ country_name: string
+ country_native: string
+ continent: string
+ capital: string
+ phone: number[]
+ currency: string[]
+ languages: string[]
+ } | null>>()
+ })
+
+ it('should return the correct geocode', async () => {
+ globalThis.fetch = vi.fn().mockImplementation((url: URL) => {
+ return Promise.resolve({
+ ok: true,
+ status: 200,
+ headers: new Headers(),
+ arrayBuffer: async () => {
+ const data = await readFile(resolve(__dirname, `../indexes${url.pathname.split('indexes')[1]}`))
+ return data.buffer
+ },
+ })
+ })
+ const result = await IpLookup('207.97.227.239')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({
+ country: 'US',
+ country_name: 'United States',
+ country_native: 'United States',
+ continent: 'NA',
+ capital: 'Washington D.C.',
+ phone: [1],
+ currency: [
+ 'USD',
+ 'USN',
+ 'USS',
+ ],
+ languages: ['en'],
+ latitude: 38.9072,
+ longitude: -77.0369,
+ })
+
+ const result2 = await IpLookup('2607:F8B0:4005:801::200E')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({
+ country: 'US',
+ country_name: 'United States',
+ country_native: 'United States',
+ continent: 'NA',
+ capital: 'Washington D.C.',
+ phone: [1],
+ currency: [
+ 'USD',
+ 'USN',
+ 'USS',
+ ],
+ languages: ['en'],
+ latitude: 37.422,
+ longitude: -122.084,
+ })
+
+ await expect.soft(IpLookup('invalid')).rejects.toThrow('Invalid IPv4 address: invalid')
+ await expect.soft(IpLookup('0000:0000:0000:0000:0000:0000:0000:0000')).resolves.toBeNull()
+ })
+})
diff --git a/packages/geocode-extra/src/index.ts b/packages/geocode-extra/src/index.ts
new file mode 100644
index 0000000..fbc3ca6
--- /dev/null
+++ b/packages/geocode-extra/src/index.ts
@@ -0,0 +1,83 @@
+import { setup } from '@iplookup/util/browser'
+import { countries } from 'countries-list'
+
+const BaseLookup = setup<'geocode'>()
+
+/**
+ * Lookup geocode from IP address
+ * @param ipInput - IP address to lookup
+ * @returns latitude, longitude, country code, country name, country native name, continent, capital, phone, currency and languages. `null` if not found.
+ */
+export default async function IpLookup(ipInput: string): Promise<{
+ /**
+ * The approximate WGS84 latitude of the IP address
+ *
+ * @see https://en.wikipedia.org/wiki/World_Geodetic_System
+ */
+ latitude: number
+ /**
+ * The approximate WGS84 longitude of the IP address
+ *
+ * @see https://en.wikipedia.org/wiki/World_Geodetic_System
+ */
+ longitude: number
+ /**
+ * The country of the IP address
+ * @example 'NL'
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ */
+ country: string
+ /**
+ * The name of the country of the IP address (In English)
+ * @example 'Netherlands'
+ */
+ country_name: string
+ /**
+ * The name of the country of the IP address (In the native language of the country)
+ * @example 'Nederland'
+ */
+ country_native: string
+ /**
+ * The continent of the IP address (alpha-2 code)
+ * @example 'EU'
+ */
+ continent: string
+ /**
+ * The capital of the country of the IP address (In English)
+ * @example 'Amsterdam'
+ */
+ capital: string
+ /**
+ * The phone codes of the country of the IP address
+ * @example ['31']
+ */
+ phone: number[]
+ /**
+ * The currency of the country of the IP address
+ * @example ['EUR']
+ */
+ currency: string[]
+ /**
+ * The languages of the country of the IP address
+ * @example ['nl']
+ */
+ languages: string[]
+} | null> {
+ const result = await BaseLookup(ipInput)
+ if (!result)
+ return null
+
+ const country = countries[result.country as keyof typeof countries]
+ return {
+ latitude: result.latitude,
+ longitude: result.longitude,
+ country: result.country,
+ country_name: country.name,
+ country_native: country.native,
+ continent: country.continent,
+ capital: country.capital,
+ phone: country.phone,
+ currency: country.currency,
+ languages: country.languages,
+ }
+}
diff --git a/packages/geocode-extra/tsconfig.json b/packages/geocode-extra/tsconfig.json
new file mode 100644
index 0000000..0f3afd7
--- /dev/null
+++ b/packages/geocode-extra/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "noEmit": true
+ }
+}
diff --git a/packages/geocode-extra/vite.config.ts b/packages/geocode-extra/vite.config.ts
new file mode 100644
index 0000000..215a1b1
--- /dev/null
+++ b/packages/geocode-extra/vite.config.ts
@@ -0,0 +1,62 @@
+import type { IpLocationApiInputSettings } from '@iplookup/util'
+import { resolve } from 'node:path'
+import { createBrowserIndex } from '@iplookup/util/browserIndex'
+import { update } from '@iplookup/util/dbIpUpdate'
+import replace from '@rollup/plugin-replace'
+import { defineConfig } from 'vite'
+import checker from 'vite-plugin-checker'
+import dts from 'vite-plugin-dts'
+
+export default defineConfig({
+ build: {
+ emptyOutDir: true,
+ lib: {
+ entry: {
+ index: 'src/index.ts',
+ },
+ formats: ['es', 'cjs', 'iife'],
+ fileName: (format, entryName) => {
+ switch (format) {
+ case 'es':
+ return `${entryName}.mjs`
+ case 'cjs':
+ return `${entryName}.cjs`
+ case 'iife':
+ return `${entryName}.min.js`
+ default:
+ return `${entryName}.js`
+ }
+ },
+ name: 'IpLookup',
+ },
+ sourcemap: true,
+ rollupOptions: {
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/geocode-extra"',
+ __DATA_TYPE__: '"geocode"',
+ }),
+ ],
+ },
+ },
+ plugins: [
+ checker({
+ typescript: true,
+ }),
+ dts(),
+ {
+ name: 'createBrowserIndex',
+ async buildStart() {
+ const settings: IpLocationApiInputSettings = {
+ dataDir: resolve('../../data/geocode'),
+ tmpDataDir: resolve('../../tmp/geocode'),
+ fields: ['latitude', 'longitude'],
+ silent: true,
+ }
+ await update(settings)
+ await createBrowserIndex('geocode', settings, resolve('./indexes'))
+ },
+ },
+ ],
+})
diff --git a/packages/geocode-extra/vitest.config.ts b/packages/geocode-extra/vitest.config.ts
new file mode 100644
index 0000000..cc55495
--- /dev/null
+++ b/packages/geocode-extra/vitest.config.ts
@@ -0,0 +1,13 @@
+import replace from '@rollup/plugin-replace'
+
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/geocode-extra"',
+ __DATA_TYPE__: '"geocode"',
+ }),
+ ],
+})
diff --git a/packages/geocode/README.md b/packages/geocode/README.md
new file mode 100644
index 0000000..6f04c61
--- /dev/null
+++ b/packages/geocode/README.md
@@ -0,0 +1,49 @@
+# @iplookup/geocode
+
+[](https://badge.fury.io/js/%40iplookup%2Fgeocode)
+[](https://www.npmjs.com/package/%40iplookup%2Fgeocode)
+[](https://github.com/sapics/ip-location-api/actions/workflows/build.yml)
+
+## Usage
+
+### Browser
+
+```html
+
+
+```
+
+### ESM
+
+```ts
+import IpLookup from '@iplookup/geocode'
+
+const location = await IpLookup('207.97.227.239')
+```
+
+### CommonJS
+
+```ts
+const IpLookup = require('@iplookup/geocode')
+
+const location = await IpLookup('207.97.227.239')
+```
+
+If you need extra information about country, try to use [@iplookup/geocode-extra](https://github.com/sapics/ip-location-api/tree/main/browser/geocode-extra).
+
+## License
+
+The database is published under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) by [DB-IP](https://db-ip.com/db/download/ip-to-city-lite).
+
+The software itself is published under MIT license by [sapics](https://github.com/sapics).
diff --git a/packages/geocode/package.json b/packages/geocode/package.json
new file mode 100644
index 0000000..def4989
--- /dev/null
+++ b/packages/geocode/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "@iplookup/geocode",
+ "type": "module",
+ "version": "2.0.0",
+ "description": "Browser api to lookup geocode from IP address",
+ "author": "sapics",
+ "license": "CC BY 4.0 and MIT",
+ "homepage": "https://github.com/sapics/ip-location-api",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sapics/ip-location-api.git",
+ "directory": "packages/geocode"
+ },
+ "keywords": [
+ "location",
+ "iplookup",
+ "lookup",
+ "geo",
+ "geocode",
+ "geoip",
+ "ip",
+ "ipv4",
+ "ipv6",
+ "ip-location-db",
+ "country"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist",
+ "indexes"
+ ],
+ "scripts": {
+ "build": "vite build",
+ "build:watch": "vite build --watch",
+ "release": "bumpp package.json --commit --push --tag"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@iplookup/util": "workspace:*"
+ }
+}
diff --git a/packages/geocode/src/index.test.ts b/packages/geocode/src/index.test.ts
new file mode 100644
index 0000000..5149af4
--- /dev/null
+++ b/packages/geocode/src/index.test.ts
@@ -0,0 +1,48 @@
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { describe, expect, expectTypeOf, it, vi } from 'vitest'
+import IpLookup from './index'
+
+describe('ipLookup', () => {
+ it('should export the IpLookup function', () => {
+ expect.soft(IpLookup).toBeDefined()
+ expect.soft(typeof IpLookup).toBe('function')
+ expectTypeOf(IpLookup).toEqualTypeOf<(ipInput: string) => Promise<{
+ latitude: number
+ longitude: number
+ country: string
+ } | null>>()
+ })
+
+ it('should return the correct geocode', async () => {
+ globalThis.fetch = vi.fn().mockImplementation((url: URL) => {
+ return Promise.resolve({
+ ok: true,
+ status: 200,
+ headers: new Headers(),
+ arrayBuffer: async () => {
+ const data = await readFile(resolve(__dirname, `../indexes${url.pathname.split('indexes')[1]}`))
+ return data.buffer
+ },
+ })
+ })
+ const result = await IpLookup('207.97.227.239')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({
+ country: 'US',
+ latitude: 38.9072,
+ longitude: -77.0369,
+ })
+
+ const result2 = await IpLookup('2607:F8B0:4005:801::200E')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({
+ country: 'US',
+ latitude: 37.422,
+ longitude: -122.084,
+ })
+
+ await expect.soft(IpLookup('invalid')).rejects.toThrow('Invalid IPv4 address: invalid')
+ await expect.soft(IpLookup('0000:0000:0000:0000:0000:0000:0000:0000')).resolves.toBeNull()
+ })
+})
diff --git a/packages/geocode/src/index.ts b/packages/geocode/src/index.ts
new file mode 100644
index 0000000..5469b69
--- /dev/null
+++ b/packages/geocode/src/index.ts
@@ -0,0 +1,31 @@
+import { setup } from '@iplookup/util/browser'
+
+const BaseLookup = setup<'geocode'>()
+
+/**
+ * Lookup geocode from IP address
+ * @param ipInput - IP address to lookup
+ * @returns country code, latitude and longitude. `null` if not found.
+ */
+export default async function IpLookup(ipInput: string): Promise<{
+ /**
+ * The approximate WGS84 latitude of the IP address
+ *
+ * @see https://en.wikipedia.org/wiki/World_Geodetic_System
+ */
+ latitude: number
+ /**
+ * The approximate WGS84 longitude of the IP address
+ *
+ * @see https://en.wikipedia.org/wiki/World_Geodetic_System
+ */
+ longitude: number
+ /**
+ * The country of the IP address
+ * @example 'NL'
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ */
+ country: string
+} | null> {
+ return BaseLookup(ipInput)
+}
diff --git a/packages/geocode/tsconfig.json b/packages/geocode/tsconfig.json
new file mode 100644
index 0000000..0f3afd7
--- /dev/null
+++ b/packages/geocode/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "noEmit": true
+ }
+}
diff --git a/packages/geocode/vite.config.ts b/packages/geocode/vite.config.ts
new file mode 100644
index 0000000..c841844
--- /dev/null
+++ b/packages/geocode/vite.config.ts
@@ -0,0 +1,62 @@
+import type { IpLocationApiInputSettings } from '@iplookup/util'
+import { resolve } from 'node:path'
+import { createBrowserIndex } from '@iplookup/util/browserIndex'
+import { update } from '@iplookup/util/dbIpUpdate'
+import replace from '@rollup/plugin-replace'
+import { defineConfig } from 'vite'
+import checker from 'vite-plugin-checker'
+import dts from 'vite-plugin-dts'
+
+export default defineConfig({
+ build: {
+ emptyOutDir: true,
+ lib: {
+ entry: {
+ index: 'src/index.ts',
+ },
+ formats: ['es', 'cjs', 'iife'],
+ fileName: (format, entryName) => {
+ switch (format) {
+ case 'es':
+ return `${entryName}.mjs`
+ case 'cjs':
+ return `${entryName}.cjs`
+ case 'iife':
+ return `${entryName}.min.js`
+ default:
+ return `${entryName}.js`
+ }
+ },
+ name: 'IpLookup',
+ },
+ sourcemap: true,
+ rollupOptions: {
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/geocode"',
+ __DATA_TYPE__: '"geocode"',
+ }),
+ ],
+ },
+ },
+ plugins: [
+ checker({
+ typescript: true,
+ }),
+ dts(),
+ {
+ name: 'createBrowserIndex',
+ async buildStart() {
+ const settings: IpLocationApiInputSettings = {
+ dataDir: resolve('../../data/geocode'),
+ tmpDataDir: resolve('../../tmp/geocode'),
+ fields: ['latitude', 'longitude'],
+ silent: true,
+ }
+ await update(settings)
+ await createBrowserIndex('geocode', settings, resolve('./indexes'))
+ },
+ },
+ ],
+})
diff --git a/packages/geocode/vitest.config.ts b/packages/geocode/vitest.config.ts
new file mode 100644
index 0000000..0a1b2c2
--- /dev/null
+++ b/packages/geocode/vitest.config.ts
@@ -0,0 +1,13 @@
+import replace from '@rollup/plugin-replace'
+
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ plugins: [
+ replace({
+ preventAssignment: true,
+ __CDN_URL__: '"https://cdn.jsdelivr.net/npm/@iplookup/geocode"',
+ __DATA_TYPE__: '"geocode"',
+ }),
+ ],
+})
diff --git a/packages/ip-location-api/README.md b/packages/ip-location-api/README.md
new file mode 100644
index 0000000..9647246
--- /dev/null
+++ b/packages/ip-location-api/README.md
@@ -0,0 +1,112 @@
+# ip-location-api
+
+[](https://badge.fury.io/js/ip-location-api)
+[](https://www.npmjs.com/package/ip-location-api)
+[](https://github.com/sapics/ip-location-api/actions/workflows/build.yml)
+
+## Usage
+
+```ts
+import { clear, lookup, reload, update } from 'ip-location-api'
+
+await reload({ fields: ['country'] })
+
+const location = await lookup('8.8.8.8')
+console.log(location) // { country: 'US' }
+
+await update({ fields: ['country', 'city'] }) // Update the database from the remote source
+await reload({ fields: ['country', 'city'] }) // Reload the in-memory database with the updated data
+
+const location2 = await lookup('8.8.8.8')
+console.log(location2) // { country: 'US', city: 'Mountain View' }
+
+clear() // Clear the in-memory database
+
+const location3 = await lookup('8.8.8.8')
+console.log(location3) // null
+```
+
+## Configuration
+
+You can configure the library in three ways:
+
+- Environment variables: `ILA_FIELDS=country,city`
+- CLI arguments: `node index.js ILA_FIELDS=country,city`
+- Configuration object: `reload({ fields: ['country', 'city'] })`
+
+| Option | ENV Variable | Default | Description |
+| ------------------- | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
+| fields | ILA_FIELDS | country | Specify which fields to retrieve from MaxMind. Use "all" to display all available fields. |
+| addCountryInfo | ILA_ADD_COUNTRY_INFO | false | When set to "true", adds additional country information from the Countries database. Requires the "country" field to be selected. |
+| dataDir | ILA_DATA_DIR | ../data | Directory path for the database file |
+| tmpDataDir | ILA_TMP_DATA_DIR | ../tmp | Directory path for temporary files |
+| smallMemory | ILA_SMALL_MEMORY | false | When true, operates in asynchronous mode. When false, operates in synchronous mode. |
+| smallMemoryFileSize | ILA_SMALL_MEMORY_FILE_SIZE | 4096 | Maximum file size for asynchronous data processing (changing not recommended) |
+| licenseKey | ILA_LICENSE_KEY | redist | MaxMind license key for downloading the latest database. Set to "redist" to use the redistributed database from node-geolite2-redist. |
+| series | ILA_SERIES | GeoLite2 | Set to "GeoIP2" to use the premium GeoIP2 database |
+| language | ILA_LANGUAGE | en | Supported languages: "de", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN". Affects the language of region and city names. |
+| silent | ILA_SILENT | false | When true, suppresses non-essential console output |
+
+## Operating Modes
+
+The library operates in two modes:
+
+1. **Synchronous Mode** (default):
+
+ - Loads all data into memory at startup
+ - Higher memory usage
+ - Slower startup time
+ - Very fast lookups (>300x faster than async)
+
+2. **Asynchronous Mode** (`smallMemory: true`):
+ - Minimal memory footprint
+ - Fast startup time
+ - Slower lookups (loads data from disk as needed)
+
+For optimal performance, use synchronous mode when sufficient memory is available.
+
+## Available Fields
+
+The library aims to maintain compatibility with `geoip-lite` field names where possible, while offering additional fields:
+
+| Field Name | geoip-lite | Source | Description |
+| -------------- | ---------- | --------- | ---------------------------------------------- |
+| country | ✓ | MaxMind | Two-letter country code (ISO-3166-1 alpha-2) |
+| region1 | region | MaxMind | First-level region code (ISO 3166-2) |
+| region1_name | ✗ | MaxMind | First-level region name (localized) |
+| region2 | ✗ | MaxMind | Second-level region code (ISO 3166-2) |
+| region2_name | ✗ | MaxMind | Second-level region name (localized) |
+| city | ✓ | MaxMind | City name (localized) |
+| metro | ✓ | MaxMind | Google Geolocation target code |
+| eu | ✓ | MaxMind | EU membership flag (true for EU member states) |
+| timezone | ✓ | MaxMind | Time zone identifier |
+| latitude | ll[0] | MaxMind | WGS84 latitude |
+| longitude | ll[1] | MaxMind | WGS84 longitude |
+| area | ✓ | MaxMind | Accuracy radius in kilometers |
+| postcode | ✗ | MaxMind | Postal code |
+| country_name | ✗ | Countries | Country name in English |
+| country_native | ✗ | Countries | Country name in native language |
+| continent | ✗ | Countries | Continent code |
+| continent_name | ✗ | Countries | Continent name |
+| capital | ✗ | Countries | Capital city name |
+| phone | ✗ | Countries | International calling code |
+| currency | ✗ | Countries | Common currencies |
+| languages | ✗ | Countries | Common languages |
+
+## License and Terms of Use
+
+This project includes multiple components with different licenses:
+
+1. **Software Library**: MIT License (© sapics)
+
+2. **GeoLite2 Database**: CC BY-SA 4.0 (© MaxMind)
+
+ - Usage restrictions:
+ - No FCRA-regulated uses
+ - No identification of specific households/individuals
+ - Must allow database updates
+ - Attribution to MaxMind required
+
+ [View full GeoLite2 EULA](https://www.maxmind.com/en/geolite2/eula)
+
+3. **Countries Database**: MIT License (© Annexare Studio)
diff --git a/packages/ip-location-api/package.json b/packages/ip-location-api/package.json
new file mode 100644
index 0000000..805a4a5
--- /dev/null
+++ b/packages/ip-location-api/package.json
@@ -0,0 +1,66 @@
+{
+ "name": "ip-location-api",
+ "type": "module",
+ "version": "3.0.0",
+ "description": "Fast location lookup from IP address",
+ "author": "sapics",
+ "license": "Multiple licenses",
+ "homepage": "https://github.com/sapics/ip-location-api",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sapics/ip-location-api.git",
+ "directory": "packages/ip-location-api"
+ },
+ "keywords": [
+ "location",
+ "lookup",
+ "geo",
+ "geoip",
+ "geolite",
+ "maxmind",
+ "ip",
+ "ipv4",
+ "ipv6",
+ "ip-location-db",
+ "country",
+ "city"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "vite build",
+ "build:watch": "vite build --watch",
+ "release": "bumpp package.json --commit --push --tag"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "peerDependencies": {
+ "countries-list": "^3.1.1"
+ },
+ "peerDependenciesMeta": {
+ "countries-list": {
+ "optional": true
+ }
+ },
+ "dependencies": {
+ "@fast-csv/parse": "^5.0.0",
+ "ip-address": "^9.0.5",
+ "ky": "^1.7.2",
+ "yauzl": "^3.1.3"
+ },
+ "devDependencies": {
+ "@iplookup/util": "workspace:*",
+ "countries-list": "^3.1.1"
+ }
+}
diff --git a/packages/ip-location-api/src/functions/lookup.ts b/packages/ip-location-api/src/functions/lookup.ts
new file mode 100644
index 0000000..62978cc
--- /dev/null
+++ b/packages/ip-location-api/src/functions/lookup.ts
@@ -0,0 +1,382 @@
+import { Buffer } from 'node:buffer'
+import { open } from 'node:fs/promises'
+import { join } from 'node:path'
+import { binarySearch, getSmallMemoryFile, type IpLocationApiSettings, type LocalDatabase, log, number37ToString, parseIp, SAVED_SETTINGS } from '@iplookup/util'
+import { LOADED_DATA } from './reload.js'
+
+/**
+ * Represents geographical data for an IP address
+ */
+interface GeoData {
+ /**
+ * The approximate WGS84 latitude of the IP address
+ *
+ * @see https://en.wikipedia.org/wiki/World_Geodetic_System
+ */
+ latitude?: number
+ /**
+ * The approximate WGS84 longitude of the IP address
+ *
+ * @see https://en.wikipedia.org/wiki/World_Geodetic_System
+ */
+ longitude?: number
+ /**
+ * The region-specific postcode nearest to the IP address
+ */
+ postcode?: string
+ /**
+ * The radius in kilometers of the specified location where the IP address is likely to be
+ */
+ area?: number
+ /**
+ * The country of the IP address
+ * @example 'NL'
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ */
+ country?: string
+ /**
+ * Whether the country is a member of the European Union
+ *
+ * @requires country
+ */
+ eu?: boolean
+ /**
+ * The first region of the IP address
+ * @example 'NL-ZH' // (Netherlands, South Holland)
+ * @see https://en.wikipedia.org/wiki/ISO_3166-2
+ */
+ region1?: string
+ /**
+ * The name of the first region of the IP address (Can be aquired in multiple languages using the `language` setting)
+ * @example 'South Holland'
+ */
+ region1_name?: string
+ /**
+ * The second region of the IP address
+ * @example 'NL-ZH' // (Netherlands, South Holland)
+ * @see https://en.wikipedia.org/wiki/ISO_3166-2
+ */
+ region2?: string
+ /**
+ * The name of the second region of the IP address (Can be aquired in multiple languages using the `language` setting)
+ * @example 'South Holland'
+ */
+ region2_name?: string
+ /**
+ * The metropolitan area of the IP address (Google AdWords API)
+ * @example 1000
+ * @see https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions
+ */
+ metro?: number
+ /**
+ * The timezone of the IP address
+ * @example 'Europe/Amsterdam'
+ */
+ timezone?: string
+ /**
+ * The city of the IP address
+ * @example 'Amsterdam'
+ */
+ city?: string
+ /**
+ * The name of the country of the IP address (In English)
+ * @example 'Netherlands'
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ country_name?: string
+ /**
+ * The name of the country of the IP address (In the native language of the country)
+ * @example 'Nederland'
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ country_native?: string
+ /**
+ * The continent of the IP address (alpha-2 code)
+ * @example 'EU'
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ continent?: string
+ /**
+ * The name of the continent of the IP address (In English)
+ * @example 'Europe'
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ continent_name?: string
+ /**
+ * The capital of the country of the IP address (In English)
+ * @example 'Amsterdam'
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ capital?: string
+ /**
+ * The phone codes of the country of the IP address
+ * @example ['31']
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ phone?: number[]
+ /**
+ * The currency of the country of the IP address
+ * @example ['EUR']
+ * @requires country
+ * @requires country-list (optional peer dependency)
+ */
+ currency?: string[]
+ /**
+ * The languages of the country of the IP address
+ * @example ['nl']
+ */
+ languages?: string[]
+}
+
+/**
+ * Looks up geographical data for a given IP address
+ * @param ip - The IP address to look up
+ * @returns A Promise that resolves to GeoData or null if not found
+ */
+export async function lookup(ip: string): Promise {
+ //* We don't use net.isIP(ip) as it is slow for ipv6
+ const { version, ip: ipNumber } = parseIp(ip)
+
+ const settings = SAVED_SETTINGS
+ const db = version === 4 ? settings.v4 : settings.v6
+
+ if (!db.loadedData || !(ipNumber >= db.loadedData.firstIp))
+ return null
+ const list = db.loadedData.startIps
+ const line = binarySearch(list, ipNumber)
+ /* c8 ignore next 2 */ //* Line should never be null
+ if (line === null)
+ return null
+
+ if (settings.smallMemory) {
+ const buffer = await lineToFile(line, db, settings)
+ const endIp = version === 4 ? buffer.readUInt32LE(0) : buffer.readBigUInt64LE(0)
+ if (ipNumber > endIp)
+ return null
+
+ if (settings.dataType === 'Country') {
+ return setCountryInfo({
+ country: buffer.toString('latin1', version === 4 ? 4 : 8, version === 4 ? 6 : 10),
+ }, settings)
+ }
+ return setCityInfo(buffer, version === 4 ? 4 : 8, settings)
+ }
+
+ const endIps = db.loadedData.endIps
+ if (!endIps || ipNumber > endIps[line]!)
+ return null
+
+ if (settings.dataType === 'Country') {
+ return setCountryInfo({
+ country: db.loadedData.mainBuffer!.toString('latin1', line * db.recordSize, line * db.recordSize + 2),
+ }, settings)
+ }
+ return setCityInfo(db.loadedData.mainBuffer!, line * db.recordSize, settings)
+}
+
+/**
+ * Reads a specific line from a file in the database
+ * @param line - The line number to read
+ * @param db - The database object
+ * @param settings - The API settings
+ * @returns A Promise that resolves to a Buffer containing the line data
+ */
+async function lineToFile(line: number, db: LocalDatabase, settings: IpLocationApiSettings): Promise {
+ const [dir, file, offset] = getSmallMemoryFile(line, db)
+ const fd = await open(join(settings.fieldDir, dir, file), 'r')
+ const buffer = Buffer.alloc(db.recordSize)
+ await fd.read(buffer, 0, db.recordSize, offset)
+ fd.close().catch(() => {
+ log('warn', 'Failed to close file descriptor')
+ })
+ return buffer
+}
+
+/**
+ * Extracts city information from a buffer
+ * @param buffer - The buffer containing city data
+ * @param offset - The starting offset in the buffer
+ * @param settings - The API settings
+ * @returns A Promise that resolves to GeoData
+ */
+function setCityInfo(buffer: Buffer, offset: number, settings: IpLocationApiSettings): Promise {
+ let locationId: number | undefined
+ const geodata: GeoData = {}
+
+ //* Read location ID if location file is available
+ if (settings.locationFile) {
+ locationId = buffer.readUInt32LE(offset)
+ offset += 4
+ }
+
+ //* Read latitude and longitude if included in fields
+ if (settings.fields.includes('latitude')) {
+ geodata.latitude = buffer.readInt32LE(offset) / 10000
+ offset += 4
+ }
+ if (settings.fields.includes('longitude')) {
+ geodata.longitude = buffer.readInt32LE(offset) / 10000
+ offset += 4
+ }
+
+ //* Read and decode postcode if included in fields
+ if (settings.fields.includes('postcode')) {
+ const postcodeValue = buffer.readUInt32LE(offset)
+ const postcodeLength = buffer.readInt8(offset + 4)
+ if (postcodeValue) {
+ let postcode: string
+ if (postcodeLength < -9) {
+ const code = (-postcodeLength).toString()
+ postcode = postcodeValue.toString(36)
+ postcode = `${getZeroFill(
+ postcode.slice(0, -Number.parseInt(code[1]!)),
+ Number.parseInt(code[0]!) - 0,
+ )}-${getZeroFill(postcode.slice(-Number.parseInt(code[1]!)), Number.parseInt(code[1]!) - 0)}`
+ }
+ else if (postcodeLength < 0) {
+ postcode = getZeroFill(postcodeValue.toString(36), -postcodeLength)
+ }
+ else if (postcodeLength < 10) {
+ postcode = getZeroFill(postcodeValue.toString(10), postcodeLength)
+ }
+ else if (postcodeLength < 72) {
+ const code = String(postcodeLength)
+ postcode = getZeroFill(postcodeValue.toString(10), (Number.parseInt(code[0]!) - 0) + (Number.parseInt(code[1]!) - 0))
+ postcode = `${postcode.slice(0, Number.parseInt(code[0]!) - 0)}-${postcode.slice(Number.parseInt(code[0]!) - 0)}`
+ /* c8 ignore next 4 */ //* Should never happen
+ }
+ else {
+ postcode = postcodeLength.toString(36).slice(1) + postcodeValue.toString(36)
+ }
+ geodata.postcode = postcode.toUpperCase()
+ }
+ offset += 5
+ }
+
+ //* Read area if included in fields
+ if (settings.fields.includes('area')) {
+ const areaMap = LOADED_DATA.sub?.area
+ if (areaMap) {
+ geodata.area = areaMap[buffer.readUInt8(offset)]
+ }
+ // offset += 1
+ }
+
+ //* Process location data if locationId is available
+ if (locationId) {
+ let locationOffset = (locationId - 1) * settings.locationRecordSize
+ const locationBuffer = LOADED_DATA.location
+ if (locationBuffer) {
+ if (settings.fields.includes('country')) {
+ geodata.country = locationBuffer.toString('utf8', locationOffset, locationOffset += 2)
+ const euMap = LOADED_DATA.sub?.eu
+ if (settings.fields.includes('eu') && euMap) {
+ geodata.eu = euMap[geodata.country]
+ }
+ }
+ if (settings.fields.includes('region1')) {
+ const region1 = locationBuffer.readUInt16LE(locationOffset)
+ locationOffset += 2
+ if (region1 > 0) {
+ geodata.region1 = number37ToString(region1)
+ }
+ }
+ if (settings.fields.includes('region1_name')) {
+ const region1Name = locationBuffer.readUInt16LE(locationOffset)
+ locationOffset += 2
+ const region1Map = LOADED_DATA.sub?.region1
+ if (region1Name > 0 && region1Map) {
+ geodata.region1_name = region1Map[region1Name]
+ }
+ }
+ if (settings.fields.includes('region2')) {
+ const region2 = locationBuffer.readUInt16LE(locationOffset)
+ locationOffset += 2
+ if (region2 > 0) {
+ geodata.region2 = number37ToString(region2)
+ }
+ }
+ if (settings.fields.includes('region2_name')) {
+ const region2Name = locationBuffer.readUInt16LE(locationOffset)
+ locationOffset += 2
+ const region2Map = LOADED_DATA.sub?.region2
+ if (region2Name > 0 && region2Map) {
+ geodata.region2_name = region2Map[region2Name]
+ }
+ }
+ if (settings.fields.includes('metro')) {
+ const metro = locationBuffer.readUInt16LE(locationOffset)
+ locationOffset += 2
+ if (metro > 0) {
+ geodata.metro = metro
+ }
+ }
+ if (settings.fields.includes('timezone')) {
+ const timezone = locationBuffer.readUInt16LE(locationOffset)
+ locationOffset += 2
+ const timezoneMap = LOADED_DATA.sub?.timezone
+ if (timezone > 0 && timezoneMap) {
+ geodata.timezone = timezoneMap[timezone]
+ }
+ }
+ if (settings.fields.includes('city')) {
+ const city = locationBuffer.readUInt32LE(locationOffset)
+ // locationOffset += 4
+ const cityMap = LOADED_DATA.city
+ if (city > 0 && cityMap) {
+ const start = city >>> 8
+ geodata.city = cityMap.toString('utf8', start, start + (city & 255))
+ }
+ }
+ }
+ }
+
+ return setCountryInfo(geodata, settings)
+}
+
+/**
+ * Pads a string with leading zeros
+ * @param text - The text to pad
+ * @param length - The desired length of the padded string
+ * @returns The zero-padded string
+ */
+function getZeroFill(text: string, length: number) {
+ return '0'.repeat(length - text.length) + text
+}
+
+/**
+ * Adds additional country information to the GeoData object
+ * @param geodata - The GeoData object to enhance
+ * @param settings - The API settings
+ * @returns A Promise that resolves to the enhanced GeoData
+ */
+async function setCountryInfo(geodata: GeoData, settings: IpLocationApiSettings): Promise {
+ if (settings.addCountryInfo && geodata.country) {
+ //* Import the countries-list package (optional peer dependency)
+ try {
+ const { countries, continents } = await import('countries-list')
+ const country = countries[geodata.country as keyof typeof countries]
+
+ //* Enhance geodata with additional country information
+ geodata.country_name = country.name
+ geodata.country_native = country.native
+ geodata.continent = country.continent
+ geodata.continent_name = continents[country.continent]
+ geodata.capital = country.capital
+ geodata.phone = country.phone
+ geodata.currency = country.currency
+ geodata.languages = country.languages
+ /* c8 ignore next 5 */ //* We don't check the try-catch as it's an optional peer dependency
+ }
+ catch (error) {
+ log('warn', 'Error importing countries-list', error)
+ }
+ }
+ return geodata
+}
diff --git a/packages/ip-location-api/src/functions/reload.ts b/packages/ip-location-api/src/functions/reload.ts
new file mode 100644
index 0000000..d13c773
--- /dev/null
+++ b/packages/ip-location-api/src/functions/reload.ts
@@ -0,0 +1,140 @@
+import type { IpLocationApiInputSettings } from '@iplookup/util'
+import type { Buffer } from 'node:buffer'
+import { existsSync } from 'node:fs'
+import { readFile } from 'node:fs/promises'
+import { join } from 'node:path'
+import { getSettings, SAVED_SETTINGS } from '@iplookup/util'
+import { update } from '@iplookup/util/db'
+
+export const LOADED_DATA: {
+ location?: Buffer
+ city?: Buffer
+ sub?: {
+ region1?: string[]
+ region2?: string[]
+ timezone?: string[]
+ area?: number[]
+ eu?: Record
+ }
+} = {}
+
+/**
+ * Reloads the IP location data based on the provided settings.
+ * @param inputSettings - Optional input settings to override the default settings.
+ */
+export async function reload(inputSettings?: IpLocationApiInputSettings): Promise {
+ const settings = getSettings(inputSettings)
+
+ //* If the data directory doesn't exist, update the database
+ if (!existsSync(join(settings.fieldDir, '4-1.dat'))) {
+ await update(settings)
+ }
+
+ const buffers = {
+ v4: {
+ dat1: undefined as Buffer | undefined,
+ dat2: undefined as Buffer | undefined,
+ dat3: undefined as Buffer | undefined,
+ },
+ v6: {
+ dat1: undefined as Buffer | undefined,
+ dat2: undefined as Buffer | undefined,
+ dat3: undefined as Buffer | undefined,
+ },
+ location: undefined as Buffer | undefined,
+ city: undefined as Buffer | undefined,
+ sub: undefined as Buffer | undefined,
+ }
+
+ //* Create an array of promises to read all necessary files
+ const promises: Promise[] = [
+ readFile(join(settings.fieldDir, '4-1.dat')).then((buffer) => { buffers.v4.dat1 = buffer }),
+ readFile(join(settings.fieldDir, '6-1.dat')).then((buffer) => { buffers.v6.dat1 = buffer }),
+ ]
+
+ //* Add additional file reading promises if not in small memory mode
+ if (!settings.smallMemory) {
+ promises.push(
+ readFile(join(settings.fieldDir, '4-2.dat')).then((buffer) => { buffers.v4.dat2 = buffer }),
+ readFile(join(settings.fieldDir, '4-3.dat')).then((buffer) => { buffers.v4.dat3 = buffer }),
+ readFile(join(settings.fieldDir, '6-2.dat')).then((buffer) => { buffers.v6.dat2 = buffer }),
+ readFile(join(settings.fieldDir, '6-3.dat')).then((buffer) => { buffers.v6.dat3 = buffer }),
+ )
+ }
+
+ //* Add location-related file reading promises based on settings
+ if (settings.locationFile) {
+ promises.push(
+ readFile(join(settings.fieldDir, 'location.dat')).then((buffer) => { buffers.location = buffer }),
+ )
+
+ if (settings.fields.includes('city')) {
+ promises.push(
+ readFile(join(settings.fieldDir, 'name.dat')).then((buffer) => { buffers.city = buffer }),
+ )
+ }
+
+ if (settings.fields.some(field => ['region1_name', 'region2_name', 'timezone', 'area', 'eu'].includes(field))) {
+ promises.push(
+ readFile(join(settings.fieldDir, 'sub.json')).then((buffer) => { buffers.sub = buffer }),
+ )
+ }
+ }
+
+ //* Wait for all file reading operations to complete
+ await Promise.all(promises)
+
+ const v4 = settings.v4
+ const v6 = settings.v6
+
+ //* Create typed arrays from the loaded buffer data
+ const v4StartIps = new Uint32Array(buffers.v4.dat1!.buffer, 0, buffers.v4.dat1!.byteLength >> 2)
+ const v6StartIps = new BigUint64Array(buffers.v6.dat1!.buffer, 0, buffers.v6.dat1!.byteLength >> 3)
+
+ //* Set up v4 loaded data
+ v4.loadedData = {
+ startIps: v4StartIps,
+ endIps: buffers.v4.dat2 ? new Uint32Array(buffers.v4.dat2.buffer, 0, buffers.v4.dat2.byteLength >> 2) : undefined,
+ mainBuffer: buffers.v4.dat3,
+ lastLine: v4StartIps.length - 1,
+ firstIp: v4StartIps[0]!,
+ }
+
+ //* Set up v6 loaded data
+ v6.loadedData = {
+ startIps: v6StartIps,
+ endIps: buffers.v6.dat2 ? new BigUint64Array(buffers.v6.dat2.buffer, 0, buffers.v6.dat2.byteLength >> 3) : undefined,
+ mainBuffer: buffers.v6.dat3,
+ lastLine: v6StartIps.length - 1,
+ firstIp: v6StartIps[0]!,
+ }
+
+ //* Load additional data for City dataType
+ if (settings.dataType === 'City') {
+ LOADED_DATA.location = buffers.location
+ LOADED_DATA.city = buffers.city
+ if (buffers.sub) {
+ const subJson = JSON.parse(buffers.sub.toString())
+ LOADED_DATA.sub = {
+ region1: subJson.region1_name,
+ region2: subJson.region2_name,
+ timezone: subJson.timezone,
+ area: subJson.area,
+ eu: subJson.eu,
+ }
+ }
+ }
+}
+
+/**
+ * Clears the loaded IP location data from memory.
+ */
+export function clear(): void {
+ const settings = SAVED_SETTINGS
+ settings.v4.loadedData = undefined
+ settings.v6.loadedData = undefined
+
+ LOADED_DATA.location = undefined
+ LOADED_DATA.city = undefined
+ LOADED_DATA.sub = undefined
+}
diff --git a/packages/ip-location-api/src/index.all.smallMemory.test.ts b/packages/ip-location-api/src/index.all.smallMemory.test.ts
new file mode 100644
index 0000000..da642cf
--- /dev/null
+++ b/packages/ip-location-api/src/index.all.smallMemory.test.ts
@@ -0,0 +1,158 @@
+import { randomUUID } from 'node:crypto'
+import { rm } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { afterAll, beforeAll, describe, it } from 'vitest'
+import { clear, lookup, reload } from './index'
+
+describe('lookup (all, smallMemory)', () => {
+ const id = randomUUID()
+
+ beforeAll(async () => {
+ await reload({
+ dataDir: resolve(__dirname, '../data', id),
+ tmpDataDir: resolve(__dirname, '../tmp', id),
+ fields: 'all',
+ smallMemory: true,
+ addCountryInfo: true,
+ // silent: true,
+ })
+ }, 25 * 60_000)
+
+ afterAll(async () => {
+ clear()
+ await rm(resolve(__dirname, '../data', id), { recursive: true, force: true })
+ await rm(resolve(__dirname, '../tmp', id), { recursive: true, force: true })
+ })
+
+ it('should return all fields for a valid IP address', async ({ expect }) => {
+ //* Ipv4 (Metro code)
+ const result1 = await lookup('170.171.1.0')
+ expect.soft(result1).not.toBeNull()
+ expect.soft(result1).toEqual({
+ area: 10,
+ capital: 'Washington D.C.',
+ city: 'New York',
+ continent: 'NA',
+ continent_name: 'North America',
+ country: 'US',
+ country_name: 'United States',
+ country_native: 'United States',
+ currency: [
+ 'USD',
+ 'USN',
+ 'USS',
+ ],
+ eu: undefined,
+ languages: [
+ 'en',
+ ],
+ latitude: 40.7621,
+ longitude: -73.9517,
+ metro: 501,
+ phone: [
+ 1,
+ ],
+ postcode: '10044',
+ region1: 'NY',
+ region1_name: 'New York',
+ timezone: 'America/New_York',
+ })
+
+ //* Ipv4 (Region2)
+ const result2 = await lookup('213.189.170.238')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({
+ area: 50,
+ capital: 'Brussels',
+ city: 'Charleroi',
+ continent: 'EU',
+ continent_name: 'Europe',
+ country: 'BE',
+ country_name: 'Belgium',
+ country_native: 'België',
+ currency: [
+ 'EUR',
+ ],
+ eu: true,
+ languages: [
+ 'nl',
+ 'fr',
+ 'de',
+ ],
+ latitude: 50.4102,
+ longitude: 4.4472,
+ phone: [
+ 32,
+ ],
+ postcode: '6000',
+ region1: 'WAL',
+ region1_name: 'Wallonia',
+ region2: 'WHT',
+ region2_name: 'Hainaut Province',
+ timezone: 'Europe/Brussels',
+ })
+
+ const postalCodesTest = [
+ {
+ ip: '45.67.84.0',
+ postcode: 'EC1A',
+ },
+ {
+ ip: '106.160.170.0',
+ postcode: '402-0051',
+ },
+ {
+ ip: '172.94.24.0',
+ postcode: 'LV-1063',
+ },
+ ]
+
+ for (const { ip, postcode } of postalCodesTest) {
+ const result = await lookup(ip)
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toMatchObject({ postcode })
+ }
+
+ //* Ipv6
+ const result4 = await lookup('2607:F8B0:4005:801::200E')
+ expect.soft(result4).not.toBeNull()
+ expect.soft(result4).toEqual({
+ area: 1000,
+ capital: 'Washington D.C.',
+ continent: 'NA',
+ continent_name: 'North America',
+ country: 'US',
+ country_name: 'United States',
+ country_native: 'United States',
+ currency: [
+ 'USD',
+ 'USN',
+ 'USS',
+ ],
+ eu: undefined,
+ languages: [
+ 'en',
+ ],
+ latitude: 37.751,
+ longitude: -97.822,
+ phone: [
+ 1,
+ ],
+ timezone: 'America/Chicago',
+ })
+
+ //* Invalid IP address
+ await expect.soft(lookup('invalid')).rejects.toThrowError('Invalid IPv4 address: invalid')
+
+ //* Too high IP address
+ await expect.soft(lookup('256.256.256.256')).resolves.toBeNull()
+
+ //* Clear data
+ clear()
+
+ //* Ips should return null after data is cleared
+ await expect.soft(lookup('8.8.8.8')).resolves.toBeNull()
+
+ await expect.soft(lookup('2607:F8B0:4005:801::200E')).resolves.toBeNull()
+ })
+})
diff --git a/packages/ip-location-api/src/index.city.test.ts b/packages/ip-location-api/src/index.city.test.ts
new file mode 100644
index 0000000..e617534
--- /dev/null
+++ b/packages/ip-location-api/src/index.city.test.ts
@@ -0,0 +1,36 @@
+import { randomUUID } from 'node:crypto'
+import { rm } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { afterAll, beforeAll, describe, it } from 'vitest'
+import { clear, lookup, reload } from './index'
+
+describe('lookup (city)', () => {
+ const id = randomUUID()
+
+ beforeAll(async () => {
+ await reload({
+ dataDir: resolve(__dirname, '../data', id),
+ tmpDataDir: resolve(__dirname, '../tmp', id),
+ fields: ['city', 'country'],
+ silent: true,
+ })
+ }, 25 * 60_000)
+
+ afterAll(async () => {
+ clear()
+ await rm(resolve(__dirname, '../data', id), { recursive: true, force: true })
+ await rm(resolve(__dirname, '../tmp', id), { recursive: true, force: true })
+ })
+
+ it('should return the city and country for a valid IP address', async ({ expect }) => {
+ //* Ipv4
+ const result = await lookup('170.171.1.0')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({ city: 'New York', country: 'US' })
+
+ //* Ipv6
+ const result2 = await lookup('2606:2e00:8003::216:3eff:fe82:68ab')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({ city: 'New York', country: 'US' })
+ })
+})
diff --git a/packages/ip-location-api/src/index.country.smallMemory.test.ts b/packages/ip-location-api/src/index.country.smallMemory.test.ts
new file mode 100644
index 0000000..2c04811
--- /dev/null
+++ b/packages/ip-location-api/src/index.country.smallMemory.test.ts
@@ -0,0 +1,55 @@
+import { randomUUID } from 'node:crypto'
+import { rm } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { afterAll, beforeAll, describe, it } from 'vitest'
+import { clear, lookup, reload } from './index'
+
+describe('lookup (country, smallMemory)', () => {
+ const id = randomUUID()
+
+ beforeAll(async () => {
+ await reload({
+ dataDir: resolve(__dirname, '../data', id),
+ tmpDataDir: resolve(__dirname, '../tmp', id),
+ fields: ['country'],
+ smallMemory: true,
+ silent: true,
+ })
+ }, 5 * 60_000)
+
+ afterAll(async () => {
+ clear()
+ await rm(resolve(__dirname, '../data', id), { recursive: true, force: true })
+ await rm(resolve(__dirname, '../tmp', id), { recursive: true, force: true })
+ })
+
+ it('should return the country for a valid IP address', async ({ expect }) => {
+ //* Ipv4
+ const result = await lookup('8.8.8.8')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({ country: 'US' })
+
+ const result2 = await lookup('207.97.227.239')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({ country: 'US' })
+
+ //* Ipv6
+ const result3 = await lookup('2607:F8B0:4005:801::200E')
+ expect.soft(result3).not.toBeNull()
+ expect.soft(result3).toEqual({ country: 'US' })
+
+ //* Invalid IP address
+ await expect.soft(lookup('invalid')).rejects.toThrowError('Invalid IPv4 address: invalid')
+
+ //* Too high IP address
+ await expect.soft(lookup('256.256.256.256')).resolves.toBeNull()
+
+ //* Clear data
+ clear()
+
+ //* Ips should return null after data is cleared
+ await expect.soft(lookup('8.8.8.8')).resolves.toBeNull()
+
+ await expect.soft(lookup('2607:F8B0:4005:801::200E')).resolves.toBeNull()
+ })
+})
diff --git a/packages/ip-location-api/src/index.country.test.ts b/packages/ip-location-api/src/index.country.test.ts
new file mode 100644
index 0000000..a03dc91
--- /dev/null
+++ b/packages/ip-location-api/src/index.country.test.ts
@@ -0,0 +1,54 @@
+import { randomUUID } from 'node:crypto'
+import { rm } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { afterAll, beforeAll, describe, it } from 'vitest'
+import { clear, lookup, reload } from './index'
+
+describe('lookup (country)', () => {
+ const id = randomUUID()
+
+ beforeAll(async () => {
+ await reload({
+ dataDir: resolve(__dirname, '../data', id),
+ tmpDataDir: resolve(__dirname, '../tmp', id),
+ fields: ['country'],
+ silent: true,
+ })
+ }, 5 * 60_000)
+
+ afterAll(async () => {
+ clear()
+ await rm(resolve(__dirname, '../data', id), { recursive: true, force: true })
+ await rm(resolve(__dirname, '../tmp', id), { recursive: true, force: true })
+ })
+
+ it('should return the country for a valid IP address', async ({ expect }) => {
+ //* Ipv4
+ const result = await lookup('8.8.8.8')
+ expect.soft(result).not.toBeNull()
+ expect.soft(result).toEqual({ country: 'US' })
+
+ const result2 = await lookup('207.97.227.239')
+ expect.soft(result2).not.toBeNull()
+ expect.soft(result2).toEqual({ country: 'US' })
+
+ //* Ipv6
+ const result3 = await lookup('2607:F8B0:4005:801::200E')
+ expect.soft(result3).not.toBeNull()
+ expect.soft(result3).toEqual({ country: 'US' })
+
+ //* Invalid IP address
+ await expect.soft(lookup('invalid')).rejects.toThrowError('Invalid IPv4 address: invalid')
+
+ //* Too high IP address
+ await expect.soft(lookup('256.256.256.256')).resolves.toBeNull()
+
+ //* Clear data
+ clear()
+
+ //* Ips should return null after data is cleared
+ await expect.soft(lookup('8.8.8.8')).resolves.toBeNull()
+
+ await expect.soft(lookup('2607:F8B0:4005:801::200E')).resolves.toBeNull()
+ })
+})
diff --git a/packages/ip-location-api/src/index.ts b/packages/ip-location-api/src/index.ts
new file mode 100644
index 0000000..069dda6
--- /dev/null
+++ b/packages/ip-location-api/src/index.ts
@@ -0,0 +1,3 @@
+export { lookup } from './functions/lookup.js'
+export { clear, reload } from './functions/reload.js'
+export { update } from '@iplookup/util/db'
diff --git a/packages/ip-location-api/tsconfig.json b/packages/ip-location-api/tsconfig.json
new file mode 100644
index 0000000..0f3afd7
--- /dev/null
+++ b/packages/ip-location-api/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "noEmit": true
+ }
+}
diff --git a/packages/ip-location-api/vite.config.ts b/packages/ip-location-api/vite.config.ts
new file mode 100644
index 0000000..f475926
--- /dev/null
+++ b/packages/ip-location-api/vite.config.ts
@@ -0,0 +1,27 @@
+import { defineConfig } from 'vite'
+import checker from 'vite-plugin-checker'
+import dts from 'vite-plugin-dts'
+
+export default defineConfig({
+ build: {
+ emptyOutDir: true,
+ lib: {
+ entry: {
+ index: 'src/index.ts',
+ },
+ formats: ['es', 'cjs'],
+ name: 'IpLookup',
+ },
+ rollupOptions: {
+ external: ['@fast-csv/parse', 'ip-address', 'ky', 'yauzl', 'countries-list'],
+ },
+ sourcemap: true,
+ ssr: true,
+ },
+ plugins: [
+ checker({
+ typescript: true,
+ }),
+ dts(),
+ ],
+})
diff --git a/packages/util/environment.d.ts b/packages/util/environment.d.ts
new file mode 100644
index 0000000..e8320e2
--- /dev/null
+++ b/packages/util/environment.d.ts
@@ -0,0 +1,6 @@
+declare global {
+ const __DATA_TYPE__: 'country' | 'geocode'
+ const __CDN_URL__: string
+}
+
+export {}
diff --git a/packages/util/package.json b/packages/util/package.json
new file mode 100644
index 0000000..441df0b
--- /dev/null
+++ b/packages/util/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@iplookup/util",
+ "type": "module",
+ "version": "0.0.0",
+ "private": true,
+ "exports": {
+ ".": "./dist/index.js",
+ "./browser": "./dist/browser.js",
+ "./browserIndex": "./dist/browserIndex.js",
+ "./db": "./dist/db.js",
+ "./dbIpUpdate": "./dist/dbIpUpdate.js"
+ },
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "build:watch": "tsc --watch"
+ },
+ "dependencies": {
+ "@fast-csv/parse": "^5.0.0",
+ "ip-address": "^9.0.5",
+ "ky": "^1.7.2",
+ "yauzl": "^3.1.3"
+ },
+ "devDependencies": {
+ "@types/yauzl": "^2.10.3"
+ }
+}
diff --git a/packages/util/src/browser.ts b/packages/util/src/browser.ts
new file mode 100644
index 0000000..544738f
--- /dev/null
+++ b/packages/util/src/browser.ts
@@ -0,0 +1,184 @@
+import { binarySearch } from './functions/binarySearch.js'
+import { fetchArrayBuffer } from './functions/fetchArrayBuffer.js'
+import { log } from './functions/log.js'
+import { numberToCountryCode } from './functions/numberToCountryCode.js'
+import { numberToDir } from './functions/numberToDir.js'
+import { parseIp } from './functions/parseIp.js'
+
+/**
+ * Sets up an IP lookup function based on the specified data type.
+ * @template T - The type of data to return ('country' or 'geocode')
+ * @returns A function that takes an IP address and returns location data
+ */
+export function setup(): (ipInput: string) => Promise {
+ const CDN_URL = __CDN_URL__
+ const MAIN_RECORD_SIZE = __DATA_TYPE__ === 'country' ? 2 : 8
+ const INDEXES: {
+ 4?: Uint32Array
+ 6?: BigUint64Array
+ } = {}
+ const DATA_URL = {
+ 4: CDN_URL,
+ 6: CDN_URL,
+ }
+
+ /**
+ * Performs an IP lookup and returns location data.
+ * @param ipInput - The IP address to look up
+ * @returns A promise that resolves to location data or null if not found
+ */
+ return async function IpLookup(ipInput: string) {
+ const { version, ip } = parseIp(ipInput)
+
+ //* Get the index for the IP version
+ const index = INDEXES[version] ?? (await loadIndex(version))
+ if (!index) {
+ log('warn', 'No index found')
+ return null
+ }
+
+ //* If the IP is less than the first index, return null
+ if (!(ip >= index[0]!)) {
+ log('warn', `IP ${ipInput} is out of range`)
+ return null
+ }
+
+ //* Binary search to find the correct line in the index
+ const lineIndex = binarySearch(index, ip)
+ if (lineIndex === null) {
+ return null
+ }
+
+ //* Get the data file for the line
+ const dataResponse = await fetchArrayBuffer(
+ new URL(`${DATA_URL[version]}/indexes/${version}/${numberToDir(lineIndex)}`),
+ )
+ if (!dataResponse) {
+ log('warn', 'Index file not found, is it corrupted?')
+ return null
+ }
+
+ const { buffer: dataBuffer } = dataResponse
+ const ipSize = (version - 2) * 2
+ const recordSize = MAIN_RECORD_SIZE + ipSize * 2
+ const recordCount = dataBuffer.byteLength / recordSize
+ const startList = version === 4
+ ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount))
+ : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount))
+
+ const recordIndex = binarySearch(startList, ip)
+ if (recordIndex === null) {
+ return null
+ }
+
+ const endIp = getEndIp(dataBuffer, version, recordCount, recordIndex, ipSize)
+ if (ip >= startList[recordIndex]! && ip <= endIp) {
+ return parseRecord(dataBuffer, recordCount, recordIndex, ipSize, MAIN_RECORD_SIZE)
+ }
+
+ return null
+ } as (ipInput: string) => Promise
+
+ /**
+ * Loads the index for the specified IP version.
+ * @param ipVersion - The IP version (4 or 6)
+ * @returns A promise that resolves to the loaded index
+ */
+ async function loadIndex(ipVersion: 4 | 6) {
+ const baseUrl = getBaseUrl()
+ return downloadIndex(baseUrl, ipVersion)
+ }
+
+ /**
+ * Determines the base URL for downloading the index.
+ * @returns The base URL as a string
+ */
+ function getBaseUrl(): string {
+ //* If we are not in the DOM we just use the CDN_URL to download the index
+ if (typeof document === 'undefined' || !document.currentScript) {
+ return CDN_URL
+ }
+
+ //* If we aren't in a SCRIPT element we use the CDN_URL
+ if (!(document.currentScript instanceof HTMLScriptElement)) {
+ return CDN_URL
+ }
+
+ //* Extract the base URL from the script's src attribute
+ return document.currentScript.src.split('/').slice(0, -1).join('/')
+ }
+
+ /**
+ * Downloads the index file for the specified IP version.
+ * @param baseUrl - The base URL for downloading
+ * @param version - The IP version (4 or 6)
+ * @returns A promise that resolves to the downloaded index
+ */
+ async function downloadIndex(baseUrl: string, version: 4 | 6) {
+ const result = await fetchArrayBuffer(
+ new URL(`${baseUrl}/indexes/${version}.idx`),
+ )
+ if (!result) {
+ log('warn', 'Index file not found, is it corrupted?')
+ return null
+ }
+
+ const { versionHeader, buffer } = result
+ if (versionHeader)
+ DATA_URL[version] = `${CDN_URL}@${versionHeader}`
+ if (version === 4)
+ return (INDEXES[version] = new Uint32Array(buffer))
+ return (INDEXES[version] = new BigUint64Array(buffer))
+ }
+
+ /**
+ * Retrieves the end IP for a given record.
+ * @param dataBuffer - The buffer containing IP data
+ * @param ipVersion - The IP version (4 or 6)
+ * @param recordCount - The total number of records
+ * @param recordIndex - The index of the current record
+ * @param ipSize - The size of an IP address in bytes
+ * @returns The end IP as a number
+ */
+ function getEndIp(dataBuffer: ArrayBuffer, ipVersion: 4 | 6, recordCount: number, recordIndex: number, ipSize: number) {
+ const endIpBuffer = dataBuffer.slice(
+ (recordCount + recordIndex) * ipSize,
+ (recordCount + recordIndex + 1) * ipSize,
+ )
+ return ipVersion === 4
+ ? new Uint32Array(endIpBuffer)[0]!
+ : new BigUint64Array(endIpBuffer)[0]!
+ }
+
+ /**
+ * Parses a record and returns location data.
+ * @param dataBuffer - The buffer containing record data
+ * @param recordCount - The total number of records
+ * @param recordIndex - The index of the current record
+ * @param ipSize - The size of an IP address in bytes
+ * @param MAIN_RECORD_SIZE - The size of the main record data
+ * @returns Location data object
+ */
+ function parseRecord(dataBuffer: ArrayBuffer, recordCount: number, recordIndex: number, ipSize: number, MAIN_RECORD_SIZE: number) {
+ const recordBuffer = dataBuffer.slice(
+ recordCount * ipSize * 2 + recordIndex * MAIN_RECORD_SIZE,
+ recordCount * ipSize * 2 + (recordIndex + 1) * MAIN_RECORD_SIZE,
+ )
+
+ if (__DATA_TYPE__ === 'country') {
+ const ccCode = new Uint16Array(recordBuffer)[0]!
+ return { country: String.fromCharCode(ccCode & 255, ccCode >> 8) }
+ }
+ else {
+ const recordData = new Int32Array(recordBuffer)
+ const latitudeData = recordData[0]!
+ const longitudeData = recordData[1]!
+ const countryCode = numberToCountryCode(latitudeData & 1023)
+ return {
+ latitude: (latitudeData >> 10) / 10000,
+ longitude: longitudeData / 10000,
+ country: countryCode,
+ }
+ }
+ }
+}
diff --git a/packages/util/src/browserIndex.ts b/packages/util/src/browserIndex.ts
new file mode 100644
index 0000000..2f0854f
--- /dev/null
+++ b/packages/util/src/browserIndex.ts
@@ -0,0 +1,93 @@
+import { Buffer } from 'node:buffer'
+import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
+import { join } from 'node:path'
+import { getSettings, type IpLocationApiInputSettings, type IpLocationApiSettings, processDirectory } from './functions/getSettings.js'
+import { numberToDir } from './functions/numberToDir.js'
+
+/**
+ * Creates a browser index for IP location data.
+ * @param type - The type of data to create an index for ('country' or 'geocode').
+ * @param inputSettings - The input settings for the IP location API.
+ * @param directory - The directory to store the created index.
+ */
+export async function createBrowserIndex(
+ type: 'country' | 'geocode',
+ inputSettings: IpLocationApiInputSettings,
+ directory: string,
+) {
+ const settings = getSettings(inputSettings)
+ const exportDir = processDirectory(directory)
+ await rm(exportDir, { recursive: true, force: true })
+
+ await processIpVersion(type, settings, exportDir, '4')
+ await processIpVersion(type, settings, exportDir, '6')
+}
+
+/**
+ * Processes and creates index files for a specific IP version.
+ * @param type - The type of data to process ('country' or 'geocode').
+ * @param settings - The settings for the IP location API.
+ * @param exportDir - The directory to export the processed data.
+ * @param ipVersion - The IP version to process ('4' or '6').
+ */
+async function processIpVersion(
+ type: 'country' | 'geocode',
+ settings: IpLocationApiSettings,
+ exportDir: string,
+ ipVersion: '4' | '6',
+) {
+ //* Create the directory for the IP version
+ await mkdir(join(exportDir, ipVersion), { recursive: true })
+
+ const indexSize = type === 'country' ? 1024 : 2048
+ const startBuf = await readFile(join(settings.fieldDir, `${ipVersion}-1.dat`))
+ const endBuf = await readFile(join(settings.fieldDir, `${ipVersion}-2.dat`))
+ const dbInfo = await readFile(join(settings.fieldDir, `${ipVersion}-3.dat`))
+
+ //* Create typed arrays for efficient data processing
+ const startList = ipVersion === '4' ? new Uint32Array(startBuf.buffer) : new BigUint64Array(startBuf.buffer)
+ const endList = ipVersion === '4' ? new Uint32Array(endBuf.buffer) : new BigUint64Array(endBuf.buffer)
+ const dbList = type === 'country' ? new Uint16Array(dbInfo.buffer) : new Int32Array(dbInfo.buffer)
+
+ const length = startList.length
+ const indexList = ipVersion === '4' ? new Uint32Array(indexSize) : new BigUint64Array(indexSize)
+ const recordSize = settings.mainRecordSize + (ipVersion === '4' ? 8 : 16)
+
+ //* Process and write index files
+ for (let i = 0; i < indexSize; ++i) {
+ const index = length * i / indexSize | 0
+ indexList[i] = startList[index] as number | bigint
+ const nextIndex = length * (i + 1) / indexSize | 0
+ const count = nextIndex - index
+
+ const exportBuf = Buffer.alloc(recordSize * count)
+ for (let j = index, k = 0; j < nextIndex; ++j) {
+ //* Write start and end IP addresses
+ if (ipVersion === '4') {
+ exportBuf.writeUInt32LE(startList[j] as number, k * 4)
+ exportBuf.writeUInt32LE(endList[j] as number, 4 * count + k * 4)
+ }
+ else {
+ exportBuf.writeBigUInt64LE(startList[j] as bigint, k * 8)
+ exportBuf.writeBigUInt64LE(endList[j] as bigint, 8 * count + k * 8)
+ }
+
+ //* Write country or geocode data
+ const offset = (ipVersion === '4' ? 8 : 16) * count + k * settings.mainRecordSize
+ if (type === 'country') {
+ exportBuf.writeUInt16LE(dbList[j]!, offset)
+ }
+ else {
+ exportBuf.writeInt32LE(dbList[2 * j]!, offset)
+ exportBuf.writeInt32LE(dbList[2 * j + 1]!, offset + 4)
+ }
+ ++k
+ }
+
+ //* Write the processed data to a file
+ await writeFile(join(exportDir, ipVersion, numberToDir(i)), exportBuf)
+ }
+
+ //* Write the index list to a file
+ await writeFile(join(exportDir, `${ipVersion}.idx`), Buffer.from(indexList.buffer))
+}
diff --git a/packages/util/src/constants.ts b/packages/util/src/constants.ts
new file mode 100644
index 0000000..368884c
--- /dev/null
+++ b/packages/util/src/constants.ts
@@ -0,0 +1,44 @@
+import type { IpLocationApiSettings, LocalDatabase } from './functions/getSettings.js'
+
+export const MAXMIND_URL = 'https://download.maxmind.com/app/geoip_download'
+export const DATABASE_SUFFIX_SHA = '.zip.sha256'
+export const DATABASE_SUFFIX_ZIP = '.zip'
+export const MAIN_FIELDS = ['latitude', 'longitude', 'area', 'postcode'] as const
+export const LOCATION_FIELDS = ['country', 'region1', 'region1_name', 'region2', 'region2_name', 'metro', 'timezone', 'city', 'eu'] as const
+export const v4: LocalDatabase<4> = {
+ version: 4,
+ recordSize: 0,
+ fileLineMax: 0,
+ folderLineMax: 0,
+}
+export const v6: LocalDatabase<6> = {
+ version: 6,
+ recordSize: 0,
+ fileLineMax: 0,
+ folderLineMax: 0,
+}
+export const DEFAULT_SETTINGS: IpLocationApiSettings = {
+ licenseKey: 'redist',
+ series: 'GeoLite2',
+ dataDir: '../data',
+ tmpDataDir: '../tmp',
+ fields: ['country'],
+ language: 'en',
+ smallMemory: false,
+ smallMemoryFileSize: 4096,
+ //* Generated settings
+ fieldDir: '',
+ dataType: 'Country',
+ locationFile: false,
+ mainRecordSize: 2,
+ locationRecordSize: 0,
+ v4,
+ v6,
+ addCountryInfo: false,
+ silent: false,
+}
+// eslint-disable-next-line import/no-mutable-exports
+export let SAVED_SETTINGS: IpLocationApiSettings = DEFAULT_SETTINGS
+export function setSavedSettings(settings: IpLocationApiSettings) {
+ return SAVED_SETTINGS = settings
+}
diff --git a/packages/util/src/db.ts b/packages/util/src/db.ts
new file mode 100644
index 0000000..0028b22
--- /dev/null
+++ b/packages/util/src/db.ts
@@ -0,0 +1,47 @@
+import type { IpLocationApiInputSettings } from './functions/getSettings.js'
+
+import { cp, readdir, rename, rm, writeFile } from 'node:fs/promises'
+import path from 'node:path'
+import { DATABASE_SUFFIX_SHA } from './constants.js'
+import { createDatabase } from './functions/database/createDatabase.js'
+import { downloadAndExtractDatabase } from './functions/database/downloadAndExtractDatabase.js'
+import { ensureDirectoriesExist } from './functions/database/ensureDirectoriesExist.js'
+import { getSettings } from './functions/getSettings.js'
+import { log } from './functions/log.js'
+
+/**
+ * Updates the GeoIP database based on the provided settings.
+ * @param inputSettings - Partial settings to override the default ones.
+ */
+export async function update(inputSettings?: Partial): Promise {
+ const settings = getSettings(inputSettings)
+ await ensureDirectoriesExist(settings)
+ log('info', 'Downloading database...')
+ const { files, sha256 } = await downloadAndExtractDatabase(settings)
+ if (!files || files.length === 0)
+ return
+
+ log('info', 'Creating database...')
+ await createDatabase(files, settings)
+ log('info', 'Database created')
+
+ //* Save the sha256 hash of the database
+ await writeFile(path.join(settings.fieldDir, `${settings.series}-${settings.dataType}-CSV${DATABASE_SUFFIX_SHA}`), sha256)
+
+ //* Rename the temporary files to the correct name
+ const tmpFiles = (await readdir(settings.fieldDir)).filter(file => file.endsWith('.tmp'))
+ for (const file of tmpFiles) {
+ await rename(path.join(settings.fieldDir, file), path.join(settings.fieldDir, file.replace('.tmp', '')))
+ }
+
+ if (settings.smallMemory) {
+ //* Copy the temporary files to the correct name
+ await cp(path.join(settings.fieldDir, 'v4-tmp'), path.join(settings.fieldDir, 'v4'), { recursive: true, force: true })
+ await cp(path.join(settings.fieldDir, 'v6-tmp'), path.join(settings.fieldDir, 'v6'), { recursive: true, force: true })
+ //* Remove the temporary files
+ await rm(path.join(settings.fieldDir, 'v4-tmp'), { recursive: true, force: true, maxRetries: 3 })
+ await rm(path.join(settings.fieldDir, 'v6-tmp'), { recursive: true, force: true, maxRetries: 3 })
+ }
+
+ log('info', 'Database successfully updated')
+}
diff --git a/packages/util/src/dbIpUpdate.ts b/packages/util/src/dbIpUpdate.ts
new file mode 100644
index 0000000..804e851
--- /dev/null
+++ b/packages/util/src/dbIpUpdate.ts
@@ -0,0 +1,132 @@
+import type { IpLocationApiInputSettings } from './functions/getSettings.js'
+import { Buffer } from 'node:buffer'
+import { createReadStream, createWriteStream, existsSync } from 'node:fs'
+import { writeFile } from 'node:fs/promises'
+import path from 'node:path'
+import { pipeline } from 'node:stream/promises'
+import { parse } from '@fast-csv/parse'
+import { aton4 } from './functions/aton4.js'
+import { aton6 } from './functions/aton6.js'
+import { countryCodeToNumber } from './functions/countryCodeToNumber.js'
+import { ensureDirectoriesExist } from './functions/database/ensureDirectoriesExist.js'
+import { getSettings } from './functions/getSettings.js'
+
+/**
+ * Updates the GeoIP database based on the provided settings using db-ip.com database.
+ * Downloads and processes a CSV file containing IP ranges with their corresponding geographical data.
+ * Creates binary data files for both IPv4 and IPv6 addresses.
+ *
+ * @param inputSettings - Partial settings to override the default configuration
+ * @returns Promise that resolves when the database update is complete
+ */
+export async function update(inputSettings?: Partial): Promise {
+ const settings = getSettings(inputSettings)
+ await ensureDirectoriesExist(settings)
+
+ //* Construct filename based on current year and month (db-ip updates monthly)
+ const fileName = `dbip-city-lite-${new Date().getFullYear()}-${new Date().getMonth() + 1}.csv`
+ const tempFilePath = path.join(settings.tmpDataDir, fileName)
+
+ //* Download and decompress the database file if it doesn't exist locally
+ if (!existsSync(tempFilePath)) {
+ const response = await fetch(`https://download.db-ip.com/free/${fileName}.gz`)
+ // @ts-expect-error DecompressionStream is supported in Node.js
+ await pipeline(response.body!.pipeThrough(new DecompressionStream('gzip')), createWriteStream(tempFilePath))
+ }
+
+ return new Promise((resolve, reject) => {
+ //* Arrays to store processed IP ranges and their metadata
+ const v4: [number, number, number, number][] = [] // [startIp, endIp, latitudeWithCountry, longitude]
+ const v6: [bigint, bigint, number, number][] = [] // [startIp, endIp, latitudeWithCountry, longitude]
+
+ //* Keep track of previous entries to merge consecutive ranges with same metadata
+ let previousDataV4: [number, number, number, number] | null = null
+ let previousDataV6: [bigint, bigint, number, number] | null = null
+
+ createReadStream(tempFilePath)
+ .pipe(parse())
+ .on('error', reject)
+ .on('data', ([
+ ipStart,
+ ipEnd,
+ , //* Skip unused columns from CSV
+ countryCode,
+ ,
+ ,
+ latitudeString,
+ longitudeString,
+ ]: [string, string, string, string, string, string, string, string]) => {
+ //* Skip invalid or special country codes
+ if (!countryCode || countryCode === 'ZZ' || countryCode === 'EU')
+ return
+
+ //* Convert coordinates to integers with 4 decimal precision
+ const latitude = Math.round((Number.parseFloat(latitudeString)) * 10000) // -90 ~ 90 -> -900000 ~ 900000
+ const longitude = Math.round((Number.parseFloat(longitudeString)) * 10000) // -180 ~ 180 -> -1800000 ~ 1800000
+
+ //* Combine latitude with country code for efficient storage
+ const latitudeWithCountryCode = (latitude) << 10 | countryCodeToNumber(countryCode)
+
+ if (ipStart.includes(':')) {
+ //* Handle IPv6 addresses
+ const start = aton6(ipStart)
+ //* Merge with previous range if consecutive and has same metadata
+ if (previousDataV6 && previousDataV6[1] + 1n === start
+ && previousDataV6[2] === latitudeWithCountryCode
+ && previousDataV6[3] === longitude) {
+ previousDataV6[1] = aton6(ipEnd)
+ return
+ }
+ v6.push(previousDataV6 = [aton6(ipStart), aton6(ipEnd), latitudeWithCountryCode, longitude])
+ }
+ else {
+ //* Handle IPv4 addresses
+ const start = aton4(ipStart)
+ //* Merge with previous range if consecutive and has same metadata
+ if (previousDataV4 && previousDataV4[1] + 1 === start
+ && previousDataV4[2] === latitudeWithCountryCode
+ && previousDataV4[3] === longitude) {
+ previousDataV4[1] = aton4(ipEnd)
+ return
+ }
+ v4.push(previousDataV4 = [aton4(ipStart), aton4(ipEnd), latitudeWithCountryCode, longitude])
+ }
+ })
+ .on('end', async () => {
+ //* Create binary buffers for IPv4 data
+ const v4Buf1 = Buffer.alloc(v4.length * 4) // Start IPs
+ const v4Buf2 = Buffer.alloc(v4.length * 4) // End IPs
+ const v4Buf3 = Buffer.alloc(v4.length * 8) // Metadata (latitude+country, longitude)
+
+ //* Write IPv4 data to buffers
+ for (let i = 0; i < v4.length; ++i) {
+ v4Buf1.writeUInt32LE(v4[i]![0], i * 4)
+ v4Buf2.writeUInt32LE(v4[i]![1], i * 4)
+ v4Buf3.writeInt32LE(v4[i]![2], i * 8)
+ v4Buf3.writeInt32LE(v4[i]![3], i * 8 + 4)
+ }
+
+ //* Create binary buffers for IPv6 data
+ const v6Buf1 = Buffer.alloc(v6.length * 8) // Start IPs
+ const v6Buf2 = Buffer.alloc(v6.length * 8) // End IPs
+ const v6Buf3 = Buffer.alloc(v6.length * 8) // Metadata (latitude+country, longitude)
+
+ //* Write IPv6 data to buffers
+ for (let i = 0; i < v6.length; ++i) {
+ v6Buf1.writeBigUInt64LE(v6[i]![0], i * 8)
+ v6Buf2.writeBigUInt64LE(v6[i]![1], i * 8)
+ v6Buf3.writeInt32LE(v6[i]![2], i * 8)
+ v6Buf3.writeInt32LE(v6[i]![3], i * 8 + 4)
+ }
+
+ //* Write all buffers to separate data files
+ await writeFile(path.join(settings.fieldDir, '4-1.dat'), v4Buf1)
+ await writeFile(path.join(settings.fieldDir, '4-2.dat'), v4Buf2)
+ await writeFile(path.join(settings.fieldDir, '4-3.dat'), v4Buf3)
+ await writeFile(path.join(settings.fieldDir, '6-1.dat'), v6Buf1)
+ await writeFile(path.join(settings.fieldDir, '6-2.dat'), v6Buf2)
+ await writeFile(path.join(settings.fieldDir, '6-3.dat'), v6Buf3)
+ resolve()
+ })
+ })
+}
diff --git a/packages/util/src/functions/aton4.test.ts b/packages/util/src/functions/aton4.test.ts
new file mode 100644
index 0000000..e459b69
--- /dev/null
+++ b/packages/util/src/functions/aton4.test.ts
@@ -0,0 +1,35 @@
+import { describe, expect, it } from 'vitest'
+import { aton4 } from './aton4'
+
+describe('aton4', () => {
+ it('should convert valid IPv4 addresses to 32-bit integers', () => {
+ expect.soft(aton4('192.168.1.1')).toBe(3232235777)
+ expect.soft(aton4('10.0.0.1')).toBe(167772161)
+ expect.soft(aton4('172.16.0.1')).toBe(2886729729)
+ expect.soft(aton4('255.255.255.255')).toBe(4294967295)
+ expect.soft(aton4('207.97.227.239')).toBe(3479299055)
+ expect.soft(aton4('0.0.0.0')).toBe(0)
+ })
+
+ it('should handle leading zeros in octets', () => {
+ expect.soft(aton4('192.168.001.001')).toBe(3232235777)
+ expect.soft(aton4('010.000.000.001')).toBe(167772161)
+ })
+
+ it('should throw an error for invalid IPv4 addresses', () => {
+ expect.soft(() => aton4('192.168.1')).toThrow('Invalid IPv4 address: 192.168.1')
+ expect.soft(() => aton4('192.168.1.1.1')).toThrow('Invalid IPv4 address: 192.168.1.1.1')
+ expect.soft(() => aton4('')).toThrow('Invalid IPv4 address: ')
+ })
+
+ it('should handle edge cases', () => {
+ expect.soft(aton4('127.0.0.1')).toBe(2130706433) // Localhost
+ expect.soft(aton4('255.255.255.0')).toBe(4294967040) // Subnet mask
+ expect.soft(aton4('1.2.3.4')).toBe(16909060)
+ })
+
+ it('should handle all zeros and all ones', () => {
+ expect.soft(aton4('0.0.0.0')).toBe(0)
+ expect.soft(aton4('255.255.255.255')).toBe(4294967295)
+ })
+})
diff --git a/packages/util/src/functions/aton4.ts b/packages/util/src/functions/aton4.ts
new file mode 100644
index 0000000..33e116f
--- /dev/null
+++ b/packages/util/src/functions/aton4.ts
@@ -0,0 +1,26 @@
+/**
+ * Convert an IPv4 string to a 32-bit integer
+ * @param string - The IPv4 string to convert
+ * @returns The 32-bit integer
+ * @example
+ * ```ts
+ * aton4('192.168.1.1') // 3232235777
+ * ```
+ * @throws Will throw an error if the input is not a valid IPv4 address
+ */
+export function aton4(string: string): number {
+ const parts = string.split(/\./) as [string, string, string, string]
+ if (parts.length !== 4) {
+ throw new Error(`Invalid IPv4 address: ${string}`)
+ }
+
+ //* Extract each octet and shift them to their correct position
+ const [a, b, c, d] = parts
+ const octet1 = Number.parseInt(a) << 24
+ const octet2 = Number.parseInt(b) << 16
+ const octet3 = Number.parseInt(c) << 8
+ const octet4 = Number.parseInt(d)
+
+ //* Combine the octets using bitwise OR and return the result
+ return (octet1 | octet2 | octet3 | octet4) >>> 0
+};
diff --git a/packages/util/src/functions/aton6.test.ts b/packages/util/src/functions/aton6.test.ts
new file mode 100644
index 0000000..3317b4b
--- /dev/null
+++ b/packages/util/src/functions/aton6.test.ts
@@ -0,0 +1,45 @@
+import { describe, expect, it } from 'vitest'
+import { aton6 } from './aton6'
+
+describe('aton6', () => {
+ it('converts a full IPv6 address correctly', () => {
+ expect.soft(aton6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(2306139570357600256n)
+ })
+
+ it('converts a shortened IPv6 address correctly', () => {
+ expect.soft(aton6('2001:db8::1')).toBe(2306139568115548160n)
+ })
+
+ it('handles IPv6 address with double colons in the middle', () => {
+ expect.soft(aton6('2001:db8::8a2e:370:7334')).toBe(2306139568115548160n)
+ })
+
+ it('handles IPv6 address with double colons at the end', () => {
+ expect.soft(aton6('2001:db8:85a3::')).toBe(2306139570357600256n)
+ })
+
+ it('handles IPv6 address with leading zeros omitted', () => {
+ expect.soft(aton6('2001:db8:1:2:3:4:5:6')).toBe(2306139568115613698n)
+ })
+
+ it('converts ::1 (localhost) correctly', () => {
+ expect.soft(aton6('::1')).toBe(0n)
+ })
+
+ it('converts :: (unspecified address) correctly', () => {
+ expect.soft(aton6('::')).toBe(0n)
+ })
+
+ it('handles IPv6 address with quotes', () => {
+ expect.soft(aton6('"2001:db8::1"')).toBe(2306139568115548160n)
+ })
+
+ it('returns 0 for invalid IPv6 address', () => {
+ expect.soft(aton6('invalid')).toBe(0n)
+ })
+
+ it('returns valid parts of IPv6 address with too many parts', () => {
+ expect.soft(aton6('2001:db8:85a3:0000:0000:8a2e:0370:7334')).toBe(2306139570357600256n)
+ expect.soft(aton6('2001:db8:85a3:0000:0000:8a2e:0370:7334:extra')).toBe(2306139570357600256n)
+ })
+})
diff --git a/packages/util/src/functions/aton6.ts b/packages/util/src/functions/aton6.ts
new file mode 100644
index 0000000..ca400e6
--- /dev/null
+++ b/packages/util/src/functions/aton6.ts
@@ -0,0 +1,33 @@
+/**
+ * Convert an IPv6 string to a 128-bit BigInt
+ * @param ipv6String - The IPv6 string to convert
+ * @returns The 128-bit BigInt representation
+ * @example
+ * ```ts
+ * aton6('2001:db8::1') // 2306139568115548160n
+ * ```
+ */
+export function aton6(ipv6String: string): bigint {
+ const parts = ipv6String.replace(/"/g, '').split(/:/)
+
+ const length = parts.length - 1
+ if (parts[length] === '')
+ parts[length] = '0'
+ if (length < 7) {
+ const omitted = 8 - parts.length
+ const omitStart = parts.indexOf('')
+ const omitEnd = omitStart + omitted
+ for (let i = 7; i >= omitStart; i--) {
+ parts[i] = i > omitEnd ? parts[i - omitted]! : '0'
+ }
+ }
+
+ let result = 0n
+ for (let i = 0; i < 4; i++) {
+ const part = parts[i]
+ if (part) {
+ result += BigInt(Number.parseInt(part, 16)) << BigInt(16 * (3 - i))
+ }
+ }
+ return result
+}
diff --git a/packages/util/src/functions/binarySearch.test.ts b/packages/util/src/functions/binarySearch.test.ts
new file mode 100644
index 0000000..986b8e8
--- /dev/null
+++ b/packages/util/src/functions/binarySearch.test.ts
@@ -0,0 +1,80 @@
+import { describe, expect, it } from 'vitest'
+import { binarySearch } from './binarySearch'
+
+describe('binarySearch', () => {
+ describe('uint32Array tests', () => {
+ const uint32Array = new Uint32Array([1, 3, 5, 7, 9, 11, 13, 15])
+
+ it('should find existing elements', () => {
+ expect.soft(binarySearch(uint32Array, 1)).toBe(0)
+ expect.soft(binarySearch(uint32Array, 7)).toBe(3)
+ expect.soft(binarySearch(uint32Array, 15)).toBe(7)
+ })
+
+ it('should return insertion point for non-existing elements', () => {
+ expect.soft(binarySearch(uint32Array, 4)).toBe(1)
+ expect.soft(binarySearch(uint32Array, 10)).toBe(4)
+ expect.soft(binarySearch(uint32Array, 16)).toBe(7)
+ })
+
+ it('should return null for elements smaller than all in the array', () => {
+ expect.soft(binarySearch(uint32Array, 0)).toBe(null)
+ })
+
+ it('should work with an array of length 1', () => {
+ const singleElementArray = new Uint32Array([5])
+ expect.soft(binarySearch(singleElementArray, 5)).toBe(0)
+ expect.soft(binarySearch(singleElementArray, 3)).toBe(null)
+ expect.soft(binarySearch(singleElementArray, 7)).toBe(0)
+ })
+
+ it('should work with an empty array', () => {
+ const emptyArray = new Uint32Array([])
+ expect.soft(binarySearch(emptyArray, 5)).toBe(null)
+ })
+ })
+
+ describe('bigUint64Array tests', () => {
+ const bigUint64Array = new BigUint64Array([1n, 3n, 5n, 7n, 9n, 11n, 13n, 15n])
+
+ it('should find existing elements', () => {
+ expect.soft(binarySearch(bigUint64Array, 1n)).toBe(0)
+ expect.soft(binarySearch(bigUint64Array, 7n)).toBe(3)
+ expect.soft(binarySearch(bigUint64Array, 15n)).toBe(7)
+ })
+
+ it('should return insertion point for non-existing elements', () => {
+ expect.soft(binarySearch(bigUint64Array, 4n)).toBe(1)
+ expect.soft(binarySearch(bigUint64Array, 10n)).toBe(4)
+ expect.soft(binarySearch(bigUint64Array, 16n)).toBe(7)
+ })
+
+ it('should return null for elements smaller than all in the array', () => {
+ expect.soft(binarySearch(bigUint64Array, 0n)).toBe(null)
+ })
+
+ it('should work with an array of length 1', () => {
+ const singleElementArray = new BigUint64Array([5n])
+ expect.soft(binarySearch(singleElementArray, 5n)).toBe(0)
+ expect.soft(binarySearch(singleElementArray, 3n)).toBe(null)
+ expect.soft(binarySearch(singleElementArray, 7n)).toBe(0)
+ })
+
+ it('should work with an empty array', () => {
+ const emptyArray = new BigUint64Array([])
+ expect.soft(binarySearch(emptyArray, 5n)).toBe(null)
+ })
+ })
+
+ describe('edge cases', () => {
+ it('should handle the maximum possible Uint32 value', () => {
+ const maxUint32Array = new Uint32Array([4294967295])
+ expect.soft(binarySearch(maxUint32Array, 4294967295)).toBe(0)
+ })
+
+ it('should handle the maximum possible BigUint64 value', () => {
+ const maxBigUint64Array = new BigUint64Array([18446744073709551615n])
+ expect.soft(binarySearch(maxBigUint64Array, 18446744073709551615n)).toBe(0)
+ })
+ })
+})
diff --git a/packages/util/src/functions/binarySearch.ts b/packages/util/src/functions/binarySearch.ts
new file mode 100644
index 0000000..57c6aa8
--- /dev/null
+++ b/packages/util/src/functions/binarySearch.ts
@@ -0,0 +1,31 @@
+/**
+ * Performs a binary search on a sorted array of BigUint64 or Uint32 values.
+ *
+ * @param array - The sorted array to search in (BigUint64Array or Uint32Array).
+ * @param target - The value to search for (number or bigint).
+ * @returns The index of the target if found, or the index where it would be inserted if not found.
+ * Returns null if the target is smaller than all elements in the array.
+ */
+export function binarySearch(array: BigUint64Array | Uint32Array, target: number | bigint): number | null {
+ let low = 0
+ let high = array.length - 1
+
+ while (low <= high) {
+ //* Bitwise right shift by 1 is equivalent to Math.floor((low + high) / 2)
+ const mid = (low + high) >> 1
+ const midValue = array[mid]!
+
+ if (target < midValue) {
+ high = mid - 1
+ }
+ else if (target > midValue) {
+ low = mid + 1
+ }
+ else {
+ return mid //* Target found
+ }
+ }
+
+ //* If target not found, return the insertion point or null if smaller than all elements
+ return high >= 0 ? high : null
+}
diff --git a/packages/util/src/functions/countryCodeToNumber.test.ts b/packages/util/src/functions/countryCodeToNumber.test.ts
new file mode 100644
index 0000000..cc9a6b1
--- /dev/null
+++ b/packages/util/src/functions/countryCodeToNumber.test.ts
@@ -0,0 +1,40 @@
+import { describe, expect, it } from 'vitest'
+import { countryCodeToNumber } from './countryCodeToNumber'
+
+describe('countryCodeToNumber', () => {
+ it('should convert "AA" to 0', () => {
+ expect.soft(countryCodeToNumber('AA')).toBe(0)
+ })
+
+ it('should convert "ZZ" to 675', () => {
+ expect.soft(countryCodeToNumber('ZZ')).toBe(675)
+ })
+
+ it('should convert "CA" to 52', () => {
+ expect.soft(countryCodeToNumber('CA')).toBe(52)
+ })
+
+ it('should convert "US" to 538', () => {
+ expect.soft(countryCodeToNumber('US')).toBe(538)
+ })
+
+ it('should throw an error for lowercase input', () => {
+ expect.soft(() => countryCodeToNumber('aa')).toThrow('Input must be a valid two-letter country code (A-Z)')
+ })
+
+ it('should throw an error for input with non-alphabetic characters', () => {
+ expect.soft(() => countryCodeToNumber('A1')).toThrow('Input must be a valid two-letter country code (A-Z)')
+ })
+
+ it('should throw an error for input with more than two characters', () => {
+ expect.soft(() => countryCodeToNumber('USA')).toThrow('Input must be a valid two-letter country code (A-Z)')
+ })
+
+ it('should throw an error for input with less than two characters', () => {
+ expect.soft(() => countryCodeToNumber('A')).toThrow('Input must be a valid two-letter country code (A-Z)')
+ })
+
+ it('should throw an error for empty input', () => {
+ expect.soft(() => countryCodeToNumber('')).toThrow('Input must be a valid two-letter country code (A-Z)')
+ })
+})
diff --git a/packages/util/src/functions/countryCodeToNumber.ts b/packages/util/src/functions/countryCodeToNumber.ts
new file mode 100644
index 0000000..2297553
--- /dev/null
+++ b/packages/util/src/functions/countryCodeToNumber.ts
@@ -0,0 +1,15 @@
+/**
+ * Converts a two-letter country code to a unique number.
+ * @param code - A two-letter country code (e.g., 'AA', 'AB', ..., 'ZZ').
+ * @returns A number representing the country code (0-675).
+ * @throws {Error} If the input code is not a valid two-letter code.
+ */
+export function countryCodeToNumber(code: string): number {
+ if (!/^[A-Z]{2}$/.test(code)) {
+ throw new Error('Input must be a valid two-letter country code (A-Z)')
+ }
+
+ //* Convert two-letter code to number using ASCII values
+ //* First letter: A-Z (65-90), Second letter: A-Z (65-90)
+ return (code.charCodeAt(0) - 65) * 26 + (code.charCodeAt(1) - 65)
+}
diff --git a/packages/util/src/functions/database/createBlockDatabase.ts b/packages/util/src/functions/database/createBlockDatabase.ts
new file mode 100644
index 0000000..4d06cc9
--- /dev/null
+++ b/packages/util/src/functions/database/createBlockDatabase.ts
@@ -0,0 +1,336 @@
+import type { WriteStream } from 'node:fs'
+import type { IpLocationApiSettings } from '../getSettings.js'
+import type { LocationData } from './createDatabase.js'
+import { Buffer } from 'node:buffer'
+import { createReadStream, createWriteStream, existsSync } from 'node:fs'
+import { rm } from 'node:fs/promises'
+import path from 'node:path'
+import { parse } from '@fast-csv/parse'
+import { Address4, Address6 } from 'ip-address'
+import { aton4 } from '../aton4.js'
+import { aton6 } from '../aton6.js'
+import { getPostcodeDatabase } from '../getPostcodeDatabase.js'
+import { log } from '../log.js'
+import { makeDatabase } from '../makeDatabase.js'
+import { createSmallMemoryFile } from './createSmallMemoryFile.js'
+
+/**
+ * Represents a row in the block database CSV file.
+ */
+interface BlockDatabaseRow {
+ network: string
+ geoname_id: string
+ latitude: string
+ longitude: string
+ accuracy_radius: string
+ postal_code: string
+}
+
+/**
+ * Creates a block database for IP geolocation.
+ * @param file - The CSV file containing IP block data.
+ * @param locationData - An array of records containing location data.
+ * @param locationIdList - A list of location IDs.
+ * @param areaDatabase - A database of area data.
+ * @param settings - IP location API settings.
+ */
+export async function createBlockDatabase(
+ file: string,
+ locationData: Record[],
+ locationIdList: number[],
+ areaDatabase: Record,
+ settings: IpLocationApiSettings,
+): Promise {
+ const version = file.endsWith('v4.csv') ? 4 : 6
+ log('info', `Creating block database for IPv${version}...`)
+
+ //* First count total lines
+ let totalLines = 0
+ await new Promise((resolve) => {
+ createReadStream(path.join(settings.tmpDataDir, file))
+ .pipe(parse({ headers: true }))
+ .on('data', () => { totalLines++ })
+ .on('end', resolve)
+ })
+
+ log('info', `Found ${totalLines.toLocaleString()} total records to process`)
+
+ const readStream = createReadStream(path.join(settings.tmpDataDir, file))
+ const writeStreamDat1 = createWriteStream(path.join(settings.fieldDir, `${version}-1.dat.tmp`), { highWaterMark: 1024 * 1024 })
+
+ let writeStreamDat2: WriteStream | undefined
+ let writeStreamDat3: WriteStream | undefined
+ let writeStreamSmallMemory: WriteStream | undefined
+
+ if (!settings.smallMemory) {
+ writeStreamDat2 = createWriteStream(path.join(settings.fieldDir, `${version}-2.dat.tmp`), { highWaterMark: 1024 * 1024 })
+ writeStreamDat3 = createWriteStream(path.join(settings.fieldDir, `${version}-3.dat.tmp`), { highWaterMark: 1024 * 1024 })
+ }
+ else {
+ const dir = path.join(settings.fieldDir, `v${version}-tmp`)
+ if (existsSync(dir)) {
+ await rm(dir, { recursive: true, force: true })
+ }
+ }
+
+ return new Promise((resolve, reject) => {
+ let checkCount = 0
+ function check() {
+ if (++checkCount === 3) {
+ log('info', `Completed processing IPv${version} block database`)
+ resolve()
+ }
+ }
+
+ let previousData: {
+ countryCode?: string
+ end: number | bigint
+ buffer1?: Buffer
+ buffer2?: Buffer
+ buffer3?: Buffer
+ locationId?: number
+ latitude?: number
+ longitude?: number
+ accuracyRadius?: string
+ postalCode?: string
+ counter?: number
+ } | undefined
+ let lineCount = 0
+ let processedCount = 0
+ const logInterval = 100000 // Log every 100k records
+
+ readStream.pipe(parse({ headers: true }))
+ .on('error', (error) => {
+ log('error', `Error processing IPv${version} block database: ${error.message}`)
+ reject(error)
+ })
+ .on('data', (row: BlockDatabaseRow) => {
+ processedCount++
+ if (processedCount % logInterval === 0) {
+ const percentage = ((processedCount / totalLines) * 100).toFixed(2)
+ log('info', `Processed ${processedCount.toLocaleString()} IPv${version} records (${percentage}%)...`)
+ }
+
+ const addr = version === 4 ? new Address4(row.network!) : new Address6(row.network!)
+ const start = version === 4 ? aton4(addr.startAddress().correctForm()) : aton6(addr.startAddress().correctForm())
+ const end = version === 4 ? aton4(addr.endAddress().correctForm()) : aton6(addr.endAddress().correctForm())
+
+ if (settings.dataType === 'Country') {
+ const locationDataMap = locationData[0] as Record
+ const countryCode = locationDataMap[Number.parseInt(row.geoname_id)]
+
+ if (!countryCode || countryCode.length !== 2) {
+ return //* Invalid country code
+ }
+
+ if (
+ countryCode === previousData?.countryCode
+ && (
+ (version === 4 && (previousData.end as number) + 1 === start)
+ || (version === 6 && (previousData.end as bigint) + 1n === start)
+ )
+ ) {
+ if (version === 4) {
+ previousData.buffer2?.writeUInt32LE(end as number)
+ }
+ else {
+ previousData.buffer2?.writeBigUInt64LE(end as bigint)
+ }
+ }
+ else {
+ const buffer1 = Buffer.allocUnsafe(version === 4 ? 4 : 8)
+ const buffer2 = Buffer.allocUnsafe(version === 4 ? 4 : 8)
+ if (version === 4) {
+ buffer1.writeUInt32LE(start as number)
+ buffer2.writeUInt32LE(end as number)
+ }
+ else {
+ buffer1.writeBigUInt64LE(start as bigint)
+ buffer2.writeBigUInt64LE(end as bigint)
+ }
+
+ const buffer3 = Buffer.allocUnsafe(2)
+ buffer3.write(countryCode)
+
+ if (previousData?.buffer1) {
+ if (!writeStreamDat1.write(previousData.buffer1))
+ readStream.pause()
+ if (settings.smallMemory && previousData.buffer2 && previousData.buffer3) {
+ writeStreamSmallMemory = createSmallMemoryFile(writeStreamSmallMemory!, version, lineCount++, previousData.buffer2, previousData.buffer3, settings)
+ }
+ else {
+ if (!writeStreamDat2!.write(previousData.buffer2))
+ readStream.pause()
+ if (!writeStreamDat3!.write(previousData.buffer3))
+ readStream.pause()
+ }
+ }
+
+ previousData = {
+ countryCode,
+ end,
+ buffer1,
+ buffer2,
+ buffer3,
+ }
+ }
+ }
+ else {
+ const locationDataMap = locationData[0] as Record
+ const locationId = Number.parseInt(row.geoname_id)
+ const latitude = Math.round(Number.parseFloat(row.latitude || '0') * 10000)
+ const longitude = Math.round(Number.parseFloat(row.longitude || '0') * 10000)
+ const accuracyRadius = row.accuracy_radius
+ const postalCode = row.postal_code
+
+ //* Check if any relevant fields have changed from the previous entry
+ let hasChanged = false
+ if (settings.fields.includes('latitude') && latitude !== previousData?.latitude)
+ hasChanged = true
+ if (settings.fields.includes('longitude') && longitude !== previousData?.longitude)
+ hasChanged = true
+ if (settings.fields.includes('area') && accuracyRadius !== previousData?.accuracyRadius)
+ hasChanged = true
+ if (settings.fields.includes('postcode') && postalCode !== previousData?.postalCode)
+ hasChanged = true
+
+ let counter = locationDataMap[locationId]?.counter ?? 0
+
+ //* Check if we can merge this entry with the previous one
+ if (
+ previousData
+ && (
+ locationId === previousData.locationId
+ || (counter > 0 && counter === previousData.counter)
+ || !settings.locationFile
+ )
+ && !hasChanged
+ && (
+ (version === 4 && (previousData.end as number) + 1 === start)
+ || (version === 6 && (previousData.end as bigint) + 1n === start)
+ )
+ ) {
+ //* Merge by updating the end of the previous entry
+ if (version === 4) {
+ previousData.buffer2?.writeUInt32LE(end as number)
+ }
+ else {
+ previousData.buffer2?.writeBigUInt64LE(end as bigint)
+ }
+ }
+ else {
+ if (!locationId)
+ return
+
+ const dataMap = locationDataMap[locationId]
+ if (!dataMap) {
+ log('warn', `Invalid location ID ${locationId}`)
+ return
+ }
+
+ //* Assign a counter if it doesn't exist
+ if (!dataMap.counter) {
+ locationIdList.push(locationId)
+ counter = dataMap.counter = locationIdList.length
+ }
+
+ //* Write the previous data if it exists
+ if (previousData?.buffer1) {
+ if (!writeStreamDat1.write(previousData.buffer1))
+ readStream.pause()
+ if (settings.smallMemory && previousData.buffer2 && previousData.buffer3) {
+ writeStreamSmallMemory = createSmallMemoryFile(writeStreamSmallMemory!, version, lineCount++, previousData.buffer2, previousData.buffer3, settings)
+ }
+ else {
+ if (!writeStreamDat2!.write(previousData.buffer2))
+ readStream.pause()
+ if (!writeStreamDat3!.write(previousData.buffer3))
+ readStream.pause()
+ }
+ }
+
+ //* Create new buffers for the current entry
+ const buffer1 = Buffer.allocUnsafe(version === 4 ? 4 : 8)
+ const buffer2 = Buffer.allocUnsafe(version === 4 ? 4 : 8)
+ if (version === 4) {
+ buffer1.writeUInt32LE(start as number)
+ buffer2.writeUInt32LE(end as number)
+ }
+ else {
+ buffer1.writeBigUInt64LE(start as bigint)
+ buffer2.writeBigUInt64LE(end as bigint)
+ }
+
+ const buffer3 = Buffer.alloc(settings.mainRecordSize)
+
+ let offset = 0
+ //* Write location data to buffer3 based on settings
+ if (settings.locationFile) {
+ buffer3.writeUInt32LE(dataMap.counter, offset)
+ offset += 4
+ }
+
+ if (settings.fields.includes('latitude')) {
+ buffer3.writeInt32LE(latitude, offset)
+ offset += 4
+ }
+
+ if (settings.fields.includes('longitude')) {
+ buffer3.writeInt32LE(longitude, offset)
+ offset += 4
+ }
+
+ if (settings.fields.includes('postcode')) {
+ const [postcodeLength, postcodeValue] = getPostcodeDatabase(postalCode)
+ buffer3.writeUInt32LE(postcodeValue, offset)
+ buffer3.writeInt8(postcodeLength, offset + 4)
+ offset += 5
+ }
+
+ if (settings.fields.includes('area')) {
+ buffer3.writeUInt8(makeDatabase(accuracyRadius, areaDatabase), offset)
+ }
+
+ //* Update previousData for the next iteration
+ previousData = {
+ locationId,
+ end,
+ buffer1,
+ buffer2,
+ buffer3,
+ latitude,
+ longitude,
+ accuracyRadius,
+ counter,
+ postalCode,
+ }
+ }
+ }
+ previousData = {
+ ...(previousData ?? {}),
+ end,
+ }
+ })
+ .on('pause', () => {
+ writeStreamDat1.once('drain', () => readStream.resume())
+ if (!settings.smallMemory && writeStreamDat2 && writeStreamDat3) {
+ writeStreamDat2.once('drain', () => readStream.resume())
+ writeStreamDat3.once('drain', () => readStream.resume())
+ }
+ })
+ .on('end', () => {
+ log('info', `Finished processing ${processedCount.toLocaleString()} IPv${version} records (100%)`)
+
+ if (settings.smallMemory && previousData?.buffer2 && previousData?.buffer3) {
+ writeStreamSmallMemory = createSmallMemoryFile(writeStreamSmallMemory!, version, lineCount++, previousData.buffer2, previousData.buffer3, settings)
+ writeStreamSmallMemory?.end(check)
+ ++checkCount
+ }
+ else {
+ writeStreamDat2?.end(check)
+ writeStreamDat3?.end(check)
+ }
+ writeStreamDat1?.end(check)
+ })
+ })
+}
diff --git a/packages/util/src/functions/database/createDatabase.ts b/packages/util/src/functions/database/createDatabase.ts
new file mode 100644
index 0000000..b04d8b4
--- /dev/null
+++ b/packages/util/src/functions/database/createDatabase.ts
@@ -0,0 +1,241 @@
+import type { IpLocationApiSettings } from '../getSettings.js'
+import { createReadStream } from 'node:fs'
+import path from 'node:path'
+import { parse } from '@fast-csv/parse'
+import { LOCATION_FIELDS } from '../../constants.js'
+import { createBlockDatabase } from './createBlockDatabase.js'
+import { createLocationDatabase } from './createLocationDatabase.js'
+
+/**
+ * Creates a database for IP location data.
+ * @param files - Array of file paths to process.
+ * @param settings - IP location API settings.
+ */
+export async function createDatabase(files: string[], settings: IpLocationApiSettings): Promise {
+ const locationSources = files.filter(file => file.includes('Locations'))
+ //* Ensure the English file is first (should only have max 2 location files)
+ locationSources.sort((a, b) => {
+ if (a.endsWith('-en.csv'))
+ return -1
+ if (b.endsWith('-en.csv'))
+ return 1
+ return a.localeCompare(b)
+ })
+
+ //* Extract location data from files
+ const locationData: Record[] = []
+ for (const locationSource of locationSources) {
+ locationData.push(await getLocationData(locationSource, settings))
+ }
+
+ //* Optimize location file if necessary
+ if (settings.locationFile) {
+ minifyLocationData(locationData as Record[], settings)
+ }
+
+ //* Process block sources and create databases
+ const blockSources = files.filter(file => file.includes('Blocks')).sort((a, b) => a.localeCompare(b))
+ const locationIdList: number[] = []
+ const areaDatabase: Record = {}
+
+ for (const blockSource of blockSources) {
+ await createBlockDatabase(blockSource, locationData, locationIdList, areaDatabase, settings)
+ }
+
+ if (settings.locationFile) {
+ await createLocationDatabase(locationData, locationIdList, areaDatabase, settings)
+ }
+}
+
+/**
+ * Represents the data for a location in the database.
+ */
+export interface LocationData {
+ geoname_id: string
+ locale_code: string
+ continent_code: string
+ continent_name: string
+ country_iso_code: string
+ country_name: string
+ subdivision_1_iso_code: string
+ subdivision_1_name: string
+ subdivision_2_iso_code: string
+ subdivision_2_name: string
+ city_name: string
+ metro_code: string
+ time_zone: string
+ is_in_european_union: string
+ counter?: number
+}
+
+/**
+ * Extracts location data from a CSV file.
+ * @param file - Path to the CSV file.
+ * @param settings - IP location API settings.
+ * @returns A promise that resolves to a record of location data.
+ */
+async function getLocationData(file: string, settings: IpLocationApiSettings): Promise> {
+ const stream = createReadStream(path.join(settings.tmpDataDir, file))
+ const result: Record = {}
+ return new Promise((resolve, reject) => {
+ stream
+ .pipe(parse({ headers: true }))
+ .on('error', reject)
+ .on('data', (row: LocationData) => {
+ if (settings.dataType === 'Country') {
+ result[Number.parseInt(row.geoname_id)] = row.country_iso_code
+ }
+ else {
+ result[Number.parseInt(row.geoname_id)] = row
+ }
+ })
+ .on('end', () => resolve(result))
+ })
+}
+
+/**
+ * Optimizes location data by merging and deduplicating entries.
+ * @param mapDatas - Array of location data records.
+ * @param settings - IP location API settings.
+ */
+function minifyLocationData(mapDatas: Record[], settings: IpLocationApiSettings): void {
+ const [primaryData, secondaryData] = mapDatas as [Record, Record | undefined]
+
+ //* Handle non-English languages by merging location names
+ if (settings.language !== 'en' && secondaryData) {
+ mergeNonEnglishNames(primaryData, secondaryData)
+ }
+
+ const locIds = Object.keys(primaryData).map(key => Number.parseInt(key))
+ locIds.sort((a, b) => a - b)
+
+ const locFields = getLocationFields(settings)
+ const checkFields = mapLocationFields(locFields)
+
+ //* Group location IDs by their primary field (usually country)
+ const groupedLocations = groupLocationsByPrimaryField(primaryData, locIds, checkFields[0]!)
+
+ //* Deduplicate and merge similar locations within each group
+ deduplicateLocations(primaryData, groupedLocations, checkFields.slice(1))
+}
+
+/**
+ * Merges non-English names into the primary data.
+ * @param primaryData - Primary location data record.
+ * @param secondaryData - Secondary location data record with non-English names.
+ */
+function mergeNonEnglishNames(primaryData: Record, secondaryData: Record): void {
+ for (const locId in primaryData) {
+ if (secondaryData[locId]) {
+ const fields = ['city_name', 'subdivision_1_name', 'subdivision_2_name'] as const
+ for (const field of fields) {
+ if (secondaryData[locId][field]) {
+ primaryData[locId]![field] = secondaryData[locId][field]
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Retrieves and sorts location fields based on settings.
+ * @param settings - IP location API settings.
+ * @returns Sorted array of location fields.
+ */
+function getLocationFields(settings: IpLocationApiSettings): (typeof LOCATION_FIELDS[number])[] {
+ const fieldRanking: Record = {
+ metro: 1,
+ region2_name: 2,
+ region2: 3,
+ timezone: 4,
+ region1_name: 5,
+ region1: 6,
+ country: 7,
+ city: 8,
+ eu: 9,
+ }
+ return LOCATION_FIELDS
+ .filter(field => settings.fields.includes(field))
+ .sort((a, b) => fieldRanking[b] - fieldRanking[a])
+}
+
+/**
+ * Maps location fields to their corresponding LocationData keys.
+ * @param locFields - Array of location fields.
+ * @returns Array of LocationData keys.
+ */
+function mapLocationFields(locFields: (typeof LOCATION_FIELDS[number])[]): (keyof LocationData)[] {
+ const fieldMapping: Record = {
+ country: 'country_iso_code',
+ region1: 'subdivision_1_iso_code',
+ region1_name: 'subdivision_1_name',
+ region2: 'subdivision_2_iso_code',
+ region2_name: 'subdivision_2_name',
+ city: 'city_name',
+ metro: 'metro_code',
+ timezone: 'time_zone',
+ eu: 'is_in_european_union',
+ }
+ return locFields.map(field => fieldMapping[field])
+}
+
+/**
+ * Groups location IDs by their primary field value.
+ * @param primaryData - Primary location data record.
+ * @param locIds - Array of location IDs.
+ * @param primaryField - The primary field to group by.
+ * @returns Record of grouped location IDs.
+ */
+function groupLocationsByPrimaryField(
+ primaryData: Record,
+ locIds: number[],
+ primaryField: keyof LocationData,
+): Record {
+ const groupedLocations: Record = {}
+ for (const locId of locIds) {
+ const key = primaryData[locId]![primaryField as keyof LocationData]!
+ if (!groupedLocations[key]) {
+ groupedLocations[key] = []
+ }
+ groupedLocations[key].push(locId)
+ }
+ return groupedLocations
+}
+
+/**
+ * Deduplicates locations within grouped location IDs.
+ * @param primaryData - Primary location data record.
+ * @param groupedLocations - Record of grouped location IDs.
+ * @param checkFields - Array of fields to check for equality.
+ */
+function deduplicateLocations(
+ primaryData: Record,
+ groupedLocations: Record,
+ checkFields: (keyof LocationData)[],
+): void {
+ for (const group of Object.values(groupedLocations)) {
+ for (let i = 0; i < group.length; i++) {
+ const baseLocation = primaryData[group[i]!]!
+ for (let j = i + 1; j < group.length; j++) {
+ const compareLocation = primaryData[group[j]!]!
+ if (areLocationsEqual(baseLocation, compareLocation, checkFields)) {
+ //* Merge duplicate locations by keeping the base location and removing the duplicate
+ primaryData[group[j]!] = baseLocation
+ group.splice(j, 1)
+ j--
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Checks if two locations are equal based on specified fields.
+ * @param loc1 - First location to compare.
+ * @param loc2 - Second location to compare.
+ * @param fields - Array of fields to check for equality.
+ * @returns Boolean indicating if locations are equal.
+ */
+function areLocationsEqual(loc1: LocationData, loc2: LocationData, fields: (keyof LocationData)[]): boolean {
+ return fields.every(field => loc1[field] === loc2[field])
+}
diff --git a/packages/util/src/functions/database/createLocationDatabase.ts b/packages/util/src/functions/database/createLocationDatabase.ts
new file mode 100644
index 0000000..3c929ef
--- /dev/null
+++ b/packages/util/src/functions/database/createLocationDatabase.ts
@@ -0,0 +1,281 @@
+import type { WriteStream } from 'node:fs'
+import type { IpLocationApiSettings } from '../getSettings.js'
+import type { LocationData } from './createDatabase.js'
+import { Buffer } from 'node:buffer'
+import { createWriteStream } from 'node:fs'
+import { writeFile } from 'node:fs/promises'
+import path from 'node:path'
+import { makeDatabase } from '../makeDatabase.js'
+import { stringToNumber37 } from '../stringToNumber37.js'
+
+/**
+ * Interface representing various location-related databases
+ */
+interface LocationDatabases {
+ sub1Database: Record
+ sub2Database: Record
+ timezoneDatabase: Record
+}
+
+/**
+ * Creates a location database from the provided location data.
+ * @param locationData - Array of location data records
+ * @param locationIdList - List of location IDs to process
+ * @param areaDatabase - A database of area data.
+ * @param settings - IP location API settings
+ */
+export async function createLocationDatabase(
+ locationData: Record[],
+ locationIdList: number[],
+ areaDatabase: Record,
+ settings: IpLocationApiSettings,
+): Promise {
+ const locationDataMap = locationData[0] as Record
+ const locationDataStream = createWriteStream(path.join(settings.fieldDir, 'location.dat.tmp'))
+ const nameDataStream = createWriteStream(path.join(settings.fieldDir, 'name.dat.tmp'))
+
+ const cityNameToIndex: Record = {}
+ const euCountryCodes: Record = {}
+ const regionDatabases: LocationDatabases = {
+ sub1Database: {},
+ sub2Database: {},
+ timezoneDatabase: {},
+ }
+
+ //* Process each location ID
+ for (const locationId of locationIdList) {
+ const locationInfo = locationDataMap[locationId]
+ if (!locationInfo)
+ continue
+
+ const locationBuffer = createLocationBuffer(locationInfo, settings, cityNameToIndex, euCountryCodes, regionDatabases, nameDataStream)
+ locationDataStream.write(locationBuffer)
+ }
+
+ locationDataStream.end()
+ nameDataStream.end()
+
+ await createSubJsonFile(settings, regionDatabases, euCountryCodes, areaDatabase)
+}
+
+/**
+ * Creates a buffer for a single location record.
+ * @param locationInfo - The location data
+ * @param settings - IP location API settings
+ * @param cityNameToIndex - Hash table for city names
+ * @param euCountryCodes - Hash table for EU status
+ * @param regionDatabases - Object containing various location databases
+ * @param nameDataStream - WriteStream for the name data file
+ * @returns A buffer containing the encoded location data
+ */
+function createLocationBuffer(
+ locationInfo: LocationData,
+ settings: IpLocationApiSettings,
+ cityNameToIndex: Record,
+ euCountryCodes: Record,
+ regionDatabases: LocationDatabases,
+ nameDataStream: WriteStream,
+): Buffer {
+ const buffer = Buffer.alloc(settings.locationRecordSize)
+ let offset = 0
+
+ //* Write each field to the buffer if it's included in the settings
+ const fieldWriters: Record number> = {
+ country: () => writeCountryCode(buffer, offset, locationInfo, settings, euCountryCodes),
+ region1: () => writeRegion(buffer, offset, locationInfo.subdivision_1_iso_code),
+ region1_name: () => writeRegionName(buffer, offset, locationInfo.subdivision_1_name, regionDatabases.sub1Database),
+ region2: () => writeRegion(buffer, offset, locationInfo.subdivision_2_iso_code),
+ region2_name: () => writeRegionName(buffer, offset, locationInfo.subdivision_2_name, regionDatabases.sub2Database),
+ metro: () => writeMetroCode(buffer, offset, locationInfo),
+ timezone: () => writeTimezone(buffer, offset, locationInfo, regionDatabases),
+ city: () => writeCity(buffer, offset, locationInfo, cityNameToIndex, nameDataStream),
+ }
+
+ for (const [field, writer] of Object.entries(fieldWriters)) {
+ if (settings.fields.includes(field as IpLocationApiSettings['fields'][number])) {
+ offset = writer()
+ }
+ }
+
+ return buffer
+}
+
+/**
+ * Writes the country code to the buffer and updates the EU hash if necessary.
+ * @param buffer - The buffer to write to
+ * @param offset - The current offset in the buffer
+ * @param locationInfo - The location data
+ * @param settings - IP location API settings
+ * @param euCountryCodes - Hash table for EU status
+ * @returns The new offset after writing
+ */
+function writeCountryCode(buffer: Buffer, offset: number, locationInfo: LocationData, settings: IpLocationApiSettings, euCountryCodes: Record): number {
+ const { country_iso_code: countryCode, is_in_european_union: isEU } = locationInfo
+ if (countryCode && countryCode.length === 2) {
+ buffer.write(countryCode, offset)
+ if (settings.fields.includes('eu') && Number.parseInt(isEU, 10) === 1) {
+ euCountryCodes[countryCode] = true
+ }
+ }
+ return offset + 2
+}
+
+/**
+ * Writes a region code to the buffer.
+ * @param buffer - The buffer to write to
+ * @param offset - The current offset in the buffer
+ * @param regionCode - The region code to write
+ * @returns The new offset after writing
+ */
+function writeRegion(buffer: Buffer, offset: number, regionCode: string | undefined): number {
+ if (regionCode)
+ buffer.writeUInt16LE(stringToNumber37(regionCode), offset)
+ return offset + 2
+}
+
+/**
+ * Writes a region name to the buffer.
+ * @param buffer - The buffer to write to
+ * @param offset - The current offset in the buffer
+ * @param regionName - The region name to write
+ * @param database - The database to use for encoding the region name
+ * @returns The new offset after writing
+ */
+function writeRegionName(buffer: Buffer, offset: number, regionName: string | undefined, database: Record): number {
+ if (regionName)
+ buffer.writeUInt16LE(makeDatabase(regionName, database), offset)
+ return offset + 2
+}
+
+/**
+ * Writes a metro code to the buffer.
+ * @param buffer - The buffer to write to
+ * @param offset - The current offset in the buffer
+ * @param locationInfo - The location data
+ * @returns The new offset after writing
+ */
+function writeMetroCode(buffer: Buffer, offset: number, locationInfo: LocationData): number {
+ const { metro_code: metro } = locationInfo
+ if (metro)
+ buffer.writeUInt16LE(Number.parseInt(metro, 10), offset)
+ return offset + 2
+}
+
+/**
+ * Writes a timezone to the buffer.
+ * @param buffer - The buffer to write to
+ * @param offset - The current offset in the buffer
+ * @param locationInfo - The location data
+ * @param regionDatabases - Object containing various location databases
+ * @returns The new offset after writing
+ */
+function writeTimezone(buffer: Buffer, offset: number, locationInfo: LocationData, regionDatabases: LocationDatabases): number {
+ const { time_zone: timezone } = locationInfo
+ if (timezone)
+ buffer.writeUInt16LE(makeDatabase(timezone, regionDatabases.timezoneDatabase), offset)
+ return offset + 2
+}
+
+/**
+ * Writes a city name to the buffer.
+ * @param buffer - The buffer to write to
+ * @param offset - The current offset in the buffer
+ * @param locationInfo - The location data
+ * @param cityNameToIndex - Hash table for city names
+ * @param nameDataStream - WriteStream for the name data file
+ * @returns The new offset after writing
+ */
+function writeCity(buffer: Buffer, offset: number, locationInfo: LocationData, cityNameToIndex: Record, nameDataStream: WriteStream): number {
+ const { city_name: cityName } = locationInfo
+ if (cityName) {
+ buffer.writeUInt32LE(inputBuffer(cityNameToIndex, nameDataStream, cityName), offset)
+ }
+ return offset + 4
+}
+
+/**
+ * Creates a sub.json file with additional location data.
+ * @param settings - IP location API settings
+ * @param regionDatabases - Object containing various location databases
+ * @param euCountryCodes - Hash table for EU status
+ * @param areaDatabase - A database of area data.
+ */
+async function createSubJsonFile(
+ settings: IpLocationApiSettings,
+ regionDatabases: LocationDatabases,
+ euCountryCodes: Record,
+ areaDatabase: Record,
+): Promise {
+ const subJsonData: Record = {
+ region1_name: databaseToArray(regionDatabases.sub1Database),
+ region2_name: databaseToArray(regionDatabases.sub2Database),
+ timezone: databaseToArray(regionDatabases.timezoneDatabase),
+ area: databaseToArray(areaDatabase).map(area => Number.parseInt(area, 10) || 0),
+ eu: euCountryCodes,
+ }
+
+ //* Remove unused fields based on settings
+ if (!settings.fields.includes('region1_name'))
+ delete subJsonData.region1_name
+ if (!settings.fields.includes('region2_name'))
+ delete subJsonData.region2_name
+ if (!settings.fields.includes('timezone'))
+ delete subJsonData.timezone
+ if (!settings.fields.includes('area'))
+ delete subJsonData.area
+ if (!settings.fields.includes('eu'))
+ delete subJsonData.eu
+
+ if (Object.keys(subJsonData).length > 0) {
+ await writeFile(path.join(settings.fieldDir, 'sub.json.tmp'), JSON.stringify(subJsonData))
+ }
+}
+
+/**
+ * Converts a database to an array.
+ * @param database - The database to convert.
+ * @returns The array.
+ */
+function databaseToArray(database: Record): string[] {
+ const array: string[] = ['']
+ for (const [key, value] of Object.entries(database)) {
+ array[value] = key
+ }
+ return array
+}
+
+/**
+ * Processes and stores text data in a buffer, managing offsets and hash entries.
+ * @param nameToIndex - Object to store text-to-number mappings and track buffer offset
+ * @param dataStream - WriteStream to write buffer data
+ * @param text - The text to process and store
+ * @returns A number representing the stored text (either existing hash value or new encoded value)
+ */
+function inputBuffer(nameToIndex: Record, dataStream: WriteStream, text: string): number {
+ //* If text is already in nameToIndex, return its value
+ if (nameToIndex[text])
+ return nameToIndex[text]
+
+ //* Initialize buffer offset if not yet set
+ if (nameToIndex.__offsetBB === undefined) {
+ const buffer = Buffer.alloc(1)
+ dataStream.write(buffer)
+ nameToIndex.__offsetBB = 1
+ }
+
+ const offset = nameToIndex.__offsetBB
+ const buffer = Buffer.from(text)
+
+ //* Encode text length and offset into a single number
+ //* The first 8 bits represent the length, and the remaining bits represent the offset
+ const encodedValue = buffer.length + (offset << 8)
+
+ //* Write the text buffer to the data stream
+ dataStream.write(buffer)
+
+ //* Update the offset for the next write
+ nameToIndex.__offsetBB = offset + buffer.length
+
+ //* Store and return the encoded value
+ return nameToIndex[text] = encodedValue
+}
diff --git a/packages/util/src/functions/database/createSmallMemoryFile.ts b/packages/util/src/functions/database/createSmallMemoryFile.ts
new file mode 100644
index 0000000..d0712e8
--- /dev/null
+++ b/packages/util/src/functions/database/createSmallMemoryFile.ts
@@ -0,0 +1,61 @@
+import type { IpLocationApiSettings } from '../getSettings.js'
+import { Buffer } from 'node:buffer'
+import { createWriteStream, existsSync, mkdirSync, writeFile, type WriteStream } from 'node:fs'
+import path from 'node:path'
+import { getSmallMemoryFile } from '../getSmallMemoryFile.js'
+
+/**
+ * Creates or appends to a small memory file for IP location data.
+ * @param writeStream - The current write stream, if any
+ * @param version - IP version (4 or 6)
+ * @param lineCount - Number of lines processed
+ * @param buffer2 - First buffer to write
+ * @param buffer3 - Second buffer to write
+ * @param settings - IP location API settings
+ * @returns The write stream if the operation is not complete, undefined otherwise
+ */
+export function createSmallMemoryFile(
+ writeStream: WriteStream,
+ version: 4 | 6,
+ lineCount: number,
+ buffer2: Buffer,
+ buffer3: Buffer,
+ settings: IpLocationApiSettings,
+) {
+ //* Get file path and offset based on line count and IP version
+ const [_dir, file, offset] = getSmallMemoryFile(lineCount, version === 4 ? settings.v4 : settings.v6, true)
+
+ if (offset === 0) {
+ //* We're starting a new file
+ const dir = path.join(settings.fieldDir, _dir)
+
+ //* Close the previous write stream if it exists
+ if (writeStream)
+ writeStream.end()
+
+ //* Create directory if it doesn't exist (only for the first file)
+ if (file === '_0' && !existsSync(dir)) {
+ mkdirSync(dir, { recursive: true })
+ }
+
+ //* If the total buffer size is smaller than or equal to the small memory file size,
+ //* write it all at once and return
+ if (settings.smallMemoryFileSize <= buffer2.length + buffer3.length) {
+ const buf = Buffer.alloc(buffer2.length + buffer3.length)
+ buffer2.copy(buf)
+ buffer3.copy(buf, buffer2.length)
+ writeFile(path.join(dir, file), buf, () => {})
+ return
+ }
+
+ //* Create a new write stream for the file
+ writeStream = createWriteStream(path.join(dir, file))
+ }
+
+ //* Write both buffers to the stream
+ writeStream.write(buffer2)
+ writeStream.write(buffer3)
+
+ //* Return the write stream for future use
+ return writeStream
+}
diff --git a/packages/util/src/functions/database/downloadAndExtractDatabase.ts b/packages/util/src/functions/database/downloadAndExtractDatabase.ts
new file mode 100644
index 0000000..4df650c
--- /dev/null
+++ b/packages/util/src/functions/database/downloadAndExtractDatabase.ts
@@ -0,0 +1,202 @@
+import type { IpLocationApiSettings } from '../getSettings.js'
+import { createHash } from 'node:crypto'
+import { createReadStream, createWriteStream, existsSync } from 'node:fs'
+import { readFile } from 'node:fs/promises'
+import path from 'node:path'
+import { Writable } from 'node:stream'
+import ky from 'ky'
+import { open } from 'yauzl'
+import { DATABASE_SUFFIX_SHA, DATABASE_SUFFIX_ZIP, MAXMIND_URL } from '../../constants.js'
+import { log } from '../log.js'
+
+/**
+ * Downloads and extracts the database if an update is needed.
+ * @param settings - The settings object.
+ * @returns An array of extracted file names or false if no update was needed.
+ */
+export async function downloadAndExtractDatabase(settings: IpLocationApiSettings): Promise<{
+ files: string[] | false
+ sha256: string
+}> {
+ const { edition, src } = getDatabaseInfo(settings)
+ const remoteHash = await getRemoteSha256(settings, edition)
+ const localHash = await getLocalSha256(settings, edition)
+
+ if (await isUpToDate(settings, edition, remoteHash, localHash)) {
+ return { files: false, sha256: remoteHash }
+ }
+
+ const zipPath = await downloadDatabase(settings, edition)
+ log('info', `Decompressing ${edition}...`)
+ const files = await extractDatabase(zipPath, settings.tmpDataDir, src)
+ log('info', `Decompressed ${edition}, extracted ${files.length} files`)
+ return { files, sha256: remoteHash }
+}
+
+/**
+ * Determines the database edition and source files based on settings.
+ * @param settings - The settings object.
+ * @returns An object containing the edition and source file names.
+ */
+function getDatabaseInfo(settings: IpLocationApiSettings): { edition: string, src: string[] } {
+ const edition = `${settings.series}-${settings.dataType}-CSV`
+ const baseSrc = [
+ `${settings.series}-${settings.dataType}-Locations-en.csv`,
+ `${settings.series}-${settings.dataType}-Blocks-IPv4.csv`,
+ `${settings.series}-${settings.dataType}-Blocks-IPv6.csv`,
+ ]
+
+ //* Add language-specific file for non-English City databases
+ const src = settings.language !== 'en' && settings.dataType === 'City'
+ ? [...baseSrc, `${settings.series}-${settings.dataType}-Locations-${settings.language}.csv`]
+ : baseSrc
+
+ return { edition, src }
+}
+
+/**
+ * Fetches the remote SHA256 hash for the database.
+ * @param settings - The settings object.
+ * @param databaseEdition - The database edition string.
+ * @returns The remote SHA256 hash.
+ * @throws Error if the SHA256 hash cannot be downloaded or parsed.
+ */
+async function getRemoteSha256(settings: IpLocationApiSettings, databaseEdition: string): Promise {
+ const shaUrl = settings.licenseKey === 'redist'
+ ? `https://raw.githubusercontent.com/sapics/node-geolite2-redist/master/redist/${databaseEdition}${DATABASE_SUFFIX_SHA}`
+ : `${MAXMIND_URL}?edition_id=${databaseEdition}&suffix=${DATABASE_SUFFIX_SHA.slice(1)}&license_key=${settings.licenseKey}`
+
+ const shaText = await ky.get(shaUrl).text()
+ const sha256 = shaText.match(/\w{50,}/)?.[0]
+ if (!sha256) {
+ throw new Error('Cannot download sha256')
+ }
+ return sha256
+}
+
+/**
+ * Retrieves the local SHA256 hash for the database.
+ * @param settings - The settings object.
+ * @param databaseEdition - The database edition string.
+ * @returns The local SHA256 hash or undefined if not found.
+ */
+async function getLocalSha256(settings: IpLocationApiSettings, databaseEdition: string): Promise {
+ try {
+ return await readFile(path.join(settings.fieldDir, `${databaseEdition}${DATABASE_SUFFIX_SHA}`), 'utf-8')
+ }
+ catch {
+ return undefined
+ }
+}
+
+/**
+ * Checks if the local database is up to date.
+ * @param settings - The settings object.
+ * @param databaseEdition - The database edition string.
+ * @param remoteHash - The remote SHA256 hash.
+ * @param localHash - The local SHA256 hash.
+ * @returns True if the database is up to date, false otherwise.
+ */
+async function isUpToDate(settings: IpLocationApiSettings, databaseEdition: string, remoteHash: string, localHash: string | undefined): Promise {
+ if (localHash !== remoteHash) {
+ return false
+ }
+
+ const zipPath = path.join(settings.tmpDataDir, `${databaseEdition}${DATABASE_SUFFIX_ZIP}`)
+ if (!existsSync(zipPath)) {
+ return false
+ }
+
+ const zipHash = await sha256Hash(zipPath)
+ return zipHash === remoteHash
+}
+
+/**
+ * Downloads the database ZIP file.
+ * @param settings - The settings object.
+ * @param databaseEdition - The database edition string.
+ * @returns The path to the downloaded ZIP file.
+ * @throws Error if the database cannot be downloaded.
+ */
+async function downloadDatabase(settings: IpLocationApiSettings, databaseEdition: string): Promise {
+ const zipUrl = settings.licenseKey === 'redist'
+ ? `https://raw.githubusercontent.com/sapics/node-geolite2-redist/master/redist/${databaseEdition}${DATABASE_SUFFIX_ZIP}`
+ : `${MAXMIND_URL}?edition_id=${databaseEdition}&suffix=${DATABASE_SUFFIX_ZIP.slice(1)}&license_key=${settings.licenseKey}`
+
+ const response = await ky.get(zipUrl)
+ const stream = response.body
+ if (!stream) {
+ throw new Error('Cannot download database')
+ }
+
+ const zipPath = path.join(settings.tmpDataDir, `${databaseEdition}${DATABASE_SUFFIX_ZIP}`)
+ await stream.pipeTo(Writable.toWeb(createWriteStream(zipPath)))
+ return zipPath
+}
+
+/**
+ * Extracts specific files from the downloaded ZIP database.
+ * @param zipPath - The path to the ZIP file.
+ * @param outputDir - The directory to extract files to.
+ * @param filesToExtract - An array of file names to extract.
+ * @returns A promise that resolves to an array of extracted file names.
+ */
+async function extractDatabase(zipPath: string, outputDir: string, filesToExtract: string[]): Promise {
+ return new Promise((resolve, reject) => {
+ //* Open the zip file
+ open(zipPath, { lazyEntries: true }, (err, zipfile) => {
+ if (err)
+ return reject(err)
+
+ //* Initialize an array to store extracted files
+ const extractedFiles: string[] = []
+
+ //* Listen for entry events in the zip file
+ zipfile.on('entry', (entry) => {
+ //* Check if the entry matches any files to extract
+ const matchedFile = filesToExtract.find(file => entry.fileName.endsWith(file))
+ if (matchedFile) {
+ log('info', `Extracting ${matchedFile}...`)
+ //* Open the read stream for the entry
+ zipfile.openReadStream(entry, (err, readStream) => {
+ if (err)
+ return reject(err)
+ //* Create a write stream to save the entry to the output directory
+ const writeStream = createWriteStream(path.join(outputDir, matchedFile))
+ //* Pipe the read stream to the write stream
+ readStream.pipe(writeStream)
+ //* When the read stream ends, add the file to the extracted files array and continue reading the next entry
+ readStream.on('end', () => {
+ extractedFiles.push(matchedFile)
+ zipfile.readEntry()
+ })
+ })
+ }
+ else {
+ //* If no files to extract match the entry, continue reading the next entry
+ zipfile.readEntry()
+ }
+ })
+
+ //* When the zip file is fully read, resolve the promise with the extracted files
+ zipfile.on('end', () => resolve(extractedFiles))
+ //* Start reading the entries in the zip file
+ zipfile.readEntry()
+ })
+ })
+}
+
+/**
+ * Calculates the SHA256 hash of a file.
+ * @param file - The path to the file.
+ * @returns A promise that resolves to the SHA256 hash of the file.
+ */
+function sha256Hash(file: string): Promise {
+ return new Promise((resolve, reject) => {
+ const stream = createReadStream(file)
+ const hash = createHash('sha256')
+ hash.once('finish', () => resolve(hash.digest('hex')))
+ stream.on('error', reject)
+ stream.pipe(hash)
+ })
+}
diff --git a/packages/util/src/functions/database/ensureDirectoriesExist.ts b/packages/util/src/functions/database/ensureDirectoriesExist.ts
new file mode 100644
index 0000000..2326c47
--- /dev/null
+++ b/packages/util/src/functions/database/ensureDirectoriesExist.ts
@@ -0,0 +1,19 @@
+import type { IpLocationApiSettings } from '../getSettings.js'
+import { existsSync } from 'node:fs'
+import { mkdir } from 'node:fs/promises'
+import { log } from '../log.js'
+
+/**
+ * Ensures that the necessary directories exist.
+ * @param param0 - Object containing fieldDir and tmpDataDir paths.
+ * @param param0.fieldDir - The path to the field directory.
+ * @param param0.tmpDataDir - The path to the temporary data directory.
+ */
+export async function ensureDirectoriesExist({ fieldDir, tmpDataDir }: IpLocationApiSettings): Promise {
+ for (const dir of [fieldDir, tmpDataDir]) {
+ if (!existsSync(dir)) {
+ log('info', `Creating directory ${dir}`)
+ await mkdir(dir, { recursive: true })
+ }
+ }
+}
diff --git a/packages/util/src/functions/fetchArrayBuffer.test.ts b/packages/util/src/functions/fetchArrayBuffer.test.ts
new file mode 100644
index 0000000..caf244e
--- /dev/null
+++ b/packages/util/src/functions/fetchArrayBuffer.test.ts
@@ -0,0 +1,94 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import { fetchArrayBuffer } from './fetchArrayBuffer'
+import { sleep } from './sleep'
+
+vi.mock('./sleep')
+
+describe('fetchArrayBuffer', () => {
+ beforeEach(() => {
+ vi.resetAllMocks()
+ })
+
+ afterEach(() => {
+ vi.restoreAllMocks()
+ })
+
+ it('should fetch ArrayBuffer successfully', async () => {
+ const mockArrayBuffer = new ArrayBuffer(8)
+ const mockResponse = {
+ ok: true,
+ headers: new Headers({ 'x-jsd-version': '1.0.0' }),
+ arrayBuffer: vi.fn().mockResolvedValue(mockArrayBuffer),
+ }
+ globalThis.fetch = vi.fn().mockResolvedValue(mockResponse)
+
+ const result = await fetchArrayBuffer(new URL('https://example.com'))
+
+ expect.soft(result).toEqual({
+ buffer: mockArrayBuffer,
+ versionHeader: '1.0.0',
+ })
+ expect.soft(globalThis.fetch).toHaveBeenCalledWith(new URL('https://example.com'))
+ })
+
+ it('should return null for 404 response', async () => {
+ const mockResponse = {
+ ok: false,
+ status: 404,
+ }
+ globalThis.fetch = vi.fn().mockResolvedValue(mockResponse)
+
+ const result = await fetchArrayBuffer(new URL('https://example.com'))
+
+ expect.soft(result).toBeNull()
+ expect.soft(globalThis.fetch).toHaveBeenCalledWith(new URL('https://example.com'))
+ })
+
+ it('should retry on non-404 error', async () => {
+ const mockResponse = {
+ ok: false,
+ status: 500,
+ }
+ globalThis.fetch = vi.fn().mockResolvedValue(mockResponse)
+ vi.mocked(sleep).mockResolvedValue(undefined)
+
+ await fetchArrayBuffer(new URL('https://example.com'))
+
+ expect.soft(globalThis.fetch).toHaveBeenCalledTimes(4) // Initial request + 3 retries
+ expect.soft(sleep).toHaveBeenCalledTimes(3)
+ expect.soft(sleep).toHaveBeenNthCalledWith(1, 100)
+ expect.soft(sleep).toHaveBeenNthCalledWith(2, 400)
+ expect.soft(sleep).toHaveBeenNthCalledWith(3, 900)
+ })
+
+ it('should return null after all retries fail', async () => {
+ const mockResponse = {
+ ok: false,
+ status: 500,
+ }
+ globalThis.fetch = vi.fn().mockResolvedValue(mockResponse)
+ vi.mocked(sleep).mockResolvedValue(undefined)
+
+ const result = await fetchArrayBuffer(new URL('https://example.com'))
+
+ expect.soft(result).toBeNull()
+ expect.soft(globalThis.fetch).toHaveBeenCalledTimes(4) // Initial request + 3 retries
+ })
+
+ it('should handle missing version header', async () => {
+ const mockArrayBuffer = new ArrayBuffer(8)
+ const mockResponse = {
+ ok: true,
+ headers: new Headers(),
+ arrayBuffer: vi.fn().mockResolvedValue(mockArrayBuffer),
+ }
+ globalThis.fetch = vi.fn().mockResolvedValue(mockResponse)
+
+ const result = await fetchArrayBuffer(new URL('https://example.com'))
+
+ expect.soft(result).toEqual({
+ buffer: mockArrayBuffer,
+ versionHeader: undefined,
+ })
+ })
+})
diff --git a/packages/util/src/functions/fetchArrayBuffer.ts b/packages/util/src/functions/fetchArrayBuffer.ts
new file mode 100644
index 0000000..d79d60b
--- /dev/null
+++ b/packages/util/src/functions/fetchArrayBuffer.ts
@@ -0,0 +1,37 @@
+import { sleep } from './sleep.js'
+
+/**
+ * Fetches an ArrayBuffer from a given URL with retry functionality.
+ * @param url - The URL to fetch the ArrayBuffer from.
+ * @param retry - The number of retry attempts (default: 3).
+ * @returns A Promise that resolves to an object containing the ArrayBuffer and version header, or null if the fetch fails.
+ */
+export async function fetchArrayBuffer(
+ url: URL,
+ retry = 3,
+): Promise<{
+ buffer: ArrayBuffer
+ versionHeader?: string
+} | null> {
+ return await fetch(url).then(
+ async (response) => {
+ if (!response.ok) {
+ if (response.status === 404)
+ return null
+
+ if (retry) {
+ //* Exponential backoff: Delay increases quadratically with each retry
+ await sleep(100 * (4 - retry) * (4 - retry))
+ return fetchArrayBuffer(url, retry - 1)
+ }
+ return null
+ }
+
+ return {
+ //* Extract the version header if present, otherwise undefined (jsDelivr CDN)
+ versionHeader: response.headers.get('x-jsd-version') ?? undefined,
+ buffer: await response.arrayBuffer(),
+ }
+ },
+ )
+}
diff --git a/packages/util/src/functions/getFieldsSize.test.ts b/packages/util/src/functions/getFieldsSize.test.ts
new file mode 100644
index 0000000..b612b21
--- /dev/null
+++ b/packages/util/src/functions/getFieldsSize.test.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from 'vitest'
+import { getFieldsSize } from './getFieldsSize'
+
+describe('getFieldsSize', () => {
+ it('should calculate the correct size for a single field', () => {
+ expect.soft(getFieldsSize(['postcode'])).toBe(5)
+ expect.soft(getFieldsSize(['area'])).toBe(1)
+ expect.soft(getFieldsSize(['latitude'])).toBe(4)
+ expect.soft(getFieldsSize(['longitude'])).toBe(4)
+ expect.soft(getFieldsSize(['city'])).toBe(4)
+ expect.soft(getFieldsSize(['eu'])).toBe(0)
+ expect.soft(getFieldsSize(['someOtherField'])).toBe(2)
+ })
+
+ it('should calculate the correct size for multiple fields', () => {
+ expect.soft(getFieldsSize(['postcode', 'area', 'latitude'])).toBe(10)
+ expect.soft(getFieldsSize(['city', 'longitude', 'eu'])).toBe(8)
+ expect.soft(getFieldsSize(['area', 'someOtherField', 'postcode'])).toBe(8)
+ })
+
+ it('should return 0 for an empty array', () => {
+ expect.soft(getFieldsSize([])).toBe(0)
+ })
+
+ it('should calculate the correct size for all possible fields', () => {
+ expect.soft(getFieldsSize(['postcode', 'area', 'latitude', 'longitude', 'city', 'eu', 'someOtherField'])).toBe(20)
+ })
+})
diff --git a/packages/util/src/functions/getFieldsSize.ts b/packages/util/src/functions/getFieldsSize.ts
new file mode 100644
index 0000000..6963d80
--- /dev/null
+++ b/packages/util/src/functions/getFieldsSize.ts
@@ -0,0 +1,32 @@
+/**
+ * Calculates the total size required for a set of fields.
+ *
+ * @param {string[]} fields - An array of field names to calculate the size for.
+ * @returns {number} The total size required for all fields.
+ */
+export function getFieldsSize(fields: string[]): number {
+ let size = 0
+
+ for (const field of fields) {
+ switch (field) {
+ case 'postcode':
+ size += 5
+ break
+ case 'area':
+ size += 1
+ break
+ case 'latitude':
+ case 'longitude':
+ case 'city':
+ size += 4
+ break
+ case 'eu':
+ break
+ default:
+ size += 2
+ break
+ }
+ }
+
+ return size
+}
diff --git a/packages/util/src/functions/getPostcodeDatabase.test.ts b/packages/util/src/functions/getPostcodeDatabase.test.ts
new file mode 100644
index 0000000..35e0018
--- /dev/null
+++ b/packages/util/src/functions/getPostcodeDatabase.test.ts
@@ -0,0 +1,50 @@
+import { describe, expect, it } from 'vitest'
+import { getPostcodeDatabase } from './getPostcodeDatabase'
+
+describe('getPostcodeDatabase', () => {
+ it('should return [0, 0] for empty input', () => {
+ expect.soft(getPostcodeDatabase('')).toEqual([0, 0])
+ })
+
+ describe('numeric postcodes', () => {
+ it('should handle single-part numeric postcodes', () => {
+ expect.soft(getPostcodeDatabase('12345')).toEqual([5, 12345])
+ expect.soft(getPostcodeDatabase('123456789')).toEqual([9, 123456789])
+ })
+
+ it('should handle two-part numeric postcodes', () => {
+ expect.soft(getPostcodeDatabase('123-456')).toEqual([33, 123456])
+ expect.soft(getPostcodeDatabase('12 34')).toEqual([22, 1234])
+ expect.soft(getPostcodeDatabase('123456 789012')).toEqual([66, 123456789012])
+ })
+ })
+
+ describe('string postcodes', () => {
+ it('should handle single-part string postcodes (short)', () => {
+ expect.soft(getPostcodeDatabase('ABC123')).toEqual([-6, 623698779])
+ expect.soft(getPostcodeDatabase('XYZ999')).toEqual([-6, 2054135709])
+ })
+
+ it('should handle single-part string postcodes (long)', () => {
+ expect.soft(getPostcodeDatabase('ABCDEF123456')).toEqual([82, 41474266563513784])
+ expect.soft(getPostcodeDatabase('XYZXYZ999999')).toEqual([105, 127959823201186850])
+ })
+
+ it('should handle two-part string postcodes', () => {
+ expect.soft(getPostcodeDatabase('ABC-123')).toEqual([-33, 623698779])
+ expect.soft(getPostcodeDatabase('XY 9Z')).toEqual([-22, 1584071])
+ })
+ })
+
+ it('should return [0, 0] for invalid postcodes', () => {
+ expect.soft(getPostcodeDatabase('ABC-123-XYZ')).toEqual([0, 0])
+ expect.soft(getPostcodeDatabase('!@#$%^')).toEqual([0, 0])
+ })
+
+ //* Edge cases
+ it('should handle edge cases', () => {
+ expect.soft(getPostcodeDatabase('9'.repeat(9))).toEqual([9, 999999999])
+ expect.soft(getPostcodeDatabase('Z'.repeat(6))).toEqual([-6, 2176782335])
+ expect.soft(getPostcodeDatabase('A'.repeat(6))).toEqual([-6, 621937810])
+ })
+})
diff --git a/packages/util/src/functions/getPostcodeDatabase.ts b/packages/util/src/functions/getPostcodeDatabase.ts
new file mode 100644
index 0000000..e48afa9
--- /dev/null
+++ b/packages/util/src/functions/getPostcodeDatabase.ts
@@ -0,0 +1,67 @@
+import { log } from './log.js'
+
+const isPostNumReg = /^\d+$/
+const isPostNumReg2 = /^(\d+)[-\s](\d+)$/
+const isPostStrReg = /^[A-Z\d]+$/
+const isPostStrReg2 = /^([A-Z\d]+)[-\s]([A-Z\d]+)$/
+
+/**
+ * Converts a postcode string into a tuple of two numbers for database storage.
+ * The first number represents the format, and the second number represents the postcode value.
+ *
+ * @param postcode - The input postcode string
+ * @returns A tuple [format, value] where:
+ * - format is positive for numeric postcodes, negative for string postcodes
+ * - value is the numeric representation of the postcode
+ */
+export function getPostcodeDatabase(postcode: string): [number, number] {
+ if (!postcode)
+ return [0, 0]
+
+ //* Numeric postcode handling
+ if (isPostNumReg.test(postcode)) {
+ return [
+ postcode.length, //* Format: 1~9 (length of the numeric postcode)
+ Number.parseInt(postcode, 10), //* Value: 0~999999999
+ ]
+ }
+
+ const numericMatch = isPostNumReg2.exec(postcode)
+ if (numericMatch) {
+ const [, part1, part2] = numericMatch as unknown as [string, string, string]
+ return [
+ Number.parseInt(`${part1.length}${part2.length}`, 10), //* Format: 11~66 (lengths of two parts)
+ Number.parseInt(part1 + part2, 10), //* Value: 0~999999999
+ ]
+ }
+
+ //* String postcode handling
+ const stringMatch = isPostStrReg.exec(postcode)
+ if (stringMatch) {
+ const num = Number.parseInt(postcode, 36)
+ if (num < 2 ** 32) {
+ return [
+ -postcode.length, //* Format: -1~-9 (negative length of the string postcode)
+ num, //* Value: base 36 representation
+ ]
+ }
+ else {
+ return [
+ Number.parseInt(`2${postcode.slice(0, 1)}`, 36), //* Format: 72~107 (special encoding for long postcodes)
+ Number.parseInt(postcode.slice(1), 36), //* Value: 0~2176782335 (base 36, max 6 chars)
+ ]
+ }
+ }
+
+ const stringMatch2 = isPostStrReg2.exec(postcode)
+ if (!stringMatch2) {
+ log('warn', `Invalid postcode ${postcode}`)
+ return [0, 0] //* Invalid postcode
+ }
+
+ const [, part1, part2] = stringMatch2 as unknown as [string, string, string]
+ return [
+ -Number.parseInt(`${part1.length}${part2.length}`, 10), //* Format: -11~-55 (negative sum of part lengths)
+ Number.parseInt(part1 + part2, 36), //* Value: 0~2176782335 (base 36, max 6 chars)
+ ]
+}
diff --git a/packages/util/src/functions/getSettings.test.ts b/packages/util/src/functions/getSettings.test.ts
new file mode 100644
index 0000000..891665f
--- /dev/null
+++ b/packages/util/src/functions/getSettings.test.ts
@@ -0,0 +1,198 @@
+import type { IpLocationApiInputSettings } from './getSettings'
+import { join } from 'node:path'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import { DEFAULT_SETTINGS, LOCATION_FIELDS, MAIN_FIELDS } from '../constants'
+import { getSettings } from './getSettings'
+
+describe('getSettings', () => {
+ const originalEnv = process.env
+ const originalArgv = process.argv
+
+ beforeEach(() => {
+ vi.resetModules()
+ process.env = { ...originalEnv }
+ process.argv = [...originalArgv]
+ })
+
+ afterEach(() => {
+ process.env = originalEnv
+ process.argv = originalArgv
+ })
+
+ it('should return default settings when no input is provided', () => {
+ const settings = getSettings()
+ expect.soft(settings).toMatchObject({
+ licenseKey: DEFAULT_SETTINGS.licenseKey,
+ series: DEFAULT_SETTINGS.series,
+ fields: DEFAULT_SETTINGS.fields,
+ language: DEFAULT_SETTINGS.language,
+ smallMemory: DEFAULT_SETTINGS.smallMemory,
+ smallMemoryFileSize: DEFAULT_SETTINGS.smallMemoryFileSize,
+ })
+ })
+
+ it('should override default settings with input settings', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ licenseKey: 'test-key',
+ series: 'GeoIP2',
+ fields: ['country', 'city'],
+ language: 'es',
+ smallMemory: true,
+ smallMemoryFileSize: 8192,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings).toMatchObject({
+ licenseKey: 'test-key',
+ series: 'GeoIP2',
+ fields: ['country', 'city'],
+ language: 'es',
+ smallMemory: true,
+ smallMemoryFileSize: 8192,
+ })
+ })
+
+ it('should use environment variables to override default settings', () => {
+ process.env.ILA_LICENSE_KEY = 'env-key'
+ process.env.ILA_SERIES = 'GeoIP2'
+ process.env.ILA_FIELDS = 'country,city'
+ process.env.ILA_LANGUAGE = 'fr'
+ process.env.ILA_SMALL_MEMORY = 'true'
+ process.env.ILA_SMALL_MEMORY_FILE_SIZE = '16384'
+
+ const settings = getSettings()
+ expect.soft(settings).toMatchObject({
+ licenseKey: 'env-key',
+ series: 'GeoIP2',
+ fields: ['country', 'city'],
+ language: 'fr',
+ smallMemory: true,
+ smallMemoryFileSize: 16384,
+ })
+ })
+
+ it('should use CLI arguments to override default and environment settings', () => {
+ process.env.ILA_LICENSE_KEY = 'env-key'
+ process.argv = [
+ ...process.argv,
+ 'ILA_LICENSE_KEY=cli-key',
+ 'ILA_SERIES=GeoIP2',
+ 'ILA_FIELDS=country,region1',
+ 'ILA_LANGUAGE=de',
+ 'ILA_SMALL_MEMORY=true',
+ 'ILA_SMALL_MEMORY_FILE_SIZE=32768',
+ ]
+
+ const settings = getSettings()
+ expect.soft(settings).toMatchObject({
+ licenseKey: 'cli-key',
+ series: 'GeoIP2',
+ fields: ['country', 'region1'],
+ language: 'de',
+ smallMemory: true,
+ smallMemoryFileSize: 32768,
+ })
+ })
+
+ it('should process "all" fields correctly', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ fields: 'all',
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.fields).toEqual([...MAIN_FIELDS, ...LOCATION_FIELDS])
+
+ process.argv = [...process.argv, 'ILA_FIELDS=all']
+ const settings2 = getSettings()
+ expect.soft(settings2.fields).toEqual([...MAIN_FIELDS, ...LOCATION_FIELDS])
+ })
+
+ it('should filter out invalid fields', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ fields: ['country', 'invalid_field', 'city'] as any,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.fields).toEqual(['country', 'city'])
+ })
+
+ it('should handle invalid fields input', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ fields: 0 as any,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.fields).toEqual(DEFAULT_SETTINGS.fields)
+ })
+
+ it('should use default fields if all provided fields are invalid', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ fields: ['invalid_field1', 'invalid_field2'] as any,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.fields).toEqual(DEFAULT_SETTINGS.fields)
+ })
+
+ it('should process directory paths correctly', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ dataDir: '../custom-data',
+ tmpDataDir: '/tmp/custom-tmp',
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.dataDir).toMatch(/custom-data$/)
+ expect.soft(settings.tmpDataDir).toBe('/tmp/custom-tmp')
+ })
+
+ it('should calculate correct record sizes and database settings', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ fields: ['country', 'city', 'latitude', 'longitude'],
+ smallMemory: true,
+ smallMemoryFileSize: 4096,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.dataType).toBe('City')
+ expect.soft(settings.locationFile).toBe(true)
+ expect.soft(settings.mainRecordSize).toBeGreaterThan(0)
+ expect.soft(settings.locationRecordSize).toBeGreaterThan(0)
+ expect.soft(settings.v4.recordSize).toBeGreaterThan(settings.mainRecordSize)
+ expect.soft(settings.v6.recordSize).toBeGreaterThan(settings.mainRecordSize)
+ expect.soft(settings.v4.fileLineMax).toBeGreaterThan(0)
+ expect.soft(settings.v6.fileLineMax).toBeGreaterThan(0)
+
+ const settings2 = getSettings({
+ fields: ['country', 'city', 'latitude', 'longitude'],
+ smallMemory: true,
+ smallMemoryFileSize: 0,
+ })
+ expect.soft(settings2.dataType).toBe('City')
+ expect.soft(settings2.locationFile).toBe(true)
+ expect.soft(settings2.mainRecordSize).toBeGreaterThan(0)
+ expect.soft(settings2.locationRecordSize).toBeGreaterThan(0)
+ expect.soft(settings2.v4.recordSize).toBeGreaterThan(settings2.mainRecordSize)
+ expect.soft(settings2.v6.recordSize).toBeGreaterThan(settings2.mainRecordSize)
+ expect.soft(settings2.v4.fileLineMax).toBe(1)
+ expect.soft(settings2.v6.fileLineMax).toBe(1)
+ })
+
+ it('should generate correct fieldDir', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ fields: ['country', 'city'],
+ dataDir: '/custom/data/dir',
+ }
+ const settings = getSettings(inputSettings)
+ const expectedFieldDir = join('/custom/data/dir', (16 + 2048).toString(36))
+ expect.soft(settings.fieldDir).toBe(expectedFieldDir)
+ })
+
+ it('should handle invalid language input', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ language: 'invalid_language' as any,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.language).toBe(DEFAULT_SETTINGS.language)
+ })
+
+ it('should handle invalid series input', () => {
+ const inputSettings: IpLocationApiInputSettings = {
+ series: 'InvalidSeries' as any,
+ }
+ const settings = getSettings(inputSettings)
+ expect.soft(settings.series).toBe(DEFAULT_SETTINGS.series)
+ })
+})
diff --git a/packages/util/src/functions/getSettings.ts b/packages/util/src/functions/getSettings.ts
new file mode 100644
index 0000000..19fef35
--- /dev/null
+++ b/packages/util/src/functions/getSettings.ts
@@ -0,0 +1,296 @@
+import type { Buffer } from 'node:buffer'
+import { dirname, join, resolve as resolvePath } from 'node:path'
+import process from 'node:process'
+import { fileURLToPath } from 'node:url'
+import { DEFAULT_SETTINGS, LOCATION_FIELDS, MAIN_FIELDS, setSavedSettings, v4, v6 } from '../constants.js'
+import { getFieldsSize } from './getFieldsSize.js'
+
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = dirname(__filename)
+
+export interface LocalDatabase {
+ version: Version
+ recordSize: number
+ fileLineMax: number
+ folderLineMax: number
+ loadedData?: {
+ startIps: Version extends 4 ? Uint32Array : BigUint64Array
+ endIps?: Version extends 4 ? Uint32Array : BigUint64Array
+ mainBuffer?: Buffer
+ lastLine: number
+ firstIp: Version extends 4 ? number : bigint
+ }
+}
+
+/**
+ * The settings for the IpLocationApi
+ */
+export interface IpLocationApiInputSettings {
+ /**
+ * Your MaxMind license key or 'redist' for free GeoLite2 Redistributed database.
+ * @default 'redist'
+ */
+ licenseKey?: string
+ /**
+ * The series of the database, GeoLite2 or GeoIP2. If you use 'redist', this setting is ignored.
+ * @default 'GeoLite2'
+ * @requires licenseKey
+ */
+ series?: 'GeoLite2' | 'GeoIP2'
+
+ /**
+ * The directory to save the database
+ * @default '../data'
+ */
+ dataDir?: string
+ /**
+ * The directory to save the temporary database
+ * @default '../tmp'
+ */
+ tmpDataDir?: string
+ /**
+ * The fields to include in the database
+ * @default ['country']
+ */
+ fields?: ((typeof MAIN_FIELDS)[number] | (typeof LOCATION_FIELDS)[number])[] | 'all'
+ /**
+ * The language of the database
+ * @default 'en'
+ */
+ language?: 'de' | 'en' | 'es' | 'fr' | 'ja' | 'pt-BR' | 'ru' | 'zh-CN'
+ /**
+ * Whether to use small memory mode
+ * @default false
+ */
+ smallMemory?: boolean
+ /**
+ * The file size for small memory mode
+ * @default 4096
+ */
+ smallMemoryFileSize?: number
+ /**
+ * Whether to add country info in the result
+ * @default false
+ *
+ * @requires countries-list package (optional peer dependency)
+ * @link https://www.npmjs.com/package/countries-list
+ */
+ addCountryInfo?: boolean
+ /**
+ * Whether to suppress logging
+ * @default false
+ */
+ silent?: boolean
+}
+
+export interface IpLocationApiSettings {
+ licenseKey: string
+ series: 'GeoLite2' | 'GeoIP2'
+ dataDir: string
+ tmpDataDir: string
+ fields: ((typeof MAIN_FIELDS)[number] | (typeof LOCATION_FIELDS)[number])[]
+ language: 'de' | 'en' | 'es' | 'fr' | 'ja' | 'pt-BR' | 'ru' | 'zh-CN'
+ fieldDir: string
+ smallMemory: boolean
+ smallMemoryFileSize: number
+ dataType: 'Country' | 'City'
+ locationFile: boolean
+ mainRecordSize: number
+ locationRecordSize: number
+ v4: LocalDatabase<4>
+ v6: LocalDatabase<6>
+ addCountryInfo: boolean
+ silent: boolean
+}
+
+/**
+ * Get the key from the environment variable
+ * @param key The key to get
+ * @returns The key
+ *
+ * @example
+ * getKey('testKey') -> 'ILA_TEST_KEY'
+ */
+function getKey(key: string): string {
+ return `ILA_${key.replace(/([A-Z])/g, '_$1').replace(/^_/, '').toUpperCase()}`
+}
+
+/**
+ * The bit flag for the fields
+ */
+const FIELD_BIT_FLAG: Record<((typeof MAIN_FIELDS)[number] | (typeof LOCATION_FIELDS)[number]), number> = {
+ latitude: 1,
+ longitude: 2,
+ area: 4,
+ postcode: 8,
+ country: 16,
+ region1: 32,
+ region1_name: 64,
+ region2: 128,
+ region2_name: 256,
+ metro: 512,
+ timezone: 1024,
+ city: 2048,
+ eu: 4096,
+}
+
+/**
+ * Get the settings from various sources and merge them with default values
+ * @param settings The settings from the function parameters
+ * @returns The merged and processed IpLocationApiSettings
+ */
+export function getSettings(settings?: IpLocationApiInputSettings): IpLocationApiSettings {
+ //* Fetch settings from different sources
+ const envSettings = getFromIlaObject(process.env)
+ const cliSettings = getFromIlaObject(
+ Object.fromEntries(
+ process.argv
+ .slice(2)
+ .map(arg => arg.split('=') as [string, string])
+ .filter(([key, value]) => key.startsWith('ILA_') && value),
+ ),
+ )
+
+ //* Merge settings with priority: defaults < env < CLI < function params
+ const mergedSettings = {
+ ...DEFAULT_SETTINGS,
+ ...envSettings,
+ ...cliSettings,
+ ...(settings ?? {}),
+ }
+
+ //* Process and validate individual settings
+ const fields = processFields(mergedSettings.fields)
+ const series = processSeries(mergedSettings.series)
+ const dataDir = processDirectory(mergedSettings.dataDir)
+ const tmpDataDir = processDirectory(mergedSettings.tmpDataDir)
+ const language = processLanguage(mergedSettings.language)
+ const dataType = fields.length === 1 && fields[0] === 'country' ? 'Country' : 'City'
+ const locationFile = dataType !== 'Country' && LOCATION_FIELDS.some(field => fields.includes(field))
+ let mainRecordSize = dataType === 'Country' ? 2 : getFieldsSize(fields.filter(field => (MAIN_FIELDS as unknown as string[]).includes(field)))
+ if (locationFile)
+ mainRecordSize += 4
+
+ v4.recordSize = mainRecordSize
+ v6.recordSize = mainRecordSize
+
+ if (mergedSettings.smallMemory) {
+ v4.recordSize += 4
+ v4.fileLineMax = (mergedSettings.smallMemoryFileSize / v4.recordSize | 0) || 1
+ v4.folderLineMax = v4.fileLineMax * 1024
+
+ v6.recordSize += 8
+ v6.fileLineMax = (mergedSettings.smallMemoryFileSize / v6.recordSize | 0) || 1
+ v6.folderLineMax = v6.fileLineMax * 1024
+ }
+
+ //* Construct and return the final settings
+ return setSavedSettings({
+ licenseKey: mergedSettings.licenseKey,
+ dataDir,
+ tmpDataDir,
+ fields,
+ series,
+ language,
+ fieldDir: join(dataDir, fields.reduce((sum, v) => sum + FIELD_BIT_FLAG[v], 0).toString(36)),
+ smallMemory: mergedSettings.smallMemory,
+ smallMemoryFileSize: mergedSettings.smallMemoryFileSize,
+ dataType,
+ locationFile,
+ mainRecordSize,
+ locationRecordSize: getFieldsSize(fields.filter(field => (LOCATION_FIELDS as unknown as string[]).includes(field))),
+ v4,
+ v6,
+ addCountryInfo: mergedSettings.addCountryInfo,
+ silent: mergedSettings.silent,
+ })
+}
+
+/**
+ * Extract IpLocationApi settings from an object with ILA_ prefixed keys
+ * @param ilaObject An object containing ILA_ prefixed keys
+ * @returns Partial IpLocationApiInputSettings
+ */
+function getFromIlaObject(ilaObject: Record): Partial {
+ const settings = {
+ licenseKey: ilaObject[getKey('licenseKey')],
+ series: ilaObject[getKey('series')] as 'GeoLite2' | 'GeoIP2',
+ dataDir: ilaObject[getKey('dataDir')],
+ tmpDataDir: ilaObject[getKey('tmpDataDir')],
+ fields: ilaObject[getKey('fields')] === 'all' ? 'all' : ilaObject[getKey('fields')]?.split(',') as IpLocationApiInputSettings['fields'],
+ language: ilaObject[getKey('language')] as IpLocationApiInputSettings['language'],
+ smallMemory: ilaObject[getKey('smallMemory')] ? ilaObject[getKey('smallMemory')] === 'true' : undefined,
+ smallMemoryFileSize: ilaObject[getKey('smallMemoryFileSize')] ? Number.parseInt(ilaObject[getKey('smallMemoryFileSize')]!) : undefined,
+ }
+
+ //* Remove keys with undefined values
+ return Object.fromEntries(
+ Object.entries(settings).filter(([_, value]) => value !== undefined),
+ ) as Partial
+}
+
+/**
+ * Process and validate the fields setting
+ * @param fields The input fields setting
+ * @returns Validated array of fields or default fields
+ */
+function processFields(fields: IpLocationApiInputSettings['fields']): IpLocationApiSettings['fields'] {
+ if (fields === 'all') {
+ return [...MAIN_FIELDS, ...LOCATION_FIELDS]
+ }
+
+ if (Array.isArray(fields)) {
+ const validFields = fields.filter(field =>
+ MAIN_FIELDS.includes(field as any) || LOCATION_FIELDS.includes(field as any),
+ ) as IpLocationApiSettings['fields']
+
+ return validFields.length > 0 ? validFields : DEFAULT_SETTINGS.fields
+ }
+
+ return DEFAULT_SETTINGS.fields
+}
+
+/**
+ * Process and validate the series setting
+ * @param series The input series setting
+ * @returns Validated series or default series
+ */
+function processSeries(series: IpLocationApiInputSettings['series']): IpLocationApiSettings['series'] {
+ if (series === 'GeoLite2' || series === 'GeoIP2') {
+ return series
+ }
+
+ return DEFAULT_SETTINGS.series
+}
+
+/**
+ * Regular expression to match Windows drive letters
+ */
+const WINDOWS_DRIVE_REG = /^[a-z]:\\/i
+
+/**
+ * Process and resolve the directory path
+ * @param directory The input directory path
+ * @returns Resolved absolute directory path
+ */
+export function processDirectory(directory: string): string {
+ //* If the path is not absolute, resolve it relative to __dirname
+ if (!directory.startsWith('/') && !directory.startsWith('\\\\') && !WINDOWS_DRIVE_REG.test(directory)) {
+ directory = resolvePath(__dirname, directory)
+ }
+ return directory
+}
+
+/**
+ * Process and validate the language setting
+ * @param language The input language setting
+ * @returns Validated language or default language
+ */
+function processLanguage(language: IpLocationApiInputSettings['language']): IpLocationApiSettings['language'] {
+ const validLanguages = ['de', 'en', 'es', 'fr', 'ja', 'pt-BR', 'ru', 'zh-CN'] as const
+ if (language && validLanguages.includes(language)) {
+ return language
+ }
+
+ return DEFAULT_SETTINGS.language
+}
diff --git a/packages/util/src/functions/getSmallMemoryFile.test.ts b/packages/util/src/functions/getSmallMemoryFile.test.ts
new file mode 100644
index 0000000..a2f8852
--- /dev/null
+++ b/packages/util/src/functions/getSmallMemoryFile.test.ts
@@ -0,0 +1,63 @@
+import type { LocalDatabase } from './getSettings.js'
+import { describe, expect, it } from 'vitest'
+import { getSmallMemoryFile } from './getSmallMemoryFile'
+
+describe('getSmallMemoryFile', () => {
+ const mockDb: LocalDatabase = {
+ version: 4,
+ folderLineMax: 1000,
+ fileLineMax: 100,
+ recordSize: 10,
+ }
+
+ it('should return correct values for the first line', () => {
+ const result = getSmallMemoryFile(0, mockDb)
+ expect.soft(result).toEqual(['v4/_0', '_0', 0])
+ })
+
+ it('should return correct values for a line within the first file', () => {
+ const result = getSmallMemoryFile(50, mockDb)
+ expect.soft(result).toEqual(['v4/_0', '_0', 500])
+ })
+
+ it('should return correct values for the first line of the second file', () => {
+ const result = getSmallMemoryFile(100, mockDb)
+ expect.soft(result).toEqual(['v4/_0', '_1', 0])
+ })
+
+ it('should return correct values for a line in a different folder', () => {
+ const result = getSmallMemoryFile(1500, mockDb)
+ expect.soft(result).toEqual(['v4/_1', '_5', 0])
+ })
+
+ it('should handle temporary files correctly', () => {
+ const result = getSmallMemoryFile(2000, mockDb, true)
+ expect.soft(result).toEqual(['v4-tmp/_2', '_0', 0])
+ })
+
+ it('should handle large line numbers correctly', () => {
+ const result = getSmallMemoryFile(10000, mockDb)
+ expect.soft(result).toEqual(['v4/_a', '_0', 0])
+ })
+
+ it('should use base 36 for folder and file numbers', () => {
+ const result = getSmallMemoryFile(50000, mockDb)
+ expect.soft(result).toEqual(['v4/1e', '_0', 0])
+ })
+
+ it('should calculate correct line offset', () => {
+ const result = getSmallMemoryFile(12345, mockDb)
+ expect.soft(result).toEqual(['v4/_c', '_3', 450])
+ })
+
+ it('should work with different database configurations', () => {
+ const customDb: LocalDatabase = {
+ version: 6,
+ folderLineMax: 5000,
+ fileLineMax: 500,
+ recordSize: 20,
+ }
+ const result = getSmallMemoryFile(7777, customDb)
+ expect.soft(result).toEqual(['v6/_1', '_5', 5540])
+ })
+})
diff --git a/packages/util/src/functions/getSmallMemoryFile.ts b/packages/util/src/functions/getSmallMemoryFile.ts
new file mode 100644
index 0000000..8aa0ee2
--- /dev/null
+++ b/packages/util/src/functions/getSmallMemoryFile.ts
@@ -0,0 +1,18 @@
+import type { LocalDatabase } from './getSettings.js'
+import path from 'node:path'
+import { getUnderscoreFill } from './getUnderscoreFill.js'
+
+/**
+ * Get the small memory file information for the given line number and database settings.
+ * @param line - The line number in the database.
+ * @param db - The database settings.
+ * @param isTmp - Whether the file is temporary.
+ * @returns An array containing the directory, file number, and line offset.
+ */
+export function getSmallMemoryFile(line: number, db: LocalDatabase, isTmp = false): [string, string, number] {
+ const dbNumber = line / db.folderLineMax | 0
+ const fileNumber = (line - dbNumber * db.folderLineMax) / db.fileLineMax | 0
+ const lineOffset = line - dbNumber * db.folderLineMax - fileNumber * db.fileLineMax
+ const dir = path.join(`v${db.version}${isTmp ? '-tmp' : ''}`, getUnderscoreFill(dbNumber.toString(36), 2))
+ return [dir, getUnderscoreFill(fileNumber.toString(36), 2), lineOffset * db.recordSize]
+}
diff --git a/packages/util/src/functions/getUnderscoreFill.test.ts b/packages/util/src/functions/getUnderscoreFill.test.ts
new file mode 100644
index 0000000..530567a
--- /dev/null
+++ b/packages/util/src/functions/getUnderscoreFill.test.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from 'vitest'
+import { getUnderscoreFill } from './getUnderscoreFill'
+
+describe('getUnderscoreFill', () => {
+ it('should pad the string with underscores to the left', () => {
+ expect.soft(getUnderscoreFill('test', 8)).toBe('____test')
+ })
+
+ it('should return the original string if it is already longer than the specified length', () => {
+ expect.soft(getUnderscoreFill('longstring', 5)).toBe('longstring')
+ })
+
+ it('should return the original string if it is equal to the specified length', () => {
+ expect.soft(getUnderscoreFill('equal', 5)).toBe('equal')
+ })
+
+ it('should handle empty string input', () => {
+ expect.soft(getUnderscoreFill('', 3)).toBe('___')
+ })
+
+ it('should handle length of 0', () => {
+ expect.soft(getUnderscoreFill('test', 0)).toBe('test')
+ })
+
+ it('should handle very long padding', () => {
+ expect.soft(getUnderscoreFill('x', 100)).toBe(`${'_'.repeat(99)}x`)
+ })
+})
diff --git a/packages/util/src/functions/getUnderscoreFill.ts b/packages/util/src/functions/getUnderscoreFill.ts
new file mode 100644
index 0000000..ff5ef63
--- /dev/null
+++ b/packages/util/src/functions/getUnderscoreFill.ts
@@ -0,0 +1,15 @@
+/**
+ * Pads a string with underscores to the left until it reaches the specified length.
+ * If the input string is already longer than or equal to the specified length, it's returned unchanged.
+ *
+ * @param {string} string - The input string to pad.
+ * @param {number} length - The desired total length of the resulting string.
+ * @returns {string} The padded string or the original string if it's already long enough.
+ */
+export function getUnderscoreFill(string: string, length: number): string {
+ if (string.length >= length)
+ return string
+
+ //* Calculate the number of underscores needed and add them to the left of the string
+ return '_'.repeat(length - string.length) + string
+};
diff --git a/packages/util/src/functions/log.test.ts b/packages/util/src/functions/log.test.ts
new file mode 100644
index 0000000..b0042f9
--- /dev/null
+++ b/packages/util/src/functions/log.test.ts
@@ -0,0 +1,19 @@
+import { describe, it, vi } from 'vitest'
+import { getSettings } from './getSettings'
+import { log } from './log'
+
+describe('log', () => {
+ it('should not log when silent is true', ({ expect }) => {
+ const spy = vi.spyOn(console, 'info')
+ getSettings({ silent: true })
+ log('info', 'test')
+ expect.soft(spy).not.toHaveBeenCalled()
+ })
+
+ it('should log when silent is false', ({ expect }) => {
+ const spy = vi.spyOn(console, 'info')
+ getSettings({ silent: false })
+ log('info', 'test')
+ expect.soft(spy).toHaveBeenCalled()
+ })
+})
diff --git a/packages/util/src/functions/log.ts b/packages/util/src/functions/log.ts
new file mode 100644
index 0000000..d5b5584
--- /dev/null
+++ b/packages/util/src/functions/log.ts
@@ -0,0 +1,9 @@
+import { SAVED_SETTINGS } from '../constants.js'
+
+export function log(type: 'info' | 'warn' | 'error', ...args: any[]) {
+ if (SAVED_SETTINGS.silent)
+ return
+
+ // eslint-disable-next-line no-console
+ console[type](...args)
+}
diff --git a/packages/util/src/functions/makeDatabase.test.ts b/packages/util/src/functions/makeDatabase.test.ts
new file mode 100644
index 0000000..f79e585
--- /dev/null
+++ b/packages/util/src/functions/makeDatabase.test.ts
@@ -0,0 +1,41 @@
+import { describe, expect, it } from 'vitest'
+import { makeDatabase } from './makeDatabase'
+
+describe('makeDatabase', () => {
+ it('should create a new entry in an empty database', () => {
+ const database: Record = {}
+ const result = makeDatabase('test', database)
+ expect.soft(result).toBe(0)
+ expect.soft(database).toEqual({ test: 0 })
+ })
+
+ it('should create a new entry in a non-empty database', () => {
+ const database: Record = { existing: 0 }
+ const result = makeDatabase('test', database)
+ expect.soft(result).toBe(1)
+ expect.soft(database).toEqual({ existing: 0, test: 1 })
+ })
+
+ it('should retrieve an existing entry', () => {
+ const database: Record = { existing: 0 }
+ const result = makeDatabase('existing', database)
+ expect.soft(result).toBe(0)
+ expect.soft(database).toEqual({ existing: 0 })
+ })
+
+ it('should handle multiple entries correctly', () => {
+ const database: Record = {}
+ expect.soft(makeDatabase('first', database)).toBe(0)
+ expect.soft(makeDatabase('second', database)).toBe(1)
+ expect.soft(makeDatabase('third', database)).toBe(2)
+ expect.soft(makeDatabase('first', database)).toBe(0)
+ expect.soft(database).toEqual({ first: 0, second: 1, third: 2 })
+ })
+
+ it('should work with non-string keys', () => {
+ const database: Record = {}
+ expect.soft(makeDatabase('123', database)).toBe(0)
+ expect.soft(makeDatabase('true', database)).toBe(1)
+ expect.soft(database).toEqual({ 123: 0, true: 1 })
+ })
+})
diff --git a/packages/util/src/functions/makeDatabase.ts b/packages/util/src/functions/makeDatabase.ts
new file mode 100644
index 0000000..7a1f0fe
--- /dev/null
+++ b/packages/util/src/functions/makeDatabase.ts
@@ -0,0 +1,12 @@
+/**
+ * Creates or retrieves an entry in a database.
+ * @param name - The name to store or retrieve
+ * @param database - The database to use
+ * @returns The index of the name in the database
+ */
+export function makeDatabase(name: string, database: Record): number {
+ if (database[name] === undefined) {
+ database[name] = Object.keys(database).length
+ }
+ return database[name]
+}
diff --git a/packages/util/src/functions/ntoa4.test.ts b/packages/util/src/functions/ntoa4.test.ts
new file mode 100644
index 0000000..478c31a
--- /dev/null
+++ b/packages/util/src/functions/ntoa4.test.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from 'vitest'
+import { ntoa4 } from './ntoa4'
+
+describe('ntoa4', () => {
+ it('should convert 32-bit integers to valid IPv4 addresses', () => {
+ expect.soft(ntoa4(3232235777)).toBe('192.168.1.1')
+ expect.soft(ntoa4(167772161)).toBe('10.0.0.1')
+ expect.soft(ntoa4(2886729729)).toBe('172.16.0.1')
+ expect.soft(ntoa4(4294967295)).toBe('255.255.255.255')
+ expect.soft(ntoa4(0)).toBe('0.0.0.0')
+ })
+
+ it('should handle edge cases', () => {
+ expect.soft(ntoa4(2130706433)).toBe('127.0.0.1') //* Localhost
+ expect.soft(ntoa4(4294967040)).toBe('255.255.255.0') //* Subnet mask
+ expect.soft(ntoa4(16909060)).toBe('1.2.3.4')
+ })
+
+ it('should handle all zeros and all ones', () => {
+ expect.soft(ntoa4(0)).toBe('0.0.0.0')
+ expect.soft(ntoa4(4294967295)).toBe('255.255.255.255')
+ })
+
+ it('should throw an error for invalid 32-bit integers', () => {
+ expect.soft(() => ntoa4(-1)).toThrow('Invalid 32-bit unsigned integer')
+ expect.soft(() => ntoa4(4294967296)).toThrow('Invalid 32-bit unsigned integer')
+ })
+})
diff --git a/packages/util/src/functions/ntoa4.ts b/packages/util/src/functions/ntoa4.ts
new file mode 100644
index 0000000..41bede6
--- /dev/null
+++ b/packages/util/src/functions/ntoa4.ts
@@ -0,0 +1,24 @@
+/**
+ * Convert a 32-bit integer to an IPv4 string
+ * @param num - The 32-bit integer to convert
+ * @returns The IPv4 string
+ * @example
+ * ```ts
+ * ntoa4(3232235777) // '192.168.1.1'
+ * ```
+ * @throws Will throw an error if the input is not a valid 32-bit unsigned integer
+ */
+export function ntoa4(num: number): string {
+ if (!Number.isInteger(num) || num < 0 || num > 0xFFFFFFFF) {
+ throw new Error('Invalid 32-bit unsigned integer')
+ }
+
+ //* Extract each octet using bitwise operations
+ const octet1 = (num >>> 24) & 255
+ const octet2 = (num >>> 16) & 255
+ const octet3 = (num >>> 8) & 255
+ const octet4 = num & 255
+
+ //* Combine the octets into an IPv4 string
+ return `${octet1}.${octet2}.${octet3}.${octet4}`
+}
diff --git a/packages/util/src/functions/number37ToString.test.ts b/packages/util/src/functions/number37ToString.test.ts
new file mode 100644
index 0000000..5fd3e4b
--- /dev/null
+++ b/packages/util/src/functions/number37ToString.test.ts
@@ -0,0 +1,42 @@
+import { describe, expect, it } from 'vitest'
+import { number37ToString } from './number37ToString'
+
+describe('number37ToString', () => {
+ it('should convert 0 to empty string', () => {
+ expect.soft(number37ToString(0)).toBe('')
+ })
+
+ it('should convert single-digit numbers correctly', () => {
+ expect.soft(number37ToString(11)).toBe('A')
+ expect.soft(number37ToString(36)).toBe('Z')
+ expect.soft(number37ToString(1)).toBe('0')
+ expect.soft(number37ToString(10)).toBe('9')
+ })
+
+ it('should convert multi-digit numbers correctly', () => {
+ expect.soft(number37ToString(15516)).toBe('ABC')
+ expect.soft(number37ToString(2853)).toBe('123')
+ expect.soft(number37ToString(47877)).toBe('XYZ')
+ })
+
+ it('should handle lowercase letters in output', () => {
+ expect.soft(number37ToString(15516)).toBe('ABC')
+ expect.soft(number37ToString(47877)).toBe('XYZ')
+ })
+
+ it('should handle mixed alphanumeric output', () => {
+ expect.soft(number37ToString(767144277)).toBe('A1B2C3')
+ expect.soft(number37ToString(159472233)).toBe('1A2B3C')
+ })
+
+ it('should produce different strings for different numbers', () => {
+ const result1 = number37ToString(12345)
+ const result2 = number37ToString(67890)
+ expect.soft(result1).not.toBe(result2)
+ })
+
+ it('should handle very large numbers', () => {
+ const largeNumber = 37 ** 10 - 1 //* Largest 10-digit base-37 number
+ expect.soft(() => number37ToString(largeNumber)).not.toThrow()
+ })
+})
diff --git a/packages/util/src/functions/number37ToString.ts b/packages/util/src/functions/number37ToString.ts
new file mode 100644
index 0000000..c2514c5
--- /dev/null
+++ b/packages/util/src/functions/number37ToString.ts
@@ -0,0 +1,18 @@
+/**
+ * Converts a number to a base-37 string representation.
+ *
+ * @param number - The number to convert.
+ * @returns The base-37 string representation of the input number.
+ */
+export function number37ToString(number: number): string {
+ let string = ''
+ while (number > 0) {
+ //* Subtract 1 from the remainder to handle the range 0-36
+ //* Convert to base-36 string (0-9, then A-Z)
+ string = (number % 37 - 1).toString(36) + string
+ //* Integer division by 37 for the next iteration
+ number = Math.floor(number / 37)
+ }
+ //* Convert the result to uppercase
+ return string.toUpperCase()
+}
diff --git a/packages/util/src/functions/numberToCountryCode.test.ts b/packages/util/src/functions/numberToCountryCode.test.ts
new file mode 100644
index 0000000..d04405f
--- /dev/null
+++ b/packages/util/src/functions/numberToCountryCode.test.ts
@@ -0,0 +1,40 @@
+import { describe, expect, it } from 'vitest'
+import { numberToCountryCode } from './numberToCountryCode'
+
+describe('numberToCountryCode', () => {
+ it('should convert 0 to "AA"', () => {
+ expect.soft(numberToCountryCode(0)).toBe('AA')
+ })
+
+ it('should convert 675 to "ZZ"', () => {
+ expect.soft(numberToCountryCode(675)).toBe('ZZ')
+ })
+
+ it('should convert 52 to "CA"', () => {
+ expect.soft(numberToCountryCode(52)).toBe('CA')
+ })
+
+ it('should convert 538 to "US"', () => {
+ expect.soft(numberToCountryCode(538)).toBe('US')
+ })
+
+ it('should convert 25 to "AZ"', () => {
+ expect.soft(numberToCountryCode(25)).toBe('AZ')
+ })
+
+ it('should convert 26 to "BA"', () => {
+ expect.soft(numberToCountryCode(26)).toBe('BA')
+ })
+
+ it('should throw an error for negative input', () => {
+ expect.soft(() => numberToCountryCode(-1)).toThrow('Input number must be between 0 and 675')
+ })
+
+ it('should throw an error for input greater than 675', () => {
+ expect.soft(() => numberToCountryCode(676)).toThrow('Input number must be between 0 and 675')
+ })
+
+ it('should throw an error for non-integer input', () => {
+ expect.soft(() => numberToCountryCode(3.14)).toThrow('Input number must be between 0 and 675')
+ })
+})
diff --git a/packages/util/src/functions/numberToCountryCode.ts b/packages/util/src/functions/numberToCountryCode.ts
new file mode 100644
index 0000000..5fb4d1f
--- /dev/null
+++ b/packages/util/src/functions/numberToCountryCode.ts
@@ -0,0 +1,15 @@
+/**
+ * Converts a number to a two-letter country code.
+ * @param num - A number representing the country code (0-675).
+ * @returns A two-letter country code (e.g., 'AA', 'AB', ..., 'ZZ').
+ * @throws {Error} If the input number is out of range.
+ */
+export function numberToCountryCode(num: number): string {
+ if (num < 0 || num > 675 || !Number.isInteger(num)) {
+ throw new Error('Input number must be between 0 and 675')
+ }
+
+ //* Convert number to two-letter code using ASCII values
+ //* First letter: A-Z (65-90), Second letter: A-Z (65-90)
+ return String.fromCharCode((num / 26 | 0) + 65, num % 26 + 65)
+}
diff --git a/packages/util/src/functions/numberToDir.test.ts b/packages/util/src/functions/numberToDir.test.ts
new file mode 100644
index 0000000..12560b7
--- /dev/null
+++ b/packages/util/src/functions/numberToDir.test.ts
@@ -0,0 +1,38 @@
+import { describe, expect, it } from 'vitest'
+import { numberToDir } from './numberToDir'
+
+describe('numberToDir', () => {
+ it('converts single-digit numbers to padded strings', () => {
+ expect.soft(numberToDir(0)).toBe('_0')
+ expect.soft(numberToDir(9)).toBe('_9')
+ })
+
+ it('converts double-digit numbers to base 36 strings', () => {
+ expect.soft(numberToDir(10)).toBe('_a')
+ expect.soft(numberToDir(35)).toBe('_z')
+ })
+
+ it('converts numbers greater than 35 to base 36 strings', () => {
+ expect.soft(numberToDir(36)).toBe('10')
+ expect.soft(numberToDir(61)).toBe('1p')
+ expect.soft(numberToDir(1295)).toBe('zz')
+ })
+
+ it('handles large numbers', () => {
+ expect.soft(numberToDir(1296)).toBe('100')
+ expect.soft(numberToDir(46655)).toBe('zzz')
+ expect.soft(numberToDir(46656)).toBe('1000')
+ })
+
+ it('handles the maximum safe integer', () => {
+ expect.soft(numberToDir(Number.MAX_SAFE_INTEGER)).toBe('2gosa7pa2gv')
+ })
+
+ it('throws an error for negative numbers', () => {
+ expect.soft(() => numberToDir(-1)).toThrow()
+ })
+
+ it('throws an error for non-integer numbers', () => {
+ expect.soft(() => numberToDir(3.14)).toThrow()
+ })
+})
diff --git a/packages/util/src/functions/numberToDir.ts b/packages/util/src/functions/numberToDir.ts
new file mode 100644
index 0000000..99623e4
--- /dev/null
+++ b/packages/util/src/functions/numberToDir.ts
@@ -0,0 +1,16 @@
+import { getUnderscoreFill } from './getUnderscoreFill.js'
+
+/**
+ * Converts a number to a two-character directory name.
+ *
+ * @param {number} number - The number to convert.
+ * @returns {string} A two-character string representation of the number.
+ * @throws {Error} If the input is negative or not an integer.
+ */
+export function numberToDir(number: number): string {
+ if (number < 0 || !Number.isInteger(number)) {
+ throw new Error('Input must be a non-negative integer')
+ }
+ //* Convert number to base 36 and pad with underscores to ensure at least 2 characters
+ return getUnderscoreFill(number.toString(36), 2)
+}
diff --git a/packages/util/src/functions/parseIp.test.ts b/packages/util/src/functions/parseIp.test.ts
new file mode 100644
index 0000000..8141824
--- /dev/null
+++ b/packages/util/src/functions/parseIp.test.ts
@@ -0,0 +1,44 @@
+import { describe, expect, it } from 'vitest'
+import { parseIp } from './parseIp'
+
+describe('parseIp', () => {
+ it('should correctly parse IPv4 addresses', () => {
+ const result = parseIp('192.168.0.1')
+ expect.soft(result).toEqual({ version: 4, ip: 3232235521 })
+ })
+
+ it('should correctly parse IPv6 addresses', () => {
+ const result = parseIp('2001:db8::1')
+ expect.soft(result).toEqual({ version: 6, ip: 2306139568115548160n })
+ })
+
+ it('should correctly parse IPv4-mapped IPv6 addresses', () => {
+ const result = parseIp('::ffff:192.0.2.1')
+ expect.soft(result).toEqual({ version: 4, ip: 3221225985 })
+ })
+
+ it('should throw an error for invalid IP addresses', () => {
+ expect.soft(() => parseIp('invalid')).toThrow()
+ })
+
+ it('should handle edge cases for IPv4', () => {
+ expect.soft(parseIp('0.0.0.0')).toEqual({ version: 4, ip: 0 })
+ expect.soft(parseIp('255.255.255.255')).toEqual({ version: 4, ip: 4294967295 })
+ })
+
+ it('should handle edge cases for IPv6', () => {
+ expect.soft(parseIp('::')).toEqual({ version: 6, ip: 0n })
+ expect.soft(parseIp('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')).toEqual({
+ version: 6,
+ ip: 18446744073709551615n,
+ })
+ })
+
+ it('should handle compressed IPv6 addresses', () => {
+ expect.soft(parseIp('2001:db8::')).toEqual({
+ version: 6,
+ ip: 2306139568115548160n,
+ })
+ expect.soft(parseIp('::1')).toEqual({ version: 6, ip: 0n })
+ })
+})
diff --git a/packages/util/src/functions/parseIp.ts b/packages/util/src/functions/parseIp.ts
new file mode 100644
index 0000000..0e242ce
--- /dev/null
+++ b/packages/util/src/functions/parseIp.ts
@@ -0,0 +1,26 @@
+import { aton4 } from './aton4.js'
+import { aton6 } from './aton6.js'
+
+/**
+ * Parses an IP address string and returns its details.
+ * @param {string} ip - The IP address to parse.
+ * @returns {{ version: 4 | 6, address: string, ip: number | bigint }} An object containing the IP version, original address, and numeric representation.
+ *
+ * @throws Will throw an error if the input is not a valid IP address
+ */
+export function parseIp(ip: string): {
+ version: 4 | 6
+ ip: number | bigint
+} {
+ if (ip.includes(':')) {
+ if (ip.includes('.')) {
+ //* Handle IPv4-mapped IPv6 addresses (e.g., ::ffff:192.0.2.1)
+ ip = ip.split(':').pop()!
+ return { version: 4, ip: aton4(ip) }
+ }
+ //* Handle regular IPv6 addresses
+ return { version: 6, ip: aton6(ip) }
+ }
+ //* Handle IPv4 addresses
+ return { version: 4, ip: aton4(ip) }
+}
diff --git a/packages/util/src/functions/sleep.test.ts b/packages/util/src/functions/sleep.test.ts
new file mode 100644
index 0000000..ce8c7f9
--- /dev/null
+++ b/packages/util/src/functions/sleep.test.ts
@@ -0,0 +1,44 @@
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { sleep } from './sleep'
+
+describe('sleep', () => {
+ it('should pause execution for the specified time', async () => {
+ const startTime = Date.now()
+ const sleepTime = 100
+
+ vi.useFakeTimers()
+
+ const sleepPromise = sleep(sleepTime)
+ vi.advanceTimersByTime(sleepTime)
+ await sleepPromise
+
+ const endTime = Date.now()
+ const elapsedTime = endTime - startTime
+
+ expect.soft(elapsedTime).toBeGreaterThanOrEqual(sleepTime)
+ })
+
+ it('should resolve after the specified time', async () => {
+ const sleepTime = 50
+
+ vi.useFakeTimers()
+
+ const sleepPromise = sleep(sleepTime)
+
+ expect.soft(vi.getTimerCount()).toBe(1)
+
+ vi.advanceTimersByTime(sleepTime - 1)
+ await Promise.resolve() // Allow any pending microtasks to run
+
+ expect.soft(vi.getTimerCount()).toBe(1)
+
+ vi.advanceTimersByTime(1)
+ await sleepPromise
+
+ expect.soft(vi.getTimerCount()).toBe(0)
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+})
diff --git a/packages/util/src/functions/sleep.ts b/packages/util/src/functions/sleep.ts
new file mode 100644
index 0000000..9b43c66
--- /dev/null
+++ b/packages/util/src/functions/sleep.ts
@@ -0,0 +1,6 @@
+/**
+ * Pauses execution for a specified number of milliseconds.
+ * @param {number} ms - The number of milliseconds to pause.
+ * @returns {Promise} A promise that resolves after the specified time.
+ */
+export const sleep = (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms))
diff --git a/packages/util/src/functions/stringToNumber37.test.ts b/packages/util/src/functions/stringToNumber37.test.ts
new file mode 100644
index 0000000..e058b7d
--- /dev/null
+++ b/packages/util/src/functions/stringToNumber37.test.ts
@@ -0,0 +1,42 @@
+import { describe, expect, it } from 'vitest'
+import { stringToNumber37 } from './stringToNumber37'
+
+describe('stringToNumber37', () => {
+ it('should convert empty string to 0', () => {
+ expect.soft(stringToNumber37('')).toBe(0)
+ })
+
+ it('should convert single character strings correctly', () => {
+ expect.soft(stringToNumber37('a')).toBe(11)
+ expect.soft(stringToNumber37('z')).toBe(36)
+ expect.soft(stringToNumber37('0')).toBe(1)
+ expect.soft(stringToNumber37('9')).toBe(10)
+ })
+
+ it('should convert multi-character strings correctly', () => {
+ expect.soft(stringToNumber37('abc')).toBe(15516)
+ expect.soft(stringToNumber37('123')).toBe(2853)
+ expect.soft(stringToNumber37('xyz')).toBe(47877)
+ })
+
+ it('should handle uppercase and lowercase letters', () => {
+ expect.soft(stringToNumber37('ABC')).toBe(15516)
+ expect.soft(stringToNumber37('XYZ')).toBe(47877)
+ })
+
+ it('should handle mixed alphanumeric strings', () => {
+ expect.soft(stringToNumber37('a1b2c3')).toBe(767144277)
+ expect.soft(stringToNumber37('1a2b3c')).toBe(159472233)
+ })
+
+ it('should produce different results for different strings', () => {
+ const result1 = stringToNumber37('hello')
+ const result2 = stringToNumber37('world')
+ expect.soft(result1).not.toBe(result2)
+ })
+
+ it('should handle very long strings', () => {
+ const longString = 'a'.repeat(1000)
+ expect.soft(() => stringToNumber37(longString)).not.toThrow()
+ })
+})
diff --git a/packages/util/src/functions/stringToNumber37.ts b/packages/util/src/functions/stringToNumber37.ts
new file mode 100644
index 0000000..fd0e147
--- /dev/null
+++ b/packages/util/src/functions/stringToNumber37.ts
@@ -0,0 +1,15 @@
+/**
+ * Converts a string to a number using a base-37 encoding scheme.
+ *
+ * @param string - The input string to be converted
+ * @returns The resulting number after conversion
+ */
+export function stringToNumber37(string: string): number {
+ let number = 0
+ for (const char of string) {
+ //* Convert each character to a number (0-35) and add 1 to avoid collisions with 0
+ //* Then multiply the current number by 37 and add the new value
+ number = number * 37 + Number.parseInt(char, 36) + 1
+ }
+ return number
+}
diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts
new file mode 100644
index 0000000..0bd46fc
--- /dev/null
+++ b/packages/util/src/index.ts
@@ -0,0 +1,10 @@
+export { SAVED_SETTINGS } from './constants.js'
+export * from './functions/aton4.js'
+export * from './functions/aton6.js'
+export * from './functions/binarySearch.js'
+export * from './functions/getSettings.js'
+export * from './functions/getSmallMemoryFile.js'
+export * from './functions/log.js'
+export * from './functions/ntoa4.js'
+export * from './functions/number37ToString.js'
+export * from './functions/parseIp.js'
diff --git a/packages/util/tsconfig.json b/packages/util/tsconfig.json
new file mode 100644
index 0000000..749bdab
--- /dev/null
+++ b/packages/util/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "types": ["./environment.d.ts", "@types/node"],
+ "outDir": "dist"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..99c3367
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,5703 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ '@antfu/eslint-config':
+ specifier: ^3.7.3
+ version: 3.7.3(@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(@unocss/eslint-plugin@0.63.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(@vue/compiler-sfc@3.5.10)(eslint-plugin-format@0.1.2(eslint@9.11.1(jiti@2.0.0)))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1))
+ '@rollup/plugin-replace':
+ specifier: ^6.0.1
+ version: 6.0.1(rollup@4.22.5)
+ '@total-typescript/tsconfig':
+ specifier: ^1.0.4
+ version: 1.0.4
+ '@types/node':
+ specifier: ^22.7.4
+ version: 22.7.4
+ '@unocss/eslint-plugin':
+ specifier: ^0.63.0
+ version: 0.63.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@vitest/coverage-v8':
+ specifier: ^2.1.1
+ version: 2.1.1(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1))
+ '@vitest/ui':
+ specifier: ^2.1.1
+ version: 2.1.1(vitest@2.1.1)
+ bumpp:
+ specifier: ^9.8.1
+ version: 9.8.1(magicast@0.3.5)
+ concurrently:
+ specifier: ^9.0.1
+ version: 9.0.1
+ eslint:
+ specifier: ^9.11.1
+ version: 9.11.1(jiti@2.0.0)
+ eslint-plugin-format:
+ specifier: ^0.1.2
+ version: 0.1.2(eslint@9.11.1(jiti@2.0.0))
+ semver:
+ specifier: ^7.6.3
+ version: 7.6.3
+ typescript:
+ specifier: ^5.6.2
+ version: 5.6.2
+ vite:
+ specifier: ^5.4.8
+ version: 5.4.8(@types/node@22.7.4)
+ vite-plugin-checker:
+ specifier: ^0.8.0
+ version: 0.8.0(eslint@9.11.1(jiti@2.0.0))(optionator@0.9.4)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4))
+ vite-plugin-dts:
+ specifier: ^4.2.2
+ version: 4.2.2(@types/node@22.7.4)(rollup@4.22.5)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4))
+ vitest:
+ specifier: ^2.1.1
+ version: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)
+
+ packages/country:
+ devDependencies:
+ '@iplookup/util':
+ specifier: workspace:*
+ version: link:../util
+
+ packages/country-extra:
+ devDependencies:
+ '@iplookup/util':
+ specifier: workspace:*
+ version: link:../util
+ countries-list:
+ specifier: ^3.1.1
+ version: 3.1.1
+
+ packages/geocode:
+ devDependencies:
+ '@iplookup/util':
+ specifier: workspace:*
+ version: link:../util
+
+ packages/geocode-extra:
+ devDependencies:
+ '@iplookup/util':
+ specifier: workspace:*
+ version: link:../util
+ countries-list:
+ specifier: ^3.1.1
+ version: 3.1.1
+
+ packages/ip-location-api:
+ dependencies:
+ '@fast-csv/parse':
+ specifier: ^5.0.0
+ version: 5.0.0
+ ip-address:
+ specifier: ^9.0.5
+ version: 9.0.5
+ ky:
+ specifier: ^1.7.2
+ version: 1.7.2
+ yauzl:
+ specifier: ^3.1.3
+ version: 3.1.3
+ devDependencies:
+ '@iplookup/util':
+ specifier: workspace:*
+ version: link:../util
+ countries-list:
+ specifier: ^3.1.1
+ version: 3.1.1
+
+ packages/util:
+ dependencies:
+ '@fast-csv/parse':
+ specifier: ^5.0.0
+ version: 5.0.0
+ ip-address:
+ specifier: ^9.0.5
+ version: 9.0.5
+ ky:
+ specifier: ^1.7.2
+ version: 1.7.2
+ yauzl:
+ specifier: ^3.1.3
+ version: 3.1.3
+ devDependencies:
+ '@types/yauzl':
+ specifier: ^2.10.3
+ version: 2.10.3
+
+packages:
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@antfu/eslint-config@3.7.3':
+ resolution: {integrity: sha512-vzhKtzQT+f/xBV8T5U8SFy3D7uAqL2CEcjsJVqtA7F8tdKvGuC/96uWeEKMHk5lRfijgj+xRvb+c4qQn60YlIA==}
+ hasBin: true
+ peerDependencies:
+ '@eslint-react/eslint-plugin': ^1.5.8
+ '@prettier/plugin-xml': ^3.4.1
+ '@unocss/eslint-plugin': '>=0.50.0'
+ astro-eslint-parser: ^1.0.2
+ eslint: ^9.10.0
+ eslint-plugin-astro: ^1.2.0
+ eslint-plugin-format: '>=0.1.0'
+ eslint-plugin-react-hooks: ^4.6.0
+ eslint-plugin-react-refresh: ^0.4.4
+ eslint-plugin-solid: ^0.14.3
+ eslint-plugin-svelte: '>=2.35.1'
+ prettier-plugin-astro: ^0.13.0
+ prettier-plugin-slidev: ^1.0.5
+ svelte-eslint-parser: '>=0.37.0'
+ peerDependenciesMeta:
+ '@eslint-react/eslint-plugin':
+ optional: true
+ '@prettier/plugin-xml':
+ optional: true
+ '@unocss/eslint-plugin':
+ optional: true
+ astro-eslint-parser:
+ optional: true
+ eslint-plugin-astro:
+ optional: true
+ eslint-plugin-format:
+ optional: true
+ eslint-plugin-react-hooks:
+ optional: true
+ eslint-plugin-react-refresh:
+ optional: true
+ eslint-plugin-solid:
+ optional: true
+ eslint-plugin-svelte:
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-slidev:
+ optional: true
+ svelte-eslint-parser:
+ optional: true
+
+ '@antfu/install-pkg@0.4.1':
+ resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==}
+
+ '@antfu/utils@0.7.10':
+ resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
+
+ '@babel/code-frame@7.24.7':
+ resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.24.8':
+ resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.24.7':
+ resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/highlight@7.24.7':
+ resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.25.6':
+ resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.25.6':
+ resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
+ engines: {node: '>=6.9.0'}
+
+ '@bcoe/v8-coverage@0.2.3':
+ resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+
+ '@clack/core@0.3.4':
+ resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==}
+
+ '@clack/prompts@0.7.0':
+ resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==}
+ bundledDependencies:
+ - is-unicode-supported
+
+ '@dprint/formatter@0.3.0':
+ resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==}
+
+ '@dprint/markdown@0.17.8':
+ resolution: {integrity: sha512-ukHFOg+RpG284aPdIg7iPrCYmMs3Dqy43S1ejybnwlJoFiW02b+6Bbr5cfZKFRYNP3dKGM86BqHEnMzBOyLvvA==}
+
+ '@dprint/toml@0.6.2':
+ resolution: {integrity: sha512-Mk5unEANsL/L+WHYU3NpDXt1ARU5bNU5k5OZELxaJodDycKG6RoRnSlZXpW6+7UN2PSnETAFVUdKrh937ZwtHA==}
+
+ '@es-joy/jsdoccomment@0.48.0':
+ resolution: {integrity: sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==}
+ engines: {node: '>=16'}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.23.1':
+ resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.23.1':
+ resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.23.1':
+ resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.23.1':
+ resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.23.1':
+ resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.23.1':
+ resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.23.1':
+ resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.23.1':
+ resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.23.1':
+ resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.23.1':
+ resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.23.1':
+ resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.23.1':
+ resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.23.1':
+ resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.23.1':
+ resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.23.1':
+ resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.23.1':
+ resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.23.1':
+ resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.23.1':
+ resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.23.1':
+ resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.23.1':
+ resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.23.1':
+ resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.23.1':
+ resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.23.1':
+ resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.23.1':
+ resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-plugin-eslint-comments@4.4.0':
+ resolution: {integrity: sha512-yljsWl5Qv3IkIRmJ38h3NrHXFCm4EUl55M8doGTF6hvzvFF8kRpextgSrg2dwHev9lzBZyafCr9RelGIyQm6fw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ '@eslint-community/eslint-utils@4.4.0':
+ resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.11.1':
+ resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/compat@1.1.1':
+ resolution: {integrity: sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-array@0.18.0':
+ resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.6.0':
+ resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.1.0':
+ resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.11.1':
+ resolution: {integrity: sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/markdown@6.1.1':
+ resolution: {integrity: sha512-Z+1js5AeqidwhNBbnIPM6Fn4eY9D5i1NleamS0UBW6BG0J4lpvhIVOKVIi22kmH5gvxDmHUp5MHkkkjda0TehA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.4':
+ resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.2.0':
+ resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@fast-csv/parse@5.0.0':
+ resolution: {integrity: sha512-ecF8tCm3jVxeRjEB6VPzmA+1wGaJ5JgaUX2uesOXdXD6qQp0B3EdshOIed4yT1Xlj/F2f8v4zHSo0Oi31L697g==}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.3.0':
+ resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
+ engines: {node: '>=18.18'}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+
+ '@jridgewell/gen-mapping@0.3.5':
+ resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@jsdevtools/ez-spawn@3.0.4':
+ resolution: {integrity: sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==}
+ engines: {node: '>=10'}
+
+ '@microsoft/api-extractor-model@7.29.6':
+ resolution: {integrity: sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw==}
+
+ '@microsoft/api-extractor@7.47.7':
+ resolution: {integrity: sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==}
+ hasBin: true
+
+ '@microsoft/tsdoc-config@0.17.0':
+ resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==}
+
+ '@microsoft/tsdoc@0.15.0':
+ resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@pkgr/core@0.1.1':
+ resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+ '@polka/url@1.0.0-next.28':
+ resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
+
+ '@rollup/plugin-replace@6.0.1':
+ resolution: {integrity: sha512-2sPh9b73dj5IxuMmDAsQWVFT7mR+yoHweBaXG2W/R8vQ+IWZlnaI7BR7J6EguVQUp1hd8Z7XuozpDjEKQAAC2Q==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/pluginutils@5.1.2':
+ resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.22.5':
+ resolution: {integrity: sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.22.5':
+ resolution: {integrity: sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.22.5':
+ resolution: {integrity: sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.22.5':
+ resolution: {integrity: sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.22.5':
+ resolution: {integrity: sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.22.5':
+ resolution: {integrity: sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.22.5':
+ resolution: {integrity: sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.22.5':
+ resolution: {integrity: sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.22.5':
+ resolution: {integrity: sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.22.5':
+ resolution: {integrity: sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.22.5':
+ resolution: {integrity: sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.22.5':
+ resolution: {integrity: sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.22.5':
+ resolution: {integrity: sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-win32-arm64-msvc@4.22.5':
+ resolution: {integrity: sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.22.5':
+ resolution: {integrity: sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.22.5':
+ resolution: {integrity: sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rushstack/node-core-library@5.7.0':
+ resolution: {integrity: sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ==}
+ peerDependencies:
+ '@types/node': '*'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+
+ '@rushstack/rig-package@0.5.3':
+ resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==}
+
+ '@rushstack/terminal@0.14.0':
+ resolution: {integrity: sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw==}
+ peerDependencies:
+ '@types/node': '*'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+
+ '@rushstack/ts-command-line@4.22.6':
+ resolution: {integrity: sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==}
+
+ '@stylistic/eslint-plugin@2.8.0':
+ resolution: {integrity: sha512-Ufvk7hP+bf+pD35R/QfunF793XlSRIC7USr3/EdgduK9j13i2JjmsM0LUz3/foS+jDYp2fzyWZA9N44CPur0Ow==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=8.40.0'
+
+ '@total-typescript/tsconfig@1.0.4':
+ resolution: {integrity: sha512-fO4ctMPGz1kOFOQ4RCPBRBfMy3gDn+pegUfrGyUFRMv/Rd0ZM3/SHH3hFCYG4u6bPLG8OlmOGcBLDexvyr3A5w==}
+
+ '@types/argparse@1.0.38':
+ resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
+
+ '@types/debug@4.1.12':
+ resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@0.7.34':
+ resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
+
+ '@types/node@22.7.4':
+ resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==}
+
+ '@types/normalize-package-data@2.4.4':
+ resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/yauzl@2.10.3':
+ resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
+
+ '@typescript-eslint/eslint-plugin@8.7.0':
+ resolution: {integrity: sha512-RIHOoznhA3CCfSTFiB6kBGLQtB/sox+pJ6jeFu6FxJvqL8qRxq/FfGO/UhsGgQM9oGdXkV4xUgli+dt26biB6A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/parser@8.7.0':
+ resolution: {integrity: sha512-lN0btVpj2unxHlNYLI//BQ7nzbMJYBVQX5+pbNXvGYazdlgYonMn4AhhHifQ+J4fGRYA/m1DjaQjx+fDetqBOQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/scope-manager@8.7.0':
+ resolution: {integrity: sha512-87rC0k3ZlDOuz82zzXRtQ7Akv3GKhHs0ti4YcbAJtaomllXoSO8hi7Ix3ccEvCd824dy9aIX+j3d2UMAfCtVpg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.7.0':
+ resolution: {integrity: sha512-tl0N0Mj3hMSkEYhLkjREp54OSb/FI6qyCzfiiclvJvOqre6hsZTGSnHtmFLDU8TIM62G7ygEa1bI08lcuRwEnQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/types@8.7.0':
+ resolution: {integrity: sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.7.0':
+ resolution: {integrity: sha512-MC8nmcGHsmfAKxwnluTQpNqceniT8SteVwd2voYlmiSWGOtjvGXdPl17dYu2797GVscK30Z04WRM28CrKS9WOg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/utils@8.7.0':
+ resolution: {integrity: sha512-ZbdUdwsl2X/s3CiyAu3gOlfQzpbuG3nTWKPoIvAu1pu5r8viiJvv2NPN2AqArL35NCYtw/lrPPfM4gxrMLNLPw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+
+ '@typescript-eslint/visitor-keys@8.7.0':
+ resolution: {integrity: sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@unocss/config@0.63.0':
+ resolution: {integrity: sha512-rlhki3GA6pNX05sjDGJO4vTDonOfDngL9CJWEbn9hELgMZQ3+OAO4mePMA6OsYfMxYz5qpfDKwofkuV4CGNDKQ==}
+ engines: {node: '>=14'}
+
+ '@unocss/core@0.63.0':
+ resolution: {integrity: sha512-ZGvmQ3CgGlGJy8TDYUCLRzgv+uyUj2nv0a5MsVvLAD1BKHN96YSt/zRdvRprKUkSzP0yLw44k3kv59md8Bn8xw==}
+
+ '@unocss/eslint-plugin@0.63.0':
+ resolution: {integrity: sha512-f42r0Bk9LAb/BTpsJWNmfobpR3hPPVtytjH4Q673t4Gi8EKwQ3TQ0sVdbNeV+RxwuIckdZ0k1ijmt5FWAJ6oDA==}
+ engines: {node: '>=14'}
+
+ '@vitest/coverage-v8@2.1.1':
+ resolution: {integrity: sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==}
+ peerDependencies:
+ '@vitest/browser': 2.1.1
+ vitest: 2.1.1
+ peerDependenciesMeta:
+ '@vitest/browser':
+ optional: true
+
+ '@vitest/eslint-plugin@1.1.4':
+ resolution: {integrity: sha512-kudjgefmJJ7xQ2WfbUU6pZbm7Ou4gLYRaao/8Ynide3G0QhVKHd978sDyWX4KOH0CCMH9cyrGAkFd55eGzJ48Q==}
+ peerDependencies:
+ '@typescript-eslint/utils': '>= 8.0'
+ eslint: '>= 8.57.0'
+ typescript: '>= 5.0.0'
+ vitest: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/utils':
+ optional: true
+ typescript:
+ optional: true
+ vitest:
+ optional: true
+
+ '@vitest/expect@2.1.1':
+ resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==}
+
+ '@vitest/mocker@2.1.1':
+ resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==}
+ peerDependencies:
+ '@vitest/spy': 2.1.1
+ msw: ^2.3.5
+ vite: ^5.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@2.1.1':
+ resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==}
+
+ '@vitest/runner@2.1.1':
+ resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==}
+
+ '@vitest/snapshot@2.1.1':
+ resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==}
+
+ '@vitest/spy@2.1.1':
+ resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==}
+
+ '@vitest/ui@2.1.1':
+ resolution: {integrity: sha512-IIxo2LkQDA+1TZdPLYPclzsXukBWd5dX2CKpGqH8CCt8Wh0ZuDn4+vuQ9qlppEju6/igDGzjWF/zyorfsf+nHg==}
+ peerDependencies:
+ vitest: 2.1.1
+
+ '@vitest/utils@2.1.1':
+ resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==}
+
+ '@volar/language-core@2.4.5':
+ resolution: {integrity: sha512-F4tA0DCO5Q1F5mScHmca0umsi2ufKULAnMOVBfMsZdT4myhVl4WdKRwCaKcfOkIEuyrAVvtq1ESBdZ+rSyLVww==}
+
+ '@volar/source-map@2.4.5':
+ resolution: {integrity: sha512-varwD7RaKE2J/Z+Zu6j3mNNJbNT394qIxXwdvz/4ao/vxOfyClZpSDtLKkwWmecinkOVos5+PWkWraelfMLfpw==}
+
+ '@volar/typescript@2.4.5':
+ resolution: {integrity: sha512-mcT1mHvLljAEtHviVcBuOyAwwMKz1ibXTi5uYtP/pf4XxoAzpdkQ+Br2IC0NPCvLCbjPZmbf3I0udndkfB1CDg==}
+
+ '@vue/compiler-core@3.5.10':
+ resolution: {integrity: sha512-iXWlk+Cg/ag7gLvY0SfVucU8Kh2CjysYZjhhP70w9qI4MvSox4frrP+vDGvtQuzIcgD8+sxM6lZvCtdxGunTAA==}
+
+ '@vue/compiler-dom@3.5.10':
+ resolution: {integrity: sha512-DyxHC6qPcktwYGKOIy3XqnHRrrXyWR2u91AjP+nLkADko380srsC2DC3s7Y1Rk6YfOlxOlvEQKa9XXmLI+W4ZA==}
+
+ '@vue/compiler-sfc@3.5.10':
+ resolution: {integrity: sha512-to8E1BgpakV7224ZCm8gz1ZRSyjNCAWEplwFMWKlzCdP9DkMKhRRwt0WkCjY7jkzi/Vz3xgbpeig5Pnbly4Tow==}
+
+ '@vue/compiler-ssr@3.5.10':
+ resolution: {integrity: sha512-hxP4Y3KImqdtyUKXDRSxKSRkSm1H9fCvhojEYrnaoWhE4w/y8vwWhnosJoPPe2AXm5sU7CSbYYAgkt2ZPhDz+A==}
+
+ '@vue/compiler-vue2@2.7.16':
+ resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+ '@vue/language-core@2.1.6':
+ resolution: {integrity: sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/shared@3.5.10':
+ resolution: {integrity: sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.12.1:
+ resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv-draft-04@1.0.0:
+ resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
+ peerDependencies:
+ ajv: ^8.5.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
+ ajv-formats@3.0.1:
+ resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ajv@8.12.0:
+ resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
+
+ ajv@8.13.0:
+ resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
+
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+
+ ansi-styles@3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ are-docs-informative@0.0.2:
+ resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
+ engines: {node: '>=14'}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.24.0:
+ resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer-crc32@0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+ builtin-modules@3.3.0:
+ resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
+ engines: {node: '>=6'}
+
+ bumpp@9.8.1:
+ resolution: {integrity: sha512-25W55DZI/rq6FboM0Q5y8eHbUk9eNn9oZ4bg/I5kiWn8/rdZCw6iqML076akQiUOQGhrm6QDvSSn4PgQ48bS4A==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ bundle-require@5.0.0:
+ resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ peerDependencies:
+ esbuild: '>=0.18'
+
+ c12@1.11.2:
+ resolution: {integrity: sha512-oBs8a4uvSDO9dm8b7OCFW7+dgtVrwmwnrVXYzLm43ta7ep2jCn/0MhoUFygIWtxhyy6+/MG7/agvpY0U1Iemew==}
+ peerDependencies:
+ magicast: ^0.3.4
+ peerDependenciesMeta:
+ magicast:
+ optional: true
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ call-me-maybe@1.0.2:
+ resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ caniuse-lite@1.0.30001664:
+ resolution: {integrity: sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ chai@5.1.1:
+ resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
+ engines: {node: '>=12'}
+
+ chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chownr@2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+
+ ci-info@4.0.0:
+ resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==}
+ engines: {node: '>=8'}
+
+ citty@0.1.6:
+ resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
+
+ clean-regexp@1.0.0:
+ resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
+ engines: {node: '>=4'}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+
+ comment-parser@1.4.1:
+ resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
+ engines: {node: '>= 12.0.0'}
+
+ compare-versions@6.1.1:
+ resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
+
+ computeds@0.0.1:
+ resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ concurrently@9.0.1:
+ resolution: {integrity: sha512-wYKvCd/f54sTXJMSfV6Ln/B8UrfLBKOYa+lzc6CHay3Qek+LorVSBdMVfyewFhRbH0Rbabsk4D+3PL/VjQ5gzg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ confbox@0.1.7:
+ resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==}
+
+ consola@3.2.3:
+ resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+
+ core-js-compat@3.38.1:
+ resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==}
+
+ countries-list@3.1.1:
+ resolution: {integrity: sha512-nPklKJ5qtmY5MdBKw1NiBAoyx5Sa7p2yPpljZyQ7gyCN1m+eMFs9I6CT37Mxt8zvR5L3VzD3DJBE4WQzX3WF4A==}
+
+ cross-spawn@7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ de-indent@1.0.2:
+ resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-named-character-reference@1.0.2:
+ resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
+
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ destr@2.0.3:
+ resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ doctrine@3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+
+ dotenv@16.4.5:
+ resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
+ engines: {node: '>=12'}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ electron-to-chromium@1.5.29:
+ resolution: {integrity: sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ enhanced-resolve@5.17.1:
+ resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
+ engines: {node: '>=10.13.0'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ error-ex@1.3.2:
+ resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+
+ es-module-lexer@1.5.4:
+ resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
+
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.23.1:
+ resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
+ eslint-compat-utils@0.5.1:
+ resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-config-flat-gitignore@0.3.0:
+ resolution: {integrity: sha512-0Ndxo4qGhcewjTzw52TK06Mc00aDtHNTdeeW2JfONgDcLkRO/n/BteMRzNVpLQYxdCC/dFEilfM9fjjpGIJ9Og==}
+ peerDependencies:
+ eslint: ^9.5.0
+
+ eslint-flat-config-utils@0.4.0:
+ resolution: {integrity: sha512-kfd5kQZC+BMO0YwTol6zxjKX1zAsk8JfSAopbKjKqmENTJcew+yBejuvccAg37cvOrN0Mh+DVbeyznuNWEjt4A==}
+
+ eslint-formatting-reporter@0.0.0:
+ resolution: {integrity: sha512-k9RdyTqxqN/wNYVaTk/ds5B5rA8lgoAmvceYN7bcZMBwU7TuXx5ntewJv81eF3pIL/CiJE+pJZm36llG8yhyyw==}
+ peerDependencies:
+ eslint: '>=8.40.0'
+
+ eslint-import-resolver-node@0.3.9:
+ resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+ eslint-merge-processors@0.1.0:
+ resolution: {integrity: sha512-IvRXXtEajLeyssvW4wJcZ2etxkR9mUf4zpNwgI+m/Uac9RfXHskuJefkHUcawVzePnd6xp24enp5jfgdHzjRdQ==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-parser-plain@0.1.0:
+ resolution: {integrity: sha512-oOeA6FWU0UJT/Rxc3XF5Cq0nbIZbylm7j8+plqq0CZoE6m4u32OXJrR+9iy4srGMmF6v6pmgvP1zPxSRIGh3sg==}
+
+ eslint-plugin-antfu@2.7.0:
+ resolution: {integrity: sha512-gZM3jq3ouqaoHmUNszb1Zo2Ux7RckSvkGksjLWz9ipBYGSv1EwwBETN6AdiUXn+RpVHXTbEMPAPlXJazcA6+iA==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-plugin-command@0.2.6:
+ resolution: {integrity: sha512-T0bHZ1oblW1xUHUVoBKZJR2osSNNGkfZuK4iqboNwuNS/M7tdp3pmURaJtTi/XDzitxaQ02lvOdFH0mUd5QLvQ==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-plugin-es-x@7.8.0:
+ resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=8'
+
+ eslint-plugin-format@0.1.2:
+ resolution: {integrity: sha512-ZrcO3aiumgJ6ENAv65IWkPjtW77ML/5mp0YrRK0jdvvaZJb+4kKWbaQTMr/XbJo6CtELRmCApAziEKh7L2NbdQ==}
+ peerDependencies:
+ eslint: ^8.40.0 || ^9.0.0
+
+ eslint-plugin-import-x@4.3.0:
+ resolution: {integrity: sha512-PxGzP7gAjF2DLeRnQtbYkkgZDg1intFyYr/XS1LgTYXUDrSXMHGkXx8++6i2eDv2jMs0jfeO6G6ykyeWxiFX7w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+
+ eslint-plugin-jsdoc@50.3.0:
+ resolution: {integrity: sha512-P7qDB/RckdKETpBM4CtjHRQ5qXByPmFhRi86sN3E+J+tySchq+RSOGGhI2hDIefmmKFuTi/1ACjqsnDJDDDfzg==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ eslint-plugin-jsonc@2.16.0:
+ resolution: {integrity: sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-plugin-n@17.10.3:
+ resolution: {integrity: sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=8.23.0'
+
+ eslint-plugin-no-only-tests@3.3.0:
+ resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
+ engines: {node: '>=5.0.0'}
+
+ eslint-plugin-perfectionist@3.7.0:
+ resolution: {integrity: sha512-pemhfcR3LDbYVWeveHok9u048yR7GpsnfyPvn6RsDkp/UV7iqBV0y5K0aGb9ZJMsemOyWok7akxGzPLsz+mHKQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ astro-eslint-parser: ^1.0.2
+ eslint: '>=8.0.0'
+ svelte: '>=3.0.0'
+ svelte-eslint-parser: ^0.41.1
+ vue-eslint-parser: '>=9.0.0'
+ peerDependenciesMeta:
+ astro-eslint-parser:
+ optional: true
+ svelte:
+ optional: true
+ svelte-eslint-parser:
+ optional: true
+ vue-eslint-parser:
+ optional: true
+
+ eslint-plugin-regexp@2.6.0:
+ resolution: {integrity: sha512-FCL851+kislsTEQEMioAlpDuK5+E5vs0hi1bF8cFlPlHcEjeRhuAzEsGikXRreE+0j4WhW2uO54MqTjXtYOi3A==}
+ engines: {node: ^18 || >=20}
+ peerDependencies:
+ eslint: '>=8.44.0'
+
+ eslint-plugin-toml@0.11.1:
+ resolution: {integrity: sha512-Y1WuMSzfZpeMIrmlP1nUh3kT8p96mThIq4NnHrYUhg10IKQgGfBZjAWnrg9fBqguiX4iFps/x/3Hb5TxBisfdw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-plugin-unicorn@55.0.0:
+ resolution: {integrity: sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==}
+ engines: {node: '>=18.18'}
+ peerDependencies:
+ eslint: '>=8.56.0'
+
+ eslint-plugin-unused-imports@4.1.4:
+ resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==}
+ peerDependencies:
+ '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
+ eslint: ^9.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@typescript-eslint/eslint-plugin':
+ optional: true
+
+ eslint-plugin-vue@9.28.0:
+ resolution: {integrity: sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ eslint-plugin-yml@1.14.0:
+ resolution: {integrity: sha512-ESUpgYPOcAYQO9czugcX5OqRvn/ydDVwGCPXY4YjPqc09rHaUVUA6IE6HLQys4rXk/S+qx3EwTd1wHCwam/OWQ==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-processor-vue-blocks@0.1.2:
+ resolution: {integrity: sha512-PfpJ4uKHnqeL/fXUnzYkOax3aIenlwewXRX8jFinA1a2yCFnLgMuiH3xvCgvHHUlV2xJWQHbCTdiJWGwb3NqpQ==}
+ peerDependencies:
+ '@vue/compiler-sfc': ^3.3.0
+ eslint: ^8.50.0 || ^9.0.0
+
+ eslint-scope@7.2.2:
+ resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-scope@8.1.0:
+ resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.1.0:
+ resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.11.1:
+ resolution: {integrity: sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.2.0:
+ resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ execa@8.0.1:
+ resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+ engines: {node: '>=16.17'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-diff@1.3.0:
+ resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+ fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.17.1:
+ resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+ fdir@6.3.0:
+ resolution: {integrity: sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fdir@6.4.2:
+ resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fflate@0.8.2:
+ resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up-simple@1.0.0:
+ resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==}
+ engines: {node: '>=18'}
+
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.1:
+ resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+
+ foreground-child@3.3.0:
+ resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ engines: {node: '>=14'}
+
+ fs-extra@11.2.0:
+ resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
+ engines: {node: '>=14.14'}
+
+ fs-extra@7.0.1:
+ resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-minipass@2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-func-name@2.0.2:
+ resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+
+ get-stream@8.0.1:
+ resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+ engines: {node: '>=16'}
+
+ get-tsconfig@4.8.1:
+ resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==}
+
+ giget@1.2.3:
+ resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==}
+ hasBin: true
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+
+ globals@13.24.0:
+ resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+ engines: {node: '>=8'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@15.9.0:
+ resolution: {integrity: sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==}
+ engines: {node: '>=18'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
+ hosted-git-info@2.8.9:
+ resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ human-signals@5.0.0:
+ resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+ engines: {node: '>=16.17.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+
+ import-lazy@4.0.0:
+ resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
+ engines: {node: '>=8'}
+
+ importx@0.5.0:
+ resolution: {integrity: sha512-qROz3rSOjQYclmEQAajH9RhBuqpAGHM+5CNd9fk+TsF4JKmQsAI1egafW8XZZv8vARCo4nAmmt5d0eI2B8GUsA==}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ ip-address@9.0.5:
+ resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
+ engines: {node: '>= 12'}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-builtin-module@3.2.1:
+ resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
+ engines: {node: '>=6'}
+
+ is-core-module@2.15.1:
+ resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-path-inside@3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+
+ is-stream@3.0.0:
+ resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@5.0.6:
+ resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.1.7:
+ resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
+ engines: {node: '>=8'}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jiti@1.21.6:
+ resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
+ hasBin: true
+
+ jiti@2.0.0:
+ resolution: {integrity: sha512-CJ7e7Abb779OTRv3lomfp7Mns/Sy1+U4pcAx5VbjxCZD5ZM/VJaXPpPjNKjtSvWQy/H86E49REXR34dl1JEz9w==}
+ hasBin: true
+
+ jju@1.4.0:
+ resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsbn@1.1.0:
+ resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
+
+ jsdoc-type-pratt-parser@4.1.0:
+ resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
+ engines: {node: '>=12.0.0'}
+
+ jsesc@0.5.0:
+ resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
+ hasBin: true
+
+ jsesc@3.0.2:
+ resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ jsonc-eslint-parser@2.4.0:
+ resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ jsonc-parser@3.3.1:
+ resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+
+ jsonfile@4.0.0:
+ resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+
+ jsonfile@6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ kleur@3.0.3:
+ resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+ engines: {node: '>=6'}
+
+ kolorist@1.8.0:
+ resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+ ky@1.7.2:
+ resolution: {integrity: sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg==}
+ engines: {node: '>=18'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ load-tsconfig@0.2.5:
+ resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ local-pkg@0.5.0:
+ resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
+ engines: {node: '>=14'}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.escaperegexp@4.1.2:
+ resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==}
+
+ lodash.groupby@4.6.0:
+ resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}
+
+ lodash.isfunction@3.0.9:
+ resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
+
+ lodash.isnil@4.0.0:
+ resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==}
+
+ lodash.isundefined@3.0.1:
+ resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.uniq@4.5.0:
+ resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ loupe@3.1.1:
+ resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@6.0.0:
+ resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+ engines: {node: '>=10'}
+
+ magic-string@0.30.11:
+ resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+
+ magicast@0.3.5:
+ resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
+ markdown-table@3.0.3:
+ resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
+
+ mdast-util-find-and-replace@3.0.1:
+ resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
+
+ mdast-util-from-markdown@2.0.1:
+ resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.0.0:
+ resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.0.0:
+ resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-markdown@2.1.0:
+ resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromark-core-commonmark@2.0.1:
+ resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.0:
+ resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-factory-destination@2.0.0:
+ resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
+
+ micromark-factory-label@2.0.0:
+ resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
+
+ micromark-factory-space@2.0.0:
+ resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
+
+ micromark-factory-title@2.0.0:
+ resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==}
+
+ micromark-factory-whitespace@2.0.0:
+ resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==}
+
+ micromark-util-character@2.1.0:
+ resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
+
+ micromark-util-chunked@2.0.0:
+ resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
+
+ micromark-util-classify-character@2.0.0:
+ resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==}
+
+ micromark-util-combine-extensions@2.0.0:
+ resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==}
+
+ micromark-util-decode-numeric-character-reference@2.0.1:
+ resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==}
+
+ micromark-util-decode-string@2.0.0:
+ resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==}
+
+ micromark-util-encode@2.0.0:
+ resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+
+ micromark-util-html-tag-name@2.0.0:
+ resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
+
+ micromark-util-normalize-identifier@2.0.0:
+ resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==}
+
+ micromark-util-resolve-all@2.0.0:
+ resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==}
+
+ micromark-util-sanitize-uri@2.0.0:
+ resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+
+ micromark-util-subtokenize@2.0.1:
+ resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==}
+
+ micromark-util-symbol@2.0.0:
+ resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+
+ micromark-util-types@2.0.0:
+ resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+
+ micromark@4.0.0:
+ resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mimic-fn@4.0.0:
+ resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+ engines: {node: '>=12'}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ minimatch@3.0.8:
+ resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@3.3.6:
+ resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+ engines: {node: '>=8'}
+
+ minipass@5.0.0:
+ resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+ engines: {node: '>=8'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ mlly@1.7.1:
+ resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
+
+ mrmime@2.0.0:
+ resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare-lite@1.4.0:
+ resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ node-fetch-native@1.6.4:
+ resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==}
+
+ node-releases@2.0.18:
+ resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
+
+ normalize-package-data@2.5.0:
+ resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
+ npm-run-path@5.3.0:
+ resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ nypm@0.3.12:
+ resolution: {integrity: sha512-D3pzNDWIvgA+7IORhD/IuWzEk4uXv6GsgOxiid4UU3h9oq5IqV1KtPDi63n4sZJ/xcWlr88c0QM2RgN5VbOhFA==}
+ engines: {node: ^14.16.0 || >=16.10.0}
+ hasBin: true
+
+ ohash@1.1.4:
+ resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==}
+
+ onetime@6.0.0:
+ resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+ engines: {node: '>=12'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ package-manager-detector@0.2.0:
+ resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-gitignore@2.0.0:
+ resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==}
+ engines: {node: '>=14'}
+
+ parse-imports@2.2.1:
+ resolution: {integrity: sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==}
+ engines: {node: '>= 18'}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ pathe@1.1.2:
+ resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
+ pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
+
+ pend@1.2.0:
+ resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
+
+ perfect-debounce@1.0.0:
+ resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+ picocolors@1.1.0:
+ resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
+ pkg-types@1.2.0:
+ resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==}
+
+ pluralize@8.0.0:
+ resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
+ engines: {node: '>=4'}
+
+ postcss-selector-parser@6.1.2:
+ resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ engines: {node: '>=4'}
+
+ postcss@8.4.47:
+ resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier-linter-helpers@1.0.0:
+ resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+ engines: {node: '>=6.0.0'}
+
+ prettier@3.3.3:
+ resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ prompts@2.4.2:
+ resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+ engines: {node: '>= 6'}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ rc9@2.1.2:
+ resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
+
+ read-pkg-up@7.0.1:
+ resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
+ engines: {node: '>=8'}
+
+ read-pkg@5.2.0:
+ resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
+ engines: {node: '>=8'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ refa@0.12.1:
+ resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ regexp-ast-analysis@0.7.1:
+ resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ regexp-tree@0.1.27:
+ resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
+ hasBin: true
+
+ regjsparser@0.10.0:
+ resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==}
+ hasBin: true
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve@1.22.8:
+ resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
+ hasBin: true
+
+ reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rollup@4.22.5:
+ resolution: {integrity: sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ rxjs@7.8.1:
+ resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
+
+ scslre@0.3.0:
+ resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
+ engines: {node: ^14.0.0 || >=16.0.0}
+
+ semver@5.7.2:
+ resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+ hasBin: true
+
+ semver@7.5.4:
+ resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ semver@7.6.3:
+ resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shell-quote@1.8.1:
+ resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ sirv@2.0.4:
+ resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
+ engines: {node: '>= 10'}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ slashes@3.0.12:
+ resolution: {integrity: sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ spdx-correct@3.2.0:
+ resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
+
+ spdx-exceptions@2.5.0:
+ resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
+
+ spdx-expression-parse@3.0.1:
+ resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+
+ spdx-expression-parse@4.0.0:
+ resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==}
+
+ spdx-license-ids@3.0.20:
+ resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ sprintf-js@1.1.3:
+ resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
+
+ stable-hash@0.0.4:
+ resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.7.0:
+ resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+
+ strip-final-newline@3.0.0:
+ resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+ engines: {node: '>=12'}
+
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ synckit@0.6.2:
+ resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==}
+ engines: {node: '>=12.20'}
+
+ synckit@0.9.1:
+ resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ tar@6.2.1:
+ resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+ engines: {node: '>=10'}
+
+ test-exclude@7.0.1:
+ resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
+ engines: {node: '>=18'}
+
+ text-table@0.2.0:
+ resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+
+ tiny-invariant@1.3.3:
+ resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.0:
+ resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==}
+
+ tinyglobby@0.2.10:
+ resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
+ engines: {node: '>=12.0.0'}
+
+ tinyglobby@0.2.6:
+ resolution: {integrity: sha512-NbBoFBpqfcgd1tCiO8Lkfdk+xrA7mlLR9zgvZcZWQQwU63XAfUePyd6wZBaU93Hqw347lHnwFzttAkemHzzz4g==}
+ engines: {node: '>=12.0.0'}
+
+ tinypool@1.0.1:
+ resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@1.2.0:
+ resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@3.0.2:
+ resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
+ engines: {node: '>=14.0.0'}
+
+ to-fast-properties@2.0.0:
+ resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+ engines: {node: '>=4'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toml-eslint-parser@0.10.0:
+ resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ tree-kill@1.2.2:
+ resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
+ hasBin: true
+
+ ts-api-utils@1.3.0:
+ resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
+ engines: {node: '>=16'}
+ peerDependencies:
+ typescript: '>=4.2.0'
+
+ tslib@2.7.0:
+ resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+
+ tsx@4.19.1:
+ resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ type-detect@4.1.0:
+ resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
+ engines: {node: '>=4'}
+
+ type-fest@0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ type-fest@0.6.0:
+ resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
+ engines: {node: '>=8'}
+
+ type-fest@0.8.1:
+ resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
+ engines: {node: '>=8'}
+
+ typescript@5.4.2:
+ resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ typescript@5.6.2:
+ resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ ufo@1.5.4:
+ resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
+
+ unconfig@0.6.0:
+ resolution: {integrity: sha512-4C67J0nIF2QwSXty2kW3zZx1pMZ3iXabylvJWWgHybWVUcMf9pxwsngoQt0gC+AVstRywFqrRBp3qOXJayhpOw==}
+
+ undici-types@6.19.8:
+ resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
+ unist-util-is@6.0.0:
+ resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.1:
+ resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+
+ unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+ universalify@0.1.2:
+ resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+ engines: {node: '>= 4.0.0'}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ update-browserslist-db@1.1.1:
+ resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ validate-npm-package-license@3.0.4:
+ resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+
+ vite-node@2.1.1:
+ resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+
+ vite-plugin-checker@0.8.0:
+ resolution: {integrity: sha512-UA5uzOGm97UvZRTdZHiQVYFnd86AVn8EVaD4L3PoVzxH+IZSfaAw14WGFwX9QS23UW3lV/5bVKZn6l0w+q9P0g==}
+ engines: {node: '>=14.16'}
+ peerDependencies:
+ '@biomejs/biome': '>=1.7'
+ eslint: '>=7'
+ meow: ^9.0.0
+ optionator: ^0.9.1
+ stylelint: '>=13'
+ typescript: '*'
+ vite: '>=2.0.0'
+ vls: '*'
+ vti: '*'
+ vue-tsc: ~2.1.6
+ peerDependenciesMeta:
+ '@biomejs/biome':
+ optional: true
+ eslint:
+ optional: true
+ meow:
+ optional: true
+ optionator:
+ optional: true
+ stylelint:
+ optional: true
+ typescript:
+ optional: true
+ vls:
+ optional: true
+ vti:
+ optional: true
+ vue-tsc:
+ optional: true
+
+ vite-plugin-dts@4.2.2:
+ resolution: {integrity: sha512-USwTMReZFf8yXV+cKkm4WOMqmFjbReAvkyxON5xzdnZzJEBnFgax6BBDZIGGr9WMJYvhHdpaIHLrOjXDcla4OA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ typescript: '*'
+ vite: '*'
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
+ vite@5.4.8:
+ resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ vitest@2.1.1:
+ resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/node': ^18.0.0 || >=20.0.0
+ '@vitest/browser': 2.1.1
+ '@vitest/ui': 2.1.1
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ vscode-jsonrpc@6.0.0:
+ resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==}
+ engines: {node: '>=8.0.0 || >=10.0.0'}
+
+ vscode-languageclient@7.0.0:
+ resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==}
+ engines: {vscode: ^1.52.0}
+
+ vscode-languageserver-protocol@3.16.0:
+ resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==}
+
+ vscode-languageserver-textdocument@1.0.12:
+ resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==}
+
+ vscode-languageserver-types@3.16.0:
+ resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==}
+
+ vscode-languageserver@7.0.0:
+ resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==}
+ hasBin: true
+
+ vscode-uri@3.0.8:
+ resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
+
+ vue-eslint-parser@9.4.3:
+ resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ xml-name-validator@4.0.0:
+ resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+ engines: {node: '>=12'}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+ yaml-eslint-parser@1.2.3:
+ resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+
+ yaml@2.5.1:
+ resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yauzl@3.1.3:
+ resolution: {integrity: sha512-JCCdmlJJWv7L0q/KylOekyRaUrdEoUxWkWVcgorosTROCFWiS9p2NNPE9Yb91ak7b1N5SxAZEliWpspbZccivw==}
+ engines: {node: '>=12'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@antfu/eslint-config@3.7.3(@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(@unocss/eslint-plugin@0.63.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(@vue/compiler-sfc@3.5.10)(eslint-plugin-format@0.1.2(eslint@9.11.1(jiti@2.0.0)))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1))':
+ dependencies:
+ '@antfu/install-pkg': 0.4.1
+ '@clack/prompts': 0.7.0
+ '@eslint-community/eslint-plugin-eslint-comments': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ '@eslint/markdown': 6.1.1
+ '@stylistic/eslint-plugin': 2.8.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1))
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-config-flat-gitignore: 0.3.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-flat-config-utils: 0.4.0
+ eslint-merge-processors: 0.1.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-antfu: 2.7.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-command: 0.2.6(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-import-x: 4.3.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ eslint-plugin-jsdoc: 50.3.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-jsonc: 2.16.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-n: 17.10.3(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-no-only-tests: 3.3.0
+ eslint-plugin-perfectionist: 3.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)(vue-eslint-parser@9.4.3(eslint@9.11.1(jiti@2.0.0)))
+ eslint-plugin-regexp: 2.6.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-toml: 0.11.1(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-unicorn: 55.0.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-vue: 9.28.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-plugin-yml: 1.14.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.5.10)(eslint@9.11.1(jiti@2.0.0))
+ globals: 15.9.0
+ jsonc-eslint-parser: 2.4.0
+ local-pkg: 0.5.0
+ parse-gitignore: 2.0.0
+ picocolors: 1.1.0
+ toml-eslint-parser: 0.10.0
+ vue-eslint-parser: 9.4.3(eslint@9.11.1(jiti@2.0.0))
+ yaml-eslint-parser: 1.2.3
+ yargs: 17.7.2
+ optionalDependencies:
+ '@unocss/eslint-plugin': 0.63.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ eslint-plugin-format: 0.1.2(eslint@9.11.1(jiti@2.0.0))
+ transitivePeerDependencies:
+ - '@typescript-eslint/utils'
+ - '@vue/compiler-sfc'
+ - supports-color
+ - svelte
+ - typescript
+ - vitest
+
+ '@antfu/install-pkg@0.4.1':
+ dependencies:
+ package-manager-detector: 0.2.0
+ tinyexec: 0.3.0
+
+ '@antfu/utils@0.7.10': {}
+
+ '@babel/code-frame@7.24.7':
+ dependencies:
+ '@babel/highlight': 7.24.7
+ picocolors: 1.1.0
+
+ '@babel/helper-string-parser@7.24.8': {}
+
+ '@babel/helper-validator-identifier@7.24.7': {}
+
+ '@babel/highlight@7.24.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.24.7
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ picocolors: 1.1.0
+
+ '@babel/parser@7.25.6':
+ dependencies:
+ '@babel/types': 7.25.6
+
+ '@babel/types@7.25.6':
+ dependencies:
+ '@babel/helper-string-parser': 7.24.8
+ '@babel/helper-validator-identifier': 7.24.7
+ to-fast-properties: 2.0.0
+
+ '@bcoe/v8-coverage@0.2.3': {}
+
+ '@clack/core@0.3.4':
+ dependencies:
+ picocolors: 1.1.0
+ sisteransi: 1.0.5
+
+ '@clack/prompts@0.7.0':
+ dependencies:
+ '@clack/core': 0.3.4
+ picocolors: 1.1.0
+ sisteransi: 1.0.5
+
+ '@dprint/formatter@0.3.0': {}
+
+ '@dprint/markdown@0.17.8': {}
+
+ '@dprint/toml@0.6.2': {}
+
+ '@es-joy/jsdoccomment@0.48.0':
+ dependencies:
+ comment-parser: 1.4.1
+ esquery: 1.6.0
+ jsdoc-type-pratt-parser: 4.1.0
+
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.23.1':
+ optional: true
+
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm@0.23.1':
+ optional: true
+
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
+ '@esbuild/android-x64@0.23.1':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.23.1':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.23.1':
+ optional: true
+
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.23.1':
+ optional: true
+
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.23.1':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.23.1':
+ optional: true
+
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.23.1':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.23.1':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.23.1':
+ optional: true
+
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.23.1':
+ optional: true
+
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.23.1':
+ optional: true
+
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.23.1':
+ optional: true
+
+ '@eslint-community/eslint-plugin-eslint-comments@4.4.0(eslint@9.11.1(jiti@2.0.0))':
+ dependencies:
+ escape-string-regexp: 4.0.0
+ eslint: 9.11.1(jiti@2.0.0)
+ ignore: 5.3.2
+
+ '@eslint-community/eslint-utils@4.4.0(eslint@9.11.1(jiti@2.0.0))':
+ dependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.11.1': {}
+
+ '@eslint/compat@1.1.1': {}
+
+ '@eslint/config-array@0.18.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.4
+ debug: 4.3.7
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/core@0.6.0': {}
+
+ '@eslint/eslintrc@3.1.0':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.3.7
+ espree: 10.2.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.11.1': {}
+
+ '@eslint/markdown@6.1.1':
+ dependencies:
+ '@eslint/plugin-kit': 0.2.0
+ mdast-util-from-markdown: 2.0.1
+ mdast-util-gfm: 3.0.0
+ micromark-extension-gfm: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/object-schema@2.1.4': {}
+
+ '@eslint/plugin-kit@0.2.0':
+ dependencies:
+ levn: 0.4.1
+
+ '@fast-csv/parse@5.0.0':
+ dependencies:
+ lodash.escaperegexp: 4.1.2
+ lodash.groupby: 4.6.0
+ lodash.isfunction: 3.0.9
+ lodash.isnil: 4.0.0
+ lodash.isundefined: 3.0.1
+ lodash.uniq: 4.5.0
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.3.0': {}
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@istanbuljs/schema@0.1.3': {}
+
+ '@jridgewell/gen-mapping@0.3.5':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@jsdevtools/ez-spawn@3.0.4':
+ dependencies:
+ call-me-maybe: 1.0.2
+ cross-spawn: 7.0.3
+ string-argv: 0.3.2
+ type-detect: 4.1.0
+
+ '@microsoft/api-extractor-model@7.29.6(@types/node@22.7.4)':
+ dependencies:
+ '@microsoft/tsdoc': 0.15.0
+ '@microsoft/tsdoc-config': 0.17.0
+ '@rushstack/node-core-library': 5.7.0(@types/node@22.7.4)
+ transitivePeerDependencies:
+ - '@types/node'
+
+ '@microsoft/api-extractor@7.47.7(@types/node@22.7.4)':
+ dependencies:
+ '@microsoft/api-extractor-model': 7.29.6(@types/node@22.7.4)
+ '@microsoft/tsdoc': 0.15.0
+ '@microsoft/tsdoc-config': 0.17.0
+ '@rushstack/node-core-library': 5.7.0(@types/node@22.7.4)
+ '@rushstack/rig-package': 0.5.3
+ '@rushstack/terminal': 0.14.0(@types/node@22.7.4)
+ '@rushstack/ts-command-line': 4.22.6(@types/node@22.7.4)
+ lodash: 4.17.21
+ minimatch: 3.0.8
+ resolve: 1.22.8
+ semver: 7.5.4
+ source-map: 0.6.1
+ typescript: 5.4.2
+ transitivePeerDependencies:
+ - '@types/node'
+
+ '@microsoft/tsdoc-config@0.17.0':
+ dependencies:
+ '@microsoft/tsdoc': 0.15.0
+ ajv: 8.12.0
+ jju: 1.4.0
+ resolve: 1.22.8
+
+ '@microsoft/tsdoc@0.15.0': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@pkgr/core@0.1.1': {}
+
+ '@polka/url@1.0.0-next.28': {}
+
+ '@rollup/plugin-replace@6.0.1(rollup@4.22.5)':
+ dependencies:
+ '@rollup/pluginutils': 5.1.2(rollup@4.22.5)
+ magic-string: 0.30.11
+ optionalDependencies:
+ rollup: 4.22.5
+
+ '@rollup/pluginutils@5.1.2(rollup@4.22.5)':
+ dependencies:
+ '@types/estree': 1.0.6
+ estree-walker: 2.0.2
+ picomatch: 2.3.1
+ optionalDependencies:
+ rollup: 4.22.5
+
+ '@rollup/rollup-android-arm-eabi@4.22.5':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.22.5':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.22.5':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.22.5':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.22.5':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.22.5':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.22.5':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.22.5':
+ optional: true
+
+ '@rushstack/node-core-library@5.7.0(@types/node@22.7.4)':
+ dependencies:
+ ajv: 8.13.0
+ ajv-draft-04: 1.0.0(ajv@8.13.0)
+ ajv-formats: 3.0.1(ajv@8.13.0)
+ fs-extra: 7.0.1
+ import-lazy: 4.0.0
+ jju: 1.4.0
+ resolve: 1.22.8
+ semver: 7.5.4
+ optionalDependencies:
+ '@types/node': 22.7.4
+
+ '@rushstack/rig-package@0.5.3':
+ dependencies:
+ resolve: 1.22.8
+ strip-json-comments: 3.1.1
+
+ '@rushstack/terminal@0.14.0(@types/node@22.7.4)':
+ dependencies:
+ '@rushstack/node-core-library': 5.7.0(@types/node@22.7.4)
+ supports-color: 8.1.1
+ optionalDependencies:
+ '@types/node': 22.7.4
+
+ '@rushstack/ts-command-line@4.22.6(@types/node@22.7.4)':
+ dependencies:
+ '@rushstack/terminal': 0.14.0(@types/node@22.7.4)
+ '@types/argparse': 1.0.38
+ argparse: 1.0.10
+ string-argv: 0.3.2
+ transitivePeerDependencies:
+ - '@types/node'
+
+ '@stylistic/eslint-plugin@2.8.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-visitor-keys: 4.1.0
+ espree: 10.2.0
+ estraverse: 5.3.0
+ picomatch: 4.0.2
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@total-typescript/tsconfig@1.0.4': {}
+
+ '@types/argparse@1.0.38': {}
+
+ '@types/debug@4.1.12':
+ dependencies:
+ '@types/ms': 0.7.34
+
+ '@types/estree@1.0.6': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@0.7.34': {}
+
+ '@types/node@22.7.4':
+ dependencies:
+ undici-types: 6.19.8
+
+ '@types/normalize-package-data@2.4.4': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/yauzl@2.10.3':
+ dependencies:
+ '@types/node': 22.7.4
+
+ '@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.11.1
+ '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@typescript-eslint/scope-manager': 8.7.0
+ '@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@typescript-eslint/visitor-keys': 8.7.0
+ eslint: 9.11.1(jiti@2.0.0)
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ natural-compare: 1.4.0
+ ts-api-utils: 1.3.0(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.7.0
+ '@typescript-eslint/types': 8.7.0
+ '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
+ '@typescript-eslint/visitor-keys': 8.7.0
+ debug: 4.3.7
+ eslint: 9.11.1(jiti@2.0.0)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.7.0':
+ dependencies:
+ '@typescript-eslint/types': 8.7.0
+ '@typescript-eslint/visitor-keys': 8.7.0
+
+ '@typescript-eslint/type-utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ debug: 4.3.7
+ ts-api-utils: 1.3.0(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+
+ '@typescript-eslint/types@8.7.0': {}
+
+ '@typescript-eslint/typescript-estree@8.7.0(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.7.0
+ '@typescript-eslint/visitor-keys': 8.7.0
+ debug: 4.3.7
+ fast-glob: 3.3.2
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.3
+ ts-api-utils: 1.3.0(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ '@typescript-eslint/scope-manager': 8.7.0
+ '@typescript-eslint/types': 8.7.0
+ '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
+ eslint: 9.11.1(jiti@2.0.0)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@typescript-eslint/visitor-keys@8.7.0':
+ dependencies:
+ '@typescript-eslint/types': 8.7.0
+ eslint-visitor-keys: 3.4.3
+
+ '@unocss/config@0.63.0':
+ dependencies:
+ '@unocss/core': 0.63.0
+ unconfig: 0.6.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@unocss/core@0.63.0': {}
+
+ '@unocss/eslint-plugin@0.63.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ '@unocss/config': 0.63.0
+ '@unocss/core': 0.63.0
+ magic-string: 0.30.11
+ synckit: 0.9.1
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+ - typescript
+
+ '@vitest/coverage-v8@2.1.1(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1))':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@bcoe/v8-coverage': 0.2.3
+ debug: 4.3.7
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.1.7
+ magic-string: 0.30.11
+ magicast: 0.3.5
+ std-env: 3.7.0
+ test-exclude: 7.0.1
+ tinyrainbow: 1.2.0
+ vitest: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1))':
+ dependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+ optionalDependencies:
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ typescript: 5.6.2
+ vitest: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)
+
+ '@vitest/expect@2.1.1':
+ dependencies:
+ '@vitest/spy': 2.1.1
+ '@vitest/utils': 2.1.1
+ chai: 5.1.1
+ tinyrainbow: 1.2.0
+
+ '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.8(@types/node@22.7.4))':
+ dependencies:
+ '@vitest/spy': 2.1.1
+ estree-walker: 3.0.3
+ magic-string: 0.30.11
+ optionalDependencies:
+ vite: 5.4.8(@types/node@22.7.4)
+
+ '@vitest/pretty-format@2.1.1':
+ dependencies:
+ tinyrainbow: 1.2.0
+
+ '@vitest/runner@2.1.1':
+ dependencies:
+ '@vitest/utils': 2.1.1
+ pathe: 1.1.2
+
+ '@vitest/snapshot@2.1.1':
+ dependencies:
+ '@vitest/pretty-format': 2.1.1
+ magic-string: 0.30.11
+ pathe: 1.1.2
+
+ '@vitest/spy@2.1.1':
+ dependencies:
+ tinyspy: 3.0.2
+
+ '@vitest/ui@2.1.1(vitest@2.1.1)':
+ dependencies:
+ '@vitest/utils': 2.1.1
+ fflate: 0.8.2
+ flatted: 3.3.1
+ pathe: 1.1.2
+ sirv: 2.0.4
+ tinyglobby: 0.2.6
+ tinyrainbow: 1.2.0
+ vitest: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)
+
+ '@vitest/utils@2.1.1':
+ dependencies:
+ '@vitest/pretty-format': 2.1.1
+ loupe: 3.1.1
+ tinyrainbow: 1.2.0
+
+ '@volar/language-core@2.4.5':
+ dependencies:
+ '@volar/source-map': 2.4.5
+
+ '@volar/source-map@2.4.5': {}
+
+ '@volar/typescript@2.4.5':
+ dependencies:
+ '@volar/language-core': 2.4.5
+ path-browserify: 1.0.1
+ vscode-uri: 3.0.8
+
+ '@vue/compiler-core@3.5.10':
+ dependencies:
+ '@babel/parser': 7.25.6
+ '@vue/shared': 3.5.10
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.10':
+ dependencies:
+ '@vue/compiler-core': 3.5.10
+ '@vue/shared': 3.5.10
+
+ '@vue/compiler-sfc@3.5.10':
+ dependencies:
+ '@babel/parser': 7.25.6
+ '@vue/compiler-core': 3.5.10
+ '@vue/compiler-dom': 3.5.10
+ '@vue/compiler-ssr': 3.5.10
+ '@vue/shared': 3.5.10
+ estree-walker: 2.0.2
+ magic-string: 0.30.11
+ postcss: 8.4.47
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.10':
+ dependencies:
+ '@vue/compiler-dom': 3.5.10
+ '@vue/shared': 3.5.10
+
+ '@vue/compiler-vue2@2.7.16':
+ dependencies:
+ de-indent: 1.0.2
+ he: 1.2.0
+
+ '@vue/language-core@2.1.6(typescript@5.6.2)':
+ dependencies:
+ '@volar/language-core': 2.4.5
+ '@vue/compiler-dom': 3.5.10
+ '@vue/compiler-vue2': 2.7.16
+ '@vue/shared': 3.5.10
+ computeds: 0.0.1
+ minimatch: 9.0.5
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ optionalDependencies:
+ typescript: 5.6.2
+
+ '@vue/shared@3.5.10': {}
+
+ acorn-jsx@5.3.2(acorn@8.12.1):
+ dependencies:
+ acorn: 8.12.1
+
+ acorn@8.12.1: {}
+
+ ajv-draft-04@1.0.0(ajv@8.13.0):
+ optionalDependencies:
+ ajv: 8.13.0
+
+ ajv-formats@3.0.1(ajv@8.13.0):
+ optionalDependencies:
+ ajv: 8.13.0
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ajv@8.12.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+ uri-js: 4.4.1
+
+ ajv@8.13.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+ uri-js: 4.4.1
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.1.0: {}
+
+ ansi-styles@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.1: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ are-docs-informative@0.0.2: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ assertion-error@2.0.1: {}
+
+ balanced-match@1.0.2: {}
+
+ binary-extensions@2.3.0: {}
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.11:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.24.0:
+ dependencies:
+ caniuse-lite: 1.0.30001664
+ electron-to-chromium: 1.5.29
+ node-releases: 2.0.18
+ update-browserslist-db: 1.1.1(browserslist@4.24.0)
+
+ buffer-crc32@0.2.13: {}
+
+ builtin-modules@3.3.0: {}
+
+ bumpp@9.8.1(magicast@0.3.5):
+ dependencies:
+ '@jsdevtools/ez-spawn': 3.0.4
+ c12: 1.11.2(magicast@0.3.5)
+ cac: 6.7.14
+ escalade: 3.2.0
+ js-yaml: 4.1.0
+ jsonc-parser: 3.3.1
+ prompts: 2.4.2
+ semver: 7.6.3
+ tinyglobby: 0.2.10
+ transitivePeerDependencies:
+ - magicast
+
+ bundle-require@5.0.0(esbuild@0.23.1):
+ dependencies:
+ esbuild: 0.23.1
+ load-tsconfig: 0.2.5
+
+ c12@1.11.2(magicast@0.3.5):
+ dependencies:
+ chokidar: 3.6.0
+ confbox: 0.1.7
+ defu: 6.1.4
+ dotenv: 16.4.5
+ giget: 1.2.3
+ jiti: 1.21.6
+ mlly: 1.7.1
+ ohash: 1.1.4
+ pathe: 1.1.2
+ perfect-debounce: 1.0.0
+ pkg-types: 1.2.0
+ rc9: 2.1.2
+ optionalDependencies:
+ magicast: 0.3.5
+
+ cac@6.7.14: {}
+
+ call-me-maybe@1.0.2: {}
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001664: {}
+
+ ccount@2.0.1: {}
+
+ chai@5.1.1:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.1
+ pathval: 2.0.0
+
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ character-entities@2.0.2: {}
+
+ check-error@2.1.1: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chownr@2.0.0: {}
+
+ ci-info@4.0.0: {}
+
+ citty@0.1.6:
+ dependencies:
+ consola: 3.2.3
+
+ clean-regexp@1.0.0:
+ dependencies:
+ escape-string-regexp: 1.0.5
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ commander@8.3.0: {}
+
+ comment-parser@1.4.1: {}
+
+ compare-versions@6.1.1: {}
+
+ computeds@0.0.1: {}
+
+ concat-map@0.0.1: {}
+
+ concurrently@9.0.1:
+ dependencies:
+ chalk: 4.1.2
+ lodash: 4.17.21
+ rxjs: 7.8.1
+ shell-quote: 1.8.1
+ supports-color: 8.1.1
+ tree-kill: 1.2.2
+ yargs: 17.7.2
+
+ confbox@0.1.7: {}
+
+ consola@3.2.3: {}
+
+ core-js-compat@3.38.1:
+ dependencies:
+ browserslist: 4.24.0
+
+ countries-list@3.1.1: {}
+
+ cross-spawn@7.0.3:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cssesc@3.0.0: {}
+
+ de-indent@1.0.2: {}
+
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.0.2:
+ dependencies:
+ character-entities: 2.0.2
+
+ deep-eql@5.0.2: {}
+
+ deep-is@0.1.4: {}
+
+ defu@6.1.4: {}
+
+ dequal@2.0.3: {}
+
+ destr@2.0.3: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ doctrine@3.0.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dotenv@16.4.5: {}
+
+ eastasianwidth@0.2.0: {}
+
+ electron-to-chromium@1.5.29: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ enhanced-resolve@5.17.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
+ entities@4.5.0: {}
+
+ error-ex@1.3.2:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ es-module-lexer@1.5.4: {}
+
+ esbuild@0.21.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+
+ esbuild@0.23.1:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.23.1
+ '@esbuild/android-arm': 0.23.1
+ '@esbuild/android-arm64': 0.23.1
+ '@esbuild/android-x64': 0.23.1
+ '@esbuild/darwin-arm64': 0.23.1
+ '@esbuild/darwin-x64': 0.23.1
+ '@esbuild/freebsd-arm64': 0.23.1
+ '@esbuild/freebsd-x64': 0.23.1
+ '@esbuild/linux-arm': 0.23.1
+ '@esbuild/linux-arm64': 0.23.1
+ '@esbuild/linux-ia32': 0.23.1
+ '@esbuild/linux-loong64': 0.23.1
+ '@esbuild/linux-mips64el': 0.23.1
+ '@esbuild/linux-ppc64': 0.23.1
+ '@esbuild/linux-riscv64': 0.23.1
+ '@esbuild/linux-s390x': 0.23.1
+ '@esbuild/linux-x64': 0.23.1
+ '@esbuild/netbsd-x64': 0.23.1
+ '@esbuild/openbsd-arm64': 0.23.1
+ '@esbuild/openbsd-x64': 0.23.1
+ '@esbuild/sunos-x64': 0.23.1
+ '@esbuild/win32-arm64': 0.23.1
+ '@esbuild/win32-ia32': 0.23.1
+ '@esbuild/win32-x64': 0.23.1
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ eslint-compat-utils@0.5.1(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+ semver: 7.6.3
+
+ eslint-config-flat-gitignore@0.3.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@eslint/compat': 1.1.1
+ eslint: 9.11.1(jiti@2.0.0)
+ find-up-simple: 1.0.0
+
+ eslint-flat-config-utils@0.4.0:
+ dependencies:
+ pathe: 1.1.2
+
+ eslint-formatting-reporter@0.0.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+ prettier-linter-helpers: 1.0.0
+
+ eslint-import-resolver-node@0.3.9:
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.15.1
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-merge-processors@0.1.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+
+ eslint-parser-plain@0.1.0: {}
+
+ eslint-plugin-antfu@2.7.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@antfu/utils': 0.7.10
+ eslint: 9.11.1(jiti@2.0.0)
+
+ eslint-plugin-command@0.2.6(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@es-joy/jsdoccomment': 0.48.0
+ eslint: 9.11.1(jiti@2.0.0)
+
+ eslint-plugin-es-x@7.8.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ '@eslint-community/regexpp': 4.11.1
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-compat-utils: 0.5.1(eslint@9.11.1(jiti@2.0.0))
+
+ eslint-plugin-format@0.1.2(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@dprint/formatter': 0.3.0
+ '@dprint/markdown': 0.17.8
+ '@dprint/toml': 0.6.2
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-formatting-reporter: 0.0.0(eslint@9.11.1(jiti@2.0.0))
+ eslint-parser-plain: 0.1.0
+ prettier: 3.3.3
+ synckit: 0.9.1
+
+ eslint-plugin-import-x@4.3.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
+ dependencies:
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ debug: 4.3.7
+ doctrine: 3.0.0
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-import-resolver-node: 0.3.9
+ get-tsconfig: 4.8.1
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.3
+ stable-hash: 0.0.4
+ tslib: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ eslint-plugin-jsdoc@50.3.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@es-joy/jsdoccomment': 0.48.0
+ are-docs-informative: 0.0.2
+ comment-parser: 1.4.1
+ debug: 4.3.7
+ escape-string-regexp: 4.0.0
+ eslint: 9.11.1(jiti@2.0.0)
+ espree: 10.2.0
+ esquery: 1.6.0
+ parse-imports: 2.2.1
+ semver: 7.6.3
+ spdx-expression-parse: 4.0.0
+ synckit: 0.9.1
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-jsonc@2.16.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-compat-utils: 0.5.1(eslint@9.11.1(jiti@2.0.0))
+ espree: 9.6.1
+ graphemer: 1.4.0
+ jsonc-eslint-parser: 2.4.0
+ natural-compare: 1.4.0
+ synckit: 0.6.2
+
+ eslint-plugin-n@17.10.3(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ enhanced-resolve: 5.17.1
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-plugin-es-x: 7.8.0(eslint@9.11.1(jiti@2.0.0))
+ get-tsconfig: 4.8.1
+ globals: 15.9.0
+ ignore: 5.3.2
+ minimatch: 9.0.5
+ semver: 7.6.3
+
+ eslint-plugin-no-only-tests@3.3.0: {}
+
+ eslint-plugin-perfectionist@3.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)(vue-eslint-parser@9.4.3(eslint@9.11.1(jiti@2.0.0))):
+ dependencies:
+ '@typescript-eslint/types': 8.7.0
+ '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+ eslint: 9.11.1(jiti@2.0.0)
+ minimatch: 9.0.5
+ natural-compare-lite: 1.4.0
+ optionalDependencies:
+ vue-eslint-parser: 9.4.3(eslint@9.11.1(jiti@2.0.0))
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ eslint-plugin-regexp@2.6.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ '@eslint-community/regexpp': 4.11.1
+ comment-parser: 1.4.1
+ eslint: 9.11.1(jiti@2.0.0)
+ jsdoc-type-pratt-parser: 4.1.0
+ refa: 0.12.1
+ regexp-ast-analysis: 0.7.1
+ scslre: 0.3.0
+
+ eslint-plugin-toml@0.11.1(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ debug: 4.3.7
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-compat-utils: 0.5.1(eslint@9.11.1(jiti@2.0.0))
+ lodash: 4.17.21
+ toml-eslint-parser: 0.10.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-unicorn@55.0.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@babel/helper-validator-identifier': 7.24.7
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ ci-info: 4.0.0
+ clean-regexp: 1.0.0
+ core-js-compat: 3.38.1
+ eslint: 9.11.1(jiti@2.0.0)
+ esquery: 1.6.0
+ globals: 15.9.0
+ indent-string: 4.0.0
+ is-builtin-module: 3.2.1
+ jsesc: 3.0.2
+ pluralize: 8.0.0
+ read-pkg-up: 7.0.1
+ regexp-tree: 0.1.27
+ regjsparser: 0.10.0
+ semver: 7.6.3
+ strip-indent: 3.0.0
+
+ eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+ optionalDependencies:
+ '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
+
+ eslint-plugin-vue@9.28.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ eslint: 9.11.1(jiti@2.0.0)
+ globals: 13.24.0
+ natural-compare: 1.4.0
+ nth-check: 2.1.1
+ postcss-selector-parser: 6.1.2
+ semver: 7.6.3
+ vue-eslint-parser: 9.4.3(eslint@9.11.1(jiti@2.0.0))
+ xml-name-validator: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-yml@1.14.0(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ debug: 4.3.7
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-compat-utils: 0.5.1(eslint@9.11.1(jiti@2.0.0))
+ lodash: 4.17.21
+ natural-compare: 1.4.0
+ yaml-eslint-parser: 1.2.3
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.10)(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ '@vue/compiler-sfc': 3.5.10
+ eslint: 9.11.1(jiti@2.0.0)
+
+ eslint-scope@7.2.2:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-scope@8.1.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.1.0: {}
+
+ eslint@9.11.1(jiti@2.0.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
+ '@eslint-community/regexpp': 4.11.1
+ '@eslint/config-array': 0.18.0
+ '@eslint/core': 0.6.0
+ '@eslint/eslintrc': 3.1.0
+ '@eslint/js': 9.11.1
+ '@eslint/plugin-kit': 0.2.0
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.3.0
+ '@nodelib/fs.walk': 1.2.8
+ '@types/estree': 1.0.6
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.3
+ debug: 4.3.7
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.1.0
+ eslint-visitor-keys: 4.1.0
+ espree: 10.2.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ strip-ansi: 6.0.1
+ text-table: 0.2.0
+ optionalDependencies:
+ jiti: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.2.0:
+ dependencies:
+ acorn: 8.12.1
+ acorn-jsx: 5.3.2(acorn@8.12.1)
+ eslint-visitor-keys: 4.1.0
+
+ espree@9.6.1:
+ dependencies:
+ acorn: 8.12.1
+ acorn-jsx: 5.3.2(acorn@8.12.1)
+ eslint-visitor-keys: 3.4.3
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.6
+
+ esutils@2.0.3: {}
+
+ execa@8.0.1:
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.3.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-diff@1.3.0: {}
+
+ fast-glob@3.3.2:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.17.1:
+ dependencies:
+ reusify: 1.0.4
+
+ fdir@6.3.0(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ fdir@6.4.2(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ fflate@0.8.2: {}
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up-simple@1.0.0: {}
+
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.1
+ keyv: 4.5.4
+
+ flatted@3.3.1: {}
+
+ foreground-child@3.3.0:
+ dependencies:
+ cross-spawn: 7.0.3
+ signal-exit: 4.1.0
+
+ fs-extra@11.2.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.1.0
+ universalify: 2.0.1
+
+ fs-extra@7.0.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-minipass@2.1.0:
+ dependencies:
+ minipass: 3.3.6
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-func-name@2.0.2: {}
+
+ get-stream@8.0.1: {}
+
+ get-tsconfig@4.8.1:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ giget@1.2.3:
+ dependencies:
+ citty: 0.1.6
+ consola: 3.2.3
+ defu: 6.1.4
+ node-fetch-native: 1.6.4
+ nypm: 0.3.12
+ ohash: 1.1.4
+ pathe: 1.1.2
+ tar: 6.2.1
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.4.5:
+ dependencies:
+ foreground-child: 3.3.0
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ globals@13.24.0:
+ dependencies:
+ type-fest: 0.20.2
+
+ globals@14.0.0: {}
+
+ globals@15.9.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ he@1.2.0: {}
+
+ hosted-git-info@2.8.9: {}
+
+ html-escaper@2.0.2: {}
+
+ human-signals@5.0.0: {}
+
+ ignore@5.3.2: {}
+
+ import-fresh@3.3.0:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ import-lazy@4.0.0: {}
+
+ importx@0.5.0:
+ dependencies:
+ bundle-require: 5.0.0(esbuild@0.23.1)
+ debug: 4.3.7
+ esbuild: 0.23.1
+ jiti: 2.0.0
+ pathe: 1.1.2
+ tsx: 4.19.1
+ transitivePeerDependencies:
+ - supports-color
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@4.0.0: {}
+
+ ip-address@9.0.5:
+ dependencies:
+ jsbn: 1.1.0
+ sprintf-js: 1.1.3
+
+ is-arrayish@0.2.1: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-builtin-module@3.2.1:
+ dependencies:
+ builtin-modules: 3.3.0
+
+ is-core-module@2.15.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ is-path-inside@3.0.3: {}
+
+ is-stream@3.0.0: {}
+
+ isexe@2.0.0: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@5.0.6:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ debug: 4.3.7
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.1.7:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jiti@1.21.6: {}
+
+ jiti@2.0.0: {}
+
+ jju@1.4.0: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsbn@1.1.0: {}
+
+ jsdoc-type-pratt-parser@4.1.0: {}
+
+ jsesc@0.5.0: {}
+
+ jsesc@3.0.2: {}
+
+ json-buffer@3.0.1: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ jsonc-eslint-parser@2.4.0:
+ dependencies:
+ acorn: 8.12.1
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ semver: 7.6.3
+
+ jsonc-parser@3.3.1: {}
+
+ jsonfile@4.0.0:
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonfile@6.1.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ kleur@3.0.3: {}
+
+ kolorist@1.8.0: {}
+
+ ky@1.7.2: {}
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lines-and-columns@1.2.4: {}
+
+ load-tsconfig@0.2.5: {}
+
+ local-pkg@0.5.0:
+ dependencies:
+ mlly: 1.7.1
+ pkg-types: 1.2.0
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.escaperegexp@4.1.2: {}
+
+ lodash.groupby@4.6.0: {}
+
+ lodash.isfunction@3.0.9: {}
+
+ lodash.isnil@4.0.0: {}
+
+ lodash.isundefined@3.0.1: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.uniq@4.5.0: {}
+
+ lodash@4.17.21: {}
+
+ longest-streak@3.1.0: {}
+
+ loupe@3.1.1:
+ dependencies:
+ get-func-name: 2.0.2
+
+ lru-cache@10.4.3: {}
+
+ lru-cache@6.0.0:
+ dependencies:
+ yallist: 4.0.0
+
+ magic-string@0.30.11:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ magicast@0.3.5:
+ dependencies:
+ '@babel/parser': 7.25.6
+ '@babel/types': 7.25.6
+ source-map-js: 1.2.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.6.3
+
+ markdown-table@3.0.3: {}
+
+ mdast-util-find-and-replace@3.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+
+ mdast-util-from-markdown@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.0.2
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.0
+ micromark-util-decode-numeric-character-reference: 2.0.1
+ micromark-util-decode-string: 2.0.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.1
+ micromark-util-character: 2.1.0
+
+ mdast-util-gfm-footnote@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.1
+ mdast-util-to-markdown: 2.1.0
+ micromark-util-normalize-identifier: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.1
+ mdast-util-to-markdown: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.3
+ mdast-util-from-markdown: 2.0.1
+ mdast-util-to-markdown: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.1
+ mdast-util-to-markdown: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.0.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.1
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.0.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.0
+
+ mdast-util-to-markdown@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-decode-string: 2.0.0
+ unist-util-visit: 5.0.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ micromark-core-commonmark@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.0.2
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.0
+ micromark-factory-label: 2.0.0
+ micromark-factory-space: 2.0.0
+ micromark-factory-title: 2.0.0
+ micromark-factory-whitespace: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-chunked: 2.0.0
+ micromark-util-classify-character: 2.0.0
+ micromark-util-html-tag-name: 2.0.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-resolve-all: 2.0.0
+ micromark-util-subtokenize: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.0
+ micromark-util-sanitize-uri: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.1
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-sanitize-uri: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.0
+ micromark-util-classify-character: 2.0.0
+ micromark-util-resolve-all: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm-table@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.0
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-factory-destination@2.0.0:
+ dependencies:
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-factory-label@2.0.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-factory-space@2.0.0:
+ dependencies:
+ micromark-util-character: 2.1.0
+ micromark-util-types: 2.0.0
+
+ micromark-factory-title@2.0.0:
+ dependencies:
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-factory-whitespace@2.0.0:
+ dependencies:
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-util-character@2.1.0:
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-util-chunked@2.0.0:
+ dependencies:
+ micromark-util-symbol: 2.0.0
+
+ micromark-util-classify-character@2.0.0:
+ dependencies:
+ micromark-util-character: 2.1.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-util-combine-extensions@2.0.0:
+ dependencies:
+ micromark-util-chunked: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-util-decode-numeric-character-reference@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.0
+
+ micromark-util-decode-string@2.0.0:
+ dependencies:
+ decode-named-character-reference: 1.0.2
+ micromark-util-character: 2.1.0
+ micromark-util-decode-numeric-character-reference: 2.0.1
+ micromark-util-symbol: 2.0.0
+
+ micromark-util-encode@2.0.0: {}
+
+ micromark-util-html-tag-name@2.0.0: {}
+
+ micromark-util-normalize-identifier@2.0.0:
+ dependencies:
+ micromark-util-symbol: 2.0.0
+
+ micromark-util-resolve-all@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.0
+
+ micromark-util-sanitize-uri@2.0.0:
+ dependencies:
+ micromark-util-character: 2.1.0
+ micromark-util-encode: 2.0.0
+ micromark-util-symbol: 2.0.0
+
+ micromark-util-subtokenize@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+
+ micromark-util-symbol@2.0.0: {}
+
+ micromark-util-types@2.0.0: {}
+
+ micromark@4.0.0:
+ dependencies:
+ '@types/debug': 4.1.12
+ debug: 4.3.7
+ decode-named-character-reference: 1.0.2
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.1
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.1.0
+ micromark-util-chunked: 2.0.0
+ micromark-util-combine-extensions: 2.0.0
+ micromark-util-decode-numeric-character-reference: 2.0.1
+ micromark-util-encode: 2.0.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-resolve-all: 2.0.0
+ micromark-util-sanitize-uri: 2.0.0
+ micromark-util-subtokenize: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mimic-fn@4.0.0: {}
+
+ min-indent@1.0.1: {}
+
+ minimatch@3.0.8:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minipass@3.3.6:
+ dependencies:
+ yallist: 4.0.0
+
+ minipass@5.0.0: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ yallist: 4.0.0
+
+ mkdirp@1.0.4: {}
+
+ mlly@1.7.1:
+ dependencies:
+ acorn: 8.12.1
+ pathe: 1.1.2
+ pkg-types: 1.2.0
+ ufo: 1.5.4
+
+ mrmime@2.0.0: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ nanoid@3.3.7: {}
+
+ natural-compare-lite@1.4.0: {}
+
+ natural-compare@1.4.0: {}
+
+ node-fetch-native@1.6.4: {}
+
+ node-releases@2.0.18: {}
+
+ normalize-package-data@2.5.0:
+ dependencies:
+ hosted-git-info: 2.8.9
+ resolve: 1.22.8
+ semver: 5.7.2
+ validate-npm-package-license: 3.0.4
+
+ normalize-path@3.0.0: {}
+
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
+ npm-run-path@5.3.0:
+ dependencies:
+ path-key: 4.0.0
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ nypm@0.3.12:
+ dependencies:
+ citty: 0.1.6
+ consola: 3.2.3
+ execa: 8.0.1
+ pathe: 1.1.2
+ pkg-types: 1.2.0
+ ufo: 1.5.4
+
+ ohash@1.1.4: {}
+
+ onetime@6.0.0:
+ dependencies:
+ mimic-fn: 4.0.0
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-try@2.2.0: {}
+
+ package-json-from-dist@1.0.1: {}
+
+ package-manager-detector@0.2.0: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-gitignore@2.0.0: {}
+
+ parse-imports@2.2.1:
+ dependencies:
+ es-module-lexer: 1.5.4
+ slashes: 3.0.12
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.24.7
+ error-ex: 1.3.2
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ path-browserify@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-key@4.0.0: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ pathe@1.1.2: {}
+
+ pathval@2.0.0: {}
+
+ pend@1.2.0: {}
+
+ perfect-debounce@1.0.0: {}
+
+ picocolors@1.1.0: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.2: {}
+
+ pkg-types@1.2.0:
+ dependencies:
+ confbox: 0.1.7
+ mlly: 1.7.1
+ pathe: 1.1.2
+
+ pluralize@8.0.0: {}
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss@8.4.47:
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.1.0
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier-linter-helpers@1.0.0:
+ dependencies:
+ fast-diff: 1.3.0
+
+ prettier@3.3.3: {}
+
+ prompts@2.4.2:
+ dependencies:
+ kleur: 3.0.3
+ sisteransi: 1.0.5
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ rc9@2.1.2:
+ dependencies:
+ defu: 6.1.4
+ destr: 2.0.3
+
+ read-pkg-up@7.0.1:
+ dependencies:
+ find-up: 4.1.0
+ read-pkg: 5.2.0
+ type-fest: 0.8.1
+
+ read-pkg@5.2.0:
+ dependencies:
+ '@types/normalize-package-data': 2.4.4
+ normalize-package-data: 2.5.0
+ parse-json: 5.2.0
+ type-fest: 0.6.0
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ refa@0.12.1:
+ dependencies:
+ '@eslint-community/regexpp': 4.11.1
+
+ regexp-ast-analysis@0.7.1:
+ dependencies:
+ '@eslint-community/regexpp': 4.11.1
+ refa: 0.12.1
+
+ regexp-tree@0.1.27: {}
+
+ regjsparser@0.10.0:
+ dependencies:
+ jsesc: 0.5.0
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@1.22.8:
+ dependencies:
+ is-core-module: 2.15.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.0.4: {}
+
+ rollup@4.22.5:
+ dependencies:
+ '@types/estree': 1.0.6
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.22.5
+ '@rollup/rollup-android-arm64': 4.22.5
+ '@rollup/rollup-darwin-arm64': 4.22.5
+ '@rollup/rollup-darwin-x64': 4.22.5
+ '@rollup/rollup-linux-arm-gnueabihf': 4.22.5
+ '@rollup/rollup-linux-arm-musleabihf': 4.22.5
+ '@rollup/rollup-linux-arm64-gnu': 4.22.5
+ '@rollup/rollup-linux-arm64-musl': 4.22.5
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.22.5
+ '@rollup/rollup-linux-riscv64-gnu': 4.22.5
+ '@rollup/rollup-linux-s390x-gnu': 4.22.5
+ '@rollup/rollup-linux-x64-gnu': 4.22.5
+ '@rollup/rollup-linux-x64-musl': 4.22.5
+ '@rollup/rollup-win32-arm64-msvc': 4.22.5
+ '@rollup/rollup-win32-ia32-msvc': 4.22.5
+ '@rollup/rollup-win32-x64-msvc': 4.22.5
+ fsevents: 2.3.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ rxjs@7.8.1:
+ dependencies:
+ tslib: 2.7.0
+
+ scslre@0.3.0:
+ dependencies:
+ '@eslint-community/regexpp': 4.11.1
+ refa: 0.12.1
+ regexp-ast-analysis: 0.7.1
+
+ semver@5.7.2: {}
+
+ semver@7.5.4:
+ dependencies:
+ lru-cache: 6.0.0
+
+ semver@7.6.3: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shell-quote@1.8.1: {}
+
+ siginfo@2.0.0: {}
+
+ signal-exit@4.1.0: {}
+
+ sirv@2.0.4:
+ dependencies:
+ '@polka/url': 1.0.0-next.28
+ mrmime: 2.0.0
+ totalist: 3.0.1
+
+ sisteransi@1.0.5: {}
+
+ slashes@3.0.12: {}
+
+ source-map-js@1.2.1: {}
+
+ source-map@0.6.1: {}
+
+ spdx-correct@3.2.0:
+ dependencies:
+ spdx-expression-parse: 3.0.1
+ spdx-license-ids: 3.0.20
+
+ spdx-exceptions@2.5.0: {}
+
+ spdx-expression-parse@3.0.1:
+ dependencies:
+ spdx-exceptions: 2.5.0
+ spdx-license-ids: 3.0.20
+
+ spdx-expression-parse@4.0.0:
+ dependencies:
+ spdx-exceptions: 2.5.0
+ spdx-license-ids: 3.0.20
+
+ spdx-license-ids@3.0.20: {}
+
+ sprintf-js@1.0.3: {}
+
+ sprintf-js@1.1.3: {}
+
+ stable-hash@0.0.4: {}
+
+ stackback@0.0.2: {}
+
+ std-env@3.7.0: {}
+
+ string-argv@0.3.2: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.0:
+ dependencies:
+ ansi-regex: 6.1.0
+
+ strip-final-newline@3.0.0: {}
+
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
+ strip-json-comments@3.1.1: {}
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ synckit@0.6.2:
+ dependencies:
+ tslib: 2.7.0
+
+ synckit@0.9.1:
+ dependencies:
+ '@pkgr/core': 0.1.1
+ tslib: 2.7.0
+
+ tapable@2.2.1: {}
+
+ tar@6.2.1:
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 5.0.0
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+
+ test-exclude@7.0.1:
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 10.4.5
+ minimatch: 9.0.5
+
+ text-table@0.2.0: {}
+
+ tiny-invariant@1.3.3: {}
+
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.0: {}
+
+ tinyglobby@0.2.10:
+ dependencies:
+ fdir: 6.4.2(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tinyglobby@0.2.6:
+ dependencies:
+ fdir: 6.3.0(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tinypool@1.0.1: {}
+
+ tinyrainbow@1.2.0: {}
+
+ tinyspy@3.0.2: {}
+
+ to-fast-properties@2.0.0: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toml-eslint-parser@0.10.0:
+ dependencies:
+ eslint-visitor-keys: 3.4.3
+
+ totalist@3.0.1: {}
+
+ tree-kill@1.2.2: {}
+
+ ts-api-utils@1.3.0(typescript@5.6.2):
+ dependencies:
+ typescript: 5.6.2
+
+ tslib@2.7.0: {}
+
+ tsx@4.19.1:
+ dependencies:
+ esbuild: 0.23.1
+ get-tsconfig: 4.8.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-detect@4.1.0: {}
+
+ type-fest@0.20.2: {}
+
+ type-fest@0.21.3: {}
+
+ type-fest@0.6.0: {}
+
+ type-fest@0.8.1: {}
+
+ typescript@5.4.2: {}
+
+ typescript@5.6.2: {}
+
+ ufo@1.5.4: {}
+
+ unconfig@0.6.0:
+ dependencies:
+ '@antfu/utils': 0.7.10
+ defu: 6.1.4
+ importx: 0.5.0
+ transitivePeerDependencies:
+ - supports-color
+
+ undici-types@6.19.8: {}
+
+ unist-util-is@6.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+
+ unist-util-visit@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+
+ universalify@0.1.2: {}
+
+ universalify@2.0.1: {}
+
+ update-browserslist-db@1.1.1(browserslist@4.24.0):
+ dependencies:
+ browserslist: 4.24.0
+ escalade: 3.2.0
+ picocolors: 1.1.0
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ util-deprecate@1.0.2: {}
+
+ validate-npm-package-license@3.0.4:
+ dependencies:
+ spdx-correct: 3.2.0
+ spdx-expression-parse: 3.0.1
+
+ vite-node@2.1.1(@types/node@22.7.4):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.3.7
+ pathe: 1.1.2
+ vite: 5.4.8(@types/node@22.7.4)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
+ vite-plugin-checker@0.8.0(eslint@9.11.1(jiti@2.0.0))(optionator@0.9.4)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)):
+ dependencies:
+ '@babel/code-frame': 7.24.7
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ chokidar: 3.6.0
+ commander: 8.3.0
+ fast-glob: 3.3.2
+ fs-extra: 11.2.0
+ npm-run-path: 4.0.1
+ strip-ansi: 6.0.1
+ tiny-invariant: 1.3.3
+ vite: 5.4.8(@types/node@22.7.4)
+ vscode-languageclient: 7.0.0
+ vscode-languageserver: 7.0.0
+ vscode-languageserver-textdocument: 1.0.12
+ vscode-uri: 3.0.8
+ optionalDependencies:
+ eslint: 9.11.1(jiti@2.0.0)
+ optionator: 0.9.4
+ typescript: 5.6.2
+
+ vite-plugin-dts@4.2.2(@types/node@22.7.4)(rollup@4.22.5)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)):
+ dependencies:
+ '@microsoft/api-extractor': 7.47.7(@types/node@22.7.4)
+ '@rollup/pluginutils': 5.1.2(rollup@4.22.5)
+ '@volar/typescript': 2.4.5
+ '@vue/language-core': 2.1.6(typescript@5.6.2)
+ compare-versions: 6.1.1
+ debug: 4.3.7
+ kolorist: 1.8.0
+ local-pkg: 0.5.0
+ magic-string: 0.30.11
+ typescript: 5.6.2
+ optionalDependencies:
+ vite: 5.4.8(@types/node@22.7.4)
+ transitivePeerDependencies:
+ - '@types/node'
+ - rollup
+ - supports-color
+
+ vite@5.4.8(@types/node@22.7.4):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.4.47
+ rollup: 4.22.5
+ optionalDependencies:
+ '@types/node': 22.7.4
+ fsevents: 2.3.3
+
+ vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1):
+ dependencies:
+ '@vitest/expect': 2.1.1
+ '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.8(@types/node@22.7.4))
+ '@vitest/pretty-format': 2.1.1
+ '@vitest/runner': 2.1.1
+ '@vitest/snapshot': 2.1.1
+ '@vitest/spy': 2.1.1
+ '@vitest/utils': 2.1.1
+ chai: 5.1.1
+ debug: 4.3.7
+ magic-string: 0.30.11
+ pathe: 1.1.2
+ std-env: 3.7.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.0
+ tinypool: 1.0.1
+ tinyrainbow: 1.2.0
+ vite: 5.4.8(@types/node@22.7.4)
+ vite-node: 2.1.1(@types/node@22.7.4)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 22.7.4
+ '@vitest/ui': 2.1.1(vitest@2.1.1)
+ transitivePeerDependencies:
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
+ vscode-jsonrpc@6.0.0: {}
+
+ vscode-languageclient@7.0.0:
+ dependencies:
+ minimatch: 3.1.2
+ semver: 7.6.3
+ vscode-languageserver-protocol: 3.16.0
+
+ vscode-languageserver-protocol@3.16.0:
+ dependencies:
+ vscode-jsonrpc: 6.0.0
+ vscode-languageserver-types: 3.16.0
+
+ vscode-languageserver-textdocument@1.0.12: {}
+
+ vscode-languageserver-types@3.16.0: {}
+
+ vscode-languageserver@7.0.0:
+ dependencies:
+ vscode-languageserver-protocol: 3.16.0
+
+ vscode-uri@3.0.8: {}
+
+ vue-eslint-parser@9.4.3(eslint@9.11.1(jiti@2.0.0)):
+ dependencies:
+ debug: 4.3.7
+ eslint: 9.11.1(jiti@2.0.0)
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.6.0
+ lodash: 4.17.21
+ semver: 7.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ xml-name-validator@4.0.0: {}
+
+ y18n@5.0.8: {}
+
+ yallist@4.0.0: {}
+
+ yaml-eslint-parser@1.2.3:
+ dependencies:
+ eslint-visitor-keys: 3.4.3
+ lodash: 4.17.21
+ yaml: 2.5.1
+
+ yaml@2.5.1: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yauzl@3.1.3:
+ dependencies:
+ buffer-crc32: 0.2.13
+ pend: 1.2.0
+
+ yocto-queue@0.1.0: {}
+
+ zwitch@2.0.4: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..600b4bb
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,2 @@
+packages:
+ - 'packages/**'
diff --git a/rollup.config.mjs b/rollup.config.mjs
deleted file mode 100644
index f85accf..0000000
--- a/rollup.config.mjs
+++ /dev/null
@@ -1,79 +0,0 @@
-
-import { nodeResolve } from '@rollup/plugin-node-resolve';
-import terser from '@rollup/plugin-terser';
-import ignore from 'rollup-plugin-ignore';
-import replace from '@rollup/plugin-replace';
-import strip from '@rollup/plugin-strip';
-
-const original = {
- input: 'src/browser.mjs',
- output: {
- file: 'browser/country/iplookup.js',
- format: 'iife',
- name: 'IpLookup',
- },
- plugins: [
- {
- preventAssignment: true,
- "__CDNURL__": false,
- "__DATA_TYPE__": "'country'",
- },
- nodeResolve({
- browser: true,
- }),
- ignore(["fs", "path"]),
- strip({
- functions: ['console.*'],
- })
- ]
-}
-const settings = [original]
-
-const DeepCopy = obj => {
- if(Array.isArray(obj)) {
- return obj.map(DeepCopy)
- } else if(obj.constructor === Object) {
- const newObj = {}
- for(const key in obj) {
- newObj[key] = DeepCopy(obj[key])
- }
- return newObj
- }
- return obj
-}
-
-settings[1] = DeepCopy(original)
-settings[1].output.file = settings[1].output.file.replace('.js', '.min.js')
-settings[1].plugins.push(terser())
-
-settings[2] = DeepCopy(original)
-settings[2].output.format = 'cjs'
-settings[2].output.file = settings[2].output.file.replace('.js', '.cjs')
-settings[2].plugins[0]["__CDNURL__"] = '"https://cdn.jsdelivr.net/npm/@iplookup/country/"'
-
-settings[3] = DeepCopy(original)
-settings[3].output.format = 'es'
-settings[3].output.file = settings[3].output.file.replace('.js', '.mjs')
-settings[3].plugins[0]["__CDNURL__"] = '"https://cdn.jsdelivr.net/npm/@iplookup/country/"'
-
-settings.push(...settings.map(setting => {
- const newSetting = DeepCopy(setting)
- newSetting.output.file = newSetting.output.file.replace('country', 'geocode')
- newSetting.plugins[0]["__DATA_TYPE__"] = "'geocode'"
- return newSetting
-}))
-
-const extraSettings = settings.map(setting => {
- const extraSetting = DeepCopy(setting)
- extraSetting.input = extraSetting.input.replace('browser', 'browser-extra')
- extraSetting.output.file = extraSetting.output.file.replace('country', 'country-extra').replace('geocode', 'geocode-extra')
- return extraSetting
-})
-
-const exportSettings = settings.concat(extraSettings)
-exportSettings.forEach(setting => {
-// console.log(setting.plugins[0])
- setting.plugins[0] = replace(setting.plugins[0])
-})
-
-export default exportSettings;
diff --git a/script/NODE_VERSION.md b/script/NODE_VERSION.md
deleted file mode 100644
index 392e5e0..0000000
--- a/script/NODE_VERSION.md
+++ /dev/null
@@ -1,17 +0,0 @@
-fs/promises: Needs Node.js >= v14
- Fix by require('fs').promises
-fs.rm: Needs Node.js >= v14.14.0
- USE fs.rmdir as alternative in lower version
-BigInt: Needs Node.js >= v10.4.0
-
-
-top level await >= v14.8
-dynamic import >= 14.x
-
-
-"yauzl": "3" Drop sepport for nodejs < 12
-adm-zip >= 12
-fflate >= 10
-"fast-csv": "5" Drop support for nodejs < 18
- : Cause error in Nodejs 12
-
diff --git a/script/_benchmark-all.bat b/script/_benchmark-all.bat
deleted file mode 100644
index 2fca88c..0000000
--- a/script/_benchmark-all.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /d %~dp0
-
-node benchmark-this.js ila_fields=all
diff --git a/script/_benchmark-city.bat b/script/_benchmark-city.bat
deleted file mode 100644
index 89b113c..0000000
--- a/script/_benchmark-city.bat
+++ /dev/null
@@ -1,16 +0,0 @@
-rem ---------------------
-rem CITY
-rem ---------------------
-cd /d %~dp0
-
-rem default configuration
-node benchmark-this.js ila_fields=latitude,longitude,area,country,region1,metro,timezone,city
-
-rem Run the benchmark for geoip-lite
-node benchmark-other.cjs geoip-lite
-
-rem small_memory configuration
-node benchmark-this.js ila_small_memory=true ila_fields=latitude,longitude,area,country,region1,metro,timezone,city
-
-rem Run the benchmark for fast-geoip
-node benchmark-other.cjs
diff --git a/script/_benchmark-country.bat b/script/_benchmark-country.bat
deleted file mode 100644
index 74d6c0e..0000000
--- a/script/_benchmark-country.bat
+++ /dev/null
@@ -1,10 +0,0 @@
-rem ---------------------
-rem COUNTRY
-rem ---------------------
-cd /d %~dp0
-
-rem Run default configuration
-node benchmark-this.js ila_data_type=country
-
-rem Run small_memory configuration
-node benchmark-this.js ila_data_type=country ila_small_memory=true
diff --git a/script/_benchmark-ll.bat b/script/_benchmark-ll.bat
deleted file mode 100644
index df42328..0000000
--- a/script/_benchmark-ll.bat
+++ /dev/null
@@ -1,7 +0,0 @@
-rem ---------------------
-rem
-rem ---------------------
-cd /d %~dp0
-
-rem default configuration
-node benchmark-this.js ila_fields=latitude,longitude
diff --git a/script/_updatedb-all.bat b/script/_updatedb-all.bat
deleted file mode 100644
index 793b23d..0000000
--- a/script/_updatedb-all.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-
-cd /d %~dp0
-
-node updatedb.js ila_fields=all
diff --git a/script/_updatedb-city.bat b/script/_updatedb-city.bat
deleted file mode 100644
index 17e209a..0000000
--- a/script/_updatedb-city.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-
-cd /d %~dp0
-
-node updatedb.js ila_fields=latitude,longitude,area,country,region1,metro,timezone,city
-node updatedb.js ila_fields=latitude,longitude,area,country,region1,metro,timezone,city ila_small_memory=true
diff --git a/script/_updatedb-country.bat b/script/_updatedb-country.bat
deleted file mode 100644
index 8d26ac9..0000000
--- a/script/_updatedb-country.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-cd /d %~dp0
-
-node updatedb.js debug
-node updatedb.js ila_small_memory=true debug
diff --git a/script/_updatedb-geoip-lite.bat b/script/_updatedb-geoip-lite.bat
deleted file mode 100644
index 3ae5e0f..0000000
--- a/script/_updatedb-geoip-lite.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-
-cd /d %~dp0
-
-node ../node_modules\geoip-lite\scripts\updatedb.js license_key=redist
diff --git a/script/_updatedb-ll.bat b/script/_updatedb-ll.bat
deleted file mode 100644
index 857fdba..0000000
--- a/script/_updatedb-ll.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-
-cd /d %~dp0
-
-node updatedb.js ila_fields=latitude,longitude
diff --git a/script/auto-update-country.sh b/script/auto-update-country.sh
deleted file mode 100755
index 8d49f9b..0000000
--- a/script/auto-update-country.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-cd browser/country
-RES1=`find data -name '*.idx' -size -1k 2>/dev/null`
-if [ -z "$RES1" ]; then
- cd ../country-extra
- RES2=`find data -name '*.idx' -size -1k 2>/dev/null`
- if [ -z "$RES2" ]; then
- VERT=`node -p "var j=require('./package.json');j.version=j.version.split('.').slice(0,-1).join('.')+'.'+require('dayjs')().format('YYYYMMDD');require('fs').writeFileSync('./package.json',JSON.stringify(j,null,2));j.version;"`
- npm publish
- fi
-
- cd ../country
- VERT=`node -p "var j=require('./package.json');j.version=j.version.split('.').slice(0,-1).join('.')+'.'+require('dayjs')().format('YYYYMMDD');require('fs').writeFileSync('./package.json',JSON.stringify(j,null,2));j.version;"`
- git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
- git commit -a -m "v${VERT} auto update ip database"
- npm publish
-fi
-
diff --git a/script/auto-update-geocode.sh b/script/auto-update-geocode.sh
deleted file mode 100755
index 4586b1b..0000000
--- a/script/auto-update-geocode.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-# get current version from package.json
-
-cd browser/geocode
-RES1=`find data -name '*.idx' -size -1k 2>/dev/null`
-if [ -z "$RES1" ]; then
- VERT=`node -p "var j=require('./package.json');j.version=j.version.split('.').slice(0,-1).join('.')+'.'+require('dayjs')().format('YYYYMMDD');require('fs').writeFileSync('./package.json',JSON.stringify(j,null,2));j.version;"`
- git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
- git commit -a -m "v${VERT} auto update ip database"
- npm publish
-fi
-
-cd ../..
-
-cd browser/geocode-extra
-RES2=`find data -name '*.idx' -size -1k 2>/dev/null`
-if [ -z "$RES2" ]; then
- VERT=`node -p "var j=require('./package.json');j.version=j.version.split('.').slice(0,-1).join('.')+'.'+require('dayjs')().format('YYYYMMDD');require('fs').writeFileSync('./package.json',JSON.stringify(j,null,2));j.version;"`
- git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
- git commit -a -m "v${VERT} auto update ip database"
- npm publish
-fi
diff --git a/script/auto-update.sh b/script/auto-update.sh
deleted file mode 100755
index 9b48d67..0000000
--- a/script/auto-update.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-# get current version from package.json
-RES1=`find data -name '*.dat' -size -100k 2>/dev/null`
-if [ -z "$RES1" ]; then
- if ! git diff --quiet HEAD -- *.dat; then
- VERT=`node -p "var j=require('./package.json');j.version=j.version.split('.').slice(0,-1).join('.')+'.'+require('dayjs')().format('YYYYMMDD');require('fs').writeFileSync('./package.json',JSON.stringify(j,null,2));j.version;"`
- git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
- git commit -a -m "v${VERT} auto update ip database"
- npm publish
- fi
-fi
diff --git a/script/benchmark-other.cjs b/script/benchmark-other.cjs
deleted file mode 100644
index b9b8e0f..0000000
--- a/script/benchmark-other.cjs
+++ /dev/null
@@ -1,57 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-const process = require('process')
-
-const isGeoipLite = process.argv.includes('geoip-lite')
-const type = isGeoipLite ? 'geoip-lite' : 'doc999tor-fast-geoip'
-
-console.log('----------------------')
-console.log('Using %s', type)
-console.log('----------------------')
-
-const t1 = Date.now()
-const geoip = require(type)
-const t2 = Date.now()
-console.log("Took %d ms to startup", t2 - t1)
-
-var ipv4 , ipv6
-try {
- ipv4 = fs.readFileSync(path.resolve(__dirname, 'ipv4.txt'), 'utf8').split('\n')
- ipv6 = fs.readFileSync(path.resolve(__dirname, 'ipv6.txt'), 'utf8').split('\n')
-} catch (e) {
- console.error("Please run create_ips.js first")
- process.exit(1)
-}
-
-async function run () {
- for(var ips of [ipv4, ipv6]){
- if(!isGeoipLite) {
- ips.length = 10000
- }
- const n = ips.length
- var r
- var ts, te
- if(!isGeoipLite){
- ts = Date.now()
- for (const ip of ips) {
- r = await geoip.lookup(ip)
- }
- te = Date.now()
- } else {
- ts = Date.now()
- for (const ip of ips) {
- r = geoip.lookup(ip)
- }
- te = Date.now()
- }
- console.log(ipv4 === ips ? "ipv4" : "ipv6")
- console.log("%d ips %d ms (%s ip/s) (%s μs/ip)", n, te - ts, (n * 1000 / (te - ts)).toFixed(3), ((te - ts) / n * 1000).toFixed(3))
- if(!isGeoipLite) {
- console.log('ipv6 - cannot lookup')
- break
- }
- }
- console.log('')
-}
-run()
-
diff --git a/script/benchmark-this.mjs b/script/benchmark-this.mjs
deleted file mode 100644
index b66ec90..0000000
--- a/script/benchmark-this.mjs
+++ /dev/null
@@ -1,48 +0,0 @@
-import fs from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-import process from 'process'
-
-import { lookup, reload } from '../src/main.mjs'
-import { setting } from '../src/setting.mjs'
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-const t1 = Date.now()
-await reload()
-const t2 = Date.now()
-console.log("Took %d ms to startup", t2 - t1)
-
-var ipv4 , ipv6
-try {
- ipv4 = fs.readFileSync(path.resolve(__dirname, 'ipv4.txt'), 'utf8').split('\n')
- ipv6 = fs.readFileSync(path.resolve(__dirname, 'ipv6.txt'), 'utf8').split('\n')
-} catch (e) {
- console.error("Please run create_ips.js first")
- process.exit(1)
-}
-
-for(var ips of [ipv4, ipv6]){
- if(setting.smallMemory){
- ips.length = 10000
- }
- const n = ips.length
- var r
- var ts, te
- if(setting.smallMemory){
- ts = Date.now()
- for (const ip of ips) {
- r = await lookup(ip)
- }
- te = Date.now()
- } else {
- ts = Date.now()
- for (const ip of ips) {
- r = lookup(ip)
- }
- te = Date.now()
- }
- console.log(ipv4 === ips ? "ipv4" : "ipv6")
- console.log("%d ips %d ms (%s ip/s) (%s μs/ip)", n, te - ts, (n * 1000 / (te - ts)).toFixed(3), ((te - ts) / n * 1000).toFixed(3))
-}
diff --git a/script/cjs-test.cjs b/script/cjs-test.cjs
deleted file mode 100644
index c3dcf2a..0000000
--- a/script/cjs-test.cjs
+++ /dev/null
@@ -1,35 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-const process = require('process')
-
-const t1 = Date.now()
-const geoip = require('../cjs/main.cjs')
-const t2 = Date.now()
-console.log("Took %d ms to startup", t2 - t1)
-
-var ipv4 , ipv6
-try {
- ipv4 = fs.readFileSync(path.resolve(__dirname, 'ipv4.txt'), 'utf8').split('\n')
- ipv6 = fs.readFileSync(path.resolve(__dirname, 'ipv6.txt'), 'utf8').split('\n')
-} catch (e) {
- console.error("Please run create_ips.js first")
- process.exit(1)
-}
-
-async function run () {
- for(var ips of [ipv4, ipv6]){
- const n = ips.length
- var r
- var ts, te
- ts = Date.now()
- for (const ip of ips) {
- r = geoip.lookup(ip)
- }
- te = Date.now()
- console.log(ipv4 === ips ? "ipv4" : "ipv6")
- console.log("%d ips %d ms (%s ip/s) (%s μs/ip)", n, te - ts, (n * 1000 / (te - ts)).toFixed(3), ((te - ts) / n * 1000).toFixed(3))
- }
- console.log('')
-}
-run()
-
diff --git a/script/convert-to-cjs.mjs b/script/convert-to-cjs.mjs
deleted file mode 100644
index 335b9fc..0000000
--- a/script/convert-to-cjs.mjs
+++ /dev/null
@@ -1,37 +0,0 @@
-
-import fsSync from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-
-var __filename = fileURLToPath(import.meta.url)
-var __dirname = path.dirname(__filename)
-
-var files = fsSync.readdirSync(path.resolve(__dirname, '..', 'src'))
-
-for(var file of files){
- if(file.startsWith('browser')) continue;
- if(file.endsWith('.mjs')){
- var src = fsSync.readFileSync(path.resolve(__dirname, '..', 'src', file), 'utf8')
- var exportList = []
- src = src.replace(/\nconst __filename[^\n]+/, '')
- src = src.replace(/\nconst __dirname[^\n]+/, '')
- src = src.replace(/\n\s*\/\/[^\n]+/g, '\n')
- src = src.replace(/\nawait reload\(\)/g, 'reload(undefined, true)')// remove top level await
- src = src.replace(/\nawait\s+[^\n]+/g, '\n')// remove top level await
- src = src.replace(/\nexport\s+const\s+(\w+)/g, (m, p1) => {
- exportList.push(p1)
- return '\nconst ' + p1
- })
- src = src.replace(/\nexport\s+const\s+(\w+)/g, '\nconst $1 = exports.$1')
- src = src.replace(/(?:\n|^)import\s+([^\n]+)\s+from([^\n]+)/g, (m, p1, p2) => {
- return '\nconst ' + p1 + ' = require(' + p2.replace(".mjs'", ".cjs'").replace(".mjs\"", ".cjs\"").trim() + ')'
- })
-// src = src.replace("const fs = require('fs/promises')", "var fs;\ntry{ fs=require('fs/promises') }catch(e){ fs=require('fs').promises }\n")
- src = src.replace('export { setting }', '')
-
- src += '\nmodule.exports={' + exportList.map(v => v+':'+v).join(',') + '}'
-
- var dstPath = path.resolve(__dirname, '..', 'cjs', file.replace(/\.mjs$/, '.cjs'))
- fsSync.writeFileSync(dstPath, src)
- }
-}
diff --git a/script/create_ips.mjs b/script/create_ips.mjs
deleted file mode 100644
index c5e3a51..0000000
--- a/script/create_ips.mjs
+++ /dev/null
@@ -1,39 +0,0 @@
-//---------------------------------------------------------------------------
-// This script is used to create a list of IPs from the GeoLite2 databases.
-// Before running this script, download the GeoLite2 databases and put them in the tmp folder.
-//---------------------------------------------------------------------------
-import fs from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-import {parse} from 'fast-csv'
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-var counter = {}
-var ipv4 = [], ipv6 = []
-fs.createReadStream(path.resolve(__dirname, '../tmp/GeoLite2-City-Blocks-IPv4.csv'))
- .pipe(parse())
- .on('data', row => {ipv4.push(row[0].split('/')[0])})
- .on('end', () => write('ipv4', ipv4))
-fs.createReadStream(path.resolve(__dirname, '../tmp/GeoLite2-City-Blocks-IPv6.csv'))
- .pipe(parse())
- .on('data', row => {ipv6.push(row[0].split('/')[0])})
- .on('end', () => write('ipv6', ipv6))
-fs.createReadStream(path.resolve(__dirname, '../tmp/GeoLite2-Country-Blocks-IPv4.csv'))
- .pipe(parse())
- .on('data', row => {ipv4.push(row[0].split('/')[0])})
- .on('end', () => write('ipv4', ipv4))
-fs.createReadStream(path.resolve(__dirname, '../tmp/GeoLite2-Country-Blocks-IPv6.csv'))
- .pipe(parse())
- .on('data', row => {ipv6.push(row[0].split('/')[0])})
- .on('end', () => write('ipv6', ipv6))
-
-function write(str, ips){
- if(!counter[str]) counter[str] = 1
- else {
- ips.shift()
- ips.sort(() => Math.random() - 0.5)
- fs.writeFileSync(path.resolve(__dirname, str + '.txt'), ips.join('\n'))
- }
-}
diff --git a/script/isip-benchmark.mjs b/script/isip-benchmark.mjs
deleted file mode 100644
index 4b46391..0000000
--- a/script/isip-benchmark.mjs
+++ /dev/null
@@ -1,47 +0,0 @@
-import fs from 'fs'
-import path from 'path'
-import { isIP } from 'net'
-import { fileURLToPath } from 'url'
-import process from 'process'
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-var ipv4 , ipv6
-try {
- ipv4 = fs.readFileSync(path.resolve(__dirname, 'ipv4.txt'), 'utf8').split('\n')
- ipv6 = fs.readFileSync(path.resolve(__dirname, 'ipv6.txt'), 'utf8').split('\n')
-} catch (e) {
- console.error("Please run create_ips.js first")
- process.exit(1)
-}
-
-for(var loop = 0; loop < 2; ++loop){
- if(loop === 0){
- console.log('---------------------')
- console.log('string check')
- console.log('---------------------')
- } else {
- console.log('---------------------')
- console.log('isIP')
- console.log('---------------------')
- }
- for(var ips of [ipv4, ipv6]){
- const n = ips.length
- var r
- var ts, te
- ts = Date.now()
- if(loop === 0){
- for(const ip of ips){
- r = ip.includes(':') ? 6 : 4
- }
- } else {
- for(const ip of ips){
- r = isIP(ip)
- }
- }
- te = Date.now()
- console.log(ipv4 === ips ? "ipv4" : "ipv6")
- console.log("%d ips %d ms (%s ip/s) (%s μs/ip)", n, te - ts, (n * 1000 / (te - ts)).toFixed(3), ((te - ts) / n * 1000).toFixed(3))
- }
-}
diff --git a/script/memory-usage0.mjs b/script/memory-usage0.mjs
deleted file mode 100644
index b17acc0..0000000
--- a/script/memory-usage0.mjs
+++ /dev/null
@@ -1,15 +0,0 @@
-function checkMemory(){
- if(global.gc){
- global.gc(true)
- console.log('Garbage collection done')
- }
- const used = process.memoryUsage()
- const messages = []
- for (let key in used) {
- messages.push(`${key}: ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`)
- }
- console.log(new Date(), messages.join(', '))
-}
-
-console.log('Memory usage before loading library')
-checkMemory()
diff --git a/script/memory-usage1.mjs b/script/memory-usage1.mjs
deleted file mode 100644
index 537d930..0000000
--- a/script/memory-usage1.mjs
+++ /dev/null
@@ -1,40 +0,0 @@
-function checkMemory(){
- if(global.gc){
- global.gc(true)
- console.log('Garbage collection done')
- }
- const used = process.memoryUsage()
- const messages = []
- for (let key in used) {
- messages.push(`${key}: ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`)
- }
- console.log(new Date(), messages.join(', '))
-}
-
-console.log('Memory usage before loading library')
-checkMemory()
-
-import { reload } from '../src/main.js'
-var timeStart = Date.now()
-await reload()
-var timeEnd = Date.now()
-console.log('Library load time: %d ms', timeEnd - timeStart)
-
-console.log('Memory usage after loading library')
-checkMemory()
-
-async function test(){
- return new Promise(function(resolve, reject){
- setTimeout(function(){
- checkMemory()
- resolve()
- }, 5000)
- })
-}
-async function run(){
- for(var i = 0; i < 10000; ++i){
- await test()
- }
-}
-
-run()
\ No newline at end of file
diff --git a/script/run.mjs b/script/run.mjs
deleted file mode 100644
index 44ba9e0..0000000
--- a/script/run.mjs
+++ /dev/null
@@ -1,4 +0,0 @@
-import {lookup} from '../src/main.mjs'
-
-var r = await lookup('1.6.72.55')
-console.log(r)
diff --git a/script/test-geoip-country.cjs b/script/test-geoip-country.cjs
deleted file mode 100644
index 2115dec..0000000
--- a/script/test-geoip-country.cjs
+++ /dev/null
@@ -1,69 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-const process = require('process')
-const utils = require('../cjs/utils.cjs')
-const type = '../cjs/main.cjs' //'./geoip-country5' // //
-
-console.log('----------------------')
-console.log('Using %s', type)
-console.log('----------------------')
-
-const t1 = Date.now()
-const geoip = require(type)
-const t2 = Date.now()
-console.log("Took %d ms to startup", t2 - t1)
-
-var ipv4 , ipv6
-try {
- ipv4 = fs.readFileSync(path.resolve(__dirname, 'ipv4.txt'), 'utf8').split('\n')
- ipv6 = fs.readFileSync(path.resolve(__dirname, 'ipv6.txt'), 'utf8').split('\n')
-} catch (e) {
- console.error("Please run create_ips.js first")
- process.exit(1)
-}
-
-async function run () {
- for(var ips of [ipv4, ipv6]){
- const n = ips.length
- const results = []
- var r, preIp
- var ts, te
- var isV4 = ips === ipv4
- ts = Date.now()
- for (const ip of ips) {
- r = geoip.lookup(ip)
- results.push(r && r.country)
- if(isV4){
- if(preIp){
- var ne = utils.aton4(ip) + utils.aton4(preIp) >> 1
- var newIp = utils.ntoa4(ne)
- if(newIp !== ip && newIp !== preIp){
- r = geoip.lookup(newIp)
- results.push(r && r.country)
- }
- }
- var ne = utils.aton4(ip) + 1
- var newIp = utils.ntoa4(ne)
- if(newIp !== ip && newIp !== preIp){
- r = geoip.lookup(newIp)
- results.push(r && r.country)
- }
- var ne = utils.aton4(ip) - 2
- var newIp = utils.ntoa4(ne)
- if(newIp !== ip && newIp !== preIp){
- r = geoip.lookup(newIp)
- results.push(r && r.country)
- }
- preIp = ip
- }
- }
- te = Date.now()
- console.log(isV4 ? "ipv4" : "ipv6")
- console.log("%d ips %d ms (%s ip/s) (%s μs/ip)", n, te - ts, (n * 1000 / (te - ts)).toFixed(3), ((te - ts) / n * 1000).toFixed(3))
- const fileName = path.basename(type)
- fs.writeFileSync(path.resolve(__dirname, fileName + (isV4 ? '4' : '6') + '.txt'), results.join('\n'))
- }
- console.log('')
-}
-run()
-
diff --git a/script/updatedb-first.mjs b/script/updatedb-first.mjs
deleted file mode 100644
index 71b51c2..0000000
--- a/script/updatedb-first.mjs
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import {update} from '../src/db.mjs'
-import fs from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-var dataDir = path.join(__dirname, '../data')
-var hasDataDir = true
-try{
- if(!fs.existsSync(dataDir)) {
- fs.mkdirSync(dataDir)
- }
-}catch(e){
- // No write access permission
- hasDataDir = false
- console.log(e)
-}
-
-var dataFile = path.join(__dirname, '../data/4-1.dat')
-if(hasDataDir && !fs.existsSync(dataFile)) {
- update()
-}
diff --git a/script/updatedb.cjs b/script/updatedb.cjs
deleted file mode 100644
index 52cec81..0000000
--- a/script/updatedb.cjs
+++ /dev/null
@@ -1,4 +0,0 @@
-
-const {update} = require('../cjs/db.cjs')
-
-update()
diff --git a/script/updatedb.mjs b/script/updatedb.mjs
deleted file mode 100644
index 4d460c4..0000000
--- a/script/updatedb.mjs
+++ /dev/null
@@ -1,4 +0,0 @@
-
-import {update} from '../src/db.mjs'
-
-update()
diff --git a/spec/lookup.spec.cjs b/spec/lookup.spec.cjs
deleted file mode 100644
index 2214a19..0000000
--- a/spec/lookup.spec.cjs
+++ /dev/null
@@ -1,26 +0,0 @@
-const { lookup } = require('../cjs/main.cjs')
-
-describe('CJS lookup', () => {
- console.log('CJS lookup')
- it('CJS lookup', () => {
- var ips = ['1.0.65.0', '2001:4860:b002::68', '2001:df3:e900::']
- var success = 0
- for(var ip of ips){
- var result = lookup(ip)
- console.log(ip, result)
- if(result) success++
- }
- expect(success > (ips.length >> 1)).toBe(true)
- })
- it('CJS lookup v4map', () => {
- var ips = ['::ffff:2.29.0.82', '::ffff:3.24.1.56', '0:0:0:0:0:ffff:103.175.136.16']
- var success = 0
- for(var ip of ips){
- var result = lookup(ip)
- console.log(ip, result)
- if(result) success++
- }
- expect(success > (ips.length >> 1)).toBe(true)
- })
-})
-
diff --git a/spec/lookup.spec.mjs b/spec/lookup.spec.mjs
deleted file mode 100644
index f317f6a..0000000
--- a/spec/lookup.spec.mjs
+++ /dev/null
@@ -1,27 +0,0 @@
-import { lookup } from '../src/main.mjs'
-
-describe('lookup', () => {
- console.log('MJS lookup')
- it('lookup', () => {
- var ips = ['1.0.65.0', '154.45.200.16', '2001:4860:b002::68', '2001:df3:e900::']
- var success = 0
- for(var ip of ips){
- var result = lookup(ip)
- console.log(ip, result)
- if(result) success++
- }
- expect(success > (ips.length >> 1)).toBe(true)
- })
- it('lookup v4map', () => {
- var ips = ['::ffff:2.29.0.82', '::ffff:3.24.1.56', '0:0:0:0:0:ffff:103.175.136.16']
- var success = 0
- for(var ip of ips){
- var result = lookup(ip)
- console.log(ip, result)
- if(result) success++
- }
- expect(success > (ips.length >> 1)).toBe(true)
- })
-})
-
-
diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json
deleted file mode 100644
index ff22420..0000000
--- a/spec/support/jasmine.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "spec_dir": "spec",
- "spec_files": [
- "**/*[sS]pec.?(m|c)js"
- ],
- "helpers": [
- "helpers/**/*.?(m)js"
- ],
- "env": {
- "stopSpecOnExpectationFailure": false,
- "random": true
- }
-}
\ No newline at end of file
diff --git a/src/browser-extra.mjs b/src/browser-extra.mjs
deleted file mode 100644
index 959f766..0000000
--- a/src/browser-extra.mjs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import ip_lookup from './browser.mjs'
-
-import { countries } from 'countries-list'
-
-export default async(ip) => {
- const geodata = await ip_lookup(ip)
- if(geodata && countries[geodata.country]){
- const h = countries[geodata.country]
- geodata.country_name = h.name
- geodata.country_native = h.native
- geodata.continent = h.continent
- geodata.capital = h.capital
- geodata.phone = h.phone
- geodata.currency = h.currency
- geodata.languages = h.languages
- }
- return geodata
-}
diff --git a/src/browser.mjs b/src/browser.mjs
deleted file mode 100644
index 15563ce..0000000
--- a/src/browser.mjs
+++ /dev/null
@@ -1,137 +0,0 @@
-
-import { aton4, aton6Start, numberToDir, numToCountryCode } from './utils.mjs'
-
-const TOP_URL = __CDNURL__ || document.currentScript.src.split('/').slice(0, -1).join('/') + '/'
-const MAIN_RECORD_SIZE = __DATA_TYPE__ === 'country' ? 2 : 8
-const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
-
-const downloadArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry))
- return downloadArrayBuffer(url, retry - 1)
- }
- return null
- }
- return res.arrayBuffer()
- })
-}
-
-const downloadVersionArrayBuffer = async(url, retry = 3) => {
- return fetch(url, {cache: 'no-cache'}).then(async (res) => {
- if(!res.ok) {
- if(res.status === 404) return null
- if(retry) {
- await sleep(100 * (4-retry) * (4-retry))
- return downloadVersionArrayBuffer(url, retry - 1)
- }
- return null
- }
- return [res.headers.get('x-jsd-version'), await res.arrayBuffer()]
- })
-}
-
-const downloadIdx = __CDNURL__ ? downloadVersionArrayBuffer : downloadArrayBuffer
-
-const Idx = {}, Url = {4: TOP_URL, 6: TOP_URL}
-const Preload = {
- 4: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(__CDNURL__ && buf[0]) {
- Url[4] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/')
- return Idx[4] = new Uint32Array(buf[1])
- }
- return Idx[4] = new Uint32Array(buf)
- }),
- 6: downloadIdx(TOP_URL + '4.idx').then(buf => {
- if(!buf){
-// console.log('ipv6 file cannot download')
- return
- }
- if(__CDNURL__ && buf[0]) {
- Url[6] = __CDN_URL__.replace(/\/$/, '@'+buf[0]+'/')
- return Idx[6] = new BigUint64Array(buf.slice(1))
- }
- return Idx[6] = new BigUint64Array(buf)
- })
-}
-
-export default async (ipString) => {
- var ip, version, isv4 = true
- if(ipString.includes(':')) {
- ip = aton6Start(ipString)
- version = ip.constructor === BigInt ? 6 : 4
- if(version === 6) isv4 = false
- } else {
- ip = aton4(ipString)
- version = 4
- }
-
- const ipIndexes = Idx[version] || (await Preload[version])
- if(!ipIndexes) {
-// console.log('Cannot download idx file')
- return null
- }
- if(!(ip >= ipIndexes[0])) return null
- var fline = 0, cline = ipIndexes.length-1, line
- for(;;){
- line = (fline + cline) >> 1
- if(ip < ipIndexes[line]){
- if(cline - fline < 2) return null
- cline = line - 1
- } else {
- if(fline === line) {
- if(cline > line && ip >= ipIndexes[cline]){
- line = cline
- }
- break;
- }
- fline = line
- }
- }
-
- const fileName = numberToDir(line)
- const dataBuffer = await downloadArrayBuffer(Url[version] + version + '/' + fileName)
- if(!dataBuffer) {
-// console.log('Cannot download data file')
- return null
- }
- const ipSize = (version - 2) * 2
- const recordSize = MAIN_RECORD_SIZE + ipSize * 2
- const recordCount = dataBuffer.byteLength / recordSize
- const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount))
- fline = 0, cline = recordCount - 1
- for(;;){
- line = fline + cline >> 1
- if(ip < startList[line]){
- if(cline - fline < 2) return null
- cline = line - 1
- } else {
- if(fline === line) {
- if(cline > line && ip >= startList[cline]){
- line = cline
- }
- break;
- }
- fline = line
- }
- }
- const endIp = isv4 ? new Uint32Array( dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- : new BigUint64Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0]
- if(ip >= startList[line] && ip <= endIp){
- if(__DATA_TYPE__ === 'country'){
- const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))[0]
- return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
- } else {
- const arr = new Int32Array(dataBuffer.slice(recordCount*ipSize*2+line*MAIN_RECORD_SIZE, recordCount*ipSize*2+(line+1)*MAIN_RECORD_SIZE))
- const ccCode = numToCountryCode(arr[0] & 1023)
- return {latitude: ((arr[0]>>10)) / 10000, longitude: (arr[1]) / 10000, country: ccCode}
- }
- }
- return null
-}
\ No newline at end of file
diff --git a/src/browser_utils.mjs b/src/browser_utils.mjs
deleted file mode 100644
index ac92068..0000000
--- a/src/browser_utils.mjs
+++ /dev/null
@@ -1,25 +0,0 @@
-//----------------------------
-// COUNTRY: IndexLoop = 10
-//----------------------------
-// IPv4: 1172844 >> 2 = 293211 ips
-// INDEX_FILE_SIZE = (2^IndexLoop)*4 = 4096 bytes
-// COUNTRY_FILE_SIZE = Math.ceil(293211 / IndexSize) * (4 + 4 + 2) = 2870 bytes
-// IPv6: 1914064 >> 3 = 146605 ips
-// INDEX_FILE_SIZE = (2^IndexLoop)*8 = 8192 bytes
-// COUNTRY_FILE_SIZE = Math.ceil(146605 / IndexSize) * (8 + 8 + 2) = 144 * 18 = 2592 bytes
-
-
-//----------------------------
-// LATITUDE + LONGITUDE: IndexLoop = 11
-//----------------------------
-// IPv4: 6474072 >> 2 = 1,618,518 ips
-// INDEX_FILE_SIZE = (2^IndexLoop)*4 = 8192 bytes
-// COUNTRY_FILE_SIZE = Math.ceil(1618518 / IndexSize) * (4 + 4 + 4 + 4) = 791 * 16 = 12656 bytes
-// IPv6: 7621144 >> 3 = 952,643 ips
-// INDEX_FILE_SIZE = (2^IndexLoop)*8 = 16384 bytes
-// COUNTRY_FILE_SIZE = Math.ceil(952643 / IndexSize) * (8 + 8 + 4 + 4) = 466 * 24 = 11184 bytes
-
-
-export const downloadBuffer = async (url) => {
- return fetch(url, {cache: 'no-cache'}).then(res => res.arrayBuffer())
-}
diff --git a/src/db.mjs b/src/db.mjs
deleted file mode 100644
index efacbad..0000000
--- a/src/db.mjs
+++ /dev/null
@@ -1,856 +0,0 @@
-import fs from 'fs/promises'
-import fsSync from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-import { createHash } from 'crypto'
-import { pipeline } from 'stream/promises'
-
-import axios from 'axios'
-import { parse } from '@fast-csv/parse'
-import { Address4, Address6 } from 'ip-address'
-import dayjs from 'dayjs'
-
-import { setting, consoleLog, consoleWarn } from './setting.mjs'
-import { getPostcodeDatabase, strToNum37, aton4, aton6, getSmallMemoryFile, numberToDir, countryCodeToNum } from './utils.mjs'
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-const rimraf = (dir) => {
- if(fs.rm){
- return fs.rm(dir, {recursive: true, force: true, maxRetries: 3})
- }
- return fs.rmdir(dir, {recursive: true, maxRetries: 3})
-}
-
-//---------------------------------------
-// Databse update
-//---------------------------------------
-const DownloadServer = 'https://download.maxmind.com/app/geoip_download'
-import yauzl from 'yauzl'
-export const update = async () => {
- var srcList, refreshTmpDir = setting.downloadType !== 'reuse'
- if(refreshTmpDir || !fsSync.existsSync(setting.tmpDataDir)){
- // refresh tmp folder
- await rimraf(setting.tmpDataDir)
- await fs.mkdir(setting.tmpDataDir, {recursive: true})
- }
- if (!fsSync.existsSync(setting.fieldDir)){
- await fs.mkdir(setting.fieldDir, {recursive: true})
- }
-
- consoleLog('Downloading database')
- if(setting.browserType === 'geocode'){
- await dbipLocation()
- return createBrowserIndex(setting.browserType)
- }
-
- if(setting.ipLocationDb){
- srcList = await ipLocationDb(setting.ipLocationDb.replace(/-country$/, ''))
- } else {
- srcList = await downloadZip()
- }
-
- if(!srcList){
- return console.log('ERROR TO UPDATE')
- }
- consoleLog(srcList)
- if(srcList === 'NO NEED TO UPDATE') {
- return
- }
-
- consoleLog('Creating database for ip-location-api')
- await createData(srcList)
- consoleLog('Database update completed!!')
-
- // remove tmp folder
- if(refreshTmpDir){
- await rimraf(setting.tmpDataDir, {recursive: true, force: true})
- }
- if(SHA256_RESULT){
- // save sha256
- await fs.writeFile(path.join(setting.fieldDir, setting.series + '-' + setting.dataType + '-CSV.zip.sha256'), SHA256_RESULT)
- }
-
- // replace to new database
- var tmpFiles = fsSync.readdirSync(setting.fieldDir).filter(file => file.endsWith('.tmp'))
- for(var tmpFile of tmpFiles){
- await fs.rename(path.join(setting.fieldDir, tmpFile), path.join(setting.fieldDir, tmpFile.replace('.tmp', '')))
- }
- if(setting.smallMemory && !setting.runningUpdate){
- await fs.cp(path.join(setting.fieldDir, 'v4-tmp'), path.join(setting.fieldDir, 'v4'), {recursive: true, force: true})
- await fs.cp(path.join(setting.fieldDir, 'v6-tmp'), path.join(setting.fieldDir, 'v6'), {recursive: true, force: true})
- rimraf(path.join(setting.fieldDir, 'v4-tmp')).catch(consoleWarn)
- rimraf(path.join(setting.fieldDir, 'v6-tmp')).catch(consoleWarn)
- }
-
- if(setting.browserType){
- await createBrowserIndex(setting.browserType)
- }
-
- console.log('SUCCESS TO UPDATE')
-}
-
-const ipLocationDb = async (db) => {
- var preUrl = 'https://cdn.jsdelivr.net/npm/@ip-location-db/'+db+'-country/'+db+'-country'
- var urls = [preUrl+'-ipv4.csv', preUrl+'-ipv6.csv'], fileNames = []
- for(var url of urls){
- fileNames.push(await _ipLocationDb(url))
- }
- return fileNames
-}
-
-const _ipLocationDb = async (url) => {
- var fileEnd = url.split('-').pop()
- return axios({
- method: 'get',
- url: url,
- responseType: 'stream'
- }).then(res => {
- return new Promise((resolve, reject) => {
- var fileName = setting.ipLocationDb + '-Blocks-' + fileEnd
- const ws = fsSync.createWriteStream(path.join(setting.tmpDataDir, fileName))
- ws.write('network1,network2,cc\n')
- res.data.pipe(ws)
- ws.on('finish', () => {
- resolve(fileName)
- })
- ws.on('error', reject)
- })
- })
-}
-
-const dbipLocation = async () => {
- const address = "https://download.db-ip.com/free/dbip-city-lite-" + dayjs().format('YYYY-MM') + ".csv.gz"
- const res = await fetch(address)
- const tmpFile = path.join(setting.tmpDataDir, 'dbip-city-lite.csv')
- const ws = fsSync.createWriteStream(tmpFile)
- await pipeline(res.body.pipeThrough(new DecompressionStream('gzip')), ws)
- return new Promise((resolve, reject) => {
- const v4 = [], v6 = []
- var preData
- fsSync.createReadStream(tmpFile).pipe(parse())
- .on('error', reject)
- .on('end', () => {
- var v4Buf1 = Buffer.alloc(v4.length * 4)
- var v4Buf2 = Buffer.alloc(v4.length * 4)
- var v4Buf3 = Buffer.alloc(v4.length * 8)
- for(var i = 0; i < v4.length; ++i){
- v4Buf1.writeUInt32LE(v4[i][0], i * 4)
- v4Buf2.writeUInt32LE(v4[i][1], i * 4)
- v4Buf3.writeInt32LE(v4[i][2], i * 8)
- v4Buf3.writeInt32LE(v4[i][3], i * 8 + 4)
- }
- fsSync.writeFileSync(path.join(setting.fieldDir, '4-1.dat'), v4Buf1)
- fsSync.writeFileSync(path.join(setting.fieldDir, '4-2.dat'), v4Buf2)
- fsSync.writeFileSync(path.join(setting.fieldDir, '4-3.dat'), v4Buf3)
-
- var v6Buf1 = Buffer.alloc(v6.length * 8)
- var v6Buf2 = Buffer.alloc(v6.length * 8)
- var v6Buf3 = Buffer.alloc(v6.length * 8)
- for(var i = 0; i < v6.length; ++i){
- v6Buf1.writeBigUInt64LE(v6[i][0], i * 8)
- v6Buf2.writeBigUInt64LE(v6[i][1], i * 8)
- v6Buf3.writeInt32LE(v6[i][2], i * 8)
- v6Buf3.writeInt32LE(v6[i][3], i * 8 + 4)
- }
- fsSync.writeFileSync(path.join(setting.fieldDir, '6-1.dat'), v6Buf1)
- fsSync.writeFileSync(path.join(setting.fieldDir, '6-2.dat'), v6Buf2)
- fsSync.writeFileSync(path.join(setting.fieldDir, '6-3.dat'), v6Buf3)
- resolve()
- })
- .on('data', arr => {
- if(!arr[2] || arr[3] === 'ZZ' || arr[3] === 'EU') return;
- var latitude = Math.round((parseFloat(arr[6])) * 10000) // -90 ~ 90 -> 10 ~ 190
- // -900000 ~ 900000 -> 1000000 ~ 1900000
- var longitude = Math.round((parseFloat(arr[7])) * 10000)// -180 ~ 180 -> 20 ~ 220
- // -1800000 ~ 1800000 -> 2000000 ~ 2200000
- var countryCodeNum = countryCodeToNum(arr[3]) // 0 ~ 675
- latitude = (latitude) << 10 | countryCodeNum
- if(arr[0].includes(':')){
- var start = aton6(arr[0])
- if(preData[1].constructor !== BigInt) preData = null
- if(preData && preData[1] + 1n === start && preData[2] === latitude && preData[3] === longitude){
- preData[1] = aton6(arr[1])
- return
- }
- v6.push(preData = [aton6(arr[0]), aton6(arr[1]), latitude, longitude])
- } else {
- var start = aton4(arr[0])
- if(preData && preData[1] + 1 === start && preData[2] === latitude && preData[3] === longitude){
- preData[1] = aton4(arr[1])
- return
- }
- v4.push(preData = [aton4(arr[0]), aton4(arr[1]), latitude, longitude])
- }
- })
- })
-}
-
-const createBrowserIndex = async (type) => {
- const exportDir = path.join(setting.fieldDir, type)
- await fs.rm(path.join(exportDir, '4'), {recursive: true, force: true})
- await fs.mkdir(path.join(exportDir, '4'), {recursive: true})
- await fs.rm(path.join(exportDir, '6'), {recursive: true, force: true})
- await fs.mkdir(path.join(exportDir, '6'), {recursive: true})
-
- const IndexSize = type === 'country' ? 1024 : 2048
-
- // ipv4
- var startBuf = await fs.readFile(path.join(setting.fieldDir, '4-1.dat'))
- var startList = new Uint32Array(startBuf.buffer)
- var len = startList.length, indexList = new Uint32Array(IndexSize)
- var i, j, k
- var endBuf = await fs.readFile(path.join(setting.fieldDir, '4-2.dat'))
- var endList = new Uint32Array(endBuf.buffer)
- var dbInfo = await fs.readFile(path.join(setting.fieldDir, '4-3.dat'))
- var dbList = type === 'country' ? new Uint16Array(dbInfo.buffer) : new Int32Array(dbInfo.buffer)
- var recordSize = setting.mainRecordSize + 8
- for(i = 0; i < IndexSize; ++i){
- var index = len * i / IndexSize | 0
- indexList[i] = startList[index]
- var nextIndex = len * (i + 1) / IndexSize | 0
- var count = nextIndex - index
- var exportBuf = Buffer.alloc(recordSize * count)
- for(j = index, k = 0; j < nextIndex; ++j){
- exportBuf.writeUInt32LE(startList[j], k * 4)
- exportBuf.writeUInt32LE(endList[j], 4 * count + k * 4)
- if(type === 'country'){
- exportBuf.writeUInt16LE(dbList[j], 8 * count + k * setting.mainRecordSize)
- } else {
- exportBuf.writeInt32LE(dbList[2*j], 8 * count + k * setting.mainRecordSize)
- exportBuf.writeInt32LE(dbList[2*j+1], 8 * count + k * setting.mainRecordSize + 4)
- }
- ++k
- }
- await fs.writeFile(path.join(exportDir, '4', numberToDir(i)), exportBuf)
- }
- await fs.writeFile(path.join(exportDir, '4.idx'), Buffer.from(indexList.buffer))
-
- startBuf = await fs.readFile(path.join(setting.fieldDir, '6-1.dat'))
- startList = new BigUint64Array(startBuf.buffer)
- len = startList.length
- indexList = new BigUint64Array(IndexSize)
- endBuf = await fs.readFile(path.join(setting.fieldDir, '6-2.dat'))
- endList = new BigUint64Array(endBuf.buffer)
- dbInfo = await fs.readFile(path.join(setting.fieldDir, '6-3.dat'))
- dbList = type === 'country' ? new Uint16Array(dbInfo.buffer) : new Int32Array(dbInfo.buffer)
- recordSize = setting.mainRecordSize + 16
- for(i = 0; i < IndexSize; ++i){
- var index = len * i / IndexSize | 0
- indexList[i] = startList[index]
- var nextIndex = len * (i + 1) / IndexSize | 0
- var exportBuf = Buffer.alloc(recordSize * (nextIndex - index))
- var count = nextIndex - index
- for(j = index, k = 0; j < nextIndex; ++j){
- exportBuf.writeBigUInt64LE(startList[j], k * 8)
- exportBuf.writeBigUInt64LE(endList[j], 8 * count + k * 8)
- if(type === 'country'){
- exportBuf.writeUInt16LE(dbList[j], 16 * count + k * setting.mainRecordSize)
- } else {
- exportBuf.writeInt32LE(dbList[2*j], 16 * count + k * setting.mainRecordSize)
- exportBuf.writeInt32LE(dbList[2*j+1], 16 * count + k * setting.mainRecordSize + 4)
- }
- ++k
- }
- await fs.writeFile(path.join(exportDir, '6', numberToDir(i)), exportBuf)
- }
- await fs.writeFile(path.join(exportDir, '6.idx'), Buffer.from(indexList.buffer))
-
- var exPath = path.join(__dirname, '..', 'browser', type)
- await fs.rm(path.join(exPath, '4'), {recursive: true, force: true})
- await fs.rm(path.join(exPath, '6'), {recursive: true, force: true})
- await fs.cp(exportDir, exPath, {recursive: true})
- exPath = path.join(__dirname, '..', 'browser', type + '-extra')
- await fs.rm(path.join(exPath, '4'), {recursive: true, force: true})
- await fs.rm(path.join(exPath, '6'), {recursive: true, force: true})
- await fs.cp(exportDir, exPath, {recursive: true})
- await fs.rm(exportDir, {recursive: true, force: true})
-}
-
-var SHA256_RESULT
-const downloadZip = async () => {
- SHA256_RESULT = false
- var name = setting.dataType[0].toUpperCase() + setting.dataType.slice(1)
- const database = {
- type: setting.dataType,
- edition: setting.series + '-' + name + '-CSV',
- suffix: 'zip.sha256',
- src: [
- setting.series + '-' + name + '-Locations-en.csv',
- setting.series + '-' + name + '-Blocks-IPv4.csv',
- setting.series + '-' + name + '-Blocks-IPv6.csv'
- ],
- }
- if(setting.language !== 'en' && setting.isCity){
- database.src.push(setting.series + '-' + name + '-Locations-' + setting.language + '.csv')
- }
- if(!setting.licenseKey) {
- return consoleWarn('Please set your license key')
- }
- var url = DownloadServer + '?edition_id=' + database.edition + '&suffix=' + database.suffix + "&license_key=" + setting.licenseKey
- if(setting.licenseKey === 'redist'){
- url = 'https://raw.githubusercontent.com/sapics/node-geolite2-redist/master/redist/'
- url += database.edition + '.' + database.suffix
- }
- var text = await axios.get(url)
- var reg = /\w{50,}/, r = reg.exec(text.data)
- if(!r) {
- return consoleWarn('Cannot download sha256')
- }
- var sha256 = r[0], data = ''
- try{
- data = await fs.readFile(path.join(setting.fieldDir, database.edition + '.zip.sha256'), 'utf8')
- }catch(e){
- data = ''
- }
-
- const zipPath = path.join(setting.tmpDataDir, database.edition + '.zip')
- if(data === sha256){
- if(fsSync.existsSync(zipPath)){
- const zipHash = await sha256Hash(zipPath)
- if(zipHash === sha256){
- if(!setting.multiDbDir) {
- if(setting.sameDbSetting) return 'NO NEED TO UPDATE'
- if(setting.language === 'en') return database.src
- }
- }
- } else if(!setting.multiDbDir){
- if(setting.sameDbSetting) return 'NO NEED TO UPDATE'
- }
- }
-
- SHA256_RESULT = sha256
- url = DownloadServer + '?edition_id=' + database.edition + '&suffix=' + database.suffix.replace('.sha256', '') + "&license_key=" + setting.licenseKey
- if(setting.licenseKey === 'redist'){
- url = 'https://raw.githubusercontent.com/sapics/node-geolite2-redist/master/redist/'
- url += database.edition + '.' + database.suffix.replace('.sha256', '')
- }
- return axios({
- method: 'get',
- url: url,
- responseType: 'stream'
- }).then(res => {
- const dest = fsSync.createWriteStream(zipPath)
- return new Promise((resolve, reject) => {
- consoleLog('Decompressing', database.edition + '.zip')
- res.data.pipe(dest)
- res.data.on('end', () => {
- yauzl.open(zipPath, {lazyEntries: true}, (err, zipfile) => {
- if(err) return reject(err)
- zipfile.readEntry()
- zipfile.on('entry', entry => {
- for(var src of database.src){
- if(!entry.fileName.endsWith(src)) continue;
- consoleLog('Extracting', entry.fileName)
- return (function(src){
- zipfile.openReadStream(entry, (err, readStream) => {
- if(err) return reject(err)
- readStream.pipe(fsSync.createWriteStream(path.join(setting.tmpDataDir, src)))
- readStream.on('end', () => {
- zipfile.readEntry()
- })
- })
- })(src)
- }
- zipfile.readEntry()
- })
- zipfile.on('end', () => resolve(database.src))
- })
- })
- res.data.on('error', reject)
- })
- })
-}
-
-const sha256Hash = async (file) => {
- return new Promise((resolve, reject) => {
- const stream = fsSync.createReadStream(file)
- const hash = createHash('sha256')
- hash.once('finish', () => resolve(hash.digest('hex')))
- stream.on('error', reject)
- stream.pipe(hash)
- })
-}
-
-const createData = async (src) => {
- var mapDatas = []
- var locationSrc = src.filter(file => file.includes('Locations'))
- locationSrc.sort((a,b) => {
- // Locations-en.csv should be the first
- if(a.endsWith('-en.csv')) return -1
- if(b.endsWith('-en.csv')) return 1
- })
- for(var file of locationSrc){
- mapDatas.push(await getMapData(file))
- }
- if(setting.locFile){
- minifyMapData(mapDatas)
- }
- var blockSrc = src.filter(file => file.includes('Blocks'))
- mapDatas.push([])
- for(var file of blockSrc){
- await createMainData(file, mapDatas)
- }
- if(setting.locFile){
- await createMapData(mapDatas)
- }
-}
-
-
-const createSmallMemoryFile = (ws, ipv4, line, buffer2, buffer3) => {
- const [ _dir, file, offset ] = getSmallMemoryFile(line, ipv4 ? setting.v4 : setting.v6, true)
- if(offset === 0){
- const dir = path.join(setting.fieldDir, _dir)
- if(ws) ws.end()
- if(file === '_0' && !fsSync.existsSync(dir)){
- fsSync.mkdirSync(dir, {recursive: true})
- }
- if(setting.smallMemoryFileSize <= buffer2.length + buffer3.length){
- var buf = Buffer.alloc(buffer2.length + buffer3.length)
- buffer2.copy(buf)
- buffer3.copy(buf, buffer2.length)
- fsSync.writeFile(path.join(dir, file), buf, () => {})
- return
- }
- ws = fsSync.createWriteStream(path.join(dir, file))
- }
- ws.write(buffer2)
- ws.write(buffer3)
- return ws
-}
-
-const createMainData = async (file, mapDatas) => {
- var ipv4 = file.endsWith('v4.csv')
- var ipv = ipv4 ? 4 : 6
- var rs = fsSync.createReadStream(path.join(setting.tmpDataDir, file))
- var ws1 = fsSync.createWriteStream(path.join(setting.fieldDir, ipv + '-1.dat.tmp'), {highWaterMark: 1024*1024})
- if(!setting.smallMemory){
- var ws2 = fsSync.createWriteStream(path.join(setting.fieldDir, ipv + '-2.dat.tmp'), {highWaterMark: 1024*1024})
- var ws3 = fsSync.createWriteStream(path.join(setting.fieldDir, ipv + '-3.dat.tmp'), {highWaterMark: 1024*1024})
- } else {
- var ws = null
- var dir = path.join(setting.fieldDir, 'v' + ipv + '-tmp')
- if(fsSync.existsSync(dir)){
- await fs.rm(dir, {recursive: true, force: true})
- }
- }
-
- var preBuffer1, preBuffer2, preBuffer3, preCC, preEnd
- var preLocId, preLatitude, preLongitude, preArea, prePostcode
-// var preCountryCode
- var preLocLocation
- var mapData0 = mapDatas[0], locIdList = mapDatas[mapDatas.length - 1]
- var lineCount = 0
- areaDatabase = {}, areaCount = 0
-
- return new Promise((resolve, reject) => {
- var checkCount = 0
- function check(){
- if(++checkCount === 3)resolve()
- }
- rs.pipe(parse({headers: true}))
- .on('error', reject)
- .on('data', row => {
- var cc, buffer1, buffer2, buffer3, addr, start, end
- if(setting.ipLocationDb){
- if(ipv4){
- start = aton4(row.network1)
- end = aton4(row.network2)
- } else {
- start = aton6(row.network1)
- end = aton6(row.network2)
- }
- } else {
- if(ipv4){
- addr = new Address4(row.network)
- start = aton4(addr.startAddress().correctForm())
- end = aton4(addr.endAddress().correctForm())
- } else {
- addr = new Address6(row.network)
- start = aton6(addr.startAddress().correctForm())
- end = aton6(addr.endAddress().correctForm())
- }
- }
-
- if(setting.isCountry){
- if(setting.ipLocationDb){
- cc = row.cc
- } else {
- cc = mapData0[row.geoname_id]
- }
- if(!cc || cc.length !== 2) {
- return;// console.warn('Invalid country code', cc, row.geoname_id)
- }
- if(cc === preCC && (ipv4 && preEnd + 1 === start || !ipv4 && preEnd + 1n === start)){
- if(ipv4){
- preBuffer2.writeUInt32LE(end)
- } else {
- preBuffer2.writeBigUInt64LE(end)
- }
- } else {
- if(ipv4){
- buffer1 = Buffer.allocUnsafe(4)
- buffer1.writeUInt32LE(start)
- buffer2 = Buffer.allocUnsafe(4)
- buffer2.writeUInt32LE(end)
- } else {
- buffer1 = Buffer.allocUnsafe(8)
- buffer1.writeBigUInt64LE(start)
- buffer2 = Buffer.allocUnsafe(8)
- buffer2.writeBigUInt64LE(end)
- }
- buffer3 = Buffer.allocUnsafe(2)
- buffer3.write(cc)
- if(preBuffer1){
- if(!ws1.write(preBuffer1)) rs.pause()
- if (setting.smallMemory) {
- ws = createSmallMemoryFile(ws, ipv4, lineCount++, preBuffer2, preBuffer3)
- } else {
- if(!ws2.write(preBuffer2)) rs.pause()
- if(!ws3.write(preBuffer3)) rs.pause()
- }
- }
- preCC = cc
- preBuffer1 = buffer1
- preBuffer2 = buffer2
- preBuffer3 = buffer3
- }
- } else {
- var locId = row.geoname_id
- var latitude = Math.round(row.latitude * 10000)
- var longitude = Math.round(row.longitude * 10000)
- var area = row.accuracy_radius
- var postcode = row.postal_code
- // var countryId = row.registered_country_geoname_id
- // var countryCode = null
- var locLocation = mapData0[locId] && mapData0[locId].counter
-
- var isSame = true
- if(setting.mainFieldHash.latitude && preLatitude !== latitude) isSame = false
- if(setting.mainFieldHash.longitude && preLongitude !== longitude) isSame = false
- if(setting.mainFieldHash.area && preArea !== area) isSame = false
- if(setting.mainFieldHash.postcode && prePostcode !== postcode) isSame = false
-
- if( (locId === preLocId || locLocation > 0 && locLocation === preLocLocation || setting.noLocFile)
- && isSame
-// && (locId || (mapData0[countryId] && preCountryCode === mapData0[countryId].country_iso_code))
- && (ipv4 && preEnd + 1 === start || !ipv4 && preEnd + 1n === start)){
- if(ipv4){
- preBuffer2.writeUInt32LE(parseInt(end, 10))
- } else {
- preBuffer2.writeBigUInt64LE(end)
- }
- } else {
- if(!locId){
- return;
- // save only country code
-// if(!countryId) return
-// if(!mapData0[countryId]) return console.warn('Invalid country id', countryId)
-// countryCode = mapData0[countryId].country_iso_code
-// if(!countryCode || countryCode.length !== 2) return
- }
- if(locId && !mapData0[locId]) {
- return consoleWarn('Invalid location id', locId)
- }
- if(locId){
- if(!mapData0[locId].counter){
- locIdList.push(locId)
- locLocation = mapData0[locId].counter = locIdList.length
- }
- }
- if(preBuffer1){
- if(!ws1.write(preBuffer1)) rs.pause()
- if(setting.smallMemory){
- ws = createSmallMemoryFile(ws, ipv4, lineCount++, preBuffer2, preBuffer3)
- } else {
- if(!ws2.write(preBuffer2)) rs.pause()
- if(!ws3.write(preBuffer3)) rs.pause()
- }
- }
- if(ipv4){
- buffer1 = Buffer.allocUnsafe(4)
- buffer1.writeUInt32LE(parseInt(start, 10))
- buffer2 = Buffer.allocUnsafe(4)
- buffer2.writeUInt32LE(parseInt(end, 10))
- } else {
- buffer1 = Buffer.allocUnsafe(8)
- buffer1.writeBigUInt64LE(start)
- buffer2 = Buffer.allocUnsafe(8)
- buffer2.writeBigUInt64LE(end)
- }
-
- buffer3 = Buffer.alloc(setting.mainRecordSize)
-
- var offset = 0
- if(setting.locFile){
- buffer3.writeUInt32LE(mapData0[locId].counter)
- offset += 4
- }
- if(setting.mainFieldHash.latitude) {
- buffer3.writeInt32LE(latitude, offset)
- offset += 4
- }
- if(setting.mainFieldHash.longitude) {
- buffer3.writeInt32LE(longitude, offset)
- offset += 4
- }
- if(setting.mainFieldHash.postcode) {
- var postcodeDb = getPostcodeDatabase(postcode)
- buffer3.writeUInt32LE(postcodeDb[1], offset)
- buffer3.writeInt8(postcodeDb[0], offset + 4)
- offset += 5
- }
- if(setting.mainFieldHash.area) {
- buffer3.writeUInt8(makeAreaDatabase(area), offset)
- }
-
- preLocLocation = locLocation
- preLocId = locId
- preLatitude = latitude
- preLongitude = longitude
- preArea = area
-// preCountryCode = countryCode
- prePostcode = postcode
- preBuffer1 = buffer1
- preBuffer2 = buffer2
- preBuffer3 = buffer3
- }
- }
- preEnd = end
- })
- .on('pause', () => {
- ws1.once('drain', () => rs.resume())
- if(!setting.smallMemory){
- ws2.once('drain', () => rs.resume())
- ws3.once('drain', () => rs.resume())
- }
- })
- .on('end', () => {
- if(setting.smallMemory){
- ws = createSmallMemoryFile(ws, ipv4, lineCount, preBuffer2, preBuffer3)
- if(ws) ws.end(check)
- else ++checkCount
- ++checkCount
- } else {
- ws2.end(preBuffer2, check)
- ws3.end(preBuffer3, check)
- }
- ws1.end(preBuffer1, check)
- })
- })
-}
-
-const minifyMapData = (mapDatas) => {
- var mapData0 = mapDatas[0]
- if(setting.language !== 'en') {
- var mapData1 = mapDatas.splice(1, 1)[0]
- for(var locId in mapData0){
- if(mapData1 && mapData1[locId]){
- if(mapData1[locId].city_name) mapData0[locId].city_name = mapData1[locId].city_name
- if(mapData1[locId].subdivision_1_name) mapData0[locId].subdivision_1_name = mapData1[locId].subdivision_1_name
- if(mapData1[locId].subdivision_2_name) mapData0[locId].subdivision_2_name = mapData1[locId].subdivision_2_name
- }
- }
- }
-
- var locIds = Object.keys(mapData0), locFields = Object.keys(setting.locFieldHash).filter(v => setting.locFieldHash[v])
- locIds.sort((a,b) => a-b)
- const hash = {
- country: 'country_iso_code',
- region1: 'subdivision_1_iso_code',
- region1_name: 'subdivision_1_name',
- region2: 'subdivision_2_iso_code',
- region2_name: 'subdivision_2_name',
- city: 'city_name',
- metro: 'metro_code',
- timezone: 'time_zone'
- }
- const ranking = {
- country: 8,
- region1: 7,
- region1_name: 6,
- region2: 4,
- region2_name: 3,
- city: 9,
- metro: 1,
- timezone: 5
- }
- locFields.sort((a,b) => { return ranking[b] - ranking[a]})
- var checkFields = locFields.map(v => hash[v])
-
- var best1 = checkFields.shift()
- var listHash = {}
- for(var locId of locIds){
- var data = mapData0[locId]
- if(!listHash[data[best1]]){
- listHash[data[best1]] = []
- }
- listHash[data[best1]].push(locId)
- }
-
- var i, j, len, dataI, dataJ, locIdJ, tmpLocIds
- for(var key in listHash){
- tmpLocIds = listHash[key]
- for(i = 0, len = tmpLocIds.length; i < len; ++i){
- dataI = mapData0[tmpLocIds[i]]
- loopj: for(j = i+1; j < len; ++j){
- dataJ = mapData0[locIdJ = tmpLocIds[j]]
- for(var field of checkFields){
- if(dataI[field] !== dataJ[field]){
- continue loopj
- }
- }
- mapData0[locIdJ] = dataI
- tmpLocIds.splice(j, 1)
- --j
- --len
- }
- }
- }
-}
-
-const createMapData = async (mapDatas) => {
- var locIdList = mapDatas.pop()
- var mapData0 = mapDatas[0]
- var ws1 = fsSync.createWriteStream(path.join(setting.fieldDir, 'location.dat.tmp'))
- var ws2 = fsSync.createWriteStream(path.join(setting.fieldDir, 'name.dat.tmp'))
- var cityHash = {}, euHash = {}
- sub1Database = {}, sub2Database = {}, timezoneDatabase = {}
- sub1Count = 0, sub2Count = 0, timezoneCount = 0
-
- for(var locId of locIdList){
- var data0 = mapData0[locId]
-
- var cc = data0.country_iso_code
- var region1 = data0.subdivision_1_iso_code
- var region2 = data0.subdivision_2_iso_code
- var timezone = data0.time_zone
- var metro = data0.metro_code
-
- var region1_name = data0.subdivision_1_name
- var region2_name = data0.subdivision_2_name
- var city = data0.city_name
-
- var offset = 0
- var b = Buffer.alloc(setting.locRecordSize)
- if(setting.locFieldHash.country){
- if(cc && cc.length === 2) {
- b.write(cc, offset); //country code [2 bytes]
- if(setting.locFieldHash.eu && data0.is_in_european_union == 1){
- euHash[cc] = true
- }
- }
- offset += 2
- }
- if(setting.locFieldHash.region1){
- if(region1) b.writeUInt16LE(strToNum37(region1), offset) // subdivision code [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.region1_name){
- if(region1_name) b.writeUInt16LE(makeSub1Database(region1_name), offset) // subdivision name index [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.region2){
- if(region2) b.writeUInt16LE(strToNum37(region2), offset) // subdivision code [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.region2_name){
- if(region2_name) b.writeUInt16LE(makeSub2Database(region2_name), offset) // subdivision name index [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.metro){
- if(metro) b.writeUInt16LE(metro, offset) // metro code [2 bytes]
- offset += 2
- }
- if(setting.locFieldHash.timezone){
- if(timezone) b.writeUInt16LE(makeTimezoneDatabase(timezone), offset)// timezone [2 byte]
- offset += 2
- }
- if(setting.locFieldHash.city){
- if(city){
- b.writeUInt32LE(inputBuffer(cityHash, ws2, city), offset) // cityname index [4 bytes]
- }
- }
- ws1.write(b)
- }
- ws1.end()
- ws2.end()
-
- var hash = {
- region1_name: DatabaseToArray(sub1Database),
- region2_name: DatabaseToArray(sub2Database),
- timezone: DatabaseToArray(timezoneDatabase),
- area: DatabaseToArray(areaDatabase).map(v => parseInt(v, 10)||0),
- eu: euHash
- }
- if(!setting.locFieldHash.region1_name) delete hash.region1_name
- if(!setting.locFieldHash.region2_name) delete hash.region2_name
- if(!setting.locFieldHash.timezone) delete hash.timezone
- if(!setting.mainFieldHash.area) delete hash.area
- if(!setting.locFieldHash.eu) delete hash.eu
- if(Object.keys(hash).length > 0){
- await fs.writeFile(path.join(setting.fieldDir, 'sub.json.tmp'), JSON.stringify(hash))
- }
- sub1Database = sub2Database = timezoneDatabase = areaDatabase = null
- mapDatas.length = 0
-}
-
-const DatabaseToArray = (database) => {
- var arr = ['']
- for(var key in database){
- arr[database[key]] = key
- }
- return arr
-}
-
-const inputBuffer = (hash, dataFile, text) => {
- if(hash[text]) return hash[text]
- if(hash.__offsetBB === undefined) {
- var b = Buffer.alloc(1)
- dataFile.write(b)
- hash.__offsetBB = 1
- }
- var offset = hash.__offsetBB
- var b = Buffer.from(text)
- var n = b.length + (offset << 8)
- dataFile.write(b)
- hash.__offsetBB = offset + b.length
- return hash[text] = n
-}
-
-var sub1Database = {}, sub2Database = {}, timezoneDatabase = {}, areaDatabase = {}
-var sub1Count = 0, sub2Count = 0, timezoneCount = 0, areaCount = 0
-const makeSub1Database = (sub1) => {
- if(sub1Database[sub1]) return sub1Database[sub1]
- return sub1Database[sub1] = ++sub1Count
-}
-const makeSub2Database = (sub2) => {
- if(sub2Database[sub2]) return sub2Database[sub2]
- return sub2Database[sub2] = ++sub2Count
-}
-const makeTimezoneDatabase = (tz) => {
- if(timezoneDatabase[tz]) return timezoneDatabase[tz]
- return timezoneDatabase[tz] = ++timezoneCount
-}
-const makeAreaDatabase = (area) => {
- if(areaDatabase[area]) return areaDatabase[area]
- return areaDatabase[area] = ++areaCount
-}
-
-const getMapData = async (file) => {
- const rs = fsSync.createReadStream(path.join(setting.tmpDataDir, file))
- const result = {}
- return new Promise((resolve, reject) => {
- rs.pipe(parse({headers: true}))
- .on('error', reject)
- .on('data', row => {
- if(setting.isCountry){
- result[row.geoname_id] = row.country_iso_code
- } else {
- result[row.geoname_id] = row
- }
- })
- .on('end', () => resolve(result))
- })
-}
diff --git a/src/main-pack.mjs b/src/main-pack.mjs
deleted file mode 100644
index 908ca35..0000000
--- a/src/main-pack.mjs
+++ /dev/null
@@ -1,560 +0,0 @@
-
-import fs from 'fs/promises'
-import fsSync from 'fs'
-import path from 'path'
-import { exec, execSync } from 'child_process'
-
-import { countries, continents } from 'countries-list'
-import { CronJob } from 'cron'
-
-import { setting, setSetting, getSettingCmd, consoleLog, consoleWarn } from './setting.mjs'
-import { num37ToStr, getSmallMemoryFile, getZeroFill, aton6Start, aton4 } from './utils.mjs'
-import { update as updataDbAsync } from './db.mjs'
-
-const v4db = setting.v4
-const v6db = setting.v6
-const locFieldHash = setting.locFieldHash
-const mainFieldHash = setting.mainFieldHash
-
-// JavaScript type definition
-/**
- * @typedef {Object} LookupResult
- * @property {number} [latitude]
- * @property {number} [longitude]
- * @property {string} [postcode]
- * @property {string} [area]
- * @property {string} [country]
- * @property {boolean} [eu]
- * @property {string} [region1]
- * @property {string} [region1_name]
- * @property {string} [region2]
- * @property {string} [region2_name]
- * @property {number} [metro]
- * @property {string} [timezone]
- * @property {string} [city]
- * @property {string} country_name
- * @property {string} country_native
- * @property {string} continent
- * @property {string} continent_name
- * @property {string} capital
- * @property {number[]} phone
- * @property {string[]} currency
- * @property {string[]} languages
- */
-
-
-//---------------------------------------
-// Database lookup
-//---------------------------------------
-/**
- * lookup ip address
- * @param {string} ip - ipv4 or ipv6 formatted address
- * @return {LookupResult | Promise | null} location information
- */
-export const lookup = (ip) => {
- // net.isIP(ip) is good for checking ip address format
- // but it's slow for checking ipv6 address
- // therefore, we use ip.includes(':') instead
- var isIpv6
- if(ip.includes(':')){
- ip = aton6Start(ip)
- isIpv6 = ip.constructor === BigInt
- } else {
- ip = aton4(ip)
- isIpv6 = false
- }
- const db = isIpv6 ? v6db : v4db
- if(!(ip >= db.firstIp)) return null
- const list = db.startIps
- var fline = 0, cline = db.lastLine, line
- for(;;){
- line = fline + cline >> 1
- if(ip < list[line]){
- if(cline - fline < 2) return null
- cline = line - 1
- } else {
- if(fline === line) {
- if(cline !== line && ip >= list[cline]) {
- line = cline
- }
- break
- }
- fline = line
- }
- }
-
- if(setting.smallMemory){
- // this case return Promise
- return lineToFile(line, db).then(buffer => {
- var endIp = isIpv6 ? buffer.readBigUInt64LE(0) : buffer.readUInt32LE(0)
- if(ip > endIp) return null
- if(setting.isCountry){
- return setCountryInfo({
- country: buffer.toString('latin1', isIpv6 ? 8 : 4, isIpv6 ? 10 : 6)
- })
- }
- return setCityRecord(buffer, {}, isIpv6 ? 8 : 4)
- })
- }
- if(ip > db.endIps[line]) return null
- if(setting.isCountry){
- return setCountryInfo({
- country: db.mainBuffer.toString('latin1', line * db.recordSize, line * db.recordSize + 2)
- })
- }
- return setCityRecord(db.mainBuffer, {}, line * db.recordSize)
-}
-
-/**
- * setup database without reload
- * @param {object} [_setting]
- * @return {void}
- */
-export const setupWithoutReload = setSetting
-
-/**
- * clear in-memory database
- * @type {function}
- * @return {void}
- */
-export const clear = () => {
- v4db.startIps = v6db.startIps = v4db.endIps = v6db.endIps = v4db.mainBuffer = v6db.mainBuffer = null
- Region1NameJson = Region2NameJson = TimezoneJson = LocBuffer = CityNameBuffer = EuJson = null
-}
-
-var Region1NameJson, Region2NameJson, TimezoneJson, LocBuffer, CityNameBuffer, AreaJson, EuJson
-var updateJob
-/**
- * reload in-memory database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [sync] - sync mode
- * @param {boolean} [_runningUpdate] - if it's running update [internal use]
- * @return {Promise|void}
- */
-export const reload = async (_setting, sync, _runningUpdate) => {
- var curSetting = setting
- if(_setting){
- var oldSetting = Object.assign({}, setting)
- setSetting(_setting)
- curSetting = Object.assign({}, setting)
- Object.assign(setting, oldSetting)
- }
- const dataDir = curSetting.fieldDir
- const v4 = v4db, v6 = v6db
- var dataFiles = {
- v41: path.join(dataDir, '4-1.dat'),
- v42: path.join(dataDir, '4-2.dat'),
- v43: path.join(dataDir, '4-3.dat'),
- v61: path.join(dataDir, '6-1.dat'),
- v62: path.join(dataDir, '6-2.dat'),
- v63: path.join(dataDir, '6-3.dat'),
- cityLocation: path.join(dataDir, 'location.dat'),
- cityName: path.join(dataDir, 'name.dat'),
- citySub: path.join(dataDir, 'sub.json')
- }
-
- var locBuffer, cityNameBuffer, subBuffer
- var buffer41, buffer42, buffer43, buffer61, buffer62, buffer63
- var testDir = dataDir
- if(curSetting.smallMemory){
- testDir = path.join(testDir, 'v4')
- }
-
- if(sync){
- if(!fsSync.existsSync(testDir)){
- consoleLog('Database creating ...')
- updateDb(_setting && curSetting, true, true)
- consoleLog('Database created')
- }
- buffer41 = fsSync.readFileSync(dataFiles.v41)
- buffer61 = fsSync.readFileSync(dataFiles.v61)
- if(!curSetting.smallMemory){
- buffer42 = fsSync.readFileSync(dataFiles.v42)
- buffer43 = fsSync.readFileSync(dataFiles.v43)
- buffer62 = fsSync.readFileSync(dataFiles.v62)
- buffer63 = fsSync.readFileSync(dataFiles.v63)
- }
- if(curSetting.locFile){
- locBuffer = fsSync.readFileSync(dataFiles.cityLocation)
- if(locFieldHash.city){
- cityNameBuffer = fsSync.readFileSync(dataFiles.cityName)
- }
- if(locFieldHash.region1_name || locFieldHash.region2_name || locFieldHash.timezone || mainFieldHash.area || locFieldHash.eu){
- subBuffer = fsSync.readFileSync(dataFiles.citySub)
- }
- }
- } else {
- if(!fsSync.existsSync(testDir)){
- consoleLog('Database creating ...')
- await updateDb(_setting && curSetting, true)
- consoleLog('Database created')
- }
- var prs = [
- fs.readFile(dataFiles.v41).then(data => buffer41 = data),
- fs.readFile(dataFiles.v61).then(data => buffer61 = data),
- ]
- if(!curSetting.smallMemory){
- prs.push(
- fs.readFile(dataFiles.v42).then(data => buffer42 = data),
- fs.readFile(dataFiles.v43).then(data => buffer43 = data),
- fs.readFile(dataFiles.v62).then(data => buffer62 = data),
- fs.readFile(dataFiles.v63).then(data => buffer63 = data)
- )
- }
- if(curSetting.locFile){
- prs.push(fs.readFile(dataFiles.cityLocation).then(data => locBuffer = data))
- if(locFieldHash.city){
- prs.push(fs.readFile(dataFiles.cityName).then(data => cityNameBuffer = data))
- }
- if(locFieldHash.region1_name || locFieldHash.region2_name || locFieldHash.timezone || mainFieldHash.area || locFieldHash.eu){
- prs.push(fs.readFile(dataFiles.citySub).then(data => subBuffer = data))
- }
- }
- await Promise.all(prs)
- }
-
- if(_setting){
- Object.assign(setting, curSetting)
- }
-
- v4.startIps = new Uint32Array(buffer41.buffer, 0, buffer41.byteLength >> 2)
- v6.startIps = new BigUint64Array(buffer61.buffer, 0, buffer61.byteLength >> 3)
- if(!curSetting.smallMemory){
- v4.endIps = new Uint32Array(buffer42.buffer, 0, buffer42.byteLength >> 2)
- v4.mainBuffer = buffer43
- v6.endIps = new BigUint64Array(buffer62.buffer, 0, buffer62.byteLength >> 3)
- v6.mainBuffer = buffer63
- }
-
- v4.lastLine = v4.startIps.length - 1
- v6.lastLine = v6.startIps.length - 1
- v4.firstIp = v4.startIps[0]
- v6.firstIp = v6.startIps[0]
- if(curSetting.isCity){
- LocBuffer = locBuffer
- CityNameBuffer = cityNameBuffer
- if(subBuffer){
- var tmpJson = JSON.parse(subBuffer)
- if(locFieldHash.region1_name) Region1NameJson = tmpJson.region1_name
- if(locFieldHash.region2_name) Region2NameJson = tmpJson.region2_name
- if(locFieldHash.timezone) TimezoneJson = tmpJson.timezone
- if(mainFieldHash.area) AreaJson = tmpJson.area
- if(locFieldHash.eu) EuJson = tmpJson.eu
- }
- }
-
- // To avoid the error (when the database is updated while the server is running),
- // we need to syncronous update the database
- if(setting.smallMemory && _runningUpdate){
- const rimraf = (dir) => {
- if(fs.rm){
- return fs.rm(dir, {recursive: true, force: true, maxRetries: 3})
- }
- return fs.rmdir(dir, {recursive: true, maxRetries: 3})
- }
- fsSync.cpSync(path.join(setting.fieldDir, 'v4-tmp'), path.join(setting.fieldDir, 'v4'), {recursive: true, force: true})
- fsSync.cpSync(path.join(setting.fieldDir, 'v6-tmp'), path.join(setting.fieldDir, 'v6'), {recursive: true, force: true})
- rimraf(path.join(setting.fieldDir, 'v4-tmp')).catch(consoleWarn)
- rimraf(path.join(setting.fieldDir, 'v6-tmp')).catch(consoleWarn)
- }
-
- if(!updateJob && setting.autoUpdate){
- updateJob = new CronJob(setting.autoUpdate, () => {
- updateDb().finally(() => {})
- }, null, true, 'UTC')
- } else if(updateJob && !setting.autoUpdate){
- updateJob.stop()
- updateJob = null
- }
-}
-
-const watchHash = {}
-/**
- * Watch database directory.
- * When database file is updated, it reload the database automatically
- * This causes error if you use ILA_SMALL_MEMORY=true
- * @type {function}
- * @param {string} [name] - name of watch. If you want to watch multiple directories, you can set different name for each directory
- */
-export const watchDb = (name = 'ILA') => {
- var watchId = null
- watchHash[name] = fsSync.watch(setting.fieldDir, (eventType, filename) => {
- if(!filename.endsWith('.dat')) return;
- if(fsSync.existsSync(path.join(setting.fieldDir, filename))) {
- if(watchId) clearTimeout(watchId)
- watchId = setTimeout(reload, 30 * 1000)
- }
- })
-}
-
-/**
- * Stop watching database directory
- * @type {function}
- * @param {string} [name]
- */
-export const stopWatchDb = (name = 'ILA') => {
- if(watchHash[name]){
- watchHash[name].close()
- delete watchHash[name]
- }
-}
-
-/**
- * Update database and auto reload database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [noReload] - if you don't want to reload the database after update
- * @param {boolean} [sync] - if you want to update the database in sync mode
- * @return {Promise} - true if database is updated, false if no need to update
- */
-export const updateDb = (_setting, noReload, sync) => {
- // By import { updateDb } from './db.js' is the better way for update.
- // However, db.js import many external modules, it makes slow down the startup time and uses more memory.
- // Therefore, we use exec() to run the script in the other process.
- var arg, runningUpdate = false
- if(_setting){
- var oldSetting = Object.assign({}, setting)
- setSetting(_setting)
- arg = getSettingCmd()
- Object.assign(setting, oldSetting)
- } else {
- arg = getSettingCmd()
- }
-
- if(!_setting && !sync && !setting.smallMemory){
- var sameDbSetting = setting.sameDbSetting
- if(!sameDbSetting) setting.sameDbSetting = true
- return updataDbAsync().then((r) => {
- setting.sameDbSetting = sameDbSetting
- if(r === true){
- if(!noReload) reload()
- }
- return true
- }).catch(e => {
- setting.sameDbSetting = sameDbSetting
- throw e
- })
- }
-
- var scriptPath = path.resolve(_setting ? _setting.apiDir : setting.apiDir, 'script', 'updatedb.mjs')
- if(scriptPath.includes(' ')) scriptPath = '"' + scriptPath + '"'
- var cmd = 'node ' + scriptPath
- if(!_setting){
- arg += ' ILA_SAME_DB_SETTING=true'
- }
- if(_setting && _setting.smallmemory || !_setting && setting.smallMemory){
- runningUpdate = true
- arg += ' ILA_RUNNING_UPDATE=true'
- }
-
- if(arg){
- cmd += ' ' + arg
- }
- if(sync){
- try{
- var stdout = execSync(cmd)
- if(stdout.includes('NO NEED TO UPDATE')){
- return true
- }
- if(stdout.includes('SUCCESS TO UPDATE')){
- if(!noReload){
- reload(_setting, sync)
- }
- return true
- }
- return false
- }catch(e){
- consoleWarn(e)
- return false
- }
- }
- return new Promise((resolve, reject) => {
- exec(cmd, (err, stdout, stderr) => {
- if(err) {
- consoleWarn(err)
- }
- if(stderr) {
- consoleWarn(stderr)
- }
- if(stdout) {
- consoleLog(stdout)
- }
- if(err) {
- reject(err)
- } else if(stdout.includes('ERROR TO UPDATE')){
- reject(new Error('ERROR TO UPDATE'))
- } else if(stdout.includes('NO NEED TO UPDATE')){
- resolve(false)
- } else if(stdout.includes('SUCCESS TO UPDATE')){
- if(noReload){
- resolve(true)
- } else {
- reload(_setting, false, runningUpdate).then(() => {
- resolve(true)
- }).catch(reject)
- }
- } else {
- consoleLog('UNKNOWN ERROR')
- reject(new Error('UNKNOWN ERROR'))
- }
- })
- })
-}
-
-/*
--- Remain this code for better performance check
-const lineToFile = (line, db) => {
- const [ dir, file, offset ] = getSmallMemoryFile(line, db)
- return new Promise((resolve, reject) => {
- // stream
-// fsSync.createReadStream(path.join(dir, file), {start: offset, end: offset + db.recordSize - 1}, {highWaterMark: db.recordSize})
-// .on('data', resolve)
-// .on('error', reject)
-
- // fs.readFile
-// fs.readFile(path.join(dir, file)).then(buffer => resolve(buffer.subarray(offset, offset + db.recordSize))).catch(reject)
-
- // fs.open + fs.read
- fs.open(path.join(dir, file), 'r').then(fd => {
- const buffer = Buffer.alloc(db.recordSize)
- fd.read(buffer, 0, db.recordSize, offset).then(() => {
- fd.close().catch(reject)
- resolve(buffer)
- }).catch(reject)
- }).catch(reject)
- })
-}
-*/
-const lineToFile = async (line, db) => {
- const [ dir, file, offset ] = getSmallMemoryFile(line, db)
- const fd = await fs.open(path.join(setting.fieldDir, dir, file), 'r')
- const buffer = Buffer.alloc(db.recordSize)
- await fd.read(buffer, 0, db.recordSize, offset)
- fd.close().catch(consoleWarn)
- return buffer
-}
-
-
-
-/**
- * Set city record
- * @param {any} buffer
- * @param {LookupResult} geodata
- * @param {number} offset
- * @return {LookupResult}
- */
-const setCityRecord = (buffer, geodata, offset) => {
- var locId
- if(setting.locFile){
- locId = buffer.readUInt32LE(offset)
- offset += 4
- }
- if(mainFieldHash.latitude){
- geodata.latitude = buffer.readInt32LE(offset) / 10000
- offset += 4
- }
- if(mainFieldHash.longitude){
- geodata.longitude = buffer.readInt32LE(offset) / 10000
- offset += 4
- }
- if(mainFieldHash.postcode){
- var postcode2 = buffer.readUInt32LE(offset)
- var postcode1 = buffer.readInt8(offset + 4)
- if (postcode2) {
- var postcode, tmp
- if(postcode1 < -9){
- tmp = (-postcode1).toString()
- postcode = postcode2.toString(36)
- postcode = getZeroFill(postcode.slice(0, -tmp[1]), tmp[0]-0) + '-' + getZeroFill(postcode.slice(-tmp[1]), tmp[1]-0)
- } else if(postcode1 < 0){
- postcode = getZeroFill(postcode2.toString(36), -postcode1)
- } else if(postcode1 < 10){
- postcode = getZeroFill(postcode2.toString(10), postcode1)
- } else if(postcode1 < 72){
- postcode1 = String(postcode1)
- postcode = getZeroFill(postcode2.toString(10), (postcode1[0]-0) + (postcode1[1]-0))
- postcode = postcode.slice(0, postcode1[0]-0) + '-' + postcode.slice(postcode1[0]-0)
- } else {
- postcode = postcode1.toString(36).slice(1) + postcode2.toString(36)
- }
- geodata.postcode = postcode.toUpperCase()
- }
- offset += 5
- }
- if(mainFieldHash.area){
- geodata.area = AreaJson[buffer.readUInt8(offset)]
- offset += 1
- }
-
- if(locId){
- var locOffset = (locId-1) * setting.locRecordSize
- if(locFieldHash.country){
- geodata.country = LocBuffer.toString('utf8', locOffset, locOffset += 2)
- if(locFieldHash.eu){
- geodata.eu = EuJson[geodata.country]
- }
- }
- if(locFieldHash.region1){
- var region1 = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region1 > 0) geodata.region1 = num37ToStr(region1)
- }
- if(locFieldHash.region1_name){
- var region1_name = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region1_name > 0) geodata.region1_name = Region1NameJson[region1_name]
- }
- if(locFieldHash.region2){
- var region2 = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region2 > 0) geodata.region2 = num37ToStr(region2)
- }
- if(locFieldHash.region2_name){
- var region2_name = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region2_name > 0) geodata.region2_name = Region2NameJson[region2_name]
- }
- if(locFieldHash.metro){
- var metro = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(metro > 0) geodata.metro = metro
- }
- if(locFieldHash.timezone){
- var timezone = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(timezone > 0) geodata.timezone = TimezoneJson[timezone]
- }
- if(locFieldHash.city){
- var city = LocBuffer.readUInt32LE(locOffset)
- locOffset += 4
- if(city > 0){
- var start = city >>> 8
- geodata.city = CityNameBuffer.toString('utf8', start, start + (city & 255))
- }
- }
- }
- return setCountryInfo(geodata)
-}
-
-/**
- * Set country information
- * @param {LookupResult} geodata
- * @return {LookupResult}
- */
-const setCountryInfo = (geodata) => {
- if(setting.addCountryInfo){
- var h = countries[geodata.country]
- geodata.country_name = h.name
- geodata.country_native = h.native
- geodata.continent = h.continent
- geodata.continent_name = continents[h.continent]
- geodata.capital = h.capital
- geodata.phone = h.phone
- geodata.currency = h.currency
- geodata.languages = h.languages
- }
- return geodata
-}
-
-await reload()
diff --git a/src/main-pack.patch b/src/main-pack.patch
deleted file mode 100644
index ee2aac7..0000000
--- a/src/main-pack.patch
+++ /dev/null
@@ -1,32 +0,0 @@
---- main.mjs 2024-10-24 00:32:36.050599300 +0900
-+++ main-pack.mjs 2024-10-24 21:32:41.146024800 +0900
-@@ -9,6 +9,7 @@ import { CronJob } from 'cron'
-
- import { setting, setSetting, getSettingCmd, consoleLog, consoleWarn } from './setting.mjs'
- import { num37ToStr, getSmallMemoryFile, getZeroFill, aton6Start, aton4 } from './utils.mjs'
-+import { update as updataDbAsync } from './db.mjs'
-
- const v4db = setting.v4
- const v6db = setting.v6
-@@ -295,6 +296,21 @@ export const updateDb = (_setting, noRel
- arg = getSettingCmd()
- }
-
-+ if(!_setting && !sync && !setting.smallMemory){
-+ var sameDbSetting = setting.sameDbSetting
-+ if(!sameDbSetting) setting.sameDbSetting = true
-+ return updataDbAsync().then((r) => {
-+ setting.sameDbSetting = sameDbSetting
-+ if(r === true){
-+ if(!noReload) reload()
-+ }
-+ return true
-+ }).catch(e => {
-+ setting.sameDbSetting = sameDbSetting
-+ throw e
-+ })
-+ }
-+
- var scriptPath = path.resolve(_setting ? _setting.apiDir : setting.apiDir, 'script', 'updatedb.mjs')
- if(scriptPath.includes(' ')) scriptPath = '"' + scriptPath + '"'
- var cmd = 'node ' + scriptPath
diff --git a/src/main.mjs b/src/main.mjs
deleted file mode 100644
index 151205e..0000000
--- a/src/main.mjs
+++ /dev/null
@@ -1,544 +0,0 @@
-
-import fs from 'fs/promises'
-import fsSync from 'fs'
-import path from 'path'
-import { exec, execSync } from 'child_process'
-
-import { countries, continents } from 'countries-list'
-import { CronJob } from 'cron'
-
-import { setting, setSetting, getSettingCmd, consoleLog, consoleWarn } from './setting.mjs'
-import { num37ToStr, getSmallMemoryFile, getZeroFill, aton6Start, aton4 } from './utils.mjs'
-
-const v4db = setting.v4
-const v6db = setting.v6
-const locFieldHash = setting.locFieldHash
-const mainFieldHash = setting.mainFieldHash
-
-// JavaScript type definition
-/**
- * @typedef {Object} LookupResult
- * @property {number} [latitude]
- * @property {number} [longitude]
- * @property {string} [postcode]
- * @property {string} [area]
- * @property {string} [country]
- * @property {boolean} [eu]
- * @property {string} [region1]
- * @property {string} [region1_name]
- * @property {string} [region2]
- * @property {string} [region2_name]
- * @property {number} [metro]
- * @property {string} [timezone]
- * @property {string} [city]
- * @property {string} country_name
- * @property {string} country_native
- * @property {string} continent
- * @property {string} continent_name
- * @property {string} capital
- * @property {number[]} phone
- * @property {string[]} currency
- * @property {string[]} languages
- */
-
-
-//---------------------------------------
-// Database lookup
-//---------------------------------------
-/**
- * lookup ip address
- * @param {string} ip - ipv4 or ipv6 formatted address
- * @return {LookupResult | Promise | null} location information
- */
-export const lookup = (ip) => {
- // net.isIP(ip) is good for checking ip address format
- // but it's slow for checking ipv6 address
- // therefore, we use ip.includes(':') instead
- var isIpv6
- if(ip.includes(':')){
- ip = aton6Start(ip)
- isIpv6 = ip.constructor === BigInt
- } else {
- ip = aton4(ip)
- isIpv6 = false
- }
- const db = isIpv6 ? v6db : v4db
- if(!(ip >= db.firstIp)) return null
- const list = db.startIps
- var fline = 0, cline = db.lastLine, line
- for(;;){
- line = fline + cline >> 1
- if(ip < list[line]){
- if(cline - fline < 2) return null
- cline = line - 1
- } else {
- if(fline === line) {
- if(cline !== line && ip >= list[cline]) {
- line = cline
- }
- break
- }
- fline = line
- }
- }
-
- if(setting.smallMemory){
- // this case return Promise
- return lineToFile(line, db).then(buffer => {
- var endIp = isIpv6 ? buffer.readBigUInt64LE(0) : buffer.readUInt32LE(0)
- if(ip > endIp) return null
- if(setting.isCountry){
- return setCountryInfo({
- country: buffer.toString('latin1', isIpv6 ? 8 : 4, isIpv6 ? 10 : 6)
- })
- }
- return setCityRecord(buffer, {}, isIpv6 ? 8 : 4)
- })
- }
- if(ip > db.endIps[line]) return null
- if(setting.isCountry){
- return setCountryInfo({
- country: db.mainBuffer.toString('latin1', line * db.recordSize, line * db.recordSize + 2)
- })
- }
- return setCityRecord(db.mainBuffer, {}, line * db.recordSize)
-}
-
-/**
- * setup database without reload
- * @param {object} [_setting]
- * @return {void}
- */
-export const setupWithoutReload = setSetting
-
-/**
- * clear in-memory database
- * @type {function}
- * @return {void}
- */
-export const clear = () => {
- v4db.startIps = v6db.startIps = v4db.endIps = v6db.endIps = v4db.mainBuffer = v6db.mainBuffer = null
- Region1NameJson = Region2NameJson = TimezoneJson = LocBuffer = CityNameBuffer = EuJson = null
-}
-
-var Region1NameJson, Region2NameJson, TimezoneJson, LocBuffer, CityNameBuffer, AreaJson, EuJson
-var updateJob
-/**
- * reload in-memory database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [sync] - sync mode
- * @param {boolean} [_runningUpdate] - if it's running update [internal use]
- * @return {Promise|void}
- */
-export const reload = async (_setting, sync, _runningUpdate) => {
- var curSetting = setting
- if(_setting){
- var oldSetting = Object.assign({}, setting)
- setSetting(_setting)
- curSetting = Object.assign({}, setting)
- Object.assign(setting, oldSetting)
- }
- const dataDir = curSetting.fieldDir
- const v4 = v4db, v6 = v6db
- var dataFiles = {
- v41: path.join(dataDir, '4-1.dat'),
- v42: path.join(dataDir, '4-2.dat'),
- v43: path.join(dataDir, '4-3.dat'),
- v61: path.join(dataDir, '6-1.dat'),
- v62: path.join(dataDir, '6-2.dat'),
- v63: path.join(dataDir, '6-3.dat'),
- cityLocation: path.join(dataDir, 'location.dat'),
- cityName: path.join(dataDir, 'name.dat'),
- citySub: path.join(dataDir, 'sub.json')
- }
-
- var locBuffer, cityNameBuffer, subBuffer
- var buffer41, buffer42, buffer43, buffer61, buffer62, buffer63
- var testDir = dataDir
- if(curSetting.smallMemory){
- testDir = path.join(testDir, 'v4')
- }
-
- if(sync){
- if(!fsSync.existsSync(testDir)){
- consoleLog('Database creating ...')
- updateDb(_setting && curSetting, true, true)
- consoleLog('Database created')
- }
- buffer41 = fsSync.readFileSync(dataFiles.v41)
- buffer61 = fsSync.readFileSync(dataFiles.v61)
- if(!curSetting.smallMemory){
- buffer42 = fsSync.readFileSync(dataFiles.v42)
- buffer43 = fsSync.readFileSync(dataFiles.v43)
- buffer62 = fsSync.readFileSync(dataFiles.v62)
- buffer63 = fsSync.readFileSync(dataFiles.v63)
- }
- if(curSetting.locFile){
- locBuffer = fsSync.readFileSync(dataFiles.cityLocation)
- if(locFieldHash.city){
- cityNameBuffer = fsSync.readFileSync(dataFiles.cityName)
- }
- if(locFieldHash.region1_name || locFieldHash.region2_name || locFieldHash.timezone || mainFieldHash.area || locFieldHash.eu){
- subBuffer = fsSync.readFileSync(dataFiles.citySub)
- }
- }
- } else {
- if(!fsSync.existsSync(testDir)){
- consoleLog('Database creating ...')
- await updateDb(_setting && curSetting, true)
- consoleLog('Database created')
- }
- var prs = [
- fs.readFile(dataFiles.v41).then(data => buffer41 = data),
- fs.readFile(dataFiles.v61).then(data => buffer61 = data),
- ]
- if(!curSetting.smallMemory){
- prs.push(
- fs.readFile(dataFiles.v42).then(data => buffer42 = data),
- fs.readFile(dataFiles.v43).then(data => buffer43 = data),
- fs.readFile(dataFiles.v62).then(data => buffer62 = data),
- fs.readFile(dataFiles.v63).then(data => buffer63 = data)
- )
- }
- if(curSetting.locFile){
- prs.push(fs.readFile(dataFiles.cityLocation).then(data => locBuffer = data))
- if(locFieldHash.city){
- prs.push(fs.readFile(dataFiles.cityName).then(data => cityNameBuffer = data))
- }
- if(locFieldHash.region1_name || locFieldHash.region2_name || locFieldHash.timezone || mainFieldHash.area || locFieldHash.eu){
- prs.push(fs.readFile(dataFiles.citySub).then(data => subBuffer = data))
- }
- }
- await Promise.all(prs)
- }
-
- if(_setting){
- Object.assign(setting, curSetting)
- }
-
- v4.startIps = new Uint32Array(buffer41.buffer, 0, buffer41.byteLength >> 2)
- v6.startIps = new BigUint64Array(buffer61.buffer, 0, buffer61.byteLength >> 3)
- if(!curSetting.smallMemory){
- v4.endIps = new Uint32Array(buffer42.buffer, 0, buffer42.byteLength >> 2)
- v4.mainBuffer = buffer43
- v6.endIps = new BigUint64Array(buffer62.buffer, 0, buffer62.byteLength >> 3)
- v6.mainBuffer = buffer63
- }
-
- v4.lastLine = v4.startIps.length - 1
- v6.lastLine = v6.startIps.length - 1
- v4.firstIp = v4.startIps[0]
- v6.firstIp = v6.startIps[0]
- if(curSetting.isCity){
- LocBuffer = locBuffer
- CityNameBuffer = cityNameBuffer
- if(subBuffer){
- var tmpJson = JSON.parse(subBuffer)
- if(locFieldHash.region1_name) Region1NameJson = tmpJson.region1_name
- if(locFieldHash.region2_name) Region2NameJson = tmpJson.region2_name
- if(locFieldHash.timezone) TimezoneJson = tmpJson.timezone
- if(mainFieldHash.area) AreaJson = tmpJson.area
- if(locFieldHash.eu) EuJson = tmpJson.eu
- }
- }
-
- // To avoid the error (when the database is updated while the server is running),
- // we need to syncronous update the database
- if(setting.smallMemory && _runningUpdate){
- const rimraf = (dir) => {
- if(fs.rm){
- return fs.rm(dir, {recursive: true, force: true, maxRetries: 3})
- }
- return fs.rmdir(dir, {recursive: true, maxRetries: 3})
- }
- fsSync.cpSync(path.join(setting.fieldDir, 'v4-tmp'), path.join(setting.fieldDir, 'v4'), {recursive: true, force: true})
- fsSync.cpSync(path.join(setting.fieldDir, 'v6-tmp'), path.join(setting.fieldDir, 'v6'), {recursive: true, force: true})
- rimraf(path.join(setting.fieldDir, 'v4-tmp')).catch(consoleWarn)
- rimraf(path.join(setting.fieldDir, 'v6-tmp')).catch(consoleWarn)
- }
-
- if(!updateJob && setting.autoUpdate){
- updateJob = new CronJob(setting.autoUpdate, () => {
- updateDb().finally(() => {})
- }, null, true, 'UTC')
- } else if(updateJob && !setting.autoUpdate){
- updateJob.stop()
- updateJob = null
- }
-}
-
-const watchHash = {}
-/**
- * Watch database directory.
- * When database file is updated, it reload the database automatically
- * This causes error if you use ILA_SMALL_MEMORY=true
- * @type {function}
- * @param {string} [name] - name of watch. If you want to watch multiple directories, you can set different name for each directory
- */
-export const watchDb = (name = 'ILA') => {
- var watchId = null
- watchHash[name] = fsSync.watch(setting.fieldDir, (eventType, filename) => {
- if(!filename.endsWith('.dat')) return;
- if(fsSync.existsSync(path.join(setting.fieldDir, filename))) {
- if(watchId) clearTimeout(watchId)
- watchId = setTimeout(reload, 30 * 1000)
- }
- })
-}
-
-/**
- * Stop watching database directory
- * @type {function}
- * @param {string} [name]
- */
-export const stopWatchDb = (name = 'ILA') => {
- if(watchHash[name]){
- watchHash[name].close()
- delete watchHash[name]
- }
-}
-
-/**
- * Update database and auto reload database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [noReload] - if you don't want to reload the database after update
- * @param {boolean} [sync] - if you want to update the database in sync mode
- * @return {Promise} - true if database is updated, false if no need to update
- */
-export const updateDb = (_setting, noReload, sync) => {
- // By import { updateDb } from './db.js' is the better way for update.
- // However, db.js import many external modules, it makes slow down the startup time and uses more memory.
- // Therefore, we use exec() to run the script in the other process.
- var arg, runningUpdate = false
- if(_setting){
- var oldSetting = Object.assign({}, setting)
- setSetting(_setting)
- arg = getSettingCmd()
- Object.assign(setting, oldSetting)
- } else {
- arg = getSettingCmd()
- }
-
- var scriptPath = path.resolve(_setting ? _setting.apiDir : setting.apiDir, 'script', 'updatedb.mjs')
- if(scriptPath.includes(' ')) scriptPath = '"' + scriptPath + '"'
- var cmd = 'node ' + scriptPath
- if(!_setting){
- arg += ' ILA_SAME_DB_SETTING=true'
- }
- if(_setting && _setting.smallmemory || !_setting && setting.smallMemory){
- runningUpdate = true
- arg += ' ILA_RUNNING_UPDATE=true'
- }
-
- if(arg){
- cmd += ' ' + arg
- }
- if(sync){
- try{
- var stdout = execSync(cmd)
- if(stdout.includes('NO NEED TO UPDATE')){
- return true
- }
- if(stdout.includes('SUCCESS TO UPDATE')){
- if(!noReload){
- reload(_setting, sync)
- }
- return true
- }
- return false
- }catch(e){
- consoleWarn(e)
- return false
- }
- }
- return new Promise((resolve, reject) => {
- exec(cmd, (err, stdout, stderr) => {
- if(err) {
- consoleWarn(err)
- }
- if(stderr) {
- consoleWarn(stderr)
- }
- if(stdout) {
- consoleLog(stdout)
- }
- if(err) {
- reject(err)
- } else if(stdout.includes('ERROR TO UPDATE')){
- reject(new Error('ERROR TO UPDATE'))
- } else if(stdout.includes('NO NEED TO UPDATE')){
- resolve(false)
- } else if(stdout.includes('SUCCESS TO UPDATE')){
- if(noReload){
- resolve(true)
- } else {
- reload(_setting, false, runningUpdate).then(() => {
- resolve(true)
- }).catch(reject)
- }
- } else {
- consoleLog('UNKNOWN ERROR')
- reject(new Error('UNKNOWN ERROR'))
- }
- })
- })
-}
-
-/*
--- Remain this code for better performance check
-const lineToFile = (line, db) => {
- const [ dir, file, offset ] = getSmallMemoryFile(line, db)
- return new Promise((resolve, reject) => {
- // stream
-// fsSync.createReadStream(path.join(dir, file), {start: offset, end: offset + db.recordSize - 1}, {highWaterMark: db.recordSize})
-// .on('data', resolve)
-// .on('error', reject)
-
- // fs.readFile
-// fs.readFile(path.join(dir, file)).then(buffer => resolve(buffer.subarray(offset, offset + db.recordSize))).catch(reject)
-
- // fs.open + fs.read
- fs.open(path.join(dir, file), 'r').then(fd => {
- const buffer = Buffer.alloc(db.recordSize)
- fd.read(buffer, 0, db.recordSize, offset).then(() => {
- fd.close().catch(reject)
- resolve(buffer)
- }).catch(reject)
- }).catch(reject)
- })
-}
-*/
-const lineToFile = async (line, db) => {
- const [ dir, file, offset ] = getSmallMemoryFile(line, db)
- const fd = await fs.open(path.join(setting.fieldDir, dir, file), 'r')
- const buffer = Buffer.alloc(db.recordSize)
- await fd.read(buffer, 0, db.recordSize, offset)
- fd.close().catch(consoleWarn)
- return buffer
-}
-
-
-
-/**
- * Set city record
- * @param {any} buffer
- * @param {LookupResult} geodata
- * @param {number} offset
- * @return {LookupResult}
- */
-const setCityRecord = (buffer, geodata, offset) => {
- var locId
- if(setting.locFile){
- locId = buffer.readUInt32LE(offset)
- offset += 4
- }
- if(mainFieldHash.latitude){
- geodata.latitude = buffer.readInt32LE(offset) / 10000
- offset += 4
- }
- if(mainFieldHash.longitude){
- geodata.longitude = buffer.readInt32LE(offset) / 10000
- offset += 4
- }
- if(mainFieldHash.postcode){
- var postcode2 = buffer.readUInt32LE(offset)
- var postcode1 = buffer.readInt8(offset + 4)
- if (postcode2) {
- var postcode, tmp
- if(postcode1 < -9){
- tmp = (-postcode1).toString()
- postcode = postcode2.toString(36)
- postcode = getZeroFill(postcode.slice(0, -tmp[1]), tmp[0]-0) + '-' + getZeroFill(postcode.slice(-tmp[1]), tmp[1]-0)
- } else if(postcode1 < 0){
- postcode = getZeroFill(postcode2.toString(36), -postcode1)
- } else if(postcode1 < 10){
- postcode = getZeroFill(postcode2.toString(10), postcode1)
- } else if(postcode1 < 72){
- postcode1 = String(postcode1)
- postcode = getZeroFill(postcode2.toString(10), (postcode1[0]-0) + (postcode1[1]-0))
- postcode = postcode.slice(0, postcode1[0]-0) + '-' + postcode.slice(postcode1[0]-0)
- } else {
- postcode = postcode1.toString(36).slice(1) + postcode2.toString(36)
- }
- geodata.postcode = postcode.toUpperCase()
- }
- offset += 5
- }
- if(mainFieldHash.area){
- geodata.area = AreaJson[buffer.readUInt8(offset)]
- offset += 1
- }
-
- if(locId){
- var locOffset = (locId-1) * setting.locRecordSize
- if(locFieldHash.country){
- geodata.country = LocBuffer.toString('utf8', locOffset, locOffset += 2)
- if(locFieldHash.eu){
- geodata.eu = EuJson[geodata.country]
- }
- }
- if(locFieldHash.region1){
- var region1 = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region1 > 0) geodata.region1 = num37ToStr(region1)
- }
- if(locFieldHash.region1_name){
- var region1_name = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region1_name > 0) geodata.region1_name = Region1NameJson[region1_name]
- }
- if(locFieldHash.region2){
- var region2 = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region2 > 0) geodata.region2 = num37ToStr(region2)
- }
- if(locFieldHash.region2_name){
- var region2_name = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(region2_name > 0) geodata.region2_name = Region2NameJson[region2_name]
- }
- if(locFieldHash.metro){
- var metro = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(metro > 0) geodata.metro = metro
- }
- if(locFieldHash.timezone){
- var timezone = LocBuffer.readUInt16LE(locOffset)
- locOffset += 2
- if(timezone > 0) geodata.timezone = TimezoneJson[timezone]
- }
- if(locFieldHash.city){
- var city = LocBuffer.readUInt32LE(locOffset)
- locOffset += 4
- if(city > 0){
- var start = city >>> 8
- geodata.city = CityNameBuffer.toString('utf8', start, start + (city & 255))
- }
- }
- }
- return setCountryInfo(geodata)
-}
-
-/**
- * Set country information
- * @param {LookupResult} geodata
- * @return {LookupResult}
- */
-const setCountryInfo = (geodata) => {
- if(setting.addCountryInfo){
- var h = countries[geodata.country]
- geodata.country_name = h.name
- geodata.country_native = h.native
- geodata.continent = h.continent
- geodata.continent_name = continents[h.continent]
- geodata.capital = h.capital
- geodata.phone = h.phone
- geodata.currency = h.currency
- geodata.languages = h.languages
- }
- return geodata
-}
-
-await reload()
diff --git a/src/setting.mjs b/src/setting.mjs
deleted file mode 100644
index 7d01ffd..0000000
--- a/src/setting.mjs
+++ /dev/null
@@ -1,185 +0,0 @@
-import path from 'path'
-
-import { fileURLToPath } from 'url'
-import { getFieldsSize } from './utils.mjs'
-
-const defaultSetting = {
- // -- setting for all
- fields: ['country'],
- dataDir: '../data/',
- tmpDataDir: '../tmp/',
- apiDir: '..',
-
- // ---- small memory setting
- smallMemory: false,
- smallMemoryFileSize: 4096,
-
- // ---- setting for lookup
- addCountryInfo: false,
-
- // -- setting for update
- licenseKey: 'redist',
- ipLocationDb: '',
- downloadType: 'reuse',
- series: 'GeoLite2', // or GeoIP2
- language: 'en',
- fakeData: false,
- autoUpdate: 'default',
-
- sameDbSetting: false,
- multiDbDir: false,
-
- browserType: false,
- silent: false,
-}
-
-// default setting
-export const setting = {
- v4: {ipv4: true, ipv6: false, name: 'v4'},
- v6: {ipv4: false, ipv6: true, name: 'v6'},
- mainFieldHash: {},
- locFieldHash: {},
-}
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-const mainFields = ['latitude', 'longitude', 'area', 'postcode']
-const locFields = ['country', 'region1', 'region1_name', 'region2', 'region2_name', 'metro', 'timezone', 'city', 'eu']
-
-const shortNumber = {
- latitude: 1,
- longitude: 2,
- area: 4,
- postcode: 8,
- country: 16,
- region1: 32,
- region1_name: 64,
- region2: 128,
- region2_name: 256,
- metro: 512,
- timezone: 1024,
- city: 2048,
- eu: 4096
-}
-
-const make_key = (key) => {
- return 'ILA_' + key.replace(/([A-Z])/g, char => '_' + char).toUpperCase()
-}
-
-export const consoleLog = (...args) => {
- if(setting.silent) return
- console.log(...args)
-}
-
-export const consoleWarn = (...args) => {
- if(setting.silent) return
- console.warn(...args)
-}
-
-export const getSettingCmd = () => {
- const ret = []
- for(const key in defaultSetting){
- if(setting[key] && setting[key] !== defaultSetting[key]){
- var value = String(setting[key])
- if(value.includes(' ')) value = '"' + value + '"'
- ret.push(make_key(key) + '=' + value)
- }
- }
- return ret.join(' ')
-}
-
-const inputSetting = {}
-var settingKeys = Object.keys(defaultSetting)
-for(var env in process.env){
- for(var key of settingKeys){
- if(env.toUpperCase() === make_key(key)){
- inputSetting[key] = process.env[env]
- }
- }
-}
-for(var arg of process.argv){
- var v = arg.toUpperCase()
- for(var key of settingKeys){
- if(v.includes(make_key(key) + '=')){
- inputSetting[key] = arg.split('=')[1]
- }
- }
-}
-
-const NumReg = /^\d+$/
-export const setSetting = (_setting = {}) => {
- for(var key in _setting){
- var value = setting[key] = _setting[key]
- if(value === "false") setting[key] = false
- else if(value === "true") setting[key] = true
- else if(NumReg.test(value)) setting[key] = parseInt(value)
- }
-
- if(setting.autoUpdate === 'default'){
- setting.autoUpdate = Math.floor(Math.random()*59.9) + ' ' + Math.floor(Math.random()*59.9) + ' 0 * * wed,sat'
- }
-
- // Directory Setting
- const windowsDriveReg = /^[a-zA-Z]:\\/
- if(!setting.dataDir.startsWith('/') && !setting.dataDir.startsWith('\\\\') && !windowsDriveReg.test(setting.dataDir)){
- setting.dataDir = path.resolve(__dirname, setting.dataDir)
- }
- if(!setting.tmpDataDir.startsWith('/') && !setting.tmpDataDir.startsWith('\\\\') && !windowsDriveReg.test(setting.tmpDataDir)){
- setting.tmpDataDir = path.resolve(__dirname, setting.tmpDataDir)
- }
- if(!setting.apiDir.startsWith('/') && !setting.apiDir.startsWith('\\\\') && !windowsDriveReg.test(setting.apiDir)){
- setting.apiDir = path.resolve(__dirname, setting.apiDir)
- }
-
- // Fields Setting
- if(typeof setting.fields === 'string'){
- setting.fields = setting.fields.split(/\s*,\s*/)
- }
- if(setting.fields.includes('all')) {
- setting.fields = mainFields.concat(locFields)
- } else {
- setting.fields = setting.fields.filter(v => mainFields.includes(v) || locFields.includes(v))
- }
-
- if(setting.fields.length === 1 && setting.fields[0] === 'country'){
- setting.dataType = 'country'
- } else {
- setting.dataType = 'city'
- }
- setting.isCountry = setting.dataType === 'country'
- setting.isCity = !setting.isCountry
-
- for(var field of mainFields){
- setting.mainFieldHash[field] = setting.fields.includes(field)
- }
-
- setting.noLocFile = true
- for(var field of locFields){
- setting.locFieldHash[field] = setting.fields.includes(field)
- if(setting.locFieldHash[field]){
- setting.noLocFile = false
- }
- }
- if(setting.isCountry) setting.noLocFile = true
- setting.locFile = !setting.noLocFile
-
- setting.fieldDir = path.join(setting.dataDir, setting.fields.reduce((sum, v) => sum + shortNumber[v], 0).toString(36))
-
- // Main Data Record Size
- var mainRecordSize = setting.isCountry ? 2 : getFieldsSize(setting.fields.filter(v => mainFields.includes(v)))
- if(setting.locFile) mainRecordSize += 4
- setting.v4.recordSize = setting.v6.recordSize = setting.mainRecordSize = mainRecordSize
- setting.locRecordSize = getFieldsSize(setting.fields.filter(v => locFields.includes(v)))
- if(setting.smallMemory){
- setting.v4.recordSize += 4
- setting.v6.recordSize += 8
- setting.v4.fileLineMax = (setting.smallMemoryFileSize / setting.v4.recordSize | 0) || 1
- setting.v6.fileLineMax = (setting.smallMemoryFileSize / setting.v6.recordSize | 0) || 1
- setting.fileMax = 1024
- setting.v4.folderLineMax = setting.v4.fileLineMax * setting.fileMax
- setting.v6.folderLineMax = setting.v6.fileLineMax * setting.fileMax
- }
-}
-
-setSetting(Object.assign({}, defaultSetting, inputSetting))
diff --git a/src/utils.mjs b/src/utils.mjs
deleted file mode 100644
index ba06a5d..0000000
--- a/src/utils.mjs
+++ /dev/null
@@ -1,199 +0,0 @@
-import path from 'path'
-
-// export const DEBUG = process.argv.includes('debug')
-
-//export const MaxLocationId = 0xFFFFFFFF - 26*26
-export const countryCodeToNum = (code) => { // 0~675
- code = code.toUpperCase()
- return (code.charCodeAt(0)-65)*26 + (code.charCodeAt(1)-65)
-}
-export const numToCountryCode = (num) => {
- return String.fromCharCode((num/26|0) + 65, num % 26 + 65)
-}
-
-export const getFieldsSize = (types) => {
- var size = 0
- for (const type of types) {
- switch (type) {
- case 'postcode':
- size += 5
- break
- case 'area':
- size += 1
- break
- case 'latitude':
- case 'longitude':
- case 'city':
- size += 4
- break
- case 'eu':
- break
- default:
- size += 2
- break
- }
- }
- return size
-}
-
-export const ntoa4 = (n) => {
- return [n >>> 24, n >> 16 & 255, n >> 8 & 255, n & 255].join('.')
-}
-
-export const aton4 = (a) => {
- a = a.split(/\./)
- return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
-}
-
-export const aton6Start = (a) => {
- if(a.includes('.')){
- return aton4(a.split(':').pop())
- }
- a = a.split(/:/)
- const l = a.length - 1
- var i, r = 0n
- if (l < 7) {
- const omitStart = a.indexOf('')
- if(omitStart < 4){
- const omitted = 8 - a.length, omitEnd = omitStart + omitted
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0
- }
- }
- }
- for (i = 0; i < 4; i++) {
- if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i))
- }
- return r
-}
-
-export const aton6 = (a) => {
- a = a.replace(/"/g, '').split(/:/)
-
- const l = a.length - 1
- var i
- if (a[l] === '') a[l] = 0
- if (l < 7) {
- const omitted = 8 - a.length, omitStart = a.indexOf(''), omitEnd = omitStart + omitted
- for (i = 7; i >= omitStart; i--) {
- a[i] = i > omitEnd ? a[i - omitted] : 0
- }
- }
-
- var r = 0n
- for (i = 0; i < 4; i++) {
- if (a[i]) {
- r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i))
- }
- }
- return r
-}
-
-const v4MappedReg = /^(?:0:0:0:0:0|:):ffff:(\d+\.\d+\.\d+\.\d+)$/i
-export const v4Mapped = (addr) => {
- const match = v4MappedReg.exec(addr)
- return match && match[1]
-}
-
-const PrivateIpRegList = [
- /^10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^192\.168\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^172\.16\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^169\.254\.([0-9]{1,3})\.([0-9]{1,3})/,
- /^fc00:/,
- /^fe80:/
-]
-
-export const isPrivateIP = function(addr) {
- for(const reg of PrivateIpRegList){
- if(reg.test(addr)){
- return true
- }
- }
- return false
-}
-
-export const strToNum37 = (a) => {
- var num = 0
- for(var i = 0; i < a.length; i++){
- num = num * 37 + parseInt(a[i], 36) + 1
- }
- return num
-}
-
-export const num37ToStr = (num) => {
- var str = ''
- while(num > 0){
- str = (num % 37 - 1).toString(36) + str
- num = Math.floor(num / 37)
- }
- return str.toUpperCase()
-}
-
-export const getZeroFill = (num, len) => {
- return '0'.repeat(len - num.length) + num
-}
-
-const getUnderberFill = (num, len) => {
- if(num.length > len) return num
- return '_'.repeat(len - num.length) + num
-}
-export const numberToDir = (num) => {
- return getUnderberFill(num.toString(36), 2)
-}
-export const getSmallMemoryFile = (line, db, isTmp) => {
- const dbNumber = line / db.folderLineMax | 0
- const fileNumber = (line - dbNumber * db.folderLineMax) / db.fileLineMax | 0
- const lineOffset = line - dbNumber * db.folderLineMax - fileNumber * db.fileLineMax
- var dir = path.join(db.name + (isTmp ? '-tmp' : ''), getUnderberFill(dbNumber.toString(36), 2))
- return [dir, getUnderberFill(fileNumber.toString(36), 2), lineOffset * db.recordSize]
-}
-
-const isPostNumReg = /^\d+$/
-const isPostNumReg2 = /^(\d+)[-\s](\d+)$/
-const isPostStrReg = /^([A-Z\d]+)$/
-const isPostStrReg2 = /^([A-Z\d]+)[-\s]([A-Z\d]+)$/
-export const getPostcodeDatabase = (postcode) => {
- if(!postcode) return [0, 0];
- // number type
- if(isPostNumReg.test(postcode)){
- return [
- postcode.length, // 1~9
- parseInt(postcode, 10) // 0~999999999
- ]
- }
- var r = isPostNumReg2.exec(postcode)
- if(r){
- return [
- parseInt(r[1].length + '' + r[2].length, 10), // 11~66
- parseInt(r[1] + r[2], 10) // 0~999999999
- ]
- }
-
- // string type
- r = isPostStrReg.exec(postcode)
- if(r){
- var num = parseInt(postcode, 36)
- if(num < Math.pow(2, 32)){
- return [
- -postcode.length, // -1~-9
- num
- ]
- } else {
- return [
- parseInt('2' + postcode.slice(0, 1), 36), // 72~107,
- parseInt(postcode.slice(1), 36) // 0~2176782335 MAX: 6char ZZZZZZ
- ]
- }
- }
-
- r = isPostStrReg2.exec(postcode)
- if(!r){
- console.log('Invalid postcode:', postcode)
- }
- return [
- - parseInt(r[1].length + "" + r[2].length, 10),// -11~-55
- parseInt(r[1] + r[2], 36) // 0~2176782335 MAX: 6char ZZZZZZ
- ]
-}
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 0000000..47d2ae8
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,4 @@
+{
+ "extends": "@total-typescript/tsconfig/tsc/dom/library-monorepo",
+ "exclude": ["**/**/*.config.ts", "**/*.test.ts", "**/*/dist"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..8bdeb8f
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "references": [
+ {
+ "path": "./packages/ip-location-api/tsconfig.json"
+ },
+ {
+ "path": "./packages/country/tsconfig.json"
+ },
+ {
+ "path": "./packages/country-extra/tsconfig.json"
+ },
+ {
+ "path": "./packages/geocode/tsconfig.json"
+ },
+ {
+ "path": "./packages/geocode-extra/tsconfig.json"
+ }
+ ],
+ "files": []
+}
diff --git a/types/cjs/main.d.cts b/types/cjs/main.d.cts
deleted file mode 100644
index adea463..0000000
--- a/types/cjs/main.d.cts
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { ICountry, TContinentCode, TContinents, TCountryCode } from "countries-list";
-
-/**
-* Basic lookup result object.
-*
-* All fields are made optional here, as available fields might differ based your settings.
-*
-* Requires runtime null-checks.
-*/
-export type LookupResult = {
- latitude?: number;
- longitude?: number;
- postcode?: string;
- area?: string;
- country?: TCountryCode;
- eu?: boolean;
- region1?: string;
- region1_name?: string;
- region2?: string;
- region2_name?: string;
- metro?: number;
- timezone?: string;
- city?: string;
-
- country_name?: ICountry["name"];
- country_native?: ICountry["native"];
- continent?: ICountry["continent"];
- continent_name?: TContinents[TContinentCode];
- capital?: ICountry["capital"];
- phone?: ICountry["phone"];
- currency?: ICountry["currency"];
- languages?: ICountry["languages"];
-};
-
-/**
- * lookup ip address [Sync / Async]
- * @param {string} ip - ipv4 or ipv6 formatted address
- * @return location information as either object or promise (based on settings), or null if not found.
- */
-export const lookup: (ip: string) => LookupResult | Promise | null;
-/**
- * setup database without reload
- * @param {object} [_setting]
- * @return {void}
- */
-export const setupWithoutReload: (_setting?: {}) => void;
-/**
- * clear in-memory database
- * @type {function}
- * @return {void}
- */
-export const clear: Function;
-/**
- * reload in-memory database
- * @type {function}
- * @param {object} [_setting]
- * @param {boolean} [sync] - sync mode
- * @param {boolean} [_runningUpdate] - if it's running update [internal use]
- * @return {Promise|void}
- */
-export const reload: Function;
-/**
- * Watch database directory.
- * When database file is updated, it reload the database automatically
- * This causes error if you use ILA_SMALL_MEMORY=true
- * @type {function}
- * @param {string} [name] - name of watch. If you want to watch multiple directories, you can set different name for each directory
- */
-export const watchDb: Function;
-/**
- * Stop watching database directory
- * @type {function}
- * @param {string} [name]
- */
-export const stopWatchDb: Function;
-/**
- * Update database and auto reload database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [noReload] - if you don't want to reload the database after update
- * @param {boolean} [sync] - if you want to update the database in sync mode
- * @return {Promise} - true if database is updated, false if no need to update
- */
-export const updateDb: Function;
diff --git a/types/src/main.d.mts b/types/src/main.d.mts
deleted file mode 100644
index bd7df98..0000000
--- a/types/src/main.d.mts
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { ICountry, TContinentCode, TContinents, TCountryCode } from "countries-list";
-
-/**
-* Basic lookup result object.
-*
-* All fields are made optional here, as available fields might differ based your settings.
-*
-* Requires runtime null-checks.
-*/
-export type LookupResult = {
- latitude?: number;
- longitude?: number;
- postcode?: string;
- area?: string;
- country?: TCountryCode;
- eu?: boolean;
- region1?: string;
- region1_name?: string;
- region2?: string;
- region2_name?: string;
- metro?: number;
- timezone?: string;
- city?: string;
-
- country_name?: ICountry["name"];
- country_native?: ICountry["native"];
- continent?: ICountry["continent"];
- continent_name?: TContinents[TContinentCode];
- capital?: ICountry["capital"];
- phone?: ICountry["phone"];
- currency?: ICountry["currency"];
- languages?: ICountry["languages"];
-};
-
-/**
- * lookup ip address [Sync / Async]
- * @param {string} ip - ipv4 or ipv6 formatted address
- * @return location information as either object or promise (based on settings), or null if not found.
- */
-export const lookup: (ip: string) => LookupResult | Promise | null;
-/**
- * setup database without reload
- * @param {object} [_setting]
- * @return {void}
- */
-export const setupWithoutReload: (_setting?: {}) => void;
-/**
- * clear in-memory database
- * @type {function}
- * @return {void}
- */
-export const clear: Function;
-/**
- * reload in-memory database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [sync] - sync mode
- * @param {boolean} [_runningUpdate] - if it's running update [internal use]
- * @return {Promise|void}
- */
-export const reload: Function;
-/**
- * Watch database directory.
- * When database file is updated, it reload the database automatically
- * This causes error if you use ILA_SMALL_MEMORY=true
- * @type {function}
- * @param {string} [name] - name of watch. If you want to watch multiple directories, you can set different name for each directory
- */
-export const watchDb: Function;
-/**
- * Stop watching database directory
- * @type {function}
- * @param {string} [name]
- */
-export const stopWatchDb: Function;
-/**
- * Update database and auto reload database
- * @type {function}
- * @param {object} [_setting] - if you need to update the database with different setting
- * @param {boolean} [noReload] - if you don't want to reload the database after update
- * @param {boolean} [sync] - if you want to update the database in sync mode
- * @return {Promise} - true if database is updated, false if no need to update
- */
-export const updateDb: Function;
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..97d2f95
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,11 @@
+import { coverageConfigDefaults, defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ coverage: {
+ reporter: ['json-summary', 'text', 'html'],
+ exclude: ['**/*.config.ts', '**/dist/**', ...coverageConfigDefaults.exclude],
+ reportOnFailure: true,
+ },
+ },
+})
diff --git a/vitest.workspace.ts b/vitest.workspace.ts
new file mode 100644
index 0000000..b18a56c
--- /dev/null
+++ b/vitest.workspace.ts
@@ -0,0 +1,5 @@
+import { defineWorkspace } from 'vitest/config'
+
+export default defineWorkspace([
+ 'packages/*',
+])